Search code examples
python-imaging-libraryopencvpython

fastest way to find the rgb pixel color count of image


I have a use case where i have to find the consecutive rgb pixel color count of each frame of live video after searching i found a piece of code which does the same thing but performance wise it take around ~ 3 sec to give me output but in my case i have to do this calculation as fast as possible may be 25 frames in 1 seconds. Can someone help me to figure out how to do this by refactoring the below code

from PIL import Image
import timeit

starttime = timeit.default_timer()
with Image.open("netflix.png") as image:
    color_count = {}
    width, height = image.size
    print(width,height)
    rgb_image = image.convert('RGB')
    for x in range(width):
        for y in range(height):
            rgb = rgb_image.getpixel((x, y))
            if rgb in color_count:
                color_count[rgb] += 1
            else:
                color_count[rgb] = 1

    print('Pixel Count per Unique Color:')
    print('-' * 30)
    print(len(color_count.items()))
print("The time difference is :", timeit.default_timer() - starttime)

output:

Pixel Count per Unique Color: 130869

The time difference is : 3.9660612


Solution

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

    enter image description here

    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.