python计算机视觉编程——立体图像之计算视差图

计算视差图

    • 一、立体图像
      • 1.1概念
      • 1.2关于图像配准算法
    • 二、立体重建之计算视差图
      • 2.1归一化及算法概念
      • 2.2匹配流程
    • 三、实验测试
      • 3.1实验要求
      • 3.2实验代码
      • 3.3实验结果分析
      • 3.4实验总结和遇到的问题

此次实验的内容主要有关于计算视差图的内容,与教材《python计算机视觉编程》衔接。

一、立体图像

1.1概念

一个多视图成像的特殊例子是立体视觉(或者立体成像),即使用两台只有水平(向一侧)偏移的照相机观测同一场景。当照相机的位置如上设置,两幅图像具有相同的图像平面,图像的行是垂直对齐的,那么称图像对是经过矫正的。该设置在机器人学中很常见,常被称为立体平台。通过将图像扭曲到公共的平面上,使外极线位于图像行上,任何立体照相机设置都能得到矫正(我们通常构建立体平台来产生经过矫正的图像对)。假设两幅图像经过了矫正,那么对应点的寻找限制在图像的同一行上。一旦找到对应点,由于深度是和偏移成正比的,那么深度(Z 坐标)可以直接由水平偏移来
计算:
在这里插入图片描述
其中,f 是经过矫正图像的焦距,b 是两个照相机中心之间的距离,xl 和 xr 是左右两幅图像中对应点的 x 坐标。分开照相机中心的距离称为基线。矫正后的立体照相机设置如下图:
python计算机视觉编程——立体图像之计算视差图_第1张图片
矫正后立体照相机设置的示意图,其中对应点位于两幅图像的同一行。
立体重建(有时称为致密深度重建)就是恢复深度图(或者相反,视差图),图像中每个像素的深度(或者视差)都需要计算出来。这是计算机视觉中的经典问题。

1.2关于图像配准算法

要实现立体重建必须将采集到的图像做图像配准下面这篇博文对配准算法有一个概括性的解读,可以参考。
图像配准算法简介
主要介绍几种基于灰度的图像匹配算法:平均绝对差算法(MAD)、绝对误差和算法(SAD)、误差平方和算法(SSD)、平均误差平方和算法(MSD)、归一化积相关算法(NCC)、序贯相似性检测算法(SSDA)、hadamard变换算法(SATD)。

而我这篇博文主要即NCC算法,为主作计算视差图的实验。

二、立体重建之计算视差图

2.1归一化及算法概念

在该立体重建算法中,我们将对于每个像素尝试不同的偏移,并按照局部图像周围归一化的互相关值,选择具有最好分数的偏移,后记录下该最佳偏移。因为每个偏移在某种程度上对应于一个平面,所以该过程有时称为扫平面法。虽然该方法并不是立体重建中最好的方法,但是非常简单,通常会得出令人满意的结果。当密集地应用在图像中时,归一化的互相关值可以很快地计算出来。
归一化积相关算法(Normalized Cross Correlation,简称NCC算法),与上面算法相似,依然是利用子图与模板图的灰度,通过归一化的相关性度量公式来计算二者之间的匹配程度。
这和我们在稀疏点对应的不同。我们使用每个像素周围的图像块(根本上说,是局部周边图像)来计算归一化的互相关。对于这里的情形,我们可以在像素周围重新写出公式中的 NCC,如下所示:
在这里插入图片描述
对于原始的图像内任意一个像素点(px,py) 构建一个n×n 的邻域作为匹配窗口。然后对于目标相素位置(px+d,py) 同样构建一个n×n大小的匹配窗口,对两个窗口进行相似度度量,注意这里的d有一个取值范围。对于两幅图像来说,在进行NCC 计算之前要对图像处理,也就是将两帧图像校正到水平位置,即光心处于同一水平线上,此时极线是水平的,否则匹配过程只能在倾斜的极线方向上完成,这将消耗更多的计算资源。
其中NCC(I1,I2)得到的值范围将在[-1,1]之间。
I1(x)为原始图像的像素值
μ为原始窗口内像素的均值。
I2为原始图像在目标图像上对应点位置在x方向上偏移d后的像素值。
μ2为目标图像匹配窗口像素均值。
若NCC=-1,则表示两个匹配窗口完全不相关,相反,若NCC=1时,表示两个匹配窗口相关。

