【opencv】教程代码 —video(2) optical_flow (稀疏光流、稠密光流)

news2025/3/10 5:32:04

1. optical_flow.cpp 稀疏光流

327118f3ebc8b7ffc68d1760fd32ff7b.png

62a05c48f5d9112269e05d4eae77878f.png

51be73ff14b7801fea9632f66d0711c8.png

#include <iostream> // 引入输入输出流库
#include <opencv2/core.hpp> // 引入OpenCV的核心功能模块
#include <opencv2/highgui.hpp> // 引入OpenCV的高级GUI模块,提供显示图像的功能
#include <opencv2/imgproc.hpp> // 引入OpenCV的图像处理模块
#include <opencv2/videoio.hpp> // 引入OpenCV的视频处理模块
#include <opencv2/video.hpp> // 引入OpenCV的视频分析模块


using namespace cv; // 使用OpenCV命名空间
using namespace std; // 使用标准命名空间


int main(int argc, char **argv) // 主函数
{
    const string about =
        "This sample demonstrates Lucas-Kanade Optical Flow calculation.\n" // 介绍这个程序,说它展示了如何计算Lucas-Kanade光流
        "The example file can be downloaded from:\n" // 提示样本文件可以从以下链接下载
        "  https://www.bogotobogo.com/python/OpenCV_Python/images/mean_shift_tracking/slow_traffic_small.mp4";
    const string keys =
        "{ h help |      | print this help message }" // 定义了一个帮助选项
        "{ @image | vtest.avi | path to image file }"; // 定义了一个输入参数,路径到视频文件,默认值为"vtest.avi"
    CommandLineParser parser(argc, argv, keys); // 使用命令行解析器来解析输入参数
    parser.about(about); // 设置关于信息
    if (parser.has("help")) // 如果有"help"参数
    {
        parser.printMessage(); // 打印帮助信息
        return 0; // 并退出程序
    }
    string filename = samples::findFile(parser.get<string>("@image")); // 获取输入视频文件的路径
    if (!parser.check()) // 检查解析的参数是否正确
    {
        parser.printErrors(); // 打印错误信息
        return 0; // 并退出程序
    }


    VideoCapture capture(filename); // 创建一个VideoCapture对象来捕捉视频
    if (!capture.isOpened()){ // 检查视频是否成功打开
        //error in opening the video input
        cerr << "Unable to open file!" << endl; // 如果没有打开成功,输出错误信息
        return 0; // 并退出程序
    }


    // Create some random colors
    vector<Scalar> colors; // 创建一个Scalar类型的向量,用于存储颜色
    RNG rng; // 随机数生成器
    for(int i = 0; i < 100; i++) // 生成100个随机颜色
    {
        int r = rng.uniform(0, 256); // 生成红色分量
        int g = rng.uniform(0, 256); // 生成绿色分量
        int b = rng.uniform(0, 256); // 生成蓝色分量
        colors.push_back(Scalar(r,g,b)); // 存储到颜色向量中
    }


    Mat old_frame, old_gray; // 声明两个Mat对象,用于存储前一帧的图像及其灰度图
    vector<Point2f> p0, p1; // 声明两个点的向量,用于存储角点的位置


    // Take first frame and find corners in it
    capture >> old_frame; // 从视频中获取第一帧图像
    cvtColor(old_frame, old_gray, COLOR_BGR2GRAY); // 将图像转换成灰度图
    goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04); // 寻找前100个强角点存入p0


    // Create a mask image for drawing purposes
    // 创建一个和视频帧相同大小的mask用于绘画目的
    Mat mask = Mat::zeros(old_frame.size(), old_frame.type()); 


    while(true){ // 循环处理视频的每帧图像
        Mat frame, frame_gray; // 每一帧图像及其灰度图


        capture >> frame; // 获取下一帧图像
        if (frame.empty()) // 如果获取的帧为空,即视频结束了
            break; // 跳出循环
        cvtColor(frame, frame_gray, COLOR_BGR2GRAY); // 将获取的帧转换成灰度图


        // calculate optical flow
        vector<uchar> status; // 一个标记向量,标记对于每个点,是否找到了光流
        vector<float> err; // 一个错误向量,存储了每一个点的误差
        TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03); // 设置迭代搜索算法的终止准则(10次迭代或误差小于0.03)
        calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria); // 计算光流,将结果存储于p1


        vector<Point2f> good_new; // 存储"流过"后的点
        for(uint i = 0; i < p0.size(); i++) // 遍历每一个点
        {
            // Select good points
            if(status[i] == 1) { // 如果该点的光流是良好的
                good_new.push_back(p1[i]); // 将其添加到good_new向量中
                // draw the tracks
                line(mask,p1[i], p0[i], colors[i], 2); // 在mask上画线追踪运动轨迹
                circle(frame, p1[i], 5, colors[i], -1); // 在当前帧上对运动点进行标记
            }
        }
        Mat img; // 声明一个Mat来存储结果图像
        add(frame, mask, img); // 将原图像和mask相加得到最终的图像


        imshow("Frame", img); // 显示图像


        int keyboard = waitKey(30); // 等待30ms或者某个按键被按下
        if (keyboard == 'q' || keyboard == 27) // 如果按下了'q'键或者Esc键
            break; // 跳出循环


        // Now update the previous frame and previous points
        old_gray = frame_gray.clone(); // 更新前一帧的灰度图
        p0 = good_new; // 更新角点
    }
}

