I would like to change a given block of the following image, and then rebuilding image back.
I would like to divide and change pixels from 224x224x3 blocks from such an image. As the image is 1024x1024x3, there will not be an integer number of 224x224x3 blocks, numpyso, I have to select a sub-area of the image to fit the block shape, like as follows:
>>> block_shape = np.array((224, 224, 3))
>>> nblocks = np.array(img.shape) // block_shape # integer division
>>> crop_r, crop_c, crop_ch = nblocks * block_shape
>>> cropped_img = img[:crop_r, :crop_c, :crop_ch]
The cropped image is, therefore, a sub-area of the image that will select non-overlapping blocks of 224x224x3 pixels
>>> cropped_img.shape
(896, 896, 3)
>>> Blocks = view_as_blocks(cropped_img, block_shape=(224, 224, 3))
>>> Blocks.shape
(4, 4, 1, 224, 224, 3)
Now, just suppose I want to change the pixels of a given block, like zeroing all pixels of the first block:
Blocks[0,0,:,:,:,:]=Blocks[0,0,:,:,:,:]*0
Now I have the blocks with one block changed. Then, I need to recover cropped_img with that block changed (by converting variable Blocks back to be only one image/NumPy array), and finally, save the image. How to do that in Python?
P.s= I checked a similar thread (Reverse skimage view_as_blocks() with numpy.reshape()) but the image considered has a different shape, channels and also the order of channels is different.
The nice thing here is that view_as_blocks
returns a view of the array. So if you modify the blocks in-place, you will have modified the original array and you won't need to "unblock":
from skimage import data, util
import matplotlib.pyplot as plt
image = data.camera()
blocked = util.view_as_blocks(image, (256, 256))
blocked[0, 0] *= 0
fig, ax = plt.subplots()
ax.imshow(image)
plt.show()
gives:
If you do need to unblock, you want to think of your blocks as nested lists, and use calls to np.concatenate
to concatenate the blocks on the leading axis along the remaining axes.
In this case, the leading axis is the rows of the blocks, and we want to concatenate along the rows of the remaining axes, which is axis 1:
intermediate = np.concatenate(blocked, axis=1)
print(intermediate.shape)
prints:
(2, 512, 256)
So we now have two "tall" blocks, and we want to concatenate them along the columns, which again will be axis 1 after ignoring the leading axis.
unblocked = np.concatenate(intermediate, axis=1)
fig, ax = plt.subplots()
ax.imshow(unblocked)
plt.show()
This shows the same image as above.
For your color image, the principle is the same, you just need to switch axis=1
for axis=2
.