一起学python-opencv十三(直方图反向投影和模板匹配)

2D直方图

https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_histograms/py_2d_histogram/py_2d_histogram.html#twod-histogram

 

为什么只考虑h,s就够了呢?

 

因为其实亮度是很容易受外界影响的,我们认为一个颜色的本质特征是h和s。计算2D直方图,我们用的还是calcHist函数,不过参数得输入两个通道的了。H原来是0-360,为了让8位能存下,就对应到了0-180。第四个参数是[xmin,xmax,ymin,ymax]这种形式的。x指的是行,y指的是列。

 

 

 

bins上面有很多规则,请大家大概看一下,说的是如果是一个int,那么两个维度区间的数目都是int,如果是[int,int]这个两个分别是两个维度的bins个数,还有其它规则请自行查看。上面给出了range的格式[[xmin,xmax],[ymin,ymax]]。

 

返回的H是二维的直方图分布,xedges是第一个维度的区间边界组成的数组,yedges是第二个维度的。

 

上面应该是少了一句h,s,v=cv2.split(hsv)。下面是如何画直方图:

 

 

 

这个是可以大概看一下,越白的地方数值越高。其实有点像等高图,不过我们这里最低是黑色,而不是蓝色。还有就是用matpltlib画图了:

 

还参考了https://blog.csdn.net/goldxwang/article/details/76855200

imshow里的interpolation参数是插值的意思,为什么需要插值呢?因为我们的hist只提供了整数点的值而没有提供中间的值,那么这些值该怎么补充呢?这时候就需要用到插值。

 

插值方式有很多种,上面有列出来。blinear是双线性插值。这个我们前面介绍过,还有一个nearest也很常用。参考了https://blog.csdn.net/ccblogger/article/details/72918354

这里是举了一个例子:

 

 

 

 

 

 

 

默认用的应该就是这种插值,需要注意的是,最近邻插值是还要四舍五入的。

 

当然计算机也不可能一个点一个点去算,那也是无数个点,肯定有一个最小单位。旁边的这个类似于等高图高度颜色带的这个东西是colorbar生成的,plt.colorbar()要写在plt.show()之前。

这里需要注意一下:

 

596*300=238400。

去掉中括号以后就不对了。和不知道为什么是800。

 

用np生成二维直方图也是可以的,结果是一样的。

 

减小分的区间数的话。图看起来更明显一些。

 

需要提醒的是左下角是可以看到对应的高度值的:

 

[4.02e+04]就是鼠标所在处的高度。

直方图反投影

原理介绍:参考了https://blog.csdn.net/zyzhangyue/article/details/45827261

 

这个是一个滑动窗口,每次移动一个像素,然后计算窗口中图像的直方图和模板直方图的相似度。

 

最后肯定是要阈值化的,相似度大于某一个值才认为是我们要的结果。

函数实现:

 

 

 

中间最重要的函数当然是calcBackProject了。

 

scale是比例。我来选一个模板,就是圈起来的那朵花。

 

大概在[34:73,234:263]这个范围。

 

出来的dst就是如果和模板直方图越接近(这个接近可能就是用的直方图比较中的某一种距离来度量)结果数值越大,那么用灰度显示也就会越白,当然我猜测这个函数里面是还有一些处理的,因为滑动窗口每次只滑动一个像素嘛,有很多像素就重合了,那么这些像素点的输出值怎么办呢?我猜想可能就是取平均了。注意中括号。

 

模板直方图分的区间越少,也就是分的越粗,直方图反投影得到的结果越连续,但是也能太大。20的效果还算不错。

 

下面就分得太粗略了。

 

分得太细比较难匹配到。

 

 

 

 

 

我有点不懂为什么上面要对模板直方图归一化。alpha是归一化范围的下限,而beta是上限,normal_type是归一化方法,有很多种,参考了:https://www.cnblogs.com/sddai/p/6250094.html

我们常用的是:

 

我试了一下,好像有点知道是个怎么回事了。

 

 

 

 

 

 

 

这个模板直方图的归一化的最大值好像和输出的dst的最大值是一样的,当300的话就是饱和的,会被认为是255,虽然不知道为什么有这样的设定,这个得深究到下面的c++代码了。我前面不加为什么可以呢?因为我选择的模板直方图的最大值271大于255。这算是一个巧合,是因为我选择的模板图像比较大,然后颜色又都比较偏白色。那么这个归一化的语句最好还是加上去吧。

 

596*400和我们的原图形状是一样的。

 

最终输出的图像其实有点不太理想,我们把它再阈值化一下,当然这个阈值该怎么选,这个需要自己测试。中间的那个cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))是一种形态学处理,我们先不管它。阈值操作:

 

0其实就代表的是表格中的第一种阈值化方式:

 

效果还不错。把这个和原图与一下。

 

我选的这个模板有点太白了,不太明显。

我前面也说过,直方图不能代表图像的全部信息,因为它只包含强度信息,缺少了位置信息,所以上面匹配到的结果其实还是有很多不是我们想要的。下面是一种考虑了位置信息的匹配方式。

 

模板匹配

原理:模板匹配的原理其实很简单,还是基于滑动窗口,一个像素一个像素的去移动,只不过上面我们计算的是图像直方图的相似度,我们这里是直接计算图像的相似度,方法有好多种,参考了

https://blog.csdn.net/tsvico/article/details/78817096

 

计算起来其实挺麻烦的,有的时候我们就需要用到一些比较巧的办法,比如说对于一些相乘项的求和,我们或许可以把其中一个调转180,这个是为了凑成卷积的形式,然后根据卷积定理转到频域去计算离散的傅里叶变换相乘,然后再傅里叶反变换回来,这样是会减小计算量的。

