NightLearner
NightLearner

Reputation: 305

Calculate the ratio of area of different colors in an image

I am trying to calculate the proportion of colors in an image and return this as a ratio. I have lots of images of "logs" which are vertical sequences of colors and I would like to know the difference of the total area of 1 color, compared to the other, and a ratio of the 1 color relative to the whole image.

As an example, in the image below, if I calculate the total area occupied by the blue color and compare it to the whole area (blue and brown combined, I get the ratio = 69.14. (I got that value by summing up the area of all the blue rectangles and dividing it by the summed area of all the blue and brown rectangles)

*note, the colors will not always be the ones below, and I would like to be able to use more than two colors if that is a possibility (and perhaps just give the RGB code of the color I'm focused on and want the ratio of that color to the whole of).

Note I have LOTS of images I would have to run this one so it would be helpful to loop over all images in a folder if that is a possibility.

I pieced together to code below from a couple of examples, but I am confused on a few things. One, I don't necessarily want to convert the image to binary unless I have to, and two, it looks like I'm calculating the number of white pixels rather than black. Not sure where I am going wrong.

import cv2
import numpy as np 
from matplotlib import pyplot as plt

# load image
image = cv2.imread('/Users/Me/Desktop/logtest.png',0)

# plot the binary image
imgplot = plt.imshow(image, "gray")
plt.show()

#Calculate percent of pixels that are black
ret,thresh = cv2.threshold(image,0,230, cv2.THRESH_BINARY)
height, width = image.shape

print ("Height and Width : ",height, width)
size = image.size

print ("Total number of pixels in the image is =", size)

ChosenPix = cv2.countNonZero(image)
print("Total number of black pixels =", count)

Ratio = (ChosenPix/size)*100
print("Ratio of black to total is =", Ratio)

enter image description here

Upvotes: 1

Views: 4055

Answers (1)

Mark Setchell
Mark Setchell

Reputation: 207375

I made a few sample images like this:

enter image description here enter image description here enter image description here

Then I used a glob to select all files named log*png and processed each one. That consisted of counting the unique colours in the image and iterating over those colours to count how many pixels matched each unique colour:

#!/usr/bin/env python3

from PIL import Image
import numpy as np
import glob

def processLog(filename):
    print(f"Processing log: {filename}")
    # Open this image and make a Numpy version for easy processing
    im   = Image.open(filename).convert('RGBA').convert('RGB')
    imnp = np.array(im)
    h, w = imnp.shape[:2]

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

    # Iterate through unique colours
    for index, colour in enumerate(colours):
        count = counts[index]
        proportion = (100 * count) / (h * w)
        print(f"   Colour: {colour}, count: {count}, proportion: {proportion:.2f}%")

# Iterate over all images called "log*png" in current directory
for filename in glob.glob('log*png'):
    processLog(filename)

Output

Processing log: log2.png
   Colour: [  0 255 255], count: 800, proportion: 5.00%
   Colour: [255   0 255], count: 6400, proportion: 40.00%
   Colour: [255 255   0], count: 8800, proportion: 55.00%
Processing log: log1.png
   Colour: [  0 255   0], count: 6400, proportion: 36.36%
   Colour: [255   0   0], count: 11200, proportion: 63.64%
Processing log: log9.png
   Colour: [ 83 195 187], count: 16160, proportion: 67.33%
   Colour: [ 87 190 179], count: 80, proportion: 0.33%
   Colour: [ 88 184 171], count: 80, proportion: 0.33%
   Colour: [ 89 180 165], count: 80, proportion: 0.33%
   Colour: [ 94 175 158], count: 80, proportion: 0.33%
   Colour: [ 96 164 143], count: 80, proportion: 0.33%
   Colour: [107 146 116], count: 80, proportion: 0.33%
   Colour: [120 114  71], count: 80, proportion: 0.33%
   Colour: [124  99  50], count: 80, proportion: 0.33%
   Colour: [126  88  35], count: 7120, proportion: 29.67%
   Colour: [126  90  37], count: 80, proportion: 0.33%

Of course, if you don't want to write any Python, you can just iterate through the files in a bash loop and use ImageMagick to extract the total number of pixels and the number of pixels of each colour for you. ImageMagick is installed on most Linux distros and is available for macOS and Windows:

for f in log*png; do magick "$f" -format "%f: %[fx:w*h]\n"  -write info: -format %c histogram:info: ; done

Output

log1.png: 17600
      6400: (  0,255,  0) #00FF00 lime
     11200: (255,  0,  0) #FF0000 red
log2.png: 16000
       800: (  0,255,255) #00FFFF cyan
      6400: (255,  0,255) #FF00FF magenta
      8800: (255,255,  0) #FFFF00 yellow
log9.png: 24000
     16160: ( 83,195,187,255) #53C3BBFF srgba(83,195,187,1)
        80: ( 87,190,179,255) #57BEB3FF srgba(87,190,179,1)
        80: ( 88,184,171,251) #58B8ABFB srgba(88,184,171,0.984314)
        80: ( 89,180,165,255) #59B4A5FF srgba(89,180,165,1)
        80: ( 94,175,158,246) #5EAF9EF6 srgba(94,175,158,0.964706)
        80: ( 96,164,143,255) #60A48FFF srgba(96,164,143,1)
        80: (107,146,116,246) #6B9274F6 srgba(107,146,116,0.964706)
        80: (120,114, 71,246) #787247F6 srgba(120,114,71,0.964706)
        80: (124, 99, 50,255) #7C6332FF srgba(124,99,50,1)
      7120: (126, 88, 35,255) #7E5823FF srgba(126,88,35,1)
        80: (126, 90, 37,250) #7E5A25FA srgba(126,90,37,0.980392)

Upvotes: 4

Related Questions