光流估计
光流估计是一种用于检测图像序列中像素点运动的技术。它基于这样的假设:在连续的视频帧之间,一个物体的移动会导致像素强度的连续性变化。通过分析这些变化,光流方法可以估计每个像素点的运动速度和方向。
光流估计通常用于多种应用,包括:
- 运动检测:在视频监控中识别移动物体。
- 场景重建:帮助理解三维场景的结构。
- 视频压缩:通过预测帧间的移动减少需要存储的数据量。
- 增强现实:实时跟踪现实世界中的对象和场景。
实现光流估计的方法有很多,包括基于梯度的方法(如Lucas-Kanade方法)和基于匹配的方法(如块匹配)。这些方法各有优缺点,选择哪种方法取决于应用的具体需求和可用的计算资源。
光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪。
特点
-
亮度恒定:同一点随着时间的变化,其亮度不会发生改变。
-
小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
-
空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。
cv2.calcOpticalFlowPyrLK():
参数:
-
prevImage 前一帧图像
-
nextImage 当前帧图像
-
prevPts 待跟踪的特征点向量
-
winSize 搜索窗口的大小
-
maxLevel 最大的金字塔层数
返回:
-
nextPts 输出跟踪特征点向量
-
status 特征点是否找到,找到的状态为1,未找到的状态为0
完整代码
import numpy as np
import cv2
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().astype(int)
c,d = old.ravel().astype(int)
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(150) & 0xff
if k == 27:
break
# 更新
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2)
cv2.destroyAllWindows()
cap.release()
这段代码实现了一个基于光流法的运动跟踪示例使用 OpenCV 库来检测和跟踪视频中的特征点。以下是代码的主要思路和步骤:
主要思路
- 视频捕获:从视频文件中读取帧。
- 特征点检测:在第一帧中检测角点(特征点)。
- 光流计算:使用 Lucas-Kanade 方法计算特征点在连续帧之间的运动。
- 轨迹绘制:在视频帧上绘制特征点的运动轨迹。
- 显示结果:实时显示带有运动轨迹的帧。
详细步骤
-
导入库:
import numpy as np import cv2
- 导入 NumPy 和 OpenCV 库。
-
视频捕获:
cap = cv2.VideoCapture('test.avi')
- 使用
cv2.VideoCapture
打开视频文件test.avi
。
- 使用
-
设置参数:
-
角点检测参数:
feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7)
maxCorners
:最大角点数。qualityLevel
:角点质量水平,值越大表示特征点越强。minDistance
:角点之间的最小距离。
-
Lucas-Kanade 光流参数:
lk_params = dict(winSize=(15, 15), maxLevel=2)
winSize
:窗口大小,用于计算光流。maxLevel
:金字塔层数。
-
-
随机颜色生成:
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)
- 使用
cv2.goodFeaturesToTrack
检测第一帧中的特征点。
- 使用
-
创建掩膜:
mask = np.zeros_like(old_frame)
- 创建一个与帧大小相同的掩膜,用于绘制轨迹。
-
主循环:
while(True): ret, frame = cap.read() ...
- 在循环中读取视频的每一帧,直到视频结束或用户按下
Esc
键。
- 在循环中读取视频的每一帧,直到视频结束或用户按下
-
光流计算:
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
- 使用
cv2.calcOpticalFlowPyrLK
计算特征点在当前帧中的新位置。
- 使用
-
筛选有效点:
good_new = p1[st==1] good_old = p0[st==1]
- 根据状态数组
st
筛选出有效的特征点。
- 根据状态数组
-
绘制轨迹:
for i, (new, old) in enumerate(zip(good_new, good_old)): ...
- 遍历有效的特征点,绘制它们的运动轨迹和当前特征点。
-
显示结果:
cv2.imshow('frame', img)
- 显示带有轨迹的当前帧。
-
更新状态:
old_gray = frame_gray.copy() p0 = good_new.reshape(-1, 1, 2)
- 更新前一帧的灰度图像和特征点,以便在下一次迭代中使用。
-
释放资源:
cv2.destroyAllWindows() cap.release()
- 关闭所有 OpenCV 窗口并释放视频捕获对象。
这段代码实现了一个简单的运动跟踪应用,通过检测视频中的特征点并计算它们在连续帧之间的运动,绘制出特征点的轨迹。使用 Lucas-Kanade 光流法来跟踪特征点的移动,并通过 OpenCV 显示实时结果。