光流估计(可用于目标跟踪) - 教程

news/2025/10/10 12:39:45/文章来源:https://www.cnblogs.com/yxysuanfa/p/19132806

先讲解下什么是光流估计

结果展示

代码部分

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)
# 选择好的点(状态为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()  # 获取新点的坐标
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)
# 等待150ms,检测是否按下了Esc键(键码为27)
k = cv2.waitKey(150)
if k == 27:  # 按下Esc键,退出循环
break
# 更新旧灰度图和旧特征点
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)  # 重新整理特征点为适合下次计算的形状
# 释放资源
cap.release()
cv2.destroyAllWindows()

这段代码实现了一个基于 ​Lucas-Kanade 光流法​ 

的视频特征点追踪程序。它能实时跟踪视频中物体的运动轨迹。下面我来分步解释代码的各个部分。

代码逐行解释

1. 导入库与初始化
import numpy as np
import cv2
# 打开视频文件
cap = cv2.VideoCapture('test.avi')
# 随机生成颜色,用于绘制轨迹
color = np.random.randint(0, 255, (100, 3))
  • cv2.VideoCapture('test.avi'): 创建视频捕获对象,用于读取名为 'test.avi' 的视频文件。你也可以将其替换为 0 来调用摄像头进行实时追踪。
  • np.random.randint(0, 255, (100, 3)): 随机生成100种颜色(BGR格式),用于绘制不同特征点的运动轨迹。
2. 处理第一帧与特征点检测
# 读取视频的第一帧
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)
  • cap.read(): 读取视频的第一帧。ret 是布尔值,表示是否成功读取帧。
  • cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY): 将帧转换为灰度图,因为许多图像处理算法(包括光流)通常在灰度图像上操作。
  • feature_params: 一个字典,定义了特征点(角点)检测的参数:
    • maxCorners: 要检测的最大角点数量。
    • qualityLevel: 角点质量的阈值(0-1之间),值越小,检测到的角点越多。
    • minDistance: 角点之间的最小欧氏距离,有助于避免角点过于集中。
  • cv2.goodFeaturesToTrack(): 使用Shi-Tomasi角点检测方法在灰度图像中寻找适合跟踪的特征点。返回的是这些角点的坐标。
3. 初始化与光流参数设置
# 创建一个与当前帧大小相同的全零掩模,用于绘制轨迹
mask = np.zeros_like(old_frame)
# 定义Lucas-Kanade光流参数
lk_params = dict(winSize=(15, 15),  # 窗口大小
maxLevel=2)  # 金字塔层数
  • mask = np.zeros_like(old_frame): 创建一个与视频帧大小相同的黑色图像,后续用于绘制运动轨迹。
  • lk_params: 另一个参数字典,用于配置Lucas-Kanade光流算法:
    • winSize: 每个金字塔层级上用于搜索光流的窗口大小。较大的窗口能捕获更快的运动,但计算量更大。
    • maxLevel: 图像金字塔的层数。金字塔用于处理大位移运动,层数越高,能处理的位移越大,但计算也更复杂。0表示不使用金字塔。
4. 主循环:处理视频流与光流计算
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)
  • cap.read() 在循环中读取每一帧。
  • cv2.calcOpticalFlowPyrLK(): 这是核心函数,实现了Lucas-Kanade光流法​。它计算当前帧 frame_gray 中相对于前一帧 old_gray 的特征点 p0 的新位置 p1
    • p1: 新一帧中特征点的计算位置。
    • st: 状态数组(1表示该点的光流被成功找到,0表示未找到)。
    • err: 每个特征点的误差向量。
5. 筛选有效点与绘制轨迹
# 选择好的点(状态为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()  # 获取新点的坐标
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)
  • 只保留那些光流被成功追踪到的点 (st == 1)。
  • 遍历所有好的点,在 mask 图像上从旧位置到新位置绘制一条线段,线段的颜色是在初始化时随机生成的。
6. 显示结果与更新
# 显示掩模
cv2.imshow('mask', mask)
# 将掩模添加到当前帧上,生成最终图像
img = cv2.add(frame, mask)
# 显示结果图像
cv2.imshow('frame', 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)  # 重新整理特征点为适合下次计算的形状
  • cv2.imshow(): 显示纯轨迹图像 (mask)​​ 和叠加了轨迹的当前视频帧 (img)​
  • cv2.waitKey(150): 等待150毫秒,并检测键盘输入。如果按下 ​ESC键​ (ASCII码为27),则退出循环。
  • 更新​ old_gray 和 p0 为当前帧的灰度图和特征点位置,为下一次迭代做准备。这是关键步骤,它使得光流能够逐帧持续追踪。

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

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

相关文章

