特征点法的缺陷

尽管特征点法在视觉里程计中占据主流地位,但研究者们还是认识到它至少有以下几个缺点:

(1)关键点的提取和描述子的计算十分耗时。

(2)使用特征点时,忽略了除特征点以外的所有信息。一幅图像有几十万个像素,而特征点仅有几百个,因此只使用特征点丢弃了大部分可能有用的图像信息。

(3)相机有时候会运动到特征缺失的地方,这些地方往往没有明显的纹理信息。例如:有时候我们会面对一堵白墙,或者一个空荡荡的走廊,这些场景下特征点数量会明显减少,我们可能找不到足够的匹配点来计算相机运动。

避免特征点法的几个思路

(1)保留特征点,但是只计算关键点,不计算描述子。同时,使用光流法来跟踪特征点的运动。这样可以回避计算和匹配描述子带来的时间,但是光流本身的计算需要一定时间。

(2)只计算关键点,不计算描述子,同时使用直接法来计算特征点在下一时刻图像中的位置。

(3)既不计算关键点,也不计算描述子,而是根据像素灰度的差异,直接计算相机运动。

直接法的思想

直接法根据像素的亮度信息估计相机的运动,可以完全不用计算关键点和描述子,于是既回避了特征的计算时间,也避免了特征缺失的情况。只要场景中存在明暗变化,直接法就能工作。

根据使用像素的数量,直接法分为稀疏、稠密、半稠密三种。

一、光流(Optical Flow)

直接法是从光流演变而来的。为了说明直接法,我们先来介绍一下光流。

光流是一种描述像素随着时间在图像之间运动的方法。随着时间的流逝,同一个像素会在图像中运动,而我们希望追踪它的运动过程。其中,计算部分像素运动的称为稀疏光流,计算所有像素的称为稠密光流。

本节以Lucas-Kanade光流为代表,也可以称为LK光流。

Lucas-Kanade光流

在LK光流,我们认为来自相机的图像是随时间变化的,图像可以看做时间的函数:

那么,一个在时刻,位于处的像素,它的灰度可以写成:

现在我们来考虑下面这个场景。

某个固定的空间点,它在时刻的像素坐标为。由于图像的运动,它的像素坐标将发生变化。我们希望估计这个空间点在其他时刻图像位置。怎么估计呢?光流大法来了,好好看!


首先,要引入光流法的基本假设。

