'Check if an image contains blue pixels

I have this image:

enter image description here

And I'm trying to write a function in Python that will return True if the image contains blue pixels, or False otherwise. That image is just an example. I will have others were the blue colour can be slightly different. But they will always be blue letters over a black background.

So far I have this:

def contains_blue(img):
    # Convert the image to HSV colour space
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    # Define a range for blue color
    hsv_l = np.array([100, 150, 0])
    hsv_h = np.array([140, 255, 255])
    # Find blue pixels in the image
    #
    # cv2.inRange will create a mask (binary array) where the 1 values
    # are blue pixels and 0 values are any other colour out of the blue
    # range defined by hsv_l and hsv_h
    return 1 in cv2.inRange(hsv, hsv_l, hsv_h)

The function always returns False because no 1 values are found in the array returned by cv2.inRange. Maybe the range defined by hsv_l and hsv_h is not good? I took it from here: OpenCV & Python -- Can't detect blue objects

Any help is appreciated. Thanks.



Solution 1:[1]

The problem is that you are not reading the documentation of inRange :D

Which tells the following:

That is, dst (I) is set to 255 (all 1 -bits) if src (I) is within the specified 1D, 2D, 3D, ... box and 0 otherwise.

and you check for 1

# cv2.inRange will create a mask (binary array) where the 1 values
# are blue pixels and 0 values are any other colour out of the blue
# range defined by hsv_l and hsv_h
return 1 in cv2.inRange(hsv, hsv_l, hsv_h)

So the solution is to change it to:

return 255 in cv2.inRange(hsv, hsv_l, hsv_h)

I tested it with your image and returns true, also with a black and white image (BGR though) and returns false.

In my opinion the blue ranges you have chosen are a little far to the violet side... You may use a hsv colorpicker like this one http://colorizer.org/ and select the ranges you will like. Just rememeber OpenCV uses H -> Hue / 2 and S and V are like percentages (0-100) and you just divide them by 100 (0-1.) and multiply them by 255.

Solution 2:[2]

You could have just used np.any() instead. It will return True if any one pixel has a value of 255.

So instead of

return 1 in cv2.inRange(hsv, hsv_l, hsv_h),

you can just add the following:

return np.any(cv2.inRange(hsv, hsv_l, hsv_h))

Update:

As @AKX mentioned in the comments you could rather try out the following:

return cv2.inRange(hsv, hsv_l, hsv_h).any()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 api55
Solution 2