光流法是计算机视觉领域中用于分析物体运动的经典算法,而 Lucas-Kanade(LK)光流法作为稀疏光流的代表,因其计算高效、易于实现的特点,被广泛应用于视频目标追踪、运动分析等场景。
一、实现流程
1.读取视频文件,获取第一帧并提取特征角点;
2.逐帧读取后续视频帧,计算特征点的 LK 光流;
3.筛选出追踪成功的特征点,绘制特征点运动轨迹;
4.实时展示追踪结果,支持手动退出。
二、总体代码
python
import numpy as np
import cv2
# 打开视频文件
cap = cv2.VideoCapture('test.avi')
#随机生成颜色,用于绘制轨迹
color = np.random.randint(0,255,(100,3))
#读取视频的第一顿
ret, old_frame = cap.read()
#将第一顿转换为灰度图像
old_gray = cv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)
# 定义特征点检测参数
feature_params=dict(maxCorners=100,#最大角点数量
qualityLevel=0.3,#角点质量的阀值
minDistance=7)#最小距离,用于分散角点
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)
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 # 获取新点的坐标或者[a,b]=new
c,d = old # 获取旧点的坐标
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)
# 等待150ms,检测是否按下了Esc键(键码为27)
k = cv2.waitKey(10)
if k == 27: # 按下Esc键,退出循环
break
# 更新旧灰度图和旧特征点
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2) # 重新整理特征点为适合下次计算的形状(38,2)-->(38,1,2)
cap.release()
cv2.destroyAllWindows()
三、模块解析
(1)视频初始化与特征点检测
python
# 打开视频文件
cap = cv2.VideoCapture('test.avi')
# 读取第一帧
ret, old_frame = cap.read()
# 转换为灰度图
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 检测特征角点
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
-
cv2.VideoCapture:打开视频文件 / 摄像头,支持本地视频(如 avi/mp4)或摄像头设备(参数传 0); -
goodFeaturesToTrack:Shi-Tomasi 角点检测算法,用于提取图像中具有代表性的特征点(角点),是 LK 光流的追踪基础。
(2)LK 光流计算
python
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
-
输入参数:前一帧灰度图、当前帧灰度图、前一帧特征点、空值(自动计算新特征点)、光流参数
-
输出参数:
-
p1:当前帧特征点的新坐标 -
st:追踪状态(1 表示追踪成功,0 表示特征点丢失) -
err:追踪误差(数值越小,追踪越准确)
-
(3)轨迹绘制与结果展示
python
# 筛选有效特征点
good_new = p1[st == 1]
good_old = p0[st == 1]
# 绘制轨迹
mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
img = cv2.add(frame, mask)
cv2.imshow('LK光流特征点追踪', img)
-
仅保留追踪成功的特征点(
st == 1),过滤丢失的点 -
cv2.line:在掩模上绘制新旧特征点的连线(轨迹) -
cv2.add:将轨迹掩模叠加到原始帧上,实现轨迹与视频画面的融合展示
(4)循环更新与资源释放
python
# 更新追踪基准
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)
# 释放资源
cap.release()
cv2.destroyAllWindows()
-
每次循环后更新 "前一帧" 为当前帧,"前一帧特征点" 为当前有效特征点
-
reshape(-1, 1, 2):将特征点格式从(N,2)转为(N,1,2),适配光流函数的输入要求 -
视频处理结束后,必须释放
VideoCapture资源并关闭所有 OpenCV 窗口