計算機視覺4—全景圖像拼接

目錄

1.RANSAC簡介

1.1 算法基本思想和流程

1.2 RANSAC 求解單應矩陣

2.圖像映射與全景拼接

2.1 計算第二張圖像與第一張圖像之間的變換關係

2.2 將第二張圖像疊加到第一張圖像的座標系中

2.3. 圖像映射小結

2.4 圖像拼接整體流程

3.實驗分析

3.1 代碼實現

3.2 實驗分析

    3.2.1 實驗場景一

    3.2.2 實驗場景二

3.3 實驗總結

4.錯誤


1.RANSAC簡介

RANSAC(Random Sample Consensus)隨機採樣一致算法是根據一組包含異常數據的樣本數據集,計算出數據的數學模型參數,得到有效樣本數據的算法。

1.1 算法基本思想和流程

RANSAC是通過反覆選擇數據集去估計出模型,一直迭代到估計出認爲比較好的模型。具體的實現步驟可以分爲以下幾步:

  1. 選擇出可以估計出模型的最小數據集;(對於直線擬合來說就是兩個點,對於計算Homography矩陣就是4個點)
  2. 使用這個數據集來計算出數據模型;
  3. 將所有數據帶入這個模型,計算出“內點”的數目;(累加在一定誤差範圍內的適合當前迭代推出模型的數據)
  4. 比較當前模型和之前推出的最好的模型的“內點“的數量,記錄最大“內點”數的模型參數和“內點”數;
  5. 重複1-4步,直到迭代結束或者當前模型已經足夠好了(“內點數目大於一定數量”)。

1.2 RANSAC 求解單應矩陣

RANSAC 循環:

  1. 隨機選擇四對匹配特徵
  2. 根據DLT計算單應矩陣 H (唯一解)
  3. 對所有匹配點,計算映射誤差ε= ||p i ’, H p i ||
  4. 根據誤差閾值,確定inliers(例如3-5像素)
  5. 針對最大inliers集合,重新計算單應矩陣 H

 

2.圖像映射與全景拼接

首先是在連續圖像對間使用SIFT特徵尋找匹配對應點對,SIFT是具有較強穩健性的描述子,能夠比其他描述子產生更少的錯誤點,但是該方法仍不是很完美;使用RANSAC算法估計出圖像間的單應性矩陣,判定哪些點對是正確的,哪些點對是錯誤的,即使用一個閾值來決定哪些單應性矩陣是合理的;然後將所有的圖像扭曲到一個公共的圖像平面上。

基於SIFT特徵匹配的RNASAC在上一篇博客也有相應實驗的敘述,可參見博客SIFT理解與應用

通常,這裏的公共平面爲中心圖像平面。一種方法是創建一個很大的圖像,比如將圖像中全部填充0,使其和中心圖像平行,然後將所有的圖像扭曲到上面。由於我們所有的圖像是由照相機水平旋轉拍攝的,因此我們可以使用一個較簡單的步驟:將中心圖像左邊或者右邊的區域填充爲0,以便爲扭曲的圖像騰出空間。

圖像拼接的幾何原理:全景融合的 3D 幾何解釋,圖像被投影到共同的拼接平面上(同一座標系), 在拼接平面上實現全景融合。在拼接的應用中,其實可以簡化理解爲 2D圖像的變換,疊加過程。

基礎流程
① 針對某個場景拍攝多張/序列圖像
② 計算第二張圖像與第一張圖像之間的變換關係
③ 將第二張圖像疊加到第一張圖像的座標系中
④ 變換後的融合/合成
⑤ 在多圖場景中,重複上述過程

2.1 計算第二張圖像與第一張圖像之間的變換關係

(1)變換類型選擇

將兩幅圖像疊加在一起,選擇需要採用模型,如位移、旋轉、尺度大小、仿射和透視等。

(2)2D 圖像變換原理:

圖像濾波: 改變圖像的像素點取值範圍g\left ( x \right )=h\left ( f\left ( x \right ) \right )

圖像變換: 改變圖像的座標取值範圍g\left ( x \right )=f\left ( h\left ( x \right ) \right )

(3)2D 圖像變換類型

  #DoF Preserves Icon
平移 {x}'=x+t,t=\left (t_{_{x}},t_{y} \right ) 2 orientation  

旋轉+平移 (剛體)

{x}'=Rx+t

R=\begin{bmatrix} cos\theta &-sin\theta \\sin\theta & cos\theta \end{bmatrix},t=\left ( \left t_{x} ,t_{y}\right )

3 lengths

相似

{x}'=sRx+t=\begin{bmatrix} a & -b & t_{x}\\ b & a & t_{y} \end{bmatrix}\begin{pmatrix} x\\ y\\ 1 \end{pmatrix}

4 angles

仿射

 

{x}'=\begin{bmatrix} a_{00} & a_{01} & a_{02}\\ a_{10} & a_{11} & a_{12} \end{bmatrix}\begin{pmatrix} x\\ y\\ 1 \end{pmatrix}
6 parallelism

單應性變換

 

{x}'=Hx

\begin{bmatrix} w{x}'\\ w{y}'\\ w \end{bmatrix}=\begin{bmatrix} h_{00} & h_{01} & h_{02}\\ h_{10} & h_{11} & h_{12}\\ h_{20} & h_{21} & h_{22} \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix}

8 straight lines

(4)參數求解:平移問題、仿射變換和單應性變換等

2.2 將第二張圖像疊加到第一張圖像的座標系中

2D 圖像變換

