学习目标
这一节,我们学习直方图反向投影(histogram backprojection)。
理论
该理论由Michael J. Swain , Dana H. Ballard发表在他们的论文中,Indexing via color histograms.
那么,直方图反向投影是什么意思呢?常被用于图像分割或者寻找图像中感兴趣区域。简单来说,首先创建与输入图像相同大小的单通道图像,图像中每一个像素与该像素属于目标的概率有关。简而言之,输出图像中目标区域具有更大的白色区域(相对于背景区域)。这仅仅是直觉上的理解。直方图反向投影常与CameShift算法一起使用。
那么该算法如何操作呢?首先创建包含目标的图像直方图(本节的例子,我们留下地面,去掉其余的对象)。若要得到很好的效果,那么需要目标区域足够大。那么彩色直方图将更加优于灰度直方图,因为色彩更好的定义目标。然后我们对该图像应用直方图反向投影,最终定位到目标区域。换句话说,我们估计每一个像素属于目标(地面,ground)的概率。
Numpy反向投影的计算
-
首先,我们计算目标的直方图(令为
M
),以及搜索的目标图像 (令为I
)。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('rose_red.png') hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV) #target is the image we search in target = cv2.imread('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] )
-
计算比值: R = M I R=\frac{M}{I} R=IM,然后将R作为画板,并创建新的图像,每一个像素表示与目标相关的概率,比如
B(x,y)=R[h(x,y),s(x, y)]
,这里h是hue
,s是饱和度(saturation)。然后,应用条件,B(x,y) = min(B(x,y),1).h,s,v = cv2.split(hsvt) B = R[h.ravel(),s.ravel()] B = np.minimum(B,1) B = B.reshape(hsvt.shape[:2])
-
现在,我们应用圆盘卷积,B=D*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)
-
那么最大值的位置就是目标位置。如果我们期待一个图像中的区域,那么我们可以给合适的阈值,会得到很好的结果。
ret,thresh = cv2.threshold(B,50,255,0)
OpenCV中的反向投影计算
OpenCV中提供了计算直方图的内置函数,cv2.calcBackProject()
. 它的参数几乎与cv2.calcHist()
的参数一致。他的其中一个参数是目标的直方图,我们需要寻找该目标。并且,传入的直方图必须是归一化的。返回概率图像。然后使用卷积对图像进行卷积,最后应用阈值,下面是代码和输出:
import cv2
import numpy as np
roi = cv2.imread('rose_red.png')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
target = cv2.imread('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('res.jpg',res)
运行结果如下: