With the help of crackanddie, I was able to improve my solution.

If anybody is looking to count pixel like me very fast, here is my new code :

    def countColors(self, image): 
    start_time = time.time()
    colors_Count = {}

    for i in range(len(self.colors)):
        tmp = cv.inRange(image, self.colors[i], self.colors[i])
        count = cv.countNonZero(tmp)
        colors_Count[str(self.colors[i])] = count
        
    print(time.time() - start_time, "seconds")
    return colors_Count

Thank you again crackanddie, I was so stuck ahah

Answer from Marie on Stack Overflow
🌐
The Coding Couple
thecodingcouple.com › home › counting pixels by color in python with pillow (a pil fork)
Counting Pixels by Color in Python with Pillow (a PIL fork) - The Coding Couple
December 8, 2019 - Thus pixel-color-count.py was born! Given a valid image file, the Python script will iterate through each pixel in an image keeping a running tally of how many times the color of the pixel has appeared in the image.
Top answer
1 of 2
1

With the help of crackanddie, I was able to improve my solution.

If anybody is looking to count pixel like me very fast, here is my new code :

    def countColors(self, image): 
    start_time = time.time()
    colors_Count = {}

    for i in range(len(self.colors)):
        tmp = cv.inRange(image, self.colors[i], self.colors[i])
        count = cv.countNonZero(tmp)
        colors_Count[str(self.colors[i])] = count
        
    print(time.time() - start_time, "seconds")
    return colors_Count

Thank you again crackanddie, I was so stuck ahah

2 of 2
0

PIL package has a function that counts all colors really fast. if you care only to count specific colors, just filter the response. As for run time, on small images both function (PIL and yours) are almost the same but on big images (I checked 1000,1000,3) PIL is a bit faster. The big advantage of using PIL is that there's no need to specify colors.

def count_colors_2(cv_img: np.array) -> list:  # no need to give colors
    from PIL import Image
    start_time = time.time()
    pil_image = Image.fromarray(cv_img)
    colors_count_list = pil_image.getcolors()
    print('count_colors_2 time elapsed: {:.10f}s'.format(time.time() - start_time))
    for count, c_bgr in colors_count_list:
        print('\tcolor {} appeared {} times'.format(c_bgr, count))
    return colors_count_list

time and output check (yours is count_colors_1 and PIL is 2)

image dims (1000, 1000, 3)
count_colors_1 time elapsed: 0.0039894581s
    color [0 0 0] appeared 500000 times
    color [255 255 255] appeared 250000 times
    color [  0   0 255] appeared 250000 times
count_colors_2 time elapsed: 0.0029892921s
    color (255, 255, 255) appeared 250000 times
    color (0, 0, 0) appeared 500000 times
    color (0, 0, 255) appeared 250000 times
🌐
Codecademy
codecademy.com › docs › python:pillow › image module › .getcolors()
Python:Pillow | Image Module | .getcolors() | Codecademy
April 30, 2025 - In the Pillow library, the .getcolors() method retrieves a list of colors present in an image, along with the count of how many times each color appears. This method is useful for tasks like analyzing image content, generating color palettes, ...
🌐
GitHub
github.com › townsean › pixel-color-count
GitHub - townsean/pixel-color-count: A Python script that returns the sum of pixels per a unique color. · GitHub
python pixel-color-count.py supermario3-8-bit-boo.png -i (0,255,255) -t "8-Bit Boo Pixel Count:" In addtion to displaying the color with the number of pixels, pixel-color-count.py will generate an image feature a legend of the color, color name ...
Starred by 9 users
Forked by 2 users
Languages: Python
🌐
Educative
educative.io › answers › how-to-get-the-list-of-colors-in-an-image-in-python
How to get the list of colors in an image in Python
The Image module offers a variety of factory operations, including tools for generating new pictures and loading images from files. The getcolors() function is used to get a list of colors present in the image.
Top answer
1 of 4
2

