光流估计是什么?
是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪。
基本原理
(1)亮度恒定:同一点随着时间的变化,其亮度不会发生改变。
(2)小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
(3)空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。
光流估计的计算过程主要分为两个步骤:
- 特征提取:在图像中选择合适的像素点作为特征点,通常选择具有较大亮度梯度的像素点(如边缘、角点等)。
- 光流计算:通过比较相邻两帧图像中特征点的亮度变化,计算出这些点的运动速度和方向。
代码步骤
- 初始化视频源:使用
cv2.VideoCapture
打开视频文件。import cv2 import numpy as np cap=cv2.VideoCapture('test.avi')
- 读取视频帧:读取视频的第一帧,并将其转换为灰度图像。
# 随机生成100个跟踪点的颜色 color=np.random.randint(0,255,(100,3)) ret,old_frame=cap.read() old_gray=cv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)
- 特征点检测:使用
cv2.goodFeaturesToTrack
在灰度图像中检测特征点。# 定义ShiTomasi角点检测的参数 feature_params=dict(maxCorners=100,qualityLevel=0.3,minDistance=7) # 使用ShiTomasi角点检测算法在第一帧中检测角点 p0=cv2.goodFeaturesToTrack(old_gray,mask=None,**feature_params)
- 创建掩膜:创建一个与视频帧相同大小的全零掩膜,用于绘制轨迹。
mask=np.zeros_like(old_frame)
- 光流估计:使用
cv2.calcOpticalFlowPyrLK
函数计算两帧之间的光流。# 定义Lucas-Kanade光流法的参数 lk_params=dict(winSize=(15,15),maxLevel=2) while (True): ret,frame=cap.read() if not ret: break frame_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) # 使用Lucas-Kanade方法计算两帧之间的光流 p1,st,err=cv2.calcOpticalFlowPyrLK(old_gray,frame_gray,p0,None,**lk_params)
- 绘制轨迹:在掩膜上绘制特征点的轨迹。
# 筛选出跟踪成功的点 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() # 获取旧点坐标 a,b,c,d=int(a),int(b),int(c),int(d) mask=cv2.line(mask,(a,b),(c,d),color[i].tolist(),2)# 绘制线段 cv2.imshow('mask',mask) img=cv2.add(frame,mask) cv2.imshow('frame',img)
- 更新帧和特征点:更新旧灰度图和旧特征点,为下一帧的光流估计做准备。
# 更新旧灰度图和旧特征点,为下一帧的光流估计做准备 old_gray=frame_gray.copy() p0=good_new.reshape(-1,1,2)
运行结果
完整代码
import cv2
import numpy as np
cap=cv2.VideoCapture('test.avi')
# 随机生成100个跟踪点的颜色
color=np.random.randint(0,255,(100,3))
ret,old_frame=cap.read()
old_gray=cv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)
# 定义ShiTomasi角点检测的参数
feature_params=dict(maxCorners=100,qualityLevel=0.3,minDistance=7)
# 使用ShiTomasi角点检测算法在第一帧中检测角点
p0=cv2.goodFeaturesToTrack(old_gray,mask=None,**feature_params)
mask=np.zeros_like(old_frame)
# 定义Lucas-Kanade光流法的参数
lk_params=dict(winSize=(15,15),maxLevel=2)
while (True):
ret,frame=cap.read()
if not ret:
break
frame_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
# 使用Lucas-Kanade方法计算两帧之间的光流
p1,st,err=cv2.calcOpticalFlowPyrLK(old_gray,frame_gray,p0,None,**lk_params)
# 筛选出跟踪成功的点
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() # 获取旧点坐标
a,b,c,d=int(a),int(b),int(c),int(d)
mask=cv2.line(mask,(a,b),(c,d),color[i].tolist(),2)# 绘制线段
cv2.imshow('mask',mask)
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)
cv2.destroyAllWindows()
cap.release()
总结
通过本次实验,我们了解了光流估计的基本原理和实现方法。使用OpenCV库中的函数,我们可以轻松地在视频中跟踪移动物体,并绘制其运动轨迹。光流估计在视频分析、目标跟踪、动作识别等领域有着广泛的应用。通过本次实验,我们不仅掌握了光流估计的技术,还为进一步的研究和应用打下了基础。