0

How do I downsample an image of any resolution to a quarter of the size by averaging the pixels in numpy?

What I came up through research only works for images that are square (i.e 512 X 512 down to 128 X 128) but will not work for images that are different dimensions (i.e 2400 X 1800 down to 600 X 450). In those cases I get a IndexError: index 450 is out of bounds for axis 1 with size 450.

I am trying to perform this task with numpy array manipulation and without installing other packages and libraries.

I researched a function

numpy.mean()

but I don't know how to use it in reference to this problem.

import cv2
import numpy as np

def quarter_res_avg(im):

    original_width = im.shape[1]
    original_height = im.shape[0]

    width = original_width / 4
    height = original_height / 4

    resized_image = np.zeros(shape=(width, height, 3), dtype=np.uint8)

    scale = 4

    for i in range(width):
        for j in range(height):
            temp = np.array([0, 0, 0])
            for x in range(scale):
                for y in range(scale):
                    temp += im[i*scale+x, j*scale+y]
            resized_image[i, j] = temp/(scale*scale)

    return resized_image

im = cv2.imread('Lenna_test_image.png', 1)
cv2.imwrite('Lenna_test_image_avg.png', quarter_res_avg(im))

Any ideas are much appreciated.

Thanks.

6
  • In Numpy, you index by row first, then column. So the shape of your output array should be resized_image = np.zeros(shape=(height, width, 3), dtype=np.uint8) and your indices in the loop need swapping. Commented Feb 25, 2020 at 19:26
  • If you want to use np.mean(), delete the 5 lines starting temp=... and use resized[WWW] = np.mean(im[XXX:XXX+4, YYY:YYY+4], axis=ZZZ) where you need to think about WWW, XXX, YYY, ZZZ. Commented Feb 25, 2020 at 19:33
  • @MarkSetchell thanks so much! Changing the numpy indexing to resized_image = np.zeros(shape=(height, width, 3), dtype=np.uint8) and the indices for i in range(height): for j in range(width): did the trick. I have been playing around with the np.mean() but I still don't understand how it works. Can you explain it to me in reference to this problem? Commented Feb 25, 2020 at 21:23
  • Try printing im[0:2, 0:3] and you should see the first 2 rows and 3 cols of im. Then print np.mean(im[0:2, 0:3],axis=2) so you get the means of all the elements on the top-left corner of your image. Commented Feb 25, 2020 at 21:50
  • Many thanks @MarkSetchell for i in range(0, original_height, scale): for j in range(0, original_width, scale): resized_image[i/scale, j/scale] = np.mean(im[i:i + scale, j:j+scale], axis=(0, 1)) return resized_image Commented Feb 26, 2020 at 16:17

3 Answers 3

3
import numpy as np
import skimage.measure

your_array = np.random.rand(2400, 800)

new_array = skimage.measure.block_reduce(your_array, (4,4), np.mean)
print(new_array.shape)
Out[18]: (600, 450)
Sign up to request clarification or add additional context in comments.

Comments

0

First reshape your M x N image into a (M//4) x 4 x (N//4) x 4 array, then use np.mean in the second and last dimensions.

from typing import Tuple
import numpy as np

def downsample_by_averaging(img: np.ndarray, window_shape: Tuple[int, int]) -> np.ndarray:
    return np.mean(
        img.reshape((
            *img.shape[:-2],
            img.shape[-2] // window_shape[-2], window_shape[-2],
            img.shape[-1] // window_shape[-1], window_shape[-1],
        )),
        axis=(-1, -3),
    )

downsample_by_averaging(img, (4, 4))

Comments

0

The answer that worked for me with the help from @MarkSetchell in the comments of the question.

Without using np.mean()

def quarter_res_avg(im):

    original_width = im.shape[1]
    original_height = im.shape[0]
    width = original_width / 4
    height = original_height / 4

    resized_image = np.zeros(shape=(height, width, 3), dtype=np.uint8)
    scale = 4

    for i in range(height):
        for j in range(width):
            temp = np.array([0, 0, 0])
            for x in range(scale):
                for y in range(scale):
                    temp += im[i*scale + x, j*scale + y]
            resized_image[i, j] = temp/(scale*scale)

return resized_image

im = cv2.imread('Lenna_test_image.png', 1)
cv2.imwrite('Lenna_test_image_resized.png', quarter_res_avg(im))    

By using np.mean() replace the for loops with:

for i in range(0, original_height, scale):
    for j in range(0, original_width, scale):
        resized_image[i/scale, j/scale] = np.mean(im[i:i + scale, j:j+scale], axis=(0,1))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.