Here is an approach using a KDTree for efficient nearest color lookup. Note that while KDTrees may be pretty advanced, using them is actually quite simple.

import numpy as np
from matplotlib import colors
from scipy.spatial import cKDTree as KDTree
from scipy.misc import face

REDUCED_COLOR_SPACE = True

# borrow a list of named colors from matplotlib
if REDUCED_COLOR_SPACE:
    use_colors = {k: colors.cnames[k] for k in ['red', 'green', 'blue', 'black', 'yellow', 'purple']}
else:
    use_colors = colors.cnames

# translate hexstring to RGB tuple
named_colors = {k: tuple(map(int, (v[1:3], v[3:5], v[5:7]), 3*(16,)))
                for k, v in use_colors.items()}
ncol = len(named_colors)

if REDUCED_COLOR_SPACE:
    ncol -= 1
    no_match = named_colors.pop('purple')
else:
    no_match = named_colors['purple']

# make an array containing the RGB values 
color_tuples = list(named_colors.values())
color_tuples.append(no_match)
color_tuples = np.array(color_tuples)

color_names = list(named_colors)
color_names.append('no match')

# get example picture
img = face()

# build tree
tree = KDTree(color_tuples[:-1])
# tolerance for color match `inf` means use best match no matter how
# bad it may be
tolerance = np.inf
# find closest color in tree for each pixel in picture
dist, idx = tree.query(img, distance_upper_bound=tolerance)
# count and reattach names
counts = dict(zip(color_names, np.bincount(idx.ravel(), None, ncol+1)))

print(counts)

import pylab

pylab.imshow(img)
pylab.savefig('orig.png')
pylab.clf()
pylab.imshow(color_tuples[idx])
pylab.savefig('minimal.png' if REDUCED_COLOR_SPACE else 'reduced.png')

Output with full matplotlib named color space:

{'aliceblue': 315, 'antiquewhite': 0, 'aqua': 0, 'aquamarine': 0, 'azure': 0, 'beige': 27, 'bisque': 0, 'black': 88584, 'blanchedalmond': 0, 'blue': 0, 'blueviolet': 0, 'brown': 0, 'burlywood': 76, 'cadetblue': 0, 'chartreuse': 0, 'chocolate': 0, 'coral': 0, 'cornflowerblue': 0, 'cornsilk': 0, 'crimson': 0, 'cyan': 0, 'darkblue': 0, 'darkcyan': 0, 'darkgoldenrod': 0, 'darkgray': 0, 'darkgreen': 4148, 'darkgrey': 71985, 'darkkhaki': 32907, 'darkmagenta': 0, 'darkolivegreen': 90899, 'darkorange': 0, 'darkorchid': 0, 'darkred': 0, 'darksalmon': 0, 'darkseagreen': 30171, 'darkslateblue': 134, 'darkslategray': 108608, 'darkslategrey': 0, 'darkturquoise': 0, 'darkviolet': 0, 'deeppink': 0, 'deepskyblue': 0, 'dimgray': 0, 'dimgrey': 108318, 'dodgerblue': 0, 'firebrick': 0, 'floralwhite': 0, 'forestgreen': 1, 'fuchsia': 0, 'gainsboro': 10438, 'ghostwhite': 736, 'gold': 0, 'goldenrod': 0, 'gray': 0, 'green': 0, 'greenyellow': 0, 'grey': 79835, 'honeydew': 0, 'hotpink': 0, 'indianred': 0, 'indigo': 0, 'ivory': 0, 'khaki': 1056, 'lavender': 4650, 'lavenderblush': 46, 'lawngreen': 0, 'lemonchiffon': 0, 'lightblue': 3, 'lightcoral': 0, 'lightcyan': 0, 'lightgoldenrodyellow': 0, 'lightgray': 11905, 'lightgreen': 2323, 'lightgrey': 0, 'lightpink': 0, 'lightsalmon': 0, 'lightseagreen': 0, 'lightskyblue': 0, 'lightslategray': 0, 'lightslategrey': 31920, 'lightsteelblue': 3590, 'lightyellow': 0, 'lime': 0, 'limegreen': 0, 'linen': 46, 'magenta': 0, 'maroon': 0, 'mediumaquamarine': 0, 'mediumblue': 0, 'mediumorchid': 0, 'mediumpurple': 15, 'mediumseagreen': 0, 'mediumslateblue': 0, 'mediumspringgreen': 0, 'mediumturquoise': 0, 'mediumvioletred': 0, 'midnightblue': 54, 'mintcream': 0, 'mistyrose': 19, 'moccasin': 0, 'navajowhite': 0, 'navy': 0, 'oldlace': 0, 'olive': 0, 'olivedrab': 30828, 'orange': 0, 'orangered': 0, 'orchid': 0, 'palegoldenrod': 1499, 'palegreen': 285, 'paleturquoise': 0, 'palevioletred': 0, 'papayawhip': 0, 'peachpuff': 0, 'peru': 21, 'pink': 0, 'plum': 0, 'powderblue': 0, 'purple': 0, 'rebeccapurple': 0, 'red': 0, 'rosybrown': 2831, 'royalblue': 0, 'saddlebrown': 0, 'salmon': 0, 'sandybrown': 0, 'seagreen': 0, 'seashell': 0, 'sienna': 5, 'silver': 35951, 'skyblue': 0, 'slateblue': 0, 'slategray': 7836, 'slategrey': 0, 'snow': 18, 'springgreen': 0, 'steelblue': 0, 'tan': 3925, 'teal': 0, 'thistle': 10274, 'tomato': 0, 'turquoise': 0, 'violet': 0, 'wheat': 21, 'white': 3, 'whitesmoke': 834, 'yellow': 0, 'yellowgreen': 9292, 'no match': 0}

