光流(Optical Flow)算法原理详见以及cv::calcOpticalFlowFarneback和cv::calcOpticalFlowPyrLK参数说明

光流(Optical Flow)是计算机视觉中的核心技术之一,主要用于估计图像序列中像素的运动,广泛用于运动检测、目标跟踪、视觉里程计、SLAM 等任务。
在这里插入图片描述


一、光流的基本假设(Brightness Constancy)

设一段时间内,图像亮度在像素点处保持不变,即:

I ( x , y , t ) = I ( x + Δ x , y + Δ y , t + Δ t ) I(x, y, t) = I(x + \Delta x, y + \Delta y, t + \Delta t) I(x,y,t)=I(x+Δx,y+Δy,t+Δt)

对上式进行泰勒展开,取一阶忽略高阶项,得:

I x ⋅ u + I y ⋅ v + I t = 0 I_x \cdot u + I_y \cdot v + I_t = 0 Ixu+Iyv+It=0

其中:

  • I x = ∂ I ∂ x I_x = \frac{\partial I}{\partial x} Ix=xI:x 方向梯度
  • I y = ∂ I ∂ y I_y = \frac{\partial I}{\partial y} Iy=yI:y 方向梯度
  • I t = ∂ I ∂ t I_t = \frac{\partial I}{\partial t} It=tI:时间导数
  • u = d x d t , v = d y d t u = \frac{dx}{dt}, v = \frac{dy}{dt} u=dtdx,v=dtdy:光流(像素速度)

这就是著名的光流约束方程(Optical Flow Constraint Equation)

I x u + I y v = − I t I_x u + I_y v = -I_t Ixu+Iyv=It


二、常见光流求解方法

1. Lucas-Kanade 光流(局部窗口法)

假设:邻域内所有像素点的光流 ( u , v ) (u,v) (u,v) 相同,在 N × N N \times N N×N 区域内建立约束系统:

在这里插入图片描述

写成正规方程:

A T A ⋅ [ u v ] = − A T b ⇒ [ u v ] = − ( A T A ) − 1 A T b A^T A \cdot \begin{bmatrix} u \\ v \end{bmatrix} = -A^T b \Rightarrow \begin{bmatrix} u \\ v \end{bmatrix} = -(A^T A)^{-1} A^T b ATA[uv]=ATb[uv]=(ATA)1ATb

要求 A T A A^T A ATA 可逆,即图像局部结构足够丰富(角点处求解稳定)。


2. Horn-Schunck 光流(全局光滑约束)

引入平滑项,最小化以下能量函数:

E ( u , v ) = ∬ [ ( I x u + I y v + I t ) 2 + α 2 ( ∣ ∇ u ∣ 2 + ∣ ∇ v ∣ 2 ) ] d x d y E(u, v) = \iint \left[ (I_x u + I_y v + I_t)^2 + \alpha^2 \left( |\nabla u|^2 + |\nabla v|^2 \right) \right] dxdy E(u,v)=[(Ixu+Iyv+It)2+α2(∣∇u2+∣∇v2)]dxdy

采用梯度下降或迭代法求解(偏耗时)。


三、OpenCV中的光流使用C++代码示例以及结果可视化

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void drawOptFlowMap(const Mat& flow, Mat& cflowmap, int step, const Scalar& color) {
    for (int y = 0; y < cflowmap.rows; y += step) {
        for (int x = 0; x < cflowmap.cols; x += step) {
            const Point2f& fxy = flow.at<Point2f>(y, x);
            line(cflowmap, Point(x, y), Point(cvRound(x + fxy.x), cvRound(y + fxy.y)), color);
            circle(cflowmap, Point(x, y), 1, color, -1);
        }
    }
}

int main() {
    Mat prev = imread("frame1.png", IMREAD_GRAYSCALE);
    Mat next = imread("frame2.png", IMREAD_GRAYSCALE);
    Mat flow;

    calcOpticalFlowFarneback(prev, next, flow,
                             0.5,     // pyr_scale
                             3,       // levels
                             15,      // winsize
                             3,       // iterations
                             5,       // poly_n
                             1.2,     // poly_sigma
                             0);      // flags

    Mat flow_vis;
    cvtColor(prev, flow_vis, COLOR_GRAY2BGR);
    drawOptFlowMap(flow, flow_vis, 16, Scalar(0, 255, 0));

    imshow("Farneback Flow", flow_vis);
    waitKey(0);
    return 0;
}

