归一化相关性,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 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 = xr - xl,最终我们可以得到一个与原始图像尺寸相同的视差图D。
4. 深度恢复:通过上述匹配结果得到的视差图D,我们可以很简单的利用相似三角形反推出以左视图为参考系的深度图。计算原理如下图所示:如图,Tx为双目相机基线,f为相机焦距,这些可以通过相机标定步骤得到。而xr - xl就是视差d。
通过公式 z = f * Tx / d可以很简单地得到以左视图为参考系的深度图了。
至此,我们便完成了双目立体匹配。倘若只是用于图像识别,那么到步骤3时已经可以结束了。
OK,最后一部分就是代码实现部分了,哎~ 写太累了,下次再补上。
未完待续。。。