本章将学习直方图反投影。
理论基础
该算法由Michael J. Swain和Dana H. Ballard在其论文《基于颜色直方图的索引方法》(Indexing via Color Histograms)中首次提出。
简单来说,用于图像分割或在图像中查找感兴趣的对象。 它创建了一个与我们的输入图像大小相同(但只有一个通道)的图像,其中每个像素对应于该像素属于我们对象的概率。 在更简单的世界中,输出图像将使我们感兴趣的对象比其余部分更白,这是一个直观的解释。 直方图反投影与camshift算法等一起使用。
那么如何做呢?创建一个包含感兴趣的对象(在例子中,地面、离开玩家和其他东西)的图像直方图。 对象应尽可能填充图像,以获得更好的结果。颜色直方图比灰度直方图更可取,因为对象的颜色比其灰度强度更能定义对象。 将这个直方图“反向投影”到我们的测试图像上,在那里需要找到对象,也就是说,计算每个像素属于地面的概率并显示出来。 通过适当的阈值处理得到的输出只给了我们地面。
Numpy中的算法
首先,需要计算我们需要找到的对象(设为“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('/data/cvdata/rose_red.png')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
#target is the image we search in
target = cv2.imread('/data/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 0x7fcb6874e6c0>
plt.imshow(target)
<matplotlib.image.AxesImage at 0x7fcb66b34fe0>
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])
找到比率 $R = \frac{M}{I}$。对R进行反投影,即使用R作为调色板,创建一个新的图像,每个像素都是其对应的目标概率。
即B(x,y) = R[h(x,y),s(x,y)]
,其中h是色调,s是像素在(x,y)处的饱和度。之后应用条件$B(x,y) = min[B(x,y), 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]], shape=(397, 636), dtype=uint8)
最大强度的位置给与了物体的位置。 如果期望图像中有一个区域,那么对合适的值进行阈值处理会得到很好的结果。
ret,thresh = cv2.threshold(B,50,255,0)
plt.imshow(thresh)
<matplotlib.image.AxesImage at 0x7fcb66bf4c80>
import cv2
import numpy as np
roi = cv2.imread('/data/cvdata/rose_red.png')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
target = cv2.imread('/data/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