如今考慮一個全景圖拼接的應用場景,假設現有兩張圖片須要拼接成一張全景圖,這兩張圖片是經過相機右轉必定角度拍攝出來的,兩張圖片有部分取景是重疊的。如何實現拼接?固然這是一個不簡單的問題,咱們如今只考慮實現拼接目標的第一步:找出圖像中重疊的內容,以及分別在兩張圖片中的位置。python
以下圖所示,左右是兩張稍有不一樣的圖片,但都包含了廣州塔,左圖紅色框中標出了兩個感興趣的點,我指望找出它們在右圖的對應位置(即對應點)。算法
首先,要肯定檢測哪些點,即哪些點是咱們感興趣的?這可使用Harris角檢測(見上篇筆記)方法來獲得圖像的角點集合,而後經過設置合適的閾值和座標範圍來找出咱們感興趣的點。有了兩個圖像的興趣點集後,又如何能計算出它們的對應關係呢?這就須要解決兩個問題:segmentfault
興趣點如何描述app
興趣點之間的對應關係如何計算ide
興趣點,也即用Harris角檢測出來的結果,它只有座標和像素值,只有這些信息不足以用於匹配,沒法從另外一張圖像中查找是否包含這個點。因此須要增長點的表徵信息,一種方法是使用圍繞點周圍一小塊的圖像來描述這個點,如採用上圖中所標記的方式,即:以興趣點爲中心劃出一個小矩形,將區域內全部像素值以一貫量進行存儲,用這個向量來描述這個興趣點,那麼此向量稱爲興趣點描述符(interest point descriptor,下簡稱IPD)。函數
下面實現一個函數,爲全部角點生成IPD:spa
def get_desc(image, filtered_coords, wid = 5): #image爲原圖像,filtered_coords爲角點的座標,wid爲矩形的「半徑」 desc = [] for coords in filtered_coords: ipd = image[coords[0] - wid : coords[0] + wid + 1, coords[1] - wid : coords[1] + wid + 1].flatten() if ipd.shape[0] > 0: desc.append(ipd) return desc
如何肯定左圖中的某個興趣點,對應右圖中的某個興趣點?對應關係,不必定徹底是等價關係,即兩個點雖然是對應關係,但它們對應的IPD並不徹底相同。由於咱們這裏討論的找對應點的方法,容許兩張圖像在亮度、縮放上有必定的區別。因此兩個點的對應關係不能用IPD等價來匹配,而是要採用類似度或相關度來計算,相關度越高,它們越多是對應關係。而相關度,可使用現成的數學模型——皮爾遜相關係數(Pearson's r,也被稱爲皮爾森相關係數r,下簡稱r係數)來表示。因此,計算兩個點的對應關係就轉化爲計算兩個IPD的r係數。rest
r係數被普遍用於度量兩個變量之間的相關(線性相關)程度,它是兩個變量之間的協方差和標準差的商,一種等價表達式爲標準分的均值:
r = code
I1和I2爲樣本集,μ1爲I1的平均值,μ2爲I2的平均值,σ1爲I1的標準差,σ2爲I2的標準差,上式計算結果即爲r係數,範圍爲-1到1。 r係數爲正且越大,表示I1和I2同時趨向於它們各自的平均值,變化方向一致,相關度越高。係數爲0意味着兩個變量之間沒有線性關係。orm
把兩個點對應的IPD代入上述公式的I,可獲得兩個點的相關程度。因此找兩個圖像之間興趣點的對應關係,計算步驟是:
分別對兩個圖像應用Harris角檢測,獲得圖像1的興趣點集1,和圖像2的興趣點集2
設定IPD的矩形大小,計算全部興趣點的IPD,獲得IPD_SET_1和IPD_SET_2兩個集合
設定r係數的閾值,如0.5,即相關度在[0.5,1]之間咱們才考慮,那麼,對IPD_SET_1中指定的某個IPD,計算它與IPD_SET_2中全部IPD的r係數,若最大的r係數落在[0.5,1]區間,則其對應的IPD是最相關的。
下面實現一個IPD匹配函數,傳入兩個IPD集合,找出全部r係數符合給定閾值的(即認爲有對應關係的)IPD:
def match(desc1, desc2, threshold = 0.5): n = len(desc1[0]) count1 = len(desc1) count2 = len(desc2) d = -np.ones((count1, count2)) #每一個圖1的IPD,其對應的力2的IPD下標初始化爲-1 for i in range(count1): ipd1 = desc1[i] d1 = (ipd1 - np.mean(ipd1)) / np.std(ipd1) for j in range(count2): ipd2 = desc2[j] if ipd1.shape[0] == ipd2.shape[0]: #忽略位於邊緣的IPD d2 = (ipd2 - np.mean(ipd2)) / np.std(ipd2) r = np.sum(d1 * d2) / (n - 1) if r > threshold: d[i, j] = r #i爲圖像1角點座標, j爲符合閾值的圖像2角點座標 ndx = np.argsort(-d) #將d的列降序排列,第0列即爲r係數最大的 match_index_array = ndx[:, 0] #只保留第0列 return match_index_array
上述的函數爲圖1的每一個IPD,從右邊找到最佳的匹配(若是存在),但這還不夠,由於這不表明對右邊的這個IPD來講,左邊的的這個IPD是它的最佳匹配,因此,若是使用兩向匹配,互相認爲是最佳的,咱們才認爲是對應關係,這樣效果會更好一些,雙向匹配的函數實現:
def match_twosided(desc1, desc2, threshold = 0.5): m_12 = match(desc1, desc2, threshold) m_21 = match(desc2, desc1, threshold) for i,j in enumerate(m_12): if j >= 0 and m_21[j] != i: m_12[i] = -1 #非雙向匹配的,置爲-1,上層應該忽略之 return m_12
下面代碼使用以上的兩向匹配方法找出兩張圖像的對應點,並用白色線鏈接起來,看一下效果,兩張圖像是並排顯示的:
從圖中能夠看出,兩個圖像中的廣州塔上的關鍵角點基本能找到對應的位置,但圖像的底部即建築物的角點,與右圖的建築物鏈接起來,即便它們不是相同的建築物,這是由於這些角點看起來很像,準確點講,相關度(r係數)很高,因此被認爲是對應點。
從這個例子也能夠看出,要準確的找到對象在圖像間的對應點,還須要考慮一些因素,來使效果更佳:
爲興趣點定義一個範圍,好比上面例子,若是隻關注塔尖的興趣點,得出的效果使人滿意
在尋找對應關係時,可限定對應點的y座標的距離不能超過必定範圍(如50個像素,根據實際應用而定),這樣能夠有效排除一些雖然r係數高,但事實上不對應的點。
代碼以下,注意點的疏密能夠經過參數微調:
from PIL import Image import matplotlib.pyplot as plt import numpy as np from scipy.ndimage import filters from skimage.feature import corner_peaks def harris_eps(im, sigma=3): #harris角檢測,見上個筆記 imx = np.zeros(im.shape) filters.gaussian_filter(im, (sigma,sigma), (0,1), imx) imy = np.zeros(im.shape) filters.gaussian_filter(im, (sigma,sigma), (1,0), imy) Wxx = filters.gaussian_filter(imx*imx,sigma) Wxy = filters.gaussian_filter(imx*imy,sigma) Wyy = filters.gaussian_filter(imy*imy,sigma) Wdet = Wxx*Wyy - Wxy**2 Wtr = Wxx + Wyy return Wdet * 2 / (Wtr + 1e-06) # def get_desc(image, filtered_coords, wid = 5): # 省略,見上文 # def match_twosided(desc1, desc2, threshold = 0.5): # 省略,見上文 im1 = np.array(Image.open('tower-left.jpg').convert('L')) im2 = np.array(Image.open('tower-right.jpg').convert('L')) coords_1 = corner_peaks(harris_eps(im1, sigma=1), min_distance=3, threshold_abs=0, threshold_rel=0.1) coords_2 = corner_peaks(harris_eps(im2, sigma=1), min_distance=3, threshold_abs=0, threshold_rel=0.1) desc1 = get_desc(im1, coords_1, wid=6) desc2 = get_desc(im2, coords_2, wid=6) match_index_array = match_twosided(desc1, desc2, threshold=0.5) im3 = np.concatenate((im1, im2), axis=1) #將兩個圖像左右合併成一個,以便顯示 plt.gray() plt.imshow(im3) for ipd_index_1,ipd_index_2 in enumerate(match_index_array): if ipd_index_2 != -1: x = [coords_1[ipd_index_1][4], coords_2[ipd_index_2][5] + im1.shape[1]] y = [coords_1[ipd_index_1][0], coords_2[ipd_index_2][0]] if np.abs(y[0] - y[1]) < 50: #這裏限制了對應點之間的y座標距離 plt.plot(x, y, 'w', alpha=0.5) #鏈接兩個對應點 plt.plot(coords_1[:, 1], coords_1[:, 0], '+r', markersize=5) #畫圖1角點座標 plt.plot(coords_2[:, 1] + im1.shape[1], coords_2[:, 0], '+r', markersize=5) #畫圖2角點座標 plt.axis('off') plt.show()
從實例中能夠看到,本文使用的描述點的和匹配的方法,存在誤配的狀況,矩形大小的設置也會影響匹配的結果,並且它也不適用於在圖像被旋轉和縮放的狀況下使用,近年,關於這方面的研究也在不斷取得進步,下一筆記將介紹一種稱爲尺度不變特徵轉換(Scale-invariant feature transform 或 SIFT)的算法,此算法應用很是廣。
你還能夠查看個人其它筆記