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

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已展示其商业化潜力。

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

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

相关文章:

  • 第六章 进阶06 读书群第一次团建
  • 五一去荣昌吃卤鹅?基于Java和天地图的寻找荣昌卤鹅店实践
  • 【AI】[特殊字符]生产规模的向量数据库 Pinecone 使用指南
  • 京东平台关键字搜索接口开发指南:Python实现与代码详解
  • Tailwind CSS 实战:基于 Kooboo 构建企业官网页面(一)
  • 线程函数库
  • Dify依赖管理poetry切换为uv
  • C语言 函数补充
  • 【差分隐私】basic primitive的含义
  • 【C++指南】告别C字符串陷阱:如何实现封装string?
  • 更智能的银行体验:生成式 AI 与语义搜索的实际应用
  • 深度剖析操作系统核心(第二节):从X86/ARM/MIPS处理器架构到虚拟内存、分段分页、Linux内存管理,再揭秘进程线程限制与优化秘籍,助你成为OS高手!
  • 开源项目实战学习之YOLO11:ultralytics-cfg-datasets-Objects365、open-images-v7.yaml文件(六)
  • 清理HiNas(海纳斯) Docker日志并限制日志大小
  • [原创](现代Delphi 12指南):[macOS 64bit App开发]:如何使用CFStringRef类型字符串?
  • 【Linux学习笔记】进程的fork创建 exit终止 wait等待
  • Graph Database Self-Managed Neo4j 知识图谱存储实践2:通过官方新手例子入门(未完成)
  • WebGL2简单实例
  • VsCode如何使用默认程序打开word Excel pdf等文件
  • 珍爱网:从降本增效到绿色低碳,数字化新基建价值凸显
  • 第二十届中国电影华表奖揭晓!完整获奖名单来了
  • 多家媒体及网红走进云南曲靖沾益:感受珠江源头
  • 全球首台环形CT直线加速器在沪正式开机,系我国自主研发
  • 上海2025年普通高等学校招生志愿填报与投档录取实施办法公布
  • 政治局会议:创设新的结构性货币政策工具,设立新型政策性金融工具,支持科技创新、扩大消费、稳定外贸等
  • 神二十成功对接空间站