使用OpenCV進行圖像全景拼接


點擊上方 AI小白學視覺 」,選擇加" 星標 "或「 置頂
重磅乾貨,第一時間送達


圖像拼接是計算機視覺中最成功的應用之一。現在,很難找到不包含此功能的手機或圖像處理API。在本文中,咱們將討論如何使用Python和OpenCV進行圖像拼接。也就是,給定兩張共享某些公共區域的圖像,目標是「縫合」它們並建立一個全景圖像場景。固然也能夠是給定多張圖像,可是總會轉換成兩張共享某些公共區域圖像拼接的問題,所以本文以最簡單的形式進行介紹。python


本文主要的知識點包含一下內容:
算法

  • 關鍵點檢測微信

  • 局部不變描述符(SIFT,SURF等)app

  • 特徵匹配函數

  • 使用RANSAC進行單應性估計測試

  • 透視變換ui


咱們須要拼接的兩張圖像以下:
spa



特徵檢測與提取.net

給定上述一對圖像,咱們但願將它們縫合以建立全景場景。重要的是要注意,兩個圖像都須要有一些公共區域。固然,咱們上面給出的兩張圖像時比較理想的,有時候兩個圖像雖然具備公共區域,可是一樣還可能存在縮放、旋轉、來自不一樣相機等因素的影響。可是不管哪一種狀況,咱們都須要檢測圖像中的特徵點。設計


關鍵點檢測


最初的而且多是幼稚的方法是使用諸如Harris Corners之類的算法來提取關鍵點。而後,咱們能夠嘗試基於某種類似性度量(例如歐幾里得距離)來匹配相應的關鍵點。衆所周知,角點具備一個不錯的特性:角點不變。這意味着,一旦檢測到角點,即便旋轉圖像,該角點仍將存在。

可是,若是咱們旋轉而後縮放圖像怎麼辦?在這種狀況下,咱們會很困難,由於角點的大小不變。也就是說,若是咱們放大圖像,先前檢測到的角可能會變成一條線!

總而言之,咱們須要旋轉和縮放不變的特徵。那就是更強大的方法(如SIFT,SURF和ORB)。


關鍵點和描述符

諸如SIFT和SURF之類的方法試圖解決角點檢測算法的侷限性。一般,角點檢測器算法使用固定大小的內核來檢測圖像上的感興趣區域(角)。不難看出,當咱們縮放圖像時,該內核可能變得過小或太大。爲了解決此限制,諸如SIFT之類的方法使用高斯差分(DoD)。想法是將DoD應用於同一圖像的不一樣縮放版本。它還使用相鄰像素信息來查找和完善關鍵點和相應的描述符。


首先,咱們須要加載2個圖像,一個查詢圖像和一個訓練圖像。最初,咱們首先從二者中提取關鍵點和描述符。經過使用OpenCV detectAndCompute()函數,咱們能夠一步完成它。請注意,爲了使用detectAndCompute(),咱們須要一個關鍵點檢測器和描述符對象的實例。它能夠是ORB,SIFT或SURF等。此外,在將圖像輸入給detectAndCompute()以前,咱們將其轉換爲灰度。

def detectAndDescribe(image, method=None): """ Compute key points and feature descriptors using an specific method """
assert method is not None, "You need to define a feature detection method. Values are: 'sift', 'surf'"
# detect and extract features from the image if method == 'sift': descriptor = cv2.xfeatures2d.SIFT_create() elif method == 'surf': descriptor = cv2.xfeatures2d.SURF_create() elif method == 'brisk': descriptor = cv2.BRISK_create() elif method == 'orb': descriptor = cv2.ORB_create()
# get keypoints and descriptors (kps, features) = descriptor.detectAndCompute(image, None)
return (kps, features)

咱們爲兩個圖像都設置了一組關鍵點和描述符。若是咱們使用SIFT做爲特徵提取器,它將爲每一個關鍵點返回一個128維特徵向量。若是選擇SURF,咱們將得到64維特徵向量。下圖顯示了使用SIFT,SURF,BRISK和ORB獲得的結果。

使用ORB和漢明距離檢測關鍵點和描述符

使用SIFT檢測關鍵點和描述符

使用SURF檢測關鍵點和描述符

使用BRISK和漢明距離檢測關鍵點和描述符


特徵匹配


如咱們所見,兩個圖像都有大量特徵點。如今,咱們想比較兩組特徵,並儘量顯示更多類似性的特徵點對。使用OpenCV,特徵點匹配須要Matcher對象。在這裏,咱們探索兩種方式:暴力匹配器(BruteForce)和KNN(k最近鄰)。


BruteForce(BF)Matcher的做用恰如其名。給定2組特徵(來自圖像A和圖像B),將A組的每一個特徵與B組的全部特徵進行比較。默認狀況下,BF Matcher計算兩點之間的歐式距離。所以,對於集合A中的每一個特徵,它都會返回集合B中最接近的特徵。對於SIFT和SURF,OpenCV建議使用歐幾里得距離。對於ORB和BRISK等其餘特徵提取器,建議使用漢明距離。咱們要使用OpenCV建立BruteForce Matcher,通常狀況下,咱們只須要指定2個參數便可。第一個是距離度量。第二個是是否進行交叉檢測的布爾參數。具體代碼以下:

