从数据流的角度讲一遍 DSO 代码框架。

DSO 的入口是 FullSystem::addActiveFrame,输入的影像生成 FrameHessian 和 FrameShell 的 Object,FrameShell 是 FrameHessian 的成员变量,FrameHessian 保存影像信息,FrameShell 保存帧的位置姿态信息。代码中一般用 fh 指针变量指向当前帧的 FrameHessian。在处理完成当前帧之后,会删除 FrameHessian,而保存 FrameShell 在变量 allFrameHistory 中,作为最后整条轨迹的输出。

其实,输入影像在 main_dso_pangolin.cpp 中已经处理,影像值不是原始的灰度值,而是辐射值,这些辐射值的范围依旧是 [0, 255],float 型。(如果有进行辐射标定。)

数据预处理部分是在 FullSystem::addActiveFrame 中调用的 FrameHessian::makeImages,这个函数为当前帧的影像建立影像金字塔,并且计算每一层影像的梯度。这些计算结果都存储在 FrameHessian 的成员变量中,1. dIp 每一层影像的辐射值、x 方向梯度、y 方向梯度;2. dI 指向 dIp[0] 也就是原始影像的信息;3. absSquaredGrad 存储 xy 方向梯度值的平方和。

1. 第一帧

进入 FullSystem::addActiveFrame,首先判断是否完成了初始化,如果没有完成初始化,就将当前帧 fh 输入 CoarseInitializer::setFirst 中。完成改函数之后,退出,接着处理下一帧。

CoarseInitializer::setFirst,在影像的每一层选取点,作为后续第二帧匹配生成 pointHessians 和 immaturePoints 的候选点,这些点存储在 CoarseInitializer::points 中。每一层点之间都有联系,在 CoarseInitializer::makeNN 中计算每个点最邻近的10个点 neighbours,在上一层的最邻近点 parent。

pointHessians 是成熟点,具有逆深度信息的点,能够在其他影像追踪到的点。immaturePoints 是未成熟点,需要使用非关键帧的影像对它的逆深度进行优化,在使用关键帧将它转换成 pointHessians,并且加入到窗口优化。

2. 第二帧

初始化需要有两帧,所以第二帧依然交由 CoarseInitializer。CoarseInitializer::trackFrame 处理完成之后,在 FullSystem::initializerFromInitializer 中为第一帧生成 pointHessians,一共2000个左右。随后将第二帧作为 KeyFrame 输入到 FullSystem::deliverTrackedFrame,最终流入 FullSystem::makeKeyFrame。(FullSystem::deliverTrackedFrame 的作用就是实现多线程的数据输入。)

2.1 CoarseInitializer::trackFrame

CoarseInitializer::trackFrame 中将所有 points (第一帧上的点)的逆深度初始化为1。从金字塔最高层到最底层依次匹配,每一层的匹配都是高斯牛顿优化过程,在 CoarseIntializer::calcResAndGS 中计算Hessian矩阵等信息,计算出来的结果在 CoarseInitializer::trackFrame 中更新相对位姿(存储在局部变量中,现在还没有确定要不要接受这一步优化),在 CoarseInitializer::trackFrame 中调用 CoarseInitializer::doStep 中更新点的逆深度信息。随后再调用一次 CoarseIntializer::calcResAndGS,计算新的能量,如果新能量更低,那么就接受这一步优化,在 CoarseInitializer::applyStep 中生效前面保存的优化结果。

一些加速优化过程的操作:1. 每一层匹配开始的时候,调用一次 CoarseInitializer::propagateDown,将当前层所有点的逆深度设置为的它们 parent (上一层)的逆深度;2. 在每次接受优化结果,更新每个点的逆深度,调用一次 CoarseInitializer::optReg 将所有点的 iR 设置为其 neighbour 逆深度的中位数,其实这个函数在 CoarseInitializer::propagateDown 和 CoarseInitializer::propagateUp 中都有调用,iR 变量相当于是逆深度的真值,在优化的过程中,使用这个值计算逆深度误差,效果是幅面中的逆深度平滑。

优化过程中的 lambda 和点的逆深度有关系,起一个加权的作用,也不是很明白对 lambda 增减的操作。在完成所有层的优化之后,进行 CoarseInitializer::propagateUp 操作,使用低一层点的逆深度更新其高一层点 parent 的逆深度,这个更新是基于 iR 的,使得逆深度平滑。高层的点逆深度,在后续的操作中,没有使用到,所以这一步操作我认为是无用的。

2.2 FullSystem::initializeFromInitializer

