目录

前言

一、光流估计的核心原理

二、光流估计的计算流程

1. 特征提取:找到 “好跟踪” 的点

2. 光流计算:匹配帧间特征点

三、完整实现步骤(附代码)

1. 环境准备

2. 步骤 1:处理视频第一帧

3. 步骤 2:提取第一帧的特征点(角点)

4. 步骤 3:创建轨迹掩膜与颜色

5. 步骤 4:配置光流计算参数

6. 步骤 5:主循环 —— 跟踪特征点并绘制轨迹

7. 步骤 6:释放资源

五、总结


前言


在计算机视觉领域,运动目标跟踪是核心任务之一,而光流估计则是实现该任务的经典技术。它通过捕捉连续图像帧间像素的运动向量,让我们直观地 “看到” 物体的运动轨迹。本文将从原理到代码,手把手教你用 OpenCV 实现基于 Lucas-Kanade 金字塔光流的运动轨迹绘制,适合有基础 OpenCV 知识的开发者进阶学习。

一、光流估计的核心原理

光流估计并非凭空计算,其准确性依赖三个关键假设,这也是所有传统光流算法的理论基石:

1.亮度恒定假设
同一物体的像素亮度在连续帧间保持不变。这意味着,物体运动是帧间像素变化的唯一原因,排除了光照突变、物体反光等干扰因素(实际应用中需尽量控制光照环境)。
2. 小运动假设
物体在相邻两帧间的位移极小,不会出现 “瞬移” 情况。只有满足这一假设,才能通过像素灰度对位置的偏导数,近似计算像素的运动速度。
3. 空间一致性假设
场景中相邻的像素点,投影到图像平面后仍保持相邻关系,且这些相邻点属于同一物体,运动方向和速度一致。这一假设避免了孤立像素的异常运动对整体轨迹的干扰。

二、光流估计的计算流程


要实现运动轨迹绘制,需先明确光流估计的核心步骤 ——特征提取与光流计算,二者相辅相成:

1. 特征提取:找到 “好跟踪” 的点

光流计算无需跟踪所有像素,只需聚焦于易识别、易匹配的特征点(如角点、边缘)。这类点的灰度梯度大,在帧间变化中具有唯一性,跟踪稳定性更高。
OpenCV 中常用cv2.goodFeaturesToTrack()函数提取角点,该函数能过滤低质量点、避免点过于密集,为后续光流计算提供可靠输入。

2. 光流计算:匹配帧间特征点

拿到特征点后,需计算其在相邻帧间的运动向量。常用算法包括:

  • Lucas-Kanade 算法:稀疏光流算法,仅计算特征点的光流,速度快、适合实时场景;
  • Horn-Schunck 算法:稠密光流算法,计算所有像素的光流,精度高但速度慢;
  • 金字塔 Lucas-Kanade 算法:本文使用的优化版本,通过构建图像金字塔,解决了 “大位移” 场景下的跟踪失效问题,兼顾速度与精度(对应 OpenCV 函数cv2.calcOpticalFlowPyrLK())。

三、完整实现步骤(附代码)


结合上述原理,运动轨迹绘制的整体逻辑为:读取视频→提取首帧特征点→创建轨迹掩膜→循环跟踪特征点并绘制轨迹→显示结果。下面分步骤拆解实现细节。


1. 环境准备
 

首先确保安装 OpenCV 和 NumPy(若未安装,执行以下命令):

pip install opencv-python numpy

2. 步骤 1:处理视频第一帧

视频跟踪的起点是第一帧,需先读取第一帧并转换为灰度图(光流计算仅需单通道,减少计算量):

import cv2
import numpy as np# 1. 读取视频(替换为你的视频路径,支持avi/mp4格式)
cap = cv2.VideoCapture("test.avi")  # 示例视频路径,需根据实际修改# 2. 读取第一帧,判断是否读取成功
ret, old_frame = cap.read()
if not ret:print("视频读取失败!请检查文件路径或视频格式。")cap.release()  # 释放视频资源exit()# 3. 将第一帧转换为灰度图(光流计算需灰度图输入)
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)

3. 步骤 2:提取第一帧的特征点(角点)


使用cv2.goodFeaturesToTrack()提取高质量角点,参数需根据实际场景调整:

