Reputation: 305
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)
Upvotes: 1
Views: 4055
Reputation: 207375
I made a few sample images like this:
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