计算视觉——NCC视差匹配
计算视觉——NCC视差匹配
-
一、立体匹配
-
- 1.1 原理
- 1.2 步骤
-
二、NCC视差匹配
-
- 2.1 原理
- 2.2 计算公式
-
三、实验过程
-
- 3.1 实验准备
- 3.2 实验代码
- 3.3 实验结果
-
四、总结
一、立体匹配
1.1 原理
确定两张图像之间的对应关系,并依据三角测量原理获得视差图;在获取了视差信息之后,则可利用投影模型来获得原始图像的深度数据以及三维空间中的相关信息。
1.2 步骤
匹配代价计算: 通常采用左右两幅图像对应像素三个通道灰度值差异作为衡量标准来确定匹配成本的方法。其中常用的包括基于单个像素点的匹配成本计算方法(如Ad、Sd、Tad),以及基于区域划分的方法(如Sad、Ssd、stad)。这些方法都会生成一个disparity space图像(即dsi)。这一dsi实际上是一个三维空间模型,在每个特定的视差点上都会对应一张代价图。当设置好视差点范围(例如从0到16)时,则会得到一系列不同的单页cost volume(共17张)。其中所谓的view disparity搜索范围实际上指的是取自MiddleBurry网站上的stereo pair参数值,在此范围内遍历所有候选视差位置对应的候选匹配成本矩阵中的数值信息,并最终确定该像素处最合理的视差位置即为该图像中该像素点处的空间位移量
代价聚合: 实质上是一种滤波操作, 将每副代价图进行融合处理, 其中最简单的方式是使用boxfilter。在第一步中, 我们主要计算并获得了图像上所有独立像素点的视差信息, 然而这些单独存在的视差点位带来了较多噪声干扰, 例如在一个区域内所有像素点的视差点位均为10, 这样一来, 引入大量噪声后就使得该区域内的所有像素点出现了不同的视差点位数值。因此需要通过一种滤波手段来恢复原始信息, 这也就是我们常说的局部立体匹配技术, 通过使用窗口卷积的方法实现该区域内的局部去噪处理。
视差计算:常用的算法包括局部分支的WTA算法,在图像中同一对应点上,在17幅候选图中选择匹配成本最低的那一幅作为最终结果。此外,在全局立体匹配过程中也可以通过能量函数进行建模,在这一模型中包含数据项和平滑项两部分。数据项即为各点之间的匹配成本计算;而平滑项则用于对各点的匹配结果进行平滑处理。值得注意的是,在这一模型中若将λ设为0时,则其结果与仅基于局部分支进行的视差估计完全一致。
视差精化:对获得的视差图进行优化的步骤,在此过程中包括左右一致性检测、区域投票等操作;在此过程中还会采用如遮挡消除、区域加权等方法来进一步精化视差图,在此过程中能够显著提升最终结果的质量约1%;立体匹配的关键步骤仍然是代价计算和代价聚合阶段的实现方式。
二、NCC视差匹配
2.1 原理
对于原始图像中的任意一个像素点(p_x, p_y), 构造一个n \times n大小的邻域作为匹配窗口. 接着针对目标像素位置(p_x + d, p_y), 同样构造一个n \times n规模的匹配窗口. 对这两个窗口进行相似度评估. 在此过程中需要注意的是d的变化范围. 为了在两幅图像之间实现归一化相关性计算(NCC), 首先需要对图像进行预处理以确保两帧图像具有相同的水平坐标系. 这种处理确保了极线方向为水平状态, 如果未完成这一校正则匹配过程只能在倾斜极线的方向上执行, 这将导致计算资源消耗增加.
2.2 计算公式

