I am trying to calculate the number of pixels corresponding to each of the color for the segmented image
Following is the code that I have written.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
from ast import literal_eval
try:
from PIL import Image, ImageDraw, ImageFont
except ImportError:
exit("This script requires the PIL module. Install with pip install Pillow")
try:
import webcolors
except ImportError:
exit("This script uses webcolors for displaying named colors. Install with pip install webcolors")
def count_pixels(filename):
"""
Returns a count of pixels per a unique color
Args:
filename (str): the image to count the number of pixels of
Returns:
a key-value pairing of the rgb color value and the number of times the color was present in the image
"""
color_count = {}
with Image.open(filename) as image:
width, height = image.size
rgb_image = image.convert('RGB')
# iterate through each pixel in the image and keep a count per unique color
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
return color_count
def create_legend_image(filename, colors, title, ignore_color):
"""
Create an image of color swatches paired with a color name and number
Args:
filename (str): the name of the legend image file
colors (dict): a key-value pairing of the color name and count
title (str): a title for the legend image
ignore_color (tuple): do not add this color to the legend image
Returns:
None
"""
margin = 10
rect_width = 25
rect_outline_width = 2
font_size = 20
img_width = 250
img_height = len(colors) * (rect_width rect_outline_width margin) (0 if title is None else font_size)
legend_img = Image.new("RGBA", (img_width, img_height), "white")
draw = ImageDraw.Draw(legend_img)
font = ImageFont.truetype("arialbd.ttf", font_size)
# draw title for legend if applicable
text_height = 0
if title is not None:
_, text_height = draw.textsize(title, font=font)
draw.text((0, 0), title, font=font, fill="black")
color_index = 1
for color, count in colors.items():
if color == ignore_color:
continue
try:
# convert RGB color to a human readable color if applicable
color_name = webcolors.rgb_to_name(color)
except ValueError:
color_name = color
# draw square for color legend
y0 = rect_width * (color_index - 1) margin * color_index text_height
y1 = rect_width * color_index margin * color_index text_height
draw.rectangle([(0, y0), (rect_width, y1)], fill=color, outline="black", width=2)
# draw color name next and pixel count for legend colors
draw.text((rect_width margin, y0), "{}: {}".format(color_name, count), font=font, fill="black")
color_index = 1
legend_img.save(filename, mode="w")
def main():
parser = argparse.ArgumentParser(description='Calculates the sum of pixels per a color')
parser.add_argument('image', nargs='?', default='.', help='The image to sum the pixels per a color of')
parser.add_argument('-i', '--ignore-color', help='Skip counting pixels of this color')
parser.add_argument('-t', '--title', help='Title for the image legend')
parser.add_argument('-l', '--legend-image', help='Generate an image with color swatches paired with the pixel count')
args = parser.parse_args()
ignore_color = literal_eval(args.ignore_color) if args.ignore_color is not None else None
color_count = count_pixels(args.image)
if args.legend_image is not None:
create_legend_image(args.legend_image, color_count, args.title, ignore_color)
# outputs pixel color count to console
print('Pixel Count per Unique Color:')
print('-' * 30)
color_index = 1
for color, count in color_count.items():
if color == ignore_color:
continue
try:
# convert RGB color to a human readable color if applicable
color_name = webcolors.rgb_to_name(color)
except ValueError:
color_name = color
print('{}.) {}: {}'.format(color_index, color_name, count))
color_index = 1
# Display the total number of pixels not ignored
print('-' * 30)
print('\t{} pixels'.format(sum(color_count[color] for color in color_count if color != ignore_color)))
if __name__ == '__main__':
main()
When I test my code, I am not getting the accurate result which indicates total number of pixels for each of the unique colors represented in the picture i.e. (Red, Yellow and Black), Can you suggest me some alternative approach to calculate the total number of pixels in a segmented image
CodePudding user response:
This is the simplest way to get the colours and their corresponding counts:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
# Open image and ensure RGB
im = Image.open('UMN9c.png').convert('RGB')
# Make into Numpy array
na = np.array(im)
# Get colours and corresponding counts
colours, counts = np.unique(na.reshape(-1,3), axis=0, return_counts=1)
print(colours, counts)
Sample Outputs
[[ 37 36 36]
[226 44 11]
[228 239 80]] [255169 1059 5916]
If you don't want to write any Python code, you can just use ImageMagick in the Terminal
magick -verbose UMN9c.png -format %c histogram:info:-
Output
UMN9c.png PNG 512x512 512x512 0 0 8-bit sRGB 3572B 0.020u 0:00.008
255169: (37,36,36,255) #252424FF srgba(37,36,36,1)
1059: (226,44,11,255) #E22C0BFF srgba(226,44,11,1)
5916: (228,239,80,255) #E4EF50FF srgba(228,239,80,1)
UMN9c.png=>info:- PNG 512x512 512x512 0 0 8-bit sRGB 3572B 0.320u 0:00.166
I'm not sure if outright speed is an issue for you. If so, there is a significantly faster method using np.dot()
at the end of this answer.
CodePudding user response:
Solution using OpenCV for import.
# Load image
img = cv2.imread("UMN9c.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Flatten image into rows = number of pixels, columns = 3 (RGB)
img = img.reshape(-1, img.shape[2])
# Count number of unique colors
unique_colors, unique_count = np.unique(img, axis=0, return_counts=True)
# Print info
print(unique_colors)
print(unique_count)
print("Total number of unique colors: ", unique_colors.shape[0])