I have started working with images and currently I am trying to rescale and grayscale an image (Size 6000×4000 -> 600×400) to better work with it. For this I am using Numpy and PIL.Images.
import PIL.Image as Img
import numpy as np
img = Img.open('rendering/testpic.jpg', 'r')
r, g, b = img.split()
channels = np.array([np.array(r), np.array(g), np.array(b)])
small_channels = []
for channel in channels:
x_len = len(channel)//10
y_len = len(channel[0])//10
for chunk_x in range(x_len):
for chunk_y in range(y_len):
pix_sum = 0
for x_in_chunk in range(10):
for y_in_chunk in range(10):
pix_sum += channel[chunk_x*10+x_in_chunk,chunk_y*10+y_in_chunk]
channel[chunk_x,chunk_y] = pix_sum // 100
small_channels.append(channel[:x_len,:y_len])
channels = np.array(small_channels)
grayscale = np.round((channels[0]*0.3+ channels[1]*0.6+ channels[2]*0.1)).astype('uint8')
pixels = np.stack([grayscale, grayscale, grayscale], axis = 2)
new_img = Img.fromarray(pixels)
new_img.show()
So what I am doing is chunking the channels into chunks size 10, then mapping the average of the chunk into the topleft corner. In the end I cut off the rest of the picture.
In total this takes around 100 to 130 seconds for me. Is there a faster way to do this? Where am I being inefficient? I’m new so I’m probably doing wrong a lot of stuff. How does Photoshop for example scale pictures up and down so fast?
2
Answers
Instead of looping over every pixel in your image we can use
numpy
array slicing and some methods to speed things up. I have removed the inner loops and used slicing and the.sum()
method ofnumpy
arrays:This algorithm is 3-4 times faster by my testing. I hope this helps. Definitely have a look at
numpy
arrays- they are very useful especially for images and the computation is quicker in a lot of cases.I wouldn’t use loops in this case,
cv2.resize()
will do the job.Here is a time comparison between the three approaches:
Out: