双目立体匹配 SAD

最近开始研究双目匹配算法,于是在网上代码网站比如CSDN,GITHUB,虽然找到很多代码,但是由于用的编译环境或者库不一样,导致有些代码无法理解,所以,为了得到自己想要的结果,本人花了几天的时间根据算法的基本原理,结合自己的编译环境,编写了代码。具体代码和步骤如下所示:
编译环境: visual studio 2015 + opencv3.2.0

先对SAD立体匹配算法做一个详细的解释(摘抄自某个作者的博客)。
SAD 算法:SAD算法是一种最简单的匹配算法,用公式表示为:

SAD(x,y,d) = Sum{|Left(x,y) - Right(x,y,d)|} 选择最小值,其中d为视差范围

此方法就是以左目图像的匹配点为中心,定义一个窗口D,其大小为 (2 * hwind+1) (2 * hwind+1),统计其窗口的灰度值的和,然后在右目图像中逐步计算其左右窗口的灰度和的差值,最后搜索到的差值最小的区域的中心像素即为匹配点。

基本流程:

1.构造一个小窗口,类似与卷积核。

2.用窗口覆盖左边的图像,选择出窗口覆盖区域内的所有像素点。

3.同样用窗口覆盖右边的图像并选择出覆盖区域的像素点。

4.左边覆盖区域减去右边覆盖区域,并求出所有像素点差的绝对值的和。

5.移动右边图像的窗口,重复3,4的动作。(这里有个搜索范围,超过这个范围跳出)

6.找到这个范围内SAD值最小的窗口,即找到了左边图像的最佳匹配的像素块。

以下这段话是对于双目立体匹配小白而言的,如果读者对于双目匹配有一定的了解,可以直接略过这段话直接看下面的程序。
有些人看到上面的这段定义就疑惑了,想着为什么两幅图像相减就能得到立体的图像呢。下面为大家解释一下。
双目立体匹配 SAD_第1张图片

双目立体匹配 SAD_第2张图片

大家看到上面的这两幅图像,renwu_left(以下统称为左图),renwu_right(以下统称为右图)。在这两个图像的最右边我标记了两个红色的方框。从图中可以看出,左图相对于右图相对靠左,所以说,在左图中要找到与右图相匹配的像素,就要在固定左图Left(x,y)的前提下,在右图中以同样的坐标为基点 Right(x,y),向左寻找匹配区域 Right(x,y,d)//d为选择移动的像素范围。 这样通过遍历图像,就可以得到匹配图像。又有读者问了,就算匹配上,那怎么得到深度呢?下面为读者解释一下(这个概念也是花了我好长时间才搞明白的)
双目立体匹配 SAD_第3张图片
双目立体匹配 SAD_第4张图片
从上面两幅图可以看出我们看出A点较B点近,所以A点的视差(x1-x1’)要大于B点的视差(x2-x2’)。(注意:在图中x1’,x2’为负数!!!!!!)所以在图中A,B,C三点(D为遮挡点,以后再讨论)的视差大小为:A>B>C,而三点的深度大小比较为:C>B>A,所以,深度和视差成反比。
双目立体匹配 SAD_第5张图片
(具体理论可以参考:http://blog.csdn.net/xiaohaijiejie/article/details/49721415)
讲了这么多,读者明白为什么可以凭借视差得到深度了吧。如果还是不明白,可以留言问我。下面上代码(结果不是很理想,以后会再改进。如果读者有更好的建议,可以留言)

//******************SAD************************
#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

//定义图片读取位置
string file_dir = "C:\\Program Files\\FLIR Integrated Imaging Solutions\\Triclops Stereo Vision SDK\\stereomatching\\Grab_Stereo\\pictures\\";
//定义图片存储位置
string save_dir = "C:\\Program Files\\FLIR Integrated Imaging Solutions\\Triclops Stereo Vision SDK\\stereomatching\\Grab_Stereo\\";
//-------------------定义Sad处理图像函数---------------------
//int sub_kernel(Mat &kernel_left, Mat &kernel_right);
//Mat Left_sad, Right_sad;

//--------------------得到Disparity图像-----------------------
int winSize = 7;//匹配窗口的大小   //可以改变大小
float sub_Sum;//存储匹配范围的视差和。声明为float类型是因为一个像素为8位的uchar类型,像素差加起来要比8位多,float是32位

int DSR = 30;//视差搜索范围   //可以改变大小

Mat getDisparity(Mat &left, Mat &right);//函数声明 以Mat类型返回

int main()
{
    //-----------------显示左右灰度图像---------------
    Mat LeftImg = imread(file_dir + "Renwu_left.png", 0);
    Mat RightImg = imread(file_dir + "Renwu_right.png", 0);
    Mat Disparity;
    namedWindow("Renwu_left", 1);
    namedWindow("Renwu_right", 1);
    imshow("Renwu_left", LeftImg);
    waitKey(5);
    imshow("Renwu_right", RightImg);
    waitKey(5);

    //------------------处理左右图得到视差图像---------
    Disparity = getDisparity(LeftImg, RightImg);
    namedWindow("Disparity", 1);
    imshow("Disparity", Disparity);
    waitKey(0);

    return 0;
}

int sub_kernel(Mat &kernel_left, Mat &kernel_right)
{
    Mat Dif;
    absdiff(kernel_left, kernel_right, Dif);
    Scalar Add ;
    Add = sum(Dif);
    sub_Sum = Add[0];
    return sub_Sum;//返回匹配窗像素相减之后的和
}

Mat getDisparity(Mat &left, Mat &right)
{
    double start, end;//定义开始处理图像的时间和结束时间
    start = getTickCount();
    int ImgHeight = left.rows;
    int ImgWidth = left.cols;

    //------------------处理图像kernel大小--------------
    Mat Kernel_L(Size(winSize, winSize), CV_8UC1, Scalar::all(0));
    Mat Kernel_R(Size(winSize, winSize), CV_8UC1, Scalar::all(0));
    Mat disparity(ImgHeight, ImgWidth, CV_8UC1,Scalar(0));//视差图


    for (int i = 0; i < ImgHeight - winSize; i++)
    {
        for (int j = 0; j < ImgWidth - winSize; j++)
        {
            Kernel_L = left(Rect(j, i, winSize, winSize));
            Mat Temp(1, DSR, CV_32F, Scalar(0));//之所以是float型,是因为求取两个图像的差之后相加得到的和可能会大于8位
            //------------------将左右图视差放入matchLevel数组中-----------
            for (int k = 0; k < DSR; k++)
            {
                int y = j - k;
                if (y >= 0)
                {
                    Kernel_R = right(Rect(y, i, winSize, winSize));
                    //---------------对左右图kernel进行处理---------------
                    Temp.at<float>(k) = sub_kernel(Kernel_L, Kernel_R);
                }
            }
            //---------------寻找最佳匹配点--------------
            Point minLoc;
            minMaxLoc(Temp, NULL, NULL, &minLoc, NULL);

            int loc = minLoc.x;//之所以是x坐标,请参考我文中的解释
            disparity.at(i, j) = loc * 16;
        }
    }
    //记录运行 时间
    end = getTickCount();
    double time = ((end - start)/CLOCKS_PER_SEC);
    cout << "Time is : " << time << "ms" << endl;
    return disparity;
}

运行结果图如下
双目立体匹配 SAD_第6张图片

你可能感兴趣的:(stereo,matching-双目匹配)