ironpython使用opencv_一起学opencv-python三十七(视频分析:光流)

96b272306bebaf2ee8ea9eaa07c2d9a0350fa657.png

光流

参考了https://blog.csdn.net/jobbofhe/article/details/80448961

https://my.oschina.net/u/3702502/blog/1815343

和https://blog.csdn.net/qq_38906523/article/details/80781242

和https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html#lucas-kanade2ecb8aaf3c18a8e5b1f151fc1d717fedac572fb5.png

66f244d48687ff44f7fe6f69a7b6e1714aa9bc7b.png

亮度就是灰度值或者BGR通道的值,这个值不完全发生变化是不太可能的。0d3ac8ab646fe850aeda629e2444e5b0bf3e9b5e.png

d26e754446802c3c4d3a9767c110ddfcd48c8820.png

下面的Lucas-Kanade就是基于梯度的方法,基于匹配的方法问题其实上一讲也说过,如果我们要检测的特征点的匹配效果很差,那么光流的效果就会很差,,这个其实就是因为光流太稀疏,就是特征点太少导致的。d3738ad3166c9b25c9c8d4353648272ee2597187.png

239988b44bcc49ca33efb8ef2767113c3be83282.png

be41c282efce74f91b4a0a0bd753f319682b775d.png

上面是对光流的一些简单介绍。下面就稍微来展开说一说。f781d237b578f52ea269bf982e8aeaf29742a7a9.png

也就是说,上一帧在x,y处的点现在到了x+dx,y+dy处,经过了dt时间,上式之所以可以舍弃二二阶以上的高阶项,就要满足运动微小假设。970402fd4b28d1eb2973397818e9470080fabaf9.png

上面给出了求u的一种方法。这种方法叫做Lucas-Kanade方法。用的其实就是最小二乘法。里面Ix,Iy就是图像在(x,y)点的梯度分量,It的话,就拿后一帧的(x,y)处的亮度减去前一帧的,然后帧之间的时间我们是知道的。776df316b77e0fc2cd6b92a74ed2b0f3e2471e94.png

3fb99bb11ddf3324e16a195d83213c8bce6c000d.png

这里我再说说第三个条件,空间一致性的意思,其实说的意思是:比如我们要同时跟踪的是两只鸟,但是这两只鸟一只向左飞,一只向右飞,那最后得到的结果肯定是很奇葩的。就像上面的两个蓝点,它们不能同时作为跟踪的对象。0323f4c1532f8c0fdbf92961eda63f611cf12547.png

补充:http://blog.sina.com.cn/s/blog_50363a7901011215.html7d200227ad77c0d268f92749a5c5d96908126b65.png

这个W我们还是一般用高斯或者其它的核,这个和meanshift的思想一样,就是为了减少噪声的影响。上面我们讨论的都是在小运动的情况下,但是如果有比较大的运动呢,所以我们用了金字塔,当我们使用图像金字塔的时候,小运动会被消除,这个我想应该是先缩小图片,然后再放大,用的是这个过程吧,这样的话,确实几乎看不出来小运动,但是这样能把大运动变成小运动?我还是比较存疑的,这里的大运动应该指的也不能是很大范围的运动吧。0710862fa65aaa6ad740af75edd205079932721a.png

opencv中有函数,calcOpticalFlowPyrLK()可以用来做光流分析,其实光流法也是用于目标追踪的一种方法。首先我们需要指明需要跟踪物体的特征点,例子用的是Shi-Tomasi方法检测的,然后我们就用光流法来跟踪这些点。这里只用了一次特征检测,就是第一帧的时候或者预备帧的时候,第二帧的时候特征点的位置不是靠特征检测,而是根据Lucas-Kanade光流法计算出来的偏移量直接移动的,那么其实这样的误差是会累积的。这个函数里,输入参数有前一帧,后一帧,前一帧中特征点坐标。返回的是后一阵中的点,还有一些状态数,如果状态数为1,表示下一帧的点找到了,如果是0,则没有找到。我们先来看看这个函数:f77765712fd26431459f4eeb676501bf10d99355.png

b3ced8fd26014c8de430ecb8aa65058ddd4ce676.png

参数蛮多的,参考https://blog.csdn.net/qq_29541381/article/details/80154329cc196a29234d52f0bf94c5125d8010be11b96333.png

这个minEigThreshold参数的设立就是为了过滤调一些不好的特征点,虽然前面特征检测都已经过滤过了。这个矩阵指的是:dbbc2a5269a059306eef078f8937b13415b06096.png

