Reputation: 255
I want to construct an algorithm in Python to slice an image into n-by-n equal blocks. I am aware of existing Python packages that can do this, but I am interested in performing mathematical computations using the dimensions of the resulting blocks, which need not be integer values.
I have successfully converted my image input into ndarray
using numpy
and cv2
. I am curious if I can slice an ndarray
object in a way so that when I split a "pixel", I simply take the weighted average of that "pixel's" contribution to their respective block.
For example, if the image input have dimensions 100px x 100px, and I want to split this image into 7x7 blocks, since 100/7 = ~14.2857, each block will have dimensions 14.2857 x 14.2857. But it doesn't make sense to have a fraction of a pixel. Instead, I wish to interpret this so that the first block contains all of the information from pixels (1,1),(1,2),...,(1,14),(2,1),...,(3,1),...,(14,1),... ,(14,14), 0.2857 of all pixels satisfying (15,k) and (k,15), and 0.2857*0.2857 for pixel (15,15). I wish to do this for all 49 blocks.
Any insight would be appreciated! Thanks.
Upvotes: 2
Views: 195
Reputation: 23637
As you correctly observed, you cannot have 14.29 pixel in an image. However you can perform downsampling to 14 (or less) pixels or upsampling to 15 (or more) pixels. I think the former is what you intend, but the latter is a viable option too.
An easy approach is to resize the image to a new size that is easily split into 7x7. If you use the correct function to do this (e.g. scipy.ndimage.interpolation.zoom
) it will automatically interpolate pixel values.
For simplicity let's assume you want to split 5x5 image into 2x2 blocks:
from scipy.ndimage.interpolation import zoom
import numpy as np
blocks = (2, 2)
image = np.arange(25).reshape(5, 5) * 10
print(image)
# [[ 0 10 20 30 40]
# [ 50 60 70 80 90]
# [100 110 120 130 140]
# [150 160 170 180 190]
# [200 210 220 230 240]]
# use np.ceil instead of np.floor to do upsampling
zoom_factor = [np.floor(s / b) * b / s for s, b in zip(image.shape, blocks)]
zoomed = zoom(image, zoom_factor)
print(zoomed)
# [[ 0 14 26 40]
# [ 69 83 95 109]
# [131 145 157 171]
# [200 214 226 240]]
for b1 in range(0, zoomed.shape[0], blocks[0]):
for b2 in range(0, zoomed.shape[1], blocks[1]):
print('block:')
print(zoomed[b1:b1+blocks[0], :][:, b2:b2+blocks[1]])
# block:
# [[ 0 14]
# [69 83]]
# block:
# [[ 26 40]
# [ 95 109]]
# block:
# [[131 145]
# [200 214]]
# block:
# [[157 171]
# [226 240]]
Note that in the zoomed image the second pixel in the first row has a value of 14, which contains contributions from both neigbors 10 and 20. (It probably contains contribution from other pixels too because zoom uses by default an order 3 spline for interpolation). Other pixels in the middle of the image contain contribution from the entire surrounding neigborhood.
Upvotes: 1