Image Histograms and Contrast

Pigeon and Pillar - High contrast black and white photos

Image Histograms and Contrast

Today we will be talking about image histograms and contrast. In the past, we’ve talked about how to generate Histograms and visualizations in Python. Subsequently, we want to talk about histograms of an image. As an illustration, we take a black and white photo and look at one important property; the intensity of the pixel.

Image of a payphone in greyscale

Obviously we see pixels in different shades of grey; often ranging from 0 to 255. If we were to count the number of pixels with intensity 0, 1, 2,… 255, then we would be able to plot a histogram with bin size 1.

Getting Started – Image Histograms and Contrast

In the same fashion as we did in the past, let us start off with importing our necessary libraries and setting up a helper function to display images in a Jupyter notebook. Accordingly, we import OpenCV and Numpy first.

import cv2
import numpy as np

#The line below is necessary to show Matplotlib's plots inside a Jupyter Notebook
%matplotlib inline
from matplotlib import pyplot as plt

#Use this helper function if you are working in Jupyter Lab
#If not, then directly use cv2.imshow(<window name>, <image>)

def showimage(myimage):
    if (myimage.ndim>2):  #This only applies to RGB or RGBA images (e.g. not to Black and White images)
        myimage = myimage[:,:,::-1] #OpenCV follows BGR order, while matplotlib likely follows RGB order
        
    fig, ax = plt.subplots(figsize=[10,10])
    ax.imshow(myimage, cmap = 'gray', interpolation = 'bicubic')
    plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis
    plt.show()

Once we have our pre-requisites, we further read in our desired image. For example, to import our image of our phone, we would run the following code:

# Read in our image as Grayscale
Phone = cv2.imread("Phone.jpg")
Phone_grey = cv2.cvtColor(Phone, cv2.COLOR_BGR2GRAY)
showimage(Phone_grey)  #This displays our image

Now that we have our grayscale image, we could use Numpy’s built in histogram function to display the intensity of each shade of grey [0,256].

# Using Numpy Histogram function, we determine the number of pixels with intensity
# between 0..255.
Phone_hist, Phone_bin = np.histogram(Phone_grey,256,[0,256])
print(Phone_hist)
[62159 32142 20571 16777 15704 14238 13971 14202 14055 14297 14733 14538
 14563 14494 14456 14220 13772 12991 12127 11354 10221  9051  8294  7374
  6713  6078  5549  5254  4902  4740  4761  4649  4758  4739  4701  4897
  4924  5080  5141  5271  5298  5278  5161  5202  5197 16679 25682 10031
  6859  6515  6839  7303  8077  9176 10610 11832 12597 12931 13121 12800
 12104 10932 10595 10194 10332 10057 10228 10078 10487 10744 11106 11512
 11978 12220 11552 10690  9754  8949  8408  7605  6761  6064  5334  4873
  4509  4329  4114  4100  4178  4214  4264  4237  4447  4334  4507  4768
  4943  5143  5229  5511  5662  5997  6400  6537  7249  7390  7328  7269
  7457  7125  7114  6976  7105  7237  7167  7352  7285  7700  8078  7664
  7567  7409  7513  7387  7738  8348  8634  9848 10887 11282 11562 11457
 11563 12403 14251 15410 15373 14759 14888 14641 14158 15741 16402 17411
 17613 17418 18261 17902 19599 20042 20596 20315 20794 20784 21351 20427
 20334 20155 21208 22038 22842 25040 28708 28866 28154 28950 30275 30831
 31348 31191 31352 32489 34578 36821 36320 35080 33622 30535 31602 30730
 30439 31084 31260 34045 34494 30445 26415 24507 23420 22475 23980 25397
 23930 23657 25491 27739 27148 28581 27827 29206 32001 30511 32834 34835
 40346 40781 35353 28588 24932 23584 16178  9350  4951  3648  2861  2530
  2420  2048  1938  1794  1760  1699  1512  1441  1205  1148  1082  1080
  1065  1093   931   942   851   821   755   828   811   859   868   848
   778   684   630   572   512   479   499   486   541   616   719   813
  1140  2279  5795  4407]

As shown above, we see many pixels that fall within the range of [0,20] (the first two rows of our output). These likely represent the handset of the pay phone as well as the bottom half as they are mainly black. Additionally, we can also find a high number of pixels in the second half of the table. Evidently, these are the lighter gray pixels that make up the general background. Lastly, we observe not many pixels are in the last two rows, meaning there are not a lot of white pixels.

At this point, we can also plot the histogram using matplotlib to visually represent our image pixel intensity.

plt.hist(Phone_grey.ravel(), 256,[0,256])
plt.show()
Histogram of greyscale image

Enhancing Contrast by Histogram Equalization

A common means to enhance contrast of an image is by equalizing the intensity across the entire range {0,256]. Furthermore, spreading out the intensity allows the largest possible contrast between the pixels. For that reason, OpenCV provides a histogram equalization function that helps us achieve this with only a few lines of code.

# Using OpenCV's built in equalizeHist function, we could easily equalize the image
# intensity by passing in our greyscale image
Phone_e = cv2.equalizeHist(Phone_grey)

We then plot the histogram to show how the effects of the equalization. If you don’t recall how this can be done, we recommend checking our earlier post on Data Visualizations with MatPlotLib. In short, with a few lines of code, we can plot the two histograms.

# With Matplotlib, we now plot the two histograms side by side for comparison
# This illustrates how the histogram looks like after equalization

fig, ax = plt.subplots(ncols=2, figsize=(12,6))
ax[0].hist(Phone_grey.ravel(), 256,[0,256])
ax[1].hist(Phone_e.ravel(),256,[0,256])
plt.show()
Image histogram and contrast - Comparison of before and after

Finally we want to display our image before and after contrast equalization took place.

# Using Numpy's function to append the two images horizontally
side_by_side = np.hstack((Phone_grey,Phone_e))

showimage(side_by_side)
Image comparison with image contrast improvements using histogram equalization

Unquestionably, the contrast of the image on the right has been improved. For instance, notice how the number pad or some of the text at the top has a clearer distinction. Furthermore the cable connecting the ear piece on the left also has greatly improved contrast against its background.

FreedomvcAbout Alan Wong
Alan is a part time Digital enthusiast and full time innovator who believes in freedom for all via Digital Transformation. 
兼職人工智能愛好者,全職企業家利用數碼科技釋放潛能與自由。

LinkedIn

Leave a Reply