Color detection using OpenCV and Python

Color detection is an important task in computer vision where we identify a specific or multiple colors in an image or video. It has various applications in fields such as robotics, object tracking, image processing, etc. In this post, we will learn how to detect colors using OpenCV and Python.

If you are new to image analysis and computer vision, I suggest you take this course from Coursera to strengthen your foundational knowledge: Computer Vision – Image Basics with OpenCV and Python.

Understand Color Spaces

Before we start writing our code, let’s understand the concept about different types of color spaces. A color space is nothing but how we represent a set of colors (or an image) mathematically.

In computer vision or image analysis most commonly used color spaces are RGB, HSV, and YUV.

  • RGB: represents colors using red, green, and blue values (RGB). Note that OpenCV uses BGR color code instead of RBG.
  • HSV: This type of color space separates the color information from the brightness information of an image. It represents colors using hue, saturation, and value.
    • Hue represents the color of a pixel, such as red, green, blue. We can measure this in degrees with range of 0 to 360. 0 represents red, 120 represents green, and 240 represents blue.
    • Saturation represents how pure the color is. A highly saturated color is pure, while a less saturated color is mixed with white. Saturation is measured in percentages with range of 0% to 100%. 0% means white and 100% means purest color.
    • Value represents the brightness of the pixel. A high value represents a bright pixel, while a low value represents a dark pixel. We can also measure in percentage. 0% represents black and 100% represents white.
  • YUV: We can use this color space to separate color information into luminance (Y) and chrominance (UV). It is slightly more complex than HSV.

To do color detection using OpenCV, we are going to use HSV color space in this tutorial.

Course for You: Computer Vision – Image Basics with OpenCV and Python

Single color detection

Now let’s do a simple color detection project. I will break the entire project into some steps. So let’s get started.

Step1: Import Libraries

We need only two libraries to recognize color from any image. Let’s import those.

import cv2
import numpy as np

Step2: Load image

For this tutorial, I am going to use a photo of different color bottle caps as my input image. So let’s load this image in OpenCV.

# Read image in OpenCV
input_img = cv2.imread("bottle_cap.jpg")
img = cv2.resize(input_img, (640, 480))
# Make a copy to draw contour outline
input_image_cpy = img.copy()

# Display input image
cv2.imshow('image', img)
cv2.waitKey(0)
input-image-for-color-recognition

Step3: Convert image to HSV color space

It is difficult to use RGB (BRG in OpenCV) color space for color detection due to variations in lighting conditions and complex relationships between the three channels (R, G, and B).

For example, let’s say we want to find a red apple in an image that has a green background. It can be hard to find the apple in the picture because the green background can look similar (mathematically) to the apple’s red color.

This happens because, in RGB color system, the red color of the apple is made up of a lot of “red” and very little “green” and “blue,”. Similarly, the green background might also have a similar combination of colors (R, G, and B).

Also Read:  Practical Image Registration and Alignment with OpenCV and Python

This is the reason, we need to convert RGB color space to HSV. Because in the HSV color space, we only need to look at the Hue channel to detect the red color of the apple. We can set a specific range of values for the Hue channel to detect the red color of the apple.

Following one line of code is to convert BGR to HSV color space in OpenCV and Python.

# Convert RGB/ BGR to HSV color space
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

Step4: Define a color range to detect

Now we need to define the color range to filter any specific color from the image. Let’s say we only want to detect the red bottle cap in our image. In that case, we need to define the color range for red color.

# define range of red color in HSV
lower_red = np.array([0, 50, 50])
upper_red = np.array([10, 255, 255])

The upper_red represents the maximum amount of red that we want to detect and the lower_red represents the minimum amount of red. Any pixels in the image that have a red value within this range will be considered as “red” color.

Step5: Filtering part of image based on the color range

So we defined the red color range. Now just we need to filter our image (all pixels) based on color range. This mathematical filtering can be done using one line of code in OpenCV.

# create a mask for red color
mask_red = cv2.inRange(hsv, lower_red, upper_red)

# Display filtered image
cv2.imshow('mask_red', mask_red)
cv2.waitKey(0)
filtered-image-after-color-detection

If you compare it with the original input image, you can see that it is correctly recognizing our red bottle cap in the white region.

Step6: Contour Detection

So we have successfully identified our red bottle cap using color detection techniques using OpenCV and Python.

Now let’s go one step further. Let’s try to draw bounding box around the detected red object. To do that we need to know the coordinate information of the detected bounding box.

To get the coordinate information of our detected region, we need to perform contour detection. Let’s do that in the below Python code.