看到这个函数的输入参数,感觉其实有点懵逼。这个我还真的满看出来哪里用了迭代。这里推荐《学习opencv》这本书,里面对很多方法都有详细的介绍,下载方法在https://www.linuxidc.com/Linux/2011-08/39907.htm4d5fd8cdb4a098648aee13320dc0972ad8d1a28f.png

这个是一个一维的例子。11289dada8ea40b6fea29216a5b9877f6344bcef.png

这里我们就看到了用牛顿法迭代的过程了,这个解决了亮度不变这个假设不能满足的问题,虽然可能结果并不收敛,所以说会出现找不到下一帧对应点的情况啊。21c74fdf1d47f036abcf35f7916513a5584ebd92.png

也就是说会对原图进行降采样,缩小图片大小,这样的话想想的确可以把大运动变成小运动,因为中间差的像素数少多了,原来可能差4个像素,一变小就差2个了。b9de3f499572bbc27ee72596ac24ed59dcea4abb.png

先从小的图片开始用光流法,因为它容易满足小运动的假设。e261d738777bf50d74a631aec3f5b6c6a4153c2c.png

这样稍微大一点的运动我们也能处理了。cba3c6af1cce4b942b755ef6b1c00fefdca1c766.png

这本书真的很不错呢,把原理都讲得很清楚,我很喜欢。真的是为什么不早点找这本书,这样查资料就不用那么费时了,左查一点,右查一点。opencv代码:40a2100e39624a815e216652c685a88a0a2010d3.png

看到有人问我要代码,其实代码这些教程网站上都有啊。我还是只用两张图片(原来的眼镜清洗液的图片)来测试,其中修改了部分代码:94667cb101416f58e85dae0fec7b1b96601c7bce.png

98daf32a62982b7566832f940bab8e7b4173120b.png

我在特征检测哪里加了掩模,只希望检测眼镜清洗液的特征点。color = np.random.randint(0, 255, (100, 3))是来产生随机颜色的,范围是在0,255之间,形状是100*3。zeros_like函数参考

https://blog.csdn.net/wuguangbin1230/article/details/7285033355aa3420c5b301865d4008b9eef63e5dd2348c9e.png

返回的是和输入数组一样形状和数据类型的数组,不过元素都是0。

结果:aa9c2c68394c0b51423dd4a7b85617c56f1108d7.png

效果并不好,我来看看图一里面特征点检测得怎么样:9ca835050ccacb5531e8326877a1d4a47099f65d.png

这个检测得其实挺好的嘛。1a5a2c77064fdf513daa4cc1d7dcddad8c4e23f4.png

看来需要改一些参数呢,46a5802b7003149e3381466b65284f0e1265add4.png

不过效果还是不好呢,614e33cb0e01974b2f5c4054117158f877f21875.png

那些比较大的点是前一帧中的特征点,小的点是这一帧的特征点,这次的点都偏左,而上次是偏右。金字塔的层数会是一个比较关键的参数,如果太小,可能即使降采样也不符合小运动,所以这个参数尽量大一些。60ea790d45d3fd707c7f54a19ce43d3a1888b399.png

11364cca9fc625e638df57fce5c08119bd590dcc.png

这个结果其实挺不错了。08e029d9b97ead339975c276eb890cab15dc2a4b.png

其它参数不变,加大金字塔层数,结果反而更差了,这是因为每次降采样都要丢失信息,如果降采样次数太多,会连特征点的特征信息都被丢弃了,所以说金字塔层数要选择合适,不能太大也不能太小。ad674756a0f93bb3fae8d7f42d4ddb9505356965.png

稠密光流380c2afcb463112da6a83a66fa229d1bccdd1252.png

5a83b41364093e1ff4a1fc2cd6de5e6a824a2322.png

0e03009b3f4128e78ccc18ab25ef2690177347ef.png

0eb2965b8d3dfeb04bec7ead80f39236ef3f508e.png

98c81dc5de3b6d8dfd1c7f63e2182156736b73d2.png

不过这两种方法似乎已经被opencv舍弃了,至少3.4.2里面我没找到这几个函数,好吧原来上面那本书是基于opencv1的。上面的Lucas-Kanade光流方法的特征点用的比较少。opencv还提供了另外一种算法来找到稠密光流,它计算这一帧的所有点的光流,用的方法是基于Gunner Fareback的算法,这种算法用的是基于多项式展开的两帧运动估计。206867b669c4f40f30544f34fed37042bdc3e3e5.png

