计算视觉——NCC视差匹配

计算视觉——NCC视差匹配

  • 一、立体匹配
    • 1.1 原理
    • 1.2 步骤
  • 二、NCC视差匹配
    • 2.1 原理
    • 2.2 计算公式
  • 三、实验过程
    • 3.1 实验准备
    • 3.2 实验代码
    • 3.3 实验结果
  • 四、总结

一、立体匹配

1.1 原理

找出两张图像的对应关系,根据三角测量原理,得到视差图;在获得了视差信息后,根据投影模型可以得到原始图像的深度信息和三维信息。

1.2 步骤

  • 匹配代价计算: 一般是通过计算左右两图对应像素3个通道的灰度值差来决定匹配代价的,常用的就是基于像素点匹配代价计算,一般有AD, SD,TAD,基于区域的匹配代价计算一般有SAD,SSD, STAD。匹配代价计算会生成一个disparity space image,也就是DSI。这个DSI是一个三维的空间,也就是每一个视差,得到一张代价图。假如视差范围是0~16,则会得到17幅代价图。视差搜索范围就是MiddleBurry网站上的stereo pair值,也就是说在视差范围内(比如0-16)搜索匹配代价,得到17张匹配代价图,然后找到匹配代价最小的对应的视差值就是此像素对应的视差。

  • **代价聚合:**其实就是一个滤波的过程,对每一幅代价图进行聚合,最简单的就是采用boxfilter。第一步代价计算只是得到了图像上所有孤立像素的视差值,但是这些时差值都是孤立的,引入了过多噪声,比如一片区域的视差值都是10,可是引入噪声后就会导致这一片的视差值都不一样,那么就需要一个滤波的过程,也就是我们所说的局部立体匹配方法,即采用窗口卷积达到局部滤波的目的。

  • 计算视差:常用的方法就是WTA算法(局部),对于图像中的同一个点,选出17幅代价图中匹配代价最小的那张图,该幅图对应的视差值就选取为最终的视差。或者在全局立体匹配中采用能量函数的方法,分为数据项和平滑项,数据项其实就是代价计算,平滑项就是代价聚合,只不过窗口大小是整幅图像,也可以试试如果把平滑项前面的系数lamda设为0,那么得到的结果和单纯代价计算的局部立体匹配是一样的。

  • 视差精化:对得到的视差进行优化的过程,如:左右一致性检测、区域投票等;这步就是做如遮挡处理,中值滤波,左右一致性检测等,都能使最后的是视差图提升1%左右,立体匹配最关键的步骤仍然是代价计算和代价聚合步骤。

二、NCC视差匹配

2.1 原理

对于原始的图像内任意一个像素点 ( p x , p y ) ( p x , p y ) ( p x ​ , p y ) (p_x,p_y) (p_x,p_y)(p_x​ ,p_y) (px,py)(px,py)(px,py)构建一个 n × n n×n n×n的邻域作为匹配窗口。然后对于目标相素位置 ( p x + d , p y ) ( p x + d , p y ) ( p x + d , p y ) (p_x+d,p_y) (p_x+d, p_y)(p_x+d,p_y) (px+d,py)(px+d,py)(px+d,py)同样构建一个 n × n n×n n×n大小的匹配窗口,对两个窗口进行相似度度量,注意这里的 d d d有一个取值范围。对于两幅图像来说,在进行 N C C NCC NCC计算之前要对图像处理,也就是将两帧图像校正到水平位置,即光心处于同一水平线上,此时极线是水平的,否则匹配过程只能在倾斜的极线方向上完成,这将消耗更多的计算资源。

2.2 计算公式

计算视觉——NCC视差匹配_第1张图片

  • 其中NCC(P, d)得到的值得范围将在[-1, 1]之间。
  • Wp为之前提到的匹配窗口。
  • I 1 ( x , y ) I_1(x, y) I1(x,y)为原始图像的像素值。
  • I 1 ( P x , P y ) I_1(P_x, P_y) I1(Px,Py)为原始窗口内像素的均值。
  • I 2 ( x + d , y ) I_2(x + d, y) I2(x+d,y)为原始图像在目标图像上对应点位置在方向上偏移d后的像素值。
  • I 2 ( P x + d , P y ) I_2(P_x + d, P_y) I2(Px+d,Py)为目标图像匹配窗口像素均值。
  • 若NCC= -1,则表示两个匹配窗口完全不相关,相反,若NCC= 1时,表示两个匹配窗口相关程度非常高。

三、实验过程

3.1 实验准备

  1. 实验环境 python2.7
  2. 实验数据:(图片下载地址: http://vision.middlebury.edu/stereo/data/)
    计算视觉——NCC视差匹配_第2张图片

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
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'D:\ComputerVision_code\img6\left.png').convert('L'), 'f')
im_r = array(Image.open(r'D:\ComputerVision_code\img6\right.png').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 15
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
scipy.misc.imsave('depth1.png',res)
show()

# -*- 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'D:\ComputerVision_code\img6\left.png').convert('L'), 'f')
im_r = array(Image.open(r'D:\ComputerVision_code\img6\right.png').convert('L'),'f')
# 开始偏移,并设置步长
steps = 12
start = 4
# ncc 的宽度
wid = 15
res = plane_sweep_ncc(im_l,im_r,start,steps,wid)
import scipy.misc
from numpy.core.numeric import zeros
from numpy.core.multiarray import zeros
scipy.misc.imsave('depth1.png',res)
show()

3.3 实验结果

  1. wid = 2 计算视觉——NCC视差匹配_第3张图片
    当窗口大小为2*2时,匹配结果图中噪点很多,颗粒状明显,模糊,轮廓近似于原图,但不易于分辨。
  2. wid = 6 计算视觉——NCC视差匹配_第4张图片
    当窗口大小增加为66时,匹配结果相比22的窗口效果要好些,噪声点基本没有了,图像层次感明显,成像线条也会缓和很多,但不贴近于原图中物体的轮廓。
  3. wid = 10 计算视觉——NCC视差匹配_第5张图片
    当窗口大小增加到10*10时,匹配结果图轮廓鲜明清晰,对比度更强,所有轮廓都能与原图中的物体相对应。

四、总结

wid值大小的设置会影响NCC视差匹配结果,wid值越小,噪点较多,成像模糊,但是物体的轮廓较准确;随着wid值的增大,噪声点明显减少,对比度逐渐提高,成像较为清晰,但是物体轮廓会出现一些弯曲的现象,再使用NCC算法时应适当取wid值。

参考链接:
[1] https://blog.csdn.net/u010368556/article/details/72823875

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