Chapter 4: Histogram and Backprojection

a. Histogram

An histogram a spectrum of intensity repartition. Concretely this is a list that contains for each possible value of pixel the number of pixels that have this value. You can say well, but in the end how can it be really useful ? for which purposes ? For instance calculate the histogram of an area on a photography, then the same area on another photography from another point of view for instance is more likely to have the same histogram. So it can be really really useful to make pattern recognition “from scratch”. Indeed OpenCV provide all the needed function to do it thank’s to CalcHist and CompareHist that return a float of the distance (accuracy).

As we have seen in the filter chapter equalizehist function intent modify an image to flatten the image histogram so that the balance between black and white is almost the same. The example below calculate the histogram of a gray picture and  show with a graph the associated histogram (on another picture), then it equalize the picture and recalculate the histogram to show the difference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import cv2.cv as cv

def drawGraph(ar,im, size): #Draw the histogram on the image
    minV, maxV, minloc, maxloc = cv.MinMaxLoc(ar) #Get the min and max value
    hpt = 0.9 * histsize
    for i in range(size):
        intensity = ar[i] * hpt / maxV #Calculate the intensity to make enter in the image
        cv.Line(im, (i,size), (i,int(size-intensity)),cv.Scalar(255,255,255)) #Draw the line
        i += 1

#---- Gray image
orig = cv.LoadImage("img/lena.jpg", cv.CV_8U)

histsize = 256 #Because we are working on grayscale pictures which values within 0-255

hist = cv.CreateHist([histsize], cv.CV_HIST_ARRAY, [[0,histsize]], 1)

cv.CalcHist([orig], hist) #Calculate histogram for the given grayscale picture

histImg = cv.CreateMat(histsize, histsize, cv.CV_8U) #Image that will contain the graph of the repartition of values
drawGraph(hist.bins, histImg, histsize)

cv.ShowImage("Original Image", orig)
cv.ShowImage("Original Histogram", histImg)
#---------------------

#---- Equalized image
imEq = cv.CloneImage(orig)
cv.EqualizeHist(imEq, imEq) #Equlize the original image

histEq = cv.CreateHist([histsize], cv.CV_HIST_ARRAY, [[0,histsize]], 1)
cv.CalcHist([imEq], histEq) #Calculate histogram for the given grayscale picture
eqImg = cv.CreateMat(histsize, histsize, cv.CV_8U) #Image that will contain the graph of the repartition of values
drawGraph(histEq.bins, eqImg, histsize)

cv.ShowImage("Image Equalized", imEq)
cv.ShowImage("Equalized HIstogram", eqImg)
#--------------------------------

cv.WaitKey(0)

Original image:

Equalized image:

b. Backprojection

The backprojection is calculated from the histogram. It basically replace every pixel by it’s probability to occur in the image. It is not really useful to use it by hand, but this is used by other algorithm like meanshift. The example below shows the common usage of backprojection associated with Region Of Interest. In this example we select a rectangle in the upper left corner of the image, we compute the histogram and then apply a backprojection on the whole image to detect others parts of the image which have the same histogram.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import cv2.cv as cv

im = cv.LoadImage("img/lena.jpg", cv.CV_8U)

cv.SetImageROI(im, (1, 1,30,30))

histsize = 256 #Because we are working on grayscale pictures
hist = cv.CreateHist([histsize], cv.CV_HIST_ARRAY, [[0,histsize]], 1)
cv.CalcHist([im], hist)


cv.NormalizeHist(hist,1) # The factor rescale values by multiplying values by the factor
_,max_value,_,_ = cv.GetMinMaxHistValue(hist)

if max_value == 0:
    max_value = 1.0
cv.NormalizeHist(hist,256/max_value)

cv.ResetImageROI(im)

res = cv.CreateMat(im.height, im.width, cv.CV_8U)
cv.CalcBackProject([im], res, hist)

cv.Rectangle(im, (1,1), (30,30), (0,0,255), 2, cv.CV_FILLED)
cv.ShowImage("Original Image", im)
cv.ShowImage("BackProjected", res)
cv.WaitKey(0)

Note: The result is more or less accurate. To get a better result, you can do it on every channel of a colour image to get a far more accurate result.

<<Pixel Access and Matrix Iteration | Home | Line Edge And Contours Detection>>