FullSystem::initializeFromInitializer,第一帧是 firstFrame,第二帧是 newFrame,从 CoarseInitializer 中抽取出 2000 个点作为 firstFrame 的 pointHessians。设置的逆深度有 CoarseIntiailzier::trackFrame 中计算出来的 iR 和 idepth,而这里使用了 rescaleFactor 这个局部变量,保证所有 iR 的均值为 1。iR 设置的是 PointHessian 的 idepth,而 idepth 设置的是 PointHessian 的 idepth_zero,idepth_zero 相当于估计的真值,用于计算误差。(这里和 CoarseInitializer 对 idepth 和 iR 的定义是相反的,我再想想。)

注意这里已经将第一帧加入到 EnergyFunctional 的帧中,为后面的优化做准备。搜索 ef 变量就能看到这个操作。

3. 第 3 4 5 6 7 8 ... 帧

后面帧的流程就是先使用 FullSystem::trackNewCoarse 将当前帧与上一个关键帧进行匹配,得到初始位姿,随后判断当前帧是否需要成为关键帧,并输入到 FullSystem::deliverTrackedFrame,最终输入到 FullSystem::makeKeyFrame 或 FullSystem::makeNonKeyFrame 中。

3.1 FullSystem::trackNewCoarse

coarseTracker->lastRef 中存储了最新关键帧,allFrameHistory 存储了所有帧的位姿。按照 1 倍,2倍,0.5倍,0 倍速度的假设,构造当前帧的位姿假设。这些位姿的假设都来源于前面两帧与关键帧两两之间的相对位姿和关键帧的绝对位姿。而这些假设中最重要的是,当前帧到前一帧的相对位姿等于前一帧到前前一帧的相对位姿,之所以说这个重要,是因为这样后面在这样计算出来的当前帧位姿上,进行了 a TON of 旋转作为假设,加入到总的假设(lastF_2_fh_tries)中。

进行好这些假设之后,就从第一个假设开始,用 CoarseTracker::trackNewestCoarse 与最新关键帧匹配。依旧是高斯牛顿优化,而这个优化只优化相两帧的相对状态(相对位姿 6 + 光度仿射变换 2)。当然也不是所有的假设都需要优化一遍,当前假设得到的结果与前面假设得到的结果比较,当前结果与前面一帧匹配的结果比较(这个跨度有点大,是上一帧),满足条件就可以跳出了。

3.2 FullSystem::makeNonKeyFrame

非关键帧的作用是更新窗口中关键帧 immaturePoints 的逆深度,FullSystem::makeNonKeyFrame 调用 FullSystem::traceNewCoarse 做这件事情。对于每一个 ImmaturePoint 是在 ImmaturePoint::traceOn 中完成的。

3.2.1 ImmaturePoint::traceOn

ImmaturePoint::traceOn 这个函数是将关键帧上的点与非关键帧影像进行匹配,得到在非关键帧影像上的位置,知道这个信息就可以更新深度了。先进行极线搜索,搜索得到一个比较好的值,再使用高斯牛顿优化。

最后更新了 ImmaturePoint::idepth_min 和 ImmaturePoint::idepth_max,对于 immaturePoint 仅仅只有一个 idepth 的范围,没有确切的 idepth。

3.3 FullSystem::makeKeyFrame

一开始的套路和非关键帧一样,调用 FullSystem::traceNewCoarse。

调用 FullSystem::flagFramesForMarginalization 标记需要被 marg 掉的帧,这些帧在 frameHessians 这个 std::vector 中存储方式是越老的帧越前。

遍历窗口中所有帧的 pointHessians,建立它们链接自己 hostframe 与当前帧的 PointFrameResidual,加入到 EnergyFunctional 中。

调用 FullSystem::activatePointsMT 遍历窗口中所有帧的 immaturePoints,如果可以投影到的当前帧上,那么再试一试这些帧能不能投影到窗口中其他帧上去,找到所有能够投影的链接,形成 PointFrameresisual。(FullSystem::optimizeImmaturePoint)为了将这些点投影到尽可能多的帧上去,也是进行了高斯牛顿优化它的逆深度。这里的逆深度就是 immaturePoint 的 idepth_max 和 idepth_min,在 ImmaturePoint::traceOn 中计算得到的。

接下来就是调用 FullSystem::optimize 窗口优化。窗口优化完,就是 marg 掉不需要的帧和点。

做完这些操作之后,有一个重要的操作是 FullSystem::makeNewTraces 在当前关键帧提取 immaturePoints,这样下一个关键帧处理的时候可以生成 pointHessians 加入到窗口优化过程中。

