在这一章当中,html
光流是由物体或相机的运动引发的图像对象在两个连续帧之间的视在运动模式。它是2D矢量场,其中每一个矢量是一个位移矢量,显示点从第一帧到第二帧的移动。考虑下面的图片(图片提供:维基百科有关光流的文章)。python
它显示了一个连续5帧移动的球。箭头显示其位移矢量。光流在如下领域有许多应用:算法
- 运动结构
- 视频压缩
- 视频稳定...
光流在几个假设下工做:dom
考虑第一帧中的像素(检查一个新的维度,时间,在这里添加,以前咱们只处理图像,因此不须要时间)。它
在
时间以后的下一帧中按距离移动。因此,因为这些像素相同,强度不变,咱们能够说,ide
而后采用泰勒级数近似的右手边,去除经常使用项并除以获得如下等式:函数
哪里:编码
以上等式称为光流方程。在它中,咱们能够找到而且
它们是图像渐变。一样
是沿着时间的梯度。但
未知。咱们没法用两个未知变量解决这个方程。因此有几种方法能够解决这个问题,其中之一就是Lucas-Kanade。spa
咱们以前已经看到一个假设,即全部相邻的像素都会有类似的运动。卢卡斯 - 卡纳德方法在这点上须要3x3的补丁。全部9点都有相同的动做。咱们能够找到这9点。因此如今咱们的问题变成了求解9个有两个未知变量的方程,这些变量是超定的。使用最小二乘拟合法能够得到更好的解决方案。如下是最终的解决方案,它是两个方程 - 两个未知问题并解决获得解决方案。code
(检查逆矩阵与哈里斯角点检测器的类似性,它表示角点是更好的跟踪点。)orm
因此从用户的角度来看,想法很简单,咱们给出一些跟踪点,咱们收到这些点的光流向量。但也有一些问题。直到如今,咱们正在处理小的议案。因此当运动很大时就失败了。咱们再一次去金字塔。当咱们在金字塔上走时,小的运动被移除,大的运动变成小的运动。所以,在那里应用卢卡斯 - 卡纳德,咱们能够获得光流和规模。
OpenCV在一个函数cv2.calcOpticalFlowPyrLK()中提供了全部这些。在这里,咱们建立一个简单的应用程序来跟踪视频中的某些点。为了决定点,咱们使用cv2.goodFeaturesToTrack()。咱们采用第一帧,检测一些Shi-Tomasi角点,而后使用Lucas-Kanade光流迭代地跟踪这些点。对于函数cv2.calcOpticalFlowPyrLK(),咱们传递前一帧,前一点和下一帧。若是找到下一个点,它将返回下一个点以及一些状态值为1的值,不然为零。咱们迭代地将这些下一点做为下一步中的前几点。请参阅下面的代码:
import numpy as np import cv2 cap = cv2.VideoCapture('slow.flv') # params for ShiTomasi corner detection feature_params = dict( maxCorners = 100, qualityLevel = 0.3, minDistance = 7, blockSize = 7 ) # Parameters for lucas kanade optical flow lk_params = dict( winSize = (15,15), maxLevel = 2, criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) # Create some random colors color = np.random.randint(0,255,(100,3)) # Take first frame and find corners in it ret, old_frame = cap.read() old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params) # Create a mask image for drawing purposes mask = np.zeros_like(old_frame) while(1): ret,frame = cap.read() frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # calculate optical flow p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) # Select good points good_new = p1[st==1] good_old = p0[st==1] # draw the tracks for i,(new,old) in enumerate(zip(good_new,good_old)): a,b = new.ravel() c,d = old.ravel() mask = cv2.line(mask, (a,b),(c,d), color[i].tolist(), 2) frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1) img = cv2.add(frame,mask) cv2.imshow('frame',img) k = cv2.waitKey(30) & 0xff if k == 27: break # Now update the previous frame and previous points old_gray = frame_gray.copy() p0 = good_new.reshape(-1,1,2) cv2.destroyAllWindows() cap.release()
(这段代码并无检查下一个关键点的正确性,因此即便任何特征点在图像中消失了,光流也有可能找到可能看起来接近它的下一个点。应该在特定的时间间隔内检测点,OpenCV样本会出现这样一个样本,它每隔5帧就会找到一个特征点,并对所选择的光学流点进行后向检查samples/python2/lk_track.py
。
查看咱们获得的结果:
Lucas-Kanade方法计算稀疏特征集的光流(在咱们的例子中,使用Shi-Tomasi算法检测拐角)。OpenCV提供了另外一种算法来查找密集的光流。它计算帧中全部点的光流。它基于Gunner Farneback的算法,该算法在Gunner Farneback于2003年在“基于多项式展开的两帧运动估计”中进行了解释。
如下示例显示了如何使用上述算法找到密集的光流。咱们获得了一个带有光流矢量的双通道阵列。咱们发现它们的规模和方向。咱们对结果进行颜色编码以实现更好的可视化 方向对应于图像的色调值。大小对应于数值平面。请参阅下面的代码:
import cv2 import numpy as np cap = cv2.VideoCapture("vtest.avi") ret, frame1 = cap.read() prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY) hsv = np.zeros_like(frame1) hsv[...,1] = 255 while(1): ret, frame2 = cap.read() next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY) flow = cv2.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0) mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1]) 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) cv2.imshow('frame2',rgb) k = cv2.waitKey(30) & 0xff if k == 27: break elif k == ord('s'): cv2.imwrite('opticalfb.png',frame2) cv2.imwrite('opticalhsv.png',rgb) prvs = next cap.release() cv2.destroyAllWindows()
看到下面的结果:
OpenCV在密集光学流程中提供了更高级的示例,请参阅samples/python2/opt_flow.py
。
samples/python2/lk_track.py
。尝试了解代码。samples/python2/opt_flow.py
。尝试了解代码。参考:
http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html