OpenCv高阶(十)——光流估计

文章目录

  • 前言
  • 一、光流估计
  • 二、使用步骤
      • 1、导库读取视频、随机初始化颜色
      • 2、初始化光流跟踪
      • 3、视频帧处理循环
      • 4、光流计算与可视化
      • 5、循环控制与资源释放
      • 完整代码
  • 总结


前言

在计算机视觉领域,光流估计是捕捉图像序列中像素点运动信息的核心技术。它描述了图像中每个像素点在相邻帧间的运动方向和速度,能从静态图像帧中解析出动态变化。
光流估计基于像素亮度不变和运动平滑两个关键假设,通过数学建模解算出像素运动场,为视频分析、目标跟踪等任务提供基础。OpenCV 集成了稀疏光流、稠密光流等计算方法,广泛应用于无人机避障、电影特效等场景。
接下来,我们将深入解析光流估计原理、OpenCV 函数使用及实战应用,探索这项技术的奥秘。

一、光流估计

光流估计是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪。

光流估计的前提:
(1)亮度恒定:同一点随着时间的变化,其亮度不会发生改变。
(2)小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
(3)空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。

在这里插入图片描述
在这里插入图片描述

二、使用步骤

1、导库读取视频、随机初始化颜色

# 导入数值计算库和计算机视觉库
import numpy as np
import cv2# 创建视频捕获对象,读取测试视频
cap = cv2.VideoCapture('../data/test.avi')# 生成100种随机颜色,用于不同特征点的轨迹绘制
# 格式为(100,3)的数组,每个元素代表BGR颜色值
color = np.random.randint(0, 255, (100, 3))

2、初始化光流跟踪

# 读取视频第一帧
ret, old_frame = cap.read()# 将第一帧转换为灰度图像(光流算法需要灰度输入)
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)# 设置特征点检测参数字典
feature_params = dict(maxCorners = 100,      # 检测的最大特征点数量qualityLevel = 0.3,    # 特征点质量阈值(0-1,值越大质量越高)minDistance = 7        # 特征点之间的最小像素距离
)# 使用Shi-Tomasi算法检测角点特征
# goodFeaturesToTrack:在灰度图像中寻找适合跟踪的强角点
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          # 金字塔层数(0表示不使用金字塔)
)

3、视频帧处理循环

while True:# 读取新帧ret, frame = cap.read()# 视频结束或读取失败时退出循环if not ret:break# 将当前帧转换为灰度图像frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

4、光流计算与可视化

     # 使用Lucas-Kanade金字塔光流法计算特征点运动# p1:新帧中特征点位置# st:状态标记(1表示成功跟踪,0表示丢失)# err:跟踪误差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.ravel().astype(int)c, d = old.ravel().astype(int)# 在mask图像上绘制运动轨迹线# 参数:目标图像,起点,终点,颜色,线宽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)

5、循环控制与资源释放

    # 等待150ms并检测ESC按键(ASCII 27)k = cv2.waitKey(150)if k == 27:break# 更新前一帧数据old_gray = frame_gray.copy()  # 更新灰度图像# 更新特征点(只保留成功跟踪的点)# reshape(-1,1,2)保持与原始p0相同的维度结构p0 = good_new.reshape(-1, 1, 2)# 释放视频资源并销毁所有窗口
cv2.destroyAllWindows()
cap.release()

效果:

完整代码

# 导入必要的库
import numpy as np
import cv2# 创建视频捕获对象,读取视频文件
cap = cv2.VideoCapture('../data/test.avi')# 生成随机颜色数组,用于绘制不同特征点的轨迹(100种颜色)
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, # 特征点质量等级(0-1之间,越大质量越高)minDistance=7     # 特征点之间的最小欧氏距离
)# 使用Shi-Tomasi方法检测初始特征点(角点检测)
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         # 金字塔层数(0表示仅当前层)
)# 主循环处理视频帧
while True:# 读取新的一帧ret, frame = cap.read()if not ret:  # 如果读取失败(如视频结束)则退出循环break# 将当前帧转换为灰度图像frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 计算光流(Lucas-Kanade方法)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上绘制运动轨迹线(使用随机颜色)mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)# 将轨迹mask与当前帧叠加显示img = cv2.add(frame, mask)cv2.imshow('frame', img)cv2.imshow('mask', mask)  # 单独显示轨迹mask# 等待按键(150ms延迟),ESC键退出k = cv2.waitKey(150)if k == 27:break# 更新前一帧的灰度图像和特征点old_gray = frame_gray.copy()p0 = good_new.reshape(-1, 1, 2)  # 更新为当前帧的有效特征点# 释放资源并关闭所有窗口
cv2.destroyAllWindows()
cap.release()

总结

光流估计的应用:
视频增强与创作:光流不仅用于运动补偿压缩(如MPEG标准),还被应用于视频插帧(生成中间帧提升流畅度)和特效合成(如电影中动态背景替换),英伟达SDK已展示其商业化潜力。

医疗精准化:在超声影像中,光流估计校正探头移动导致的图像偏移,辅助心脏手术的实时导航;还可量化器官运动参数(如心肌应变率),为疾病诊断提供动态指标。

工业智能化升级:生产线中,光流实时监测机械臂运动轨迹,结合异常检测算法预防故障;在精密装配场景,通过微位移分析提升质检精度。

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

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

相关文章

AIGC实战之如何构建出更好的大模型RAG系统

一、RAG 系统核心架构解析 1. 检索模块深度优化 1.1 混合检索技术实现 技术原理:结合稀疏检索(BM25)与密集检索(DPR),通过动态权重分配提升检索精度。例如,在医疗领域,BM25 负责精…

