【双目视觉探索路2】获取视差图(未完待续)

来源:互联网 发布:自学游戏制作 知乎 编辑:程序博客网 时间:2024/05/20 16:44

如前所述,本系列首先通过视差图的实现,从直观的角度对双目立体视觉首先进行个大概的了解。

立体视觉的整体概述:

双目立体视觉测物体空间信息的流程顺序应为:
如前一讲所述,整个双目立体视觉的实现流程可分为标定以及立体视觉的实现。

标定:

标定的目的为获得相机的内外参,其基本原理是基于相机成像平面与空间的映射对应关系(单应性)。内参为:主点位置(cx,cy),横纵向的焦距(fx,fy);外参为相机成像平面与标定平面的R(rotation)、T(transtion)矩阵。

立体视觉实现:

后面的立体标定、立体矫正、立体匹配以及映射到三维的深度映射四个部分归属于立体视觉实现这一部分内容。
由上一讲可知:立体视觉的基本模型是基于双目前向平行,而现实中基本不可能做到这一点,故需要通过算法进行解决。立体标定、矫正以及匹配的大部分就是基于这个目的而采用的方法。
立体标定的目的是获取左右相机的空间转换关系,通过这一步可以获取两种矩阵:本征矩阵(E)以及基础矩阵(F)。
立体矫正的目的是使得原来不平行的图像成为行对准图像,其利用了立体标定的R/T阵,输出行对准图像,为下一步匹配的工作量减少提供了便捷。
立体匹配的目的是输出视差图,输入为经过立体矫正的图像,输出服务于下一步:映射到三维空间坐标。
立体标定



立体校正

参考资料:

1,机器视觉学习笔记(8)----基于OpenCV的Bouguet立体校正

依据双目标定可以计算出两个相机坐标系关系的矩阵:R&T,立体校正的目的就是转换为前向平行的双目配置。
Bouguet校正的原理为:将Rrect左乘到R分解后作用于左右坐标系的矩阵。

重投影矩阵

重投影矩阵Q实现了世界坐标系与像素坐标系之间的转换

Q[x,y,d,1]T=[X,Y,Z,W]T

代码实现

一共有三个过程
计算R&T矩阵->计算校正LUT->校正两幅图像检验结果
计算R&T矩阵
调用stereoRectify函数即可
计算校正查找映射表
该部分作用是将原始图像和矫正后的图像上的点进行一一对应,调用函数为initUndistortRectifyMap函数。请注意:是分别对左右相机进行校正查找映射






--------------------------------------------------------------------------------------------------------------------

由已知内外参数输出视差图

实现这一过程主要涉及的内容为立体矫正以及立体匹配。立体矫正可以通过调用opencv中的函数进行实现,而立体匹配有几种不同的方法,可以从中选一种自己看的比较顺眼的匹配方法进行探索。
这一部分首先将扔出一个相关的实验程序以及最终的实验结果;在实验的结果上对这一部分内容进行补充。

再扔一下这部分涉及的链接:
1.OpenCV学习笔记(18)双目测距与三维重建的OpenCV实现问题集锦(三)立体匹配与视差计算
2.Opencv立体匹配算法BM、SGBM、GC算法的状态参数
3.SAD、BM、SGBM算法获得视差图
(链接内部程序通过OpenCV1.0编写,本着求新及保护作者作品的目的,本文将利用OpenCV3.0进行相应的改动编写,这两幅图像为资源中的原始数据)
4.SAD算法在opencv上的实现代码(C++)
5.双目立体视觉匹配算法-----SAD匹配算法、BM算法、SGBM算法、GC算法     
6.BM、GC、SGBM三种算法的比较和BM在OpenCV3下的参数设置
7.【图像处理】基于opencv3.0使用StereoBM计算视差
基础知识
SAD匹配算法
原理:SAD(Sum of Absolute Differences)




程序实现:

Raw data:


Experiment Results:

SAD视差图


Coding:

1,SAD

参考代码:双目立体视觉SAD匹配算法

class SAD{private:int winSize;//卷积核尺寸int DSR;//视差搜索范围public:SAD() :winSize(7), DSR(30){}SAD(int _winSize, int _DSR) :winSize(_winSize), DSR(_DSR){}Mat computerSAD(Mat&L, Mat&R);//计算SAD};Mat SAD::computerSAD(Mat&L, Mat&R){int Height = L.rows;int Width = L.cols;Mat Kernel_L(Size(winSize, winSize), CV_8U, Scalar::all(0));//CV_8U:0~255的值,大多数图像/视频的格式,该段设置全0矩阵Mat Kernel_R(Size(winSize, winSize), CV_8U, Scalar::all(0));Mat Disparity(Height, Width, CV_8U, Scalar(0));for (int i = 0; i < Width - winSize; ++i){for (int j = 0; j < Height - winSize; ++j){Kernel_L = L(Rect(i, j, winSize, winSize));//L为做图像,Kernel为这个范围内的左图Mat MM(1, DSR, CV_32F, Scalar(0));//定义匹配范围for (int k = 0; k < DSR; ++k){int x = i - k;if (x >= 0){Kernel_R = R(Rect(x, j, winSize, winSize));Mat Dif;absdiff(Kernel_L, Kernel_R, Dif);Scalar ADD = sum(Dif);float a = ADD[0];MM.at<float>(k) = a;}Point minLoc;minMaxLoc(MM, NULL, NULL, &minLoc, NULL);int loc = minLoc.x;Disparity.at<char>(j, i) = loc * 16;}double rate = double(i) / (Width);cout << "已完成" << setprecision(2) << rate * 100 << "%" << endl;}}return Disparity;}int main(){Mat left = imread("1.png");Mat right = imread("2.png");//-------图像显示-----------namedWindow("leftimag");imshow("leftimag", left);namedWindow("rightimag");imshow("rightimag", right);//--------由SAD求取视差图-----Mat Disparity;SAD mySAD(7, 30);Disparity = mySAD.computerSAD(left, right);//-------结果显示------namedWindow("Disparity");imshow("Disparity",Disparity);//-------收尾------waitKey(0);return 0;}

程序解读:

可以看到SAD算法的效果确实很一般,而且在实际运行过程中,结果图的边界会出现黑色块的现象。

主程序中先定义了卷积核尺寸为7,视差搜索范围为30,参见构造函数Mat SAD():winSize(),DSR(){}

computerSAD计算出视差图。

其程序实现如下:

首先定义左右两幅图像的kernel,大小为winSize尺寸,并定义视差图Mat矩阵

----------------------------------------------------------------------------------------------------------------

第二步就是在图像内实现块的遍历匹配

块的大小为winSize,范围为(0:width-winSize,0:height-winSize);

遍历方法如下

for (int k = 0; k < DSR; ++k){int x = i - k;if (x >= 0){Kernel_R = R(Rect(x, j, winSize, winSize));Mat Dif;absdiff(Kernel_L, Kernel_R, Dif);Scalar ADD = sum(Dif);float a = ADD[0];MM.at<float>(k) = a;}

排序后便可输出视差图。


SGBM算法是一种全局匹配算法,立体匹配的效果明显好于局部匹配算法,在OpenCV中是把SGBM作为一个类对该算法进行实现的。