Skip to content Skip to sidebar Skip to footer

How Do I Avoid Looping Through An Image Pixel By Pixel With Opencv & Numpy

I'm looping through this image pixel by pixel and it's really slow. I have the 2 images I'm comparing sliced and flattened so each element is a 3 dimensional rgb value named e1 and

Solution 1:

The way you want to do this is first compare each image to the color you want to see in that image, which makes a boolean mask where that image is the given color. You don't need to flatten the images to do this. This can be done by saying:

image == color

This works fine for grayscale images, but if color is actually along a third dimension, you want to make sure everything along that dimension matches (i.e., you want all of the r, g, and b components to match, so you use np.all along the last axis (-1 gives the last axis):

np.all(image == color, axis=-1)

Which gives a 2d array of booleans where each element is True if that pixel matches color and False if not. Do this for both images (and both colors) and then you'll have a mask where the color matches both images:

np.all(im1==c1, -1) & np.all(im2==c2, -1)

This not only tells you how many pixels match, but where they are (you could plot the above line and see dot at the points where they match). If you just want the count, just use np.sum on the mask which counts True as 1, and False as 0. All together:

defcompare_colors(im1, im2, c1, c2):
    matches = np.all(im1==c1, -1) & np.all(im2==c2, -1)
    return matches.sum()

And to use/test it with random data:

>>>a = np.random.choice([0, 255], (20,20,3))>>>b = np.random.choice([0, 255], (20,20,3))>>>compare_colors(a, b, [255, 0, 255], [0, 255, 0])
12

But before you do that, with your real input, you want to "clean" your colors by a threshold. You could easily do that with np.where which looks at each element of an array, and if a condition is met, gives one thing, and if not, gives another. Here, if the value is less than 128, it uses 0, and otherwise uses 255:

np.where(a<128, 0, 255)

In general, you could write a function like this, with the values above as defaults:

def clean(a, thresh=128, under=0, over=255):
    return np.where(a<128, under, over)

Of course to build up your dict of counts, you still have to loop through each color combination, but that's a short loop (8*8). Here's a full run through:

# some fake data (has values between 0 and 255 for r, g, and b)
H, W = 20, 20
a = np.random.randint(0, 256, (H,W,3))
b = np.random.randint(0, 256, (H,W,3))

# clean the images:
ac = clean(a)
bc = clean(b)

# build a list of all pairs of all 8 colors using itertools.product:
col_combos = itertools.product(itertools.product((0,255), repeat=3), repeat=2)

# now apply the comparison to the images for each pair of colors
col_dict = { (c1,c2): compare_colors(ac, bc, c1, c2) for c1,c2 in col_combos }

Then, the keys for col_dict are actually tuples of tuples, which are much easier to deal with than strings, in my opinion. Here's how you'd access an example key:

>>>col_dict[((0, 255, 255), (255, 0, 255))]
8

Post a Comment for "How Do I Avoid Looping Through An Image Pixel By Pixel With Opencv & Numpy"