# 定义特征点检测参数
feature_params = dict(maxCorners=100,    # 最多提取100个角点(避免轨迹过多导致画面杂乱)qualityLevel=0.3,  # 角点质量阈值:仅保留质量≥最强角点质量×0.3的点minDistance=7      # 角点间最小欧氏距离:避免角点过于密集
)# 提取第一帧的角点(mask=None表示全图检测,无区域限制)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
# p0形状:(-1, 1, 2),即(角点数量,1,(x,y)坐标),例如(50,1,2)表示50个角点

4. 步骤 3:创建轨迹掩膜与颜色


为了不破坏原始视频帧,我们创建一个全零掩膜(与视频帧尺寸相同),专门用于绘制轨迹;同时为每个特征点分配随机颜色,便于区分不同轨迹:

# 创建与视频帧尺寸、通道数相同的全零掩膜(初始为黑色,后续绘制彩色轨迹)
mask = np.zeros_like(old_frame)# 为每个特征点生成随机RGB颜色(100个点×3通道,取值0-255)
color = np.random.randint(0, 255, (100, 3))  # 颜色数量与maxCorners一致

5. 步骤 4:配置光流计算参数

使用cv2.calcOpticalFlowPyrLK()前,需定义其参数,控制跟踪精度与速度:

# Lucas-Kanade金字塔光流参数
lk_params = dict(winSize=(15, 15),  # 搜索窗口大小:窗口越大,跟踪范围越广,但速度越慢maxLevel=2,        # 金字塔最大层级:2表示使用原始图+2层下采样图,解决大位移问题criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)# 迭代终止条件:迭代10次或误差小于0.03时停止,平衡精度与速度
)

6. 步骤 5:主循环 —— 跟踪特征点并绘制轨迹

这是核心环节:循环读取每一帧,计算光流、筛选有效特征点、绘制轨迹,并更新 “上一帧” 数据用于下次计算。

while True:# 1. 读取当前帧ret, frame = cap.read()if not ret:  # 若读取失败(如视频结束),退出循环break# 2. 将当前帧转换为灰度图frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 3. 计算光流:匹配上一帧特征点(p0)在当前帧的位置# p1:当前帧特征点坐标;st:跟踪状态(1=成功,0=失败);err:跟踪误差p1, st, err = cv2.calcOpticalFlowPyrLK(prevImg=old_gray,    # 上一帧灰度图nextImg=frame_gray,  # 当前帧灰度图prevPts=p0,          # 上一帧特征点nextPts=None,        # 输出当前帧特征点(设为None自动分配)**lk_params          # 光流参数)# 4. 筛选跟踪成功的特征点(仅保留st=1的点)good_new = p1[st == 1]  # 当前帧有效特征点good_old = p0[st == 1]  # 上一帧对应有效特征点# 5. 绘制轨迹:在掩膜上连接“上一帧点”与“当前帧点”for i, (new, old) in enumerate(zip(good_new, good_old)):# 提取坐标并转换为整数(像素坐标需为整数)x_new, y_new = new.ravel()  # ravel()将数组展平为一维x_old, y_old = old.ravel()# 转换为整数(避免绘图时因浮点数报错)x_new, y_new, x_old, y_old = map(int, [x_new, y_new, x_old, y_old])# 在掩膜上绘制线段:连接上一帧点与当前帧点mask = cv2.line(img=mask,          # 绘制目标:掩膜pt1=(x_new, y_new),# 当前帧点pt2=(x_old, y_old),# 上一帧点color=color[i].tolist(),  # 轨迹颜色(与特征点对应)thickness=2        # 线段粗细)# 6. 生成最终图像:将掩膜(轨迹)叠加到原始帧上result = cv2.add(frame, mask)  # 图像叠加,轨迹覆盖在视频上# 7. 显示结果cv2.imshow("Motion Trajectory", result)  # 显示带轨迹的视频cv2.imshow("Mask Only", mask)            # 单独显示轨迹掩膜(可选)# 8. 按键控制:按下Esc键(ASCII码27)退出循环k = cv2.waitKey(30) & 0xFF  # 等待30ms(控制视频播放速度)if k == 27:break# 9. 更新“上一帧”数据:为下一循环做准备old_gray = frame_gray.copy()  # 上一帧灰度图更新为当前帧p0 = good_new.reshape(-1, 1, 2)  # 上一帧特征点更新为当前帧有效点(调整形状)

7. 步骤 6:释放资源

循环结束后,需释放视频捕获对象与销毁 OpenCV 窗口,避免内存泄漏:

# 释放视频资源
cap.release()
# 销毁所有OpenCV窗口
cv2.destroyAllWindows()