給定變換模型 x'= h(x) ,以及輸入圖像f(x), 根據f(x)計算變換後的圖像 g(x')= f(h(x))。前向映射:對於 f(x) 中的每個像素 x,根據變換模型計算相應的映射座標x' = h(x),並將x的像素值賦給 g(x')。逆向映射:對於 g(x')中的每個像素 x',根據變換模型計算相應的映射座標 x = h -1 (x'),並將x的像素值賦給g(x')。

2.3. 圖像映射小結

 流程:
① 針對兩張/多張圖像提取特徵
② 特徵匹配
③ 根據圖像變換特點,選取合適的變換結構
④ 根據DLT等方法計算變換結構
⑤ 採用正向/逆向映射,利用插值方式實現圖像映射變換

2.4 圖像拼接整體流程

  1. 根據給定圖像/集, 實現特徵匹配
  2. 通過匹配特徵計算圖像之間的變換結構
  3. 利用圖像變換結構, 實現圖像映射
  4. 針對疊加後的圖像, 採用APAP算法對齊特徵點
  5. 通過圖割方法, 自動選取拼接縫
  6. 根據multi-band bleing策略實現融合

(1)針對疊加後的圖像, 採用APAP算法對齊特徵點

對兩張圖片進行SIFT特徵匹配, 然後對SIFT特徵匹配得到的特徵點使用 RANSAC算法進行去除錯誤的特徵點對。篩選後的特徵點基本能夠一一對應。使用DLT算法,將剩下的特徵點對進行透視變換矩陣的估計。因爲得到的透視變換矩陣是基於全局特徵點對進行的,即一個剛性的單應性矩陣完成配準。爲提高配準的精度,Apap將圖像切割成無數多個小方塊,對每個小方塊的變換矩陣逐一估計。非常依賴於特徵點對。若圖像高頻信息較少,特徵點對過少,配准將完全失效,並且對大尺度的圖像進行配準,其效果也不是很好,一切都決定於特徵點對的數量。

(2)通過圖割方法, 自動選取拼接縫

尋找代價最小的分割,典型算法是最小割最大流算法,將圖內帶權值的看作帶有流量值的管道,將最大量水從源點送到匯點。

(3)根據multi-band bleing策略實現融合

融合目的在於拼縫消除, Multi-Band能夠達到比較好的融合效果,但是效率低,採用Laplacian(拉普拉斯)金字塔,通過對相鄰兩層的高斯金字塔進行差分,將原圖分解成不同尺度的子圖,對每一個之圖進行加權平均,得到每一層的融合結果,最後進行金字塔的反向重建,得到最終融合效果過程。
 

3.實驗分析

3.1 代碼實現

# -*- coding: utf-8 -*-
from pylab import *
from numpy import *
from PIL import Image
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift

featname = ['data/pic1/c' + str(i + 1) + '.sift' for i in range(3)]
imname = ['data/pic1/c' + str(i + 1) + '.jpg' for i in range(3)]

l = {}
d = {}
for i in range(3):
    sift.process_image(imname[i], featname[i])
    l[i], d[i] = sift.read_features_from_file(featname[i])

matches = {}
for i in range(2):
    matches[i] = sift.match(d[i + 1], d[i])

for i in range(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)

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

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

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()

3.2 實驗分析

3.2.1 實驗場景一

針對固定點位拍攝多張圖片,以中間圖片爲中心

原圖

 

特徵匹配圖

 

拼接全景圖

小結:

 從該全景拼接圖可以看出左邊的拼接效果較好,雖然因圖片曝光度的不同存在一定的邊緣效應,但特徵點能相對應,兩圖的拼接沒有扭曲或者其他的問題;而全景拼接圖的右邊,雖然實現了拼接在一起,但是右邊的圖片被變大扭曲了,邊緣樓房明顯傾斜變形,且越右邊變形的越厲害,右邊的圖片整個都被拉伸扭曲變形。從SIFT特徵匹配的結果來看,兩兩匹配得到的特徵點很多,匹配效果也很好,但是最後的全景拼接圖只有左邊能夠實現較好的點對點拼接。

 

3.2.2 實驗場景二

針對同一場景(選取視差變化大的場景,也就是有近景目標),更換拍攝位置

原圖

 

特徵匹配圖

 

拼接全景圖

小結:

該全景拼接圖相對於實驗(1)得到的全景拼接圖效果要好,左邊拼接依然存在曝光度不同導致的邊緣效應,但是圖片沒有被大幅度扭曲,只是在花盆左後面旁邊的瓷磚拼接有點扭曲,兩張圖片過渡不連續存在的拼接縫隙;此時的全景拼接圖沒有再出現右邊扭曲變形的情況,反而因爲曝光度的相似性,特徵點能一一對應得到了較好的拼接效果。

3.3 實驗總結

  • 圖片全景拼接把幾張圖片拼接在一起,先選取一箇中間圖像作爲中心圖像,也就是希望將其他圖像變成的圖像,由於匹配是從最右邊的圖像開始計算,所以需要注意將圖片的順序進行顛倒(即由1,2,3,4,5的存放順序變爲5,4,3,2,1),使得從左邊圖像開始扭曲,將所有圖像扭曲到一個公共的圖像平面上。
  • 圖像右邊的黑色區域可以通過減小代碼中的delta相應減小,但不能設置的過小,delta值太小會使得圖片不能完整呈現,即可能存在部分圖片被截取了。
  • 兩個實驗場景我選取的都是建築物較多的室外場景,背景比較複雜,但也因爲繁複的建築物背景可以檢測出較多的特徵點,總結兩個實驗都是左邊的匹配效果較佳,即特徵點能夠較好的實現一一對應。
  • 曝光度的不同容易導致拼接全景圖存在邊緣效應。

4.錯誤

錯誤提示爲ImportError:  No module named delaunay。

解決:

1.先進入到進入到python目錄下的Lib\site-packages\PCV\geometry\warp.py,把黑色方框中的 

import matplotlib.delaunay as md

  改爲紅色方框的  

 from scipy.spatial import Delaunay  

 

2.把紅色方框的...triangulate_points(x,y)裏面的代碼替換成

tri = Delaunay(np.c_[x,y]).simplices

 

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