DSO全家桶(二)——DSO前端:提取梯度点

  嗨,各位读者朋友们好!今天我们接着讲DSO全家桶的第二讲——提取梯度点。

  首先,我们会先介绍一下DSO中从图像读取到提取梯度点的整个流程,然后再对重点的部分——“提取梯度点”进行讲解,让大家对整个流程的逻辑有个较为清晰的认识。这样写的目的主要是让大家在后续自己实现代码的时候,可以有目标地写测试用例。

 

                                                      图1. 图像预处理到提取梯度点流程图

  OK,我们开始吧!如图1所示,我们将简单阐述一下每一步的操作。

  1. 根据输入的标定文件确定图像的原始尺寸和裁剪尺寸,并且甚至图像的路径。若有光度标定[1]文件,还需要设置光度标定参数;

  2. 在imageRW_OpenCV.cpp[2]中采用cv::imread[3]函数读取指定序号的图像,并拷贝到指定数据类型MinimalImageB中;

  3. 若事先进行了光度标定,DSO会对输入图像先进行光度校正;

  4. 对原始图像进行去畸变[4],并对输出图像进行高斯滤波,目标应该是使得图像平滑一些;

  5. 构建图像金字塔,并逐层逐像素计算梯度值以及梯度值的平方和;

  6. 对图像金字塔第0层(即原始图像)提取梯度点;

  7. 对于图像金字塔第1-5层,设置固定大小的网格,在每个网格中根据梯度值大小选择四类点;

  8. 对步骤6-7选择的点进行初始化,包括坐标、深度值和阈值等。

  上述步骤即是DSO从输入图像开始一直到完成提取梯度点的完整流程。但是我们这里并不会详细介绍每一个步骤,只挑比较重要的部分进行讲解,即上述的步骤6和步骤7。实际上,在自己实现代码的时候,可以借助OpenCV等工具完成图像读取、滤波和构建金字塔等操作,因此我们并不会花大篇幅介绍这些内容。


DSO前端:提取梯度点

  DSO中对点的提取策略分为两类:

  1. 原始图像层的梯度点提取,即图像金字塔的第0层:由小到大的比较每一个像素的梯度值与阈值,找出满足要求的点;

  2. 图像金字塔第1-5层的梯度点选择:在每个固定大小的网格中,提取四类点。

  接下来,我们对这两个步骤分别进行介绍。

图像金字塔第0层

  首先我们需要先设置密度权重,因为整张图像的像素多到吓人,为了秉承DSO中的Sparse,DSO将第0层的密度权重设置为0.03,即需要提取的目标点数是0.03*width*height。

  处理图像金字塔第0层的函数是PixelSelector::makeMaps(...),表示从无到有的过程。为了更加直观地显示整个过程,笔者引用了泡泡机器人SLAM龚益群同学发布在【泡泡读者来稿】的作品:DSO代码解读[5]中的一些图示进行介绍。

DSO全家桶(二)——DSO前端:提取梯度点_第1张图片

              图2. 梯度直方图

  1. 如图2所示,首先需要对每个32*32的网格创建梯度直方图,在最开始介绍系统流程时,我们提到过需要对图像计算梯度值以及梯度的平方和(dx2+dy2)。而在这里,就是利用梯度平方和统计直方图(梯度直方图的横轴为梯度值,纵轴为像素个数),统计该网格中同一梯度值的像素个数;

  2. 在梯度直方图中找到处于中间(即中位数)的梯度值,作为阈值;

  3. 在步骤2中我们计算了每个32*32的网格中位数的梯度值,对阈值矩阵进行3*3窗口的均值滤波,得到阈值矩阵;

 

DSO全家桶(二)——DSO前端:提取梯度点_第2张图片

