C++ OpenCV實(shí)戰(zhàn)之圖像全景拼接
前言
本文將使用OpenCV C++ 進(jìn)行圖像全景拼接。目前使用OpenCV對(duì)兩幅圖像進(jìn)行拼接大致可以分為兩類(lèi)。
一、使用OpenCV內(nèi)置API Stitcher 進(jìn)行拼接。
二、使用特征檢測(cè)算法匹配兩幅圖中相似的點(diǎn)、計(jì)算變換矩陣、最后對(duì)其進(jìn)行透視變換就可以了。
一、OpenCV Stitcher
imageA

imageB

原圖如圖所示。本案例的需求是將上述兩幅圖片拼接成一幅圖像。首先使用OpenCV提供的Stitcher進(jìn)行拼接。關(guān)于Stitcher的具體原理請(qǐng)大家自行查找相關(guān)資料。
1.功能源碼
bool OpenCV_Stitching(Mat imageA, Mat imageB)
{
vector<Mat>images;
images.push_back(imageA);
images.push_back(imageB);
Ptr<Stitcher>stitcher = Stitcher::create();
Mat result;
Stitcher::Status status = stitcher->stitch(images, result);// 使用stitch函數(shù)進(jìn)行拼接
if (status != Stitcher::OK) return false;
imshow("OpenCV圖像全景拼接", result);
return true;
}
2.效果

這就是使用OpenCV 內(nèi)置Stitcher拼接出來(lái)的效果。
二、圖像全景拼接
1.特征檢測(cè)
使用方法二進(jìn)行圖像全景拼接。目前網(wǎng)上教程大致流程歸為:
1、使用特征檢測(cè)算子提取兩幅圖像的關(guān)鍵點(diǎn),然后進(jìn)行特征描述子匹配。我這里使用的是SURF算子。當(dāng)然SIFT等其他特征檢測(cè)算子也可以。
//創(chuàng)建SURF特征檢測(cè)器 int Hessian = 800; Ptr<SURF>detector = SURF::create(Hessian); //進(jìn)行圖像特征檢測(cè)、特征描述 vector<KeyPoint>keypointA, keypointB; Mat descriptorA, descriptorB; detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA); detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB); //使用FLANN算法進(jìn)行特征描述子的匹配 FlannBasedMatcher matcher; vector<DMatch>matches; matcher.match(descriptorA, descriptorB, matches);

如圖為使用FLANN算法進(jìn)行特征描述子匹配的結(jié)果。我們需要把那些匹配程度高的關(guān)鍵點(diǎn)篩選出來(lái)用以下面計(jì)算兩幅圖像的單應(yīng)性矩陣。
2、篩選出匹配程度高的關(guān)鍵點(diǎn)
double Max = 0.0;
for (int i = 0; i < matches.size(); i++)
{
//float distance –>代表這一對(duì)匹配的特征點(diǎn)描述符(本質(zhì)是向量)的歐氏距離,數(shù)值越小也就說(shuō)明兩個(gè)特征點(diǎn)越相像。
double dis = matches[i].distance;
if (dis > Max)
{
Max = dis;
}
}
//篩選出匹配程度高的關(guān)鍵點(diǎn)
vector<DMatch>goodmatches;
vector<Point2f>goodkeypointA, goodkeypointB;
for (int i = 0; i < matches.size(); i++)
{
double dis = matches[i].distance;
if (dis < 0.15*Max)
{
//int queryIdx –>是測(cè)試圖像的特征點(diǎn)描述符(descriptor)的下標(biāo),同時(shí)也是描述符對(duì)應(yīng)特征點(diǎn)(keypoint)的下標(biāo)。
goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
//int trainIdx –> 是樣本圖像的特征點(diǎn)描述符的下標(biāo),同樣也是相應(yīng)的特征點(diǎn)的下標(biāo)。
goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
goodmatches.push_back(matches[i]);
}
}
如圖為imageA篩選出來(lái)的關(guān)鍵點(diǎn)。

如圖為imageB篩選出來(lái)的關(guān)鍵點(diǎn)。

從上圖可以看出,我們已經(jīng)篩選出imageA,imageB共有的關(guān)鍵點(diǎn)部分。接下來(lái),我們需要使用這兩個(gè)點(diǎn)集計(jì)算兩幅圖的單應(yīng)性矩陣。
2.計(jì)算單應(yīng)性矩陣
計(jì)算單應(yīng)性變換矩陣
//獲取圖像A到圖像B的投影映射矩陣,尺寸為3*3
Mat H = findHomography(goodkeypointA, goodkeypointB, RANSAC);
Mat M = (Mat_<double>(3, 3) << 1.0, 0, imageA.cols, 0, 1.0, 0, 0, 0, 1.0);
Mat Homo = M * H;
3.透視變換
根據(jù)計(jì)算出來(lái)的單應(yīng)性矩陣對(duì)imageA進(jìn)行透視變換
//進(jìn)行透視變換
Mat DstImg;
warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
imshow("透視變換", DstImg);

