LKflow

2023-05-16

cmakelists:

cmake_minimum_required(VERSION 3.7)
project(LKFlow)

set(CMAKE_CXX_STANDARD 11)

find_package(OpenCV)
include_directories(${OpenCV_INCLUDE_DIRS})

set(SOURCE_FILES main.cpp)
add_executable(LKFlow ${SOURCE_FILES})
target_link_libraries(LKFlow ${OpenCV_LIBS})

source:

#include <iostream>
#include <fstream>
#include <list>//这里用到了list列表
#include <vector>
#include <chrono>//计时的东西都被省掉了,这里没写

using namespace std;

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/video/tracking.hpp>

int main(int argc, char** argv)
{
    //命令行防呆,如果命令行个数不对,直接跳出程序
    if (argc != 2)
    {
        cout<<"usage: useLK path_to_data_folder"<<endl;
        return 1;
    }

    //将命令行转存一下,转存至path_to_data_folder。
    //这里注意,这里的argv[1]是一个文件夹,而不是像以往用的都直接是文件,
    //因为这个程序需要用到data文件夹中好多个文件,所以这里直接通过命令行引入文件夹,然后再在文件夹中寻找文件
    string path_to_data_folder=argv[1];
    //这里就是寻址文件的一种方法,由于path_to_data_folder是文件夹路径,在文件夹中选文件,中间要有/,不要忘记。
    string associate_file=path_to_data_folder+"/associate.txt";
    //通过文件输入,将associate_file文件读进来。
    ifstream fin(associate_file);

    //读入防呆,如果没有读取成功直接跳出。
    if(!fin)
    {
        cerr<<"I cann't find associate.txt!"<<endl;
        return 1;
    }

    //这里定义用于承接associate.txt文件中的输入,依次是:rgb图的时间,rgb图名称,深度图时间,深度图
    string time_rgb, rgb_file, time_depth, depth_file;
    list<cv::Point2f> keypoints;//因为要删除跟踪失败的点,所以使用list
    //定义彩色图和深度图,以及用于逐帧操作的将color承接为last_color,然后color再去读取新的图,依次循环
    cv::Mat color, depth, last_color;

    //循环,这里index指的是每一帧对应一个index。为什么是100,后面解释。
    for(int index=0; index<100; index++)
    {
        //这里可以看出,虽然程序中并不需要time_rgb和time_depth这两个数据,
        //但是由于文件输入流associate_file中有这两个信息,所以还是将其从流中读出来,保证顺次能够正确读取到rgb_file和depth_file
        fin>>time_rgb>>rgb_file>>time_depth>>depth_file;

        //到这里,我们才真正开始读到一张color图和一张depth图,path_to_data_folder寻址到data文件夹,
        //rgb_file变量格式是这样:rgb/1305031453.359684.png,自带了进入rgb文件夹中寻址彩色图
        color=cv::imread(path_to_data_folder+"/"+rgb_file);
        //此程序中好像没有用到depth做什么事,因为本来LK光流就没深度什么事,纯粹是玩两张平面图
        depth=cv::imread(path_to_data_folder+"/"+depth_file, -1);


        //单独处理第一帧,对第一帧提取FAST特征点。第一帧不需要其他工作。
        if(index==0)
        {
            //用于承接关键点的数组
            vector<cv::KeyPoint> kps;
            //创建detector指针,注意这里是指针,注意创建格式
            cv::Ptr<cv::FastFeatureDetector> detector = cv::FastFeatureDetector::create();
            //调用detector的detect方法,对color进行角点检测,检测结果存储在kps关键点数组中
            detector->detect(color,kps);
            //遍历所有关键点,并把关键点的坐标放入keypoints列表中,
            //注意keypoints类型为list,元素类型为cv::Point2f,即是坐标
            //这里还是要注意注意注意,kp类型是KeyPoint,KeyPoint是KeyPoint,跟KeyPoint的坐标是两码事,
            //要访问坐标,用.pt成员进行访问!!!
            for (auto kp:kps)
                keypoints.push_back(kp.pt);

            //由于是第一帧,所以last_color也就是color了。
            last_color = color;
            //第一帧的工作就是检测角点,并把角点坐标存储在keypoints列表中,没有其他工作,所以处理完这些就直接continue跳出
            continue;
        }

        //这里就是上方index为100的原因。在不是第一帧(即index不为0)时候,上一句就是读取彩色图和深度图,
        //cv::imread的参数为一个图片名称字符串,即使没有那个文件时并不会报错,所以这里来检测。
        //associate.txt文件中提供了很多时间对齐的color和depth图,但是我们的color和depth文件夹中只有前9帧,其他都没有。
        //所以运行操作时,index=1,2,3,4,5,6,7,8,9.当在第十帧时,这一句就起作用了,因为imread去读了一个不存在的文件,
        //所以color.data和depth.data都为空指针(这里为了以防万一用了或),用continue将这一帧跳过,
        //由于后面都没有了,所以一直continue到index=99,然后程序结束
        //猜测一下这里为什么这么用,这样设置index其实就是设置了总帧数,
        //用这个if进行continue跳过仅是跳过这一帧,因为实际工况中极有可能某一帧丢失,
        //后面又有画面帧时还会继续工作,而不会丢失一帧就直接停掉
        if ( color.data==nullptr || depth.data==nullptr )
        {
            //加上这一句就会发现index=1,2,3,4,5,6,7,8,9时会有角点跟踪图,
            //后面就是直接秒输出到index直到99,然后循环结束,程序结束
            //也就是后面在没有帧画面时还是会一直imread,只是因为没有data就被一直continue掉了,很快index到99,程序结束了
            cout<<"index="<<index<<endl;
            continue;
        }

        //在index不为0时,对其他帧用LK跟踪特征点
        //首先定义前一帧和后一帧的关键点坐标数组,这里是在循环里面直接创建,所以每次出循环,这俩就没了,进循环到这里就被定义创建
        vector<cv::Point2f> prev_keypoints;
        vector<cv::Point2f> next_keypoints;

        //将从第一帧检测得到的关键点坐标列表,赋值给prev_keypoints数组,遍历并逐个元素push_back
        for (auto kp:keypoints)
            prev_keypoints.push_back(kp);

        //匹配状态承接数组,第一幅图中的关键点,在第二幅图中匹配到就给status赋1,没匹配到就赋0.
        vector<unsigned char> status;
        //匹配误差承接数组,在匹配到的关键点中,都会有一个匹配误差,用error承接出来。
        //想想LK光流原理,每一个像素带入都会得到一个方程,所以最后求解的超定方程解会让每个像素(这里就是关键点)的方程有一点误差。
        vector<float> error;

        //这就是LK光流跟踪函数,说一下参数:
        /*CV_EXPORTS_W void calcOpticalFlowPyrLK( InputArray prevImg, InputArray nextImg,
                                                InputArray prevPts, InputOutputArray 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 );*/
        //InputArray prevImg 前一张图
        //InputArray nextImg 后一张要被跟踪的图
        //InputArray prevPts 前一张图中需要跟踪的角点坐标数组
        //InputOutputArray nextPts 后一张图中角点被跟踪出来后,承接放到此数组中,大小跟prevPts一样
        //OutputArray status 角点跟踪状态承接数组
        //OutputArray err 误差承接数组

        cv::calcOpticalFlowPyrLK( last_color, color, prev_keypoints, next_keypoints, status, error );

        //这一步是对keypoints列表进行更新,从前面的prev_keypoints更新到next_keypoints。
        int i=0;
        //这个循环中其实有两条遍历线,
        // 一条就是keypoints列表,通过iter指针控制,从keypoints.begin()开始到keypoints.end()结束。
        //遍历过程中通过判断status[]值是否为0,如果为0,则说明在跟踪过程中丢了,需要删掉。用.erase(iter)擦除
        //另外一条就是i,来遍历每两帧跟踪后得到的next_keypoints
        for ( auto iter=keypoints.begin(); iter!=keypoints.end(); i++)
        {
            if ( status[i] == 0 )
            {
                //用这一句看看那些跟丢的点,很明显在next_keypoints数组中也是有值的,只不过这里看输出发现好多负值的坐标。这就要在后面跳过了
                cout<<next_keypoints[i]<<endl;
                iter = keypoints.erase(iter);
                //keypoints.erase(iter);为什么这样不行??

                //这个continue,continue后下一次循环时,iter不会++,因为iter++为下面的语句,continue给跳过了。
                //所以在continue跳过跟丢的点时,iter还是保持当前的位置,不会++到下一个,
                //这是对的,keypoints列表保存的是跟踪到的关键点。跟丢了会等着一直到后面没跟丢的数据填充进来,而不会跳过
                //但是i就会被++了,因为i在for循环的()中,运行continue后就会被++。
                //也就是说跟踪失败的话,next_keypoints中的数据就被跳过了,至于为什么在最后面解释
                continue;
            }
            *iter = next_keypoints[i];
            iter++;
        }
        cout<<"tracked keypoints: "<<keypoints.size()<<endl;

        //所有跟踪都没了,就直接break了
        if (keypoints.size() == 0)
        {
            cout<<"all keypoints are lost."<<endl;
            break;
        }

        // 画出 keypoints
        cv::Mat img_show = color.clone();
        //将关键点坐标处画上圆圈
        for ( auto kp:keypoints )
            cv::circle(img_show, kp, 3, cv::Scalar(250, 0, 0));
        //显示画出跟踪点的图像
        cv::imshow("corners", img_show);

        cv::waitKey(500);
        //将color赋值给last_color,下一次循环将会用color读入新的帧
        last_color = color;
    }

    return 0;
}
//说一下程序中的list<cv::Point2f> keypoints、vector<cv::Point2f> prev_keypoints和vector<cv::Point2f> next_keypoints
//keypoints为list类型,定义在每帧跟踪循环之外,一直存在。被初始化是第一帧检测角点后,将所有角点坐标存入。他的更新只有减少,没有增加(只有跟丢的点进行删除)
//vector<cv::Point2f> prev_keypoints和vector<cv::Point2f> next_keypoints是在每帧跟踪循环内被定义的,也就是每次进循环被定义,出循环被释放
//循环中两两帧跟踪时,先将keypoints中点copy给prev_keypoints,然后调用追踪函数,生成next_keypoints。
//两两帧跟踪这个过程中,肯定是有关键点丢失的(因为视角变了,一些点可能出了视野范围),比如前帧100关键点,后帧98个了。
//但是丢失归丢失,整个跟踪过程没关系,prev_keypoints和next_keypoints大小是一样的,包括status数组,都是100。
//那两个跟丢的点在next_keypoints对应位置上依然存在,依然是个坐标值,只不过区分上是在status数组对应位置上,他们的状态是0,其他跟踪到的都是1。
//这样就好解释上面的i为什么被continue跳过了,next_keypoints数组中是有坐标的,只是这个坐标没啥用,因为它的状态已经被标志为0,所以就需要跳过。
//千万不要默认为跟丢之后,next_keypoints数组就自动将其舍掉不保存了。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LKflow 的相关文章

  • 关于为什么不能在头文件中包含变量定义的解释

    其实 xff0c 并不是C语言标准不允许在头文件中包含变量定义 xff0c 而是编译器本身产生了重复定义的错误 这时候有些人会很奇怪 xff0c 我不是写了 ifndef define endif这样的命令了吗 xff1f 如果你这样以为
  • 关于同一交换机下设置不同网段的PC的通信的猜测

    PC A IP 192 168 1 10 PC B IP 192 168 2 20 子网掩码24位 A和B都连接到一台二层交换机上 交换机上没有再接其他设备 A和B能否通信 从网上搜索了一下 发现有很多人问这个问题 但是大家的回答都很抽象
  • XP + Fedora 9 + Ubuntu8.10 安装过程点滴

    lt 64 page size 21cm 29 7cm margin 2cm P margin bottom 0 21cm gt XP 43 Fedora 9 43 Ubuntu8 10 安装过程点滴 fanfan 额外必须的软件 GRUB
  • 在Ubuntu下装MultiGet成功。。。

    本来用的是 xff0c deb包的1 1 2版 xff0c 下点不大的文件还可以 xff0c 可是我去下Ubuntu的DVD就出麻烦了 xff0c 早上把任务开起 xff0c 晚上回来居然什么都不见了 xff0c 连 Multiget程序都
  • 系统监控命令

    top命令 top c 在top命令显示界面显示出完整的进程名和启动参数 top H 在top命令中显示所有的线程 状 top p pid 这个pid可以是进程pid 也可以是线程pid 进程的pid就是该进程主线程的pid 该命令实际显示
  • 一个对齐关键字pack引起的副作用

    今天遇到一个很典型的因为没有留意pack关键字有效范围而引起的程序bug xff0c 觉得很有意思 xff0c 就记录下来 现象如下 xff1a 声明了一个数据结构 struct st data xff0c 这个数据结构中有一个成员是一个函
  • 什么是物联网?发展前景如何?

    物联网其实是互联网的一个延伸 xff0c 互联网的终端是计算机 PC 服务器 xff0c 我们运行的所有程序 xff0c 无非都是计算机和网络中的数据处理和数据传输 xff0c 除了计算机外 xff0c 没有涉及任何其他的终端 硬件 物联网
  • Linux上压缩文件的 5 种方法

    在 Linux 上有不少用于压缩文件的命令 最新最有效的一个方法是 xz xff0c 但是所有的方法都有节省磁盘空间和维护备份文件供以后使用的优点 在这篇文章中 xff0c 我们将比较这些压缩命令并指出显著的不同 tar tar 命令不是专
  • 新手如何从零开始学习unity

    自从 unity5发布免费过后 xff0c 有很多独立游戏开发者转向unity游戏开发 xff0c unity的优势就是多终端 跨平台打包 xff0c 入门也快 xff0c 很多人感觉自己的英文不好 xff0c 就觉得学不会 xff0c 其
  • stm32零基础入门,应学习那些知识

    1 首先我们先看看与STM32相关的文档 我们假定大家已经对STM32的书籍或者文档有一定的理解 如不理解 xff0c 请立即阅读STM32的文档 xff0c 以获取最基本的知识点 如果你手上拥有ST官方主推的STM32神舟系列的板子 xf
  • Java如何实现二维码扫码授权登陆

    如今的生活中 xff0c 登录网站也变得如此简单 xff0c 当你已经登录一微信时 xff0c 当你想要登录另一个网站时 xff0c 只需扫码便可 xff0c 可是大家知道用Java怎么实现扫码授权吗 本文讲述的就是关于如何用Java实现扫
  • Postman安装与简单使用

    Postman使用参考文档 xff1a 1 官方英文文档 2 chrome插件整理的 postman中文使用教程 Postman一款非常流行的API调试工具 其实 xff0c 开发人员用的更多 因为测试人员做接口测试会有更多选择 xff0c
  • c语言入门基础

    C语言的结构 1 Hello world 简单来说 xff0c 一个C程序就是由若干头文件和函数组成 include 包含头文件 主函数 int main printf Hello World return 0 include 就是一条预处
  • 单片机串行口介绍

    介绍 串行口是单片机与外界进行信息交换的工具 xff0c 8051单片机的通信方式有两种 xff1a 并行通信 数据的各位同时发送或接收 串行通信 数据一位一位次序发送或接收 串行通信的方式 异步通信 用一个起始位0表示字符的开始 xff0
  • 51单片机中断机制(定时器)

    单片机中断简介 52单片机一共有6个中断源 xff0c 它们的符号 xff0c 名称以及各产生的条件分别如下 xff1a INT0 外部中断0 xff0c 由P3 2端口线引入 xff0c 低电平或下降沿引起 INT1 外部中断1 xff0
  • 什么叫51单片机最小系统

    单片机最小系统 或者称为最小应用系统 是指用最少的元件组成的单片机可以工作的系统 对51系列单片机来说 最小系统一般应该包括 单片机 晶振电路 复位电路 下面给出一个51单片机的最小系统电路图 说明 复位电路 由电容串联电阻构成 由图并结合
  • 嵌入式系统C编程之错误处理

    一 错误概念 1 1 错误分类 从严重性而言 xff0c 程序错误可分为致命性和非致命性两类 对于致命性错误 xff0c 无法执行恢复动作 xff0c 最多只能在用户屏幕上打印出错消息或将其写入日志文件 xff0c 然后终止程序 而对于非致
  • 补光灯的单片机开发设计

    说到摄影灯 xff0c 相信每个人都一定听说过闪光灯和补光灯 那它们是怎么由来的呢 又是怎么达到了你想要的效果呢 不论是闪光灯还是补光灯 xff0c 它们都有一个共同点 xff0c 那就是由NY8A051D单片机开发而来 xff0c 单片机
  • 单片机C语言如何产生随机数

    单片机C语言如何产生随机数 随机数在单片机的应用中也是很多的 xff0c 当然产生随机数的方法有很多 xff0c 当中有一个就是利用单片机定时器 xff0c 取出未知的定时器THX和TLX的值 xff0c 再加以运算得到一个规定范围内的随机