其中NCC(P, d)计算出的结果范围在-1到1之间。
Wp被定义为之前所述的匹配窗口。
I_1(x, y)代表原始图像在位置(x,y)处的像素强度。
I_1(P_x, P_y)表示原始图像内包含在窗口内的像素均值。
I_2(x + d, y)代表原始图像经方向偏移d后,在目标图像上对应点位置处的像素强度。
I_2(P_x + d, P_y)代表目标图像中与当前窗口相对应区域内的像素均值。
当NCC=-1时,则表明两个匹配窗口之间没有任何相关性;相反地,则表明当NCC= 1时两个匹配窗口之间的相关性达到了极高中度。
三、实验过程
3.1 实验准备
实验平台采用Python 2.7版本

3.2 实验代码
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
import cv2
from numpy import *
from numpy.ma import array
from scipy.ndimage import filters
def plane_sweep_ncc(im_l,im_r,start,steps,wid):
""" 使用归一化的互相关计算视差图像 """
m,n = im_l.shape
# 保存不同求和值的数组
mean_l = zeros((m,n))
mean_r = zeros((m,n))
s = zeros((m,n))
s_l = zeros((m,n))
s_r = zeros((m,n))
# 保存深度平面的数组
dmaps = zeros((m,n,steps))
# 计算图像块的平均值
filters.uniform_filter(im_l,wid,mean_l)
filters.uniform_filter(im_r,wid,mean_r)
# 归一化图像
norm_l = im_l - mean_l
norm_r = im_r - mean_r
# 尝试不同的视差
for displ in range(steps):
# 将左边图像移动到右边,计算加和
filters.uniform_filter(np.roll(norm_l, -displ - start) * norm_r, wid, s) # 和归一化
filters.uniform_filter(np.roll(norm_l, -displ - start) * np.roll(norm_l, -displ - start), wid, s_l)
filters.uniform_filter(norm_r*norm_r,wid,s_r) # 和反归一化
# 保存 ncc 的分数
dmaps[:,:,displ] = s / sqrt(s_l * s_r)
# 为每个像素选取最佳深度
return np.argmax(dmaps, axis=2)
def plane_sweep_gauss(im_l,im_r,start,steps,wid):
""" 使用带有高斯加权周边的归一化互相关计算视差图像 """
m,n = im_l.shape
# 保存不同加和的数组
mean_l = zeros((m,n))
mean_r = zeros((m,n))
s = zeros((m,n))
s_l = zeros((m,n))
s_r = zeros((m,n))
# 保存深度平面的数组
dmaps = zeros((m,n,steps))
# 计算平均值
filters.gaussian_filter(im_l,wid,0,mean_l)
filters.gaussian_filter(im_r,wid,0,mean_r)
# 归一化图像
norm_l = im_l - mean_l
norm_r = im_r - mean_r
# 尝试不同的视差
for displ in range(steps):
# 将左边图像移动到右边,计算加和
filters.gaussian_filter(np.roll(norm_l, -displ - start) * norm_r, wid, 0, s) # 和归一化
filters.gaussian_filter(np.roll(norm_l, -displ - start) * np.roll(norm_l, -displ - start), wid, 0, s_l)
filters.gaussian_filter(norm_r*norm_r,wid,0,s_r) # 和反归一化
# 保存 ncc 的分数
dmaps[:,:,displ] = s / np.sqrt(s_l * s_r)
# 为每个像素选取最佳深度
return np.argmax(dmaps, axis=2)
im_l = array(Image.open(r'D:\ComputerVision_code\img6\left.png').convert('L'), 'f')
im_r = array(Image.open(r'D:\ComputerVision_code\img6\right.png').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 15
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
scipy.misc.imsave('depth1.png',res)
show()
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
import cv2
from numpy import *
from numpy.ma import array
from scipy.ndimage import filters
def plane_sweep_ncc(im_l,im_r,start,steps,wid):
""" 使用归一化的互相关计算视差图像 """
m,n = im_l.shape
# 保存不同求和值的数组
mean_l = zeros((m,n))
mean_r = zeros((m,n))
s = zeros((m,n))
s_l = zeros((m,n))
s_r = zeros((m,n))
# 保存深度平面的数组
dmaps = zeros((m,n,steps))
# 计算图像块的平均值
filters.uniform_filter(im_l,wid,mean_l)
filters.uniform_filter(im_r,wid,mean_r)
# 归一化图像
norm_l = im_l - mean_l
norm_r = im_r - mean_r
# 尝试不同的视差
for displ in range(steps):
# 将左边图像移动到右边,计算加和
filters.uniform_filter(np.roll(norm_l, -displ - start) * norm_r, wid, s) # 和归一化
filters.uniform_filter(np.roll(norm_l, -displ - start) * np.roll(norm_l, -displ - start), wid, s_l)
filters.uniform_filter(norm_r*norm_r,wid,s_r) # 和反归一化
# 保存 ncc 的分数
dmaps[:,:,displ] = s / sqrt(s_l * s_r)
# 为每个像素选取最佳深度
return np.argmax(dmaps, axis=2)
def plane_sweep_gauss(im_l,im_r,start,steps,wid):
""" 使用带有高斯加权周边的归一化互相关计算视差图像 """
m,n = im_l.shape
# 保存不同加和的数组
mean_l = zeros((m,n))
mean_r = zeros((m,n))
s = zeros((m,n))
s_l = zeros((m,n))
s_r = zeros((m,n))
# 保存深度平面的数组
dmaps = zeros((m,n,steps))
# 计算平均值
filters.gaussian_filter(im_l,wid,0,mean_l)
filters.gaussian_filter(im_r,wid,0,mean_r)
# 归一化图像
norm_l = im_l - mean_l
norm_r = im_r - mean_r
# 尝试不同的视差
for displ in range(steps):
# 将左边图像移动到右边,计算加和
filters.gaussian_filter(np.roll(norm_l, -displ - start) * norm_r, wid, 0, s) # 和归一化
filters.gaussian_filter(np.roll(norm_l, -displ - start) * np.roll(norm_l, -displ - start), wid, 0, s_l)
filters.gaussian_filter(norm_r*norm_r,wid,0,s_r) # 和反归一化
# 保存 ncc 的分数
dmaps[:,:,displ] = s / np.sqrt(s_l * s_r)
# 为每个像素选取最佳深度
return np.argmax(dmaps, axis=2)
im_l = array(Image.open(r'D:\ComputerVision_code\img6\left.png').convert('L'), 'f')
im_r = array(Image.open(r'D:\ComputerVision_code\img6\right.png').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 15
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
from numpy.core.numeric import zeros
from numpy.core.multiarray import zeros
scipy.misc.imsave('depth1.png',res)
show()
3.3 实验结果
- wid = 2

在设置窗口尺寸为2×2的情况下进行匹配运算后得到的结果图中可见较多噪声干扰,在图像中呈现出明显的颗粒状特征,并且整体上图像呈现了一定的模糊度。边缘轮廓与原始图像基本一致,在视觉效果上难以清晰辨识边缘位置
- wid = 6

当窗口尺寸扩大至6×6时,在匹配效果上较采用2×2窗口的方式有了显著提升;实验数据显示残留噪声点基本无残留,在整体图像中呈现出明显的层次感;此外,在线框处理上也得到了较大的改善,在细节刻画上更为柔和自然;然而这些处理后的线条仍未能完全忠实于原始物体边界
- wid = 10

当窗口尺寸设定为10×10时,在匹配结果图中可以看到具有明确边界且对比度更加显著的轮廓特征,并且每一个检测到的轮廓都能精确对应到原图中的物体。
四、总结
设置不同的wid值会对NCC视差匹配结果产生影响。较小的wid值会导致成像模糊效果更加显著;而较大的wid值则能有效降低噪声点数量,并使影像清晰程度得到提升。然而需要注意的是,在实际应用中建议根据具体情况选取合适的wid值范围。
参考链接:
[1]
