全景圖概述
每當(dāng)一個(gè)平面圖像映射到一個(gè)彎曲的表面就會(huì)發(fā)生圖象投影,反之亦然,這中現(xiàn)象特別常見于全景攝影。例如地球的球面可以映射到一塊平坦的紙張。由于在我們周圍的整個(gè)視場的可以被認(rèn)為是作為球體的表面(對(duì)于所有觀測角度),我們需要一種能將球形投影到2-D平面以便照片打印的方法。
小的視角相對(duì)容易進(jìn)行形變并投影到平坦的紙上。但是,當(dāng)試圖把一個(gè)球形圖像映射到一個(gè)平面上,有些變形是不可避免的。因此,每一種類型的投影僅僅嘗試避免一種類型的失真,這是以犧牲其他失真為代價(jià)的。隨著視場角增大,觀測?。╲iewing arc)變得更彎曲,從而全景投影類型之間的差異變得更加顯著。什么時(shí)候使用那一種投影,在很大程度上取決于每個(gè)投影應(yīng)用。
整個(gè)全景拼接的算法流程來說,其實(shí)并不算復(fù)雜,至少在OpenCV的條件下如此。因?yàn)镺penCV自帶了很多函數(shù),完全可以搞定很多內(nèi)容。
1.選圖,兩張圖的重疊區(qū)域不能太小,我個(gè)人認(rèn)為最少不少于15%,這樣才能保證有足夠的角點(diǎn)匹配。
2.角點(diǎn)檢測。這一步OpenCV提供了很多種方法,譬如Harris角點(diǎn)檢測,而監(jiān)測出的角點(diǎn)用CvSeq存儲(chǔ),這是一個(gè)雙向鏈表。
3.角點(diǎn)提純。在提純的時(shí)候,需要使用RANSAC提純。OpenCV自帶了一個(gè)函數(shù),F(xiàn)indHomography,不但可以提純,還可以計(jì)算出3x3的轉(zhuǎn)換矩陣。這個(gè)轉(zhuǎn)換矩陣十分重要。
4.角點(diǎn)匹配。經(jīng)過提純后的角點(diǎn),則需要匹配。
5.圖像變換。這一步我曾經(jīng)嘗試過很多辦法,最后選擇了FindHomography輸出的變換矩陣,這是一個(gè)透視變換矩陣。經(jīng)過這個(gè)透視變換后的圖像,可以直接拿來做拼接。
6.圖象拼接。完成上面步驟之后,其實(shí)這一步很容易。
7.球面變換。這一步需要對(duì)坐標(biāo)系進(jìn)行轉(zhuǎn)換,從平面坐標(biāo)到球面坐標(biāo)。
OpenCV快速實(shí)現(xiàn)全景拼接
最新版的opencv2.4里面有很多新元素。 stitching module 就是一個(gè)非常有用的。 在opencv的例程文件夾里,有一個(gè)很好的腳本叫做 stitching_detailed.cpp. 這個(gè)腳本包括了創(chuàng)建全景圖的全部過程,包括特征提取,匹配,warp,以及合成。安裝好opencv以后,可以簡單的通過命令行來測試這個(gè)例程:
$ 。/stitching_detailed Univ*.jpg
這會(huì)使用默認(rèn)參數(shù)來創(chuàng)建一個(gè)result.jpg的最終文件,來源的圖片是以 “Univ”開頭的jpg圖像。 可以通過help察看一些設(shè)置
$ 。/stitching_detailed --help
例如,可以改變投影的方式,默認(rèn)是球面投影。 下面的例程用摩卡托投影法:
$ 。/stitching_detailed Univ*.jpg --warp mercator
結(jié)果如下:原圖像可以在這里找到
簡單實(shí)例
首先看下,opencv實(shí)現(xiàn)圖像拼接的最簡單實(shí)例,這是將stitching.cpp裁剪到最簡單的代碼,和表現(xiàn)效果。
具體代碼
?。踓pp] view plain copy#include 《iostream》
#include 《fstream》
#include “opencv2/highgui/highgui.hpp”
#include “opencv2/stitching/stitcher.hpp”
using namespace std;
using namespace cv;
bool try_use_gpu = true; //false;
vector《Mat》 imgs;
string result_name = “result.jpg”;
int 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] 《《 “‘ ”;
return -1;
}
imgs.push_back(img);
imshow(argv[i], img);
}
return 0;
}
int main(int argc, char* argv[]){
int retval = parseCmdArgs(argc, argv);
if (retval) return -1;
Mat pano;
Stitcher stitcher = Stitcher::createDefault(try_use_gpu);
Stitcher::Status status = stitcher.stitch(imgs, pano);
if (status != Stitcher::OK){
cout 《《 “Can’t stitch images, error code = ” 《《 int(status) 《《 endl;
return -1;
}
imwrite(result_name, pano);
imshow(“show”, pano);
cv::waitKey(0);
return 0;
}
效果演示
運(yùn)行本例:。/tmp 1.jpg 2.jpg 3.jpg
實(shí)現(xiàn)效果如下:
輸入圖像
輸出圖像
全景圖實(shí)例2
在前面的例子中,只是簡單的使用函數(shù):stitcher.stitch來生成里默認(rèn)設(shè)置的全景圖,這里繼續(xù)使用新的方式來生成各種類型的全景圖。
實(shí)現(xiàn)代碼
具體代碼如下:
#include 《iostream》
#include 《fstream》
#include “opencv2/highgui/highgui.hpp”
#include “opencv2/stitching/stitcher.hpp”
using namespace std;
using namespace cv;
bool try_use_gpu = false;
vector《Mat》 imgs;
string result_name = “result.jpg”;
int parseCmdArgs(int argc, char** argv){
for (int i = 1; i 《 argc-1; ++i){
Mat img = imread(argv[i]);
if (img.empty()){
cout 《《 “Can‘t read image ’” 《《 argv[i] 《《 “‘ ”;
return -1;
}
imgs.push_back(img);
imshow(argv[i], img);
}
return 0;
}
int main(int argc, char* argv[]){
int retval = parseCmdArgs(argc, argv);
if (retval) return -1;
Mat pano;
/*創(chuàng)建一個(gè)stitcher對(duì)象*/
Stitcher stitcher = Stitcher::createDefault(try_use_gpu);
/*設(shè)置生成結(jié)果圖為:1:平面, 2:柱面, 3:立體畫面*/
if(argv[4][0] == ’1‘){
PlaneWarper* cw = new PlaneWarper();
stitcher.setWarper(cw);
}else if(argv[4][0] == ’2‘){
SphericalWarper* cw = new SphericalWarper();
stitcher.setWarper(cw);
}else if(argv[4][0] == ’3‘){
StereographicWarper *cw = new cv::StereographicWarper();
stitcher.setWarper(cw);
}
/*使用Surf算法來尋找特征點(diǎn)*/
detail::SurfFeaturesFinder *featureFinder = new detail::SurfFeaturesFinder();
stitcher.setFeaturesFinder(featureFinder);
/*匹配給定的圖像和估計(jì)相機(jī)的旋轉(zhuǎn)*/
Stitcher::Status status = stitcher.estimateTransform(imgs);
if (status != Stitcher::OK)
{
cout 《《 “Can’t stitch images, error code = ” 《《 int(status) 《《 endl;
return -1;
}
/*生成全景圖像*/
status = stitcher.composePanorama(pano);
if (status != Stitcher::OK)
{
cout 《《 “Can‘t stitch images, error code = ” 《《 int(status) 《《 endl;
return -1;
}
imwrite(result_name, pano);
imshow(“show”, pano);
cv::waitKey(0);
return 0;
}
代碼講解
1、填充imgs,將輸入的圖片全部填充到容器imgs中,并將輸入的圖片,一一顯示出來。
int parseCmdArgs(int argc, char** argv){
for (int i = 1; i 《 argc-1; ++i){
Mat img = imread(argv[i]);
if (img.empty()){
cout 《《 “Can‘t read image ’” 《《 argv[i] 《《 “‘ ”;
return -1;
}
imgs.push_back(img);
imshow(argv[i], img);
}
return 0;
}
2、創(chuàng)建一個(gè)stitcher對(duì)象。
Stitcher stitcher = Stitcher::createDefault(try_use_gpu);
3、設(shè)置生成結(jié)果圖為:
? ? ? ?1:平面, 2:柱面, 3:立體畫面。opencv中提供了很多可以生成的全景圖種類。在它提供的復(fù)雜版實(shí)例:stitching_detailed.cpp,
有如下種類可以選擇:
plane|cylindrical|spherical|fisheye|stereographic|compressedPlaneA2B1|
compressedPlaneA1.5B1|compressedPlanePortraitA2B1|compressedPlanePortraitA1.5B1|paniniA2B1|
paniniA1.5B1|paniniPortraitA2B1|paniniPortraitA1.5B1|mercator|transverseMercator
本例中,只使用了3種作為選擇:
/*設(shè)置生成結(jié)果圖為:1:平面, 2:柱面, 3:立體畫面*/
if(argv[4][0] == ‘1’){
PlaneWarper* cw = new PlaneWarper();
stitcher.setWarper(cw);
}else if(argv[4][0] == ‘2’){
SphericalWarper* cw = new SphericalWarper();
stitcher.setWarper(cw);
}else if(argv[4][0] == ‘3’){
StereographicWarper *cw = new cv::StereographicWarper();
stitcher.setWarper(cw);
}
4、選擇尋找特征點(diǎn)的算法,opencv中提供了Surf和Orb兩種方式可以選擇,本例中使用的是Surf。
detail::SurfFeaturesFinder *featureFinder = new detail::SurfFeaturesFinder();
stitcher.setFeaturesFinder(featureFinder);
5、生成輸出全景圖,這里使用另一種方式來實(shí)現(xiàn)。
/*匹配給定的圖像和估計(jì)相機(jī)的旋轉(zhuǎn)*/
Stitcher::Status status = stitcher.estimateTransform(imgs);
/*生成全景圖像*/
status = stitcher.composePanorama(pano);
imwrite(result_name, pano);
imshow(“show”, pano);
效果演示
本例的三種效果圖,對(duì)應(yīng)顯示如下:
1、平面
2、球面
3、立體
評(píng)論
查看更多