Jolanthan
Jolanthan

Reputation: 81

Pixel-wise comparison of equally sized images to find the most common colour for every pixel

Suppose I have a picture converted into an array

data1= numpy.asarray(source1.png) 
data2= numpy.asarray(source2.png)
data3= numpy.asarray(source3.png)
data4= numpy.asarray(source4.png)
data5= numpy.asarray(source5.png)

As I understand if I were to print(data1) I would get an array that shows the RGB Value of each pixel at each given position.

Now I would like to compare all arrays data1, data2, data3, data 4, data5 and find the RGB value of each pixel that occurs the most often and output it as a new Array/ Picture

As an example: For position X1Y1 and X2Y1the the array would look like this

data1= [[0 0 255], [0 1 0]]
data2= [[0 0 255], [0 1 0]]
data3= [[0 0 255], [0 1 0]]
data4= [[0 0 254], [0 1 0]]
data5= [[0 0 254], [0 1 0]]

As [(0,0,255)] was the most common value for position X1Y1 and X2Y1, the new array would be saved as avg= [(0, 0, 255), (0, 1, 0)]

Is there a function that can do that? Did I understand arrays correctly?

Upvotes: 2

Views: 408

Answers (1)

mawall
mawall

Reputation: 175

You could convert the rgb values to a single base 16 integer and use np.unique to find duplicates like this:

def rgb_to_base16(rgb):
    return int('0x{0:02X}{1:02X}{2:02X}'.format(*rgb), 16)

def base16_to_rgb(base16):
    return np.array([base16 >> 16, base16 >> 8 & 0xFF, base16 & 0xFF])

def find_most_common(values):
    unique_values, counts = np.unique(values, return_counts=True)
    if len(unique_values) == len(values):
        return [255, 255, 255]
    else:
        return base16_to_rgb(unique_values[np.argmax(counts)])

stacked = np.stack((img_1, img_2, img_3, img_4), axis=2)

hexified = np.apply_along_axis(rgb_to_base16, 
                               axis=-1, 
                               arr=stacked).astype(np.int)

most_common = np.apply_along_axis(lambda values: find_most_common(values), 
                                  axis=-1, 
                                  arr=hexified).astype(np.uint8)

Original answer assuming you want to compare r,g and b values individually:

You can get the most commonly occuring value with np.bincount and np.argmax, which you can apply to the last axis of the stacked image array with np.apply_along_axis:

stacked = np.stack((img_1, img_2, img_3), axis=3)
most_common = np.apply_along_axis(lambda x: np.argmax(np.bincount(x)), axis=-1, arr=stacked).astype(np.uint8)

Note that this method will return the lowest value for each r,g and b if none of these appear more than once and that np.bincount will only work with non-negative integers.

If you want to return a custom value for each of r, g and b, if none of them repeat, you can define this behavior as a function instead of the lambda expression:

def find_most_common(values):
    most_common = np.argmax(np.insert(np.bincount(values), 0, 1))
    if most_common == 0:
        return 125
    else:
        return most_common - 1

most_common = np.apply_along_axis(lambda values: find_most_common(values), axis=-1, arr=stacked).astype(np.uint8)

Here we prepend a one to the bin-counts, so that argmax will return 0 if none of the other values occur more than once.

Upvotes: 2

Related Questions