OpenCV 光流估计:从原理到实战

news2025/5/18 14:03:41

在计算机视觉领域,光流估计(Optical Flow Estimation)是一项至关重要的技术,它能够通过分析视频序列中图像像素的运动信息,捕捉物体和相机的运动情况。OpenCV 作为强大的计算机视觉库,为我们提供了高效实现光流估计的工具。本文将深入探讨光流估计的原理,并结合 OpenCV 代码进行实战演示,带你快速掌握这项技术。

一、光流估计原理

光流(Optical Flow)是指图像中像素点在相邻帧之间的运动矢量。其基本假设是:相邻帧之间物体的亮度恒定,并且相邻帧之间的运动较小。基于这两个假设,我们可以通过数学推导得到光流的约束方程。

假设在 \(t\) 时刻,图像中某点 \((x, y)\) 的亮度为 \(I(x, y, t)\) ,在 \(t + dt\) 时刻,该点运动到 \((x + dx, y + dy)\) ,亮度为 \(I(x + dx, y + dy, t + dt)\) 。根据亮度恒定假设,有 \(I(x, y, t) = I(x + dx, y + dy, t + dt)\) 。

利用泰勒展开,将 \(I(x + dx, y + dy, t + dt)\) 展开:

\( I(x + dx, y + dy, t + dt) = I(x, y, t) + \frac{\partial I}{\partial x}dx + \frac{\partial I}{\partial y}dy + \frac{\partial I}{\partial t}dt + \epsilon \)

由于 \(\epsilon\) 是高阶无穷小,可忽略不计,结合亮度恒定假设,得到:

\( \frac{\partial I}{\partial x}\frac{dx}{dt} + \frac{\partial I}{\partial y}\frac{dy}{dt} + \frac{\partial I}{\partial t} = 0 \)

令 \(u = \frac{dx}{dt}\) ,\(v = \frac{dy}{dt}\) ,分别表示 \(x\) 方向和 \(y\) 方向的光流速度,\(\frac{\partial I}{\partial x} = I_x\) ,\(\frac{\partial I}{\partial y} = I_y\) ,\(\frac{\partial I}{\partial t} = I_t\) ,则光流约束方程为:

\( I_xu + I_yv + I_t = 0 \)

然而,一个方程无法求解两个未知数 \(u\) 和 \(v\) ,因此需要额外的约束条件。常见的方法有基于梯度的方法(如 Lucas - Kanade 方法)和基于能量的方法(如 Horn - Schunck 方法) 。

1. Lucas - Kanade 方法

Lucas - Kanade 方法假设在一个小的窗口内,光流是恒定的。通过在一个小窗口内对多个像素点建立光流约束方程,利用最小二乘法求解超定方程组,得到该窗口内像素点的光流估计。

具体来说,对于一个 \(n \times n\) 的窗口,窗口内有 \(N = n^2\) 个像素点,每个像素点都满足光流约束方程 \(I_{xi}u + I_{yi}v + I_{ti} = 0\) (\(i = 1, 2, \cdots, N\))。将这些方程写成矩阵形式:

\( \begin{bmatrix} I_{x1} & I_{y1} \\ I_{x2} & I_{y2} \\ \vdots & \vdots \\ I_{xN} & I_{yN} \end{bmatrix} \begin{bmatrix} u \\ v \end{bmatrix} = \begin{bmatrix} -I_{t1} \\ -I_{t2} \\ \vdots \\ -I_{tN} \end{bmatrix} \)

记为 \(A\begin{bmatrix}u \\ v\end{bmatrix} = -b\) ,通过最小二乘法求解 \(\begin{bmatrix}u \\ v\end{bmatrix}\) :

\( \begin{bmatrix}u \\ v\end{bmatrix} = (A^TA)^{-1}A^T(-b) \)

2. Horn - Schunck 方法

Horn - Schunck 方法引入了全局平滑约束,将光流估计问题转化为一个能量最小化问题。定义能量函数:

\( E = \sum_{x,y}[(I_xu + I_yv + I_t)^2 + \alpha^2(|\nabla u|^2 + |\nabla v|^2)] \)

其中,\((I_xu + I_yv + I_t)^2\) 是数据项,保证光流满足光流约束方程;\(\alpha^2(|\nabla u|^2 + |\nabla v|^2)\) 是平滑项,\(\alpha\) 是平滑系数,用于控制平滑程度,\(|\nabla u|^2\) 和 \(|\nabla v|^2\) 分别表示 \(u\) 和 \(v\) 的梯度平方,使得光流在空间上更加平滑。

