立体重建:基于视觉的三维重建,指的是通过摄像机获取场景物体的数据图像,并对此图像进行分析处理,再结合计算机视觉知识推导出现实环境中物体的三维信息。
在本文介绍的该种算法中,我们将对于每个像素尝试不同的偏移,并按照局部图像周围 归一化的互相关值,选择具有最好分数的偏移,然后记录下该最佳偏移。因为每个 偏移在某种程度上对应于一个平面,所以该过程有时称为扫平面法。
注:虽然该方法并不是立体重建中最好的方法,但是非常简单,通常会得出令人满意的结果。
在立体匹配中,匹配问题可以看成是寻找两组数据相关程度的过程。立体匹配算法有多种分类。
①根据算法运行时约束的作用范围:分为局部(local)匹配算法和全局(Global)匹配算法。
全局匹配算法一般定义如下能量函数:
E ( d ) = E d a t a ( d ) + E s m o o t h ( d ) = ∑ p ∈ R C ( p , d ) + ∑ q , p ∈ R P ( d q − d p ) E(d)=E_{data}(d)+E_{smooth}(d)=\sum_{p\in R}C(p,d)+\sum_{q,p\in R}P(d_{q}-d_{p}) E(d)=Edata(d)+Esmooth(d)=p∈R∑C(p,d)+q,p∈R∑P(dq−dp)
其中 E d a t a ( d ) E_{data}(d) Edata(d)描述了匹配程度, E s m o o t h ( d ) E_{smooth}(d) Esmooth(d)是定义场景的约束,C是匹配代价,P是不同两个像素p和q视差的函数,一般称作惩罚项。
即使全局算法具有准确性较高的优点,其计算速度确非常慢,在实时性要求高的场合不适合使用全局立体匹配算法。
②基于生成的视差图:可分为稠密(Dense)匹配和稀疏(Sparse)匹配。
深度恢复:
通过上述匹配结果得到的视差图D,我们可以很简单的利用相似三角形反推出以左视图为参考系的深度图。
T为双目相机基线,f为相机焦距,这些可以通过相机标定得到,xr-xl就是视差d。通过 z = f ∗ T / d z=f*T/d z=f∗T/d.
绝对差值和SAD:
截断绝对差值和STAD:
差值平方和SSD:
# -*- coding:utf-8 _*-
import math
import numpy as np
from numpy import roll
from numpy import *
from numpy import argmax
from math import sqrt
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(roll(norm_l,-displ-start)*norm_r,wid,s) # 和归一化
filters.uniform_filter(roll(norm_l,-displ-start)*roll(norm_l,-displ-start),wid,s_l)
filters.uniform_filter(norm_r*norm_r,wid,s_r) # 和反归一化
# 保存 ncc 的分数
dmaps[:,:,displ] = s/np.sqrt(s_l*s_r)
# 为每个像素选取最佳深度
return 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(roll(norm_l,-displ-start)*norm_r,wid,0,s) # 和归一化
filters.gaussian_filter(roll(norm_l,-displ-start)*roll(norm_l,-displ-start),wid,0,s_l)
filters.gaussian_filter(norm_r*norm_r,wid,0,s_r) # 和反归一化
# 保存 ncc 的分数
dmaps[:,:,displ] = s/sqrt(s_l*s_r)
# 为每个像素选取最佳深度
return argmax(dmaps,axis=2)
# -*- coding:utf-8 _*-
import stereo
import array
from PIL import Image
from pylab import *
import imageio
im_l = array(Image.open(r'D:\CVphoto\cones-ppm-2\cones\im2.ppm').convert('L'),'f')
im_r = array(Image.open(r'D:\CVphoto\cones-ppm-2\cones\im6.ppm').convert('L'),'f')
#im_l = array(Image.open(r'D:\CVphoto\cones-ppm-2\cones\im2.ppm').convert('L'))
#im_r = array(Image.open(r'D:\CVphoto\cones-ppm-2\cones\im6.ppm').convert('L'))
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 9
res = stereo.plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
scipy.misc.imsave('depth.png',res)
#scipy.misc.imageio.imwrite('depth.png',res)
原条件下 :im2.ppm(左)和im6.ppm(右)
原条件下 :steps=12,start=4,wid=9,分别实现ncc(左)和gauss(右)小结:可以明显看出,不管是调用ncc或者加了高斯滤波器之后的gauss,当wid值越小,图像越密集,wid值越大图像轮廓更清晰一点,所以在窗口值很小时,会有更多的细节,噪声也会更大,当窗口值更大,有更强的鲁棒性,更加稳健,但与此同时也丢失了很多细节。
当有一个很大的wid时,边缘的细节将不再清晰,接近中心的位置的图像更加被突出。
所以我们要选择一个合适的窗口大小,使其在具有一定清晰度的情况下还可以有一定的稳定性。
同时可以看出与标准版本相比,高斯版本具有较少的噪声,但缺少很多细节信息。
在标准版本中设置 wid 为 9,高斯版本中设置 wid 为 3。
小结:可以看出,在立体图像的边缘处和棱角处更容易出现模糊的重影,猜测应该是因为两幅视图的视差在这些地方相对大一点,所以在这些有明显跨幅的地方,模糊的更加厉害。
进行高斯滤波之后的图像边缘更加平滑。
报错:TypeError: array() argument 1 or typecode must be char (string or ascii-unicode with length 1), not Image
错误原因:array()的第一参数必须是一个unicode,python array的第一个参数必须为一个typecode,用于表明支持初始化的类型,python自带的array只支持一维数组。
解决方法:使用import array
。
报错: TypeError: 'module' object is not callable
分析:Python导入模块的方法有两种,import module 和 from module import,报改错误应该是缺少模块
增加模块:from pylab import *
和from numpy import *
报错:AttributeError: 'module' object has no attribute 'uniform_filter'
解决方法:增加from scipy.ndimage import filters
报错:TypeError: only size-1 arrays can be converted to Python scalars
分析:import math中的sqrt不能直接使用。
解决方法: dmaps[:,:,displ] = s/sqrt(s_l*s_r)
中的sqrt不能之间使用,改成np.sqrt。(import numpy as np)