如圖所示為imageA進(jìn)行透視變換得到的結(jié)果。
4.圖像拼接
根據(jù)上述操作,我們已經(jīng)得到了經(jīng)透視變換的imageA,接下來(lái)只需將imageA與imageB拼接起來(lái)就可以了。
imageB.copyTo(DstImg(Rect(imageA.cols, 0, imageB.cols, imageB.rows)));
imshow("圖像全景拼接", DstImg);
5.功能源碼
bool Image_Stitching(Mat imageA, Mat imageB, bool draw)
{
//創(chuàng)建SURF特征檢測(cè)器
int Hessian = 800;
Ptr<SURF>detector = SURF::create(Hessian);
//進(jìn)行圖像特征檢測(cè)、特征描述
vector<KeyPoint>keypointA, keypointB;
Mat descriptorA, descriptorB;
detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA);
detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB);
//使用FLANN算法進(jìn)行特征描述子的匹配
FlannBasedMatcher matcher;
vector<DMatch>matches;
matcher.match(descriptorA, descriptorB, matches);
double Max = 0.0;
for (int i = 0; i < matches.size(); i++)
{
//float distance –>代表這一對(duì)匹配的特征點(diǎn)描述符(本質(zhì)是向量)的歐氏距離,數(shù)值越小也就說(shuō)明兩個(gè)特征點(diǎn)越相像。
double dis = matches[i].distance;
if (dis > Max)
{
Max = dis;
}
}
//篩選出匹配程度高的關(guān)鍵點(diǎn)
vector<DMatch>goodmatches;
vector<Point2f>goodkeypointA, goodkeypointB;
for (int i = 0; i < matches.size(); i++)
{
double dis = matches[i].distance;
if (dis < 0.15*Max)
{
//int queryIdx –>是測(cè)試圖像的特征點(diǎn)描述符(descriptor)的下標(biāo),同時(shí)也是描述符對(duì)應(yīng)特征點(diǎn)(keypoint)的下標(biāo)。
goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
//int trainIdx –> 是樣本圖像的特征點(diǎn)描述符的下標(biāo),同樣也是相應(yīng)的特征點(diǎn)的下標(biāo)。
goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
goodmatches.push_back(matches[i]);
}
}
if (draw)
{
Mat result;
drawMatches(imageA, keypointA, imageB, keypointB, goodmatches, result);
imshow("特征匹配", result);
Mat temp_A = imageA.clone();
for (int i = 0; i < goodkeypointA.size(); i++)
{
circle(temp_A, goodkeypointA[i], 3, Scalar(0, 255, 0), -1);
}
imshow("goodkeypointA", temp_A);
Mat temp_B = imageB.clone();
for (int i = 0; i < goodkeypointB.size(); i++)
{
circle(temp_B, goodkeypointB[i], 3, Scalar(0, 255, 0), -1);
}
imshow("goodkeypointB", temp_B);
}
//findHomography計(jì)算單應(yīng)性矩陣至少需要4個(gè)點(diǎn)
/*
計(jì)算多個(gè)二維點(diǎn)對(duì)之間的最優(yōu)單映射變換矩陣H(3x3),使用MSE或RANSAC方法,找到兩平面之間的變換矩陣
*/
if (goodkeypointA.size() < 4 || goodkeypointB.size() < 4) return false;
//獲取圖像A到圖像B的投影映射矩陣,尺寸為3*3
Mat H = findHomography(goodkeypointA, goodkeypointB, RANSAC);
Mat M = (Mat_<double>(3, 3) << 1.0, 0, imageA.cols, 0, 1.0, 0, 0, 0, 1.0);
Mat Homo = M * H;
//進(jìn)行透視變換
Mat DstImg;
warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
imshow("透視變換", DstImg);
imageB.copyTo(DstImg(Rect(imageA.cols, 0, imageB.cols, imageB.rows)));
imshow("圖像全景拼接", DstImg);
return true;
}
6.效果

