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.

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

    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).

    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:

    Also Read:  Train YOLOv8 on Custom dataset in Windows GPU

    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).

    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.

    Also Read:  Warp perspective and Transform OpenCV python

    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.

    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.

    Leave a Comment

    Your email address will not be published. Required fields are marked *