一、全景拼接的原理
1.RANSAC算法介紹
RANSAC算法的基本假設是樣本中包含正確數據(inliers,可以被模型描述的數據),也包含異常數據(outliers,偏離正常范圍很遠、無法適應數學模型的數據),即數據集中含有噪聲。這些異常數據可能是由於錯誤的測量、錯誤的假設、錯誤的計算等產生的。同時RANSAC也假設,給定一組正確的數據,存在可以計算出符合這些數據的模型參數的方法。
2.使用RANSAC算法來求解單應性矩陣
在進行圖像拼接時,我們首先要解決的是找到圖像之間的匹配的對應點。通常我們采用SIFT算法來實現特征點的自動匹配,SIFT算法的具體內容參照我的上一篇博客 。SIFT是具有很強穩健性的描述子,比起圖像塊相關的Harris角點,它能產生更少的錯誤的匹配,但仍然還是存在錯誤的對應點。所以需要用RANSAC算法,對SIFT算法產生的128維特征描述符進行剔除誤匹配點。
由直線的知識點可知,兩點可以確定一條直線,所以可以隨機的在數據點集中選擇兩點,從而確定一條直線。然后通過設置給定的閾值,計算在直線兩旁的符合閾值范圍的點,統計點的個數inliers。inliers最多的點集所在的直線,就是我們要選取的最佳直線。

RANSAC算法就是在一原理的基礎上,進行的改進,從而根據閾值,剔除錯誤的匹配點。首先,從已求得的匹配點對中抽取幾對匹配點,計算變換矩陣。然后對所有匹配點,計算映射誤差。接着根據誤差閾值,確定inliers。最后針對最大inliers集合,重新計算單應矩陣 H。
3.基本思想描述:
①考慮一個最小抽樣集的勢為n的模型(n為初始化模型參數所需的最小樣本數)和一個樣本集P,集合P的樣本數#(P)>n,從P中隨機抽取包含n個樣本的P的子集S初始化模型M;
②余集SC=P\S中與模型M的誤差小於某一設定閾值t的樣本集以及S構成S*。S*認為是內點集,它們構成S的一致集(Consensus Set);
③若#(S*)≥N,認為得到正確的模型參數,並利用集S*(內點inliers)采用最小二乘等方法重新計算新的模型M*;重新隨機抽取新的S,重復以上過程。
④在完成一定的抽樣次數后,若未找到一致集則算法失敗,否則選取抽樣后得到的最大一致集判斷內外點,算法結束。
4.圖像拼接
使用RANSAC算法估計出圖像間的單應性矩陣,將所有的圖像扭曲到一個公共的圖像平面上。通常,這里的公共平面為中心圖像平面。一種方法是創建一個很大的圖像,比如將圖像中全部填充0,使其和中心圖像平行,然后將所有的圖像扭曲到上面。由於我們所有的圖像是由照相機水平旋轉拍攝的,因此我們可以使用一個較簡單的步驟:將中心圖像左邊或者右邊的區域填充為0,以便為扭曲的圖像騰出空間。
二、實驗
1.源代碼

# -*- coding: utf-8 -*-
from pylab import *
from PIL import Image
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift
# set paths to data folder
featname = ['C:/Users/LE/PycharmProjects/untitled/hh/' + str(i + 1) + '.sift' for i in range(4)]
imname = ['C:/Users/LE/PycharmProjects/untitled/hh/' + str(i + 1) + '.jpg' for i in range(4)]
# extract features and match
l = {}
d = {}
for i in range(4):
sift.process_image(imname[i], featname[i])
l[i], d[i] = sift.read_features_from_file(featname[i])
matches = {}
for i in range(3):#2
matches[i] = sift.match(d[i + 1], d[i])
# visualize the matches (Figure 3-11 in the book)
for i in range(3):#2
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
# warp the images
delta = 1000 # 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)
figure()
imshow(array(im_02, "uint8"))
axis('off')
show()
