通过变分法求解能量函数的最小值,得到光流的迭代更新公式,不断迭代直到收敛。

二、OpenCV 中的光流估计实现

OpenCV 提供了多种光流估计算法的实现,下面以 Lucas - Kanade 方法和 Farneback 算法为例进行介绍。

1. Lucas - Kanade 稀疏光流

Lucas - Kanade 稀疏光流适用于跟踪少量特征点的运动。在 OpenCV 中,可以使用 cv2.calcOpticalFlowPyrLK() 函数来实现。

import cv2

import numpy as np

# 读取视频

cap = cv2.VideoCapture('your_video.mp4')

# 角点检测参数

feature_params = dict( maxCorners = 100,

qualityLevel = 0.3,

minDistance = 7,

blockSize = 7 )

# Lucas - Kanade光流参数

lk_params = dict( winSize = (15,15),

maxLevel = 2,

criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

# 随机颜色

color = np.random.randint(0,255,(100,3))

# 读取第一帧并检测角点

ret, old_frame = cap.read()

old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)

p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)

# 创建一个用于绘制的掩码图像

mask = np.zeros_like(old_frame)

while(1):

ret,frame = cap.read()

frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# 计算光流

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()

c,d = old.ravel()

mask = cv2.line(mask, (a,b),(c,d), color[i].tolist(), 2)

frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1)

img = cv2.add(frame,mask)

cv2.imshow('frame',img)

k = cv2.waitKey(30) & 0xff

if k == 27:

break

# 更新上一帧和上一帧的角点

old_gray = frame_gray.copy()

p0 = good_new.reshape(-1,1,2)

cv2.destroyAllWindows()

cap.release()

上述代码首先读取视频,使用 cv2.goodFeaturesToTrack() 函数检测第一帧中的角点作为跟踪的初始点。然后在每一帧中,通过 cv2.calcOpticalFlowPyrLK() 函数计算这些角点在下一帧中的位置,并绘制出角点的运动轨迹。

2. Farneback 稠密光流

Farneback 稠密光流可以计算图像中所有像素点的光流,得到一个稠密的光流场。在 OpenCV 中,使用 cv2.calcOpticalFlowFarneback() 函数实现。

import cv2

import numpy as np

cap = cv2.VideoCapture('your_video.mp4')

ret, prev_frame = cap.read()

prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)

while True:

ret, frame = cap.read()

if not ret:

break

frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# 计算Farneback光流

flow = cv2.calcOpticalFlowFarneback(prev_gray, frame_gray, None,

0.5, 3, 15, 3, 5, 1.2, 0)

# 计算光流的大小和方向

magnitude, angle = cv2.cartToPolar(flow[..., 0], flow[..., 1])

# 将角度映射到0-180度

angle = np.arctan2(flow[..., 1], flow[..., 0]) * 180 / np.pi

# 归一化光流大小

magnitude = cv2.normalize(magnitude, None, 0, 255, cv2.NORM_MINMAX)

# 转换为8位无符号整数

magnitude = np.uint8(magnitude)

# 生成颜色映射图像

color = cv2.applyColorMap(magnitude, cv2.COLORMAP_HSV)

# 将角度转换为HSV颜色空间的色调值

color[..., 0] = angle % 180

# 将HSV转换为BGR

color = cv2.cvtColor(color, cv2.COLOR_HSV2BGR)

cv2.imshow('Optical Flow', color)

if cv2.waitKey(1) & 0xFF == ord('q'):

break

prev_gray = frame_gray.copy()

cap.release()

cv2.destroyAllWindows()

上述代码通过 cv2.calcOpticalFlowFarneback() 函数计算每一帧与前一帧之间的稠密光流场,然后将光流的大小和方向转换为 HSV 颜色空间,再转换为 BGR 颜色空间进行可视化。

三、光流估计的应用场景与局限性

1. 应用场景

  • 运动目标跟踪:通过光流估计可以跟踪视频中物体的运动轨迹,在安防监控、自动驾驶等领域有广泛应用。例如,在安防监控中,跟踪人员或车辆的移动;在自动驾驶中,跟踪前方车辆和行人的运动。
  • 视频压缩:光流信息可以用于视频压缩算法中,减少相邻帧之间的冗余信息,提高压缩效率。
  • 动作识别:分析视频中人体或物体的动作,在人机交互、体育动作分析等方面发挥作用 。例如,在人机交互中,根据用户的动作执行相应的操作;在体育动作分析中,评估运动员的动作是否标准。

