加强版第二十二章KTL光流法

news2025/1/23 3:57:42

光流法概述

  • 光流可以看成是图像结构光的变化或者图像亮度模式明显的移动
  • 分为稀疏光流与稠密光流
  • 基于相邻视频帧进行分析

相关API:calcOpticalFlowPyrLK

calcOpticalFlowPyrLK函数属于OpenCV库,用于光流计算。在C++ 中,函数签名大致如下:
void cv::calcOpticalFlowPyrLK(
    InputArray prevImg, InputArray nextImg,
    InputArray prevPts, InputArray& nextPts,
    OutputArray status, 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
);
2. 参数解释

• prevImg和nextImg

• 这两个参数是光流计算的基础,它们分别代表前一帧图像和当前帧图像。要求是单通道(灰度图)。

• 例如,在处理视频流时,prevImg是视频中的第n - 1帧的灰度图像,nextImg是第n帧的灰度图像。

• prevPts和nextPts

• prevPts是一个输入参数,用于存储前一帧图像中的特征点坐标。这些特征点通常是通过角点检测等方法预先提取出来的感兴趣的点,例如goodFeaturesToTrack函数提取的角点。

• nextPts是一个输入/输出参数。在输入时,它可以被初始化为和prevPts相同的形状和大小(在一些情况下,也可以提供一个初始猜测值)。在函数执行后,它会被更新为计算得到的当前帧中对应的特征点坐标。

• status

• 这是一个输出参数,以向量形式存在。它用于表示每个特征点的状态。

• 如果特征点成功被跟踪,对应的状态值为1;否则为0。例如,当一个特征点因为被遮挡或者运动到图像边缘等情况而无法被正确跟踪时,其状态就会被标记为0。

• err

• 同样是一个输出参数,也是向量形式。它存储计算每个特征点时的误差。

• 这个误差可以用于评估光流计算的准确性,或者用于筛选出误差较大的特征点,例如,在后续处理中,可以舍弃误差超过一定阈值的特征点。

• winSize

• 它表示光流计算的窗口大小。默认值是Size(21,21)。

• 这个窗口用于在图像上确定一个小区域,在这个小区域内假设像素点的运动是一致的,这是基于Lucas - Kanade算法的基本假设。窗口大小的选择会影响光流计算的精度和速度。较大的窗口可以处理更复杂的运动,但计算速度可能会变慢,并且可能会受到其他物体运动的干扰;较小的窗口计算速度快,但可能无法准确处理较大的位移。

• maxLevel

• 代表金字塔的层数。默认值是3。

• 图像金字塔是通过对原始图像进行多次下采样得到的一组图像序列,最底层是原始图像,上面每一层图像的尺寸是下一层的一半。在计算光流时,先从金字塔的顶层(尺寸最小的图像)开始计算,得到一个大概的光流估计,然后把这个估计作为下一层(尺寸更大的图像)计算的初始值,逐步细化光流估计,这样可以处理较大位移的情况。

• criteria

• 这是一个迭代终止条件。它由三部分组成:TermCriteria::COUNT(迭代次数)、TermCriteria::EPS(精度要求)和两者的组合。

• 例如,默认值TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.01)表示当迭代次数达到30次或者误差小于0.01时,停止光流计算。

• flags和minEigThreshold(在某些情况下使用)

• flags用于指定一些特殊的计算模式或选项,不过在一般情况下可以使用默认值0。

• minEigThreshold主要用于在计算过程中根据特征值来筛选点,在一些高级应用或者特定算法变体中可能会用到。

3. 返回值

• 在C++ 中,函数没有返回值,但会修改传入的nextPts、status和err参数,让用户能够获取到当前帧特征点的位置、状态和误差信息。

• 在Python中,函数返回nextPts(当前帧特征点坐标)、status(特征点状态)和err(特征点计算误差)这三个值。

4. 应用场景示例

• 假设我们要跟踪视频中一个移动的物体,首先通过goodFeaturesToTrack函数在视频的第一帧提取特征点(存储在prevPts)。然后,在每一帧之间使用calcOpticalFlowPyrLK函数。根据nextPts更新特征点的位置,根据status判断特征点是否成功跟踪,根据err评估跟踪的质量。通过这些信息,我们可以在视频帧上绘制出物体的运动轨迹,实现目标跟踪。

