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. ]]