2. 局限性

  • 亮度恒定假设不满足:当光照条件发生剧烈变化时,光流估计的准确性会受到严重影响。例如,视频中突然开灯或关灯,会导致像素点的亮度发生突变,破坏亮度恒定假设。
  • 大运动目标:对于运动速度过快或运动幅度较大的物体,光流估计可能无法准确捕捉其运动信息。因为光流估计的基本假设是相邻帧之间的运动较小,大运动目标可能超出这个假设范围。
  • 遮挡问题:当物体发生遮挡时,被遮挡部分的光流无法准确估计。例如,在视频中一个物体被另一个物体遮挡,遮挡部分的像素点运动信息丢失,导致光流估计错误。

四、总结

本文详细介绍了光流估计的原理,包括 Lucas - Kanade 方法和 Horn - Schunck 方法,并结合 OpenCV 代码演示了稀疏光流(Lucas - Kanade 方法)和稠密光流(Farneback 算法)的实现过程。同时,分析了光流估计的应用场景和局限性。光流估计作为计算机视觉领域的重要技术,在众多领域都有着广泛的应用前景。通过学习和实践,我们可以更好地利用这项技术解决实际问题。

希望本文能帮助你深入理解 OpenCV 光流估计,如果你在实践过程中有任何问题或有新的想法,欢迎在评论区交流讨论!

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

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

相关文章

ZTE 7551N 中兴小鲜60 远航60 努比亚小牛 解锁BL 刷机包 刷root 展讯 T760 bl

ZTE 7551N 中兴小鲜60 远航60 努比亚小牛 解锁BL 刷机包 刷root 3款机型是一个型号,包通用, ro.product.system.modelZTE 7551N ro.product.system.nameCN_P720S15 #################################### # from generate-common-build-props # Th…

信息系统项目管理师高级-软考高项案例分析备考指南(2023年案例分析)

个人笔记整理---仅供参考 计算题 案例分析里的计算题就是进度、挣值分析、预测技术。主要考査的知识点有:找关键路径、求总工期、自由时差、总时差、进度压缩资源平滑、挣值计算、预测计算。计算题是一定要拿下的,做计算题要保持头脑清晰,认真读题把PV、…

产品经理入门(2)产品体验报告

产品体验报告大纲:重点在产品体验——优点。 1.产品概括 可以从各大平台搜产品介绍。 2.市场分析 按照产品方向分析各个指标——包括有效使用时间,市场规模等。 3. 用户分析——对用户通过各项指标画像。 4.产品体验——对各项功能与设计的体验。 5.报告总结

C43-指针与数组

一 定义一个指针变量指向数组 1.途径一:指向数组首元素的地址 代码示例: #include <stdio.h> int main() {int arr[3]{2,4,5};int *p;p&arr[0];printf("该数组的首元素是:%d",*p);return 0; }成果展示: 报错与总结: 给指针变量赋值时,未在数组首元素前输…

UDP--DDR--SFP,FPGA实现之ddr读写控制模块

DDR读写控制模块实现介绍 由于该模块接口数量较多&#xff0c;为了详细说明模块实现&#xff0c;采用文字流程进行介绍 上级模块传输数据到来捕捉数据有效上升沿传输写指令&#xff0c;写有效&#xff0c;写指令成功被下一级模块缓存&#xff0c;进行写地址一次读写长度&…

云计算与大数据进阶 | 26、解锁云架构核心:深度解析可扩展数据库的5大策略与挑战(上)

在云应用/服务的 5 层架构里&#xff0c;数据库服务层稳坐第 4 把交椅&#xff0c;堪称其中的 “硬核担当”。它的复杂程度常常让人望而生畏&#xff0c;不少人都将它视为整个架构中的 “终极挑战”。 不过&#xff0c;也有人觉得可扩展存储系统才是最难啃的 “硬骨头”&#…

AI Agent | Coze 插件使用指南:从功能解析到实操步骤

一、前言 在人工智能技术飞速发展的今天&#xff0c;低代码开发模式正成为构建智能应用的主流趋势。对于希望快速搭建 AI Bot 的开发者和业务人员而言&#xff0c;coze作为一款强大的低代码 AI 开发平台&#xff0c;凭借其高度模块化的插件体系脱颖而出。这些插件就像搭建智能…

MK米客方德SD NAND:无人机存储的高效解决方案

在无人机技术迅猛发展的当下&#xff0c;飞控系统的数据记录对于飞行性能剖析、故障排查以及飞行安全保障极为关键。以往&#xff0c;SD 卡是飞控 LOG 记录常见的存储介质&#xff0c;但随着技术的革新&#xff0c;新的存储方案不断涌现。本文聚焦于以 ESP32 芯片为主控制器的无…

