【实战】OpenCV+Python项目实战--光流估计

📅 2026/7/28 14:59:03
【实战】OpenCV+Python项目实战--光流估计
光流估计光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”根据各个像素点的速度矢量特征可以对图像进行动态分析例如目标跟踪。要求如下1亮度恒定同一点随着时间的变化其亮度不会发生改变。2小运动随着时间的变化不会引起位置的剧烈变化只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。3空间一致一个场景上邻近的点投影到图像上也是邻近点且邻近点速度一致。因为光流法基本方程约束只有一个而要求xy方向的速度有两个未知变量。所以需要连立n多个方程求解。Lucas-Kanade 算法如何求解方程组呢看起来一个像素点根本不够在物体移动过程中还有哪些特性呢要保证u可逆就ATA的特征值λ1和λ2都很大角点刚好满足这个情况所以一般都是利用角点来做。常用函数cv2.goodFeaturesToTrack(old_gray, maskNone, **feature_params)作用用于获得光流估计所需要的角点参数说明old_gray表示输入图片mask表示掩模feature_params:maxCorners100角点的最大个数可以小于该数,qualityLevel0.3角点品质,minDistance7即在这个范围内只存在一个品质最好的角点返回值为(n, 1, 2)的矩阵pl, st, err cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0,None, **lk_params)用于获得光流检测后的角点位置参数说明pl表示光流检测后的角点位置st表示是否是运动的角点err表示是否出错old_gray表示输入前一帧图片frame_gray表示后一帧图片p0表示需要检测的角点lk_paramswinSize表示选择多少个点进行u和v的求解maxLevel表示空间金字塔的层数2准备工作2.1 numpy.random.randintnumpy.random.randint(low, highNone, sizeNone, dtype‘l’)函数的作用是返回一个随机整型数范围从低包括到高不包括即[low, high)。如果没有写参数high的值则返回[0,low)的值。参数如下low: int 生成的数值最低要大于等于low。 hign None时生成的数值要在[0, low)区间内high: int (可选) 如果使用这个值则生成的数值在[low, high)区间。 size: int or tuple of ints(可选)输出随机数的尺寸比如size (m * n* k)则输出同规模即m * n* k个随机数。默认是None的仅仅返回满足要求的单一随机数。dtype: dtype(可选) 想要输出的格式。如int64、int等等np.random.randint(2,high10,size(2,3))array([[6,8,7],[2,5,2]])2.2 *args和 **kwargsdefaa(a,b,c):returnabc# 1print(aa(3,4,5))# 返回值为12# 2b[1,2,3]print(aa(*b))# print(aa(b)) # 错误# 返回值为6# “*”作用拆开数列’b’的数值作为位置参数并把这些位置参数传给函数’aa’来调用# 3b[5,3]a1print(aa(a,*b))# 9defxx(*args):print(*args)print(args)defyy(*args):returnargs*2xx(1,2,3)# 1 2 3 和 (1, 2, 3)print(yy(1,2,3))# (1, 2, 3, 1, 2, 3)defabc(x,*y):print(x)print(*y)abc(1,4,2)# 分别打印出1 和 4 2# “**”作用它unpack字典并将字典中的数据项作为键值参数传给函数。defxv(a,b,c):print(a)print(b)print(c)xv(1,**{b:1,c:2})# b和c都要对应函数defxc(**c):print(c)# print(**c) 报错xc(b1,c2)# {b: 1, c: 2}ddict(b3,c2)# 相当于是{b1, c2}xc(**d)# {b: 3, c: 2}# print(**d) # 单独的**d就会报错2.3 cv2.calcOpticalFlowPyrLK()参数prevImage 前一帧图像nextImage 当前帧图像prevPts 待跟踪的特征点向量winSize 搜索窗口的大小maxLevel 最大的金字塔层数返回nextPts 输出跟踪特征点向量status 特征点是否找到找到的状态为1未找到的状态为0err表示是否出错2.4 cv2.goodFeaturesToTrack()cv2.goodFeaturesToTrack(old_gray, maskNone, maxCorners,qualityLevel0.3,minDistance这个函数可以帮我们使用 Shi-Tomasi 方法获取图像中 N 个最好的角点如果你愿意的话也可以通过改变参数来使用 Harris 角点检测算法。通常情况下输入的应该是灰度图像。然后确定你想要检测到的角点数目。再设置角点的质量水平0到 1之间。它代表了角点的最低质量低于这个数的所有角点都会被忽略。最后在设置两个角点之间的最短欧式距离。根据这些信息函数就能在图像上找到角点。所有低于质量水平的角点都会被忽略。然后再把合格角点按角点质量进行降序排列。函数会采用角点质量最高的那个角点排序后的第一个然后将它附近最小距离之内的角点都删掉。按着这样的方式最后返回N 个最佳角点。3 代码实现importnumpyasnpimportcv2 capcv2.VideoCapture(test.avi)# 角点检测所需参数feature_paramsdict(maxCorners100,qualityLevel0.3,minDistance7)# lucas kanade参数lk_paramsdict(winSize(15,15),maxLevel2)# 窗口大小为15*15金字塔层数为2# 随机颜色条colornp.random.randint(0,255,(100,3))# 拿到第一帧图像ret,old_framecap.read()old_graycv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)# 返回所有检测特征点需要输入图像角点最大数量效率品质因子特征值越大的越好来筛选# 距离相当于这区间有比这个角点强的就不要这个弱的了p0cv2.goodFeaturesToTrack(old_gray,maskNone,**feature_params)# 寻找角点# 创建一个maskmasknp.zeros_like(old_frame)whileTrue:ret,framecap.read()# 这个是取的第二帧图像上面已经取出了第一帧图像frame_graycv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 需要传入前一帧和当前图像以及前一帧检测到的角点p1,st,errcv2.calcOpticalFlowPyrLK(old_gray,frame_gray,p0,None,**lk_params)# st1表示good_newp1[st1]print(p1.shape)# (n,1,2)print(good_new.shape)# (n, 2)good_oldp0[st1]# 绘制轨迹fori,(new,old)inenumerate(zip(good_new,good_old)):# new[692.99805 83.00432]a,bnew.ravel()# 或者[a, b] newc,dold.ravel()maskcv2.line(mask,(a,b),(c,d),color[i].tolist(),2)# python tolist()方法将数组或者矩阵转换成列表framecv2.circle(frame,(a,b),5,color[i].tolist(),-1)imgcv2.add(frame,mask)# 这个相加不会超出边界cv2.imshow(frame,img)kcv2.waitKey(150)0xffifk27:break# 更新old_grayframe_gray.copy()p0good_new.reshape(-1,1,2)cv2.destroyAllWindows()cap.release()