2.2匹配流程

  1. 采集图像:通过标定好的双目相机采集图像,当然也可以用两个单目相机来组合成双目相机。
  2. 极线校正:校正的目的是使两帧图像极线处于水平方向,或者说是使两帧图像的光心处于同一水平线上。通过校正极线可以方便后续的NCC操作。 ①由标定得到的内参中畸变信息中可以对图像去除畸变。②通过校正函数校正以后得到相机的矫正变换R和新的投影矩阵P,接下来是要对左右视图进行去畸变,并得到重映射矩阵。
  3. 特征匹配:这里便是我们利用NCC做匹配的步骤,匹配方法如上所述,右视图中与左视图待测像素同一水平线上相关性最高的即为最优匹配。完成匹配后,我们需要记录其视差d,即待测像素水平方向xl与匹配像素水平方向xr之间的差值d=xr−xl,最终我们可以得到一个与原始图像尺寸相同的视差图D。
  4. 深度恢复:通过上述匹配结果得到的视差图D DD,我们可以很简单的利用相似三角形反推出以左视图为参考系的深度图。

三、实验测试

3.1实验要求

  1. 从理论角度,分析以窗口代价计算视差的原理
  2. 实现NCC 视差匹配方法,即给定左右两张视图,根据NCC计算视差图
  3. 分析不同窗口值对匹配结果的影响,重点考查那些点(或者哪些类型的点)在不同窗口大小下的匹配精度影响。

3.2实验代码

# -*- 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
from PIL import Image
import imageio

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:/thirddown/computervision/data/visionde/img01.jpg').convert('L'), 'f')
im_r = array(Image.open(r'E:/thirddown/computervision/data/visionde/img02.jpg').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 9
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)

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


3.3实验结果分析

测试图片的大小会影响图片的精度和代码的运行,不应该过大或过小。
测试图片为:
python计算机视觉编程——立体图像之计算视差图_第2张图片python计算机视觉编程——立体图像之计算视差图_第3张图片

分别测试窗口值为5,9,12,15的情况:

python计算机视觉编程——立体图像之计算视差图_第4张图片

python计算机视觉编程——立体图像之计算视差图_第5张图片
python计算机视觉编程——立体图像之计算视差图_第6张图片
python计算机视觉编程——立体图像之计算视差图_第7张图片

根据测试情况,标准情况下的NCC用的是窗口数为9的,从上面实验结果的对比来看,窗口数越小噪声越多,窗口数越大,噪声越小,但是会丢失很多细节
下面用高斯模糊方法再测试一次:
其中标准情况下高斯法的窗口数设定为3,我测试了2,3,5的情况:
python计算机视觉编程——立体图像之计算视差图_第8张图片
python计算机视觉编程——立体图像之计算视差图_第9张图片
python计算机视觉编程——立体图像之计算视差图_第10张图片

过度高斯模糊后整体细节基本消失。高斯模糊用于去噪声,所以去过噪声的图片窗口值不易设置过大。但是可以看出经过标准窗口值高斯模糊的图片产生的效果的会比标准ncc的方法结果更能勾勒出我们想要的结果图。

3.4实验总结和遇到的问题

总结:
调整窗口值的确会对生成的视差图有影响窗口值越大,噪声越少,细节模糊,反之。所以窗口值应该适量。
必须使用在同一极线上的测试用例,人工拍摄没办法同一。导致我自己拍摄的图片无法达到测试标准。
选取测试图片时,跨度不能太大,否则得不到好效果。
测试过程中发现图片的亮度越大,效果越好,但是也要适度。

由于拍摄图片的问题,曝光参数不相同,在对应像素点的特征点可能不一定对应,即产生错配。 所以根据实验结果(白色的地方表示在场景前方,越黑色表示越远。)还是和实际情况有所区别。
问题①:
scipy.misc.imread()被弃用,应该用imageio.imwrite()来替代
原书: scipy.misc.imsave() 等方法在scipy库更新时被弃用,要用其他方法保存视差图。
解决方法:安装 imageio 库
imageio.imwrite('imageio.png',res)

你可能感兴趣的:(python计算机视觉编程——立体图像之计算视差图)