精华内容
参与话题
问答
  • 双目立体匹配

    2013-12-02 19:03:16
    立体匹配 cpp opencv 双目标定
  • 针对AD-Census变换采用固定权重将AD变换代价与Census变换代价合成的双目立体匹配代价无法体现像素点区域特征的问题,提出一种基于自适应权重AD-Census变换的双目立体匹配算法。算法首先通过增加相邻像素点的灰度差...
  • 双目立体匹配步骤详解

    万次阅读 多人点赞 2018-10-23 09:44:47
    根据Schrstein和Szeliski的总结,双目立体匹配可划分为四个步骤:匹配代价计算、代价聚合、视差计算和视差优化。


      根据Schrstein和Szeliski的总结,双目立体匹配可划分为四个步骤:匹配代价计算、代价聚合、视差计算和视差优化

    Step1 匹配代价计算


      匹配代价计算的目的是衡量待匹配像素与候选像素之间的相关性。两个像素无论是否为同名点,都可以通过匹配代价函数计算匹配代价,代价越小则说明相关性越大,是同名点的概率也越大。

      每个像素在搜索同名点之前,往往会指定一个视差搜索范围D(Dmin ~ Dmax),视差搜索时将范围限定在D内,用一个大小为W×H×D(W为影像宽度,H为影像高度)的三维矩阵C来存储每个像素在视差范围内每个视差下的匹配代价值。矩阵C通常称为DSI(Disparity Space Image)。

      匹配代价计算的方法有很多,传统的摄影测量中,使用灰度绝对值差(AD,Absolute Differences)1、灰度绝对值差之和(SAD,Sum of Absolute Differences)、归一化相关系数(NCC,Normalized Cross-correlation)等方法来计算两个像素的匹配代价;计算机视觉中,多使用互信息(MI,Mutual Information)法2 3、Census变换(CT,Census Transform)法4 5、Rank变换(RT, Rank Transform)法6 7、BT(Birchfield and Tomasi)法8 等作为匹配代价的计算方法。不同的代价计算算法都有各自的特点,对各类数据的表现也不尽相同,选择合适的匹配代价计算函数是立体匹配中不可忽视的关键步骤。

    图1 DSI示意图(C(x,y,d)代表像素(x,y)在视差为d时的匹配代价)

    Step2 代价聚合


       代价聚合的根本目的是让代价值能够准确的反映像素之间的相关性。上一步匹配代价的计算往往只会考虑局部信息,通过两个像素邻域内一定大小的窗口内的像素信息来计算代价值,这很容易受到影像噪声的影响,而且当影像处于弱纹理或重复纹理区域,这个代价值极有可能无法准确的反映像素之间的相关性,直接表现就是真实同名点的代价值非最小。

      而代价聚合则是建立邻接像素之间的联系,以一定的准则,如相邻像素应该具有连续的视差值,来对代价矩阵进行优化,这种优化往往是全局的,每个像素在某个视差下的新代价值都会根据其相邻像素在同一视差值或者附近视差值下的代价值来重新计算,得到新的DSI,用矩阵S来表示。

      实际上代价聚合类似于一种视差传播步骤,信噪比高的区域匹配效果好,初始代价能够很好的反映相关性,可以更准确的得到最优视差值,通过代价聚合传播至信噪比低、匹配效果不好的区域,最终使所有影像的代价值都能够准确反映真实相关性。常用的代价聚合方法有扫描线法、动态规划法、SGM算法中的路径聚合法等。

    图1:代价聚合前后视差图示意图

    Step3 视差计算


      视差计算即通过代价聚合之后的代价矩阵S来确定每个像素的最优视差值,通常使用赢家通吃算法(WTA,Winner-Takes-All)来计算,如图2所示,即某个像素的所有视差下的代价值中,选择最小代价值所对应的视差作为最优视差。这一步非常简单,这意味着聚合代价矩阵S的值必须能够准确的反映像素之间的相关性,也表明上一步代价聚合步骤是立体匹配中极为关键的步骤,直接决定了算法的准确性。

    图2 赢家通吃(WTA)算法示意图

    Step4 视差优化


      视差优化的目的是对上一步得到的视差图进行进一步优化,改善视差图的质量,包括剔除错误视差、适当平滑以及子像素精度优化等步骤,一般采用左右一致性检查(Left-Right Check)算法剔除因为遮挡和噪声而导致的错误视差;采用剔除小连通区域算法来剔除孤立异常点;采用中值滤波(Median Filter)、双边滤波(Bilateral Filter)等平滑算法对视差图进行平滑;另外还有一些有效提高视差图质量的方法如鲁棒平面拟合(Robust Plane Fitting)、亮度一致性约束(Intensity Consistent)、局部一致性约束(Locally Consistent)等也常被使用。

      由于WTA算法所得到的视差值是整像素精度,为了获得更高的子像素精度,需要对视差值进行进一步的子像素细化,常用的子像素细化方法是一元二次曲线拟合法,通过最优视差下的代价值以及左右两个视差下的代价值拟合一条一元二次曲线,取二次曲线的极小值点所代表的视差值为子像素视差值。如图3所示。

    图3 二次曲线拟合法子像素位置计算示意图

      局部匹配算法的步骤一般包括匹配代价计算、代价聚合和视差计算三个步骤,全局算法则包括匹配代价计算,视差计算与视差优化三个步骤,半全局算法SGM则四个步骤都有。

    恒叨立码之立体匹配
    SGM
    理论恒叨系列

    【恒叨立码】【理论恒叨】【立体匹配系列】经典SGM:(1)匹配代价计算之互信息(MI))
    【恒叨立码】【理论恒叨】【立体匹配系列】经典SGM:(2)匹配代价计算之Census变换
    【恒叨立码】【理论恒叨】【立体匹配系列】经典SGM:(3)代价聚合(Cost Aggregation)
    【恒叨立码】【理论恒叨】【立体匹配系列】经典SGM:(4)视差计算、视差优化

    码上教学系列

    【恒叨立码】【码上实战】【立体匹配系列】经典SGM:(1)框架与类设计
    【恒叨立码】【码上实战】【立体匹配系列】经典SGM:(2)代价计算
    【恒叨立码】【码上实战】【立体匹配系列】经典SGM:(3)代价聚合
    【恒叨立码】【码上实战】【立体匹配系列】经典SGM:(4)代价聚合2
    【恒叨立码】【码上实战】【立体匹配系列】经典SGM:(5)视差优化
    【恒叨立码】【码上实战】【立体匹配系列】经典SGM:(6)视差填充
    【恒叨立码】【码上实战】【立体匹配系列】经典SGM:(7)弱纹理优化

    恒叨立码之立体匹配 -- PatchMatch
    PatchMatch
    理论恒叨系列

    【恒叨立码】【理论恒叨】【立体匹配系列】经典PatchMatch: (1)Slanted support windows倾斜支持窗模型
    【恒叨立码】【理论恒叨】【立体匹配系列】经典PatchMatch: (2)基于PatchMatch的视差估计
    【恒叨立码】【理论恒叨】【立体匹配系列】经典PatchMatch: (3)后处理(一致性检查与视差填充)


    1. KANADE T, KANO H, KIMURA S, et al. Development of a video-rate stereo machine: Ieee/rsj International Conference on Intelligent Robots and Systems 95. ‘human Robot Interaction and Cooperative Robots’, Proceedings, 2002[C]. ↩︎

    2. KIM J, KOLMOGOROV V, ZABIH R. Visual Correspondence Using Energy Minimization and Mutual Information: IEEE International Conference on Computer Vision, 2003. Proceedings, 2008[C]. ↩︎

    3. EGNAL G. Mutual Information as a Stereo Correspondence Measure[J]. Technical Reports, 2000. ↩︎

    4. MA L, LI J, MA J, et al. A Modified Census Transform Based on the Neighborhood Information for Stereo Matching Algorithm: Seventh International Conference on Image and Graphics, 2013[C]. ↩︎

    5. BAIK Y K, JO J H, LEE K M. Fast Census Transform-based Stereo Algorithm using SSE2: The 12th Korea-Japan Joint Workshop on Frontiers of Computer Vision, Tokushima, Japan, 2006[C]. ↩︎

    6. GU Z, SU X, LIU Y, et al. Local stereo matching with adaptive support-weight, rank transform and disparity calibration[J]. Pattern Recognition Letters, 2008,29(9):1230-1235. ↩︎

    7. BANKS J, BENNAMOUN M, KUBIK K, et al. A constraint to improve the reliability of stereo matching using the rank transform: Acoustics, Speech, and Signal Processing, 1999. on 1999 IEEE International Conference, 1999[C]. ↩︎

    8. BIRCHFIELD S, TOMASI C. A Pixel Dissimilarity Measure That Is Insensitive to Image Sampling[J]. IEEE Transactions on Pattern Analysis & Machine Intelligence, 1998,20(4):401-406. ↩︎

    展开全文
  • 双目立体匹配系统

    2018-01-17 10:34:58
    使用MFC作为基本框架,在VS2015与OPenCV环境下编译而成,作为机器视觉课程设计。
  • 基于Opencv的双目立体匹配及其重建

    热门讨论 2012-03-21 16:06:29
    通过配置OpenCV,在VC6.0下实现双目立体匹配,包括基于稀疏点、密集点的匹配及其重建。
  • 双目立体匹配算法SGBM

    万次阅读 多人点赞 2018-08-07 23:01:20
    semi-global matching(SGM)是一种用于计算双目视觉中视差(disparity)的半全局匹配算法,在OpenCV中的实现为semi-global block matching(SGBM)。 第一部分:SGBM算法原理: 一、预处理 Step1:SGBM采用水平...

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

    第一部分:SGBM算法原理:

    一、预处理

    Step1:SGBM采用水平Sobel算子,把图像做处理,公式为:

    Sobel(x,y)=2[P(x+1,y)-P(x-1,y)]+ P(x+1,y-1)-P(x-1,y-1)+ P(x+1,y+1)-P(x-1,y+1)

    Step2:用一个函数将经过水平Sobel算子处理后的图像上每个像素点(P表示其像素值)映射成一个新的图像:PNEW表示新图像上的像素值。

    映射函数:

    preFilterCap 为一个常数参数,opencv缺省情况下取15,例程中取63。

    预处理实际上是得到图像的梯度信息,将预处理的图像保存起来,将会用于计算代价。

    二、代价计算

    代价有两部分组成:

    1、经过预处理得到的图像的梯度信息经过基于采样的方法得到的梯度代价。

    2、原图像经过基于采样的方法得到的SAD代价。

    上述两个代价都会在SAD窗口内进行计算。

    三、动态规划

    动态规划算法本身存在拖尾效应,视差突变处易产生错误的匹配,利用态规划进行一维能量累积累,会将错误的视差信息传播给后面的路径上。半全局算法利用多个方向上的信息,试图消除错误信息的干扰,能明显减弱动态规划算法产生的拖尾效应。

    半全局算法试图通过影像上多个方向上一维路径 的约束,来建立一个全局的马尔科夫能量方程,每个像素最终的匹配代价是所有路径信息的叠加,每个像素的视差选择都只是简单通过 WTA(Winner Takes All)决定的。多方向能量聚集如下图所示:

    在每个方向上按照动态规划的思想进行能量累积,然后将各个方向上的匹配代价相加得到总的匹配代价,如下式所示:

    式中L为当前路径累积的代价函数,P1、P2为像素点与相邻点视差存在较小和较大差异情况下的平滑惩罚,P1<P2,第三项无意义,仅仅是为了消除各个方向路径长度不同造成的影响。将所有r方向的匹配代价相加得到总的匹配代价,如下:

    默认4条路径,其中动态规划很重要两个参数P1,P2是这样设定的:

    P1 =8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;

    P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;

    cn是图像的通道数, SADWindowSize是SAD窗口大小,数值为奇数。

    可以看出,当图像通道和SAD窗口确定下来,SGBM的规划参数P1和P2是常数

    四、后处理

    opencvSGBM的后处理包含以下几个步骤:

    Step1:唯一性检测:视差窗口范围内最低代价是次低代价的(1 + uniquenessRatio/100)倍时,最低代价对应的视差值才是该像素点的视差,否则该像素点的视差为0。其中uniquenessRatio是一个常数参数。

    Step2:亚像素插值:

           插值公式:

    Step3:左右一致性检测:误差阈值disp12MaxDiff默认为1,可以自己设置。

    OpencvSGBM计算右视差图的方式:

    通过得到的左视察图计算右视差图

     

     

    第二部分:opencv中SGBM算法的参数含义及数值选取

    一、 预处理参数

    1:preFilterCap:水平sobel预处理后,映射滤波器大小。默认为15

    int ftzero =max(params.preFilterCap, 15) | 1;

    opencv测试例程test_stereomatching.cpp中取63。

    二 、代价参数

    2:SADWindowSize:计算代价步骤中SAD窗口的大小。由源码得,此窗口默认大小为5。

    SADWindowSize.width= SADWindowSize.height = params.SADWindowSize > 0 ?params.SADWindowSize : 5;

    注:窗口大小应为奇数,一般应在3x3到21x21之间。

    3:minDisparity:最小视差,默认为0。此参数决定左图中的像素点在右图匹配搜索的起点。int 类型

    4:numberOfDisparities:视差搜索范围,其值必须为16的整数倍(CV_Assert( D % 16 == 0 );)。最大搜索边界= numberOfDisparities+ minDisparity。int 类型

    三 、动态规划参数

    动态规划有两个参数,分别是P1、P2,它们控制视差变化平滑性的参数。P1、P2的值越大,视差越平滑。P1是相邻像素点视差增/减 1 时的惩罚系数;P2是相邻像素点视差变化值大于1时的惩罚系数。P2必须大于P1。需要指出,在动态规划时,P1和P2都是常数。

    5:opencv测试例程test_stereomatching.cpp中,P1 = 8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;

    6:opencv测试例程test_stereomatching.cpp中,P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize;

    四、后处理参数

    7:uniquenessRatio:唯一性检测参数。对于左图匹配像素点来说,先定义在numberOfDisparities搜索区间内的最低代价为mincost,次低代价为secdmincost。如果满足

    即说明最低代价和次第代价相差太小,也就是匹配的区分度不够,就认为当前匹配像素点是误匹配的。

    opencv测试例程test_stereomatching.cpp中,uniquenessRatio=10。int 类型

    8:disp12MaxDiff:左右一致性检测最大容许误差阈值。int 类型

    opencv测试例程test_stereomatching.cpp中,disp12MaxDiff =1。

    9:speckleWindowSize:视差连通区域像素点个数的大小。对于每一个视差点,当其连通区域的像素点个数小于speckleWindowSize时,认为该视差值无效,是噪点。

    opencv测试例程test_stereomatching.cpp中,speckleWindowSize=100。

    10:speckleRange:视差连通条件,在计算一个视差点的连通区域时,当下一个像素点视差变化绝对值大于speckleRange就认为下一个视差像素点和当前视差像素点是不连通的。

    opencv测试例程test_stereomatching.cpp中,speckleWindowSize=10。

     

    第三部分:C++实现

    #include <opencv2/opencv.hpp>  
    #include <iostream>  
    
    using namespace std;
    using namespace cv;
    
    const int imageWidth = 672;  //摄像头的分辨率  
    const int imageHeight = 376;
    Size imageSize = Size(imageWidth, imageHeight);
    
    Mat grayImageL,grayImageR;
    Mat rectifyImageL, rectifyImageR;
    
    Rect validROIL;//图像校正之后,会对图像进行裁剪,这里的validROI就是指裁剪之后的区域, 其内部的所有像素都有效
    Rect validROIR;
    
    Mat mapLx, mapLy, mapRx, mapRy;     //映射表  
    Mat Rl, Rr, Pl, Pr, Q;              //校正旋转矩阵R,投影矩阵P 重投影矩阵Q
    Mat xyz;              //三维坐标
    
    Point origin;     //鼠标按下的起始点
    Rect selection;      //定义矩形选框
    bool selectObject = false;    //是否选择对象
    
    int numberOfDisparities = ((imageSize.width / 8) + 15) & -16;
    int numDisparities = 6;
    
    cv::Ptr<cv::StereoSGBM> sgbm = StereoSGBM::create(0, 16, 3);
    
    Mat cameraMatrixL = (Mat_<double>(3, 3) << 350.11095, 0, 339.81480,
    	0, 349.39869, 200.42205,
    	0, 0, 1);
    Mat distCoeffL = (Mat_<double>(5, 1) << -0.16028, 0.00600, -0.00009, -0.00047, 0.00000);
    
    Mat cameraMatrixR = (Mat_<double>(3, 3) << 351.08207, 0, 343.68828,
    	0, 350.19118, 210.18586,
    	0, 0, 1);
    Mat distCoeffR = (Mat_<double>(5, 1) << -0.17678, 0.02713, -0.00033, -0.00109, 0.00000);
    //左右目之间的R,t可通过stereoCalibrate()或matlab工具箱calib求得
    Mat T = (Mat_<double>(3, 1) << -119.61078, -0.06806, 0.08105);//T平移向量
    Mat rec = (Mat_<double>(3, 1) << 0.00468, 0.02159, 0.00015);//rec旋转向量
    Mat R;//R 旋转矩阵
    Mat frame, f1, f2;
    Mat disp, disp8;
    
    /*****立体匹配*****/
    void stereo_match(int, void*)
    {
    	sgbm->setPreFilterCap(32);
    	int SADWindowSize = 9;
    	int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;
    	sgbm->setBlockSize(sgbmWinSize);
    	int cn = rectifyImageL.channels();
    	sgbm->setP1(8 * cn*sgbmWinSize*sgbmWinSize);
    	sgbm->setP2(32 * cn*sgbmWinSize*sgbmWinSize);
    	sgbm->setMinDisparity(0);
    	sgbm->setNumDisparities(numberOfDisparities);
    	sgbm->setUniquenessRatio(10);
    	sgbm->setSpeckleWindowSize(100);
    	sgbm->setSpeckleRange(32);
    	sgbm->setDisp12MaxDiff(1);
    	sgbm->setMode(cv::StereoSGBM::MODE_SGBM);
    	sgbm->compute(rectifyImageL, rectifyImageR, disp);//输入图像必须为灰度图
    
    	disp.convertTo(disp8, CV_8U, 255 / ((numDisparities * 16 + 16)*16.));//计算出的视差是CV_16S格式
    	reprojectImageTo3D(disp, xyz, Q, true); //在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16(也就是W除以16),才能得到正确的三维坐标信息。
    	xyz = xyz * 16;
    	imshow("disparity", disp8);
    }
    
    /*****描述:鼠标操作回调*****/
    static void onMouse(int event, int x, int y, int, void*)
    {
    	if (selectObject)
    	{
    		selection.x = MIN(x, origin.x);
    		selection.y = MIN(y, origin.y);
    		selection.width = std::abs(x - origin.x);
    		selection.height = std::abs(y - origin.y);
    	}
    
    	switch (event)
    	{
    	case EVENT_LBUTTONDOWN:   //鼠标左按钮按下的事件
    		origin = Point(x, y);
    		selection = Rect(x, y, 0, 0);
    		selectObject = true;
    		cout << origin << "in world coordinate is: " << xyz.at<Vec3f>(origin) << endl;
    		break;
    	case EVENT_LBUTTONUP:    //鼠标左按钮释放的事件
    		selectObject = false;
    		if (selection.width > 0 && selection.height > 0)
    			break;
    	}
    }
    
    
    /*****主函数*****/
    int main()
    {
    	Rodrigues(rec, R); //Rodrigues变换
    	//经过双目标定得到摄像头的各项参数后,采用OpenCV中的stereoRectify(立体校正)得到校正旋转矩阵R、投影矩阵P、重投影矩阵Q
    	//flags-可选的标志有两种零或者 CV_CALIB_ZERO_DISPARITY ,如果设置 CV_CALIB_ZERO_DISPARITY 的话,该函数会让两幅校正后的图像的主点有相同的像素坐标。否则该函数会水平或垂直的移动图像,以使得其有用的范围最大
    	//alpha-拉伸参数。如果设置为负或忽略,将不进行拉伸。如果设置为0,那么校正后图像只有有效的部分会被显示(没有黑色的部分),如果设置为1,那么就会显示整个图像。设置为0~1之间的某个值,其效果也居于两者之间。
    	stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY,
    		0, imageSize, &validROIL, &validROIR);
    	//再采用映射变换计算函数initUndistortRectifyMap得出校准映射参数,该函数功能是计算畸变矫正和立体校正的映射变换
    	initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pr, imageSize, CV_32FC1, mapLx, mapLy);
    	initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);
    
    	VideoCapture cap(0);
    	cap.set(CAP_PROP_FRAME_HEIGHT, 376);
    	cap.set(CAP_PROP_FRAME_WIDTH, 1344);
    	namedWindow("disparity", CV_WINDOW_AUTOSIZE);
    	namedWindow("paramemnt", CV_WINDOW_NORMAL);
    	createTrackbar("numDisparities:\n", "paramemnt", &numDisparities, 20, stereo_match);
    	setMouseCallback("disparity", onMouse, 0);
    	
    	while (1)
    	{
    		cap >> frame;
    		imshow("video", frame);
    		f1 = frame.colRange(0, 672);
    		f2 = frame.colRange(672, 1344);
    
    		cvtColor(f1, grayImageL, CV_BGR2GRAY);
    		cvtColor(f2, grayImageR, CV_BGR2GRAY);
    		//然后用remap来校准输入的左右图像
    		//interpolation-插值方法,但是不支持最近邻插值
    		remap(grayImageL, rectifyImageL, mapLx, mapLy, INTER_LINEAR);
    		remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);
    
    		stereo_match(0, 0);
    		waitKey(1);
    	}
    	waitKey(0);
    	return 0;
    }
    

    运行效果:

     

    参考:

    Accurate and Efficient Stereo Processing by Semi-Global Matching and Mutual Information

    opencvSGBM半全局立体匹配算法的研究(1)

    semi-global matching 算法总结

    密集匹配之半全局匹配SGBM

    【OpenCV】双目测距(双目标定、双目校正和立体匹配)

    双摄像头立体成像(三)-畸变矫正与立体校正

    双目立体匹配经典算法之Semi-Global Matching(SGM)概述:匹配代价计算之Census变换(Census Transform,CT)(附计算C代码)

    展开全文
  • 针对局部立体匹配算法在弱纹理区域匹配准确度低的问题,提出一种在代价初始化和代价聚合两阶段自适应优化的立体匹配算法。首先,在代价初始化阶段,通过双阈值线性约束条件构造像素点的十字支撑窗口,并依据十字支撑最短...
  • 双目立体匹配测试数据集Middlebury Stereo Datasets,包含Middlebury Stereo官网上面的03,05,06年的三部分数据集,01,14年的数据集下载失败,分享给大家。
  • 基于SIFT的OpenCV双目立体匹配系统,VS2013MFC 实现
  • 基于SUFT的OPENCV双目立体匹配系统,VS2013MFC 实现
  • 双目立体匹配 Census

    千次阅读 热门讨论 2017-11-09 16:36:06
    双目立体匹配 Census算法 编译环境: visual studio 2015 + opencv3.2.0 + windows 10

    上一篇介绍了双目立体匹配SAD算法,这一篇介绍Census算法。
    Census原理:
    在视图中选取任一点,以该点为中心划出一个例如3 × 3 的矩形,矩形中除中心点之外的每一点都与中心点进行比较,灰度值小于中心点记为1,灰度大于中心点的则记为0,以所得长度为 8 的只有 0 和 1 的序列作为该中心点的 census 序列,即中心像素的灰度值被census 序列替换。经过census变换后的图像使用汉明距离计算相似度,所谓图像匹配就是在匹配图像中找出与参考像素点相似度最高的点,而汉明距正是匹配图像像素与参考像素相似度的度量。具体而言,对于欲求取视差的左右视图,要比较两个视图中两点的相似度,可将此两点的census值逐位进行异或运算,然后计算结果为1 的个数,记为此两点之间的汉明值,汉明值是两点间相似度的一种体现,汉明值愈小,两点相似度愈大实现算法时先异或再统计1的个数即可,汉明距越小即相似度越高。
    census求汉明距离

    下面的代码是自己根据原理写的,实现的结果并没有很好,以后继续优化代码。

    具体代码如下:

    //*************************Census*********************
    #include <iostream>
    #include <opencv2/opencv.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/highgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    //-------------------定义汉明距离----------------------------
    int disparity;
    int GetHammingWeight(uchar value);//求1的个数
    
    //-------------------定义Census处理图像函数---------------------
    int hWind = 1;//定义窗口大小为(2*hWind+1)
    Mat ProcessImg(Mat &Img);//将矩形内的像素与中心像素相比较,将结果存于中心像素中
    Mat Img_census, Left_census, Right_census;
    
    //--------------------得到Disparity图像------------------------
    Mat getDisparity(Mat &left, Mat &right);
    
    //--------------------处理Disparity图像-----------------------
    Mat ProcessDisparity(Mat &disImg);
    
    int ImgHeight, ImgWidth;
    
    //int num = 0;//异或得到的海明距离
    Mat LeftImg, RightImg;
    Mat DisparityImg(ImgHeight, ImgWidth, CV_8UC1, Scalar::all(0));
    Mat DisparityImg_Processed(ImgHeight, ImgWidth, CV_8UC1, Scalar::all(0));
    Mat DisparityImg_Processed_2(ImgHeight, ImgWidth, CV_8UC1);
    //定义读取图片的路径
    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\\Census\\";
    
    int main()
    {
        LeftImg = imread(file_dir + "renwu_left.png", 0);
        RightImg = imread(file_dir + "renwu_right.png", 0);
        namedWindow("renwu_left", 1);
        namedWindow("renwu_right", 1);
        imshow("renwu_left", LeftImg);
        waitKey(5);
        imshow("renwu_right", RightImg);
        waitKey(5);
        ImgHeight = LeftImg.rows;
        ImgWidth = LeftImg.cols;
    
        Left_census= ProcessImg(LeftImg);//处理左图,得到左图的CENSUS图像 Left_census
        namedWindow("Left_census", 1);
        imshow("Left_census", Left_census);
        waitKey(5);
    //  imwrite(save_dir + "renwu_left.jpg", Left_census);
    
        Right_census= ProcessImg(RightImg);
        namedWindow("Right_census", 1);
        imshow("Right_census", Right_census);
        waitKey(5);
    //  imwrite(save_dir  + "renwu_right.jpg", Right_census);
    
        DisparityImg= getDisparity(Left_census, Right_census);
        namedWindow("Disparity", 1);
        imshow("Disparity", DisparityImg);
    //  imwrite(save_dir  + "disparity.jpg", DisparityImg);
        waitKey(5);
    
        DisparityImg_Processed = ProcessDisparity(DisparityImg);
        namedWindow("DisparityImg_Processed", 1);
        imshow("DisparityImg_Processed", DisparityImg_Processed);
    //  imwrite(save_dir + "disparity_processed.jpg", DisparityImg_Processed);
        waitKey(0);
        return 0;
    }
    
    
    //-----------------------对图像进行census编码---------------
    Mat ProcessImg(Mat &Img)
    {
        int64 start, end;
        start = getTickCount();
    
        Mat Img_census = Mat(Img.rows, Img.cols, CV_8UC1, Scalar::all(0));
        uchar center = 0;
    
        for (int i = 0; i < ImgHeight - hWind; i++)
        {
            for (int j = 0; j < ImgWidth - hWind; j++)
            {
                center = Img.at<uchar>(i + hWind, j + hWind);
                uchar census = 0;
                uchar neighbor = 0;
                for (int p = i; p <= i + 2 * hWind; p++)//行
                {
                    for (int q = j; q <= j + 2 * hWind; q++)//列
                    {
                        if (p >= 0 && p <ImgHeight  && q >= 0 && q < ImgWidth)
                        {
    
                            if (!(p == i + hWind && q == j + hWind))
                            {
                                //--------- 将二进制数存在变量中-----
                                neighbor = Img.at<uchar>(p, q);
    
                                if (neighbor > center)
                                {
                                    census = census * 2;//向左移一位,相当于在二进制后面增添0
                                }
                                else
                                {
                                    census = census * 2 + 1;//向左移一位并加一,相当于在二进制后面增添1
                                }
                                //cout << "census = " << static_cast<int>(census) << endl;
                            }
                        }
                    }
    
                }
                Img_census.at<uchar>(i + hWind, j + hWind) = census;
            }
        }
        /*end = getTickCount();
        cout << "time is = " << end - start << " ms" << endl;*/
        return Img_census;
    }
    
    //------------得到汉明距离---------------
    int GetHammingWeight( uchar value)
    {
        int num = 0;
        if (value == 0)
            return 0;
        while (value)
        {
            ++num;
            value = (value - 1)&value;
        }
        return num;
    }
    
    //--------------------得到视差图像--------------
    Mat getDisparity(Mat &left, Mat &right)
    {
        int DSR =16;//视差搜索范围
        Mat disparity(ImgHeight,ImgWidth,CV_8UC1);
    
        cout << "ImgHeight = " << ImgHeight << "   " << "ImgWidth = " << ImgWidth << endl;
        for (int i = 0; i < ImgHeight; i++)
        {
            for (int j = 0; j < ImgWidth; j++)
            {
                uchar L;
                uchar R;
                uchar diff;
    
                L = left.at<uchar>(i, j);
                Mat Dif(1, DSR, CV_8UC1);
    //          Mat Dif(1, DSR, CV_32F);
    
                for (int k = 0; k < DSR; k++)
                {
                    //cout << "k = " << k << endl;
                    int y = j - k;
                    if (y < 0)
                    {
                        Dif.at<uchar>(k) = 0;
                    }
                    if (y >= 0)
                    {
                        R = right.at<uchar>(i,y);
                        //bitwise_xor(L, R, );
                        diff = L^R;
                        diff = GetHammingWeight(diff);
                        Dif.at<uchar>(k) = diff;
    //                  Dif.at<float>(k) = diff;
                    }
                }
                //---------------寻找最佳匹配点--------------
                Point minLoc;
                minMaxLoc(Dif, NULL, NULL, &minLoc, NULL);
                int loc = minLoc.x;
                //cout << "loc..... = " << loc << endl;
                disparity.at<uchar>(i,j)=loc*16;
            }
        }
        return disparity;
    }
    
    //-------------对得到的视差图进行处理-------------------
    Mat ProcessDisparity(Mat &disImg)
    {
        Mat ProcessDisImg(ImgHeight,ImgWidth,CV_8UC1);//存储处理后视差图
        for (int i = 0; i < ImgHeight; i++)
        {
            for (int j = 0; j < ImgWidth; j++)
            {
                uchar pixel = disImg.at<uchar>(i, j);
                if (pixel < 100)
                    pixel = 0;
                ProcessDisImg.at<uchar>(i, j) = pixel;
            }
        }
        return ProcessDisImg;
    }

    renwu_left_census
    经过处理后的左图census图像

    renwu_right_census
    经过处理后的右图census图像

    disparityImg
    disparity图像

    disparity_processed
    处理后的disparity图像

    实现结果并没有那么好,希望有经验的读者可以留言提供更好的建议。谢谢。

    展开全文
  • 用于双目立体匹配的测试数据集,包含标准视差图,整合了2001、2003、2005、2006四年的数据集
  • 双目立体匹配 SAD

    千次阅读 2017-11-09 15:00:02
    双目立体匹配 SAD算法 编译环境: visual studio 2015 + opencv3.2.0 + window 10
    最近开始研究双目匹配算法,于是在网上代码网站比如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值最小的窗口,即找到了左边图像的最佳匹配的像素块。

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

    renwu_right

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

    //******************SAD************************
    #include <iostream>
    #include <opencv2/opencv.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/highgui.hpp>
    
    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<uchar>(i, j) = loc * 16;
            }
        }
        //记录运行 时间
        end = getTickCount();
        double time = ((end - start)/CLOCKS_PER_SEC);
        cout << "Time is : " << time << "ms" << endl;
        return disparity;
    }

    运行结果图如下
    sad运行结果

    展开全文
  • 双目立体匹配思想

    2019-06-10 09:17:12
    一、立体匹配定义:立体匹配是立体视觉研究中的关键部分(双目匹配与深度计算(三角化),直接法中也有一定关系)。其目标是在两个或多个视点中匹配相应像素点,计算视差。通过建立一个能量代价函数,对其最小化来...
  • 双目立体匹配算法

    2019-09-17 09:52:16
    Loopy belief propagation, Markov Random Field, stereo vision website:http://nghiaho.com/?page_id=1366
  • 双目立体匹配流程详解

    万次阅读 多人点赞 2018-08-16 13:14:25
     双目立体匹配一直是双目视觉的研究热点,双目相机拍摄同一场景的左、右两幅视点图像,运用立体匹配匹配算法获取视差图,进而获取深度图。而深度图的应用范围非常广泛,由于其能够记录场景中物体距离摄像机的距离,...
  • 立体匹配双目视觉的一个重要分支领域,能够通过深度图还原出三维信息,但由于其计算量庞大,实时性难以得到保障。为此,提出了一种基于强相似点的快速立体匹配算法。首先,将双目图像通过对极处理,使匹配区域固定...
  • 双目立体匹配图片集,很多经典图片对,大多经典论文中都有用到,做立体匹配的朋友可以下载。 双目立体匹配图片集,很多经典图片对,大多经典论文中都有用到,做立体匹配的朋友可以下载。
  • 双目立体匹配回顾笔记

    千次阅读 2011-11-22 21:48:41
    1. 基于窗口的全局算法窗口设置问题(匹配是假设表面前向平行的,而事实上与此会违背,另外在不连续的区域背景部分是无法对准的,因此窗口太大不利于匹配的准确性,另外窗口太小的话对于若纹理和重复纹理区域引入更...
  • 双目立体匹配算法:ELAS

    千次阅读 2020-03-31 20:26:57
    ELAS是由Andreas Geiger于2010年提出的一种比较新颖的立体匹配算法,全称为《Efficient Large Scale Stereo Matching》。这种算法的优点是对于高分辨率的图像,能够兼顾较高的匹配速度和匹配精度,在许多公开的数据...
  • 双目立体匹配算法:SGM

    千次阅读 2020-03-27 19:45:27
    一、简介 二、SGM 三、代码实现 四、实验验证 五、总结 六、参考资料

空空如也

1 2 3 4 5 ... 20
收藏数 550
精华内容 220
关键字:

双目立体匹配