归一化相关性,normalization cross-correlation,因此简称NCC,下文中笔者将用NCC来代替这冗长的名称。

  NCC,顾名思义,就是用于归一化待匹配目标之间的相关程度,注意这里比较的是原始像素。通过在待匹配像素位置p(px,py)构建3*3邻域匹配窗口,与目标像素位置p'(px+d,py)同样构建邻域匹配窗口的方式建立目标函数来对匹配窗口进行度量相关性,注意这里构建相关窗口的前提是两帧图像之间已经校正到水平位置,即光心处于同一水平线上,此时极线是水平的,否则匹配过程只能在倾斜的极线方向上完成,这将消耗更多的计算资源。相关程度的度量方式由如下式子定义:

  

  上式中的变量需要解释一下:其中p点表示图像I1待匹配像素坐标(px,py),d表示在图像I2被查询像素位置在水平方向上与px的距离。如下图所示: 

    

  左边为图像I1,右边为图像I2。图像I1,蓝色方框表示待匹配像素坐标(px,py),图像I2蓝色方框表示坐标位置为(px,py),红色方框表示坐标位置(px+d,py)。(由于画图水平有限,只能文字和图片双重说明来完成了~)

  Wp表示以待匹配像素坐标为中心的匹配窗口,通常为3*3匹配窗口。

  没有上划线的I1表示匹配窗口中某个像素位置的像素值,带上划线的I1表示匹配窗口所有像素的均值。I2同理。

  上述公式表示度量两个匹配窗口之间的相关性,通过归一化将匹配结果限制在 [-1,1]的范围内,可以非常方便得到判断匹配窗口相关程度:

  若NCC = -1,则表示两个匹配窗口完全不相关,相反,若NCC = 1时,表示两个匹配窗口相关程度非常高。

  我们很自然的可以想到,如果同一个相机连续拍摄两张图像(注意,此时相机没有旋转也没有位移,此外光照没有明显变化,因为基于原始像素的匹配方法通常对上述条件是不具备不变性的),其中有一个位置是重复出现在两帧图像中的。比如桌子上的一个可乐瓶。那么我们就可以对这个可乐瓶的位置做一下匹配。直观的看,第一帧中可乐瓶上某一个点,它所构成邻域窗口按理说应该是与第二帧相同的,就算不完全相同,也应该是具有非常高相关性的。基于这种感性的理解,于是才有前辈提出上述的NCC匹配方法。(纯属个人理解)

  


  双目立体匹配,这一部分是说明NCC如何用于双目匹配。

  假设有校正过的两帧图像I1,I2,由上述NCC计算流程的描述可知,对图像I1一个待匹配像素构建3*3匹配窗口,在图像I2极线上对每一个像素构建匹配窗口与待匹配像素匹配窗口计算相关性,相关性最高的视为最优匹配。很明显,这是一个一对多的过程。如果图像尺寸是640*480,则每一个像素的匹配过程是是1对640,两帧图像完全匹配需要计算640*480*640 = 196608000,即一亿九千多万次~ 尽管计算机计算速度非常快,但也着实是非常消耗计算资源的。由于NCC匹配流程是通过在同一行中查找最优匹配,因此它可以并行处理,这大概也算是一种弥补吧~

  双目立体匹配流程如下:

  1. 采集图像:通过标定好的双目相机采集图像,当然也可以用两个单目相机来组合成双目相机。(标定方法下次再说)

  2. 极线校正:校正的目的是使两帧图像极线处于水平方向,或者说是使两帧图像的光心处于同一水平线上。通过校正极线可以方便后续的NCC操作。

        

      2.1 由标定得到的内参中畸变信息中可以对图像去除畸变,在OpenCV中有函数对去畸变做了实现

      void stereoRectify(InputArray cameraMatrix1, InputArray distCoeffs1, InputArray cameraMatrix2, InputArray distCoeffs2, Size imageSize, InputArray R, InputArray T, OutputArray R1, OutputArray R2, OutputArray P1, OutputArray P2, OutputArray Q, int flags=CALIB_ZERO_DISPARITY, double alpha=-1, Size newImageSize=Size(), Rect* validPixROI1=0, Rect* validPixROI2=0 )
      cameraMatrix1:第一个相机矩阵(这里我们是双目的左相机).
        cameraMatrix2: 第二个相机矩阵(双目的右相机).
        distCoeffs1:第一个相机畸变参数.
        distCoeffs2: 第二个相机畸变参数.
        imageSize:用于校正的图像大小.
        R:第一和第二相机坐标系之间的旋转矩阵(左相机相对于右相机的旋转)
        T:第一和第二相机坐标系之间的平移矩阵(左相机相对于右相机的位移)
      R1:输出第一个相机的3x3矫正变换(旋转矩阵) .
      R2:输出第二个相机的3x3矫正变换(旋转矩阵) .
      P1:在第一台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵
      P2:在第二台相机的新的坐标系统(矫正过的)输出 3x4 的投影矩阵
      Q:输出深度视差映射矩阵
      flags:操作的 flag可以是零或者是CV_CALIB_ZERO_DISPARITY。如果设置了CV_CALIB_ZERO_DISPARITY,函数的作用是使每个相机的主点在校正后的图像上有相同的像素坐标;如果未设置标志,功能还可以改变图像在水平或垂直方向(取决于极线的方向)来最大化有用的图像区域。
      alpha:自由缩放参数。如果是-1或没有,该函数执行默认缩放。否则,该参数应在0和1之间。alpha=0,校正后的图像进行缩放和偏移,只有有效像素是可见的(校正后没有黑色区域);alpha= 1意味着校正图像的抽取和转移,所有相机原始图像素像保留在校正后的图像(源图像像素没有丢失)。显然,任何中间值产生这两种极端情况之间的中间结果。
         newImageSize:校正后新的图像分辨率。
      validPixROI1: 校正后的图像可选的输出矩形,里面所有像素都是有效的。如果alpha= 0,ROIs覆盖整个图像。否则,他们可能会比较小。
      validPixROI2: 校正后的图像可选的输出矩形,里面所有像素都是有效的。如果alpha= 0,ROIs覆盖整个图像。否则,他们可能会比较小。
      
      2.2 通过校正函数校正以后得到相机的矫正变换R和新的投影矩阵P,接下来是要对左右视图进行去畸变,并得到重映射矩阵。这里,我们还是用OpenCV函数
      void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs, InputArray R, InputArray newCameraMatrix, Size size, int m1type, OutputArray map1, OutputArray map2 )
      cameraMatrix:输入的摄像机内参数矩阵
      distCoeffs:输入的摄像机畸变系数矩阵
      R:输入的第一和第二相机坐标系之间的旋转矩阵(我们这里是利用上述校正得到的旋转矩阵)
      newCameraMatrix:输入的校正后的3X3摄像机矩阵(我们这里是使用上述校正得到的投影矩阵)
      size:摄像机采集的无失真图像尺寸
      m1type:map1的数据类型,可以是CV_32FC1或CV_16SC2
      map1:输出的X坐标重映射参数
      map2:输出的Y坐标重映射参数
      2.2 根据上述得到的重映射参数map1,map2,我们需要进一步对原始图像进行重映射到新的平面中才能去除图像畸变,同样,实现方式仍是使用现有的OpenCV函数

void remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, intborderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar() )

      src:输入图像,即原图像,需要单通道8位或者浮点类型的图像

      dst:输出图像,即目标图像,需和原图形一样的尺寸和类型

      map1:它有两种可能表示的对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2,CV_32FC1等类型的x值矩阵

      map2:它有两种可能表示的对象:(1)若map1表示点(x,y)时,这个参数不代表任何值;(2)表示CV_16UC1,CV_32FC1等类型y值矩阵

      interpolation:插值方式,有四中插值方式:(1)INTER_NEAREST——最近邻插值,(2)INTER_LINEAR——双线性插值(默认),(3)INTER_CUBIC——双三样条插值(默认),(4)INTER_LANCZOS4——lanczos插值(默认)

      intborderMode :边界模式,默认BORDER_CONSTANT

      borderValue :边界颜色,默认Scalar()黑色

      2.3 通过上述两步操作,我们成功地对图像去除了畸变,并且校正了图像极线。注意,在立体校正阶段需要设置alpha = 0才能完成对图像的裁剪,否则会有黑边。

  3. 特征匹配:这里便是我们利用NCC做匹配的步骤啦,匹配方法如上所述,右视图中与左视图待测像素同一水平线上相关性最高的即为最优匹配。完成匹配后,我们需要记录其视差d,即待测像素水平方向xl与匹配像素水平方向xr之间的差值d = x- xl,最终我们可以得到一个与原始图像尺寸相同的视差图D。

  4. 深度恢复:通过上述匹配结果得到的视差图D,我们可以很简单的利用相似三角形反推出以左视图为参考系的深度图。计算原理如下图所示:

        

        如图,Tx为双目相机基线,f为相机焦距,这些可以通过相机标定步骤得到。而x- xl就是视差d。

        通过公式 z = f * Tx / d可以很简单地得到以左视图为参考系的深度图了。

  至此,我们便完成了双目立体匹配。倘若只是用于图像识别,那么到步骤3时已经可以结束了。


  OK,最后一部分就是代码实现部分了,哎~ 写太累了,下次再补上。

  未完待续。。。

  

      

        

  

