光流估计
光流估计
光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪。
三个前提条件:
- 亮度恒定:同一点随着时间的变化,其亮度不会发生改变。
- 小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
- 空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。
Lucas—Kanade算法
如何求解方程组呢?看起来一个像素点根本不够,在物体移动过程中还有哪些特性呢?
注意前提条件里的第三个空间一致还没有使用
例如对于一个n*n的像素范围点1和点2邻近和点3邻近...由空间一致得
最终可以表示为
但ATA 不一定可逆。由于可逆矩阵的特征值非0。
若ATA可逆,则λ1,λ2非0,联想到角点检测λ1,λ2都不靠近0时,即λ1,λ2都很大时,为角点
所以,可以利用判断是否是角点来判断是否可逆
当是角点是,成立。
步骤:
- 1.读入视频
- 2. 对视频的每一帧操作,转换为灰度图
- 3.检测角点
- 4 .创建掩膜
- 5.利用cv2.calcOpticalFlowPyrLK跟踪特征向量
参数:
- prevImage 前一帧图像- nextImage 当前帧图像
- prevPts 待跟踪的特征点向量
- winSize 搜索窗口的大小
- maxLevel 最大的金字塔层数
返回:
- nextPts 输出跟踪特征点向量
- status 特征点是否找到,找到的状态为1,未找到的状态为0
- 6.对于找到的特征点绘制轨迹
- 7.展示
- 8.更新旧特征点
cap = cv2.VideoCapture('test.avi') #角点检测所需参数 feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7) #lucas kanade参数 lk_params = dict(winSize=(15, 15), maxLevel=2) #随机颜色条 color = np.random.randint(0, 255, (100, 3)) #拿到第一帧图像 ret, old_frame = cap.read() old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) #退回所有检测特征点,需要输入图像,角点最大数量(效率),品质因子(特征值越大的越好,来筛选) #距离相当于这个区间有比这个角点强的,就不要这个弱的了 p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params) #创建一个mask mask = np.zeros_like(old_frame) while(True): 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) #st=1表示 good_new = p1[st == 1] good_old = p0[st == 1] # #绘制轨迹 # 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) #绘制轨迹 for i, (new, old) in enumerate(zip(good_new, good_old)): a = new.ravel() b = old.ravel() mask = cv2.line(mask, tuple(a), tuple(b), color[i].tolist(), 2) frame = cv2.circle(frame, tuple(a), 5, color[i].tolist(), -1) img = cv2.add(frame, mask) cv2.imshow('frame', img) k = cv2.waitKey(150) & 0xff if k == 27: break #更新 old_gray = frame_gray.copy() p0 = good_new.reshape(-1, 1,2) cap.release() cv2.destroyAllWindows()