最終拼接效果如圖所示。
三、源碼
#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/xfeatures2d.hpp>
#include<opencv2/stitching.hpp>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
//1、使用特征檢測(cè)算法找到兩張圖像中相似的點(diǎn),計(jì)算變換矩陣
//2、將A透視變換后得到的圖片與B拼接
bool Image_Stitching(Mat imageA, Mat imageB, bool draw)
{
//創(chuàng)建SURF特征檢測(cè)器
int Hessian = 800;
Ptr<SURF>detector = SURF::create(Hessian);
//進(jìn)行圖像特征檢測(cè)、特征描述
vector<KeyPoint>keypointA, keypointB;
Mat descriptorA, descriptorB;
detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA);
detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB);
//使用FLANN算法進(jìn)行特征描述子的匹配
FlannBasedMatcher matcher;
vector<DMatch>matches;
matcher.match(descriptorA, descriptorB, matches);
double Max = 0.0;
for (int i = 0; i < matches.size(); i++)
{
//float distance –>代表這一對(duì)匹配的特征點(diǎn)描述符(本質(zhì)是向量)的歐氏距離,數(shù)值越小也就說(shuō)明兩個(gè)特征點(diǎn)越相像。
double dis = matches[i].distance;
if (dis > Max)
{
Max = dis;
}
}
//篩選出匹配程度高的關(guān)鍵點(diǎn)
vector<DMatch>goodmatches;
vector<Point2f>goodkeypointA, goodkeypointB;
for (int i = 0; i < matches.size(); i++)
{
double dis = matches[i].distance;
if (dis < 0.15*Max)
{
//int queryIdx –>是測(cè)試圖像的特征點(diǎn)描述符(descriptor)的下標(biāo),同時(shí)也是描述符對(duì)應(yīng)特征點(diǎn)(keypoint)的下標(biāo)。
goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
//int trainIdx –> 是樣本圖像的特征點(diǎn)描述符的下標(biāo),同樣也是相應(yīng)的特征點(diǎn)的下標(biāo)。
goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
goodmatches.push_back(matches[i]);
}
}
if (draw)
{
Mat result;
drawMatches(imageA, keypointA, imageB, keypointB, goodmatches, result);
imshow("特征匹配", result);
Mat temp_A = imageA.clone();
for (int i = 0; i < goodkeypointA.size(); i++)
{
circle(temp_A, goodkeypointA[i], 3, Scalar(0, 255, 0), -1);
}
imshow("goodkeypointA", temp_A);
Mat temp_B = imageB.clone();
for (int i = 0; i < goodkeypointB.size(); i++)
{
circle(temp_B, goodkeypointB[i], 3, Scalar(0, 255, 0), -1);
}
imshow("goodkeypointB", temp_B);
}
//findHomography計(jì)算單應(yīng)性矩陣至少需要4個(gè)點(diǎn)
/*
計(jì)算多個(gè)二維點(diǎn)對(duì)之間的最優(yōu)單映射變換矩陣H(3x3),使用MSE或RANSAC方法,找到兩平面之間的變換矩陣
*/
if (goodkeypointA.size() < 4 || goodkeypointB.size() < 4) return false;
//獲取圖像A到圖像B的投影映射矩陣,尺寸為3*3
Mat H = findHomography(goodkeypointA, goodkeypointB, RANSAC);
Mat M = (Mat_<double>(3, 3) << 1.0, 0, imageA.cols, 0, 1.0, 0, 0, 0, 1.0);
Mat Homo = M * H;
//進(jìn)行透視變換
Mat DstImg;
warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
imshow("透視變換", DstImg);
imageB.copyTo(DstImg(Rect(imageA.cols, 0, imageB.cols, imageB.rows)));
imshow("圖像全景拼接", DstImg);
return true;
}
bool OpenCV_Stitching(Mat imageA, Mat imageB)
{
vector<Mat>images;
images.push_back(imageA);
images.push_back(imageB);
Ptr<Stitcher>stitcher = Stitcher::create();
Mat result;
Stitcher::Status status = stitcher->stitch(images, result);// 使用stitch函數(shù)進(jìn)行拼接
if (status != Stitcher::OK) return false;
imshow("OpenCV圖像全景拼接", result);
return true;
}
int main()
{
Mat imageA = imread("image1.jpg");
Mat imageB = imread("image2.jpg");
if (imageA.empty() || imageB.empty())
{
cout << "No Image!" << endl;
system("pause");
return -1;
}
if (!Image_Stitching(imageA, imageB, true))
{
cout << "can not stitching the image!" << endl;
}
if (!OpenCV_Stitching(imageA, imageB))
{
cout << "can not stitching the image!" << endl;
}
waitKey(0);
system("pause");
return 0;
}
總結(jié)
本文使用OpenCV C++進(jìn)行圖像全景拼接,關(guān)鍵步驟有以下幾點(diǎn)。
1、使用特征檢測(cè)算子提取兩幅圖像的關(guān)鍵點(diǎn),然后進(jìn)行特征描述子匹配。
2、篩選出匹配程度高的關(guān)鍵點(diǎn)計(jì)算兩幅圖的單應(yīng)性矩陣。
3、利用計(jì)算出來(lái)的單應(yīng)性矩陣對(duì)其中一張圖片進(jìn)行透視變換。
4、將透視變換的圖片與另一張圖片進(jìn)行拼接。
以上就是C++ OpenCV實(shí)戰(zhàn)之圖像全景拼接的詳細(xì)內(nèi)容,更多關(guān)于 OpenCV圖像全景拼接的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C++中string使用+號(hào)與int拼接方式
- 詳解C++?OpenCV實(shí)現(xiàn)圖像拼接的原理及方法
- C++實(shí)現(xiàn)批量圖片拼接
- 使用c++實(shí)現(xiàn)OpenCV圖像橫向&縱向拼接
- 基于C++的攝像頭圖像采集及拼接程序的簡(jiǎn)單實(shí)現(xiàn)
- C++ 兩個(gè)vector對(duì)象拼接方式
- C++字符串拼接效率對(duì)比(+=、append、stringstream、sprintf)
- 關(guān)于c++11與c風(fēng)格路徑拼接的速度對(duì)比
- C++使用join拼接字符串的技巧
- C++整數(shù)拼接技巧大揭秘
相關(guān)文章
使用C語(yǔ)言打造通訊錄管理系統(tǒng)和教學(xué)安排系統(tǒng)的代碼示例
這篇文章主要介紹了使用C語(yǔ)言打造通訊錄管理系統(tǒng)和教學(xué)安排系統(tǒng)的代碼示例,利用C語(yǔ)言強(qiáng)大的數(shù)組和指針能夠更加清晰地體現(xiàn)設(shè)計(jì)思路:D 需要的朋友可以參考下2016-06-06
如何在C++類(lèi)的外部調(diào)用類(lèi)的私有方法
這篇文章主要介紹了如何在C++類(lèi)的外部調(diào)用類(lèi)的私有方法,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-09-09
C語(yǔ)言中邏輯運(yùn)算符與條件運(yùn)算符的學(xué)習(xí)教程
這篇文章主要介紹了C語(yǔ)言中邏輯運(yùn)算符與條件運(yùn)算符的學(xué)習(xí)教程,條件運(yùn)算符問(wèn)號(hào)即三目運(yùn)算符使用起來(lái)十分方便,需要的朋友可以參考下2016-04-04
解析內(nèi)存對(duì)齊 Data alignment: Straighten up and fly right的詳解
對(duì)于所有直接操作內(nèi)存的程序員來(lái)說(shuō),數(shù)據(jù)對(duì)齊都是很重要的問(wèn)題.數(shù)據(jù)對(duì)齊對(duì)你的程序的表現(xiàn)甚至能否正常運(yùn)行都會(huì)產(chǎn)生影響2013-05-05
C++實(shí)現(xiàn)LeetCode(642.設(shè)計(jì)搜索自動(dòng)補(bǔ)全系統(tǒng))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(642.設(shè)計(jì)搜索自動(dòng)補(bǔ)全系統(tǒng)),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
用C語(yǔ)言實(shí)現(xiàn)鏈?zhǔn)綏=榻B
大家好,本篇文章主要講的是用C語(yǔ)言實(shí)現(xiàn)鏈?zhǔn)綏=榻B,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12
C語(yǔ)言深入探索浮點(diǎn)數(shù)的使用秘密
在C語(yǔ)言中,浮點(diǎn)數(shù)是一個(gè)很重要的類(lèi)型,浮點(diǎn)數(shù)可以使數(shù)據(jù)更為精確。浮點(diǎn)數(shù)說(shuō)白了就是帶有小數(shù)點(diǎn)的數(shù)。比如1.6?0.0000?765.2等等,浮點(diǎn)數(shù)具體是怎么用的呢,讓我們一起來(lái)看看2022-04-04
如何使用C語(yǔ)言實(shí)現(xiàn)平衡二叉樹(shù)數(shù)據(jù)結(jié)構(gòu)算法
對(duì)于判斷是否為平衡二叉樹(shù)而言,我們需要知道以下特性:是一個(gè)二叉樹(shù)也是一個(gè)二叉排序樹(shù)該樹(shù)的每個(gè)結(jié)點(diǎn)上的(深度)左子樹(shù) - 右子樹(shù)的值為平衡因子(BF(Balance Factor))該樹(shù)的每一個(gè)節(jié)點(diǎn)的左子樹(shù)和右子樹(shù)的高度至多等于1(平衡因子只可能是-1,0,1)2021-08-08