还有一种加快运算速度的方法叫做积分图法。参考了

https://blog.csdn.net/yuan1125/article/details/70274515

 

就是从左上角开始累积像素值,最后生成一个图,就叫积分图。

 

一个区域的特征值就是这个区域里所有像素点对应强度的和。

 

上面原理和如何加快计算的方法,下面就是代码的实验了。

 

为什么输出图像的大小会变小呢?这是因为输出的图像其实是由上面的方法计算出来的相似度,输出的结果其实只需要取从左上角开始取(W-w+1,H-h+1)就够了,因为剩下的区域是不可能作为匹配区域的左上角的,因为不够啊。为什么取左上角,因为底层的c++是这么写的。

 

红色代表目标图片,蓝色是模板图片,红色是输出区域。

 

官方例子是个灰度图像,shape[::-1是步长为-1,为什么要为-1呢?这是因为w宽度,也就是列数和h高度,行数的位置是反过来的,我们完全可以写h,w=template.shape。官方也是有点皮。我们还是匹配那朵白玫瑰。我先来介绍几个函数,首先是模板匹配的函数:

 

这里面的参数的英文单词很常见了。templ是模板的意思。

 

这个函数是找出最大值,最小值以及对应的位置。上面这些比较方法应该都在c++里面由宏定义的。eval就是为了去掉引号,那一开始在method列表里面别加引号不就行了吗?搞不懂。

 

plt.xticks是选x轴坐标刻度的,他这样写就是不要刻度。plt.suptitle是大标题,因为有几个子图嘛,所以会有一个大标题咯。我就改了四处。

 

 

 

400-29+1=372,598-38+1=559。

 

如果把xTicks和yTick里删掉,那么就会有刻度了。这个第一种方法结果稍微有点不对。

 

这其实和模板匹配算法有关系,下面其实写错了,我画红线的话都是错的,不用在意。

 

这种方法取的是应该是左图中最亮的地方。那么这样的算法为什么不是一样的最大呢?

 

设想黑线是均值,然后红色是模板,蓝色是我设定的图像,那么你说按照上面的公式是红色和红色的值大,还是红色和蓝色的值大呢?毫无疑问是红色和蓝色。上面的方法是判断相关性的而不是相似性,所谓相关性就是你增大,我也增大,你减小我也减小,那么我们就叫做正相关,反之叫做负相关,和增大和减小前的值和增大或者减小了多少都无关,所以说这个模板匹配方法很不好。

 

这个匹配的是对的。也是取左图中最亮的地方。这种方法才是1代表正相关,-1代表负相关,0代表线性无关。

 

为什么标准相关匹配结果就可以呢?我想用柯西不等式来说明问题

 

-1<=R(x,y)<=1,这个取等条件比较苛刻,是所有位置对应像素的强度成比例才可以,一般这种条件在图像中也就是同一张图才能达到这样的条件了,至少在我们这张图上是的。

 

这个也是取最亮的地方,也是稍微有一点偏差的。

 

这个匹配的结果也还好。

 

上面两种方法的差别和上面的相似,只不过上面是减去了绝对值,这里没有减去绝对值而已,还是用相关性不是相似度和柯西不等式来解释。

 

这个是找最暗的。匹配的也不错。

 

上面两个是做差后平方和的结果,当然是同一张图片的输出区域最暗了。对于我们这张图,六种里面四种匹配的都还不错,但是都是有一些条件的,不过我觉得没有标准化的那两个相关性方法就不要用了,比较差。

 

一个模板匹配多个对象

 

 

 

这个不只是取一个最大的或者最小的了,而是规定一个阈值,这个阈值时自己设定的,可以找到多个对象。我们还是用上面的来做实验。

 

阈值设为0.8也还是只能匹配到我们的原图。

 

0.5的时候又出现一个新的区域。

 

0.45的时候就比较多了。

 

这里还要提醒一点,plt.imshow单通道的时候,选择cmap='gray',也就是颜色图取的是灰度图才会和cv2显示的一致。不然用的时它使用的不是灰度系统。

 

用的时什么image.cmap。不知道这是个什么东西,但是绝对不是灰度。如果时3维就没事,因为它会直接用RGB。

 

没使用灰度之前。

 

之后:

 

虽然不知道为什么显示的一个黑一个白。

 

不过好歹时回到了灰度空间。我有点理解为什么全是黑的了。首先colorbar会让plt.imshow按照我们的灰度值来。注释了colorbar后:

 

不注释:

 

 

 

 

 

感觉其实挺奇怪的,我的理解是plt.imshow单通道的时候必须要有一个对比才行,如果全是一种颜色,colorbar也算是一种对比的手段,这个时候显示的是完美的按照灰度。如果不是一个值,显示的是相对灰度,也就是里面的最大值就是白色,最小值是黑色,线性插值来显示灰度值。

 

前面又出现了变量前加*号的操作。

 

这个以前其实也说过的,不过相信很多人都忘了,其实也包括我,233,这很符合遗忘规律嘛,忘了不要紧,复习一下:https://zhidao.baidu.com/question/2140001532025683868.html

 

 

 

那这里为什么还要反着来呢?其实很简单了,这个其实是用rectangle画画的一个特性:

 

这个实验我们可以看到,rectangle里面传递参数的时候是列在前的,行在后的,这可能是为了适应我们的x轴水平,y轴垂直,一般写坐标都是(x,y)这样的习惯。不过这和np的格式就反过来了,因为np是行在前,列在后。还有一点需要注意:顶点坐标必须是元组。

 

好的,马上是十一了,但是休息是不可能休息的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值