Output with only basic colors:

{'red': 0, 'green': 403561, 'blue': 3262, 'black': 153782, 'yellow': 225827, 'no match': 0}

Original picture:

Reduced color version:

Basic color versioin:

2 of 4
1

Paul's answer is more elegant, but basically I think you can solve it by defining a function that gives you a "distance" between any two RGB colours something like this:

def distance(col1, col2):
    (r1,g1,b1) = col1
    (r2,g2,b2) = col2
    return (r1 - r2)**2 + (g1 - g2) ** 2 + (b1 - b2) **2

Now, all you need to do (in pseudo-code) is:

load your pre-existing reference colours into a list
load your new image
for each pixel in new image
    # Calculate distance from this pixel to first one in reference list
    mindist=distance(this pixel, first pixel in reference colours)
    nearest=first pixel in reference colours
    # Now see if any other colour in reference list is closer
    for index=1 to number of colours in reference list
        d=distance(this pixel, reference list[index])
        if d < mindist:
           mindist=d
           nearest=reference list[index]
    end
    replace pixel with nearest one from reference list as determined above
end

I am still learning Python, so my translation of the above may not be optimal but it works!

#!/usr/local/bin/python3
from PIL import Image
import numpy as np

# Calculate distance in RGB space between two RGB pixels
def distance(col1, col2):
    r1,g1,b1 = col1
    r2,g2,b2 = col2
    return (r1 - r2)**2 + (g1 - g2) ** 2 + (b1 - b2) **2

# All colours in the image will be forced to nearest one in this list
refColours=(
      [[255,   0,   0],    # red
       [  0, 255,   0],    # green
       [  0,   0, 255],    # blue
       [255, 255,   0],    # yellow
       [  0, 255, 255],    # cyan
       [255,   0, 255],    # magenta
       [  0,   0,   0],    # black
       [255, 255, 255]])   # white

# Load your new image and note its width and height
im = Image.open('colorwheel.png')
imrgb = im.convert("RGB")
(w,h)=im.size[0],im.size[1]

# Make a buffer for our output pixels
px=np.zeros(w*h,dtype=np.uint8)
idx=0

