KITTI下使用SGBM立体匹配算法获得深度图

以下内容不涉及原理,仅为工程性内容:

经典的立体匹配算法主要由:BM(Block Matching),SGBM(Semi-Global Block matching),GC。更高级的就直接用上了深度学习,这里就不在考虑了。

上述三种算法速度:BM > SGBM > GC,效果:BM < SGBM < GC;暂取折中的SGBM算法研究。

SGBM及相关程序参考博客:

  1. https://www.cnblogs.com/riddick/p/8486223.html
  2. https://blog.csdn.net/cxgincsu/article/details/74451940?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

SGBM算法参数众多,而且对于不同场景默认参数表现不好,这个问题也出现在了BM算法里面,由于接触的不多,并不清楚这个问题是不是立体匹配的通病。

我使用了KITTI截取的两张图片作为输入,但是在前几次输入时发现一个小问题,我的表现效果和博客上表现的相差极大:

这是我输出的时差图,可以明显看出这个图片表述的一塌糊涂,一开始我还以为是我参数的问题,所以我调了半天的参数,但是情况并没有本质的改变。于是我向沈老师请教。

沈老师给了我一个很有用的表格:

但是效果仍然改变不大。这时候老师突然想起来:你是不是左右图颠倒了?这个算法对于左右视图顺序是有要求的。

我试了一下,果然。下图是ndisparities为64、80、128时:

可以看出随着ndisparities逐渐增加,右侧的碎片数量逐渐减少。到128时基本符合我们的要求。当然,亮度有所下降,但是因为距离拉大了,这个倒是很正常。

值得注意的一点是disp这个参数,opencv解释

disp – Output disparity map. It is a 16-bit signed single-channel image of the same size as the input image. It contains disparity values scaled by 16. So, to get the floating-point disparity map, you need to divide each disp element by 16.

这个意思是:disp每个元素包含16位,其中12位整数,4位小数。如果转化为可读形式的,需要对1.0/16;

对应类型和比特的博客参考:
https://blog.csdn.net/YunLaowang/article/details/86583351

为了验证得到的时差图是否正确,我们需要手工标注验证一下:找出左图和右图中对应的一组特征点,打开画图工具得到他们的坐标,一般来说纵坐标是相同的。将横坐标相减得到时差,在时差图中找到改点(位置同左图),访问该点的元素。

当然,获得视差图仅仅是第一步,后面我们需要通过视差图来获得深度图。

获得深度图之前需要先对视差图进行预处理,我们预处理选择的是之前引用博客中作者给出的方法,直接copy来测试,然后利用视差公式求解深度。

这里有5点注意的地方:

  1. 视差图左边黑条不能去:左边黑条代表右侧相机没有采集到的位置,因此是没有深度的。

  2. 深度图的表示问题:如果仅仅使用8UC1还是没有问题的,但是万一需要使用16UC1及更高的32FC1等时,显示范围扩大到了255以上,这样在imshow基本就是一片黑,无法验证我们的猜想。有一个巧妙的思路是利用.xml文件采集图片的矩阵元素,这样可以看整体的情况,虽不是很直观,但是比一抹黑强很多。关于xml文件的使用详见博客https://blog.csdn.net/YunLaowang/article/details/86583351。另一种方法是对图像像素值进行缩放,至0-255之前,这样可以之间观察像素变化,但是缺点是由于缩放原因,像素差值不大。两种方法各有利弊,综合使用效果更好。

  3. 在对深度值缩放至0-255之间以方便进行表示时,删去空洞填充中高斯模糊部分。因为深度缩放后差距极小,很容易全部变成一种颜色。

  4. 一开始我模仿博客代码选择16UC1,但是由于不便于直接观察,因此我试图将depthMap选择我8UC1格式,但是结果显示,道路部分深度误差较大问题,在时差图上表现良好的道路在深度图中出现了明显的层次,详见下面:

    经过老师沟通,发现是范围出现了问题,8U表示范围在2的8次方,255,而最大深度值f*b/1=718.856*35=25159.96,远超8位depthMap所能表示的数据范围,因此被截断了,出现了奇怪的现象。修正方法详见第二点,修正后的图片如下:

    测试代码如下:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <cmath>
