5.4. 直方图-4:直方图反投影

5.4.1. 目标

在本章中,我们将学习直方图反投影。

5.4.2. 理论

它是由 迈克尔·J·斯温,达娜·H·巴拉德 在他们的报纸上 颜色直方图索引 .

用简单的话来说到底是什么? 它用于图像分割或在图像中查找感兴趣的对象。简单地说,它创建的图像与输入图像的大小相同(但只有一个通道),其中每个像素对应于该像素属于我们的对象的概率。在更简单的世界中,输出图像将使我们感兴趣的对象比剩余部分更白。好吧,这是一个直观的解释。(我无法使它更简单)。直方图反投影与camshift算法等结合使用。

我们怎么做? 我们创建一个包含感兴趣对象的图像直方图(在我们的例子中,是地面、离开播放器和其他东西)。对象应尽可能填充图像以获得更好的效果。与灰度直方图相比,颜色直方图更受欢迎,因为对象的颜色比其灰度强度更好地定义对象。然后,我们将这个直方图“投影”到我们的测试图像上,在那里我们需要找到目标,也就是说,我们计算出属于地面的每个像素的概率并显示出来。在适当的阈值上产生的输出给了我们单独的土地。

5.4.3. Numpy中的算法

  1. 首先,我们需要计算我们需要查找的对象(设为“M”)和要搜索的图像(设为“I”)的颜色直方图。

>>> %matplotlib inline
>>>
>>> import cv2
>>> import numpy as np
>>> from matplotlib import pyplot as plt
>>>
>>> #roi is the object or region of object we need to find
>>> roi = cv2.imread('/cvdata/rose_red.png')
>>>
>>> hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
>>>
>>> #target is the image we search in
>>> target = cv2.imread('/cvdata/rose.png')
>>> hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
>>>
>>> # Find the histograms using calcHist. Can be done with np.histogram2d also
>>> M = cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
>>> I = cv2.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )
>>> plt.imshow(roi)
<matplotlib.image.AxesImage at 0x7fe6cb7e8cc0>
../_images/sec04-histogram-backprojection_2_1.png
>>> plt.imshow(target)
<matplotlib.image.AxesImage at 0x7fe6cb7bea20>
../_images/sec04-histogram-backprojection_3_1.png
>>> cv = cv2
>>> h,s,v = cv.split(hsvt)
>>> B = M[h.ravel(),s.ravel()]
>>> B = np.minimum(B,1)
>>> B = B.reshape(hsvt.shape[:2])
  1. 找到比率 \(R = \frac{{M}}{{I}}\) . 然后以R为调色板,以每一个像素作为对应的目标概率,创建一个新的图像。工业工程 B(x,y) = R[h(x,y),s(x,y)] 其中h是色调,s是(x,y)处像素的饱和度。之后适用条件 \(B(x,y) = min[B(x,y), 1]\) .

  1. 现在用圆盘作卷积, \(B = D \ast B\) ,其中D是磁盘内核。

>>> disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
>>> cv2.filter2D(B,-1,disc,B)
>>> B = np.uint8(B)
>>> cv2.normalize(B,B,0,255,cv2.NORM_MINMAX)
array([[255, 255, 255, ..., 255, 255, 255],
       [255, 255, 255, ..., 255, 255, 255],
       [255, 255, 255, ..., 255, 255, 255],
       ...,
       [  0,   0,   0, ...,   0,   0,   0],
       [  0,   0,   0, ...,   0,   0,   0],
       [  0,   0,   0, ...,   0,   0,   0]], dtype=uint8)
  1. 现在最大强度的位置给出了物体的位置。如果我们期望图像中有一个区域,则对适当的值进行阈值化可以得到很好的结果。

>>> ret,thresh = cv2.threshold(B,50,255,0)
>>> plt.imshow(thresh)
<matplotlib.image.AxesImage at 0x7fe6cb715cf8>
../_images/sec04-histogram-backprojection_11_1.png

就这样!!

5.4.4. OpenCV中的反投影

OpenCV提供了一个内置函数 cv2.calcBackProject() . 其参数几乎与 化学气相色谱仪() 功能。它的一个参数是直方图,它是物体的直方图,我们必须找到它。此外,在传递给backproject函数之前,应该对对象直方图进行规范化。它返回概率图像。然后将图像与磁盘核卷积,并应用阈值。下面是我的代码和输出:

>>> import cv2
>>> import numpy as np
>>>
>>> roi = cv2.imread('/cvdata/rose_red.png')
>>> hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
>>>
>>> target = cv2.imread('/cvdata/rose.png')
>>> hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
>>>
>>> # calculating object histogram
>>> roihist = cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
>>>
>>> # normalize histogram and apply backprojection
>>> cv2.normalize(roihist,roihist,0,255,cv2.NORM_MINMAX)
>>> dst = cv2.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
>>>
>>> # Now convolute with circular disc
>>> disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
>>> cv2.filter2D(dst,-1,disc,dst)
>>>
>>> # threshold and binary AND
>>> ret,thresh = cv2.threshold(dst,50,255,0)
>>> thresh = cv2.merge((thresh,thresh,thresh))
>>> res = cv2.bitwise_and(target,thresh)
>>>
>>> res = np.vstack((target,thresh,res))
>>> cv2.imwrite('xx_res.jpg',res)
True

下面是我工作过的一个例子。我使用蓝色矩形内的区域作为示例对象,并希望提取整个地面。

5.4.5. 额外资源

  1. “通过颜色直方图进行索引”,斯温,迈克尔J.第三届计算机视觉国际会议,1990年。

5.4.6. 练习