双目立体匹配——归一化互相关(NCC)的更多相关文章

  1. 图像匹配 | NCC 归一化互相关损失 | 代码 + 讲解

    文章转载自:微信公众号「机器学习炼丹术」 作者:炼丹兄(已授权) 作者联系方式:微信cyx645016617(欢迎交流共同进步) 本次的内容主要讲解NCCNormalized cross-correl ...

  2. 真实场景的双目立体匹配(stereo matching)以及虚拟视点合成(virtual view synthsis)示例

    双目立体匹配一直是双目视觉的研究热点,双目相机拍摄同一场景的左.右两幅视点图像,运用立体匹配匹配算法获取视差图,进而获取深度图.而深度图的应用范围非常广泛,由于其能够记录场景中物体距离摄像机的距离,可 ...

  3. 双目立体匹配经典算法之Semi-Global Matching(SGM)概述:匹配代价计算之互信息(Mutual Information,MI)

      半全局立体匹配算法Semi-Global Matching,SGM由学者Hirschmüller在2005年所提出1,提出的背景是一方面高效率的局部算法由于所基于的局部窗口视差相同的假设在很多情况 ...

  4. 双目立体匹配经典算法之Semi-Global Matching(SGM)概述:视差计算、视差优化

    文章目录 视差计算 视差优化 剔除错误匹配 提高视差精度 抑制噪声 视差计算   在SGM算法中,视差计算采用赢家通吃(WTA)算法,每个像素选择最小聚合代价值所对应的视差值作为最终视差,视差计算的结 ...

  5. 双目立体匹配经典算法之Semi-Global Matching(SGM)概述:代价聚合(Cost Aggregation)

      由于代价计算步骤只考虑了局部的相关性,对噪声非常敏感,无法直接用来计算最优视差,所以SGM算法通过代价聚合步骤,使聚合后的代价值能够更准确的反应像素之间的相关性,如图1所示.聚合后的新的代价值保存 ...

  6. 立体匹配-----NCC视差匹配

    目录 一.立体匹配算法 1.立体匹配算法分类 二.NCC 视差匹配方法 1.原理 2.NCC计算公式 3.算法流程 4.代码实现     5.不同场景运行 三.结论 四.遇到的问题及解决方法 一.立体 ...

  7. 学习OpenCV双目测距原理及常见问题解答

    学习OpenCV双目测距原理及常见问题解答 转自博客:https://blog.csdn.net/angle_cal/article/details/50800775 一. 整体思路和问题转化.  图 ...

  8. 三角化---深度滤波器---单目稠密重建(高翔slam---十三讲)

    一.三角化 [1]三角化得到空间点的三维信息(深度值) (1)三角化的提出 三角化最早由高斯提出,并应用于测量学中.简单来讲就是:在不同的位置观测同一个三维点P(x, y, z),已知在不同位置处观察 ...

  9. Computer Vision_33_SIFT:A novel coarse-to-fine scheme for automatic image registration based on SIFT and mutual information——2014

    此部分是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面.对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献.有一些刚刚出版的 ...

随机推荐

  1. Effective C++ 第0章 explicit构造函数

    按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示: class String { String ( const char* p ); ...

  2. CodeForces Round #555 Div.3

    A. Reachable Numbers 代码: #include <bits/stdc++.h> using namespace std; ; int N; set<int> ...

  3. Spring Cloud Netflix vs Spring Cloud Alibaba

    Spring Cloud Netflixhttps://spring.io/projects/spring-cloud-netflix spring-cloud-alibaba/README-zh.m ...

  4. x86汇编寄存器,函数参数入栈说明

    https://en.wikipedia.org/wiki/X86_calling_conventions

  5. 迁移git

    转自:https://www.darrenfang.com/2016/03/transferring-a-repository/ 因为更换服务器,需要将原来的 git 项目迁移到新的服务器上,需要保留 ...

  6. JQ高级

    一.选择器 css语法选择器 $('css3 选择器位‘) 索引匹配 $('div:eq(0)') $('div').eq(0) 内容 $('div:contains(标签文本内容)') // 注:采 ...

  7. jatoolsprinter html实现每隔几秒获取数据直接后台打印不弹窗

    1.流程说明 jatoolspringter 必须要能在html代码里面看到  id =page1 page2 page3..... 才能打印,所以无法动态打印,必须先把要打印的内容放到页面某个地方隐 ...

  8. IDictionary使用/声明

    因为不常用,老师忘记怎么申明..这次记下来,哪天用了又忘了就来翻翻 主要代码 IDictionary<string, string> openWith = new Dictionary&l ...

  9. P1417 烹调方案 (0/1背包+贪心)

    题目背景 由于你的帮助,火星只遭受了最小的损失.但gw懒得重建家园了,就造了一艘飞船飞向遥远的earth星.不过飞船飞到一半,gw发现了一个很严重的问题:肚子饿了~ gw还是会做饭的,于是拿出了储藏的 ...

  10. 自定义滚动条样式-transition无效

    问题 需求是自定义滚动条样式,然后2秒内无操作隐藏滚动条. 2s内隐藏比较麻烦,不能用css实现,只能监听容器的touch事件,然后给滚动条加个opacity: 0的class. .class::-w ...