Python圖像拼接:建立全景圖
- 算法原理
-
- 基礎流程
- 幾何原理
- 核心步驟(RANSAC算法)
- 源代碼
- 結果分析
-
- 綜述
- 夜景(中華城)
- 室内(引桐樓)
- 多建築(鹭江道周邊)
- 單調場景(海灘)
- 複雜場景(集大校園)
算法原理
基礎流程
1.針對某個場景拍攝多張/序列圖像
2.計算第二張圖像與第一張圖像之間的變換關系
3.将第二張圖像疊加到第一張圖像的坐标系中
4.變換後的融合/合成
5.在多圖場景中,重複上述過程
首先提取一張圖檔的特征點,生成對應描述子

接着進行特征比對
同時,将圖像變換到目标坐标系,計算變換結構,進行圖像融合
幾何原理
全景融合相當于将圖像投影到共同的拼接平面上(同一坐标系)
,接着在拼接平面上實作全景融合
在拼接的應用中,其實可以簡化了解為 2D圖像的變換,疊加過程
核心步驟(RANSAC算法)
圖像拼接的核心步驟在于計算圖檔間的變換結構,即如何實作圖像的映射變換,我們通過RANSAC算法實作。
PANSAC是“RANdom SAmple Consensus”(随機一緻性采樣)的縮寫。該方法是用來找到正确模型來拟合帶有噪聲資料的疊代方法。給定一個模型,例如點集之間的單應性矩陣,RANSAC基本的思想是,資料中包含着正确的點和噪聲點,合理的模型應該能夠在描述正确資料點的同時摒棄噪聲點。其大緻過程如下:
源代碼
from pylab import *
from numpy import *
from PIL import Image
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift
# 将比對轉換成齊次坐标點的函數
def convert_points(j):
ndx = matches[j].nonzero()[0]
fp = homography.make_homog(l[j + 1][ndx, :2].T)
ndx2 = [int(matches[j][i]) for i in ndx]
tp = homography.make_homog(l[j][ndx2, :2].T)
fp = vstack([fp[1], fp[0], fp[2]])
tp = vstack([tp[1], tp[0], tp[2]])
return fp, tp
if __name__=='__main__':
featname = ['img\\jmu0' + str(i + 1) + '.sift' for i in range(5)]
imname = ['img\\jmu0' + str(i + 1) + '.jpg' for i in range(5)]
im = [array(Image.open(imname[i]).convert('L')) for i in range(5)]
l = {}
d = {}
for i in range(5):
sift.process_image(imname[i], featname[i])
l[i], d[i] = sift.read_features_from_file(featname[i])
matches = {}
for i in range(4):
matches[i] = sift.match(d[i + 1], d[i])
model = homography.RansacModel()
fp, tp = convert_points(1)
H_12 = homography.H_from_ransac(fp, tp, model)[0]
fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0]
tp, fp = convert_points(2)
H_32 = homography.H_from_ransac(fp, tp, model)[0]
tp, fp = convert_points(3)
H_43 = homography.H_from_ransac(fp, tp, model)[0]
delta = 1000
im1 = array(Image.open(imname[1]), "uint8")
im2 = array(Image.open(imname[2]), "uint8")
im_12 = warp.panorama(H_12,im1,im2,delta,delta)
im1 = array(Image.open(imname[0]), "f")
im_02 = warp.panorama(dot(H_12,H_01),im1,im_12,delta,delta)
im1 = array(Image.open(imname[3]), "f")
im_32 = warp.panorama(H_32,im1,im_02,delta,delta)
im1 = array(Image.open(imname[4]), "f")
im_42 = warp.panorama(dot(H_32,H_43),im1,im_32,delta,2*delta)
figure()
imshow(array(im_42, "uint8"))
axis('off')
show()
結果分析
綜述
通過對結過分析,得出了如下圖所示問題,其中 “√” 表示效果相對較好。從中我們可以得出圖檔資料越多與場景月複雜則越容易出現錯配,以一端為基準進行拼接容易産生圖像扭曲,且圖檔資料越多越容易出現,具體各情況間下文分析。
夜景(中華城)
首先,對于夜景,以中華城為例,圖檔資料如下
結果如下,從結果中我們可以看出在黑夜環境下背景與建築對比度高,能夠很好進行特征比對,但是最終的拼接結果還是稍有瑕疵。如下圖中的警示杆沒能夠很好的進行拼接,個人認為這是由于兩張圖檔的亮度相差較大,雖然sift特征比對能夠一定程度上對亮度有差異的圖像特征進行比對,但不能夠實作完全比對,在該情況中由于部分特征點的錯配而導緻了拼接錯位。
同時結果中也可以看出有很明顯的拼接縫隙,如下圖,可以很明顯的看到兩側圖檔的明暗對比,這是由于兩張圖檔本就存在亮度差距,而該算法并沒有實作對圖檔灰階值的優化,為了解決該問題可以在拼接後對圖檔全局進行均衡化以均衡明暗程度
室内(引桐樓)
室内拼接狀況以引桐樓宿舍為例,資料如下
該組資料不為同一平面拍攝,更真實的還原了全景圖像的拍攝。但其效果如圖所示,圖像的扭曲過于嚴重,拼接效果極差。這是由于該算法僅能對二維平面實作很好的特征比對,而對于不在同一平面的三維場景
無法進行高準确度的比對,是以出現了圖檔的過渡扭曲與拼接錯位。
多建築(鹭江道周邊)
對于多建築的戶外場景,我們以鹭江道周邊為例,圖檔資料如下圖:
該拼接結果同樣很明顯的可以看出拼接縫隙即左右的圖檔明暗對比。但在一些細節之處,如圖中國際銀行大廈頂上的一片雲彩都進行了很好的拼接。而若要解決圖檔明暗不均問題還是需要上文所提到進行均衡化。
對于同樣多建築的情景,通過複現書本中的代碼與圖檔資料,可以發現在位于邊緣區域的圖檔在拼接時出現“拉伸”的情況,這是由于圖檔的尺度不同而導緻,在與周圍圖檔進行拼接時對圖檔進行的一定程度的放縮。對于該問題可以使用平差法進行優化,将每張圖檔比對後的誤差總和計算均值,在均分到每一張圖檔,這樣能夠達到更優的圖檔拼接效果。
單調場景(海灘)
對于單調場景,我們取白城沙灘一隅為例,圖檔資料如下圖:
在對于這種單調且景觀及其相似的場景,尤其考研算法的精度,從以下結果中我們也可以看出出現了不少圖檔的錯配。
在這種景物高度相似的場景下特征不易進行比對,且容易出現錯配,是以需要優化特征點的比對算法才能夠更好的解決該問題
複雜場景(集大校園)
對于複雜場景即内容豐富的場景,以集大校園為例,圖檔資料如下圖:
在該情景下由于景物豐富,我們的算法可以找到大量的比對特征點,充分利用了場景中的各個景物,是以也能夠得到極優的圖像拼接結果。但不容忽視的是對于旋轉扭曲的圖檔該算法還是出現了拼接錯誤。
如下圖所示,圖檔間雖然有很多特征點能夠進行比對但是在拼接過程中卻出現了問題,個人認為這是由于圖檔中錯配的特征點太多,導緻取均值後出現誤差過大而導緻,若想解決該問題則需要運用更加精确的特征比對算法或是對特征比對的結果進行優化,出去錯配特征。