for pixel in list(imrgb.getdata()):
   # Calculate distance from this pixel to first one in reference list
   mindist = distance([pixel[0],pixel[1],pixel[2]],refColours[0])
   nearest = 0
   # Now see if any other colour in reference list is closer
   for index in range(1,len(refColours)):
       d=distance([pixel[0],pixel[1],pixel[2]],refColours[index])
       if d < mindist:
          mindist=d
          nearest=index
   # Set output pixel to nearest
   px[idx]=nearest
   idx += 1

# Reshape our output pixels to match input image
px=px.reshape(w,h)
# Make output image from our pixels
outimg=Image.fromarray(px).convert('P')
# Put our palette of favourite colours into the output image
palette=[item for sublist in refColours for item in sublist]
outimg.putpalette(palette)
outimg.save("result.png")

So, I start with this as colorwheel.png:

and end up with this:


Of course, the easier solution, like I suggested in the comments is to use a tool like ImageMagick to remap the colours in your new image to those in your "reference" image, which you do like this on the command-line:

convert colorwheel.png +dither -remap colormap.png result.png

as shown in my other answer here. So in Python, you could do that with the system() call or using the subprocess module:

cmd="https://stackoverflow.com/a/38328879/2836621"
system(cmd)
Top answer
1 of 2
2

One possible solution (I encoded the PNG image in Base64):

from PIL import Image
from io import BytesIO
import base64

data = '''iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+ACCAAXMEa+fpcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAx9JREFUeNrt3MtNA1EMQFGMpqWIgpAiakJKA6mBBrJOL2wfBYQVDrI9c85+8pnPlWfxXrxQbSWPD6eQv3p1CkAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAurKWPC+1nv/z/S315efLzT2ICQAQAEAAAAEABAAQAEAAAAEABAAEABAAQAAAAQAEABAAYH+sxc4r3Q8g6+vju/T7r6e7e9gEAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIADAo236H1jJ45+wmDz1EefLbfR+ApgAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAD4F5tTcGzX0736J4SrYAIABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAOBB+X4AK3l85D+C2bdAtdH7GZgAwCsAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIABAWx3WMq+yg12A2Rd/H9ev9BYwAYBXAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAgLaesRb50Ov5ofgBTH2ECQC8AgACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACALS1OQWMtop3lIgYffpMAOAVABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAoC37ATBbdj1+9X4CJgBAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAH5nPwCOLbufgAkAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAKBKNPgNq+xgqH+ASp9BEwB4BQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEAGgrdvAf7CdA5QMw+hkyAYBXAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAgLbCKbAlgGfABAAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAQDs/+5skuT18qwIAAAAASUVORK5CYII='''

img = Image.open(BytesIO(base64.b64decode(data)))

unique_colors = set()
for i in range(img.size[0]):
    for j in range(img.size[1]):
        pixel = img.getpixel((i, j))
        unique_colors.add(pixel)

print('Image info = ', img)
print('Unique color count = ', len(unique_colors))

Prints:

Image info =  <PIL.PngImagePlugin.PngImageFile image mode=RGBA size=256x256 at 0x7FFFF670A400>
Unique color count =  7
2 of 2
0

Is it possible to list the pixel RGBs and scan through that? As John Anderson said, you can use Image.getcolors to get that. But to get the number of unique variables, you can convert it to a set, which removes duplicates, then get the length of that, with len(set(list)).

