【计算机视觉】立体视觉计算视差图
实验要求:
- 从理论层面阐述基于窗口代价进行视差计算的原理。
- 具体实施NCC视差匹配方法:即通过左、右两幅图像应用NCC算法生成对应的视差图。
- 探讨不同窗口大小对匹配效果的影响,并重点关注哪些类型的点在不同尺寸下表现出较高的或较低的匹配精度。
文章目录
- 1. 实验基础
-
- 1.1 三维成像技术
- 1.2 视差计算模块
- 1.3 实验操作流程
-
- 1.3.1 数据获取阶段
- 1.3.2 校准极线路径:
- 1.3.3 特征配准:
- 1.3.4 深度估计过程
-
2. 实验内容
-
- 2.1 代码
- 2.2 实验结果
- 2.3 分析与总结
-
3. 错误及解决方法
-
1. 实验原理
立体视觉作为多视图成像的一种特例,在人类感知世界方面发挥着重要作用。
人类之所以能够感知世界归功于其发达的视觉系统。
为了实现机器人如同人类般辨识物体的能力,
我们不仅需要让机器人具备能识别物体的能力,
还要让它具备能测定障碍物与自身的距离。
立体视觉通过将各幅图像扭曲至同一基准平面,
使基线方向对齐于同一行,
从而实现不同视角下的画面校正。
在实际应用中,则会构建特定的立体平台以生成校正后的图像对。
1.1 立体图像

立体视觉技术即通过两台仅水平方向存在位置偏移的照相机观测同一个场景。当照相机按此设置布置时,所得两幅图像将在同一个图像平面上呈现,且其行坐标具有对齐关系,因此可称这对图像为校正过的像对。这种配置在机器人学领域极为常见,通常被统称为立体平台配置

其中,f代表经过校正后的图像焦距;b表示两台摄像头中心之间的距离;xl和xr分别代表左右两张图中对应点的x坐标值。将两台摄像头中心之间的距离称为基线长度
三维重建(致密深度重建)即相当于恢复深度图或视差图,在图像中每个像素的深度值都需要通过计算获得。
1.2 计算视差图
在该立体重建算法(NCC)中

在前面我们1.通过每个像素及其周边区域的图像数据计算归一化互相关值;2.忽略归一化常数因子后将该像素周围局部区域内所有像素的数值相加得到结果;3.针对整个图像中的每一个像素执行上述步骤以完成处理工作流程。所有这些累加运算均基于局部邻域区域完成以提高计算效率
1.3 实验步骤
1.3.1 采集图像
经过标定的双眼摄像头能够采集图像。同样地,两个单眼摄像头可以通过特定的方式协同工作以实现双眼效果。
1.3.2 极线校正:
校正的主要目的是让两帧图像中的极线水平排列,并让这两幅图像中的焦点位于同一水平线上。完成这种校正后会使得后续的NCC操作更加简便。
- 基于标定获得的内参中的畸变信息能够用于去除图像中的畸变。
- 经过校正函数处理后得到了相机的矫正变换R和新的投影矩阵P。下一步则是对左右视图进行去畸变,并计算出重映射矩阵。
1.3.3 特征匹配
此处为NCC匹配算法的主要步骤。如前所述,在右视图中与左视图待测像素同一水平线上具有最高相关性的即为最佳配对。完成配准后,则需记录其位移量d,并计算其中待测像素在水平方向上的位置xl与其配对像素位置xr之间的差距记作d= xr - xl。最终我们可以得到一个与原始图像尺寸相同的位移分布图D。
1.3.4 深度恢复
基于所述匹配结果所得出的视差图D, 我们能够较为便捷地借助相似三角形原理从左视图中推导出对应的深度信息. 计算原理阐述如下:

2. 实验内容
2.1 代码
# -*- 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
import scipy.misc
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'C://Users//yangyang//Desktop//image1//pic1.jpg').convert('L'), 'f')
im_r = array(Image.open(r'C://Users//yangyang//Desktop//image1//pic2.jpg').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 9
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
scipy.misc.imsave('depth.png',res)
show()
代码解读
2.2 实验结果
输入:

输出:

2.3 分析与总结
当窗口较小时(即较小尺寸下),... 的处理结果会保留更多的边缘细节但整体噪 声较高;而在较大尺寸下(即窗口较大时),虽然能得到较低水平的噪声抑制效果但会丢失 一部分细节信息。因此,在实际应用中选择合适的 ... 参数至关重要(如上图所示)。根据实验结果发现,在测试中选取 ...=9 是一个较为合适的选择。
当左右对比范围的选择也不能过大时(即选择 的左右图片跨度不能过大),则可能导致整体模糊不清的情况出现(如上文所述)。如果跨度太大,则可能导致整体模糊不清的情况出现(如上文所述)。如果跨度太大,则可能导致整体模糊不清的情况出现(如上文所述)。如果跨度太大,则可能导致整体模糊不清的情况出现(如上文所述)。如果跨度太大,则可能导致整体模糊不清的情况出现(如上文所述)。
3. 错误及解决方法
问题:type object 'Image' lacks the attribute 'open' 解决方案:由于库加载顺序不当导致无法获取图像打开方法。重新调整加载顺序可解决此问题
软件包'scipymisc'不包含属性'simirade';解决方案:将Python库'scrapylib'升级至'scrapylib==1.2.1'能够解决问题。
参考博客:
