视差

视差-Python

 

立体校正     

 立体校正的目的是将拍摄于同一场景的左右两个视图进行数学上的投影变换,使得两个成像平面平行于基线,且同一个点在左右两幅图中位于同一行,简称共面行对准。只有达到共面行对准以后才可以应用三角原理计算距离。

 

立体匹配

立体匹配的目的是为左图中的每一个像素点在右图中找到其对应点(世界中相同的物理点),这样就可以计算出视差: ,在视觉领域,视差disparity有时也被称为inverse depth,因此disparity和inverse depth指的是同一件东西。大部分立体匹配算法的计算过程可以分成以下几个阶段:匹配代价计算、代价聚合、视差优化、视差细化。立体匹配是立体视觉中一个很难的部分,主要困难在于:1.图像中的重复纹理和弱纹理区域很难匹配正确;2.遮挡区域像素匹配:对于遮挡区域,左图中有一些像素点在右图中并没有对应的点;3.左右相机所接收的光照情况不同;4.过度曝光区域难以匹配;5.倾斜表面、弯曲表面、非朗伯体表面;6.较高的图像噪声等。此外还可以对视差图进行滤波后处理,比较常用的方法有Guided Filter、Fast Global Smooth Filter(一种快速WLS滤波方法)、Bilatera Filter、TDSR、RBS等,可以在一定程度上降低噪声,改善视差图的视觉效果,并能够进行稀疏视差插值,不过比较依赖于初始的匹配结果的好坏。 

 

计算视差图

在立体重建算法中对于每个像素尝试不同的偏移,并按照局部图像周围 归一化的互相关值,选择具有最好分数的偏移,然后记录下该最佳偏移。因为每个 偏移在某种程度上对应于一个平面,所以该过程有时称为扫平面法。当密集地应用在图像中时,归一化的互相关值可以很快地计算出来。在像素周围重新写出公式中的 NCC,如下所示

 

 

双目测距的精度       

  双目测距的精度是很难进行理论分析的,需要根据实际情况去验证。一般来说,距离越远,误差越大,因此双目测距不适宜测量太远的目标。如果想要对与较远的目标能够得到较为可靠的深度,需要提高相机的基线距离,但是同时会导致左右视图的差异变大从而提高立体匹配的难度。

 

NCC 视差匹配方法

 

 

 

 

 

 

 

 

图片显示的较为模糊,但是大体上能够看出两张图片的重合,侧面的杯子和正面的杯子进行了重叠,但是背景墙面的花纹会影响匹配的结果,导致重叠出现偏差,从而重叠重点变为墙面的花纹,忽略了图片的主体,建议不要在有贴图的墙面前拍照。

 

更改窗口值进行测试:

支架图片

 窗口值为3时:

 

窗口值为6时:

 

 

窗口值为9时:

 

茶杯图片

 窗口值为3时:

 

窗口值为6时:

 

窗口值为9时:

 

 

 

总结

  • 基于NCC用来比较两幅图像的相似程度可以有效降低光照对图像比较结果的影响,而且NCC最终结果在0到1之间,所以特别容易量化比较结果,只要给出一个阈值就可以判断结果的好与坏。但是NCC比较耗时,虽然可以通过调整窗口大小和每次检测的步长矩形部分优化,但是运行速度实在是太慢了。
  • 通过调节窗口大小,可以提升运行的速率,当图像尺寸和模板窗口越大、左右图像视差越大时,耗时减少更为显著,且保持了较好的匹配精度。
  • 运行的结果与图片的质量也有关,大体量的图片运行时间不仅久,而且对电脑内存的占用也是相当大的,我仅仅跑3M的图,内存占用就达到88%,尽量选取小图片进行测试吧。
  • wid值改变后,经过滤波后图像的每一个像素的灰度值都是原图像中窗口值大小内的所有灰度值的均值,wid值越小,像素值与周围像素点的灰度值越偏离,越不明显;wid值越大,像素值与周围像素点的灰度值越接近,越清楚。但是边缘的细节就会变得模糊,更加凸显主体

 

 

 

 


# -*- coding: utf-8 -*-
import imageio
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'E:/test_pic/shicha/3.jpg').convert('L'), 'f')
im_r = array(Image.open(r'E:/test_pic/shicha/4.jpg').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 9
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)


imageio.imwrite('depth.png',res)
show()
 

 

posted on 2020-04-26 15:43  BC_CJ  阅读(1011)  评论(0编辑  收藏  举报

导航