这段代码的主要功能是使用OpenCV库来实现Lucas-Kanade光流算法的计算。程序首先读取一个视频文件,然后在视频的第一帧中找到角点使用这些角点计算每一帧之间的光流,从而追踪画面中的物体运动。追踪的过程中,程序对每个有效的点在画面上绘制标记,并通过颜色区分不同的轨迹。最终显示在窗口中的是这些运动轨迹的叠加效果。当按下'q'键或Esc键时,程序结束。

29271e84f8d884f75e9f6ae849cf5125.png

calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria);

9e6aa44cd095adb11c3a81f869b78315.png

2. optical_flow_dense.cpp 稠密光流

c54626959b667db9c97cd633dcf0f598.png

31a3ebd7bc547729b8b218bc087d7398.png

e4432f42e48c9cea98c19aa706483218.png

#include <iostream> // 引入输入输出流库
#include <opencv2/core.hpp> // 引入OpenCV核心功能模块头文件
#include <opencv2/highgui.hpp> // 引入OpenCV高级用户界面模块头文件
#include <opencv2/imgproc.hpp> // 引入OpenCV图像处理模块头文件
#include <opencv2/videoio.hpp> // 引入OpenCV视频输入输出模块头文件
#include <opencv2/video.hpp> // 引入OpenCV视频分析模块头文件


using namespace cv; // 使用OpenCV命名空间
using namespace std; // 使用标准命名空间


int main() // 主函数
{
    VideoCapture capture(samples::findFile("vtest.avi")); // 创建视频捕获对象并打开文件
    if (!capture.isOpened()){ // 如果视频文件没有成功打开
        cerr << "Unable to open file!" << endl; // 输出错误信息到标准错误
        return 0; // 退出程序
    }


    Mat frame1, prvs; // 定义两个Mat对象,分别用于存储第一帧和前一帧的图像
    capture >> frame1; // 从视频中读取第一帧
    cvtColor(frame1, prvs, COLOR_BGR2GRAY); // 将第一帧转换为灰度图像,存储在prvs中


    while(true){ // 循环处理视频中的每一帧
        Mat frame2, next; // 定义两个Mat对象,用于存储当前帧和转换后的灰度帧
        capture >> frame2; // 从视频中读取当前帧
        if (frame2.empty()) // 如果当前帧为空,表示视频结束
            break; // 跳出循环
        cvtColor(frame2, next, COLOR_BGR2GRAY); // 将当前帧转换为灰度图像


        Mat flow(prvs.size(), CV_32FC2); // 定义一个Mat对象,用于存储光流
        calcOpticalFlowFarneback(prvs, next, flow, 0.5, 3, 15, 3, 5, 1.2, 0); // 计算前一帧和当前帧之间的光流


        // 下面的代码块用于可视化光流
        Mat flow_parts[2]; // 定义一个Mat数组,用于分割光流的x和y分量
        split(flow, flow_parts); // 分割光流
        Mat magnitude, angle, magn_norm; // 定义幅度、角度和归一化幅度的Mat对象
        cartToPolar(flow_parts[0], flow_parts[1], magnitude, angle, true); // 将笛卡尔坐标转换为极坐标,获取幅度和角度
        normalize(magnitude, magn_norm, 0.0f, 1.0f, NORM_MINMAX); // 将幅度归一化到0到1之间
        angle *= ((1.f / 360.f) * (180.f / 255.f)); // 将角度转换到0到255之间的范围内(用于HSV颜色空间)


        //构建HSV图像
        Mat _hsv[3], hsv, hsv8, bgr; // 定向Mat对象,用于构建HSV图像和转换后的BGR图像
        _hsv[0] = angle; // 角度作为H通道
        _hsv[1] = Mat::ones(angle.size(), CV_32F); // 饱和度设置为1,全部是白色
        _hsv[2] = magn_norm; // 归一化后的幅度作为V通道
        merge(_hsv, 3, hsv); // 合并上面的三个通道来构建一个HSV图像
        hsv.convertTo(hsv8, CV_8U, 255.0); // 将32位float型的HSV图像转换为8位unsigned char型
        cvtColor(hsv8, bgr, COLOR_HSV2BGR); // 将HSV颜色空间的图像转换为BGR颜色空间


        imshow("frame2", bgr); // 在窗口中显示BGR图像


        int keyboard = waitKey(30); // 等待30ms的按键事件,若无事件则继续
        if (keyboard == 'q' || keyboard == 27) // 如果按键是'q'或者ESC键
            break; // 跳出循环,结束程序


        prvs = next; // 更新前一帧的图像
    }
}