Rust 学习笔记:函数和控制流

Rust 学习笔记:函数和控制流 Rust 学习笔记:函数和控制流函数(Function)语句和表达式带返回值的函数注释控制流if 表达式使用 else if 处理多个条件在 let 语句中使用 if循环loop从循环中返回值循环标签消除多个循环之间的歧义带 …

c#加密证件号的中间部分,改为*号

前言 使用场景:在我项目中,我需要给前端提供接口,所以我要吧证件号进行加密。例如:411421199510225612,这是一个身份证号,18为的,那么我加密完成之后就会是 411421********5612,类似…

存储新势力:助力DeepSeek一体机

宝子们,今天要给大家分享一个超酷的科技话题——各大厂商陆续推出的DeepSeek训推一体机方案。 【集成人工智能训推平台】 它就像是一个超级智能的大脑中枢,为各种复杂的AI任务搭建AI模型流水线。预置算法模版、训练框架、推理框架、模型任务调度和自动…

同样机身尺寸下伺服电机比无刷电机扭矩更大的原因

点击下面图片带您领略全新的嵌入式学习路线 🔥爆款热榜 88万阅读 1.6万收藏 在电机应用领域,伺服电机和无刷电机(BLDC)都是常见的动力源,但两者在性能上存在显著差异。尤其是在相同机身尺寸下,伺服电机的…

.dat 文件一般可以用什么打开

DAT文件是一种常见的文件格式,通常由多种应用程序生成。打开DAT文件的方法取决于其内容和生成它的软件。 使用文本编辑器 如果DAT文件是一个简单的文本文件,可以使用Windows的记事本或macOS的文本编辑器打开它。 右键点击文件 -> 选择“打开方式” -> 选择“记事本”…

Java实现加密(七)国密SM2算法的签名和验签(附商用密码检测相关国家标准/国密标准下载)

目录 一、国密标准中,关于SM2签名验签的定义二、SM2签名和验签的实现原理1. 前置知识2. 签名生成过程3. 验签过程4. 数学正确性证明5. 安全性与注意事项 三、带userId、不带userId的区别1. 核心区别2.算法区别(1) 哈希计算过程(2) 签名验签流程 四、Java代码实现1. …

Feign接口调用失败降级机制

是的,通过 FallbackFactory 实现的降级逻辑在 Feign 接口调用失败时会被触发,但需要注意以下关键点以确保降级生效: 一、代码有效性分析 降级逻辑是否生效? • 是的,当 Feign 调用 BaseServiceFeign 接口的 updateMoni…

React-JSX语法

1、React和Vue的区别 (1)设计理念:react是一个声明式UI库,强调的是函数式编程,学习难度较高,vue是渐进式框架,学习难度较低 (2)模板语法:react使用的是JSX语…

RocketMQ 主题与队列的协同作用解析(既然队列存储在不同的集群中,那要主题有什么用呢?)---管理命令、配置安装

学习之前呢需要会使用linux的基础命令 一.RocketMQ 主题与队列的协同作用解析 在 RocketMQ 中,‌主题(Topic)‌与‌队列(Queue)‌的协同设计实现了消息系统的逻辑抽象与物理存储分离。虽然队列实际存储在不同集群的 B…

三菱FX PLC频率采集

基于高速计数器,计算从X点输入方波个数,定时提取计数器值,换算得到频率。直接通过定时器数值判断来实现定时计数的精度不高,提高精度需要考虑定时中断方式。 初始化寄存器,通过M8235,M8236复位来选择C235&a…

一种专用车辆智能配电模块的设计解析:技术革新与未来展望

关键词:智能配电模块、STM32、CAN总线、电子开关、新能源汽车 引言:传统配电系统的痛点与智能化转型 传统配电系统依赖继电器和保险丝,存在体积大、寿命短、智能化低等缺陷(如图1)。而新能源汽车和无人驾驶技术对配电…

python——异常

1、定义 异常是在代码执行过程中发生的,它会影响到程序的正常运行。python程序不会自动来进行异常处理。python中常见异常父类:Exception。 2、常见异常 TypeError:类型错误异常。ValueError:值的异常。KeyError:键…

深入浅出Sentinel:分布式系统的流量防卫兵

引言 在当今的微服务架构和分布式系统中,服务间的依赖关系错综复杂,一个服务的故障可能会像多米诺骨牌一样引发整个系统的崩溃。如何有效地保护系统免受突发流量、不稳定依赖服务的影响,成为每个架构师和开发者必须面对的挑战。今天&#xf…

leetcode0106. 从中序与后序遍历序列构造二叉树-medium

1 题目:从中序与后序遍历序列构造二叉树 官方标定难度:中 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入…

【Pandas】pandas DataFrame rsub

Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象(如 DataFrame、Series 或标量)的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…

【信息系统项目管理师】高分论文:论人力资源管理与成本管理(医院信息系统)

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论文一、规划人力资源管理二、组建项目团队三、建设项目团队四、管理项目团队论文 一个完善的医院信息系统通常由上百个子系统构成,而这些系统随着医院发展需求逐步建设的,他们来源于不同厂家,基于不同的技…

【python】如何将python程序封装为cpython的库

python程序在发布时,往往会打包为cpython的库,并且根据应用服务器的不同架构(x86/aarch64),以及python的不同版本,封装的输出类型也是非常多。本文介绍不同架构指定python下的代码打包方式: 首…

Android 14 修改侧滑手势动画效果

涉及关键类 SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt 修改如下: 一,覆盖系统的默认手势效果 SystemUI/src/com/andro…