目录
一、视差计算原理
1.1 NCC视差匹配方法
1.2 双目立体匹配
二、实验内容
2.1 不同窗口对匹配结果的影响
2.2 分析
三、实验代码
四、总结
归一化相关性(normalization cross-correlation),简称NCC。其是对图像内的像素点来构建一个nn的邻域作为匹配窗口,然后对目标像素位置同样的构建一个nn大小的匹配窗口,对两个窗口进行相似度度量。对于两幅图像来说,在进行NCC计算之前要对图像进行处理,也就是讲两帧图像进行极线校正,使两帧图像的光心处于同一水平线上。通过校正极线可以方便NCC操作。
NCC计算公式如下:
为匹配窗口;
为原始图像的像素值;
I1(px,py)为原始窗口内像素的均值,为原始图像在目标图像上对应点位置在x方向上偏移d之后的像素值,I2(px+d,py)为目标图像匹配窗口像素均值。
公式通过归一化将匹配结果限制在 [-1,1]的范围内,可以非常方便得到判断匹配窗口相关程度:当NCC= -1,表示两个匹配窗口完全不相关,相反的当NCC=1时,表示两个匹配窗口相关程度非常高。
视差:左右双目图像中,两个匹配块中心像素的水平距离。
双目立体匹配流程如下:
1. 采集图像:通过标定好的双目相机采集图像,当然也可以用两个单目相机来组合成双目相机。(标定方法下次再说)
2. 极线校正:校正的目的是使两帧图像极线处于水平方向,或者说是使两帧图像的光心处于同一水平线上。通过校正极线可以方便后续的NCC操作。
3. 特征匹配:这里便是我们利用NCC做匹配的步骤啦,匹配方法如上所述,右视图中与左视图待测像素同一水平线上相关性最高的即为最优匹配。完成匹配后,我们需要记录其视差d,即待测像素水平方向xl与匹配像素水平方向xr之间的差值d = xr - xl,最终我们可以得到一个与原始图像尺寸相同的视差图D。
4. 深度恢复:通过上述匹配结果得到的视差图D,我们可以很简单的利用相似三角形反推出以左视图为参考系的深度图。计算原理如下图所示:
如上图,Tx为双目相机基线,f为相机焦距,这些可以通过相机标定步骤得到。而xr - xl就是视差d。
通过公式 z = f * Tx / d可以很简单地得到以左视图为参考系的深度图了。
左右两张图像:
wid=3
wid=6
wid=9
wid=12
从图上不同的wid值,总体看的话窗口值取值很小的时候杂点很多,窗口值过大的时候图像中物体的轮廓特点又被隐去了,不能突出。我们从每张图片右下角的区域分析,在wid=3的时候,很多噪点以至于物体的形状都不能够看清,轮廓很细,在wid=12的时候看到右下角区域轮廓很泛,基本看不出原来的样子,视差图效果也很差。同时每个实验结果我们都能够发现越是靠前的位置图像越亮,即靠近相机的地方越明亮。
# -*- 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
import scipy.misc
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'C:/Users/asus/Pictures/window/shicha/conesF/im4.ppm').convert('L'), 'f')
im_r = array(Image.open(r'C:/Users/asus/Pictures/window/shicha/conesF/im5.ppm').convert('L'), 'f')
# imshow(im_l)
# imshow(im_r)
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 12
res = plane_sweep_ncc(im_l, im_r, start, steps, wid)
# scipy.misc.imsave('depth.png', res)
imageio.imsave('C:/Users/asus/Pictures/window/shicha/conesF/depth1.png', res)
imshow(res)
show()
实验中不同的窗口对匹配结果有一定的影响,窗口设置较大的时候,图像变得比较的糊,不能够清晰的看到细节;但是窗口设置过小的话会产生很多其他的点,对于图像中物体的体现有一定的干扰。所以经过多次窗口值的比较找到合适的值。
问题:实验准备的时候下载过来的图片很大,每次运行就很卡,一直与电脑自己就关掉很多东西都没有了。后面去下载了比较小的图片运行就不卡了,随便改变像素的大小的话是有影响的,所以可以下载图片相对小一点的。
参考:https://www.cnblogs.com/yepeichu/p/7354083.html