Search code examples
pythoncolorsdetectionopencv

OpenCV - detecting color ranges and display on console


So I have the following code and works fine when displaying the colors on a image. I know it uses a mask over the original image and only shows the colors which are defined in the declared boundaries. So basically it does not "detect" color, rather covers everything else which is not in the ranges.

usage: python code.py

An example image you can find here

code:

import numpy as np
import cv2
import sys

image = cv2.imread(sys.argv[1])


colors = {
    "red_lower" : "[  0   0 255]",
    "red_upper" : "[  0   0 127]",
    "blue_lower" : "[255  38   0]",
    "blue_upper" : "[255  38   0]",
    "yellow_lower" : "[  0 216 255]",
    "yellow_upper" : "[  0 216 255]",
    "gray_lower" : "[160 160 160]",
    "gray_upper" : "[160 160 160]"
}

boundaries = [
    ([0, 0, 255], [127, 0, 255]), #red
    ([255, 38, 0], [255, 38, 0]), #blue
    ([0, 216, 255], [0, 216, 255]), #yellow
    ([160, 160, 160], [160, 160, 160]) #gray
]

# loop over the boundaries
for (lower, upper) in boundaries:

    # create NumPy arrays from the boundaries
    lower = np.array(lower, dtype = np.uint8)
    upper = np.array(upper, dtype = np.uint8)

    # find the colors within the specified boundaries and apply the mask
    mask = cv2.inRange(image, lower, upper)
    output = cv2.bitwise_and(image, image, mask = mask)

    # show the images
    cv2.imshow("Climbing Holds", np.hstack([image, output]))

    cv2.waitKey(0)

I am trying to catch the color on console through an If statement when matching one of the boundaries. If I compare directly with my colors dictionary wont work as expected, since all the boundaries go through the loop.

Example of If statement:

if str(lower) == colors["red_lower"]:
    print "red"
elif str(upper) == colors["red_upper"]:
    print "dark red"
elif str(lower) == colors["blue_lower"]:
    print "blue"
elif str(lower) == colors["yellow_lower"]:
    print "yellow"
elif str(lower) == colors["gray_lower"]:
    print "gray

I tried to debug by printing mask and output, but these return only zero tuples:

 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]]

Does anyone know how to return the mask matching? Is it possible using cv2.imshow or cv2.read?


Solution

  • You could combine colors and boundaries into one object to iterate over. Combining the data is a good idea anyhow, as the lower/upper bounds are duplicated currently, once in boundaries and once as strings in the colors values. That duplication is not great, as it can lead to subtle errors. Some of the values already seem out of sync, so it's less error-prone to have them just in a single place.

    color_boundaries = {
        "red":    ([0,   0,   255], [127, 0,   255]),
        "blue":   ([255, 38,  0],   [255, 38,  0]),
        "yellow": ([0,   216, 255], [0,   216, 255]),
        "gray":   ([160, 160, 160], [160, 160, 160])
    }
    
    for color_name, (lower, upper) in color_boundaries.items():
        # create NumPy arrays from the boundaries
        lower = np.array(lower, dtype = np.uint8)
        upper = np.array(upper, dtype = np.uint8)
    
        # find the colors within the specified boundaries and apply the mask
        mask = cv2.inRange(image, lower, upper)
        output = cv2.bitwise_and(image, image, mask = mask)
    
        if mask.any():
            print(f"{color_name}: {mask.sum()}")