运行结果如下:为一个视频检测人们的运动轨迹

五、总结


本文通过 OpenCV 实现了基于 Lucas-Kanade 金字塔光流的运动轨迹绘制,核心逻辑是 “提取特征点→跟踪特征点→绘制帧间连线”。该方法兼顾实时性与精度,适用于摄像头监控、车辆跟踪、机器人视觉等场景。
掌握光流估计后,可进一步探索更复杂的应用,如结合目标检测(如 YOLO)实现特定物体的轨迹跟踪,或使用稠密光流算法(如cv2.calcOpticalFlowFarneback())获取全像素运动信息。
希望本文能帮助你理解光流估计的实践逻辑,动手尝试调整参数,感受不同场景下的轨迹绘制效果吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/100498.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/100498.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/100498.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

InnoDB ACID实现:数据库可靠性的核心秘密

这段内容出自 MySQL 官方文档第 17.2 节《InnoDB 与 ACID 模型》,深入解释了 InnoDB 是如何实现 ACID 特性 的。ACID 是数据库系统中最核心的设计原则,确保数据在各种异常情况下依然可靠、一致、安全。 我们来逐部分解析并通俗理解:&#x1f…

CoolUtils Total Excel Converter:全能的 Excel 文件转换工具

一、软件简介 CoolUtils Total Excel Converter 是一款功能强大的 Excel 文件转换工具,专为高效处理和转换 Excel 文件而设计。它支持将 Excel 文件(包括 XLS 和 XLSX 格式)转换为多种常见的文件格式,如 PDF、CSV、HTML、TXT 等&…

告别静态图谱!TextSSL如何用「稀疏学习」实现更智能的文档分类?

文章链接:https://mp.weixin.qq.com/s/danmd9lSQpmck4tVsM37bQ 今天分享一篇将图神经网络应用于文本分类的创新模型——TextSSL。在传统的文档理解中,模型往往难以同时捕捉文本的局部句法细节和全局语义关联。针对这一挑战,TextSSL提出了一种…

开源商城mall项目功能评估与优化建议

项目地址:https://github.com/macrozheng/mall 开源项目是大多数程序员用来练手的最好途径,但是技术面和技术深度同样重要。一个商城项目能够称之为商城不光有基础的商品后台管理、移动端、支付管理,要打造一个全链路的生态系统,…

我的页面开发

