0

Here is a figure from which I want to count the number of objects from each color. What is a simple way of doing this, without using opencv perhaps?

[Edit 2]: The approach I have tried is the following: (1) Colored objects count

from PIL import Image
im = Image.open('./colored-polka-dots.png').getcolors()
im.sort(key=lambda k: (k[0]), reverse=True)
print('Top 5 colors: {}'.format((im[:5])))

# View non-background colors
color_values = []
for color in im[1:5]:
    color_values.append(color[1])
    arr = np.asarray(color[1]).reshape(1,1,4).astype(np.uint8)
    plt.imshow(arr)
    plt.show() # get top 4 frequent colors as green,blue,pink,ornage

# Create a dict of color names and their corressponding rgba values
color_dict = {}
for color_name,color_val in zip(['green','blue','pink','orange'],color_values):
    color_dict[color_name] = color_val

# Make use of ndimage.measurement.labels from scipy 
# to get the number of distinct connected features that satisfy a given threshold
for color_name,color_val in color_dict.items():
    b = ((img[:,:,0] ==color_val[0]) * (img[:,:,1] ==color_val[1]) * (img[:,:,2] ==color_val[2]))*1
    labeled_array, num_features = scipy.ndimage.measurements.label(b.astype('Int8'))
    print('Color:{} Count:{}'.format(color_name,num_features))

> Output:

orange: 288

green: 288

pink: 288

blue: 288

Although this achieves the purpose, I want to know if there is a more efficient and elegant way of solving this problem.

phoenixwing
  • 337
  • 1
  • 6
  • 14
  • 4
    Can you show us what you have tried so far? – Professor_Joykill Jul 11 '17 at 20:08
  • 1
    [This](https://stackoverflow.com/questions/5298884/finding-number-of-colored-shapes-from-picture-using-python) post tells how to find the count of coloured objects using just numpy/scipy. You can modify this for your purpose – Sriram Sitharaman Jul 11 '17 at 20:12
  • possible duplicate of [this](https://stackoverflow.com/questions/44439555/count-colored-dots-in-image/44443494#44443494). The solution [here](https://stackoverflow.com/a/44443494/6411572) might help – Shawn Mathew Jul 11 '17 at 20:27
  • @Professor_Joykill, I have added my solution. Could you suggest improvements? – phoenixwing Jul 11 '17 at 22:10

1 Answers1

6

Here's a simpler solution based on scikit-image:

Code:

import numpy as np
from skimage import io, morphology, measure
from sklearn.cluster import KMeans

img = io.imread('https://i.stack.imgur.com/du0XZ.png')

rows, cols, bands = img.shape
X = img.reshape(rows*cols, bands)

kmeans = KMeans(n_clusters=5, random_state=0).fit(X)
labels = kmeans.labels_.reshape(rows, cols)

for i in np.unique(labels):
    blobs = np.int_(morphology.binary_opening(labels == i))
    color = np.around(kmeans.cluster_centers_[i])
    count = len(np.unique(measure.label(blobs))) - 1
    print('Color: {}  >>  Objects: {}'.format(color, count))

Output:

Color: [ 254.  253.  253.  255.]  >>  Objects: 1
Color: [ 255.  144.   36.  255.]  >>  Objects: 288
Color: [  39.  215.  239.  255.]  >>  Objects: 288
Color: [ 255.   38.  135.  255.]  >>  Objects: 288
Color: [ 192.  231.   80.  255.]  >>  Objects: 288

Remarks:

  • I've clustered colors through KMeans to make the program robust to slight variations in pixel colors.

  • The RGB coordinates of the cluster centers have been rounded through around just for visualization purposes.

  • I've also performed an opening operation through binary_opening in order to get rid of isolated pixels.

  • It is necessary to subtract 1 from the number of labels yielded by label to take into account only those connected regions with the considered color label.

  • The first line of the output obviously corresponds to the white background.

Tonechas
  • 13,398
  • 16
  • 46
  • 80