四 、cv::calcOpticalFlowFarneback和cv::calcOpticalFlowPyrLK参数说明

calcOpticalFlowPyrLK函数原型

void cv::calcOpticalFlowPyrLK(
    InputArray prevImg,              // 前一帧灰度图
    InputArray nextImg,              // 当前帧灰度图
    InputArray prevPts,              // 前一帧点坐标(vector<Point2f>)
    InputOutputArray nextPts,        // 当前帧点坐标(输出)
    OutputArray status,              // 跟踪成功标志(uchar,1: 成功,0: 失败)
    OutputArray err,                 // 每个点的误差(如残差)
    Size winSize = Size(21, 21),     // 每层金字塔窗口大小
    int maxLevel = 3,                // 最大金字塔层数
    TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),
    int flags = 0,                   // 额外标志(可选)
    double minEigThreshold = 1e-4    // 最小特征值阈值
);

参数解释

参数含义
prevImg前一帧图像(必须为单通道 CV_8U 或 CV_32F)
nextImg当前帧图像
prevPts需要跟踪的点(例如通过角点检测提取)
nextPts跟踪得到的新位置(如果 flags = OPTFLOW_USE_INITIAL_FLOW,将作为初始估计)
status输出每个点是否成功跟踪(uchar)
err每个点的残差或误差
winSize每层金字塔的光流搜索窗口大小
maxLevel金字塔层数(0 表示不使用金字塔)
criteria收敛条件(迭代次数与残差变化)
flags标志位,如 OPTFLOW_USE_INITIAL_FLOWOPTFLOW_LK_GET_MIN_EIGENVALS
minEigThreshold特征值小于此阈值的点会被认为跟踪失败

calcOpticalFlowFarneback函数原型

void cv::calcOpticalFlowFarneback(
    InputArray prev,               // 前一帧图像(灰度)
    InputArray next,               // 当前帧图像(灰度)
    OutputArray flow,              // 输出光流(CV_32FC2,每个像素的(x, y)光流向量)
    double pyr_scale,              // 金字塔缩放因子(如 0.5)
    int levels,                    // 金字塔层数
    int winsize,                   // 每层窗口大小(如 15)
    int iterations,                // 每层迭代次数
    int poly_n,                    // 多项式展开邻域大小(如 5)
    double poly_sigma,             // 高斯平滑标准差(如 1.1)
    int flags                      // 额外标志(如 OPTFLOW_USE_INITIAL_FLOW)
);

参数说明

参数说明
prev / next灰度图像,CV_8U 或 CV_32F
flow输出光流矩阵(类型为 CV_32FC2flow(y, x)[0] 为水平位移,[1] 为垂直位移)
pyr_scale每层金字塔图像的缩放比例,通常为 0.5
levels金字塔层数(例如 3~5)
winsize每层图像窗口大小,影响精度与鲁棒性(常用 15~25)
iterations每层迭代次数(3~5)
poly_n多项式邻域的大小(5 或 7)
poly_sigma高斯平滑标准差(通常 1.1)
flags可用 OPTFLOW_USE_INITIAL_FLOW(重用上一次光流),或 OPTFLOW_FARNEBACK_GAUSSIAN(使用高斯而非框形窗口)

五、光流法比较总结

方法特点优点缺点
Lucas-Kanade局部窗口法,线性方程组快速,适用于稀疏光流靠角点稀疏性,易受遮挡干扰
Horn-Schunck全局最小化,带平滑项稠密估计效果好迭代复杂,慢
Farneback多项式逼近+金字塔密集光流,性能稳定较慢
DeepFlow/PWCNet深度学习方法精度高,适应复杂变形需 GPU,模型大

推荐

  • 推荐读物:《Multiple View Geometry》、《Computer Vision: Algorithms and Applications》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云SLAM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值