goodFeaturesToTrack

goodFeaturesToTrack是OpenCV中的一个函数,用于在图像中检测出具有显著特征的点,这些点可以用于后续的处理,比如目标跟踪、图像配准等操作。

1. 函数签名(以C++为例)

• 函数原型如下:
void cv::goodFeaturesToTrack(
    InputArray image,
    OutputArray corners,
    int maxCorners,
    double qualityLevel,
    double minDistance,
    InputArray mask = noArray(),
    int blockSize = 3,
    bool useHarrisDetector = false,
    double k = 0.04
);

2. 参数解释

• image

• 这是输入参数,必须是单通道(灰度图)。它是要在其中检测特征点的图像。例如,在一个视频处理场景中,可能是视频帧转换后的灰度图像。

• corners

• 输出参数,用于存储检测到的特征点坐标。坐标形式通常是(x,y)二维点的集合。

• maxCorners

• 它指定了要检测的最大特征点数。例如,如果设置为100,函数会尝试找出100个最佳的特征点(如果图像中有足够多的特征点)。

• qualityLevel

• 这是一个重要的参数,用于定义特征点的质量水平。它是一个介于0和1之间的数。

• 具体来说,函数会计算每个潜在特征点周围的最小特征值(如果不使用哈里斯角点检测)或者哈里斯响应值(如果使用哈里斯角点检测),只有当这个值大于qualityLevel乘以图像中该区域的最大特征值(或哈里斯响应值)时,这个点才会被考虑作为一个特征点。例如,设置qualityLevel = 0.01,表示只有那些特征值(或哈里斯响应值)大于图像中最大特征值(或哈里斯响应值)的1%的点才会被当作特征点。

• minDistance

• 用于指定两个特征点之间的最小距离。以像素为单位。

• 这个参数的目的是避免检测到过于密集的特征点。例如,设置minDistance = 10,则检测到的任意两个特征点之间的距离至少为10像素。

• mask(可选)

• 这是一个可选的输入参数,它是一个和image大小相同的单通道图像(通常是uint8类型),用于指定在图像的哪些区域进行特征点检测。

• 如果某个像素位置的mask值为0,则在这个位置不会检测特征点;如果mask值非0,则会在对应的位置考虑检测特征点。例如,可以使用一个二值图像,白色区域(非0值)表示允许检测特征点的区域,黑色区域(0值)表示禁止检测的区域。

• blockSize(可选)

• 这个参数主要用于计算特征点周围区域的大小。默认值是3。

• 当计算特征点的特征值(或哈里斯响应值)时,会考虑以这个点为中心,边长为blockSize的正方形区域内的像素。例如,blockSize = 3表示考虑一个3x3的像素区域。

• useHarrisDetector(可选)

• 这是一个布尔值,用于指定是否使用哈里斯角点检测算法。默认值是false,即不使用。

• 如果设置为true,则函数会使用哈里斯角点检测来计算特征点的质量,这种方法在某些情况下可能会检测到更鲁棒的角点。

• k(可选)

• 当useHarrisDetector为true时,这个参数会被用到。它是哈里斯角点检测算法中的一个系数,默认值是0.04。

3. 返回值

• 在C++中,函数没有返回值,但会将检测到的特征点存储在corners参数中。

• 在Python中,函数返回corners,即检测到的特征点坐标。

4. 应用场景示例

• 在目标跟踪场景中,首先使用goodFeaturesToTrack函数在视频的第一帧图像中检测出一些特征点。然后,利用光流法(如calcOpticalFlowPyrLK函数)跟踪这些特征点在后续视频帧中的位置,从而实现对目标物体的跟踪。