//#include <Eigen/Core>
//#include <Eigen/Geometry>
using namespace std;
using namespace cv;void insertDepth32f(cv::Mat& depth);float fx = 718.856;
float baseline = 35;int main(int argc, char const *argv[])
{cv::Mat imgR,imgL;imgL = cv::imread( argv[1],IMREAD_GRAYSCALE );imgR = cv::imread( argv[2],IMREAD_GRAYSCALE );int mindisparity = 0;int ndisparities = 128;int SADWindowSize = 11;//SGBMcv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(mindisparity, ndisparities, SADWindowSize);int P1 = 4 * imgL.channels() * SADWindowSize* SADWindowSize;int P2 = 32 * imgR.channels() * SADWindowSize* SADWindowSize;sgbm->setP1(P1);sgbm->setP2(P2);sgbm->setPreFilterCap(63);sgbm->setUniquenessRatio(10);sgbm->setSpeckleRange(32);sgbm->setSpeckleWindowSize(100);sgbm->setDisp12MaxDiff(1);//sgbm->setMode(cv::StereoSGBM::MODE_HH);cv::Mat disp32F;sgbm->compute(imgL, imgR, disp32F);disp32F.convertTo(disp32F, CV_32F, 1.0/16);//float* inData = disp32F.ptr<float>(284);//cout << float(inData[322]) << endl;//insertDepth32f(disp32F);Mat disp8U = Mat(disp32F.rows, disp32F.cols, CV_8UC1);//normalize(disp8U, disp32F, 0, 255, NORM_MINMAX, CV_8UC1);disp32F.convertTo(disp8U, CV_8UC1);imshow("disparity", disp8U);waitKey(0);cv::Mat depthMap = cv::Mat::zeros(disp32F.size(), CV_32FC1);int height = disp32F.rows;int width = disp32F.cols;for(int k = 0;k < height; k++){const float* inData = disp32F.ptr<float>(k);float* outData = depthMap.ptr<float>(k);for(int i = 0; i < width; i++){if(!inData[i]) continue;outData[i] = float(fx *baseline / inData[i]);}}FileStorage fswrite("test.xml", FileStorage::WRITE);// 新建文件,覆盖掉已有文件fswrite << "src1" << depthMap;fswrite.release();Mat depthMap8U = Mat(depthMap.rows, depthMap.cols, CV_8UC1);normalize(depthMap, depthMap8U, 0, 255, NORM_MINMAX, CV_8U);imshow("depth:8U",depthMap8U);waitKey(0);return 0;
}void insertDepth32f(cv::Mat& depth)
{const int width = depth.cols;const int height = depth.rows;float* data = (float*)depth.data;cv::Mat integralMap = cv::Mat::zeros(height, width, CV_64F);cv::Mat ptsMap = cv::Mat::zeros(height, width, CV_32S);double* integral = (double*)integralMap.data;int* ptsIntegral = (int*)ptsMap.data;memset(integral, 0, sizeof(double) * width * height);memset(ptsIntegral, 0, sizeof(int) * width * height);for (int i = 0; i < height; ++i){int id1 = i * width;for (int j = 0; j < width; ++j){int id2 = id1 + j;if (data[id2] > 1e-3){integral[id2] = data[id2];ptsIntegral[id2] = 1;}}}// 积分区间for (int i = 0; i < height; ++i){int id1 = i * width;for (int j = 1; j < width; ++j){int id2 = id1 + j;integral[id2] += integral[id2 - 1];ptsIntegral[id2] += ptsIntegral[id2 - 1];}}for (int i = 1; i < height; ++i){int id1 = i * width;for (int j = 0; j < width; ++j){int id2 = id1 + j;integral[id2] += integral[id2 - width];ptsIntegral[id2] += ptsIntegral[id2 - width];}}int wnd;double dWnd = 2;while (dWnd > 1){wnd = int(dWnd);dWnd /= 2;for (int i = 0; i < height; ++i){int id1 = i * width;for (int j = 0; j < width; ++j){int id2 = id1 + j;int left = j - wnd - 1;int right = j + wnd;int top = i - wnd - 1;int bot = i + wnd;left = max(0, left);right = min(right, width - 1);top = max(0, top);bot = min(bot, height - 1);int dx = right - left;int dy = (bot - top) * width;int idLeftTop = top * width + left;int idRightTop = idLeftTop + dx;int idLeftBot = idLeftTop + dy;int idRightBot = idLeftBot + dx;int ptsCnt = ptsIntegral[idRightBot] + ptsIntegral[idLeftTop] - (ptsIntegral[idLeftBot] + ptsIntegral[idRightTop]);double sumGray = integral[idRightBot] + integral[idLeftTop] - (integral[idLeftBot] + integral[idRightTop]);if (ptsCnt <= 0){continue;}data[id2] = float(sumGray / ptsCnt);}}int s = wnd / 2 * 2 + 1;if (s > 201){s = 201;}//cv::GaussianBlur(depth, depth, cv::Size(s, s), s, s);}
}

