How to find the most frequent pixel value in an image?

Question:

Editors comment:


  • How to count pixels occurences in an image?

I have a set of images where each pixel consists of 3 integers in the range 0-255.

I am interested in finding one pixel that is "representative" (as much as possible) for the entire pixel-population as a whole, and that pixel must occur in the pixel-population.
I am determining which pixel is the most common (the median mode) in my set of images makes the most sense.

I am using python, but I am not sure how to go about it.
The images are stored as an numpy array with dimensions [n, h, w, c], where n is the number of images, h is the height, w is the widthandc` is the channels (RGB).

Asked By: Toke Faurby

||

Answers:

I’m going to assume you need to find the most common element, which as Cris Luengo mentioned is called the mode. I’m also going to assume that the bit depth of the channels is 8-bit (value between 0 and 255, i.e. modulo 256).

Here is an implementation independent approach:

The aim is to maintain a count of all the different kinds of pixels encountered. It makes sense to use a dictionary for this, which would be of the form {pixel_value : count}.

Once this dictionary is populated, we can find the pixel with the highest count.

Now, ‘pixels’ are not hashable and hence cannot be stored in a dictionary directly. We need a way to assign an integer(which I’ll be referring to as the pixel_value) to each unique pixel, i.e., you should be able to convert pixel_value <–> RGB value of a pixel

This function converts RGB values to an integer in the range of 0 to 16,777,215:

def get_pixel_value(pixel):
    return pixel.red + 256*pixel.green + 256*256*pixel.blue 

and to convert pixel_value back into RGB values:

def get_rgb_values(pixel_value):
    red = pixel_value%256
    pixel_value //= 256
    green = pixel_value%256
    pixel_value //= 256
    blue = pixel_value
    return [red,green,blue]

This function can find the most frequent pixel in an image:

def find_most_common_pixel(image):
    histogram = {}  #Dictionary keeps count of different kinds of pixels in image

    for pixel in image:
        pixel_val = get_pixel_value(pixel)
        if pixel_val in histogram:
            histogram[pixel_val] += 1 #Increment count
        else:
            histogram[pixel_val] = 1 #pixel_val encountered for the first time

    mode_pixel_val = max(histogram, key = histogram.get) #Find pixel_val whose count is maximum
    return get_rgb_values(mode_pixel_val)  #Returna a list containing RGB Value of the median pixel

If you wish to find the most frequent pixel in a set of images, simply add another loop for image in image_set and populate the dictionary for all pixel_values in all images.

Answered By: Aayush Mahajan

You can iterate over the x/y of the image.
a pixel will be img_array[x, y, :] (the : for the RBG channel)
you will add this to a Counter (from collections)
Here is an example of the concept over an Image

from PIL import Image
import numpy as np
from collections import Counter

# img_path is the path to your image
cnt = Counter()
img = Image.open(img_path)
img_arr = np.array(img)

for x in range(img_arr.shape[0]):
    for y in range(img_arr.shape[1]):
        cnt[str(img_arr[x, y, :])] += 1

print(cnt)

# Counter({'[255 255 255]': 89916, '[143 143 143]': 1491, '[0 0 0]': 891, '[211 208 209]': 185, ...

A More efficient way to do it is by using the power of numpy and some math manipulation (because we know values are bound [0, 255]

img = Image.open(img_path)
img_arr = np.array(img)
pixels_arr = (img_arr[:, :, 0] + img_arr[:, :, 1]*256 + img_arr[:, :, 2]*(256**2)).flatten()
cnt = Counter(pixels_arr)

# print(cnt)
# Counter({16777215: 89916, 9408399: 1491, 0: 891, 13750483: 185, 14803425: 177, 5263440: 122 ...

# print(cnt.most_common(1))
# [(16777215, 89916)]
pixel_value = cnt.most_common(1)[0][0]

Now a conversion back to the original 3 values is exactly like Aayush Mahajan have writte in his answer. But I’ve shorten it for the sake of simplicity:

r, b, g = pixel_value%256, (pixel_value//256)%256, pixel_value//(256**2)

So you are using the power of numpy fast computation (and it’s significate improvement on run time.

You use Counter which is an extension of python dictionary, dedicated for counting.

Answered By: Eran Moshe