Home > Net >  What can be a faster way to iterate through the pixels of an image?
What can be a faster way to iterate through the pixels of an image?

Time:04-21

I'm trying to loop through all the pixels of an image using Pillow then append the pixels with their corresponding RGBA color into a dictionary

What I have at present is:

pixels = {}
frame = frame.convert('RGBA')
w = frame.size[0]
h = frame.size[1]
pix = frame.load()
for y in range(h):
    for x in range(w):
        point = str(x)   ","   str(y)
        if not point in pixels:
            pixels[point] = (pix[x,y][0], pix[x,y][1], pix[x,y][2], pix[x,y][3])

where frame is the Pillow Image object

However I couldn't achieve the fastest outcome out of this (the above code takes ~ 0.55 to 1.0 sec for a 800x600 image)

I tried making use of a Pandas DataFrame instead of the for loop but it turned out to be much worse (took 13 secs for the same image)

What can be a faster way to achieve the same goal?

EDIT: I'm trying to make a screen sharing application with low bandwidth usage by sending only the updated pixels to the user. The above code runs in an infinite loop and compares the pixels dict with the previous dict every time then sends only the dict of the updated pixels.

What's the purpose of the loop? The loop was put so I can get the x and y points of every RGBA value. Cause if I use something like list(frame.getdata()) for example I'll get only a list of the RGBA values but I don't know how to access the coordinates of each

CodePudding user response:

I managed to get a slightly faster version using list comprehension:

pixels = {f"{x},{y}": (*pix[x,y],) for y in range(h) for x in range(w)}

It also looks like f-strings are faster than str(x) "," str(y).

By the way why do you need the if condition in your loop? If I'm not wrong, you should never get the same pair (x,y). Removing the condition should slightly reduce the execution time in your version.

Here are the performances I got on a big image (~4000x1300):

Your version:          8.60s (157.61it/s)
Your version (no if): 8.06s (168.25it/s)
List comprehension:   4.29s (315.67it/s)

However, I don't really understand what you are trying to do here. There is probably a much more efficient way to do it if you tell us a bit more information about your real objective.

CodePudding user response:

I still don't understand what you are trying to do, but a vectorised way of getting a list of where specific colours occur in an image is with np.where() like this:

import numpy as np

# Get repeatable randomness
np.random.seed(42)

# Generate an image 100x100 pixels in range 0..255
im = np.random.randint(0,256,(100,100), np.uint8)

# Find Y and X values where the image is 128
Y, X = np.where(im==128)

Check how many we found - we expect 1/256 of the 100x100 pixels to be 128, i.e. around 39:

print(len(X), len(Y))
35 35

There are some more sophisticated examples of similar things here.

CodePudding user response:

You haven't heard of numpy.

Do not use .getdata()! Use np.asarray() instead.

from PIL import Image
import numpy as np

pil_image = Image.open("some_image.jpg")

data = np.asarray(pil_image)

print(data.shape) # (height, width, channels)

x = 123
y = 456
print(data[y, x]) # the three color values at that position

# all red values (red ist the first channel, 0):
red = data[:, :, 0]
  • Related