Histogram Equalization — a simple way to improve the contrast of your image

Shilpi Bhattacharyya
Towards Data Science
3 min readOct 25, 2019

--

I learnt about histogram equalization from Professor Shilkrot in my Computer Vision class at Stony Brook University. This is a very simple concept and takes few lines of code to realize.

What do you think a histogram representation of an image look like? Something like the red plot on the left? YES!

(a) input image and its histogram

We see from the plot above that the histogram lies in brighter region in (a). But, if we need a uniform distribution of the brightness, we are going to need a transformation function which maps the input pixels in brighter region to output pixels in full region. That is what histogram equalization does. It enhances the contrast of the image. The resultant image from histogram equalization can be seen on the right in (b).

(b) resulting image post histogram equalization technique
## code to plot histogram in pythonimport numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('Chatth_Puja_Bihar_India.jpeg',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.show()

We can do this in OpenCV using a function cv2.equalizeHist(). If its input is just grayscale image, then output is our histogram equalized image. If it is colored (RGB) image, we can segregate all three different streams — red, green, blue; call cv2.equalizeHist() individually on these channels and finally merge back, as shown in the code below. I am also providing code through numpy arrays for better comprehension.

def histogram_equalization(img_in):# segregate color streams
b,g,r = cv2.split(img_in)
h_b, bin_b = np.histogram(b.flatten(), 256, [0, 256])
h_g, bin_g = np.histogram(g.flatten(), 256, [0, 256])
h_r, bin_r = np.histogram(r.flatten(), 256, [0, 256])
# calculate cdf
cdf_b = np.cumsum(h_b)
cdf_g = np.cumsum(h_g)
cdf_r = np.cumsum(h_r)

# mask all pixels with value=0 and replace it with mean of the pixel values
cdf_m_b = np.ma.masked_equal(cdf_b,0)
cdf_m_b = (cdf_m_b - cdf_m_b.min())*255/(cdf_m_b.max()-cdf_m_b.min())
cdf_final_b = np.ma.filled(cdf_m_b,0).astype('uint8')

cdf_m_g = np.ma.masked_equal(cdf_g,0)
cdf_m_g = (cdf_m_g - cdf_m_g.min())*255/(cdf_m_g.max()-cdf_m_g.min())
cdf_final_g = np.ma.filled(cdf_m_g,0).astype('uint8')
cdf_m_r = np.ma.masked_equal(cdf_r,0)
cdf_m_r = (cdf_m_r - cdf_m_r.min())*255/(cdf_m_r.max()-cdf_m_r.min())
cdf_final_r = np.ma.filled(cdf_m_r,0).astype('uint8')
# merge the images in the three channels img_b = cdf_final_b[b]
img_g = cdf_final_g[g]
img_r = cdf_final_r[r]

img_out = cv2.merge((img_b, img_g, img_r))
# validation
equ_b = cv2.equalizeHist(b)
equ_g = cv2.equalizeHist(g)
equ_r = cv2.equalizeHist(r)
equ = cv2.merge((equ_b, equ_g, equ_r))
#print(equ)
#cv2.imwrite('output_name.png', equ)
return img_out

Applications

  1. You can use histogram equalization to improve the lighting of any low contrast image.
  2. In face recognition techniques, before training the face data, the images of faces are histogram equalized to make them all with same lighting conditions.

Bonus

For starters, convert an image to gray and black & white using the following code.

import cv2

originalImage = cv2.imread('avalon_lake.jpeg')
grayImage = cv2.cvtColor(originalImage, cv2.COLOR_BGR2GRAY)
(thresh, blackAndWhiteImage) = cv2.threshold(grayImage, 127, 255, cv2.THRESH_BINARY)
cv2.imwrite('blackAndWhiteImage.png', blackAndWhiteImage)
cv2.imwrite('grayImage.png', grayImage)
Original -> Gray -> BW

--

--