def createMatcher(method,crossCheck): "Create and return a Matcher Object"
if method == 'sift' or method == 'surf': bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=crossCheck) elif method == 'orb' or method == 'brisk': bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=crossCheck) return bf

交叉檢查布爾參數表示這兩個特徵是否具備相互匹配才視爲有效。換句話說,對於被認爲有效的一對特徵(f1,f2),f1須要匹配f2,f2也必須匹配f1做爲最接近的匹配。此過程可確保提供更強大的匹配功能集,這在原始SIFT論文中進行了描述。


可是,對於要考慮多個候選匹配的狀況,可使用基於KNN的匹配過程。KNN不會返回給定特徵的單個最佳匹配,而是返回k個最佳匹配。須要注意的是,k的值必須由用戶預先定義。如咱們所料,KNN提供了更多的候選功能。可是,在進一步操做以前,咱們須要確保全部這些匹配對都具備魯棒性。


比率測試

爲了確保KNN返回的特徵具備很好的可比性,SIFT論文的做者提出了一種稱爲比率測試的技術。通常狀況下,咱們遍歷KNN獲得匹配對,以後再執行距離測試。對於每對特徵(f1,f2),若是f1和f2之間的距離在必定比例以內,則將其保留,不然將其丟棄。一樣,必須手動選擇比率值。


本質上,比率測試與BruteForce Matcher的交叉檢查選項具備相同的做用。二者都確保一對檢測到的特徵確實足夠接近以致於被認爲是類似的。下面2個圖顯示了BF和KNN Matcher在SIFT特徵上的匹配結果。咱們選擇僅顯示100個匹配點以清晰顯示。

使用KNN和SIFT的定量測試進行功能匹配

在SIFT特徵上使用暴力匹配器進行特徵匹配

須要注意的是,即便作了多種篩選來保證匹配的正確性,也沒法徹底保證特徵點徹底正確匹配。儘管如此,Matcher算法仍將爲咱們提供兩幅圖像中最佳(更類似)的特徵集。接下來,咱們利用這些點來計算將兩個圖像的匹配點拼接在一塊兒的變換矩陣。


這種變換稱爲單應矩陣。簡而言之,單應性是一個3x3矩陣,可用於許多應用中,例如相機姿態估計,透視校訂和圖像拼接。它將點從一個平面(圖像)映射到另外一平面。

估計單應性

隨機採樣一致性(RANSAC)是用於擬合線性模型的迭代算法。與其餘線性迴歸器不一樣,RANSAC被設計爲對異常值具備魯棒性。


像線性迴歸這樣的模型使用最小二乘估計將最佳模型擬合到數據。可是,普通最小二乘法對異常值很是敏感。若是異常值數量很大,則可能會失敗。RANSAC經過僅使用數據中的一組數據估計參數來解決此問題下圖顯示了線性迴歸和RANSAC之間的比較。須要注意數據集包含至關多的離羣值。

咱們能夠看到線性迴歸模型很容易受到異常值的影響。那是由於它試圖減小平均偏差。所以,它傾向於支持使全部數據點到模型自己的總距離最小的模型。包括異常值。相反,RANSAC僅將模型擬合爲被識別爲點的點的子集。


這個特性對咱們的用例很是重要。在這裏,咱們將使用RANSAC來估計單應矩陣。事實證實,單應矩陣對咱們傳遞給它的數據質量很是敏感。所以,重要的是要有一種算法(RANSAC),該算法能夠從不屬於數據分佈的點中篩選出明顯屬於數據分佈的點。

估計了單應矩陣後,咱們須要將其中一張圖像變換到一個公共平面上。在這裏,咱們將對其中一張圖像應用透視變換。透視變換能夠組合一個或多個操做,例如旋轉,縮放,平移或剪切。咱們可使用OpenCV warpPerspective()函數。它以圖像和單應矩陣做爲輸入。

# Apply panorama correctionwidth = trainImg.shape[1] + queryImg.shape[1]height = trainImg.shape[0] + queryImg.shape[0]
result = cv2.warpPerspective(trainImg, H, (width, height))result[0:queryImg.shape[0], 0:queryImg.shape[1]] = queryImg
plt.figure(figsize=(20,10))plt.imshow(result)
plt.axis('off')plt.show()


生成的全景圖像以下所示。如咱們所見,結果中包含了兩個圖像中的內容。另外,咱們能夠看到一些與照明條件和圖像邊界邊緣效應有關的問題。理想狀況下,咱們能夠執行一些處理技術來標準化亮度,例如直方圖匹配,這會使結果看起來更真實和天然一些。







交流羣

歡迎加入公衆號讀者羣一塊兒和同行交流,目前有SLAM、三維視覺、傳感器自動駕駛、計算攝影、檢測、分割、識別、醫學影像、GAN算法競賽等微信羣(之後會逐漸細分),請掃描下面微信號加羣,備註:」暱稱+學校/公司+研究方向「,例如:」張三 + 上海交大 + 視覺SLAM「。請按照格式備註,不然不予經過。添加成功後會根據研究方向邀請進入相關微信羣。請勿在羣內發送廣告,不然會請出羣,謝謝理解~


本文分享自微信公衆號 - 小白學視覺(NoobCV)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索