一个多视图成像的特殊例子是立体视觉(或者立体成像),即使用两台只有水平(向一侧)偏移的照相机观测同一场景。当照相机的位置如上设置,两幅图像具有相同的图像平面,图像的行是垂直对齐的,那么称图像对是经过矫正的。该设置在机器人学中很常见,常被称为立体平台。
通过将图像扭曲到公共的平面上,使外极线位于图像行上,任何立体照相机设置都能得到矫正(我们通常构建立体平台来产生经过矫正的图像对)。假设两幅图像经过了矫正,那么对应点的寻找限制在图像的同一行上。一旦找到对应点,由于深度是和偏移成正比的,那么深度(Z 坐标)可以直接由水平偏移来计算,,f 是经过矫正图像的焦距,b 是两个照相机中心之间的距离,xl 和 xr 是左右两幅图像中对应点的 x 坐标。分开照相机中心的距离称为基线。矫正后的立体照相机设置如图 所示。
基于滑动窗口的图像匹配
上述问题的解决方法:使用滑动窗口来进行匹配。如下图所示。对于左图中的一个像素点(左图中红色方框中心),在右图中从左到右用一个同尺寸滑动窗口内的像素和它计算相似程度,相似度的度量有很多种方法,比如 误差平方和法(Sum of Squared Differences,简称SSD),左右图中两个窗口越相似,SSD越小。下图中下方的SSD曲线显示了计算结果,SSD值最小的位置对应的像素点就是最佳的匹配结果。滑动窗口匹配原理示意图如下图所示。
具体操作中还有很多实际问题,比如滑动窗口尺寸。滑动窗口的大小选取还是很有讲究的。
对于原始的图像内任意一个像素点(px,py)构建一个n×n的邻域作为匹配窗口。然后对于目标相素位置(px+d,py)同样构建一个n×n大小的匹配窗口,对两个窗口进行相似度度量,注意这里的d有一个取值范围。对于两幅图像来说,在进行NCC计算之前要对图像处理,也就是将两帧图像校正到水平位置,即光心处于同一水平线上,此时极线是水平的,否则匹配过程只能在倾斜的极线方向上完成,这将消耗更多的计算资源。
NCC计算公式如下图所示:
其中NCC(p,d)得到的值得范围将在[−1,1]之间
Wp为之前提到的匹配窗口。
I1(x,y)为原始图像的像素值。
I1¯¯¯(px,py)为原始窗口内像素的均值。
I2(x+d,y)为原始图像在目标图像上对应点位置在x方向上偏移d后的像素值。
I2¯¯¯(px+d,py)为目标图像匹配窗口像素均值。
若NCC=−1则表示两个匹配窗口完全不相关,相反,若NCC=1时,表示两个匹配窗口相关程度非常高。
匹配流程:
①采集图像:通过标定好的双目相机采集图像,当然也可以用两个单目相机来组合成双目相机。
②极线校正:校正的目的是使两帧图像极线处于水平方向,或者说是使两帧图像的光心处于同一水平线上。通过校正极线可以方便后续的NCC 操作。
·由标定得到的内参中畸变信息中可以对图像去除畸变。
·通过校正函数校正以后得到相机的矫正变换R和新的投影矩阵P,接下来是要对左右视图进行去畸变,并得到重映射矩阵
③特征匹配:这里便是我们利用NCC做匹配的步骤啦,匹配方法如上所述,右视图中与左视图待测像素同一水平线上相关性最高的即为最优匹配。完成匹配后,我们需要记录其视差d,即待测像素水平方向xl与匹配像素水平方向xr之间的差值d=xr−xl,最终我们可以得到一个与原始图像尺寸相同的视差图D。
实验要求:
1 实现NCC 视差匹配方法,即给定左右两张视图,根据NCC计算视差图
2.分析不同窗口值对匹配结果的影响,重点考查那些点(或者哪些类型的点)在不同窗口大小下的匹配精度影响
# -*- coding: utf-8 -*-
# -*- 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
np.seterr(divide='ignore', invalid='ignore')
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:/computervisionpicture/left.jpg').convert('L'), 'f')
im_r = array(Image.open(r'D:/computervisionpicture/right.jpg').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 12
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
scipy.misc.imsave('D:/computervisionpicture/depth.jpg',res)
show()
实验用的图片:
实验结果(不同窗口值下的结果):
窗口值为2时
窗口值为3时
窗口值为6时
窗口值为9时
窗口值为12时
把图片尺寸改大一点,增加图片精度后的结果:
窗口值为12时
窗口值为9时
窗口值为6时
窗口值为3时
窗口值为2时
小结:
①自己的图片拍的是比较平面化,左右视差不明显,所以整体特征没有清晰地展现出来
②视差图特别复杂,是因为光线明亮变化区域很大,匹配的时候出现了许多错误匹配
③当窗口值为2或3比较小时,视差图模糊,是因为窗口值太小,里面的像素点也就越少,这样就会使匹配不准确,造成较大误差
④当窗口值为6或9时,视差图变得清晰,窗口变大,里面的数据变多,匹配点也会更加精确
⑤当窗口值为12时,结果图像又会丢失一些边缘化信息,这是因为窗口过大,虽然里面的像素点会更多,但是相邻两个匹配点的窗口的中的值会出现重复,导致丢失了一些原本的信息。
①小尺寸的窗口:精度更高、细节更丰富,匹配代价低,在弱纹理区域比较容易出现错误匹配但是对噪声特别敏感;
②大尺寸的窗口:精度不高、细节不够,匹配代价过高,在复杂的纹理区域容易出现错误匹配,但是对噪声比较鲁棒
③改变图片尺寸,增大图片精度,在一定程度上有利于窗口匹配,得到的视差图也会相对更清晰,所以图片大小要适宜。
④不同尺寸的滑动窗口对深度图计算结果的影响,虽然基于滑动窗口的匹配方法可以计算得到深度图,但是这种方法匹配效果并不好,而且由于要逐点进行滑动窗口匹配,计算效率也很低。
⑤NCC算法由于计算量偏大,所以运行起来还是比较耗时间的。