If you have missed the previous post on image channel decomposition, follow this link and then come back here.
Colour image histogram
We can have two main types of colour image histogram: luminance histogram and component histogram. The principle used is the same as for the grey-level histogram in both the types, with one main difference between them: the first type is obtained from a grey-scale version of the colour image, while the second type, from the image's three separate channels.
Component histogram
To plot the component histogram of a colour image, after decomposing the image's channels into three separate images, each one containing only the colour for each channel, we must convert these images to grayscale. This step should account for the difference in human perception between red, green and blue, not just average the three channels' intensity, as explained here.
In the following picture, we can see the result of the above procedure executed on a 24 bits RGB image:
clockwise from top left:original image, red, blue and green channel |
Some problems with the contrast are soon very evident, especially with the blue channel.
Once we have the arrays of the intensity values for each channel, we can apply the same steps to each channel's intensity array, as we would do for a grayscale image. That would produce the histogram for each one of the image's channels, as we can see in the next picture:
import tkinter as tk
import sys
def rgb2gray(input_image):
if input_image.mode != 'RGB':
return None
else:
output_image = Image.new('L', (input_image.width,
input_image.height))
for x in range(input_image.width):
for y in range(input_image.height):
pix = input_image.getpixel((x, y))
pix = int(pix[0]*0.299 +
pix[1]*0.587 +
pix[2]*0.114)
output_image.putpixel((x,y), pix)
return output_image
def histogram(input_image):
if input_image.mode != 'L' and input_image.mode != 'P':
return None
else:
IHIST = [0 for i in range(256)]
HIST = [0 for i in range(256)]
SUM = 0
for x in range(input_image.width):
for y in range(input_image.height):
pix = input_image.getpixel((x, y))
IHIST[pix] = IHIST[pix]+1
SUM += 1
for i in range(256):
HIST[i] = float(IHIST[i]/SUM)
return HIST
def draw_histogram(canvas, IHIST, hist_w, hist_h, color, bins=False):
hist_max = max(IHIST)## get the max value
## Normalize between 0 and hist_h
for i in range(256):
IHIST[i] = float(IHIST[i]/hist_max) * hist_h
## A bin is a bar of the histogram
bin_w = round(float(hist_w/256))
offset = hist_w + 20 ## where we draw the first
prev_x = offset
prev_y = hist_h
for i in range(256):
if bins:
canvas.create_line(bin_w*i+offset, hist_h,
bin_w*i+offset, hist_h-IHIST[i],fill=color)
else:
canvas.create_line(prev_x, prev_y,
bin_w*i+offset, hist_h-IHIST[i],fill=color)
prev_x = bin_w*i+offset
prev_y = hist_h-IHIST[i]
def get_channel(input_image, channel_key):
if input_image.mode != 'RGB':
return None
else:
channels_indices = {'R':0 ,'G':1 ,'B':2}
output_image = Image.new('RGB', (input_image.width,
input_image.height))
ch_idx = channels_indices[channel_key]
for x in range(input_image.width):
for y in range(input_image.height):
pix = input_image.getpixel((x, y))
pix = pix[ch_idx]
if ch_idx == 0:
output_image.putpixel((x,y), (pix,0,0))
elif ch_idx == 1:
output_image.putpixel((x,y), (0,pix,0))
elif ch_idx == 2:
output_image.putpixel((x,y), (0,0,pix))
return output_image
if __name__ == "__main__":
root = tk.Tk()
img = Image.open("retriver.png")
if img == None:
print("Error opening image file"),
sys.exit(1)
width = img.width*2+20
height = img.height+20
root.title("RGB Image histogram")
root.geometry(f'{width}x{height}')
output_R = get_channel(img, 'R')
output_G = get_channel(img, 'G')
output_B = get_channel(img, 'B')
gray_R = rgb2gray(output_R)
gray_G = rgb2gray(output_G)
gray_B = rgb2gray(output_B)
IHISTR = histogram(gray_R)
IHISTG = histogram(gray_G)
IHISTB = histogram(gray_B)
hist_w = img.width
hist_h = img.height
if output_R != None and output_G!= None and output_B!=None:
output_R = ImageTk.PhotoImage(output_R)
output_G = ImageTk.PhotoImage(output_G)
output_B = ImageTk.PhotoImage(output_B)
input_im = ImageTk.PhotoImage(img)
canvas = tk.Canvas(root, width=width, height=height, bg="#ffffff")
canvas.create_image(width/4-1, height/2-1, image=input_im, state="normal")
draw_histogram(canvas, IHISTR, hist_w, hist_h, "#f00")
draw_histogram(canvas, IHISTG, hist_w, hist_h, "#0f0")
draw_histogram(canvas, IHISTB, hist_w, hist_h, "#00f")
canvas.place(x=0, y=0)
canvas.pack()
root.mainloop()
else:
print("Input image must be in 'RGB' colour space")