现在我们要使用第二条假设,邻域内的所有点都有相似的运动。LucasKanade 法就是利用一个 3x3 邻域中的 9 个点具有相同运动的这一点。这样我们就可以找到这 9 个点的光流方程,用它们组成一个具有两个未知数 9 个等式的方程组,这是一个约束条件过多的方程组。一个好的解决方法就是使用最小二乘拟合。下面就是求解结果:
(有没有发现上边的逆矩阵与 Harris 角点检测器非常相似,这说明角点很适合被用来做跟踪)从使用者的角度来看,想法很简单,我们取跟踪一些点,然后我们就会获得这些点的光流向量。但是还有一些问题。直到现在我们处理的都是很小的运动。如果有大的运动怎么办呢?图像金字塔。我们可以使用图像金字塔的顶层,此时小的运动被移除,大的运动装换成了小的运动,现在再使用 Lucas-Kanade算法,我们就会得到尺度空间上的光流
import cv2 import numpy as np from matplotlib import pyplot as plt # import video # from video import presets cap = cv2.VideoCapture(0) feature_params = dict(maxCorners = 100,qualityLevel = 0.3,minDistance = 7,blockSize = 7) lk_params = dict( winSize = (15,15),maxLevel = 2,criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) color = np.random.randint(0,255,(100,3)) # ret, frame = capture.read() ret,old_frame = cap.read() ##读出旧的图片 old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params) ##角点检测 mask = np.zeros_like(old_frame) ##旧图片复制并全部清零 while(1): ret,frame =cap.read() frame_gray =cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) ##提供光流金子塔的点位置 good_new = p1[ st==1 ] ##选择最优的点 1表示已经找到的最优点 good_old = p0[st == 1] for i,(new,old) in enumerate(zip(good_new,good_old)): ##zip将打包成一个个元组,并打包成列表 enumerate再转化成枚举类型 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 old_gray = frame_gray.copy() p0 = good_new.reshape(-1,1,2) cv2.destroyAllWindows() cap.release()
以下为本人测试人脸从左到右移动的光流。因为坐着不能平移左右是条弧线