Reputation: 111
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
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
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
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