Search code examples
pythonopencvcontourimage-segmentation

how to get only contour area color range


I want to get contour area color range and do some condition for example this is input image:input image Below is the code to find contours:

import cv2

img = cv2.imread('D:/original.png', cv2.IMREAD_UNCHANGED)

#convert img to grey
img_grey = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#set a thresh
thresh = 100
#get threshold image
ret,thresh_img = cv2.threshold(img_grey, thresh, 255, cv2.THRESH_BINARY)
#find contours
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

Now I am trying to get each contour color and write condition for example:

if contours[0] in color range ((100,100,100),(200,200,200)) then drawContour

All the things I'm trying to do are: get each contour area and check if selected contour is in specific color range or not.


Solution

  • We may start with cv2.kmeans for performing color clustering - kind of what described in the following tutorial.

    The result is a list of labels.
    Each label (label 0, label 1,...) represents all the pixels that belongs to a specific color cluster.

    Example of applying K-Means for colors clustering:

    # Reshape the image into a 2D array with one row per pixel and three columns for the color channels.
    data = image.reshape((cols * rows, 3))
    
    # Perform K-Means clustering on the image.
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    _, labels, centroids = cv2.kmeans(data, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    

    Iterate the labels, and create a mask with 255 where labels == k:

    mask = np.zeros((rows*cols, 1), np.uint8)  # Create a zerod mask in the size of image.
    mask[labels == k] = 255  # Place 255 where labels == k (all labels equals 0 are going to be 255 then all labels equals 1...)
    mask = mask.reshape((rows, cols))  # Reshape the mask back to the size of the image.
    

    For each mask apply the following stages:

    • Find Contours.
    • For each contour, find contour area, and ignore relatively small contours.
    • Get the first coordinate in the contour: x, y = tuple(c[0][0]).
    • Get the color of the pixel in that coordinate: color = original_image[y, x].
    • Draw the contour with that color (mainly for testing): cv2.drawContours(colored_mask, [c], 0, color.tolist(), -1).

    Complete code sample:

    import cv2
    import numpy as np
    
    K = 16  # Number of color clusters (16 is a bit larger than the accrual number of colors).
    
    # Load the image and convert it float32 (kmeans requires float32 type).
    original_image = cv2.imread('original.png')
    image = original_image.astype(np.float32)
    
    cols, rows = image.shape[1], image.shape[0]
    
    # Reshape the image into a 2D array with one row per pixel and three columns for the color channels.
    data = image.reshape((cols * rows, 3))
    
    # Perform K-Means clustering on the image.
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    _, labels, centroids = cv2.kmeans(data, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    
    # Convert the labels back into an image (for testing).
    quantized_image = centroids[labels].reshape(image.shape).astype(np.uint8)
    
    # Save the quantized_image image (for testing).
    cv2.imwrite('quantized_image.png', quantized_image)
    
    for k in range(K):
        mask = np.zeros((rows*cols, 1), np.uint8)  # Create a zeroed mask in the size of image.
        mask[labels == k] = 255  # Place 255 where labels == k (all labels equals 0 are going to be 255 then all labels equals 1...)
        mask = mask.reshape((rows, cols))  # Reshape the mask back to the size of the image.
        #cv2.imshow(f'mask {k}', mask)  # Show mask for testing
    
        cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]  # Find contours
        for c in cnts:
            area_tresh = 500
            area = cv2.contourArea(c)
            if area > area_tresh:  # Ignore relatively small contours
                colored_mask = np.zeros_like(original_image)  # Initialize colored_mask with zeros
                x, y = tuple(c[0][0])  # First coordinate in the contour
                color = original_image[y, x]  # Get the color of the pixel in that coordinate
                cv2.drawContours(colored_mask, [c], 0, color.tolist(), -1)  # Draw contour with the specific color
                cv2.imshow(f'colored_mask {k}', colored_mask)  # Show colored_mask for testing
                cv2.imwrite(f'colored_mask_{k}.png', colored_mask)  # Save as PNG for testing
                continue  # Assume only the first large contour is relevant - continue to next iteration
    
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    Few output samples:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here