python實現全景拼接

一、原理

1全景拼接

將SIFT應用到圖像拼接上,根據特徵點匹配的方式,則利用這些匹配的點來估算單應矩陣(使用上面的RANSAC算法,也就是把其中一張通過個關聯性和另一張匹配的方法。通過單應矩陣H,可以將原圖像中任意像素點座標轉換爲新座標點,轉換後的圖像即爲適合拼接的結果圖像。可以簡單分爲以下幾步:
1.根據給定圖像/集,實現特徵匹配。
2.通過匹配特徵計算圖像之間的變換結構。
3.利用圖像變換結構,實現圖像映射。
4.針對疊加後的圖像,採用APAP之類的算法,對齊特徵點。(圖像配準)
5.通過圖割方法,自動選取拼接縫。
6.根據multi-band blending策略實現融合。

1.1圖像配準

圖像配準是對圖像進行變換,使變換後的圖像能夠在常見的座標系中對齊。爲了能夠進行圖像對比和更精細的圖像分析,圖像配準是一步非常重要的操作。圖像配準的方法有很多,這裏以APAP算法爲例:
1.提取兩張圖片的sift特徵點
2.對兩張圖片的特徵點進行匹配
3.匹配後,仍有很多錯誤點,此時用RANSAC進行特徵點對的篩選。篩選後的特徵點基本能夠一一對應。
4.使用DLT算法,將剩下的特徵點對進行透視變換矩陣的估計。
5.因爲得到的透視變換矩陣是基於全局特徵點對進行的,即一個剛性的單應性矩陣完成配準。爲提高配準的精度,Apap將圖像切割成無數多個小方塊,對每個小方塊的變換矩陣逐一估計。
在這裏插入圖片描述

1.2圖割方法

最大流最小割算法原理,在下文博客中有很好的講解
https://blog.csdn.net/qq_28739605/article/details/80684704
1.最小割問題
一個有向圖,並有一個源頂點(source vertex)和目標頂點(target vertex).邊的權值爲正,又稱之爲容量(capacity)。如下圖
在這裏插入圖片描述
一個st-cut(簡稱割cut)會把有向圖的頂點分成兩個不相交的集合,其中s在一個集合中,t在另外一個集合中。
這個割的容量(capacity of the cut)就是A到B所有邊的容量和。注意這裏不包含B到A的。最小割問題就是要找到割容量最小的情況。

2.最大流問題
跟mincut問題類似,maxflow要處理的情況也是一個有向圖,並有一個原頂點(source vertex)和目標(target vertex),邊的權值爲正,又稱之爲容量(capacity)。
(1)初始化,所有邊的flow都初始化爲0。
(2)沿着增廣路徑增加flow。增廣路徑是一條從s到t的無向路徑,但也有些條件,可以經過沒有滿容量的前向路徑(s到t)或者是不爲空的反向路徑(t->s)。

1.3圖像融合

圖像拼接完成以後會發現在拼接的交界處有明顯的銜接痕跡,存在邊緣效應,所以需要特定的處理解決這種不自然。這時候可以採用blending方法。multi-band blending是目前圖像融和方面比較好的方法。

二、代碼

# -*- coding: utf-8 -*-
from pylab import *
from numpy import *
from PIL import Image

# If you have PCV installed, these imports should work
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift

np.seterr(invalid='ignore')
"""
This is the panorama example from section 3.3.
"""

# set paths to data folder
featname = ['C:\Python\Pictrue/apap/pic' + str(i + 1) + '.sift' for i in range(5)]  # 圖片路徑記得修改
imname = ['C:\Python\Pictrue/apap/pic' + str(i + 1) + '.jpg' for i in range(5)]

# extract features and match
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])

# visualize the matches (Figure 3-11 in the book)
for i in range(4):
    im1 = array(Image.open(imname[i]))
    im2 = array(Image.open(imname[i + 1]))
    figure()
    sift.plot_matches(im2, im1, l[i + 1], l[i], matches[i], show_below=True)


# function to convert the matches to hom. points
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)

    # switch x and y - TODO this should move elsewhere
    fp = vstack([fp[1], fp[0], fp[2]])
    tp = vstack([tp[1], tp[0], tp[2]])
    return fp, tp


# estimate the homographies
model = homography.RansacModel()

fp, tp = convert_points(1)
H_12 = homography.H_from_ransac(fp, tp, model)[0]  # im 1 to 2

fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0]  # im 0 to 1

tp, fp = convert_points(2)  # NB: reverse order
H_32 = homography.H_from_ransac(fp, tp, model)[0]  # im 3 to 2

tp, fp = convert_points(3)  # NB: reverse order
H_43 = homography.H_from_ransac(fp, tp, model)[0]  # im 4 to 3

# warp the images
delta = 1600  # for padding and translation

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_02, "uint8"))
axis('off')
savefig("example1.png", dpi=300)
show()

三、實驗結果及分析

1.固定點位拍攝多張圖片

圖片集:
在這裏插入圖片描述
運行結果:
在這裏插入圖片描述
在第一組場景中,因爲角度夠大(大約有160°),所以能將畫布鋪滿,並且五張圖片都成功拼接,由於拍攝是手持手機拍攝,沒有固定的高度,亮度也有些許不同,所以拼接時會有不可避免的高度差異和亮度差異,在該圖中存在三處明顯的拼接縫(圖中紅框所示),一處因爲拼接而丟失的部分(圖中藍框所示),還有兩處變形扭曲(圖中黃框所示),圖片拼接後出現扭曲是因爲APAP算法是對每個小格做單應性變換,從單個小格看是沒有特徵的,所以拼接起來會出現扭曲的現象,可以嘗試通過blending進行融合來達到更好的效果。

2.針對同一場景,更換拍攝位置

圖片集:
在這裏插入圖片描述
運行結果:
在這裏插入圖片描述
在第二組場景中採用更換拍攝位置獲取三張圖片進行拼接,近景花盆並沒有進行拼接而是隻取了第三幅圖的花盆,遠景部分有拼接但是沒有完整拼接(如圖中紅框所示),可能與拍攝手持有一定的關係,遠景也有一定的扭曲(如圖中黃框所示),更換拍攝位置以後會的效果一直都不太好,有試過很多組拍攝位置不同的圖片,這一組是因爲角度偏小可以得到拼接圖片,角度過大就會變成如下所示:
角度過大數據集:在這裏插入圖片描述
角度過大拼接結果:
在這裏插入圖片描述
角度過大時會按照遠景進行拼接而忽略了近景(如圖中紅框所示)。

四、遇到的問題及解決方法

1.圖像尺寸過大/圖像尺寸不一致

這兩個都會問題都會造成程序中斷運行,運用軟件將其調整爲大小一致即可,圖片我統一調整大小爲寬1000*長按比例縮放
在這裏插入圖片描述

2.runtimewarning:invalid value encountered in divide

使用阿Q方法,用以下代碼句忽略該錯誤,不會報錯就能繼續運行,但是得到的結果往往不會理想

np.seterr(invalid='ignore')

3.did not meet fit acceptance criteria

這個錯誤代表拼接失敗,只在同一場景,不同拍攝位置中出現該錯誤,重新選擇了好幾組圖片纔得到解決。

4.圖片編號問題

圖片應該按拍攝角度從右到左編號,這樣才能正確編譯,因爲匹配是從最右邊的圖片開始計算的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章