该段代码是使用 OpenCV 图像处理库和视频分析库来实现对视频文件进行光流分析和可视化的程序。首先通过VideoCapture对象捕获视频文件中的帧,然后将每两帧之间的光流分析出来,并将光流的信息可视化为BGR颜色空间中的图像,最后在窗口中显示这些图像。通过键盘输入可以控制程序的结束。美观化显示的过程主要是将光流场的每个点的方向(角度)转换成颜色,速度(幅度)转换成颜色亮度来实现。这对于理解和分析视频中的运动模式非常有帮助。

3d8f6aae9370c8c28ccf721f3d78347e.png

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

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

相关文章

微信小程序python+uniapp高校图书馆图书借阅管理系统ljr9i

根据日常实际需要&#xff0c;一方面需要在系统中实现基础信息的管理&#xff0c;同时还需要结合实际情况的需要&#xff0c;提供图书信息管理功能&#xff0c;方便图书管理工作的展开&#xff0c;综合考虑&#xff0c;本套系统应该满足如下要求&#xff1a; 首先&#xff0c;在…

软考--软件设计师(软件工程总结1)

目录 1.定义 2.软件生存周期 3.软件过程&#xff08;即软件开发中遵循的一系列可预测的步骤&#xff09; ​编辑4.软件开发模型 5.需求分析&#xff08;软件需求分析&#xff0c;系统需求分析或需求分析工程&#xff09; 6. 需求工程 7.系统设计 8.系统测试 1.定义 软件…

VS2022使用属性表快速设置OpenCV工程属性

1.创建C控制台应用 2.配置工程 3.打开工程后,为工程添加属性表 打开属性管理器窗口,选择Debug|x64 然后右击选择添加新的项目属性表 并命名为opencv490_debug_x64 点击添加 Debug版本属性表添加成功 使用相同方法添加Release版本属性表 双击debug版本属性表并添加包含目录 添…

第四百四十二回

文章目录 1. 概念介绍2. 使用方法2.1 获取思路2.2 获取方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取屏幕相关参数"相关的内容&#xff0c;本章回中将介绍如何获取AppBar的高度.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

2024年腾讯云4核8G服务器性能可以满足哪些使用场景?

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

替代 Redis 和 Memcached:25 倍吞吐量! | 开源日报 No.213

dragonflydb/dragonfly Stars: 22.4k License: NOASSERTION Dragonfly 是一个内存数据存储&#xff0c;适用于现代应用工作负载&#xff0c;可替代 Redis 和 Memcached。与传统的内存数据存储相比&#xff0c;Dragonfly 提供了 25 倍的吞吐量、更高的缓存命中率和更低尾部延…

【Apache Doris】周FAQ集锦:第 2 期

【Apache Doris】周FAQ集锦&#xff1a;第 2 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户和…

Azure的VFP和虚拟IP地址

Azure 的Virtual filtering platform (VFP) 是Azure 网络地址转换,端口转换和端口分配的基础。 下面我们来深入介绍一下VFP的工作方式。 VFP的出站动作。 对于客户端地址作为虚拟IP的出站目的地址的时候,VFP 驱动会负责做以下两个动作。 源地址转换。端口地址转换。VFP 和 S…

flink1.18源码编译后standalone模式-master启动

