Search code examples
pythonopencvrgbmask

Create conditional mask for image based on Value channel of HSV image


I have an image on which I want to lay a mask based on the values of the value-channel of the xy-pixel of the image.

def rgb_mask(img):
   r, g, b = img[:,:,2], img[:,:,1], img[:,:,0]
   intensity = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)[:,:,2]

   mask_if_intensity_below_50 = (np.logical_and((0.85 * g > 0.95 * b), (0.95 * g > 0.85 * r)) * 255).astype(np.uint8)
   mask_if_intensity_between_50_and_200 = (np.logical_and((0.85 * g > 0.95 * b), (0.95 * g > 0.85 * r), (g - r + b > 30)) * 255).astype(np.uint8)
   mask_if_intensity_above_200 = (np.logical_and((0.85 * g > 0.95 * b), (0.95 * g > 0.85 * r), (g - b < 150)) * 255).astype(np.uint8)


   masked = cv2.bitwise_and(img, img, mask=?) # I typed ? because I am note sure how to include this in the code

   return masked

img.shape returns the following:

(720, 1280, 3)

How do I assign the correct mask to each pixel? Ideally, I don't want to use a for x, for y-loop. Thanks in advance


Solution

  • You can duplicate the mask to be 3D matrix before using bitwise_and.

    You can duplicate the mask using numpy dstak function.
    For example: np.dstack((msk, msk, msk)) gets msk in shape (720, 1280) and return shape (720, 1280, 3)

    In the following sample I assumed intensity matrix is in range [0, 255] (not [0, 1]):

    mask_if_intensity_between_100_and_200 = (np.logical_and(intensity > 100, intensity < 200) * 255).astype(np.uint8)
    masked = cv2.bitwise_and(img, np.dstack((mask_if_intensity_between_100_and_200, mask_if_intensity_between_100_and_200, mask_if_intensity_between_100_and_200)))
    

    When the mask shape and image shape are the same, you can apply bitwise and.

    Sample masked image:
    enter image description here