DSO 代码框架的更多相关文章

  1. x01.CodeBuilder: 生成代码框架

    根据 Assembly 生成代码框架. 这是学习 AvalonEdit 的一个副产品.学习时,照着源代码新建文件夹,新建文件,添加方法与属性,虽然只是个框架,也要花费大量时间.为什么不让它自动生成呢? ...

  2. twemproxy代码框架概述——剖析twemproxy代码前编

    本篇将去探索twemproxy源码的主干流程,想来对于想要开始啃这份优秀源码生肉的童鞋会有不小的帮助.这里我们首先要找到 twemproxy正确的打开方式--twemproxy的文件结构,接着介绍tw ...

  3. [C++]Linux之多进程运行代码框架

    声明:如需引用或者摘抄本博文源码或者其文章的,请在显著处注明,来源于本博文/作者,以示尊重劳动成果,助力开源精神.也欢迎大家一起探讨,交流,以共同进步- 0.0  多进程代码框架示例 /* @url: ...

  4. python爬取网页的通用代码框架

    python爬取网页的通用代码框架: def getHTMLText(url):#参数code缺省值为‘utf-8’(编码方式) try: r=requests.get(url,timeout=30) ...

  5. 深入浅出etcd系列Part 1 – etcd架构和代码框架

    1.绪论 etcd作为华为云PaaS的核心部件,实现了PaaS大多数组件的数据持久化.集群选举.状态同步等功能.如此重要的一个部件,我们只有深入地理解其架构设计和内部工作机制,才能更好地学习华为云Ku ...

  6. 自适应大邻域搜索代码系列之(1) - 使用ALNS代码框架求解TSP问题

    前言 上次出了邻域搜索的各种概念科普,尤其是LNS和ALNS的具体过程更是描述得一清二楚.不知道你萌都懂了吗?小编相信大家早就get到啦.不过有个别不愿意透露姓名的热心网友表示上次没有代码,遂不过瘾啊 ...

  7. 使用EA生成多层次的代码框架

    最近工作期间发现了一个非常棒的UML软件[Enterprise Architect UML 建模工具]简称EA,在该软件上绘制框架层面的类之间关系后,可以自动生成相关语言的代码. EA上目前支持的语言 ...

  8. Onvif开发之代码框架生成篇

    看了前一篇的ONVIF的简单介绍应该对它的基本使用都有了一些基本的了解了吧!下面我讲一步分解向大家介绍下如何通过gsoap生成需要的代码,以及代码中需要注意的问题[基于Linux平台 C开发] 生成O ...

  9. OpenDaylight开发hello-world项目之代码框架搭建

    OpenDaylight开发hello-world项目之开发环境搭建 OpenDaylight开发hello-world项目之开发工具安装 OpenDaylight开发hello-world项目之代码 ...

随机推荐

  1. ACG图片站\python爬虫\LAMP环境

    最近突然对web很感兴趣,碰巧看到阿里云服务器学生价十块钱一个月,果断买了一个自己搭建了一个网站. 网址 这里 LAMP环境就搭建了好久,linux+apache2+mysql+php,都是开源的软件 ...

  2. POI获取单元格的宽和高

    获取单元格的宽,即获取所在列的宽.先获取单元格所在的sheet:cell.getSheet() sheet.getColumnWidth( cell.getColumnIndex() )  单位不是像 ...

  3. 【BZOJ2000】[HNOI2000]取石头游戏(贪心,博弈论)

    [BZOJ2000][HNOI2000]取石头游戏(贪心,博弈论) 题面 BZOJ 洛谷 题解 这题好神仙啊,窝不会QaQ. 假装一下只有三个元素\(a_{i-1},a_i,a_{i+1}\),并且满 ...

  4. SharePoint 2013 首页修改

    最近客户要求统一首页的风格,所以对各网站的首页进行了统一的修改. 1. 左边导航菜单修改: 修改的地方: Site Settings –> Look and feel –> Navigat ...

  5. bzoj5280/luogu4376 MilkingOrder (二分答案+拓扑序)

    二分答案建图,然后判环,就可以了. 字典序输出的话,只要做拓扑序的时候用优先队列来维护就可以了. (其实判环也可以用拓扑序...) #include<cstdio> #include< ...

  6. 牛客网小白月赛5I区间(差分数组)

    链接:https://www.nowcoder.com/acm/contest/135/I来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 32768K,其他语言65536 ...

  7. LOJ#2095 选数

    给定n,k,l,r 问从[l, r]中选出n个数gcd为k的方案数. 解:稍微一想就能想到反演,F(x)就是[l, r]中x的倍数个数的n次方. 后面那个莫比乌斯函数随便怎么搞都行,当然因为这是杜教筛 ...

  8. C++11并发——多线程std::thread (一)

    https://www.cnblogs.com/haippy/p/3284540.html 与 C++11 多线程相关的头文件 C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是< ...

  9. double

    看double精度 和 数字经过计算后的最小精度

  10. 走进HTTP协议之二 基本HTTP机制

    本系列第一节,我们回顾了与HTTP协议有关的基本术语和概念,本文将分析HTTP协议的基本原理与机制 HTTP协议的用途 HTTP协议用于客户端与服务器之间的通信,在通信线路两端,必定一端是客户端,另一 ...