Redis 64字节分界线与跳表实现原理 - 实践

Redis 64字节分界线与跳表实现原理 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

使用建造者模式创建对象 - 教程

使用建造者模式创建对象 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&quo…

01-Mybatis实现分页查询(手写)

01-Mybatis实现分页查询(手写)$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");前端数据分页展示的特点mysql中分页的代码特点分页查询的规律原生的Mybatis…

详细介绍:K8s实践中的重点知识

详细介绍:K8s实践中的重点知识2025-10-10 12:29 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importa…

VUE---await的运用

通常在做VUE进行异步请求,都是使用常规的请求,然后在回调里面进行业务以及页面的渲染调整。 使用await能够简便很多,而且在循环里面使用await可以进行按顺序请求。 基础使用:在VUE组件里面,await 函数需要在 asyn…

新手报道

新手报道开通博客,旨在系统性地记录我的学习轨迹,包括对新技术(如Go语言、Rust)的探索、经典算法与数据结构的学习笔记,以及项目开发中遇到的核心问题与解决方案。我相信,“输出倒逼输入”是最好的学习方式。通过…

深入解析:Oracle等待事件的sequential read和scattered read

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

供应链业务架构设计概览

供应链业务架构设计概览供应链业务架构设计概览

深入解析:微服务通信:5大消息队列横向对比

深入解析:微服务通信:5大消息队列横向对比2025-10-10 12:06 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: bl…

深入解析:抖音私信助手私域用户触达私信群聊小工具小程序开源

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

VS Code保存.vue文件自动格式化标签的问题

<view class="input-item align-center flex"><view class="iconfont icon-user icon" /><inputv-model="loginForm.username"class="input"type="text&…

基于最小二乘(LS)信道估计的MATLAB实现

基于最小二乘(LS)信道估计的MATLAB实现,包含误码率(BER)计算和性能分析一、算法原理与流程 1. LS信道估计核心公式其中:\(Y_p[k]\):接收导频位置的频域信号 \(X_p[k]\):发送导频位置的频域信号2. 系统模型 \(Y…

让老弟做个数据同步,结果踩了 7 个大坑!

第一天上班,老板就交给你一个艰巨的任务……你是小阿巴,刚入职一家电商公司。 第一天上班,老板就交给你一个艰巨的任务:定期把公司的订单数据同步到数据分析仓库。 一听到数据同步这 4 个字,你立刻汗流浃背了。 …

2025焊接件加工制造厂家口碑最新推荐榜:实力工艺与市场口碑

2025焊接件加工制造厂家口碑最新推荐榜:实力工艺与市场口碑一、行业背景在制造业蓬勃发展的当下,焊接件加工作为基础且关键的环节,在众多领域都发挥着至关重要的作用。从机械制造、汽车工业到航空航天、建筑工程等,…

2025 年最新推荐!依托优质运输网络的国际搬家海运公司排行榜:覆盖澳洲多地家具海运需求澳洲/悉尼/墨尔本/大型家具海运公司推荐

2025 年国际搬家海运需求持续攀升,但市场乱象让用户面临诸多抉择难题。部分服务商运输网络覆盖狭隘,偏远地区用户难享上门服务;不少企业整合能力薄弱,用户需自行对接打包、仓储、清关等环节,沟通成本陡增;更有甚…

完整教程:计算机环境、用户与系统变量

完整教程:计算机环境、用户与系统变量pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mon…

aardio在控件事件里获取控件ui自身对象

aardio在控件事件里获取控件ui自身对象winform.wishRadio.oncommand = function(id,event){current_status = owner.text }

2025舒适轮胎最新推荐榜:卓越减震与静音性能的驾乘体验之选

2025舒适轮胎最新推荐榜:卓越减震与静音性能的驾乘体验之选行业背景在汽车工业蓬勃发展的当下,轮胎作为汽车与地面接触的唯一部件,其性能直接影响着车辆的安全性、舒适性和操控性。随着消费者对驾乘体验的要求不断提…

螺杆泵厂家最新推荐榜:高效耐用与技术创新实力解析

螺杆泵厂家最新推荐榜:高效耐用与技术创新实力解析在工业生产领域,螺杆泵作为关键流体输送设备,其性能表现直接影响生产效率和运营成本。随着制造业升级和技术进步,市场对螺杆泵的要求已从基础输送功能转向高效节能…

2025机械加工厂家实力排行榜:技术精度与供货效率权威测评

2025机械加工厂家实力排行榜:技术精度与供货效率权威测评随着制造业向高质量发展阶段迈进,机械加工行业正经历着深刻变革。在智能化、精密化的发展趋势下,企业对机械加工供应商的技术实力、生产精度和供货效率提出了…