1、编译成功后 2、准备运行环境 • 在项⽬根⽬录下&#xff0c;创建如下两个⽂件夹&#xff1a; • 找到如下⼦模块&#xff0c;并展开如图 • 将上图中conf下的⽂件&#xff0c;拷⻉到项⽬根⽬录下创建的conf下 • 将上图中的lib下的jar包&#xff0c;拷⻉到项⽬根⽬录下…

论文阅读:Walk These Ways: 通过行为多样性调整机器人控制以实现泛化

Walk These Ways: 通过行为多样性调整机器人控制以实现泛化 摘要&#xff1a; 通过学习得到的运动策略可以迅速适应与训练期间经历的类似环境&#xff0c;但在面对分布外测试环境失败时缺乏快速调整的机制。这就需要一个缓慢且迭代的奖励和环境重新设计周期来在新任务上达成良…

Cortex-M7 中断优先级

1 前言 除了Reset&#xff08;优先级-3&#xff09;,NMI&#xff08;优先级-2&#xff09; 和 HardFault&#xff08;优先级-1&#xff09;三个异常的有限制为固定值外&#xff0c;对于其他优先级可配置的异常&#xff0c;Cortex-M7最大支持0~255共计256个优先级。具体支持的优…

Mac电脑清理垃圾软件 Mac电脑清理垃圾的文件在哪 cleanMyMac X 4.8.0激活号码

Mac用户经常会有这样一些烦恼&#xff0c;比如软件之间的管理&#xff0c;应用生成的缓冲文件怎样删除&#xff0c;还有软件的卸载等等... 如何有效清理Mac中的垃圾文件&#xff0c;删除多余的软件成为Mac用户迫切的需求。本文就为大家介绍几款好用的Mac电脑清理垃圾软件&#…

Linux学习:进程(4)程序地址空间(笔记)

目录 1. Linux下各种资源的内存分布2. 物理地址与虚拟(线性)地址3. 程序地址空间的区域划分4. 地址映射与页表5. 缺页中断 1. Linux下各种资源的内存分布 2. 物理地址与虚拟(线性)地址 在有关进程创建的初步学习中&#xff0c;我们了解了fork函数创建子进程的方式。此种进程的创…

Acwing-石子合并

282. 石子合并 - AcWing题库 所需知识&#xff1a;区间dp 区间dp模板题。 区间dp常用模板&#xff1a; for (int len 1; len < n; len) { // 遍历区间的长度for (int i 1; i len - 1 < n; i) { // 枚举区间起点int j i len - 1; // 区间…

黑马java-JavaWeb-Maven

1.Maven是专门用于管理和构建java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构提供了一套标准化的构建流程提供了一套依赖管理机制&#xff08;管理项目所依赖的第三方资源&#xff09; 2.Maven仓库 本地仓库&#xff1a;自己计算机上的一个目…

Lvgl9 WindowsSimulator Visual Studio2017

因为在操作过程中遇到了一些错误&#xff0c;所以将操作及解决问题的过程记录下来。 一、下载lv_port_pc_visual_studio github链接:GitHub - lvgl/lv_port_pc_visual_studio: Visual Studio projects for LVGL embedded graphics library. Recommended on Windows. Linux su…

代码随想录阅读笔记-二叉树【验证二叉搜索树】

题目 给定一个二叉树&#xff0c;判断其是否是一个有效的二叉搜索树。 假设一个二叉搜索树具有如下特征&#xff1a; 节点的左子树只包含小于当前节点的数。节点的右子树只包含大于当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 思路 要知道中序遍历下&#x…

金融中的数学知识

随机偏微分方程相比普通偏微分方程具有额外的随机项&#xff0c;反映了其描述的现象具有随机性质

认识 Redis 与 分布式

Redis 官网页面 Redis官网链接 Redis 的简介 Redis 是一个在内存中存储数据的中间件 一方面用于作为数据库&#xff0c;另一方面用于作为数据缓存&#xff0c;适用于分布式系统中 Redis 基于网络&#xff0c;进行进程间通信&#xff0c;把自己内存中的变量给别的进程&#xf…

Redis中的复制功能(五)

心跳检测 概述 在命令传播阶段&#xff0c;从服务器默认会以每秒一次的频率&#xff0c;向主服务器发送命令: REPLCONF ACK < replication_offset >其中replication_offset是从服务器当前的复制偏移量。 发送REPLCONF ACK命令对于主从服务器有三个作用: 1.检测主从服…