# find contours in the red mask
contours_red, _ = cv2.findContours(mask_red, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Draw detected contour in input image
contour_red_cap = cv2.drawContours(input_image_cpy, contours_red, -1, (255, 0, 255), 3)

# Dispay contour
cv2.imshow('contour_red_cap', contour_red_cap)
cv2.waitKey(0)
detected-contour-for-red-color

You can see it is correctly drawing the line for detected contour. This is because our color detection works accurately.

Note: If you want to understand how contour detection works, please read below detailed article about contour detection:

Step7: Draw bounding box

Since we have contour information of our detected region (red bottle cap), we can easily draw a bounding box surounding it based on coordinate information. Let’s do that using following Python code.

# loop through the red contours and draw a rectangle around them
for cnt in contours_red:
    contour_area = cv2.contourArea(cnt)
    if contour_area > 1000:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
        cv2.putText(img, 'Red', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)

# Display final output for color detection
cv2.imshow('image', img)
cv2.waitKey(0)
final-output-for-single-color-detection-using-opencv-and-python

Here at line 4, we are rejecting small contours based on area size (greater than 1000).

Also Read:  Clip Raster with a Shape file in Python

Multiple color detection

In the above section we only detected red color. Now if we want to detect multiple colors, we just need to mention color ranges for those colors.

In our input image, we just need to extract red, green, and blue colors. So we need to define color ranges for those colors. The code will be same. Below Python code is to do multiple color detection using OpenCV.

import cv2
import numpy as np

input_img = cv2.imread("bottle_cap.jpg")
img = cv2.resize(input_img, (640, 480))
# Make a copy to draw contour outline
input_image_cpy = img.copy()

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# define range of red color in HSV
lower_red = np.array([0, 50, 50])
upper_red = np.array([10, 255, 255])

# define range of green color in HSV
lower_green = np.array([40, 20, 50])
upper_green = np.array([90, 255, 255])

# define range of blue color in HSV
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([130, 255, 255])

# create a mask for red color
mask_red = cv2.inRange(hsv, lower_red, upper_red)

# create a mask for green color
mask_green = cv2.inRange(hsv, lower_green, upper_green)

# create a mask for blue color
mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)

# find contours in the red mask
contours_red, _ = cv2.findContours(mask_red, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# find contours in the green mask
contours_green, _ = cv2.findContours(mask_green, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# find contours in the blue mask
contours_blue, _ = cv2.findContours(mask_blue, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# loop through the red contours and draw a rectangle around them
for cnt in contours_red:
    contour_area = cv2.contourArea(cnt)
    if contour_area > 1000:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
        cv2.putText(img, 'Red', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
    
# loop through the green contours and draw a rectangle around them
for cnt in contours_green:
    contour_area = cv2.contourArea(cnt)
    if contour_area > 1000:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.putText(img, 'Green', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)

# loop through the blue contours and draw a rectangle around them
for cnt in contours_blue:
    contour_area = cv2.contourArea(cnt)
    if contour_area > 1000:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
        cv2.putText(img, 'Blue', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)

# Display final output for multiple color detection opencv python
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
multiple-color-detection-opencv-python

In the above code, we are not drawing contour. Just displaying the final output with bounding boxes. You can see the accuracy is pretty good.

Real-Time Color Detection

Since we have our code ready for image. You know we can easily implement that code for video to detect color real time. To do that we just need to read the video frame by frame. Let’s do that using below Python code.

Note: I am reading a video but you can also use your webcam to do this.

import cv2
import numpy as np

# Below function will read video imgs
cap = cv2.VideoCapture('bottle_cap_v1.mp4')

while True:
    read_ok, img = cap.read()
    img_bcp = img.copy()
 
    img = cv2.resize(img, (640, 480))
    # Make a copy to draw contour outline
    input_image_cpy = img.copy()

    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

    # define range of red color in HSV
    lower_red = np.array([0, 50, 50])
    upper_red = np.array([10, 255, 255])
    
    # define range of green color in HSV
    lower_green = np.array([40, 20, 50])
    upper_green = np.array([90, 255, 255])
    
    # define range of blue color in HSV
    lower_blue = np.array([100, 50, 50])
    upper_blue = np.array([130, 255, 255])

    # create a mask for red color
    mask_red = cv2.inRange(hsv, lower_red, upper_red)
    # create a mask for green color
    mask_green = cv2.inRange(hsv, lower_green, upper_green)
    # create a mask for blue color
    mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)

    # find contours in the red mask
    contours_red, _ = cv2.findContours(mask_red, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    # find contours in the green mask
    contours_green, _ = cv2.findContours(mask_green, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    # find contours in the blue mask
    contours_blue, _ = cv2.findContours(mask_blue, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # loop through the red contours and draw a rectangle around them
    for cnt in contours_red:
        contour_area = cv2.contourArea(cnt)
        if contour_area > 1000:
            x, y, w, h = cv2.boundingRect(cnt)
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
            cv2.putText(img, 'Red', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)

    # loop through the green contours and draw a rectangle around them
    for cnt in contours_green:
        contour_area = cv2.contourArea(cnt)
        if contour_area > 1000:
            x, y, w, h = cv2.boundingRect(cnt)
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
            cv2.putText(img, 'Green', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)

    # loop through the blue contours and draw a rectangle around them
    for cnt in contours_blue:
        contour_area = cv2.contourArea(cnt)
        if contour_area > 1000:
            x, y, w, h = cv2.boundingRect(cnt)
            cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
            cv2.putText(img, 'Blue', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)
 
    cv2.imshow('Color Recognition Output', img)
    
    # Close video window by pressing 'x'
    if cv2.waitKey(1) & 0xFF == ord('x'):
        break

Conclusion

In this tutorial, I gave you a starting point for color detection using OpenCV and Python. You can implement this in your color detection or recognition project.

Also Read:  Learn CNN from scratch with Python and Numpy

If background color is complex or near to the object color for which you are trying to extract color, then you first need to detect object after that crop that object. Then you can implement color detection to that cropped image.

To detect object you can use pre-trained model or even you can train your custom object detection model.

This is it for this tutorial. If you have any questions or suggestions regarding this post, please let me know in the comment section below.

Similar Read:

2 thoughts on “Color detection using OpenCV and Python”

Leave a comment