skip to Main Content

I’m trying to split an image depending on the colors it contains.

My previous steps have been to simplify it to just 3 colors using the KMeans algorithm that Sklearn offers and I get a result like the following image.

enter image description here

Now I need to split it in 3 images one for each color. And obtain something similar to this (I have done it with photoshop).

The examples are done in black and white because really if I can do the division, I no longer need the colors. But I would do just as well with 3 color images.

Mask 1:

enter image description here

Mask 2:

enter image description here

Mask 3:

enter image description here

I found this question, but I can’t achieve my goal.

I have thought about separating by channels, but I think it is wrong.

# set green and red channels to 0
blue_img[:, :, 1] = 0
blue_img[:, :, 2] = 0
# set blue and red channels to 0
green_img[:, :, 0] = 0
green_img[:, :, 2] = 0
# set blue and green channels to 0
red_img[:, :, 0] = 0
red_img[:, :, 1] = 0

I think the key is in my kmeans algorithm, because with it I obtain labels and centroids of my colors but I really don’t know how to do it, and I can’t find anybody doing it.

My KMeans algorithm is:

def get_colors(img, number_of_colors, show_chart, show_segmented_img):
    
    modified_image = img.reshape(img.shape[0]*img.shape[1], 3)
    
    myKMeans = KMeans(n_clusters = number_of_colors)
    
    labels = myKMeans.fit_predict(modified_image)
    
    counts = Counter(labels)
    
    centroids = myKMeans.cluster_centers_
    
    ordered_colors = [centroids[i] for i in counts.keys()]
    
    hex_colors = [RGB2HEX(ordered_colors[i]) for i in counts.keys()]
    
    rgb_colors = [ordered_colors[i] for i in counts.keys()]
    
    if (show_chart):
        plt.figure(figsize = (8, 6))
        plt.pie(counts.values(), labels = hex_colors, colors = hex_colors)
        plt.show()

    if (show_segmented_img):
        centroids = np.uint8(centroids)
        segmented_data = centroids[labels.flatten()]
        segmented_image = segmented_data.reshape(img.shape)
        segmented_image = cv2.cvtColor(segmented_image, cv2.COLOR_RGB2BGR)
        cv2.imwrite('segmentedImg.png', segmented_image)

    return hex_colors, rgb_colors

can somebdoy help me please?

Thank you very much!

EDIT: From Hihikomori’s answer.

From Hihikomori’s answer, I understand that I should do the following, this is based on the question that I linked before, but the problem is that I get 3 black masks without any contour so I thought that this would not suit me.

def get_colors(img, number_of_colors, show_chart, show_segmented_img):
    
    modified_image = img.reshape(img.shape[0]*img.shape[1], 3)
    
    myKMeans = KMeans(n_clusters = number_of_colors)
    
    labels = myKMeans.fit_predict(modified_image)
    
    counts = Counter(labels)
    
    centroids = myKMeans.cluster_centers_
    
    ordered_colors = [centroids[i] for i in counts.keys()]
    
    hex_colors = [RGB2HEX(ordered_colors[i]) for i in counts.keys()]
    
    rgb_colors = [ordered_colors[i] for i in counts.keys()]

    # TRYING THE ASNWER
    color1, color2,color3 = rgb_colors
    first_color_indices = np.where(np.all(img == color1, axis=-1))
    second_color_indices = np.where(np.all(img == color2, axis=-1))
    third_color_indices = np.where(np.all(img == color3, axis=-1))

    img1 = np.zeros_like(img)
    img1[first_color_indices]=color1

    img2 = np.zeros_like(img)
    img2[second_color_indices]=color2

    img3 = np.zeros_like(img)
    img3[third_color_indices]=color3

    print('***')
    cv2_imshow(img1)
    print('***')
    cv2_imshow(img2)
    print('***')
    cv2_imshow(img3)
    print('***')
    
    if (show_chart):
        plt.figure(figsize = (8, 6))
        plt.pie(counts.values(), labels = hex_colors, colors = hex_colors)
        plt.show()

    if (show_segmented_img):
        centroids = np.uint8(centroids)
        segmented_data = centroids[labels.flatten()]
        segmented_image = segmented_data.reshape(img.shape)
        segmented_image = cv2.cvtColor(segmented_image, cv2.COLOR_RGB2BGR)
        cv2.imwrite('segmentedImg.png', segmented_image)

    return hex_colors, rgb_colors

2

Answers


  1. color1 = (0,0,160)
    color2 = (0,160,160)
    color3 = (160,160,160)
    img = np.zeros((640,480,3),np.uint8)
    
    img[100:200,100:200] = color1
    img[150:250,150:250] = color2
    img[200:300,200:300] = color3
    
    first_color_indices = np.where(np.all(img == color1,axis=-1))
    second_color_indices = np.where(np.all(img == color2,axis=-1))
    third_color_indices = np.where(np.all(img == color3,axis=-1))
    
    img1 = np.zeros_like(img)
    img1[first_color_indices]=color1
    
    img2 = np.zeros_like(img)
    img2[second_color_indices]=color2
    
    img3 = np.zeros_like(img)
    img3[third_color_indices]=color3
    
    cv2.imshow('origin', img)
    cv2.imshow('img1', img1)
    cv2.imshow('img2', img2)
    cv2.imshow('img3', img3)
    cv2.waitKey(0)
    
    Login or Signup to reply.
  2. You can find the unique colours in your image with np.unique() and then iterate over them setting each pixel to either white or black depending whether it is equal to that colour or not:

    #!/usr/bin/env python3
    
    import cv2
    import numpy as np
    
    # Load image
    im = cv2.imread('cheese.png')
    
    # Reshape into a tall column of pixels, each with 3 RGB pixels and get unique rows (colours)
    colours  = np.unique(im.reshape(-1,3), axis=0)
    
    # Iterate over the colours we found
    for i,colour in enumerate(colours):
        print(f'DEBUG: colour {i}: {colour}')
        res = np.where((im==colour).all(axis=-1),255,0)
        cv2.imwrite(f'colour-{i}.png', res)
    

    Sample Output

    DEBUG: colour 0: [0 0 0]
    DEBUG: colour 1: [0 141 196]
    DEBUG: colour 2: [1 102 133]
    

    enter image description here

    enter image description here

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search