# Part 3: Studying Digital Image Processing with OpenCV-Python

1. Histograms
It gives you an overall idea about the intensity distribution of an image. It is a plot with pixel values (ranging from 0 to 255, not always) in X-axis and corresponding number of pixels with that pixel value in the image on Y-axis.
With histogram plot, you get intuition about contrast, brightness, intensity distribution, ... of that image.
Figure: example from OpenCV
Left region of histogram shows the amount of darker pixels in image and right region shows the amount of brighter pixels. Dark region is more than brighter region, and pixel values in mid-range are very less.
2. Find Histogram
Let 's see some terms:

BINS : instead of showing the number of pixels for every pixel value, from 0 to 255, need 256 values. We find the number of pixels lying between 0 to 15, then 16 to 31, ..., 240 to 255, then need only 16 values (16 sub-parts) to represent the histogram. This each sub-part is called "BIN". It is histSize in OpenCV.

DIMS : It is the number of parameters for which we collect the data. In the example above, it is intensity value. So it is 1.
RANGE : It is the range of intensity values you want to measure. Normally, it is [0,256].
OpenCV provides a function to find the histogram:
cv.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
channels : it is also given in square brackets. It is the index of channel for which we calculate histogram (gray-scale image is [0]. For color image, [0], [1], [2] are according to calculate histogram of blue, green or red channel).
mask : mask image. To find histogram of particular region of image.
3. Plotting Histograms
Use Matplotlib plotting functions for the image below:
Figure: input image
- For gray-scale image
 1 2 3 4 5 6 import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('images/paris.jpg',0) plt.hist(img.ravel(),256,[0,256]); plt.show() 
Figure: gray-scale converted image with its histogram
- For BGR image
  1 2 3 4 5 6 7 8 9 10 import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('images/paris.jpg') color = ('b','g','r') for i,col in enumerate(color): histr = cv.calcHist([img],[i],None,[256],[0,256]) plt.plot(histr,color = col) plt.xlim([0,256]) plt.show() 
Figure: BGR image with its histogram
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('images/paris.jpg') # create a mask mask = np.zeros(img.shape[:2], np.uint8) mask[100:300, 100:400] = 255 masked_img = cv.bitwise_and(img,img,mask = mask) # Calculate histogram with mask and without mask # Check third argument for mask hist_full = cv.calcHist([img],[0],None,[256],[0,256]) hist_mask = cv.calcHist([img],[0],mask,[256],[0,256]) plt.subplot(221), plt.imshow(img, 'gray') plt.subplot(222), plt.imshow(mask,'gray') plt.subplot(223), plt.imshow(masked_img, 'gray') plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0,256]) plt.show() 
3. Histogram Equalization
A good image will have pixels from all regions of the image. For example: a brighter image will have all pixels confined to high values, this is not good. in order to improve it, we use Histogram Equalization. It supports to stretch this histogram to either ends.
Figure: stretch left histogram to right histogram
OpenCV provides cv.equalizeHist(). Its input is just grayscale image and output is histogram equalized image.
Let 's make a demo for the image below.
Figure: input image
Figure: Its histogram
After running Histogram Equalization
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('images/histogram_equal.jpg',0) equ = cv.equalizeHist(img) res = np.hstack((img,equ)) #stacking images side-by-side cv.imwrite('res.png',res) hist,bins = np.histogram(res.flatten(),256,[0,256]) cdf = hist.cumsum() cdf_normalized = cdf * float(hist.max()) / cdf.max() plt.plot(cdf_normalized, color = 'b') plt.hist(res.flatten(),256,[0,256], color = 'r') plt.xlim([0,256]) plt.legend(('cdf','histogram'), loc = 'upper left') plt.show() 
Figure: the image looks brighter

Figure: new histogram
4. CLAHE (Contrast Limited Adaptive Histogram Equalization)
The histogram equalization above, only considers the global contrast of the image. Let 's use the image below:
Figure: input image
Figure: histogram - dark region is more than brighter region
Figure: histogram equalization - brighter region is more than dark region
The whole image looks over-brightness because its histogram is not confined to a particular region.
In order to improve this, we use adaptive histogram equalization. Using this method, image is divided into small blocks called "tiles" (tileSize is 8x8 by default in OpenCV). Then each of these blocks are histogram equalized. So histogram is confined to a particular small region.
If noise is in "tiles", it will be amplified. To avoid this, contrast limiting (by default 40 in OpenCV) is applied. If any histogram bin is above the specified contrast limit, those pixels are clipped and distributed uniformly to other bins before applying histogram equalization.
After equalization, to remove artifacts in tile borders, bilinear interpolation is applied.
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('images/zues.png',0) # create a CLAHE object (Arguments are optional). clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) clahe = clahe.apply(img) res = np.hstack((img,clahe)) #stacking images side-by-side cv.imwrite('res.png',res) hist,bins = np.histogram(clahe.flatten(),256,[0,256]) cdf = hist.cumsum() cdf_normalized = cdf * float(hist.max()) / cdf.max() plt.plot(cdf_normalized, color = 'b') plt.hist(clahe.flatten(),256,[0,256], color = 'r') plt.xlim([0,256]) plt.legend(('cdf','histogram'), loc = 'upper left') plt.show() 
Figure: adaptive histogram equalization-brighter region and dark region are more reasonable
5. 2D Histograms
We are taking only one feature to plot histogram. So It is called one-dimensional.
But  in two-dimensional histograms, we consider two features.
2D Histograms is used for finding color histograms where two features are Hue & Saturation values for each pixel. For color histograms, we need to convert the image from BGR to HSV.
We use cv.calcHist() to calculate 2D Histogram with parameters:
+ channels = [0,1] for H and S plane.
+ bins = [180,256] 180 for H plane and 256 for S plane.
+ range = [0,180,0,256] Hue value lies between 0 and 180 & Saturation lies between 0 and 256.
We use Matplotlib to plot 2D Histogram. it provides matplotlib.pyplot.imshow() function to plot 2D histogram with different color maps.
Note: While using this function, remember, interpolation flag should be 'nearest' for better results.
Calculate 2D Histogram for the image below:
Figure: input image

  1 2 3 4 5 6 7 8 9 10 11 import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread('images/paris.jpg') hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV) hist = cv.calcHist( [hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] ) plt.imshow(hist,interpolation = 'nearest') cbar = plt.colorbar() cbar.ax.set_ylabel('Counts') plt.show() 
Figure: 2D Histogram
6. Histogram Backprojection
It is used for image segmentation or finding objects of interest in an image.
The algorithm creates an image of the same size (but single channel) of our input image, where each pixel corresponds to the probability of that pixel belonging to our object.
OpenCV provides cv.calcBackProject(). One of its parameter is model histogram of the object that we want to find.
You may consider to normalize the model histogram, so the model histogram can be visible for you.
Let 's make a demo: extract only the red rose of the image below:
Figure: rose image
Figure: red rose template
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import numpy as np import cv2 as cv #template of the object to find roi = cv.imread('images/rose_temp.png') hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV) #input image target = cv.imread('images/rose.jpg') hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV) # calculating object histogram roihist = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] ) dst = cv.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1) #from dst => use dialate disc = np.ones((7,7),np.uint8) dst = cv.dilate(dst,disc,iterations = 1) # threshold and binary AND ret,thresh = cv.threshold(dst,50,255,0) #create mask bgr thresh = cv.merge((thresh,thresh,thresh)) res = cv.bitwise_and(target,thresh) cv.imshow('res',res) cv.waitKey(0) cv.destroyAllWindows() 
Figure: output