#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;
RNG rng(12345);
void draw_lines(Mat &frame, vector<Point2f> pts1, vector<Point2f> pts2);
int main(int argc, char** argv) {
    VideoCapture capture("D:/images/video/CarsDrivingUnderBridge.mp4");
    if (!capture.isOpened()) {
        printf("could not open the camera...\n");
    }
    namedWindow("frame", WINDOW_AUTOSIZE);
    Mat old_frame, old_gray;
    capture.read(old_frame);
    cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);
    vector<Point2f> feature_pts;
    vector<Point2f> initPoints;
    double quality_level = 0.01;
    int minDistance = 10;
    goodFeaturesToTrack(old_gray, feature_pts, 5000, quality_level, minDistance, Mat(), 3, false);//获取关键点位坐标
    Mat frame, gray;
    vector<Point2f> pts[2];
    pts[0].insert(pts[0].end(), feature_pts.begin(), feature_pts.end());//迭代
    initPoints.insert(initPoints.end(), feature_pts.begin(), feature_pts.end());
    vector<uchar> status;
    vector<float> err;
    TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.01);

    while (true) {
        bool ret = capture.read(frame);
        if (!ret) break;
        imshow("frame", frame);
        cvtColor(frame, gray, COLOR_BGR2GRAY);

        // calculate optical flow
        calcOpticalFlowPyrLK(old_gray, gray, pts[0], pts[1], status, err, Size(31, 31), 3, criteria, 0);
        size_t i = 0, k = 0;


        for (i = 0; i < pts[1].size(); i++) {
            // 距离与状态检测
            double dist = abs(pts[0][i].x - pts[1][i].x) + abs(pts[0][i].y - pts[1][i].y);
            if (status[i] && dist > 2) {
                pts[0][k] = pts[0][i];
                initPoints[k] = initPoints[i];
                pts[1][k++] = pts[1][i];
                int b = rng.uniform(0, 255);
                int g = rng.uniform(0, 255);
                int r = rng.uniform(0, 255);
                circle(frame, pts[1][i], 2, Scalar(b, g, r), 2, 8, 0);
            }
        }

以下是对这段代码的详细解释:

1. 变量声明与初始化
size_t i = 0, k = 0;
这里声明了两个无符号整型变量 i 和 k,并都初始化为 0。在后续的循环和相关操作中,i 通常用于遍历数组或者向量中的元素,k 更多地是作为一个新的索引来更新有效元素的存储位置,起到筛选和整理元素的作用。

2. 循环部分
for (i = 0; i < pts[1].size(); i++) {
    // 距离与状态检测
    double dist = abs(pts[0][i].x - pts[1][i].x) + abs(pts[0][i].y - pts[1][i].y);
    if (status[i] && dist > 2) {
        // 相关操作
    }
}
• 循环目的:
这是一个 for 循环,其目的是遍历 pts[1] 这个 vector<Point2f> 类型的容器(前面代码中应该是存储了经过光流计算后当前帧的特征点坐标等相关信息)中的每一个元素,以便对每个特征点进行一些后续的筛选和处理操作。循环条件 i < pts[1].size() 表示只要索引 i 小于 pts[1] 的元素个数,就会持续循环遍历。

• 距离与状态检测:
在循环内部,首先计算了一个距离变量 dist,它是通过计算当前帧(对应 pts[1] 中的元素)和前一帧(对应 pts[0] 中的元素)在同一索引位置 i 处的特征点坐标差值的绝对值之和来得到的。具体来说,abs(pts[0][i].x - pts[1][i].x) 计算了 x 坐标方向上的差值绝对值,abs(pts[0][i].y - pts[1][i].y) 计算了 y 坐标方向上的差值绝对值,两者相加得到特征点从 pts[0] 到 pts[1] 的整体位置变化距离。

然后有一个 if 条件判断 if (status[i] && dist > 2),这里涉及到两个判断条件:
- status[i]:status 是一个 vector<uchar> 类型的向量(在前面代码中应该是记录了每个特征点的跟踪状态,值为 1 表示跟踪成功,值为 0 表示跟踪失败),通过检查 status[i] 的值来确定当前索引 i 对应的特征点是否被成功跟踪。
- dist > 2:表示只有当特征点的位置变化距离大于 2 (这里的 2 可能是根据实际情况设定的一个阈值,用于筛选出有明显位移、相对更值得关注的特征点)时,才会进入后续的操作。

3. 满足条件后的操作

当满足上述 if 条件(即特征点跟踪成功且位置变化距离大于 2 )时,会执行以下操作:
pts[0][k] = pts[0][i];
initPoints[k] = initPoints[i];
pts[1][k++] = pts[1][i];
• 更新特征点数据:
这里把 pts[0]、initPoints 和 pts[1] 这三个 vector<Point2f> 类型的容器中,索引为 i 的元素(也就是满足条件的那个特征点相关的数据),赋值给它们各自索引为 k 的位置。相当于把这些有效特征点重新整理,按照新的索引 k 来存储,而 k++ 这个操作在完成赋值后会让 k 的值自增 1,为下一次满足条件的特征点数据存储做准备,这样就实现了筛选出有效特征点并更新存储它们的位置,去除了那些不符合条件的特征点(比如跟踪失败或者位移过小的特征点)在对应向量中的原有位置。
int b = rng.uniform(0, 255);
int g = rng.uniform(0, 255);
int r = rng.uniform(0, 255);
circle(frame, pts[1][i], 2, Scalar(b, g, r), 2, 8, 0);
• 可视化绘制操作:
首先,通过 rng.uniform(0, 255) 三次调用,利用之前初始化的随机数生成器 rng 分别生成了三个范围在 0 到 255 之间的随机整数 b、g、r,这三个值分别代表蓝色(Blue)、绿色(Green)、红色(Red)通道的颜色值,也就是生成了一个随机颜色。

然后,调用 circle 函数在 frame (从代码上下文来看应该是一个表示图像帧的 Mat 类型变量,可能是视频中的某一帧画面)上进行绘制操作。具体是以 pts[1][i] (也就是当前帧中满足条件的那个特征点坐标位置)为圆心,半径为 2 绘制一个实心圆,颜色使用刚才生成的随机颜色 Scalar(b, g, r),并且设置了圆的线条粗细等绘制参数(这里 2 表示线条粗细,8 和 0 等其他参数也与绘制的具体细节相关,比如 8 可能涉及到绘制的边界类型等情况)。通过这样的绘制操作,在图像帧上就可以直观地看到那些满足条件的特征点被标记出来了,方便后续查看和分析特征点的分布以及运动情况等。

总体来说,这段代码的核心功能是对经过光流计算后的特征点进行筛选,保留跟踪成功且有明显位移的特征点,同时更新相关数据存储并在图像帧上对这些有效特征点进行可视化标记,便于后续的目标跟踪等相关操作进一步利用这些信息。

        // update key points
        pts[0].resize(k);
        pts[1].resize(k);
        initPoints.resize(k);

        // 绘制跟踪线
        draw_lines(frame, initPoints, pts[1]);

        // 绘制跟踪
        imshow("KLT-demo", frame);

        char c = waitKey(50);
        if (c == 27) { // ESC
            break;
        }

        // update to old
        std::swap(pts[1], pts[0]);
        cv::swap(old_gray, gray);//这行代码使用了C++标准库中的std::swap函数,用于交换pts容器中索引为0和索引为1的两个元素。假设pts是一个std::vector或者其他支持随机访问且元素可交换的容器,pts[0]和pts[1]的内容会被交换。

        // re-init
        if (pts[0].size() < 40) {
            goodFeaturesToTrack(old_gray, feature_pts, 5000, quality_level, minDistance, Mat(), 3, false);
            pts[0].insert(pts[0].end(), feature_pts.begin(), feature_pts.end());
            initPoints.insert(initPoints.end(), feature_pts.begin(), feature_pts.end());
        }
    }
    capture.release();

    waitKey(0);
    destroyAllWindows();
    return 0;
}