灰度不变假设:同一个空间点的像素灰度值,在各个图像中是固定不变的。(灰度不变假设是一个很强的假设,在实际当中很可能不成立。然而,所有算法都是在一定假设下工作的。如果我们什么假设都不做,就无法设计实用的算法。所以,我们先暂且认为该假设是成立的,看看光流到底是个什么鬼!

对于时刻位于处的像素,我们设时刻它运动到了处,

由灰度不变假设,我们有

把上式的左边在处进行泰勒展开(泰勒展开式如果忘了可以看多元函数的泰勒展开式),可以得到下列等式:

由灰度不变假设,可以得到,

两边同时除以,得到

(注意一下数学意义:为像素在x轴上运动速度,为像素在y轴上的运动速度,把他们记作可以看做图像在该点处沿方向的梯度,可以看做图像在该点处沿方向的梯度,记为

把图像灰度对时间的变化率记为,将上式写成矩阵的形式有:

我们要计算的是像素在方向上的运动速度,但是仅凭这一个等式计算出两个未知量是不可能的。因此,必须要引入额外的约束来计算

在LK光流中,我们假设某一个窗口内的像素具有相同的运动。

现在考虑一个大小为的窗口,它含有个数量的像素。

由于该窗口内的像素具有相同的运动,因此我们可以得到个这样的方程:

记:

于是整个方程就可以写成

这是一个关于的超定线性方程,传统解法是最小二乘解(我也忘了最小二乘解,之后再补吧,还是那个想法,现在最重要的就是迅速入门,掌握SLAM的大体框架和方法)。

直接给出解:

这样就得到了像素在图像间的运动速度。当取离散的时刻而不是连续时间时,我们可以估计某块像素在若干个图像中出现的位置。

实践:LK光流

使用TUM公开数据集

先对这个数据集做一个简单的介绍。TUM数据集是来自于慕尼黑工业大学(TUM)提供的公开RGB-D数据集。这里只使用了其中一部分的图像。下面解释一下它的数据格式:

解压后文件中是这个样子的。

  1. rgb.txt和depth.txt记录了各文件的采集时间和对应的文件名
  2. rgb/和depth/目录存放着采集到的PNG格式图像文件。彩色图像为8位3通道,深度图为16位单通道图像。文件名即采集时间。
  3. groundtruth.txt问外部运动捕捉系统采集到的相机位姿,格式为,我们可以把它看成标准轨迹。

注:彩色图、深度图、标准轨迹的采集都是独立的,在使用数据之前,需要根据采集时间对数据进行一次时间上的对齐,以便彩色图和深度图进行配对。对齐原则:我们可以把采集时间相近于一个阈值内的数据,看成是一对图像,并把相近时间的位姿,看作是该图像的真实采集位置。

注:TUM提供了一个python脚本associate.py来帮我们完成这些工作,运行python associate.py rgb.txt depth.txt > associate.txt即可。

源代码:

#include <iostream>
#include <fstream>  //进行文件io
#include <list>
#include <vector>
#include <chrono>//opencv
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/video/tracking.hpp>//namespace
using namespace std;int main(int argc, char **argv)
{if(argc!=2){cout<<"usage: useLK path_to_dataset"<<endl;return 1;}string path_to_dataset=argv[1]; //数据集路径string associate_file=path_to_dataset+"/associate.txt";ifstream fin(associate_file);   //输入文件流对象,如果提供了文件名,则open()函数会自动调用string rgb_file, depth_file, time_rgb, time_depth;list<cv::Point2f> key_points;cv::Mat color, depth, last_color;for(int index=0; index<100; index++){fin>>time_rgb>>rgb_file>>time_depth>>depth_file;    //注意这个要和associate.txt中的数据意义对应color=cv::imread(path_to_dataset+"/"+rgb_file);depth=cv::imread(path_to_dataset+"/"+depth_file, -1);if(index==0){   //只对第一帧图像提取关键点vector<cv::KeyPoint> kps;cv::Ptr<cv::FastFeatureDetector> detector=cv::FastFeatureDetector::create();detector->detect(color, kps);   //提取到的关键点保存在kps当中for(auto kp:kps){key_points.push_back(kp.pt);    //kp.pt返回关键点的坐标}last_color=color;continue;}if(color.data==nullptr || depth.data==nullptr)continue;//对其他帧用LK跟踪特征点vector<cv::Point2f> next_key_points;vector<cv::Point2f> prev_key_points;for(auto kp:key_points){prev_key_points.push_back(kp);}vector<unsigned char> status;vector<float> error;chrono::steady_clock::time_point t1=chrono::steady_clock::now();cv::calcOpticalFlowPyrLK(last_color, color, prev_key_points, next_key_points, status, error);//status: 输出状态向量,如果找到了对应特征的流,则将向量的相应元素设置为1;否则,置0//error: 误差输出向量,vector的每个元素被设置为对应特征的误差chrono::steady_clock::time_point t2=chrono::steady_clock::now();//计算跟踪一次需要的时间chrono::duration<double> time_used=chrono::duration_cast<chrono::duration<double >>(t2-t1);cout<<"LK FLOW use time: "<<time_used.count()<<" seconds."<<endl;//把跟丢的点去掉int i=0;for(auto iter=key_points.begin(); iter!=key_points.end(); i++){if(status[i]==0){iter=key_points.erase(iter);    //删除跟丢的点continue;}//下面这两句代码不是很懂*iter=next_key_points[i];iter++;}cout<<"tracked keypoints: "<<key_points.size()<<endl;if(key_points.size()==0){cout<<"all keypoints are lost. "<<endl;break;}//画出keypointscv::Mat img_show=color.clone();for(auto kp:key_points){cv::circle(img_show, kp, 10, cv::Scalar(0, 240, 0), 1);}cv::imshow("corners", img_show);cv::waitKey(0);last_color=color;}return 0;
}

CMakeLists.txt的内容:

cmake_minimum_required(VERSION 3.12)
project(useLK)set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )find_package(OpenCV 3.4 REQUIRED)include_directories(${OpenCV_INCLUDE_DIRS}
)add_executable(useLK main.cpp)target_link_libraries(useLK ${OpenCV_LIBS})

运行结果:只放第一帧的结果

从这里应该可以想到:角点具有更好的辨识度,边缘次之,区块最小。

第8讲 视觉里程计2 --- 光流相关推荐

  1. 【slam十四讲第二版】【课本例题代码向】【第七讲~视觉里程计Ⅱ】【使用LK光流(cv)】【高斯牛顿法实现单层光流和多层光流】【实现单层直接法和多层直接法】

    [slam十四讲第二版][课本例题代码向][第七讲~视觉里程计Ⅱ][使用LK光流(cv)][高斯牛顿法实现单层光流和多层光流][实现单层直接法和多层直接法] 0 前言 1 使用LK光流(cv) 1.1 ...

  2. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-三角测量和实践

     专栏汇总 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第 ...

  3. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-对极几何和对极约束、本质矩阵、基础矩阵

    专栏系列文章如下:  专栏汇总 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLA ...

  4. 高博14讲--第七讲 视觉里程计-7.3 2D-2D:对极几何

    高博14讲--第七讲 视觉里程计-7.3 2D-2D:对极几何 基本问题 对极约束 对极约束推导过程 本质矩阵 八点法 八点法推导过程 本质矩阵$\ E$的SVD分解 单目SLAM的一些问题 尺度不确 ...

  5. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-PnP和实践

      专栏汇总 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记- ...

  6. 视觉SLAM十四讲学习笔记 第7讲 视觉里程计

    第七讲 视觉里程计 概要:前端视觉里程计(Visual Odometry, VO).视觉里程计的主要任务是估算相邻图像间相机的运动,以及局部地图的样子.VO又称前端. 主要学习目标: 1.理解图像特征 ...

  7. 第7讲 视觉里程计1

    7.1 特征点法 前面提到过,SLAM中主要分为前端和后端,前端主要是根据相邻图像的信息,粗略估计相机的运动,从而给后端一个比较好的初始值.前端的实现,根据是否需要提取特征,分为特征点法和直接法. 对 ...

  8. 视觉SLAM十四讲-高翔 第8讲 视觉里程计2

    视觉里程计 1.直接法的引出 尽管特征点法在视觉里程计中占 据主流地位,研究者们认识到它至少有以下几个缺点: 关键点的提取与描述子的计算非常耗时.实践当中,SIFT 目前在 CPU 上是无法实时计算的 ...

  9. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-特征点法和特征提取和匹配实践

    专栏系列文章如下: 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习 ...

最新文章

  1. 在Word中调用外部程序两法
  2. ajax获取php cookie,Ajax 无法跨域获取 cookie
  3. Hadoop中Block和Split区别
  4. 腾讯商业数据分析师培养计划
  5. ubuntu 下安装memcache 以及php扩展
  6. idea启动tomcat时蓝屏
  7. pclose与fclose的区别
  8. DataGridView分页
  9. mysql的时间函数_MySQL常用时间函数
  10. 面向对象闲话(一)——什么是对象
  11. 芯烨打印机api密钥php,CCXT中文开发手册
  12. html+字体有白色的重影+重影字体设置,win10字体重影怎么解决_win10电脑字体不清晰有重影修复方法...
  13. EXCEL VBA编程入门三:VBA开发环境认识
  14. 删除重复节点(细节每太明白)
  15. java计算机毕业设计论文评审系统源码+系统+mysql数据库+lw文档
  16. SQL Server 错误:尝试打开或创建物理文件时,CREATE FILE 遇到操作系统错误
  17. Scrapy是什么?Scrapy怎么用?Scrapy基础使用(基于scrapy2.0+编写) ๑乛◡乛๑ Scrapy框架使用方法
  18. correl函数_教你利用Correl函数返回相关系数并确定属性关系
  19. 分布式存储与集中式存储
  20. dataframe去掉行索引_DataFrame按索引删除行、列

热门文章

  1. Pytorch中torch.unsqueeze()和torch.squeeze()函数解析
  2. 适用多个行业的商业模式,健身房利用这种模式,3天收款108万?
  3. 关于Unity3d五国战争笔记
  4. java 接受输入不换行_java回车不换行
  5. Jenkins安装及项目配置
  6. 马蜂窝事件背后暴露出的数据风险 1
  7. Python 根据生成的txt对多目标跟踪结果画框可视化显示
  8. SQL联合查询及SQL语句中日期格式的转换
  9. 【数据结构】(图解)leetcode刷题之单链表(中)
  10. 防护服EN14126标准对防护新冠病毒起到的重要性。