精华内容
下载资源
问答
  • 双目立体匹配

    2021-01-06 14:50:57
    双目立体匹配 https://blog.csdn.net/m0_37604894/article/details/81020846 https://www.cnblogs.com/ding-jing/p/8654137.html 1 视差生成深度 公式... 2 全局方法 其中数据项描述了匹配程度,平滑项体现了...
  • 双目立体匹配步骤详解

    万次阅读 多人点赞 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. ↩︎

    展开全文
  • 点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达 来源:https://blog.csdn.net/rs_lys/article/details/83302323根据Schrstein和Szeliski的总结,双目立体匹配可划分为四个步骤:匹配代价...

    点击上方“小白学视觉”,选择加"星标"或“置顶”

    重磅干货,第一时间送达

    3d4ceb81f7ac3cac3691414d0a3f6f89.png

    来源:

    https://blog.csdn.net/rs_lys/article/details/83302323

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

    一 、匹配代价计算

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

    每个像素在搜索同名点之前,往往会指定一个视差搜索范围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 等作为匹配代价的计算方法。不同的代价计算算法都有各自的特点,对各类数据的表现也不尽相同,选择合适的匹配代价计算函数是立体匹配中不可忽视的关键步骤。

    47cb83d954717f3c65023e9b73fc888d.png

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

    二、 代价聚合

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

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

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

    be536a07a0ac8c72f541bafaa6a72874.png

    图2 代价聚合前后视差图示意图

    三 、视差计算

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

    d212209ce3ad1f82c836d153614b6de0.png

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

    四、视差优化

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

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

    03a9f17c5f001a739737900fa3a4fd3b.png

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

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

    参考文献:

    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-08-16 13:14:25
     双目立体匹配一直是双目视觉的研究热点,双目相机拍摄同一场景的左、右两幅视点图像,运用立体匹配匹配算法获取视差图,进而获取深度图。而深度图的应用范围非常广泛,由于其能够记录场景中物体距离摄像机的距离,...

    原文链接 https://www.cnblogs.com/riddick/p/8486223.html

     

    真实场景的双目立体匹配(Stereo Matching)获取深度图详解

      双目立体匹配一直是双目视觉的研究热点,双目相机拍摄同一场景的左、右两幅视点图像,运用立体匹配匹配算法获取视差图,进而获取深度图。而深度图的应用范围非常广泛,由于其能够记录场景中物体距离摄像机的距离,可以用以测量、三维重建、以及虚拟视点的合成等。

      之前有两篇博客简要讲过OpenCV3.4中的两种立体匹配算法效果比较:http://www.cnblogs.com/riddick/p/8318997.html 。以及利用视差图合成新视点: http://www.cnblogs.com/riddick/p/7355353.html。里面用到的匹配图像对是OpenCV自带校正好的图像对。而目前大多数立体匹配算法使用的都是标准测试平台提供的标准图像对,比如著名的有如下两个:
      MiddleBury: http://vision.middlebury.edu/stereo/

      KITTI:http://www.cvlibs.net/datasets/kitti/eval_scene_flow.php?benchmark=stereo

      但是对于想自己尝试拍摄双目图片进行立体匹配获取深度图,进行三维重建等操作的童鞋来讲,要做的工作是比使用校正好的标准测试图像对要多的。因此博主觉得有必要从用双目相机拍摄图像开始,捋一捋这整个流程。

      主要分四个部分讲解:

    • 摄像机标定(包括内参和外参)
    • 双目图像的校正(包括畸变校正和立体校正)
    • 立体匹配算法获取视差图,以及深度图
    • 利用视差图,或者深度图进行虚拟视点的合成

      

      注:如果没有双目相机,可以使用单个相机平行移动拍摄,外参可以通过摄像机自标定算出。我用自己的手机拍摄,拍摄移动时尽量保证平行移动。

    一、摄像机标定

      1.内参标定

      摄像机内参反映的是摄像机坐标系到图像坐标系之间的投影关系。摄像机内参的标定使用张正友标定法,简单易操作,具体原理请拜读张正友的大作《A Flexible New Technique for Camera Calibration》。当然网上也会有很多资料可供查阅,MATLAB 有专门的摄像机标定工具包,OpenCV封装好的摄像机标定API等。使用OpenCV进行摄像机标定的可以参考我的第一篇博客:http://www.cnblogs.com/riddick/p/6696858.html。里面提供有张正友标定法OpenCV实现的源代码git地址,仅供参考。

      摄像机的内参包括,fx, fy, cx, cy,以及畸变系数[k1,k2,p1,p2,k3],详细就不赘述。我用手机对着电脑拍摄各个角度的棋盘格图像,棋盘格图像如图所示:

      使用OpenCV3.4+VS2015对手机进行内参标定。标定结果如下,手机镜头不是鱼眼镜头,因此使用普通相机模型标定即可:

      图像分辨率为:3968 x 2976。上面标定结果顺序依次为fx, fy, cx, cy,   k1, k2, p1, p2, k3, 保存到文件中供后续使用。

      2.外参标定

      摄像机外参反映的是摄像机坐标系和世界坐标系之间的旋转R和平移T关系。如果两个相机的内参均已知,并且知道各自与世界坐标系之间的R1、T1和R2,T2,就可以算出这两个相机之间的Rotation和Translation,也就找到了从一个相机坐标系到另一个相机坐标系之间的位置转换关系。摄像机外参标定也可以使用标定板,只是保证左、右两个相机同时拍摄同一个标定板的图像。外参一旦标定好,两个相机的结构就要保持固定,否则外参就会发生变化,需要重新进行外参标定。

      那么手机怎么保证拍摄同一个标定板图像并能够保持相对位置不变,这个是很难做到的,因为后续用来拍摄实际测试图像时,手机的位置肯定会发生变化。因此我使用外参自标定的方法,在拍摄实际场景的两张图像时,进行摄像机的外参自标定,从而获取当时两个摄像机位置之间的Rotation和Translation。

      比如:我拍摄这样两幅图像,以后用来进行立体匹配和虚拟视点合成的实验。

      

     ① 利用摄像机内参进行畸变校正,手机的畸变程度都很小,校正后的两幅图如下:

      

      ② 将上面两幅畸变校正后的图作为输入,使用OpenCV中的光流法提取匹配特征点对,pts1和pts2,在图像中画出如下:

      

      ③ 利用特征点对pts1和pts2,以及内参矩阵camK,解算出本质矩阵E:

        cv::Mat E = cv::findEssentialMat(tmpPts1, tmpPts2, camK, CV_RANSAC);

      ④  利用本质矩阵E解算出两个摄像机之间的Rotation和Translation,也就是两个摄像机之间的外参。以下是OpenCV中API函数实现的,具体请参见API文档:

        cv::Mat R1, R2;
        cv::decomposeEssentialMat(E, R1, R2, t);
    
        R = R1.clone();
        t = -t.clone();

     

    二、双目图像的校正

      1. 畸变校正

      畸变校正前面已经介绍过,利用畸变系数进行畸变校正即可,下面说一下立体校正。

      2. 立体校正

      ① 得到两个摄像机之间的 Rotation和Translation之后,要用下面的API对两幅图像进行立体对极线校正,这就需要算出两个相机做对极线校正需要的R和T,用R1,T1, R2, T2表示,以及透视投影矩阵P1,P2:

    cv::stereoRectify(camK, D, camK, D, imgL.size(), R, -R*t,  R1, R2, P1, P2, Q);

       ② 得到上述参数后,就可以使用下面的API进行对极线校正操作了,并将校正结果保存到本地:

    复制代码

      cv::initUndistortRectifyMap(P1(cv::Rect(0, 0, 3, 3)), D, R1, P1(cv::Rect(0, 0, 3, 3)), imgL.size(), CV_32FC1, mapx, mapy);
        cv::remap(imgL, recImgL, mapx, mapy, CV_INTER_LINEAR);
        cv::imwrite("data/recConyL.png", recImgL);
    
        cv::initUndistortRectifyMap(P2(cv::Rect(0, 0, 3, 3)), D, R2, P2(cv::Rect(0, 0, 3, 3)), imgL.size(), CV_32FC1, mapx, mapy);
        cv::remap(imgR, recImgR, mapx, mapy, CV_INTER_LINEAR);
        cv::imwrite("data/recConyR.png", recImgR);

    复制代码

       对极线校正结果如下所示,查看对极线校正结果是否准确,可以通过观察若干对应点是否在同一行上粗略估计得出:

      

     


     

     

    三、立体匹配

       1. SGBM算法获取视差图

      立体校正后的左右两幅图像得到后,匹配点是在同一行上的,可以使用OpenCV中的BM算法或者SGBM算法计算视差图。由于SGBM算法的表现要远远优于BM算法,因此采用SGBM算法获取视差图。SGBM中的参数设置如下:

    复制代码

       int numberOfDisparities = ((imgSize.width / 8) + 15) & -16;
        cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(0, 16, 3);
        sgbm->setPreFilterCap(32);
        int SADWindowSize = 9;
        int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;
        sgbm->setBlockSize(sgbmWinSize);
        int cn = imgL.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);
        int alg = STEREO_SGBM;
        if (alg == STEREO_HH)
            sgbm->setMode(cv::StereoSGBM::MODE_HH);
        else if (alg == STEREO_SGBM)
            sgbm->setMode(cv::StereoSGBM::MODE_SGBM);
        else if (alg == STEREO_3WAY)
            sgbm->setMode(cv::StereoSGBM::MODE_SGBM_3WAY);
        sgbm->compute(imgL, imgR, disp);

    复制代码

      默认计算出的是左视差图,如果需要计算右视差图,则将上面加粗的三条语句替换为下面前三条语句。由于视差值计算出来为负值,disp类型为16SC1,因此需要取绝对值,然后保存:

       sgbm->setMinDisparity(-numberOfDisparities);
        sgbm->setNumDisparities(numberOfDisparities);
        sgbm->compute(imgR, imgL, disp);
    
        disp = abs(disp);

      SGBM算法得到的左、右视差图如下,左视差图的数据类型为CV_16UC1,右视差图的数据类型为CV_16SC1 (SGBM中视差图中不可靠的视差值设置为最小视差(mindisp-1)*16。因此在此例中,左视差图中不可靠视差值设置为-16,截断值为0;右视差图中不可靠视差值设置为(-numberOfDisparities-1)*16,取绝对值后为(numberOfDisparities+1)*16,所以两幅图会有较大差别):

    左视差图(不可靠视差值为0)                                                      右视差图(不可靠视差值为 (numberOfDisparities+1)*16 )

      

      如果将右视差图不可靠视差值也设置为0,则如下

      至此,左视差图和右视差图遥相呼应。

      2. 视差图空洞填充

      视差图中视差值不可靠的视差大多数是由于遮挡引起,或者光照不均匀引起。既然牛逼如SGBM也觉得不可靠,那与其留着做个空洞,倒不如用附近可靠的视差值填充一下。

      空洞填充也有很多方法,在这里我检测出空洞区域,然后用附近可靠视差值的均值进行填充。填充后的视差图如下:

    填充后左视差图                                                                             填充后右视差图

      

     


     

      3. 视差图转换为深度图

      视差的单位是像素(pixel),深度的单位往往是毫米(mm)表示。而根据平行双目视觉的几何关系(此处不再画图推导,很简单),可以得到下面的视差与深度的转换公式:

     depth = ( f * baseline) / disp

       上式中,depth表示深度图;f表示归一化的焦距,也就是内参中的fx; baseline是两个相机光心之间的距离,称作基线距离;disp是视差值。等式后面的均已知,深度值即可算出。

      

      在上面我们用SGBM算法获取了视差图,接下来转换为深度图,函数代码如下:

    复制代码

    /*
    函数作用:视差图转深度图
    输入:
      dispMap ----视差图,8位单通道,CV_8UC1
      K       ----内参矩阵,float类型
    输出:
      depthMap ----深度图,16位无符号单通道,CV_16UC1
    */
    void disp2Depth(cv::Mat dispMap, cv::Mat &depthMap, cv::Mat K)
    {
        int type = dispMap.type();
    
        float fx = K.at<float>(0, 0);
        float fy = K.at<float>(1, 1);
        float cx = K.at<float>(0, 2);
        float cy = K.at<float>(1, 2);
        float baseline = 65; //基线距离65mm
    
        if (type == CV_8U)
        {
            const float PI = 3.14159265358;
            int height = dispMap.rows;
            int width = dispMap.cols;
    
            uchar* dispData = (uchar*)dispMap.data;
            ushort* depthData = (ushort*)depthMap.data;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int id = i*width + j;
                    if (!dispData[id])  continue;  //防止0除
                    depthData[id] = ushort( (float)fx *baseline / ((float)dispData[id]) );
                }
            }
        }
        else
        {
            cout << "please confirm dispImg's type!" << endl;
            cv::waitKey(0);
        }
    }

    复制代码

     

      注:png的图像格式可以保存16位无符号精度,即保存范围为0-65535,如果是mm为单位,则最大能表示约65米的深度,足够了。

      上面代码中我设置深度图的精度为CV_16UC1,也就是ushort类型,将baseline设置为65mm,转换后保存为png格式即可。如果保存为jpg或者bmp等图像格式,会将数据截断为0-255。所以保存深度图,png格式是理想的选择。(如果不是为了获取精确的深度图,可以将baseline设置为1,这样获取的是相对深度图,深度值也是相对的深度值)

       转换后的深度图如下:

    左深度图                                                                                        右深度图 

      

      空洞填充后的深度图,如下:

    左深度图(空洞填充后)                                                                右深度图(空洞填充后)

      

      视差图到深度图完成。

     

      注:视差图和深度图中均有计算不正确的点,此文意在介绍整个流程,不特别注重算法的优化,如有大神望不吝赐教。


     

    附:视差图和深度图的空洞填充

       步骤如下:

      ① 以视差图dispImg为例。计算图像的积分图integral,并保存对应积分图中每个积分值处所有累加的像素点个数n(空洞处的像素点不计入n中,因为空洞处像素值为0,对积分值没有任何作用,反而会平滑图像)。

      ② 采用多层次均值滤波。首先以一个较大的初始窗口去做均值滤波(积分图实现均值滤波就不多做介绍了,可以参考我之前的一篇博客),将大区域的空洞赋值。然后下次滤波时,将窗口尺寸缩小为原来的一半,利用原来的积分图再次滤波,给较小的空洞赋值(覆盖原来的值);依次类推,直至窗口大小变为3x3,此时停止滤波,得到最终结果。

      ③ 多层次滤波考虑的是对于初始较大的空洞区域,需要参考更多的邻域值,如果采用较小的滤波窗口,不能够完全填充,而如果全部采用较大的窗口,则图像会被严重平滑。因此根据空洞的大小,不断调整滤波窗口。先用大窗口给所有空洞赋值,然后利用逐渐变成小窗口滤波覆盖原来的值,这样既能保证空洞能被填充上,也能保证图像不会被过度平滑。

     

     空洞填充的函数代码如下,仅供参考:

    复制代码

     1 void insertDepth32f(cv::Mat& depth)
     2 {
     3     const int width = depth.cols;
     4     const int height = depth.rows;
     5     float* data = (float*)depth.data;
     6     cv::Mat integralMap = cv::Mat::zeros(height, width, CV_64F);
     7     cv::Mat ptsMap = cv::Mat::zeros(height, width, CV_32S);
     8     double* integral = (double*)integralMap.data;
     9     int* ptsIntegral = (int*)ptsMap.data;
    10     memset(integral, 0, sizeof(double) * width * height);
    11     memset(ptsIntegral, 0, sizeof(int) * width * height);
    12     for (int i = 0; i < height; ++i)
    13     {
    14         int id1 = i * width;
    15         for (int j = 0; j < width; ++j)
    16         {
    17             int id2 = id1 + j;
    18             if (data[id2] > 1e-3)
    19             {
    20                 integral[id2] = data[id2];
    21                 ptsIntegral[id2] = 1;
    22             }
    23         }
    24     }
    25     // 积分区间
    26     for (int i = 0; i < height; ++i)
    27     {
    28         int id1 = i * width;
    29         for (int j = 1; j < width; ++j)
    30         {
    31             int id2 = id1 + j;
    32             integral[id2] += integral[id2 - 1];
    33             ptsIntegral[id2] += ptsIntegral[id2 - 1];
    34         }
    35     }
    36     for (int i = 1; i < height; ++i)
    37     {
    38         int id1 = i * width;
    39         for (int j = 0; j < width; ++j)
    40         {
    41             int id2 = id1 + j;
    42             integral[id2] += integral[id2 - width];
    43             ptsIntegral[id2] += ptsIntegral[id2 - width];
    44         }
    45     }
    46     int wnd;
    47     double dWnd = 2;
    48     while (dWnd > 1)
    49     {
    50         wnd = int(dWnd);
    51         dWnd /= 2;
    52         for (int i = 0; i < height; ++i)
    53         {
    54             int id1 = i * width;
    55             for (int j = 0; j < width; ++j)
    56             {
    57                 int id2 = id1 + j;
    58                 int left = j - wnd - 1;
    59                 int right = j + wnd;
    60                 int top = i - wnd - 1;
    61                 int bot = i + wnd;
    62                 left = max(0, left);
    63                 right = min(right, width - 1);
    64                 top = max(0, top);
    65                 bot = min(bot, height - 1);
    66                 int dx = right - left;
    67                 int dy = (bot - top) * width;
    68                 int idLeftTop = top * width + left;
    69                 int idRightTop = idLeftTop + dx;
    70                 int idLeftBot = idLeftTop + dy;
    71                 int idRightBot = idLeftBot + dx;
    72                 int ptsCnt = ptsIntegral[idRightBot] + ptsIntegral[idLeftTop] - (ptsIntegral[idLeftBot] + ptsIntegral[idRightTop]);
    73                 double sumGray = integral[idRightBot] + integral[idLeftTop] - (integral[idLeftBot] + integral[idRightTop]);
    74                 if (ptsCnt <= 0)
    75                 {
    76                     continue;
    77                 }
    78                 data[id2] = float(sumGray / ptsCnt);
    79             }
    80         }
    81         int s = wnd / 2 * 2 + 1;
    82         if (s > 201)
    83         {
    84             s = 201;
    85         }
    86         cv::GaussianBlur(depth, depth, cv::Size(s, s), s, s);
    87     }
    88 }

    复制代码

    展开全文
  • 基于SIFT的OpenCV双目立体匹配系统,VS2013MFC 实现
  • 基于SUFT的OPENCV双目立体匹配系统,VS2013MFC 实现
  • 双目立体匹配算法漫谈双目立体匹配算法漫谈前提一些基本假设框架matching cost computationcost (support) aggregation;代价聚合功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何...

    双目立体匹配算法漫谈

    双目立体匹配算法是计算机视觉中比较经典的问题。有大量经典的双目立体匹配算法。本文简要介绍一下双目立体匹配的常用基本流程,也可以说是套路。 第一次写这么长的博文,我想到哪里写哪里。
    更详细内容的可见文章A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms。
    测试数据:www.middlebury.edu/stereo

    前提

    通常来说,硬件采集的图像首先经过畸变矫正,极线纠正,然后进行立体匹配,最后三角化为点云。如图1所示。首先需要说明的是,这里讲的立体匹配算法假定图像已经经过极线1纠正,从而两幅图像极线约束匹配变成图像的一行内匹配问题。在图1中对应的为红色的部分。以下没有特殊说明,两幅图像均指经过极线纠正后的图像。立体匹配问题这里主要考虑怎样计算极线纠正后的两幅图像之间的视差图。因为在基线和焦距已知的情况下,视差图可以和深度图互相转换,从而完成由双目估计物体深度(三维信息)的工作。
    常见的立体视觉流程

    图1 由立体视觉设备采集的图像生成点云的基本步骤

    按行匹配

    一些基本假设

    如果物体是一个朗伯体2,或者说物体表面是朗伯表面,那么,从任意位置和方向看物体表面同一点,得到的辐射能量是相同的。在可见光范围内,辐射也可以简单理解为RGB颜色值。那么匹配问题自然是按行寻找相同颜色的像素。理想很丰满,现实很骨感。一方面有可能在图像同一行内出现相同的颜色,无法区分。另一方面现实世界物体很难是一个朗伯体,即便是表面同一个点,在不同图像上的颜色都有差异。所以就有了诸多的算法来解决这些问题。

    首先可以假设物体表面深度是连续的,或者是分段连续的。(因为前文说了视差和深度在基线和焦距已知的情况可以相互转换,这里有时候说视差,有时候说深度,不必在意)。这样在图像邻域之间可以产生一个平滑约束。另外虽然场景不是一个朗伯表面,但是不同相机看相同点颜色还是非常接近的。

    框架

    现在讲一讲立体匹配的套路。主要由四个算法模块组成:

    1. matching cost computation; 匹配代价的计算
    2. cost (support) aggregation;代价聚合
    3. disparity computation/optimization; 视差图计算
    4. disparity refinement;视差图优化

    matching cost computation

    一个简单的想法是对于灰度图像,直接计算像素之间灰度差来作为匹配代价。但是这不够鲁棒。通常采用一个以当前像素为中心的window来代替像素,这些像素可以排成一个向量。向量的值为像素的灰度值。这样两个像素的匹配问题就转换成两个向量之间的距离。可以是L1,L2,或者夹角的余弦值等等。可以参考MAD、SAD、SSD、MSD、NCC、SSDA、SATD等匹配算子。

    将一个像素附近窗口转换为一个向量

    图1 将一个像素附近窗口转换为一个向量
    通常来说,以一幅图像作为参考图像,另外一幅成为目标图像,比如左图像为参考图像,右图像为目标图像。匹配过程为按行搜索,以某一行为。对于参考图像一个像素,目标图像有w个可能得匹配像素,w为图像宽度,这里假设两幅图像大小相同。所以对于每一行,需要做w*w次的匹配。当然,通过一定的假设和前提条件,我们可以限定视差范围为dmax那么匹配即为w*dmax次,参见图2。对于参考图像每一像素的匹配代价的次数按照深度排列,一个w*h的图像,得到w*h*dmax的代价立方体,深度为视差值或者可以可以转换为视差的值。如图3。有了代价立方体以后,我们需要得到每个像素的最优代价。这通常使用下面的技术方案:代价聚合。

    cost (support) aggregation;代价聚合

    代价聚合通常是一个立体匹配算法中非常关键的部分。因为一些噪声,光照差异,颜色不同和匹配歧义,匹配代价最优(代价最小)的不一定是真正的同名点,可以通过假设场景是连续的来进行处理。比较容易想到的是要考虑每个像素邻域信息。这里如果考虑某个像素与周围所有图像的关系,那么就是一个全局算法:如果仅仅考虑局部窗口内,那么就是一个局部算法。还有一种介于二者之间,称为半全局算法。显然,考虑的越多,计算量越大,但效果越好。通常来说,全局算法较慢,到效果很棒。



    1. 极线几何也称作核线几何,二者是一个意思,在摄影测量领域中常称作核线几何。极线约束也称作核线约束 ↩︎

    2. 朗伯体是指当入射能量在所有方向均匀反射,即入射能量以入射点为中心,在整个半球空间内向四周各向同性的反射能量的现象,称为漫反射,也称各向同性反射,一个完全的漫射体称为朗伯体 ↩︎

    展开全文
  • 双目立体匹配 Census

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

    千次阅读 2017-11-09 15:00:02
    双目立体匹配 SAD算法 编译环境: visual studio 2015 + opencv3.2.0 + window 10
  • 大部分基于卷积神经网络的双目立体匹配算法往往将双目图像对的像素级别特征作为匹配代价进行计算,缺乏将全局特征信息结合到立体匹配算法的能力,导致不适定区域(如弱纹理区域、反光表面、细长结构、视差不连续区域等)...
  • 双目立体匹配图片集,很多经典图片对,大多经典论文中都有用到,做立体匹配的朋友可以下载。 双目立体匹配图片集,很多经典图片对,大多经典论文中都有用到,做立体匹配的朋友可以下载。
  • 用于双目立体匹配的测试数据集,包含标准视差图,整合了2001、2003、2005、2006四年的数据集
  • 作者:李迎松来源:公众号 @3D视觉工坊链接:双目立体匹配步骤详解根据Schrstein和Szeliski的总结,双目立体匹配可划分为四个步骤:匹配代价计算、代价聚合、视差计算和视差优化。一 、匹配代价计算匹配代价计算的...
  • 针对AD-Census变换采用固定权重将AD变换代价与Census变换代价合成的双目立体匹配代价无法体现像素点区域特征的问题,提出一种基于自适应权重AD-Census变换的双目立体匹配算法。算法首先通过增加相邻像素点的灰度差...
  • ADCensus立体匹配算法是Xing Mei, Xun Sun, Mingcai Zhou等几个人 在2011年,发表的一篇题目为:《On ... ADCensus双目立体匹配算法曾一度排在MiddleBurry官网上第一的位置,引用量很高。 其主要优点是并行加速...
  • 双目立体匹配一直是双目视觉的研究热点,双目相机拍摄同一场景的左、右两幅视点图像,运用立体匹配匹配算法获取视差图,进而获取深度图。而深度图的应用范围非常广泛,由于其能够记录场景中物体距离摄像机的距离,...
  • 这里开源了一些经典的立体匹配算法,你可能会感兴趣:ethan-li-coding - Overview​github.com根据Schrstein和Szeliski的总结,双目立体匹配可划分为四个步骤:匹配代价计算、代价聚合、视差计算和视差优化。...
  • 双目立体匹配测试数据集Middlebury Stereo Datasets,包含Middlebury Stereo官网上面的03,05,06年的三部分数据集,01,14年的数据集下载失败,分享给大家。
  • 基于Opencv的双目立体匹配及其重建

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

空空如也

空空如也

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

双目立体匹配