zoukankan      html  css  js  c++  java
  • OpenCV stereo matching 代码 matlab实现视差显示

    转载请注明出处:http://blog.csdn.net/wangyaninglm/article/details/44151213

    来自:shiter编写程序的艺术


    • 基础知识

    计算机视觉是一门研究使用计算机来模拟人的视觉系统的学科。“一图胜千言”,人类对于图像中的信息感知效率远超文字等其他媒介,人类获取的信息总量中更是有高达80%依靠视觉系统[1]。相对于人类高效的图像信息提取能力,计算机在图像信息的理解上仍然效率低下。 
    计算机视觉作为一门交叉学科,综合了生物学,心理学,数学,计算机科学等学科,从20世纪60年代至今其在科学研究领域中的大量成果已经应用于工程领域,并影响了我们每个人生活的方方面面。 
    双目立体视觉是计算机视觉领域的重要分支,它通过模拟人的视觉系统来处理现实世界。以机器人,无人汽车导航为例,由于双目立体匹配在非接触测量中的优秀性能,视觉测量在探月工程,火星探测工程中起到了重要作用[2],如图所示的我国嫦娥探月工程的巡航车就配备了立体视觉导航系统,来进行行进间的运动控制和路径规划[3]。
     

    主要参考:http://blog.csdn.net/wangyaninglm/article/details/51533549

     

     之前在网上也没有现成的代码,现在把库中的sample拿出来,分享下

    /*
     *  stereo_match.cpp
     *  calibration
     *
     *  Created by Victor  Eruhimov on 1/18/10.
     *  Copyright 2010 Argus Corp. All rights reserved.
     *
     */
    
    #include "opencv2/calib3d/calib3d.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/contrib/contrib.hpp"
    
    #include <stdio.h>
    
    using namespace cv;
    
    static void print_help()
    {
        printf("
    Demo stereo matching converting L and R images into disparity and point clouds
    ");
        printf("
    Usage: stereo_match <left_image> <right_image> [--algorithm=bm|sgbm|hh|var] [--blocksize=<block_size>]
    "
               "[--max-disparity=<max_disparity>] [--scale=scale_factor>] [-i <intrinsic_filename>] [-e <extrinsic_filename>]
    "
               "[--no-display] [-o <disparity_image>] [-p <point_cloud_file>]
    ");
    }
    
    static void saveXYZ(const char* filename, const Mat& mat)
    {
        const double max_z = 1.0e4;
        FILE* fp = fopen(filename, "wt");
        for(int y = 0; y < mat.rows; y++)
        {
            for(int x = 0; x < mat.cols; x++)
            {
                Vec3f point = mat.at<Vec3f>(y, x);
                if(fabs(point[2] - max_z) < FLT_EPSILON || fabs(point[2]) > max_z) continue;
                fprintf(fp, "%f %f %f
    ", point[0], point[1], point[2]);
            }
        }
        fclose(fp);
    }
    
    int main(int argc, char** argv)
    {
        const char* algorithm_opt = "--algorithm=";
        const char* maxdisp_opt = "--max-disparity=";
        const char* blocksize_opt = "--blocksize=";
        const char* nodisplay_opt = "--no-display";
        const char* scale_opt = "--scale=";
    
        if(argc < 3)
        {
            print_help();
            return 0;
        }
        const char* img1_filename = 0;
        const char* img2_filename = 0;
        const char* intrinsic_filename = 0;
        const char* extrinsic_filename = 0;
        const char* disparity_filename = 0;
        const char* point_cloud_filename = 0;
    
        enum { STEREO_BM=0, STEREO_SGBM=1, STEREO_HH=2, STEREO_VAR=3 };
        int alg = STEREO_SGBM;
        int SADWindowSize = 0, numberOfDisparities = 0;
        bool no_display = false;
        float scale = 1.f;
    
        StereoBM bm;
        StereoSGBM sgbm;
        StereoVar var;
    
        for( int i = 1; i < argc; i++ )
        {
            if( argv[i][0] != '-' )
            {
                if( !img1_filename )
                    img1_filename = argv[i];
                else
                    img2_filename = argv[i];
            }
            else if( strncmp(argv[i], algorithm_opt, strlen(algorithm_opt)) == 0 )
            {
                char* _alg = argv[i] + strlen(algorithm_opt);
                alg = strcmp(_alg, "bm") == 0 ? STEREO_BM :
                      strcmp(_alg, "sgbm") == 0 ? STEREO_SGBM :
                      strcmp(_alg, "hh") == 0 ? STEREO_HH :
                      strcmp(_alg, "var") == 0 ? STEREO_VAR : -1;
                if( alg < 0 )
                {
                    printf("Command-line parameter error: Unknown stereo algorithm
    
    ");
                    print_help();
                    return -1;
                }
            }
            else if( strncmp(argv[i], maxdisp_opt, strlen(maxdisp_opt)) == 0 )
            {
                if( sscanf( argv[i] + strlen(maxdisp_opt), "%d", &numberOfDisparities ) != 1 ||
                    numberOfDisparities < 1 || numberOfDisparities % 16 != 0 )
                {
                    printf("Command-line parameter error: The max disparity (--maxdisparity=<...>) must be a positive integer divisible by 16
    ");
                    print_help();
                    return -1;
                }
            }
            else if( strncmp(argv[i], blocksize_opt, strlen(blocksize_opt)) == 0 )
            {
                if( sscanf( argv[i] + strlen(blocksize_opt), "%d", &SADWindowSize ) != 1 ||
                    SADWindowSize < 1 || SADWindowSize % 2 != 1 )
                {
                    printf("Command-line parameter error: The block size (--blocksize=<...>) must be a positive odd number
    ");
                    return -1;
                }
            }
            else if( strncmp(argv[i], scale_opt, strlen(scale_opt)) == 0 )
            {
                if( sscanf( argv[i] + strlen(scale_opt), "%f", &scale ) != 1 || scale < 0 )
                {
                    printf("Command-line parameter error: The scale factor (--scale=<...>) must be a positive floating-point number
    ");
                    return -1;
                }
            }
            else if( strcmp(argv[i], nodisplay_opt) == 0 )
                no_display = true;
            else if( strcmp(argv[i], "-i" ) == 0 )
                intrinsic_filename = argv[++i];
            else if( strcmp(argv[i], "-e" ) == 0 )
                extrinsic_filename = argv[++i];
            else if( strcmp(argv[i], "-o" ) == 0 )
                disparity_filename = argv[++i];
            else if( strcmp(argv[i], "-p" ) == 0 )
                point_cloud_filename = argv[++i];
            else
            {
                printf("Command-line parameter error: unknown option %s
    ", argv[i]);
                return -1;
            }
        }
    
        if( !img1_filename || !img2_filename )
        {
            printf("Command-line parameter error: both left and right images must be specified
    ");
            return -1;
        }
    
        if( (intrinsic_filename != 0) ^ (extrinsic_filename != 0) )
        {
            printf("Command-line parameter error: either both intrinsic and extrinsic parameters must be specified, or none of them (when the stereo pair is already rectified)
    ");
            return -1;
        }
    
        if( extrinsic_filename == 0 && point_cloud_filename )
        {
            printf("Command-line parameter error: extrinsic and intrinsic parameters must be specified to compute the point cloud
    ");
            return -1;
        }
    
        int color_mode = alg == STEREO_BM ? 0 : -1;
        Mat img1 = imread(img1_filename, color_mode);
        Mat img2 = imread(img2_filename, color_mode);
    
        if( scale != 1.f )
        {
            Mat temp1, temp2;
            int method = scale < 1 ? INTER_AREA : INTER_CUBIC;
            resize(img1, temp1, Size(), scale, scale, method);
            img1 = temp1;
            resize(img2, temp2, Size(), scale, scale, method);
            img2 = temp2;
        }
    
        Size img_size = img1.size();
    
        Rect roi1, roi2;
        Mat Q;
    
        if( intrinsic_filename )
        {
            // reading intrinsic parameters
            FileStorage fs(intrinsic_filename, CV_STORAGE_READ);
            if(!fs.isOpened())
            {
                printf("Failed to open file %s
    ", intrinsic_filename);
                return -1;
            }
    
            Mat M1, D1, M2, D2;
            fs["M1"] >> M1;
            fs["D1"] >> D1;
            fs["M2"] >> M2;
            fs["D2"] >> D2;
    
            M1 *= scale;
            M2 *= scale;
    
            fs.open(extrinsic_filename, CV_STORAGE_READ);
            if(!fs.isOpened())
            {
                printf("Failed to open file %s
    ", extrinsic_filename);
                return -1;
            }
    
            Mat R, T, R1, P1, R2, P2;
            fs["R"] >> R;
            fs["T"] >> T;
    
            stereoRectify( M1, D1, M2, D2, img_size, R, T, R1, R2, P1, P2, Q, CALIB_ZERO_DISPARITY, -1, img_size, &roi1, &roi2 );
    
            Mat map11, map12, map21, map22;
            initUndistortRectifyMap(M1, D1, R1, P1, img_size, CV_16SC2, map11, map12);
            initUndistortRectifyMap(M2, D2, R2, P2, img_size, CV_16SC2, map21, map22);
    
            Mat img1r, img2r;
            remap(img1, img1r, map11, map12, INTER_LINEAR);
            remap(img2, img2r, map21, map22, INTER_LINEAR);
    
            img1 = img1r;
            img2 = img2r;
        }
    
        numberOfDisparities = numberOfDisparities > 0 ? numberOfDisparities : ((img_size.width/8) + 15) & -16;
    
        bm.state->roi1 = roi1;
        bm.state->roi2 = roi2;
        bm.state->preFilterCap = 31;
        bm.state->SADWindowSize = SADWindowSize > 0 ? SADWindowSize : 9;
        bm.state->minDisparity = 0;
        bm.state->numberOfDisparities = numberOfDisparities;
        bm.state->textureThreshold = 10;
        bm.state->uniquenessRatio = 15;
        bm.state->speckleWindowSize = 100;
        bm.state->speckleRange = 32;
        bm.state->disp12MaxDiff = 1;
    
        sgbm.preFilterCap = 63;
        sgbm.SADWindowSize = SADWindowSize > 0 ? SADWindowSize : 3;
    
        int cn = img1.channels();
    
        sgbm.P1 = 8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;
        sgbm.P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;
        sgbm.minDisparity = 0;
        sgbm.numberOfDisparities = numberOfDisparities;
        sgbm.uniquenessRatio = 10;
        sgbm.speckleWindowSize = bm.state->speckleWindowSize;
        sgbm.speckleRange = bm.state->speckleRange;
        sgbm.disp12MaxDiff = 1;
        sgbm.fullDP = alg == STEREO_HH;
    
        var.levels = 3;                                 // ignored with USE_AUTO_PARAMS
        var.pyrScale = 0.5;                             // ignored with USE_AUTO_PARAMS
        var.nIt = 25;
        var.minDisp = -numberOfDisparities;
        var.maxDisp = 0;
        var.poly_n = 3;
        var.poly_sigma = 0.0;
        var.fi = 15.0f;
        var.lambda = 0.03f;
        var.penalization = var.PENALIZATION_TICHONOV;   // ignored with USE_AUTO_PARAMS
        var.cycle = var.CYCLE_V;                        // ignored with USE_AUTO_PARAMS
        var.flags = var.USE_SMART_ID | var.USE_AUTO_PARAMS | var.USE_INITIAL_DISPARITY | var.USE_MEDIAN_FILTERING ;
    
        Mat disp, disp8;
        //Mat img1p, img2p, dispp;
        //copyMakeBorder(img1, img1p, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);
        //copyMakeBorder(img2, img2p, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);
    
        int64 t = getTickCount();
        if( alg == STEREO_BM )
            bm(img1, img2, disp);
        else if( alg == STEREO_VAR ) {
            var(img1, img2, disp);
        }
        else if( alg == STEREO_SGBM || alg == STEREO_HH )
            sgbm(img1, img2, disp);
        t = getTickCount() - t;
        printf("Time elapsed: %fms
    ", t*1000/getTickFrequency());
    
        //disp = dispp.colRange(numberOfDisparities, img1p.cols);
        if( alg != STEREO_VAR )
            disp.convertTo(disp8, CV_8U, 255/(numberOfDisparities*16.));
        else
            disp.convertTo(disp8, CV_8U);
        if( !no_display )
        {
            namedWindow("left", 1);
            imshow("left", img1);
            namedWindow("right", 1);
            imshow("right", img2);
            namedWindow("disparity", 0);
            imshow("disparity", disp8);
            printf("press any key to continue...");
            fflush(stdout);
            waitKey();
            printf("
    ");
        }
    
        if(disparity_filename)
            imwrite(disparity_filename, disp8);
    
        if(point_cloud_filename)
        {
            printf("storing the point cloud...");
            fflush(stdout);
            Mat xyz;
            reprojectImageTo3D(disp, xyz, Q, true);
            saveXYZ(point_cloud_filename, xyz);
            printf("
    ");
        }
    
        return 0;
    }
    


     

     调试参数:

    view_l.png view_r.png --algorithm=bm --blocksize=5 --max-disparity=256  --scale=1.0 --no-display -o disparity.bmp

    立体匹配效果:

     

    根据大牛的代码增加一个函数:实现视差数据保存成txt又matlab显示

    void saveDisp(const char* filename, const Mat& mat)		
    {
    	FILE* fp = fopen(filename, "wt");
    	fprintf(fp, "%02d
    ", mat.rows);
    	fprintf(fp, "%02d
    ", mat.cols);
    	for(int y = 0; y < mat.rows; y++)
    	{
    		for(int x = 0; x < mat.cols; x++)
    		{
    			int disp = (int)mat.at<float>(y, x);	// 这里视差矩阵是CV_16S 格式的,故用 short 类型读取
    			fprintf(fp, "%d
    ", disp);			// 若视差矩阵是 CV_32F 格式,则用 float 类型读取
    		}
    		//fprintf(fp, "
    ");
    	}
    	fclose(fp);
    }

    matlab代码:

    function img = txt2img(filename)
    data = importdata(filename);
    r = data(1);    % 行数
    c = data(2);    % 列数
    disp = data(3:end); % 视差
    vmin = min(disp);
    vmax = max(disp);
    disp = reshape(disp, [c,r])'; % 将列向量形式的 disp 重构为 矩阵形式
    %  OpenCV 是行扫描存储图像,Matlab 是列扫描存储图像
    %  故对 disp 的重新排列是首先变成 c 行 r 列的矩阵,然后再转置回 r 行 c 列
    img = uint8( 255 * ( disp - vmin ) / ( vmax - vmin ) );
    mesh(disp);
    set(gca,'YDir','reverse');  % 通过 mesh 方式绘图时,需倒置 Y 轴方向
    axis tight; % 使坐标轴显示范围与数据范围相贴合,去除空白显示区
    


     

    • 实现效果


     

    • 大牛博客中的解释

    1. opencv2.1和opencv2.0在做stereo vision方面有什么区别了?

    2.1版增强了Stereo Vision方面的功能:

    (1) 新增了 SGBM 立体匹配算法(源自Heiko Hirschmuller的《Stereo Processing by Semi-global Matching and Mutual Information》),可以获得比 BM 算法物体轮廓更清晰的视差图(但低纹理区域容易出现横/斜纹路,在 GCstate->fullDP 选项使能时可消减这种异常纹路,但对应区域视差变为0,且运行速度会有所下降),速度比 BM 稍慢, 352*288的帧处理速度大约是 5 帧/秒;

    (2) 视差效果:BM < SGBM < GC;处理速度:BM > SGBM > GC ;

    (3) BM 算法比2.0版性能有所提升,其状态参数新增了对左右视图感兴趣区域 ROI 的支持(roi1 和 roi2,由stereoRectify函数产生);

    (4) BM 算法和 GC 算法的核心代码改动不大,主要是面向多线程运算方面的(由 OpenMP 转向 Intel TBB);

    (5) cvFindStereoCorrespondenceBM 函数的disparity参数的数据格式新增了 CV_32F 的支持,这种格式的数据给出实际视差,而 2.0 版只支持 CV_16S,需要除以 16.0 才能得到实际的视差数值。

    2. 用于立体匹配的图像可以是彩色的吗?

    在OpenCV2.1中,BM和GC算法只能对8位灰度图像计算视差,SGBM算法则可以处理24位(8bits*3)彩色图像。所以在读入图像时,应该根据采用的算法来处理图像:

    int color_mode = alg == STEREO_SGBM ? 1 : 0;
    //////////////////////////////////////////////////////////////////////////
    // 载入图像
    cvGrabFrame( lfCam );
    cvGrabFrame( riCam );
    frame1 = cvRetrieveFrame( lfCam );
    frame2 = cvRetrieveFrame( riCam );
    if(frame1.empty()) break;
    resize(frame1, img1, img_size, 0, 0);
    resize(frame2, img2, img_size, 0, 0);
    // 选择彩色或灰度格式作为双目匹配的处理图像
    if (!color_mode && cn>1)
    {
    cvtColor(img1, img1gray, CV_BGR2GRAY);
    cvtColor(img2, img2gray, CV_BGR2GRAY);
    img1p = img1gray;
    img2p = img2gray;
    }
    else
    {
    img1p = img1;
    img2p = img2;
    }

    3. 怎样获取与原图像有效像素区域相同的视差图?

    OpenCV2.0及以前的版本中,所获取的视差图总是在左侧和右侧有明显的黑色区域,这些区域没有有效的视差数据。视差图有效像素区域与视差窗口(ndisp,一般取正值且能被16整除)和最小视差值(mindisp,一般取0或负值)相关,视差窗口越大,视差图左侧的黑色区域越大,最小视差值越小,视差图右侧的黑色区域越大。其原因是为了保证参考图像(一般是左视图)的像素点能在目标图像(右视图)中按照设定的视差匹配窗口匹配对应点,OpenCV 只从参考图像的第 (ndisp - 1 + mindisp) 列开始向右计算视差,第 0 列到第 (ndisp - 1 + mindisp) 列的区域视差统一设置为 (mindisp - 1) *16;视差计算到第 width + mindisp 列时停止,余下的右侧区域视差值也统一设置为 (mindisp - 1) *16。  

    00177 static const int DISPARITY_SHIFT = 4;
    …
    00411     int ndisp = state->numberOfDisparities;
    00412     int mindisp = state->minDisparity;
    00413     int lofs = MAX(ndisp - 1 + mindisp, 0);
    00414     int rofs = -MIN(ndisp - 1 + mindisp, 0);
    00415     int width = left->cols, height = left->rows;
    00416     int width1 = width - rofs - ndisp + 1;
    …
    00420     short FILTERED = (short)((mindisp - 1) << DISPARITY_SHIFT);
    …
    00466     // initialize the left and right borders of the disparity map
    00467     for( y = 0; y < height; y++ )
    00468     {
    00469         for( x = 0; x < lofs; x++ )
    00470             dptr[y*dstep + x] = FILTERED;
    00471         for( x = lofs + width1; x < width; x++ )
    00472             dptr[y*dstep + x] = FILTERED;
    00473     }
    00474     dptr += lofs;
    00475
    00476     for( x = 0; x < width1; x++, dptr++ )
    
    …
    

    这样的设置很明显是不符合实际应用的需求的,它相当于把摄像头的视场范围缩窄了。因此,OpenCV2.1 做了明显的改进,不再要求左右视图和视差图的大小(size)一致,允许对视差图进行左右边界延拓,这样,虽然计算视差时还是按上面的代码思路来处理左右边界,但是视差图的边界得到延拓后,有效视差的范围就能够与对应视图完全对应。具体的实现代码范例如下:

    //////////////////////////////////////////////////////////////////////////
    // 对左右视图的左边进行边界延拓,以获取与原始视图相同大小的有效视差区域
    copyMakeBorder(img1r, img1b, 0, 0, m_nMaxDisp, 0, IPL_BORDER_REPLICATE);
    copyMakeBorder(img2r, img2b, 0, 0, m_nMaxDisp, 0, IPL_BORDER_REPLICATE);
    
    //////////////////////////////////////////////////////////////////////////
    // 计算视差
    if( alg == STEREO_BM )
    {
    	bm(img1b, img2b, dispb);
    	// 截取与原始画面对应的视差区域(舍去加宽的部分)
    	displf = dispb.colRange(m_nMaxDisp, img1b.cols);	
    }
    else if(alg == STEREO_SGBM)
    {
    	sgbm(img1b, img2b, dispb);
    	displf = dispb.colRange(m_nMaxDisp, img1b.cols);
    }
    

    4. cvFindStereoCorrespondenceBM的输出结果好像不是以像素点为单位的视差?

    @scyscyao:在OpenCV2.0中,BM函数得出的结果是以16位符号数的形式的存储的,出于精度需要,所有的视差在输出时都扩大了16倍(2^4)。其具体代码表示如下:

    dptr[y*dstep] = (short)(((ndisp - mind - 1 + mindisp)*256 + (d != 0 ? (p-n)*128/d : 0) + 15) >> 4);

    可以看到,原始视差在左移8位(256)并且加上一个修正值之后又右移了4位,最终的结果就是左移4位。

    因此,在实际求距离时,cvReprojectTo3D出来的X/W,Y/W,Z/W都要乘以16 (也就是W除以16),才能得到正确的三维坐标信息。”

    OpenCV2.1中,BM算法可以用 CV_16S 或者 CV_32F 的方式输出视差数据,使用32位float格式可以得到真实的视差值,而CV_16S 格式得到的视差矩阵则需要 除以16 才能得到正确的视差。另外,OpenCV2.1另外两种立体匹配算法SGBM 和 GC 只支持 CV_16S 格式的 disparity 矩阵

    5. 如何设置BM、SGBM和GC算法的状态参数?

    (1)StereoBMState

    // 预处理滤波参数

    • preFilterType:预处理滤波器的类型,主要是用于降低亮度失真(photometric distortions)、消除噪声和增强纹理等, 有两种可选类型:CV_STEREO_BM_NORMALIZED_RESPONSE(归一化响应) 或者CV_STEREO_BM_XSOBEL(水平方向Sobel算子,默认类型), 该参数为 int 型;
    • preFilterSize:预处理滤波器窗口大小,容许范围是[5,255],一般应该在 5x5..21x21 之间,参数必须为奇数值, int 型
    • preFilterCap:预处理滤波器的截断值,预处理的输出值仅保留[-preFilterCap, preFilterCap]范围内的值,参数范围:1 - 31(文档中是31,但代码中是 63), int

    // SAD 参数

    • SADWindowSize:SAD窗口大小,容许范围是[5,255],一般应该在 5x5 至 21x21 之间,参数必须是奇数,int 型
    • minDisparity:最小视差默认值为 0, 可以是负值,int 型
    • numberOfDisparities:视差窗口,即最大视差值与最小视差值之差, 窗口大小必须是 16 的整数倍,int 型

    // 后处理参数

    • textureThreshold:低纹理区域的判断阈值。如果当前SAD窗口内所有邻居像素点的x导数绝对值之和小于指定阈值,则该窗口对应的像素点的视差值为 0(That is, if the sum of absolute values of x-derivatives computed over SADWindowSize by SADWindowSize pixel neighborhood is smaller than the parameter, no disparity is computed at the pixel),该参数不能为负值,int 型
    • uniquenessRatio:视差唯一性百分比, 视差窗口范围内最低代价是次低代价的(1 + uniquenessRatio/100)倍时,最低代价对应的视差值才是该像素点的视差,否则该像素点的视差为 0 (the minimum margin in percents between the best (minimum) cost function value and the second best value to accept the computed disparity, that is, accept the computed disparity d^ only if SAD(d) >= SAD(d^) x (1 + uniquenessRatio/100.) for any d != d*+/-1 within the search range ),该参数不能为负值,一般5-15左右的值比较合适,int 型
    • speckleWindowSize:检查视差连通区域变化度的窗口大小, 值为 0 时取消 speckle 检查,int 型
    • speckleRange:视差变化阈值,当窗口内视差变化大于阈值时,该窗口内的视差清零,int 型

    // OpenCV2.1 新增的状态参数

    • roi1, roi2:左右视图的有效像素区域,一般由双目校正阶段的 cvStereoRectify 函数传递,也可以自行设定。一旦在状态参数中设定了 roi1 和 roi2,OpenCV 会通过cvGetValidDisparityROI 函数计算出视差图的有效区域,在有效区域外的视差值将被清零。
    • disp12MaxDiff:左视差图(直接计算得出)和右视差图(通过cvValidateDisparity计算得出)之间的最大容许差异。超过该阈值的视差值将被清零。该参数默认为 -1,即不执行左右视差检查。int 型。注意在程序调试阶段最好保持该值为 -1,以便查看不同视差窗口生成的视差效果。具体请参见《使用OpenGL动态显示双目视觉三维重构效果示例》一文中的讨论。

    在上述参数中,对视差生成效果影响较大的主要参数是 SADWindowSize、numberOfDisparities 和 uniquenessRatio 三个,一般只需对这三个参数进行调整,其余参数按默认设置即可

    在OpenCV2.1中,BM算法有C和C++ 两种实现模块。

    (2)StereoSGBMState

    SGBM算法的状态参数大部分与BM算法的一致,下面只解释不同的部分:

    • SADWindowSize:SAD窗口大小,容许范围是[1,11],一般应该在 3x3 至 11x11 之间,参数必须是奇数,int 型
    • P1, P2:控制视差变化平滑性的参数。P1、P2的值越大,视差越平滑。P1是相邻像素点视差增/减 1 时的惩罚系数;P2是相邻像素点视差变化值大于1时的惩罚系数。P2必须大于P1。OpenCV2.1提供的例程stereo_match.cpp 给出了 P1 和 P2 比较合适的数值
    • fullDP:布尔值,当设置为 TRUE 时,运行双通道动态编程算法(full-scale 2-pass dynamic programming algorithm),会占用O(W*H*numDisparities)个字节,对于高分辨率图像将占用较大的内存空间。一般设置为 FALSE

    注意OpenCV2.1的SGBM算法是用C++ 语言编写的,没有C实现模块。与H. Hirschmuller提出的原算法相比,主要有如下变化:

    1. 算法默认运行单通道DP算法,只用了5个方向,而fullDP使能时则使用8个方向(可能需要占用大量内存)。
    2. 算法在计算匹配代价函数时,采用块匹配方法而非像素匹配(不过SADWindowSize=1时就等于像素匹配了)。
    3. 匹配代价的计算采用BT算法("Depth Discontinuities by Pixel-to-Pixel Stereo" by S. Birchfield and C. Tomasi),并没有实现基于互熵信息的匹配代价计算。
    4. 增加了一些BM算法中的预处理和后处理程序。

    (3)StereoGCState

    GC算法的状态参数只有两个:numberOfDisparities 和 maxIters ,并且只能通过 cvCreateStereoGCState 在创建算法状态结构体时一次性确定,不能在循环中更新状态信息。GC算法并不是一种实时算法,但可以得到物体轮廓清晰准确的视差图,适用于静态环境物体的深度重构。

    注意GC算法只能在C语言模式下运行,并且不能对视差图进行预先的边界延拓,左右视图和左右视差矩阵的大小必须一致。

    6. 如何实现视差图的伪彩色显示?

    首先要将16位符号整形的视差矩阵转换为8位无符号整形矩阵,然后按照一定的变换关系进行伪彩色处理。我的实现代码如下: 

    // 转换为 CV_8U 格式,彩色显示
    dispLfcv = displf, dispRicv = dispri, disp8cv = disp8;
    if (alg == STEREO_GC)
    {
    	cvNormalize( &dispLfcv, &disp8cv, 0, 256, CV_MINMAX );
    } 
    else
    {
    	displf.convertTo(disp8, CV_8U, 255/(m_nMaxDisp*16.));
    }
    F_Gray2Color(&disp8cv, vdispRGB);
    

    灰度图转伪彩色图的代码,主要功能是使灰度图中 亮度越高的像素点,在伪彩色图中对应的点越趋向于 红色;亮度越低,则对应的伪彩色越趋向于 蓝色;总体上按照灰度值高低,由红渐变至蓝,中间色为绿色。其对应关系如下图所示:

     

    图20

    void F_Gray2Color(CvMat* gray_mat, CvMat* color_mat)
    {
    	if(color_mat)
    		cvZero(color_mat);
    		
    	int stype = CV_MAT_TYPE(gray_mat->type), dtype = CV_MAT_TYPE(color_mat->type);
    	int rows = gray_mat->rows, cols = gray_mat->cols;
    
    	// 判断输入的灰度图和输出的伪彩色图是否大小相同、格式是否符合要求
    	if (CV_ARE_SIZES_EQ(gray_mat, color_mat) && stype == CV_8UC1 && dtype == CV_8UC3)
    	{
    		CvMat* red = cvCreateMat(gray_mat->rows, gray_mat->cols, CV_8U);
    		CvMat* green = cvCreateMat(gray_mat->rows, gray_mat->cols, CV_8U);
    		CvMat* blue = cvCreateMat(gray_mat->rows, gray_mat->cols, CV_8U);
    		CvMat* mask = cvCreateMat(gray_mat->rows, gray_mat->cols, CV_8U);
    
    		// 计算各彩色通道的像素值
    		cvSubRS(gray_mat, cvScalar(255), blue);	// blue(I) = 255 - gray(I)
    		cvCopy(gray_mat, red);			// red(I) = gray(I)
    		cvCopy(gray_mat, green);			// green(I) = gray(I),if gray(I) < 128
    		cvCmpS(green, 128, mask, CV_CMP_GE );	// green(I) = 255 - gray(I), if gray(I) >= 128
    		cvSubRS(green, cvScalar(255), green, mask);
    		cvConvertScale(green, green, 2.0, 0.0);
    
    		// 合成伪彩色图
    		cvMerge(blue, green, red, NULL, color_mat);
    
    		cvReleaseMat( &red );
    		cvReleaseMat( &green );
    		cvReleaseMat( &blue );
    		cvReleaseMat( &mask );
    	}
    }
    

    7. 如何将视差数据保存为 txt 数据文件以便在 Matlab 中读取分析?

    由于OpenCV本身只支持 xml、yml 的数据文件读写功能,并且其xml文件与构建网页数据所用的xml文件格式不一致,在Matlab中无法读取。我们可以通过以下方式将视差数据保存为txt文件,再导入到Matlab中。 

    void saveDisp(const char* filename, const Mat& mat)		
    {
    	FILE* fp = fopen(filename, "wt");
    	fprintf(fp, "%02d/n", mat.rows);
    	fprintf(fp, "%02d/n", mat.cols);
    	for(int y = 0; y < mat.rows; y++)
    	{
    		for(int x = 0; x < mat.cols; x++)
    		{
    			short disp = mat.at<short>(y, x); // 这里视差矩阵是CV_16S 格式的,故用 short 类型读取
    			fprintf(fp, "%d/n", disp); // 若视差矩阵是 CV_32F 格式,则用 float 类型读取
    		}
    	}
    	fclose(fp);
    }
    

     

    相应的Matlab代码为:

      

    function img = txt2img(filename)
    data = importdata(filename);
    r = data(1);    % 行数
    c = data(2);    % 列数
    disp = data(3:end); % 视差
    vmin = min(disp);
    vmax = max(disp);
    disp = reshape(disp, [c,r])'; % 将列向量形式的 disp 重构为 矩阵形式
    %  OpenCV 是行扫描存储图像,Matlab 是列扫描存储图像
    %  故对 disp 的重新排列是首先变成 c 行 r 列的矩阵,然后再转置回 r 行 c 列
    img = uint8( 255 * ( disp - vmin ) / ( vmax - vmin ) );
    mesh(disp);
    set(gca,'YDir','reverse');  % 通过 mesh 方式绘图时,需倒置 Y 轴方向
    axis tight; % 使坐标轴显示范围与数据范围相贴合,去除空白显示区
    
    

    科普一下,很好的stereo Vision 资料200多页的pdf:

    http://www.vision.deis.unibo.it/smatt/Seminars/StereoVision.pdf

    结合大牛的博客,好好学习下:

     http://blog.csdn.net/chenyusiyuan/article/details/5967291


    • 需要注意的点

    OpenCV自带的cvStereoCalibrate感觉不怎么好用,用这个函数求出的内参外参和旋转平移矩阵进行校准,往往无法达到行对准,有时甚至会出现比较可怕的畸变。在看了piao的http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=4603帖子之后,也曾经尝试过现用cvCalibrateCamera2单独标定(左右各20幅图),得出的结果基本和Matlab单独标定的相同,然后再在cvStereoCalibrate中将参数设成CV_CALIB_USE_INTRINSIC_GUESS,用来细化内参数和畸变参数,结果得出的标定结果就又走样了。

    不知道有谁在这方面有过成功经验的,可以出来分享一下。毕竟用Matlab工具箱还是麻烦了些。


    参考:http://blog.lehu.shu.edu.cn/byman/A263366.html

  • 相关阅读:
    Mysql高可用MHA
    centos7.2 安装mysql5.7.13
    keepalived+双主实现数据库的高可用
    xtrabackup 完全备份+xtrabacup 增量备份
    xtrabackup 进行 MySQL 数据库备份
    mysql所有备份与恢复
    sysbench压测工具 压测 mysql
    查看CPU性能参数(mpstat, iostat, sar、vmstat)等命令详解
    脚本
    解决 Let’s Encrypt SSL 证书配置错误
  • 原文地址:https://www.cnblogs.com/wuyida/p/6301411.html
Copyright © 2011-2022 走看看