KITTI下使用SGBM立体匹配算法获得深度图相关推荐

  1. [双目视差] 立体匹配算法推理 - SGBM算法(一)

    文章目录 立体匹配算法推理 - SGBM算法(一) 一.SGBM与SGM的区别 二.代价计算 立体匹配算法推理 - SGBM算法(一) SGBM立体匹配算法,总体来讲包含以下6个步骤: Preproc ...

  2. 双目立体匹配算法SGBM

    semi-global matching(SGM)是一种用于计算双目视觉中视差(disparity)的半全局匹配算法,在OpenCV中的实现为semi-global block matching(SG ...

  3. python 立体匹配算法_OpenCV3.4两种立体匹配算法效果对比

    以OpenCV自带的Aloe图像对为例: 1.BM算法(Block Matching) 参数设置如下: int numberOfDisparities = ((imgSize.width / 8) + ...

  4. 立体匹配算法-初步了解

    双目立体匹配一直是双目视觉的研究热点,双目相机拍摄同一场景的左.右两幅视点图像,运用立体匹配匹配算法获取视差图,进而获取深度图.而深度图的应用范围非常广泛,由于其能够记录场景中物体距离摄像机的距离,可 ...

  5. 双目立体匹配算法漫谈

    双目立体匹配算法漫谈 双目立体匹配算法漫谈 前提 一些基本假设 框架 matching cost computation cost (support) aggregation;代价聚合 双目立体匹配算 ...

  6. Census立体匹配算法算法的Python实现

    1.前言 Census作为立体匹配的代价函数之一,不论是局部立体匹配还是全局立体匹配都有很重要的作用,今天直入主题,直接给出代码并解释一下代码的实现,具体原理有很多优秀的博文已经贴出来了,本文不再赘述 ...

  7. 双目立体匹配算法:SGM

    一.简介   立体匹配旨在为校正后的左右视图提供稠密的匹配对,这种问题称为"stereo correspondence problem".有大量的算法用于求解立体匹配问题,根据Sc ...

  8. OpenCV中的立体图像创建深度图

    OpenCV中的立体图像创建深度图 1. 效果图 2. 源码 参考 这篇博客将介绍如何从立体图像创建深度图. 1. 效果图 原图 VS 视差图效果如下: 可以看到结果受到高度噪音的污染.通过调整 nu ...

  9. 双目立体匹配算法:Patch Match Stereo实用详解教程

    来源:CSDN 作者:dulingwen 01 简介 我们知道,现有立体匹配算法一般被分类为局部算法.全局算法和半全局算法,其中局部算法和半全局算法是应用最为广泛的.在局部算法中,一个最简单的做法就是 ...

最新文章

  1. 用于分子生成的数据高效性图语法学习
  2. linux定时任务案例,Linux定时任务案例
  3. 网络爬虫Java还是Python还是c++?
  4. 除了 MySQL 数据库,你还要了解的一些数据库
  5. tomcat的安装和部署项目
  6. 聚类(下) 谱聚类算法
  7. POJ 1833 排列【STL/next_permutation】
  8. LeetCode 1921. 消灭怪物的最大数量(排序)
  9. 解决js动态改变dom元素属性后页面及时渲染问题
  10. 信贷违约风险预测(四)TrAiNiNG MoDeL
  11. 【Stimulsoft Reports.WPF教程】在代码中使用报表变量
  12. oracle数据库简单的学多久,讲讲新人的oracle数据库学习
  13. 医院设备管理系统方案/案列/软件/APP/小程序/网站
  14. 有监督学习、无监督学习和半监督学习的分类
  15. 迭代法求根c语言程序对数方程,【实验一】方程求根:牛顿迭代法
  16. 手机mstsc远程工具_如何通过手机远程控制计算机
  17. hiveserver2 HA
  18. 做外贸如何防止邮箱被封?已解决!
  19. JavaScript debugger调试
  20. U盘量产,以及U盘“请将磁盘插入u盘”等问题。

热门文章

  1. 第2季极客沙龙资料分享 - 知行·前端体验主题交流会
  2. 显示网站统计量和访客地图
  3. Gym - 101635J - Frosting on the Cake
  4. 网管“北向接口”与“南向接口”
  5. 谷歌浏览器(chrome)在线翻译 解决方式
  6. 实时数据库:优势和报价
  7. 【ABAP】销售订单修改税率
  8. Oracle查询某一天数据的SQL语句的几种写法
  9. Pwn-高阶ROP-[栈溢出]/篇3
  10. 数仓项目——在线教育平台离线数据仓库效果展示