好吧,我重新找了学习opencv3的电子书,不过没找到中文版的。看一下里面对这个calcOpticalFlowFarneback方法的描述:

多项式展开算法基于把图像估计为一个连续平面的分析技术来尝试计算光流。当然,真正的图像是离散的,所以Farneback方法添加一些额外的复杂度来让我们可以把基本方法应用到真正的数字图像中去。Farneback算法的基本思想是在图像的每个点用分段多项式拟合的方法来估计图像(虽然我感觉应该是插值啊,但是用的是fit这个单词)。

这个算法的名字是从算法的第一步操作得来的,第一步是用二次多项式在每个点附近进行拟合。多项式是基于点附近的窗口来估计的,这个窗口的每个位置有相应的权重,这是为了让拟合对离窗口中心的点更敏感。所以,窗口的尺度决定了算法敏感的特征的尺度。理想情况下,图像可以按照光滑连续函数来对待。6610912fe93dde6cefd93d5c49b450575a1fe0f7.png

如果把图像中两个位置的像素值互换,会导致在同一点多项式展开系数的变化。从这个变化

我们可以计算得到这个置换导致的一个幅值。当然,这对点的互换应该位置比较近才有意义。然而,有一个技巧可以处理很远的互换。ed7e60cca46435c8e5cd247f2a3c4edb51cef1b1.png

一维情况下:左边到右边的图,只是稍微换了一下中心点,中心点位置用虚线标出,两张都用二次曲线拟合,离中心点越近的点,和拟合出来的二次曲线越契合。由于中心点的移动,二次曲线发生了变化,系数关系是:a2=a1,b2=b1-2a1*d,c2=a1*d^2-b1*d+c1。d是中心点移动的距离。用这些关系,可以解出关于d的表达式,见上图。d574dca0c3645be7bb4a35958c92b11cdb56db94.png

关于这个技巧,首先,如果你知道一些有关这个置换的信息,也就是我们可以预估一个光流,那么你可以不在同一个点比较两幅图像的拟合系数,可以在那些你预估的地方比较。这种情况下,根据上面的分析技术,我们可以计算出一个比较小的修正值来修正预估值。实际上,这种机制有助于减少迭代次数。这种方法其实用于比较远的点的置换。其实还是用图像金字塔,先用分辨率比较低的,降采样很多次的结果,然后在用上一层降采样的结果。和上面Lucas-Kanade用金字塔的方法一样。1c1b1872e36f310fd5a6eb6d093d1601fcc88f2e.png

2b9ee717ad3ed6a00d6c3fd169f6026898dcda19.png

参数含义参考https://blog.csdn.net/ironyoung/article/details/608849299a3f96e5c86d5532cddbe99fad34072129b98952.png

4就是每次缩小的宽高的倍数,0.5是典型值,8就是每个窗口的权值要用的高斯分布参数,99bd3e42ebc4d1ee4e421dc3e68fb53638cb7adc.png

关于flags,上面一种是用输入的作为估计值,下面是用高斯核对原来的估计值的窗口那里滤波一下,这种方法比较慢,高斯窗口的值要大一些为了保证鲁棒性。用例子中的参数,修改层数为6,74b1f41ee5101101c2a196c633a608eb278b6738.png

这个确实很花费时间。flow就是从上一帧到这一帧每个点对应的移动距离。

mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])

cartToPolar就是从直角坐标转极坐标的函数,以前也见过的,输出是幅值和相角(默认是弧度)。

hsv[...,0] = ang*180/np.pi/2

hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)

rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)

opencv这么处理是为了可视化,这样的话,颜色就代表移动的方向,ang是弧度,需要转成角度,然后亮度代表移动的速度,这种处理还是比较好的一种方式。404ddf1dfb9bd095860aac0d9f72dcb93ef9c269.png

例子里面,这个应该是用摄像头拍的,周围的景色不东,所以是黑色,这三个人的运动方向不同,所以颜色不同,亮度代表运动速度。1e412f7a93e55cc1845764560ae004d22c64987a.png

结果:70a4affa5e0aa1f760b673d0a4f7fe8416280d4a.png

其它还有很多光流法和目标跟踪方法,这里不在介绍,都在第17章里面。746a0d3e755eb2662bf446e8e600945ce775c7c4.png

a9a8988ed66fcf97f03a6ee95c87e74c72045cc4.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值