Find elsewhere
🌐
Lindevs
lindevs.com › code-snippets › count-number-of-unique-colors-in-image-using-python
Count Number of Unique Colors in Image using Python | Lindevs
November 2, 2022 - from PIL import Image imgPath = 'test.jpg' img = Image.open(imgPath) uniqueColors = set() w, h = img.size for x in range(w): for y in range(h): pixel = img.getpixel((x, y)) uniqueColors.add(pixel) totalUniqueColors = len(uniqueColors) print(totalUniqueColors)
🌐
Andrewshay
andrewshay.me › blog › find-most-common-color-in-image-with-python
Find Most Common Color in an Image with Python - andrewshay.me
April 25, 2019 - An image is made up of individual pixels. Each pixel is a color. To find the most common color we can simply loop through every pixel, get the color, and keep a count of how many times the color appears.
🌐
GeeksforGeeks
geeksforgeeks.org › find-most-used-colors-in-image-using-python
Find most used colors in image using Python | GeeksforGeeks
October 16, 2021 - Not only supported by any system, such as Windows, Linux, Mac, etc. but also it can be run in any programming language like Python, C++, Java, etc. OpenCV also allows you to identify color in images.
🌐
Towards Data Science
towardsdatascience.com › home › latest › finding most common colors in python
Finding Most Common Colors in Python | Towards Data Science
March 5, 2025 - We can just use numpy‘s unique function, with the parameter return_counts=True . Done, let’s run it to our images. ... This makes more sense than the first one right? The most common colors are in the black area. But we can go further. What if we take not just one most common color, but more than that?
🌐
Python Forum
python-forum.io › thread-41547.html
Count image's colors very fast
Dear community, I found a code at stackoverflow which shall get the image's color count very fast. https://stackoverflow.com/questions/71399313/count-pixel-color-in-an-image I'm trying to get the second function working: def count_colors_2(cv_img:...
Top answer
1 of 2
17

You need to use Numpy, or OpenCV, for fast image processing in Python. I made a 9-colour version of Paddington:

from PIL import Image
import numpy as np

# Open Paddington and make sure he is RGB - not palette
im = Image.open('paddington.png').convert('RGB')

# Make into Numpy array
na = np.array(im)

# Arrange all pixels into a tall column of 3 RGB values and find unique rows (colours)
colours, counts = np.unique(na.reshape(-1,3), axis=0, return_counts=1)

print(colours)
print(counts)

Results

[[ 14  48  84]
 [ 19  21  30]
 [ 33 108 163]
 [ 33 152 190]
 [ 72  58  58]
 [ 96 154 210]
 [180  89  64]
 [205 210 200]
 [208 151  99]]

[20389 40269 12820  1488 17185 25371 17050 16396  9032]

That means there are 20,389 pixels of RGB(14,48,84), and so on.

That takes 125ms on my Mac for a 400x400 image, which will give you 8 fps, so you better have at least 4 CPU cores and use all of them to get 25+ fps.


Update

I think you can actually go significantly faster than this. If you take the dot-product of each of the pixels with [1,256,65536], you will get a single 24-bit number for each pixel, rather than 3 8-bit numbers. It is then a lot faster to find the unique values. That looks like this:

# Open Paddington and make sure he is RGB - not palette
im = Image.open('paddington.png').convert('RGB')

# Make into Numpy array
na = np.array(im)

# Make a single 24-bit number for each pixel
f = np.dot(na.astype(np.uint32),[1,256,65536]) 

nColours = len(np.unique(f))     # prints 9

That takes 4ms rather than 125ms on my Mac :-)


Keywords: Python, Numpy, PIL/Pillow, image processing, count unique colours, count colors.

2 of 2
0

This function returns a pretty DataFrame with all the information you need and is lightning fast - 10 times faster than colours, counts = np.unique(...)

import numexpr  # Import the mighty mighty 'numexpr' library for efficient numerical expression evaluation - up to 10x faster than numpy
import numpy as np
import pandas as pd


