一、立體視覺簡介
1、立體視覺的研究背景及意義
立體視覺是計算機視覺領域的一個重要課題,它的目的在於重構場景的三維幾何信息。立體視覺的研究具有重要的應用價值,其應用包括航空及遙感測量,工業自動化系統等。
立體視覺的研究方法之一,利用多幅圖象來恢復三維信息的方法,它是被動方式的。根據圖象獲取方式的區別又可以划分成普通立體視覺和通常所稱的光流(optical flow)兩大類。普通立體視覺研究的是由兩攝像機同時拍攝下的兩幅圖象,而光流法中研究的是單個攝像機沿任一軌道運動時順序拍下的兩幅或更多幅圖象。前者可以看作后者的一個特例,它們具有相同的幾何構形,研究方法具有共同點。雙目立體視覺是它的一個特例。
2、立體匹配算法的基本實現思想
該算法是基於灰度的匹配算法,這是一種區域相關方法, 在一幅圖象中以一點為中心選定一區域(窗口),在另一幅圖象中尋找與該區域相關系數最大的區域,把該找到的區域的中心認為是原來那區域中心的對應點。它對噪聲很敏感,所以需要搭配去噪濾波使用。
二、視差圖計算
1、什么是視差圖
視差圖是以圖像對中任一幅圖像為基准,其大小為該基准圖像的大小,元素值為視差值的圖像。由於視差圖包含了場景的距離信息,因此從立體圖像對中提取視差圖的圖像匹配,一直是雙目視覺研究中最為活躍的領域,值得我們去深度的學習。
2、NCC方法計算視差圖
NCC是歸一化相關性(normalization cross-correlation)的簡稱,NCC,就是用於歸一化待匹配目標之間的相關程度,比較的是原始像素。通過在待匹配像素位置p(px,py)構建匹配窗口,與目標像素位置p'(px+d,py)同樣構建鄰域匹配窗口的方式建立目標函數來對匹配窗口進行度量相關性,注意這里構建相關窗口的前提是兩幀圖像之間已經校正到水平位置,即光心處於同一水平線上,此時極線是水平的,否則匹配過程只能在傾斜的極線方向上完成。度量方式由如下式子定義:
其中p點表示圖像I1待匹配像素坐標(px,py),d表示在圖像I2被查詢像素位置在水平方向上與px的距離。左邊為圖像I1,右邊為圖像I2。左視圖和右視圖兩幅圖像大小相同,只有水平方向上的視角變換。
三、實驗環境
Python + PyCharm
四、實驗數據
圖片下載地址為:http://vision.middlebury.edu/stereo/data/scenes2003/
五、實驗步驟與結果展示
5.1 當窗口大小為3時,視差圖如下:
分析:匹配窗口中出現很多不連續的點,圖像邊緣特征也很模糊。
5.2 當窗口大小為5時,視差圖如下:
5.3 當窗口大小為7時,視差圖如下:
5.4 當窗口大小為11時,視差圖如下:
- 分析總結:從以上實驗結果可以看出,當窗口值為3時視差匹配結果很模糊,基本看不清;把窗口值大小增大到5后,運行的匹配結果圖片稍微清晰了一點,但仍無法看出圖中是何物品,當真大窗口值后,可以很明顯地看出,運行結果中的圖片越來越靠近實物圖像。所以,窗口值大小越大,視差圖更清晰。但窗口值也不是越大運行結果就越清晰,當窗口值過大時,圖片噪聲也增多,從而使圖片與實物圖片相比更為雜亂,運行耗時越長。
六、實驗總結
從以上對im2和im3的計算結果可以看出窗口值過小時,在低紋理區域出現誤匹配,匹配精度較低,視差圖特別模糊;隨着窗口值增大,匹配區分度逐漸清晰,誤匹配區域得到矯正,匹配精度隨着窗口值增大而變高。但當窗口值過大時,在深度區域容易出現誤匹配。窗口值過小時,匹配代價區分度過低,在低紋理區域容易出現誤匹配,匹配精度較低;窗口值的大小應適中,不宜過大也不宜過小。隨着窗口值增大,匹配區分度逐漸清晰,誤匹配區域得到矯正,匹配精度隨着窗口值增大而變高。
七、實驗代碼展示
# -*- 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:\Users\Administrator\Desktop\im2.png').convert('L'), 'f') im_r = array(Image.open(r'C:\Users\Administrator\Desktop\im3.png').convert('L'),'f') # 開始偏移,並設置步長 steps = 40 start = 4 # ncc 的寬度 wid = 9 res = plane_sweep_ncc(im_l,im_r,start,steps,wid) import scipy.misc scipy.misc.imsave('depth.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'C:\image\im3.png').convert('L'), 'f') im_r = array(Image.open(r'C:\image\im4.png').convert('L'),'f') # 開始偏移,並設置步長 steps = 40 start = 4 # ncc 的寬度 wid = 9 res = plane_sweep_ncc(im_l,im_r,start,steps,wid) import scipy.misc scipy.misc.imsave('depth.png',res) show()