OpenCV-Python系列之直方图反投影
本次教程我们将介绍OpenCV直方图部分的最后一个知识点——直方图反投影。
反向投影可以用来做图像分割,寻找感兴趣区间,类似于我们前面讲过的ROI区域分割。它会输出与输入图像大小相同的图像,每一个像素值代表了输入图像上对应点属于目标对象的概率,简言之,输出图像中像素值越高的点越可能代表想要查找的目标。直方图投影经常与camshift(追踪算法)算法一起使用。
更通俗一点,反向投影可以通过颜色直方图来理解,我们检测图像中某个像素点的颜色是否位于直方图中,如果位于则将颜色加亮,通过对图像的检测,得出结果图像,结果图像一定和直方图像匹配。那么对于图像颜色的取样点越多,越能更好的找出目标图形。这里直方图的作用在于提供一个比较标准(也就是模版),即对于要检测的图像来说,需要给它提供一个模版,用于识别出和模版相应的特征。
算法实现的方法,首先要为包含我们感兴趣区域的图像建立直方图。被查找的对象最好是占据整个图像。最好使用颜色直方图,物体的颜色信息比灰度图像更容易被分割和识别。再将颜色直方图投影到输入图像查找目标,也就是找到输入图像中每一个像素点的像素值在直方图中对应的概率,这样就得到一个概率图像,最后设置适当的阈值对概率图像进行二值化。
经过上面的分析,下面给出反向投影的作用:反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,以下将其称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。
反向投影查找原理:查找的方式就是不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。
反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。
可以这样理解:对于calcBackProjectPatch,也就是是基于块的反向投影形式,利用直方图做匹配,类似于模板匹配,只不过这些模板转换为直方图,而原图中以某点为基准,抠出来作对比的部分也转换为直方图,两个直方图作匹配,匹配的结果作为此点的值,结果会是一张灰度图。
如果输入图像和模板图像一样大,那么反向投影相当于直方图对比。如果输入图像比模板图像还小,那么则无法进行反向投影。
OpenCV提供了一个内置函数cv.calcBackProject()。 它的参数与cv.calcHist()函数几乎相同。 此外,在传递给Backproject函数之前,应该对象直方图进行标准化。它返回概率图像,然后我们将图像与内核卷积并应用阈值。
接下来我们进行演示,模板图片:
原图:
来看代码:
def Back(): # roi图片,就想要找的的图片 roi = cv2.imread('temp.jpg') hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) # 目标搜索图片 target = cv2.imread('cat.jpg') hsvt = cv2.cvtColor(target, cv2.COLOR_BGR2HSV) # 计算目标直方图 roihist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256]) # 归一化,参数为原图像和输出图像,归一化后值全部在2到255范围 cv2.normalize(roihist, roihist, 0, 255, cv2.NORM_MINMAX) dst = cv2.calcBackProject([hsvt], [0, 1], roihist, [0, 180, 0, 256], 1) # 卷积连接分散的点 disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) dst = cv2.filter2D(dst, -1, disc) ret, thresh = cv2.threshold(dst, 50, 255, 0) # 使用merge变成通道图像 thresh = cv2.merge((thresh, thresh, thresh)) # 蒙板 res = cv2.bitwise_and(target, thresh) # 矩阵按列合并,就是把target,thresh和res三个图片横着拼在一起 res = np.hstack((target, thresh, res)) cv2.imwrite('res.jpg', res) # 显示图像 cv2.imshow('1', res) cv2.waitKey(0)
这就是直方图反投影的应用,其从本质上来讲,原理类似于模板匹配,我们在之前讲过的轮廓特征里也有这种模板和原图的匹配的应用,这些我们综合起来联系看更加有趣。
截至到本次教程为止,OpenCV基础篇的应用就到此结束了,从下一个教程开始,我们将进入OpenCV进阶篇的学习,后面的内容会较之前复杂,但也更为有趣,同时功能更加强大。