找出两张图像的对应关系,根据三角测量原理,得到视差图;在获得了视差信息后,根据投影模型可以得到原始图像的深度信息和三维信息。
匹配代价计算: 一般是通过计算左右两图对应像素3个通道的灰度值差来决定匹配代价的,常用的就是基于像素点匹配代价计算,一般有AD, SD,TAD,基于区域的匹配代价计算一般有SAD,SSD, STAD。匹配代价计算会生成一个disparity space image,也就是DSI。这个DSI是一个三维的空间,也就是每一个视差,得到一张代价图。假如视差范围是0~16,则会得到17幅代价图。视差搜索范围就是MiddleBurry网站上的stereo pair值,也就是说在视差范围内(比如0-16)搜索匹配代价,得到17张匹配代价图,然后找到匹配代价最小的对应的视差值就是此像素对应的视差。
**代价聚合:**其实就是一个滤波的过程,对每一幅代价图进行聚合,最简单的就是采用boxfilter。第一步代价计算只是得到了图像上所有孤立像素的视差值,但是这些时差值都是孤立的,引入了过多噪声,比如一片区域的视差值都是10,可是引入噪声后就会导致这一片的视差值都不一样,那么就需要一个滤波的过程,也就是我们所说的局部立体匹配方法,即采用窗口卷积达到局部滤波的目的。
计算视差:常用的方法就是WTA算法(局部),对于图像中的同一个点,选出17幅代价图中匹配代价最小的那张图,该幅图对应的视差值就选取为最终的视差。或者在全局立体匹配中采用能量函数的方法,分为数据项和平滑项,数据项其实就是代价计算,平滑项就是代价聚合,只不过窗口大小是整幅图像,也可以试试如果把平滑项前面的系数lamda设为0,那么得到的结果和单纯代价计算的局部立体匹配是一样的。
视差精化:对得到的视差进行优化的过程,如:左右一致性检测、区域投票等;这步就是做如遮挡处理,中值滤波,左右一致性检测等,都能使最后的是视差图提升1%左右,立体匹配最关键的步骤仍然是代价计算和代价聚合步骤。
对于原始的图像内任意一个像素点 ( p x , p y ) ( p x , p y ) ( p x , p y ) (p_x,p_y) (p_x,p_y)(p_x ,p_y) (px,py)(px,py)(px,py)构建一个 n × n n×n n×n的邻域作为匹配窗口。然后对于目标相素位置 ( p x + d , p y ) ( p x + d , p y ) ( p x + d , p y ) (p_x+d,p_y) (p_x+d, p_y)(p_x+d,p_y) (px+d,py)(px+d,py)(px+d,py)同样构建一个 n × n n×n n×n大小的匹配窗口,对两个窗口进行相似度度量,注意这里的 d d d有一个取值范围。对于两幅图像来说,在进行 N C C NCC 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:\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()
wid值大小的设置会影响NCC视差匹配结果,wid值越小,噪点较多,成像模糊,但是物体的轮廓较准确;随着wid值的增大,噪声点明显减少,对比度逐渐提高,成像较为清晰,但是物体轮廓会出现一些弯曲的现象,再使用NCC算法时应适当取wid值。
参考链接:
[1] https://blog.csdn.net/u010368556/article/details/72823875