随机推荐

  • 使用mac终端编译运行c程序

    使用mac终端编译运行c程序 本文介绍如何利用mac自带文本编辑软件编写c代码 xff0c 并在mac自带终端内用命令行编译运行c程序 1 在mac上安装c编译环境 打开mac自带的终端 在终端命令行里输入xcode select inst
  • HtmlParser 一个不错的网站爬虫工具

    有时候我们需要在网上获取自己需要的内容时 xff0c 而且需求量达到一定程度时 xff0c 就要通过代码来实现重复的操作 当用Java来帮我们解决这个问题时 xff0c 我们又如何通过Java来过滤掉多余的内容 xff0c 剩余自己想要的信
  • 因为jsoup,再见了我的htmlparser

    jsoup 一款Java 的HTML解析器 xff0c 可直接解析某个URL地址 HTML文本内容 它提供了一套非常省力的API xff0c 可通过DOM xff0c CSS以及类似于jQuery的操作方法来取出和操作数据 这里是jsoup
  • Python 当前时间是那一年第几周的周几

    isocalendar 函数 返回 xff08 XX年 xff0c 一年中的第几周 xff0c 这一天是周几 xff09 gt gt gt from datetime import datetime gt gt gt datetime no
  • 对Socket CAN的理解(1)——【CAN总线原理】

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 由于Socket CAN涉及到CAN总线协议 套接字 Linux网络设备驱动等 因此 xff0c 为了能够全面地了解Socket CAN的
  • 对Socket CAN的理解(2)——【Socket的原理及使用】

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 为了能够对Socket CAN的深入理解 xff0c 我们需要了解Socket的机制 Socket的中文翻译为 插座 xff0c 在计算机
  • 【智能家居篇】wifi网络结构(上)

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 WIFI是什么 xff0c 相信大家都知道 xff0c 这里就不作说明了 我们需要做的是深入了解其工作原理 xff0c 包括软硬件 网络结
  • 【智能家居篇】wifi网络结构(下)

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 由于WIFI网络具有移动性 xff0c 同时WIFI以无线电波作为传输媒介 xff0c 这种媒介本质上是开放的 xff0c 且容易被拦截
  • 【智能家居篇】wifi网络接入原理(上)——扫描Scanning

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 对于低头党来说 xff0c 在使用WIFI功能时 xff0c 经常性的操作是打开手机上的WIFI设备 xff0c 搜索到心目中的热点 xf
  • 【智能家居篇】wifi网络接入原理(下)——关联Association

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 认证完成后 xff0c 下一步就是关联 xff08 Association xff09 工作站与基站进行关联 xff0c 以便获得网络的完
  • 【智能家居篇】wifi驱动的理解(3)——usb接口在wifi模块中的角色

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 上一篇文章已经提到USB接口在wifi模块中的最重要两个函数是usb read port 和usb write port 那它们是怎么和w
  • 【智能家居篇】wifi驱动的理解(4)——usb接口在wifi模块中的角色

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 还有1天就到2017年了 xff0c 回顾整个2016年至此 xff0c 都没发表过一篇技术文章 做软件开发已有5 6年 xff0c 作为
  • mx51 TVOUT分析

    1397 static int init enable tve setup char options 1398 1399 g enable tve 61 true 1400 1401 return 1 1402 1403 setup 34
  • 2D图形加速引擎(GE2D)

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 英文原文 NUC970 Series Technical Reference Manual Chapter 5 28 一 概述 32位2D
  • Redis 安装

    安装 下载 解压 编译Redis wget http download redis io releases redis 6 0 6 tar gz tar xzf redis 6 0 6 tar gz cd redis 6 0 6 make
  • linux下 tcpdump实现原理

    linux下抓包实现原理 一 tcpdump 对于本机中进程的系统行为调用跟踪 xff0c strace是一个很好的工具 xff0c 而在网络问题的调试中 xff0c tcpdump应该说是一个必不可少的工具 xff0c 和大部分linux
  • eigen求特征值和特征向量

    Eigen Matrix2d matrix 22 matrix 22 lt lt 2 3 2 1 cout lt lt 34 matrix 61 n 34 lt lt matrix 22 lt lt endl Eigen SelfAdjoi
  • sophus库的一些使用

    首先是cmakelists cmake minimum required VERSION 2 8 project useSophus 为使用 sophus xff0c 您需要使用find package命令找到它 find package
  • OpenCV中ORB特征点检测和匹配简单用法

    cmakelists span class hljs keyword cmake minimum required span VERSION span class hljs number 3 7 span span class hljs k
  • LKflow

    cmakelists span class hljs keyword cmake minimum required span VERSION span class hljs number 3 7 span span class hljs k