def count_colors(img, sort=True):
    """
    Count the unique colors in an image and return a DataFrame with color counts.

    Parameters:
    img (numpy.ndarray): Input image as a NumPy array with shape (height, width, 3).
    sort (bool, optional): Whether to sort the resulting DataFrame by color count. Default is True.

    Returns:
    pandas.DataFrame: A DataFrame containing color information with columns 'a' ,'r', 'g', 'b', and 'c' (count).
    """
    r, g, b = (
        img[..., 2],
        img[..., 1],
        img[..., 0],
    )  # Extract the red, green, and blue color channels from the image.

    absnum = numexpr.evaluate(
        # Use 'numexpr' to efficiently calculate a unique numerical representation for each color.
        "(r << 16) + (g << 8) + b",
        global_dict={},
        local_dict={"r": r, "g": g, "b": b},
    )

    npunique = np.unique(
        absnum, return_counts=True
    )  # Find unique values and their counts in the 'absnum' array.

    return pd.merge(  # Merge DataFrames to combine color information and color counts.
        pd.DataFrame(
            {
                "r": r.ravel(),
                "g": g.ravel(),
                "b": b.ravel(),
                "a": absnum.ravel(),
            }
        )
        .drop_duplicates()  # Remove duplicate rows based on the unique numerical representation 'a'.
        .reset_index(drop=True)  # Reset the DataFrame index after dropping duplicates.
        .set_index("a"),  # Set the 'a' column as the index of the DataFrame.
        pd.DataFrame({"a": npunique[0], "c": npunique[1]}).set_index("a"),
        # Create another DataFrame with 'a' and 'c' columns.
        left_index=True,  # Merge DataFrames using the index of the left DataFrame ('a' column).
        right_index=True,  # Merge DataFrames using the index of the right DataFrame ('a' column).
        sort=sort,  # Sort the resulting DataFrame by color count if 'sort' is True.
    ).reset_index(
        drop=False
    )  # Reset the index of the merged DataFrame and return it.


# df
# Out[3]:
#           a    r    g    b      c
# 0    929876   14   48   84  20389
# 1   1250590   19   21   30  40269
# 2   2190499   33  108  163  12820
# 3   2201790   33  152  190   1488
# 4   4733498   72   58   58  17185
# 5   6331090   96  154  210  25371
# 6  11819328  180   89   64  17050
# 7  13488840  205  210  200  16396
# 8  13670243  208  151   99   9032
# %timeit colours, counts = np.unique(pic.reshape(-1,3), axis=0, return_counts=1)
# %timeit df = count_colors(pic)
# 127 ms ± 3.81 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 12.8 ms ± 130 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
🌐
YouTube
youtube.com › watch
Object Counting by Color | OpenCV - YouTube
Learn how to count selected color objects using OpenCV and Python.---RESOURCES---https://github.com/codegiovanni/Counting_by_color.gitOpenCVMouseCallback fun
Published: April 1, 2022
Top answer
1 of 5
10

You could do it with GIMP which is free, open source, and works on Mac, Windows, and Linux.

  1. Open an image in GIMP, using File > Open

  2. In the main menu click Windows > Dockable Dialogs > Histogram

  3. Choose the Select by Color tool Shift+O, and set the Threshold to 0, in the Tool Options, under the Toolbox

  1. Click on a colour to select it, and the number of pixels will show in the Histogram. Repeat for other colours

Note: If your image is very small, you might want to zoom in. Use + and - to zoom in/out. Holding down Space while you move the mouse allows panning around the image. Also note that the grey and white checkerboard in the above example is to show the transparent background. If your image has a solid background you won't see these.

2 of 5
3

Drawing heavily on Billy Kerr's answer using Gimp, this is how you can achieve the same in Affinity Photo:

First ensure you have the histogram panel active and visible using the View > Studio > Histogram menu option. (You might need to set the Advanced option for the panel, using the small burger menu top right, in order to view the statistics associated with the histogram.) Ensure that the "Marquee" option is selected, to limit the scope to selected pixels.

Then use the Select > Select Sampled Colour... option. A small panel will be shown:

Click in your image to choose the colour to be sampled. There's a Threshold slider to determine how closely each pixel must match to be selected. Adjust the slider to include/exclude the pixels you need. Then click Apply. The histogram and statistics will be updated to reflect the selection:

In my example you can see 136 pixels were selected. If you see a small warning triangle top right of the histogram (as in my first example), click it to force a full refresh of the data.

Repeat for the other colours. It's slightly tiresome because you will need to use the Select > Select Sampled Colour... menu option each time.