我的页面开发 后端data\me_page.js我的页面静态数据module.exports () > {return {superCard: {beanCount: 1555,tips: "下单得5倍吃货豆,兑专享红包",},cards: [{label: "常用功能",size: 30,items: [{iconUrl: "/imgs/me_page/coupang.png"…

Java Swagger2 能显示页面但看不到一个接口

反复检查之后,发现问题出在的代码如下: ApiModelProperty(value "材料链接地址", example "{ApiHost}/storage/test.pdf")private String url; 结论:example的值包括了 { 和 } ,导致网页解析的JSON数据失败…

2025年- H143-Lc344. 反转字符串(字符串)--Java版

1.题目2.思路 方法一&#xff1a;比如有5个元素 s[0],s[1],s[2],s[3],s[4] 反转之后对应 s[4],s[3],s[2],s[1],s[0] 所以s[0]s[4], s[1]s[3] s[i]s[n-1-i] 方法2:双指针 left0,rights.length-1; 当left<right的时候&#xff0c;交换两个元素的位置&#xff0c;左指针左移&am…

微服务高可用流程讲解

如何理解从前端nginx到后端微服务高可用架构问题&#xff0c;下面从nginx、gateway、nacos、各个服务节点的角度讲解下应该如何进行高可用&#xff0c;比如nginx是前端向后端进行的负载均衡&#xff0c;也相当于均衡地向各个gateway网关进行请求&#xff0c;再由gateway网关拉取…

留个档,Unity,Animation控制相机,出现抖动的问题记录

起因是项目用了一段高度自定义的过程复杂的相机Animation&#xff0c;来控制虚拟相机位移旋转。 发现在不同的电脑上&#xff0c;出现了不同程度的抖动。 搜索过程中&#xff0c;发现关键词&#xff1a;World Origin Rebasing。 Unity 世界坐标使用 float&#xff08;单精度浮点…

组合对冲策略(外汇版)

在复杂多变的外汇市场中&#xff0c;投资者常常面临着汇率波动带来的风险。为了降低这种风险&#xff0c;对冲策略成为了一种有效的风险管理工具。以下将详细介绍三种组合对冲策略&#xff0c;它们分别是基于多货币正负相关对冲、区域性货币对冲以及全日元货币对冲的策略。①多…

GPT-5-Codex 正式发布:迈向真正的“自主编程”时代

在 Anthropic Claude 近期遭遇争议的同时&#xff0c;OpenAI 推出了其编程领域的王牌产品——GPT-5-Codex。这并非简单的模型升级&#xff0c;而是基于 GPT-5 专为“自主编程”&#xff08;Autonomous Programming&#xff09;场景深度优化的专用版本&#xff0c;标志着 AI 编程…

java面试:了解redis的集群么,怎么通过redis的集群来实现redis的高可用?

我们知道&#xff0c;为了帮助数据库缓解高并发的压力&#xff0c;我们会上reids缓存帮助数据库分摊&#xff0c;虽说常见场景的并发量还不足以让redis宕机&#xff0c;但假设出现了极高的并发场景&#xff0c;redis依旧是有宕机的可能的&#xff0c;毕竟单点部署的redis容易出…

氧气科技亮相GDMS全球数字营销峰会,分享AI搜索时代GEO新观

2025年9月16日&#xff0c;全球数字营销领域的年度盛会——GDMS&#xff08;Global Digital Marketing Summit&#xff09;在上海国家会展中心盛大举行。作为品牌数字化转型的风向标&#xff0c;本届峰会汇聚来自全球的CEO、CMO、CDO及营销领域高管&#xff0c;共同探讨AI驱动下…

搭建Gin通用框架

Gin Web 开发脚手架技术文档 项目概述 本项目是一个基于 Gin 框架的 Go Web 开发脚手架模板&#xff0c;提供了完整的项目结构、配置管理、日志记录、MySQL 和 Redis 数据库连接等常用功能集成。 项目结构 gindemo/ ├── gindemo.exe # 编译后的可执行文件 ├── g…

windows 平台下 ffmpeg 硬件编解码环境查看

环境&#xff1a; 1&#xff0c;nvidia 显卡 2&#xff0c;驱动安装 powershell 下 执行如下命令&#xff0c;出现GPU信息 说明驱动安装正常。 nvidia-smi 3&#xff0c;安装支持 NVENC 的 FFmpeg &#xff08;1&#xff09;Windows 下 编译 FFmpeg 需要 CUDA Toolkit &am…

08_多层感知机

1. 单层感知机 1.1 感知机① 线性回归输出的是一个实数&#xff0c;感知机输出的是一个离散的类。1.2 训练感知机 ① 如果分类正确的话y<w,x>为正数&#xff0c;负号后变为一个负数&#xff0c;max后输出为0&#xff0c;则梯度不进行更新。 ② 如果分类错了&#xff0c;y…

安卓实现miniLzo压缩算法

LZO官方源码 http://www.oberhumer.com/opensource/lzo 找到miniLZO点击Dowload miniLZO下载源码 http://www.oberhumer.com/opensource/lzo/download/minilzo-2.10.tar.gz demo源码(包含安卓) https://github.com/xzw421771880/MiniLzo_Mobile.git 1.代码部分 1.1.测试…

如何在ubuntu下用pip安装aider,解决各种报错问题

aider中文文档网站上给出的安装说明比较简单&#xff1a; https://aider.doczh.com/docs/install.html 但是在一个干净的ubuntu环境中按文档中的命令安装时&#xff0c;会报错&#xff0c;经过一番尝试之后&#xff0c;解决了报错问题&#xff0c;成功完成了安装。 成功安装执…

Kotlin flow详解

流式数据处理基础 Kotlin Flow 是基于协程的流式数据处理 API&#xff0c;要深入理解 Flow&#xff0c;首先需要明确流的概念及其处理方式。 流(Stream)如同水流&#xff0c;是一种连续不断的数据序列&#xff0c;在编程中具有以下核心特征&#xff1a; 数据按顺序产生和消费支…

DeepSeek V3 深度解析:MoE、MLA 与 GRPO 的架构革新

简介 DeepSeek&#xff08;深度求索&#xff09;是一家源自中国的人工智能公司&#xff0c;成立于2023年&#xff0c;总部位于中国杭州。前身是国内量化投资巨头幻方量化的子公司。公司专注于开发低成本、高性能的AI模型&#xff0c;致力于通过技术创新推动人工智能技术的普惠…