void draw_lines(Mat &frame, vector<Point2f> pts1, vector<Point2f> pts2) {
    vector<Scalar> lut;
    for (size_t t = 0; t < pts1.size(); t++) {
        int b = rng.uniform(0, 255);
        int g = rng.uniform(0, 255);
        int r = rng.uniform(0, 255);
        lut.push_back(Scalar(b, g, r));
    }
    for (size_t t = 0; t < pts1.size(); t++) {
        line(frame, pts1[t], pts2[t], lut[t], 2, 8, 0);
    }
}

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

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

相关文章

MySQL(4)多表查询

引言&#xff1a;为什么需要多表的查询&#xff1f; A&#xff1a;提高效率&#xff0c;多线进行。 高内聚、低耦合。 一、多表查询的条件 1、错误的多表查询&#xff1a; SELECT employee_id,department_name FROM employees,departments; SELECT employee_id,department…

‘list‘ object has no attribute ‘mul‘

原来运行得好好的&#xff0c;突然出现错误&#xff1a;list object has no attribute mul &#xff0c;更换一个输入路径&#xff0c;又没问题&#xff0c;改一个路径&#xff0c;还是出现错误&#xff0c;很奇怪&#xff0c;后来又没有问题&#xff0c;记录一下。 参考这文…

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的旅行社在线预订与行程管理系统