【vscode】解决vscode无法安装远程服务器插件问题,显示正在安装

文章目录 现状分析采用VSIX离线安装第一步&#xff1a;离线下载插件包第二步&#xff1a;把下载好的插件文件上传到远程服务器上第三步&#xff1a;在windows下打开vscode&#xff0c;并链接远端&#xff0c;进行安装 现状分析 vscode无法远程安装扩展插件&#xff0c;显示正在…

【Spring】Spring的请求处理

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 欢迎评论交流&#xff0c;感谢您的阅读&#x1f604;。 目录 引言HTTP/HTTPS协议Spring Web与Spring Web MVCSpring WebFlux 自定义的TPC/IP协议FTP、S…

粒子群算法(PSO算法)

粒子群算法概述 1.粒子群优化算法&#xff08;Particle Swarm Optimization&#xff0c;简称PSO&#xff09;。粒子群优化算法是在1995年由Kennedy博士和Eberhart博士一起提出的&#xff0c;它源于对鸟群捕食行为的研究。 2.基本核心是利用群体中的个体对信息的共享从而使得整…

LLM智能体新纪元:深入解析MCP与A2A协议,赋能智能自动化协作

LLM智能体&#xff08;LLM agents&#xff09;是能够自主行动以实现特定目标的AI系统。在实际应用中&#xff0c;智能体能够将用户请求拆解为多个步骤&#xff0c;利用知识库或API获取数据&#xff0c;最终整合出答案。这让智能体相比于传统独立聊天机器人拥有更强大的能力——…

SAP学习笔记 - 开发豆知识01 - CDS SDK命令出乱码 (cds init CAP-Test03 --add java)

1&#xff0c;现象 安装完VSCode以及各种需要的插件&#xff08;比如SAP CDS Language Support&#xff09;&#xff0c;就可以做CAP开发。 用这个命令创建Project&#xff1a;cds init CAP-Test03 --add java 然后出来一个乱码错误 adding java The derived package name c…

(C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)(网页版预告)(html)(js)(json)

目录 前言&#xff1a; 源代码&#xff1a; product.h product.c fileio.h fileio.c main.c json_export.h json_export.c tasks.json idex.html script.js 相关步骤&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第五步…

进阶-数据结构部分:​​​​​​​2、常用排序算法

飞书文档https://x509p6c8to.feishu.cn/wiki/FfpIwIPtviMMb4kAn3Sc40ABnUh 常用排序算法 这几种算法都是常见的排序算法&#xff0c;它们的优劣和适用场景如下&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a;简单易懂&#xff0c;时间复杂度较高&…

25、DeepSeek-R1论文笔记

DeepSeek-R1论文笔记 1、研究背景与核心目标2、核心模型与技术路线3、蒸馏技术与小模型优化4、训练过程简介5、COT思维链&#xff08;Chain of Thought&#xff09;6、强化学习算法&#xff08;GRPO&#xff09;7、冷启动**1. 冷启动的目的****2. 冷启动的实现步骤****3. 冷启动…

LeetCode --- 156双周赛

题目列表 3541. 找到频率最高的元音和辅音 3542. 将所有元素变为 0 的最少操作次数 3543. K 条边路径的最大边权和 3544. 子树反转和 一、找到频率最高的元音和辅音 分别统计元音和辅音的出现次数最大值&#xff0c;然后相加即可&#xff0c;代码如下 // C class Solution {…

npm 报错 gyp verb `which` failed Error: not found: python2 解决方案

一、背景 npm 安装依赖报如下错&#xff1a; gyp verb check python checking for Python executable "python2" in the PATH gyp verb which failed Error: not found: python2 一眼看过去都觉得是Python环境问题&#xff0c;其实并不是你python环境问题&#xf…

初识Linux · IP协议· 下

目录 前言&#xff1a; 内网IP和公网IP 内网IP 公网IP 路由 前言&#xff1a; 前文我们介绍了IP协议的协议头&#xff0c;通过源码等方式我们理解了IP协议中的字段&#xff0c;比如8位协议&#xff0c;比如通过环回问题引出的8位最大生存时间&#xff0c;比如8位协议&…

JAVA的常见API文档(上)

游戏打包 注意API文档中的方法不需要记忆&#xff01;&#xff01; 了解之后如果需要可以查询API文档 对Math的方法总结&#xff1a; 运用刚学的Math方法加快代码的运行效率 可以减少循环次数 找规律&#xff1a; 发现因子有规律&#xff1a; 必定一个大于平方根&#xff0c;…