当前位置: 首页 > news >正文

计算机视觉(opencv)实战二十七——目标跟踪 - 教程


光流法与特征点跟踪 —— 原理解析与代码实现

在计算机视觉中,目标跟踪是一个非常常见的任务。它的目标是:在一段视频中,自动地跟踪目标物体或者特征点的运动轨迹。本文通过一个实际的 OpenCV 代码示例,演示如何用 角点检测 + Lucas-Kanade 光流法 来跟踪视频中显著的特征点,并绘制出它们的运动轨迹。


一、核心原理

1. 特征点检测(Shi-Tomasi)

首先需要找到“容易被跟踪”的点。OpenCV 提供了 cv2.goodFeaturesToTrack 函数,它基于 Shi-Tomasi 角点检测算法,能够找到一幅图像中最稳定、最有代表性的角点。

  • 角点(Corner):图像局部区域的梯度变化显著的点。例如:十字路口、物体边缘的尖角、纹理丰富的区域。这类点在相邻帧中比较容易匹配。

  • Shi-Tomasi 算法:通过计算图像的二阶矩矩阵(结构张量),选择响应值最大的若干个点作为角点。

2. 光流法(Optical Flow)

当我们获得了角点,就需要在下一帧图像中找到它们的新位置。光流法的基本思想是:

  • 假设相邻两帧之间,像素的灰度值保持不变;

  • 在一个小的邻域窗口内,通过最小化误差来求解像素的新位置。

这里使用的是 Lucas-Kanade 光流法,它在小窗口内假设所有像素具有相同的运动,从而用最小二乘法快速解算出运动向量。

3. 金字塔光流法

为了应对较大位移,OpenCV 提供了 金字塔 Lucas-Kanade 光流法:先在低分辨率图像中计算粗略位移,再逐级在更高分辨率的图像中细化结果,从而提高算法的鲁棒性和准确性。


二、代码详解

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)

参数解释:

  • maxCorners=100:最多检测 100 个角点;

  • qualityLevel=0.3:只保留质量值大于 0.3 * 最大质量值的角点;

  • minDistance=7:确保角点之间的最小距离,避免聚集在一起。


光流法参数

lk_params = dict(winSize=(15, 15),
maxLevel=2)
  • winSize:搜索窗口大小,越大越能容忍较大位移,但会降低速度;

  • maxLevel:金字塔层数,2 表示在原图、缩小一半、再缩小一半的三层图像上计算。


主循环与光流计算

mask = np.zeros_like(old_frame)
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, nextPts=None, **lk_params)

这里使用 cv2.calcOpticalFlowPyrLK 计算光流,得到:

  • p1:新位置;

  • st:状态向量(1 表示跟踪成功,0 表示跟踪失败);

  • err:跟踪误差。


绘制轨迹

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()  # 获取新点的坐标 或者[a, b] = new
c, d = old.ravel()  # 获取旧点的坐标
a, b, c, d = int(a), int(b), int(c), int(d)  # 转换为整数
# 在掩模上绘制线段,连接新点和旧点
mask = cv2.line(mask, pt1=(a, b), pt2=(c, d), color=color[i].tolist(), thickness=2)
cv2.imshow(winname='mask', mat=mask)

每对跟踪到的特征点用一条彩色线段连接,形成运动轨迹。轨迹被累积到 mask 上,叠加到视频帧中显示。


更新特征点

img = cv2.add(frame, mask)
# 显示结果图像
cv2.imshow(winname='frame', mat=img)
# 等待150ms,检测是否按下了Esc键(键码为27)
k = cv2.waitKey(150)
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. 特征点丢失:如果某些点跟踪失败,需要重新检测角点。

  2. 遮挡问题:遮挡会导致跟踪失败,可结合对象检测动态更新特征点。

  3. 参数调整:根据视频分辨率和运动速度调整 winSizemaxLevel 以平衡精度和速度。

  4. 性能优化:在实时视频中,建议降低分辨率或使用多线程以提高帧率。


总结
本文完整解析了基于角点检测与 Lucas-Kanade 光流法的特征点跟踪方法,结合 OpenCV 代码演示了如何实现轨迹可视化。通过理解参数和原理,你可以灵活调整,应用在不同的计算机视觉任务中。

http://www.hskmm.com/?act=detail&tid=17328

相关文章:

  • P8367 [LNOI2022] 盒
  • 蓝桥杯 2025 省 B 题:画展布置 - 题解笔记
  • 二维坐标下的运算
  • Polar2025秋季个人挑战赛web-writeup
  • Day4
  • 题解:P12751 [POI 2017 R2] 集装箱 Shipping containers
  • 弱网配置
  • 绘制金融集团监控大屏的地图demo
  • 实用指南:《原神助手》开源神器:游戏体验大升级
  • 9-25
  • AT_agc021_d [AGC021D] Reversed LCS
  • adb shell 常用文件命令
  • Java文件编程
  • 自我介绍与规划
  • 软件工程学习日志2025.9.25
  • 从50ms到30ms:YOLOv10部署中图像预处理的性能优化实践 - 实践
  • redis实现分布式锁1
  • 完整教程:(13)GPS/无GPS转换
  • Transformer自回归关键技术:掩码注意力原理与PyTorch完整实现
  • 第四篇
  • PyTorch图神经网络(六)
  • Qwen多模态系列模型笔记—Qwen-VL
  • go 语法里变量前面增加、*区别
  • 历程回顾-(2024-2025)
  • CF Round 1053(2150 2151) 总结
  • 20250922_QQ_backdoor
  • 实用指南:【Java八股文】13-中间件面试篇
  • AT_agc012_d [AGC012D] Colorful Balls
  • 02、Python从入门到癫狂:函数与资料容器
  • 第二周第四天2.4