Sushma Suresh Kalkunte
Sushma Suresh Kalkunte

Reputation: 111

Vectorized approach to masking red and blue channels in an image

I am trying to change all the red and blue pixels in an image to black to have only green pixels (based on certain conditions).

To do this right now I am using multiple for loops and the process, although it works, is extremely slow.

The code I have so far -

### Test image is my original RGB image
mat = np.asarray(testim)

for elemento in mat: 

    for pixel in element:

        if ((pixel[0] + pixel[2]) >= (2*pixel[1])): #Non-vegetation background changing to black according to the rule (R + B >= 2G)
          pixel[0] = 0
          pixel[1] = 0
          pixel[2] = 0
        
        elif pixel[1] > pixel[0] and pixel[1] > pixel[2] and pixel[1] > 25: # Treat these as green vegetation and do not change anything
          continue
        
        else: # Change background to black
          pixel[0] = 0
          pixel[1] = 0
          pixel[2] = 0
cv2_imshow(testim)
print(testim.shape)

Is there any way I can vectorize this using Numpy without using nested for loops to make the same process work faster? I am slightly new to NumPy operations and am confused as to how to get it done. I appreciate the help!

For example : My input image - [1]: https://i.sstatic.net/zWGtA.jpg

The output image I have now with the above logic - [2]: https://i.sstatic.net/VkmLC.jpg

I would like the same output with a faster code preferably using NumPy instead of the nested for loops that I currently have

Upvotes: -1

Views: 443

Answers (3)

JoostVn
JoostVn

Reputation: 252

I split the image array into three arrays (r, g, b) to make the rules more intuitive. It should be easy to add any new ones with this format!

r, g, b = mat[:,:,0], mat[:,:,1], mat[:,:,2]
rule_1 = (r + b > 2 * g)
rule_2 = ~((g > r) & (g > b) & (g > 25))
mat[rule_1 | rule_2] = 0

Upvotes: 2

Mustafa Aydın
Mustafa Aydın

Reputation: 18315

From your code, you're not changing the values when

pixel[1] > pixel[0] and pixel[1] > pixel[2] and pixel[1] > 25

So we can form a boolean array of this condition and index with its inverse into the array and set those places to 0:

# since used more than once, assign to a variable
green = mat[:, :, 1]

# your "elif" condition
not_change_condition = (green > mat[:, :, 0]) & (green > mat[:, :, 2]) & (green > 25)

# index with its inverse and change
mat[~not_change_condition] = 0

Upvotes: 1

ALai
ALai

Reputation: 799

You should be able to access your red matrix with mat[:,:,0], and similar with green and blue.

Your mask of green pixels is then:

mask = (mat[:,:,1]>mat[:,:,0]) & (mat[:,:,1]>mat[:,:,2]) & (mat[:,:,1]>25)

where you are doing element-wise comparisons and element-wise AND to combine comparisons in a boolean 2D matrix.

You can then zero each other pixel by doing:

# Repeat mask along the three channels
mask = np.repeat(mask[:,:,np.newaxis], 3, axis=2)

# Zero non-green elements
mat[~mask] = 0

Upvotes: 1

Related Questions