Nik
Nik

Reputation: 1221

Numpy find all pixels of a specific color that are next to pixels of another specific color

I want to find all pixels in an image that are grey and that are left of a black pixel.

to find all pixels that have the shades of grey that I'm looking for, I can do:

np.where(np.all(np.abs(img - (80, 71, 71)) < 10, axis=-1))

Is there a way of combining this statement with the condition that the pixel next to the grey pixel must be black? I.e. only return grey pixels that are immediate to the left of a black pixel?

I could do a for loop, loop over all the grey pixels I found in the first step and then check if the pixel next to them is black, but this seems quite inefficient. Is there a more efficient way of doing this in numpy?

Upvotes: 0

Views: 362

Answers (1)

Rotem
Rotem

Reputation: 32084

For finding gray pixels that are immediate to the left of a black pixel, we may apply the following stages:

  • Create a mask with True where pixel is gray and False otherwise.
    Same as your above code, but without np.where:
    gray_mask = np.all(np.abs(img - (80, 71, 71)) < 10, axis=-1)
  • Create a mask with True where pixel is black and False otherwise.
    black_mask = np.all(img == 0, axis=-1)
  • Shift the black mask one column to the left.
    This stage is used for placing the black pixel in the position of the gray pixels we want to find.
    We add a column of False values at the right hand size of the mask (for "correcting" the mask size, after cropping).
    shifted_black_mask = np.pad(black_mask[:, 1:], ((0, 0), (0, 1)), 'constant')
  • Apply and operation between the gray mask and the shifted black mask.
    gray_next_to_black = gray_mask & shifted_black_mask
    # gray_next_to_black_idx = np.where(gray_next_to_black) # Get the indices if required

Code sample:

import cv2
import numpy as np

# Sample image for testing:
img = np.array([[[0, 100, 0], [0, 100, 0], [0, 100, 0], [0, 0, 0]],
                [[0, 100, 0], [80, 80, 80], [80, 80, 80], [0, 0, 0]],
                [[0, 100, 0], [80, 80, 80], [80, 80, 80], [0, 100, 0]],
                [[0, 100, 0], [80, 80, 80], [0, 0, 0], [0, 100, 0]]], np.uint8)

gray_mask = np.all(np.abs(img - (80, 71, 71)) < 10, axis=-1)  # Create a mask with True where pixel is gray and False otherwise.
black_mask = np.all(img == 0, axis=-1)  # Create a mask with True where pixel is black and False otherwise.

# Shift the black mask one column to the left
shifted_black_mask = np.pad(black_mask[:, 1:], ((0, 0), (0, 1)), 'constant')  # Crop from the second column to the end, and add column of zeros at the right hand side.

# Apply and operation between the gray mask and the shifted black mask
# The result is True where gray pixel has a black pixel from it's right
gray_next_to_black = gray_mask & shifted_black_mask

# Get the indices if required:
# gray_next_to_black_idx = np.where(gray_next_to_black)

# Show image and masks for testing:
cv2.imshow("img", cv2.resize(img, (256, 256), interpolation=cv2.INTER_NEAREST))
cv2.imshow("gray_mask", cv2.resize(gray_mask.astype(np.uint8)*255, (256, 256), interpolation=cv2.INTER_NEAREST))
cv2.imshow("black_mask", cv2.resize(black_mask.astype(np.uint8)*255, (256, 256), interpolation=cv2.INTER_NEAREST))
cv2.imshow("shifted_black_mask", cv2.resize(shifted_black_mask.astype(np.uint8)*255, (256, 256), interpolation=cv2.INTER_NEAREST))
cv2.imshow("gray_next_to_black", cv2.resize(gray_next_to_black.astype(np.uint8)*255, (256, 256), interpolation=cv2.INTER_NEAREST))
cv2.waitKey()
cv2.destroyAllWindows()

img:
enter image description here

gray_mask:
enter image description here

black_mask:
enter image description here

shifted_black_mask:
enter image description here

gray_next_to_black:
enter image description here

Upvotes: 1

Related Questions