天天看點

opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像

Opencv使用Stitcher類圖像拼接生成全景圖像

Opencv中自帶的Stitcher類可以實作全景圖像,效果不錯。下邊的例子是Opencv Samples中的stitching.cpp的簡化,源檔案可以在這個路徑裡找到:

\opencv\sources\samples\cpp\stitching.cpp

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

#include <fstream>

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/stitching/stitcher.hpp"

#include <iostream>

using

namespace

cv;

using

namespace

std;

vector<Mat> imgs;

//儲存拼接的原始圖像向量

//導入所有原始拼接圖像函數

void

parseCmdArgs(

int

argc,

char

** argv);

int

main(

int

argc,

char

* argv[])

{

//導入拼接圖像

parseCmdArgs(argc, argv);  

Mat pano;

Stitcher stitcher = Stitcher::createDefault(

false

);

Stitcher::Status status = stitcher.stitch(imgs, pano);

//拼接

if

(status != Stitcher::OK)

//判斷拼接是否成功

{

cout <<

"Can't stitch images, error code = "

<<

int

(status) << endl;

return

-1;

}

namedWindow(

"全景拼接"

,0);

imshow(

"全景拼接"

,pano);

imwrite(

"D:\\全景拼接.jpg"

,pano);

waitKey();  

return

0;

}

//導入所有原始拼接圖像函數

void

parseCmdArgs(

int

argc,

char

** argv)

{

for

(

int

i=1;i<argc;i++)

{

Mat img = imread(argv[i]);

if

(img.empty())

{

cout <<

"Can't read image '"

<< argv[i] <<

"'\n"

;

}

imgs.push_back(img);

}

}

圖1:

opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像

圖2:

opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像

圖3:

opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像

圖4:

opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像

圖5:

opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像

5個圖檔的拍攝角度合起來在180°左右,沒有經過壓縮的,下載下傳下來可以直接測試使用,傳入順序随意,Stitcher會自動排列。全景拼接效果很贊:

下面是python的:

一、拼接介紹

在同一位置拍攝的兩幅或多幅圖像是單應性相關的。我們可以使用該限制将很多圖像拼接起來,拼成一幅大的圖像來建立全景圖像

全景圖像拼接最重要的兩個步驟就是:

(一)特征點比對

這部分主要采用SIFT算法實作,之前的部落格有介紹就不再詳細介紹了,主要是為了找到兩幅圖像相同的特征點并将其進行比對。

(二)圖檔比對

圖檔比對就是找到圖像之間所有重疊的部分,将其拼接後就能得到一幅全景圖。

基本原理

1.單應性矩陣

定義:在計算機視覺領域,空間同一平面的任意兩幅圖像被單應矩陣聯系着(假設在針孔相機模型中),即一個相機拍攝空間同一平面的兩張圖像,這兩張圖像之間的映射關系可以用單應矩陣表示。

在兩視幾何中,也可以這樣了解,兩架相機拍同一空間上得到兩幅圖像A、B,其中圖像A到圖像B存在一種變換,而且這種變換是一一對應的關系,這個變換矩陣用單應矩陣表示。OpenCV中可以用函數findHomography計算得到單應矩陣H。

要實作兩張圖檔的簡單拼接,其實隻需找出兩張圖檔中相似的點 (至少四個,因為 homography 矩陣的計算需要至少四個點), 計算一張圖檔可以變換到另一張圖檔的變換矩陣 (homography 單應性矩陣),用這個矩陣把那張圖檔變換後放到另一張圖檔相應的位置 ( 就是相當于把兩張圖檔中定好的四個相似的點給重合在一起)。如此,就可以實作簡單的全景拼接。當然,因為拼合之後圖檔會重疊在一起,是以需要重新計算圖檔重疊部分的像素值,否則結果會很難看。

2.RANSAC算法

RANSAC(Random Sample Consensus)即随機采樣一緻性,該方法是用來找到正确模型來拟合帶有噪聲資料的疊代方法。給定一個模型,例如點集之間的單應性矩陣,RANSAC的基本思想在于,找到正确資料點的同時摒棄噪聲點。

3.利用RANSAC算法求解單應性矩陣

雖然SIFT是具有很強穩健性的描述子,當這方法仍遠非完美,還會存在一些錯誤的比對。而單應性矩陣需要選取4對特征點計算,萬一選中了不正确的比對點,那麼計算的單應性矩陣肯定是不正确的。是以,為了提高計算結果的魯棒性,我們下一步就是要把這些不正确的比對點給剔除掉,獲得正确的單應性矩陣。在這裡使用了RANSAC算法:随機抽取不同的4對特征比對坐标(在圖1中随機抽取4個特征坐标,以及這4個特征坐标在圖2中比對的4個特征坐标,組成4對特征比對坐标),利用這4對特征比對坐标計算出矩陣H1(3x3的一個矩陣,圖2經過矩陣變換後,可以把圖2映射到圖1的坐标空間中,再将圖2進行簡單的平移即可與圖1實作無縫拼接),再将圖2中所有特征比對點經過該透視矩陣H1映射到圖1的坐标空間,然後與圖1比對點實際坐标求歐氏距離(就是為了驗證計算出來的這個H1矩陣是否滿足絕大多數特征比對點);之後重複上面内容,再随機抽取不同的四組特征比對坐标,再計算透視矩陣H2,再求歐式距離,如此重複多次。最後以歐式距離最小的那個透視矩陣(表示這個特征矩陣H滿足最多的特征比對點,它最優秀)作為最終計算結果。

4.圖檔融合

在用計算出的變換矩陣對其中一張圖做變換,然後把變換的圖檔與另一張圖檔重疊在一起,并重新計算重疊區域新的像素值。對于計算重疊區域的像素值,其實可以有多種方法去實作一個好的融合效果,這裡就用最簡單粗暴的但效果也不錯的方式。直白來說就是實作一個圖像的線性漸變,對于重疊的區域,靠近左邊的部分,讓左邊圖像内容顯示的多一些,靠近右邊的部分,讓右邊圖像的内容顯示的多一些。用公式表示就是,假設 alpha 表示像素點橫坐标到左右重疊區域邊界橫坐标的距離,新的像素值就為 newpixel = 左圖像素值 × (1 - alpha) + 右圖像素值 × alpha 。這樣就可以實作一個簡單的融合效果。

2代碼及運作結果

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

"""
This is the panorama example from section 3.3.
"""

# set paths to data folder
featname = ['C:\\Users\DELL\Desktop\PCV\jmu\panorama/z0'+str(i+1)+'.sift' for i in range(5)]
imname = ['C:\\Users\DELL\Desktop\PCV\jmu\panorama/z0'+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 = 500 # 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_42, "uint8"))
axis('off')
show()
           
opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像
opencv Stitcher多圖拼接Opencv使用Stitcher類圖像拼接生成全景圖像

因為相機和光照強度的差異,會造成一幅圖像内部,以及圖像之間亮度的不均勻,拼接後的圖像會出現明暗交替,這樣給觀察造成極大的不便。室外場景的拼接效果比室内的要好很多,多圖拼接對圖檔的要求也比較高,差異性大或太小(幾乎相同)的拼接效果都很差。