目錄
一、視差計算原理
1.1 NCC視差匹配方法
歸一化相關性(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 雙目立體匹配
雙目立體匹配流程如下:
1. 採集圖像:通過標定好的雙目相機採集圖像,當然也可以用兩個單目相機來組合成雙目相機。(標定方法下次再說)
2. 極線校正:校正的目的是使兩幀圖像極線處於水平方向,或者說是使兩幀圖像的光心處於同一水平線上。通過校正極線可以方便後續的NCC操作。
3. 特徵匹配:這裏便是我們利用NCC做匹配的步驟啦,匹配方法如上所述,右視圖中與左視圖待測像素同一水平線上相關性最高的即爲最優匹配。完成匹配後,我們需要記錄其視差d,即待測像素水平方向xl與匹配像素水平方向xr之間的差值d = xr - xl,最終我們可以得到一個與原始圖像尺寸相同的視差圖D。
4. 深度恢復:通過上述匹配結果得到的視差圖D,我們可以很簡單的利用相似三角形反推出以左視圖爲參考系的深度圖。計算原理如下圖所示:
如上圖,Tx爲雙目相機基線,f爲相機焦距,這些可以通過相機標定步驟得到。而xr - xl就是視差d。
通過公式 z = f * Tx / d可以很簡單地得到以左視圖爲參考系的深度圖了。
二、實驗內容
左右兩張圖像:
2.1 不同窗口對匹配結果的影響
wid=3
wid=6
wid=9
wid=12
2.2 分析
從圖上不同的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()
四、總結
實驗中不同的窗口對匹配結果有一定的影響,窗口設置較大的時候,圖像變得比較的糊,不能夠清晰的看到細節;但是窗口設置過小的話會產生很多其他的點,對於圖像中物體的體現有一定的干擾。所以經過多次窗口值的比較找到合適的值。
問題:實驗準備的時候下載過來的圖片很大,每次運行就很卡,一直與電腦自己就關掉很多東西都沒有了。後面去下載了比較小的圖片運行就不卡了,隨便改變像素的大小的話是有影響的,所以可以下載圖片相對小一點的。