计算机视觉——NCC视差匹配方法

文章目录

  • 1.NCC匹配介绍
    • 1.1原理
    • 1.2匹配流程
  • 2.代码
  • 3.结果和分析
    • 3.1实验结果展示
    • 3.2小结
  • 4.遇到的问题及解决方法

1.NCC匹配介绍

1.1原理

对于原始的图像内任意一个像素点(px,py) 构建一个n×n的邻域作为匹配窗口。然后对于目标相素位置(px+d,py) 同样构建一个n×n大小的匹配窗口,对两个窗口进行相似度度量,注意这里的d有一个取值范围。对于两幅图像来说,在进行NCC计算之前要对图像处理,也就是将两帧图像校正到水平位置,即光心处于同一水平线上,此时极线是水平的,否则匹配过程只能在倾斜的极线方向上完成,这将消耗更多的计算资源。
NCC计算公式如下图所示:
计算机视觉——NCC视差匹配方法_第1张图片
其中NCC(p,d)得到的值得范围将在[−1,1]之间 。
Wp 为之前提到的匹配窗口。
I1 (x,y) 为原始图像的像素值。
I1(px,py) 为原始窗口内像素的均值。
I2(x+d,y) 为原始图像在目标图像上对应点位置在x方向上偏移d后的像素值。
I2(px+d,py) 为目标图像匹配窗口像素均值。

  • 若NCC=−1则表示两个匹配窗口完全不相关,相反,若NCC=1 时,表示两个匹配窗口相关程度非常高。

1.2匹配流程

1.采集图像:通过标定好的双目相机采集图像,当然也可以用两个单目相机来组合成双目相机。
2.极线校正:校正的目的是使两帧图像极线处于水平方向,或者说是使两帧图像的光心处于同一水平线上。通过校正极线可以方便后续的NCC操作。

  • 由标定得到的内参中畸变信息中可以对图像去除畸变。
  • 通过校正函数校正以后得到相机的矫正变换R和新的投影矩阵P,接下来是要对左右视图进行去畸变,并得到重映射矩阵。

3.特征匹配:这里便是我们利用NCC 做匹配的步骤啦,匹配方法如上所述,右视图中与左视图待测像素同一水平线上相关性最高的即为最优匹配。完成匹配后,我们需要记录其视差d,即待测像素水平方向xl与匹配像素水平方向xr之间的差值d=xr-xl,最终我们可以得到一个与原始图像尺寸相同的视差图D
4.深度恢复:通过上述匹配结果得到的视差图D,我们可以很简单的利用相似三角形反推出以左视图为参考系的深度图。
计算机视觉——NCC视差匹配方法_第2张图片

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
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:\Python\Pictrue\NCC/im2.ppm').convert('L'), 'f')
im_r = array(Image.open(r'C:\Python\Pictrue\NCC/im6.ppm').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 2
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
scipy.misc.imsave('depth.png',res)
show()

3.结果和分析

3.1实验结果展示

实验使用图片:
计算机视觉——NCC视差匹配方法_第3张图片

窗口值为5时:
计算机视觉——NCC视差匹配方法_第4张图片

窗口值为9时:计算机视觉——NCC视差匹配方法_第5张图片

窗口值为20时:
计算机视觉——NCC视差匹配方法_第6张图片

3.2小结

  • 通过对比窗口值为5,9和20时的视差图可以看出,在窗口值为9 时的匹配深度选取较好窗口值为5和20时略好,层次较为清晰,错配也较少。在窗口值为5时出现大幅度错误匹配。
  • 通过调节窗口大小和图片的大小,可以适当提高运行的速率,由原理可知,视差越大,在视差图中体现出来的越亮。再通过对视差图的对比可知,距离摄像机距离越近当窗口值增大时,去噪效果有显著的提升,但是细节处的深度处理会随着窗口值变大而逐渐被略去而使轮廓变得模糊。
  • 双目测距的精度是很难进行理论分析的,需要根据实际情况去验证。一般来说,距离越远,误差越大,因此双目测距不适宜测量太远的目标。如果想要对与较远的目标能够得到较为可靠的深度,需要提高相机的基线距离,但是同时会导致左右视图的差异变大从而提高立体匹配的难度。

4.遇到的问题及解决方法

4.1图片呈雪花状

计算机视觉——NCC视差匹配方法_第7张图片
解决方法:在试过了多组图片以后发现,图片在300多k的情况下就会呈雪花状,所以将图片压缩到了20多k就可以成功显示视差图了。

你可能感兴趣的:(计算机视觉——NCC视差匹配方法)