图3. 图像金字塔第0层梯度点筛选策略

  4. 如图3所示,DSO中采用了四级网格策略,其中蓝色网格是16*16,绿色网格是8*8,红色网格是4*4,最内层是1*1,并且彼此之间是包含关系;

  5. 在提取梯度点的时候需要用到我们步骤3计算得到的阈值矩阵,通过由内到外比较每个点的梯度值与阈值的大小,确定提取到的点。这里有几个值得注意的地方,第一是阈值的选取:

  假设阈值是th,

  在1*1的窗口时,阈值被设置为2*th;

  在4*4的窗口中,阈值被设置为th;

  在8*8的窗口中,阈值被设置为0.75*th;

  在16*16的窗口中,阈值被设置为0.75*0.75*th。

  由于DSO中采取的策略是,先从最小的网格开始提取梯度点,在确保梯度值大于阈值的前提下找出网格中梯度值最大的点。若不满足阈值条件,则进入更大的网格进行筛选。而更大的网格都是从小的网格组合得到的,若是阈值不改变,仍然是无法提取到点的。因此,DSO才有这种渐变式的梯度阈值条件。

  值得注意的是,DSO中的起始网格大小并不是图示的4*4,而是3*4。当且仅当第一次提取的点数量不满足要求时,才有可能改变为2*4或者4*4。DSO中采用0.25作为分界线:

  当quotia = numWant / numHave > 1.25,表示提取到的点远小于期望值,需要减小窗口大小以获得更多的点,这时窗口会变成2*4;

  当quotia = numWant / numHave < 0.25,表示提取到的点远大于期望值,需要增大窗口大小以获得更少的点,这时窗口会变成4*4。

图像金字塔第1-5层

  不同于第0层,第1-5层的梯度点筛选是通过在固定网格中计算四类点的最大值来确定的。

DSO全家桶(二)——DSO前端:提取梯度点_第3张图片

图4. 图像金字塔第1-5层梯度点筛选策略

  1. 首先需要确定的是,DSO预设的网格大小为5*5;

  2. 如图4所示,emmmm....笔者纯粹只是为了避免去画图(偷懒),姑且这里就按4*4算好了。DSO会在每个4*4的网格中逐个像素地计算梯度值sqrt(dx2+dy2);

  3. 整体的阈值条件被设置为th = 1 * 10 *0.75 = 7.5;

  4. 后续我们需要对网格中的所有大于阈值th的像素挨个比较gradX / gradY / gradX - gradY / gradX + gradY的大小,选择该网格中四类条件中最大的四个点。

总结

  至此,第二讲——提取梯度点,我们就已经讲完了。我们在开头介绍了DSO从输入流进来到完成提取梯度点的整个流程,可以帮助读者更快地建立起写一个测试用例应该要考虑的东西,同时也是直接给读者介绍了DSO在图像预处理这块儿做了哪些功夫。后面我们详细介绍了DSO是如何提取梯度点的,主要还是区分图像金字塔第0层和第1-5层的策略。后续笔者完成代码以后会在总结这边贴一些实际运行的结果图,先欠着哈。

  再次感谢一些龚益群同学提供的PPT,感兴趣的同学可以详细阅读参考文献[5]。也非常欢迎各位同行向泡泡机器人SLAM投稿,我们有专门的泡泡读者来稿专栏平台供大家发挥,心动不如行动哇。

  最后的最后,劳烦各位亲爱的读者朋友们给小弟我点个关注吧!谢谢撒!

PS:

       哈喽,今天是2021-01-21,笔者来补一个源码实现:PixelSelector,供大家参考,若是大家对代码满意,麻烦给我点个star哈,谢谢。

参考文献

[1] 光度标定.

[2] imageRW_OpenCV.cpp.

[3] cv::imread.

[4] 去畸变.

[5] 【泡泡读者来稿】DSO代码解读.

版权声明

  如需转载,请联系本人邮箱peichu.ye at mail2.gdut.edu.cn。未经允许,不可转载。

你可能感兴趣的:(DSO,计算机视觉,slam)