【计算机视觉07】视差计算图
立体视觉
-
一. 概念理解
-
- 1.视差:
- 2.窗口计算视差:
- 3.影响视差的因素
-
二. 视差计算步骤
-
三. NCC视差匹配
-
四. 代码
-
五. 结果分析
一. 概念理解
1.视差:
在左右双目成像系统中,在左眼和右眼所成的图像中(即左右双目图像),对应区域中心点之间的横向间距被定义为视差值(disparity)。当该值越大时,则说明观察物体离摄像头越近(即物体与摄像头之间的距离与之相关联)。反之,在视差点的位置上(即离摄像头较远的地方),对应的视差点之间的横向间距会较小。
2.窗口计算视差:
创建一个较小的窗口,并以此为基础在左图上进行覆盖操作;提取该区域内所有像素点;同时,在右图中进行相同操作;计算左图中被选区与右图中被选区之间的差异;然后计算所有被选 pixels 的灰度差绝对值总和;此算法常用于图像块匹配问题中;该算法通过计算各 pixels 对应数值之差绝对值总和来评估两个图像块之间的相似程度。
3.影响视差的因素
1) 光学畸变与干扰(明度、色调、饱和度失衡)
2) 镜面反射特性
3) 投影尺寸缩减
4) 透视变形
5) 低纹理细节
6) 周期性纹理重复
7) 半透光物体
8) 复合结构与不连续性
二. 视差计算步骤
必须对相机进行标定
- 立体标定
获取左右相机的空间转换关系
极线校正
在同一个空间中的一个点,在两张不同的图像中对应的映射关系经过极线校正后,在两张图像中对应点的y坐标保持一致,并且这种校正是为了减少需要进行匹配的点对数量
- 立体匹配
通过计算各点之间的视差值,并对每个视差值进行计算后,则确定了各点的空间坐标。
三. NCC视差匹配
对于原始图像中的任意一个像素点(Px, Py), 我们需要建立一个n \times n的邻域区域作为匹配窗口. 同时, 我们还需要建立另一个n \times n大小的匹配窗口, 用于定位目标像素点(Px + d, Py). 通过计算两者之间的相似度, 我们能够确定d的具体取值范围. 在实际应用中, 进行NCC(归一化相关性交叉)计算之前, 需要对图像进行预处理工作. 这种预处理包括将两幅图像水平对齐, 并确保光心处于同一水平位置. 这种操作使得极线呈现水平状态. 如果极线并非水平分布, 那么在倾斜方向上完成匹配过程将会消耗更多的计算资源.

四. 代码
/# -*- 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:\PycharmProjects\photo\left.png').convert('L'), 'f')
im_r = array(Image.open(r'D:\PycharmProjects\photo\right.png').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 10
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
scipy.misc.imsave('depth1.png',res)
show()
五. 结果分析





- 被圈出的区域因物体表面反射较强的原因,在实际应用中难以准确判断其与摄像头之间的远近距离。
- 窗口大小直接影响最终结果的表现:较大的模板会产生较低水平的深度图噪声;计算开销增加且过大的模板会导致物体边缘细节信息丢失。