开题报告 本文旨在设计研究与开发一个旅行社在线预订与行程管理系统。首先&#xff0c;本文介绍了旅行社在线预订与行程管理系统相关技术集成开发环境、软硬件环境&#xff0c;SpringBoot框架、Vue.js框架的基本原理和优势&#xff0c;以及在旅行社在线预订与行程管理系统开发…

HarmonyOS快速入门

HarmonyOS快速入门 1、基本概念 UI框架&#xff1a; HarmonyOS提供了一套UI开发框架&#xff0c;即方舟开发框架&#xff08;ArkUI框架&#xff09;。方舟开发框架可为开发者提供应用UI开发所必需的能力&#xff0c;比如多种组件、布局计算、动画能力、UI交互、绘制等。 方…

[论文阅读] (36)CS22 MPSAutodetect:基于自编码器的恶意Powershell脚本检测模型

《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座&#xff0c;并分享给大家&#xff0c;希望您喜欢。由于作者的英文水平和学术能力不高&#xff0c;需要不断提升&#xff0c;所以还请大家批评指正&#xff0c;非常欢迎大家给我留言评论&#xff0c;学术路上期…

SQL sever数据导入导出实验

1.创建数据库TCP-H &#xff08;1&#xff09;右键“数据库”&#xff0c;点击“新建数据库”即可 &#xff08;2&#xff09;用sql语言创建&#xff0c;此处以创建数据库DB_test为例&#xff0c;代码如下&#xff1a; use master;go--检查在当前服务器系统中的所有数据里面…

Vue平台开发三——项目管理页面

前言 对于多个项目的使用&#xff0c;可能需要进行项目切换管理&#xff0c;所以这里创建一个项目管理页面&#xff0c;登录成功后跳转这个页面&#xff0c;进行选择项目&#xff0c;再进入Home页面展示对应项目的内容。 一、实现效果图预览 二、页面内容 功能1、项目列表展…

PyCharm+RobotFramework框架实现UDS自动化测试- (四)项目实战0x10

1.环境搭建 硬件环境&#xff1a;CANoe、待测设备&#xff08;包含UDS诊断模块&#xff09; 2.pythonPyCharm环境 pip install robotframework pip install robotframework-ride pip install openpyxl pip install udsoncan pip install python-can pip install can-isotp3…

Qt基础项目篇——Qt版Word字处理软件

一、核心功能 本软件为多文档型程序&#xff0c;界面是标准的 Windows 主从窗口 拥有&#xff1a;主菜单、工具栏、文档显示区 和 状态栏。 所要实现的东西&#xff0c;均在下图了。 开发该软件&#xff0c;主要分为下面三个阶段 1&#xff09;界面设计开发 多窗口 MDI 程序…

USART_串口通讯轮询案例(HAL库实现)

引言 前面讲述的串口通讯案例是使用寄存器方式实现的&#xff0c;有利于深入理解串口通讯底层原理&#xff0c;但其开发效率较低&#xff1b;对此&#xff0c;我们这里再讲基于HAL库实现的串口通讯轮询案例&#xff0c;实现高效开发。当然&#xff0c;本次案例需求仍然和前面寄…

leetcode刷题记录(七十二)——146. LRU 缓存

&#xff08;一&#xff09;问题描述 146. LRU 缓存 - 力扣&#xff08;LeetCode&#xff09;146. LRU 缓存 - 请你设计并实现一个满足 LRU (最近最少使用) 缓存 [https://baike.baidu.com/item/LRU] 约束的数据结构。实现 LRUCache 类&#xff1a; * LRUCache(int capacity)…

C++ 类- 构造和析构

空类 class A {};空类大小&#xff1a; sizeof(A) 1编译器会默认生成 6 个成员函数&#xff1a; class A { public:A();//构造函数 - 完成对象初始化工作~A();//析构函数 - 完成对象的资源清理A(const A& a);//拷贝构造函数 - 使用同一类中之前创建的对象来初始化新创建…

ubuntu20.04有亮度调节条但是调节时亮度不变

尝试了修改grub文件&#xff0c;没有作用&#xff0c;下载了brightness-controllor&#xff0c;问题解决了。 sudo add-apt-repository ppa:apandada1/brightness-controller sudo apt update sudo apt install brightness-controller 之后在应用软件中找到brightness-contro…

macOS如何进入 Application Support 目录(cd: string not in pwd: Application)

错误信息 cd: string not in pwd: Application 表示在当前目录下找不到名为 Application Support 的目录。可能的原因如下&#xff1a; 拼写错误或路径错误&#xff1a;确保你输入的目录名称正确。目录名称是区分大小写的&#xff0c;因此请确保使用正确的大小写。正确的目录名…

包文件分析器 Webpack Bundle Analyzer

webpack-bundle-analyzer 是一个非常有用的工具&#xff0c;用于可视化和分析 Webpack 打包生成的文件。这使得开发者能够更好地理解应用的依赖关系、包的大小&#xff0c;以及优化打包的机会。以下是关于 webpack-bundle-analyzer 的详细介绍&#xff0c;包括它的安装、使用以…

【深度解析Java 20天速成】04_IDEA的安装与使用

【Why IDEA ?】 【注】JetBrains官方说明&#xff1a; 尽管我们采取了多种措施确保受访者的代表性&#xff0c;但结果可能会略微偏向 JetBrains 产品的用户&#xff0c;因为这些用户更有可能参加调查。 此外&#xff0c;2022年&#xff0c;某美国软件开发商在对近千名专业的Ja…

算法竞赛之差分进阶——等差数列差分 python

目录 前置知识进入正题实战演练 前置知识 给定区间 [ l, r ]&#xff0c;让我们把数组中的[ l, r ] 区间中的每一个数加上c,即 a[ l ] c , a[ l 1 ] c , a[ l 2] c , a[ r ] c; 怎么做&#xff1f;很简单&#xff0c;差分一下即可 还不会的小伙伴点此进入学习 进入正题 …

【HarmonyOS NEXT】华为分享-碰一碰开发分享

关键词&#xff1a;鸿蒙、碰一碰、systemShare、harmonyShare、Share Kit 华为分享新推出碰一碰分享&#xff0c;支持用户通过手机碰一碰发起跨端分享&#xff0c;可实现传输图片、共享wifi等。我们只需调用系统 api 传入所需参数拉起对应分享卡片模板即可&#xff0c;无需对 U…

小程序 -- uni-app开发微信小程序环境搭建(HBuilder X+微信开发者工具)

目录 前言 一 软件部分 1. 微信开发者工具 2. HBuilder X 开发工具 二 配置部分 1. 关于 HBuilder X 配置 2. 关于 微信开发工具 配置 三 运行项目 1. 新建项目 2. 代码编写 3. 内置浏览器 编译 4. 配置小程序 AppID获取 注意 四 实现效果 前言 uni-app开发小程…

Element修改表格结构样式集合(后续实时更新)

场景 修改前端Element组件el-table样式 实现 线表格 <div class"tablepro"><el-table:data"tableData":header-cell-style"{ textAlign:center}"class"tablepro-table"borderstyle"width: 100%;height:100%"&g…