1

I tried to squeeze an image using this code:

def squeeze_image(im,factor):
    new_n = im.shape[0]/ factor
    new_m = im.shape[1]/factor
    new_mat = np.zeros((new_n,new_m)) 
    for j in range(new_mat.shape[1]):
        curr_range = range(j*factor,min((j+1)*factor,im.shape[1])) 
        new_mat[:,j] = im[:,curr_range].mean(axis=1) 
    for i in range(new_mat.shape[0]):
        curr_range1 = range(i*factor,min((i+1)*factor,im.shape[0])) 
        new_mat[i,:] = im[curr_range1,:].mean(axis=0)           
    return new_mat    

when i try it on an image it tells me:

ValueError: could not broadcast input array from shape (512) into shape (256)

for some reason it only works when i use it only for rows or only for columns, like this:

def squeeze_image(im,factor):
    new_n = im.shape[0]/ factor
    new_m = im.shape[1]/factor
    new_mat = np.zeros((new_n,new_m)) 
    for j in range(new_mat.shape[1]):
        curr_range = range(j*factor,min((j+1)*factor,im.shape[1])) 
        new_mat[:,j] = im[:,curr_range].mean(axis=1) 
    return mat

how can i make it work on both rows an columns?

Rtucan
  • 157
  • 1
  • 11

1 Answers1

1

Consider the shapes of the arrays. Suppose im has shape (factor*H, factor*W), so that new_mat has shape (H, W).

Now consider the line

new_mat[:,j] = im[:,curr_range].mean(axis=1) 

On the right-hand side, im[:, curr_range] has shape (factor*H, L) for some L, and im[:,curr_range].mean(axis=1) has shape (factor*H,).

On the left-hand side, new_mat[:, j] has shape (H,).

So except for when factor = 1, the shape of the left- and right-hand sides of the assignment do not match and can not be broadcasted to agree.

This problem affects both versions of squeeze_image.

To fix the problem, you could use

new_mat[i, j] = im[i*factor:(i+1)*factor, j*factor:(j+1)*factor].mean()

to slice off a 2D rectangular patch of im all at once, and take the mean of that 2D patch.

import numpy as np
import itertools as IT

def squeeze_image(im,factor):
    H, W = im.shape
    H2, W2 = H/factor, W/factor
    new_mat = np.zeros((H2, W2)) 
    for i, j in IT.product(range(H2), range(W2)):
        new_mat[i, j] = im[i*factor:(i+1)*factor, j*factor:(j+1)*factor].mean()
    return new_mat

im = np.arange(100).reshape((10,10))
print(im)
# [[  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.]
#  [ 10.  11.  12.  13.  14.  15.  16.  17.  18.  19.]
#  [ 20.  21.  22.  23.  24.  25.  26.  27.  28.  29.]
#  [ 30.  31.  32.  33.  34.  35.  36.  37.  38.  39.]
#  [ 40.  41.  42.  43.  44.  45.  46.  47.  48.  49.]
#  [ 50.  51.  52.  53.  54.  55.  56.  57.  58.  59.]
#  [ 60.  61.  62.  63.  64.  65.  66.  67.  68.  69.]
#  [ 70.  71.  72.  73.  74.  75.  76.  77.  78.  79.]
#  [ 80.  81.  82.  83.  84.  85.  86.  87.  88.  89.]
#  [ 90.  91.  92.  93.  94.  95.  96.  97.  98.  99.]]
im2 = squeeze_image(im, 2)
print(im2)

yields

[[  5.5   7.5   9.5  11.5  13.5]
 [ 25.5  27.5  29.5  31.5  33.5]
 [ 45.5  47.5  49.5  51.5  53.5]
 [ 65.5  67.5  69.5  71.5  73.5]
 [ 85.5  87.5  89.5  91.5  93.5]]

A fancier, but far more efficient way to compute this result is to use reshape/swapaxes to chop the array into blocks of shape (factor,factor), and then take the mean of each block:

def downsample(im,factor):
    H, W = im.shape
    H2, W2 = H/factor, W/factor
    im = im[:H2*factor, :W2*factor]
    new_mat = (im.reshape(H2, factor, -1, factor)
               .swapaxes(1, 2)).reshape(H2, W2, -1).mean(axis=-1)
    return new_mat

This is about 17x faster for the small array im.

In [91]: %timeit squeeze_image(im, 2)
1000 loops, best of 3: 319 µs per loop

In [97]: %timeit downsample(im, 2)
100000 loops, best of 3: 17.2 µs per loop

The speed advantage increases with the number of iterations in the for-loop in squeeze_image, which equals H2*W2.


Note that scipy.ndimage.zoom can squeeze or zoom an image too. It uses spline interpolation instead of taking means. The result, even with spline order=1, is a bit different, however:

import scipy.ndimage as ndimage
print(ndimage.zoom(im, 0.5, order=1))

yields

[[  0.     2.25   4.5    6.75   9.  ]
 [ 22.5   24.75  27.    29.25  31.5 ]
 [ 45.    47.25  49.5   51.75  54.  ]
 [ 67.5   69.75  72.    74.25  76.5 ]
 [ 90.    92.25  94.5   96.75  99.  ]]
Community
  • 1
  • 1
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677