OpenCV C++案例實戰十二《影像全景拼接》
- 前言
- 一、OpenCV Stitcher
- 1.功能原始碼
- 2.效果
- 二、影像全景拼接
- 1.特征檢測
- 2.計算單應性矩陣
- 3.透視變換
- 4.影像拼接
- 5.功能原始碼
- 6.效果
- 三、原始碼
- 總結
前言
本文將使用OpenCV C++ 進行影像全景拼接,目前使用OpenCV對兩幅影像進行拼接大致可以分為兩類,
一、使用OpenCV內置API Stitcher 進行拼接,
二、使用特征檢測演算法匹配兩幅圖中相似的點、計算變換矩陣、最后對其進行透視變換就可以了,
一、OpenCV Stitcher
imageA

imageB

原圖如圖所示,本案例的需求是將上述兩幅圖片拼接成一幅影像,首先使用OpenCV提供的Stitcher進行拼接,關于Stitcher的具體原理請大家自行查找相關資料,
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函式進行拼接
if (status != Stitcher::OK) return false;
imshow("OpenCV影像全景拼接", result);
return true;
}
2.效果

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

如圖為使用FLANN演算法進行特征描述子匹配的結果,我們需要把那些匹配程度高的關鍵點篩選出來用以下面計算兩幅影像的單應性矩陣,
2、篩選出匹配程度高的關鍵點
double Max = 0.0;
for (int i = 0; i < matches.size(); i++)
{
//float distance –>代表這一對匹配的特征點描述符(本質是向量)的歐氏距離,數值越小也就說明兩個特征點越相像,
double dis = matches[i].distance;
if (dis > Max)
{
Max = dis;
}
}
//篩選出匹配程度高的關鍵點
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 –>是測驗影像的特征點描述符(descriptor)的下標,同時也是描述符對應特征點(keypoint)的下標,
goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
//int trainIdx –> 是樣本影像的特征點描述符的下標,同樣也是相應的特征點的下標,
goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
goodmatches.push_back(matches[i]);
}
}
如圖為imageA篩選出來的關鍵點,

如圖為imageB篩選出來的關鍵點,

從上圖可以看出,我們已經篩選出imageA,imageB共有的關鍵點部分,接下來,我們需要使用這兩個點集計算兩幅圖的單應性矩陣,
2.計算單應性矩陣
3、計算單應性變換矩陣
//獲取影像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.透視變換
4、根據計算出來的單應性矩陣對imageA進行透視變換
//進行透視變換
Mat DstImg;
warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
imshow("透視變換", DstImg);

如圖所示為imageA進行透視變換得到的結果,
4.影像拼接
5、根據上述操作,我們已經得到了經透視變換的imageA,接下來只需將imageA與imageB拼接起來就可以了,
imageB.copyTo(DstImg(Rect(imageA.cols, 0, imageB.cols, imageB.rows)));
imshow("影像全景拼接", DstImg);
5.功能原始碼
bool Image_Stitching(Mat imageA, Mat imageB, bool draw)
{
//創建SURF特征檢測器
int Hessian = 800;
Ptr<SURF>detector = SURF::create(Hessian);
//進行影像特征檢測、特征描述
vector<KeyPoint>keypointA, keypointB;
Mat descriptorA, descriptorB;
detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA);
detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB);
//使用FLANN演算法進行特征描述子的匹配
FlannBasedMatcher matcher;
vector<DMatch>matches;
matcher.match(descriptorA, descriptorB, matches);
double Max = 0.0;
for (int i = 0; i < matches.size(); i++)
{
//float distance –>代表這一對匹配的特征點描述符(本質是向量)的歐氏距離,數值越小也就說明兩個特征點越相像,
double dis = matches[i].distance;
if (dis > Max)
{
Max = dis;
}
}
//篩選出匹配程度高的關鍵點
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 –>是測驗影像的特征點描述符(descriptor)的下標,同時也是描述符對應特征點(keypoint)的下標,
goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
//int trainIdx –> 是樣本影像的特征點描述符的下標,同樣也是相應的特征點的下標,
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計算單應性矩陣至少需要4個點
/*
計算多個二維點對之間的最優單映射變換矩陣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;
//進行透視變換
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、使用特征檢測演算法找到兩張影像中相似的點,計算變換矩陣
//2、將A透視變換后得到的圖片與B拼接
bool Image_Stitching(Mat imageA, Mat imageB, bool draw)
{
//創建SURF特征檢測器
int Hessian = 800;
Ptr<SURF>detector = SURF::create(Hessian);
//進行影像特征檢測、特征描述
vector<KeyPoint>keypointA, keypointB;
Mat descriptorA, descriptorB;
detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA);
detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB);
//使用FLANN演算法進行特征描述子的匹配
FlannBasedMatcher matcher;
vector<DMatch>matches;
matcher.match(descriptorA, descriptorB, matches);
double Max = 0.0;
for (int i = 0; i < matches.size(); i++)
{
//float distance –>代表這一對匹配的特征點描述符(本質是向量)的歐氏距離,數值越小也就說明兩個特征點越相像,
double dis = matches[i].distance;
if (dis > Max)
{
Max = dis;
}
}
//篩選出匹配程度高的關鍵點
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 –>是測驗影像的特征點描述符(descriptor)的下標,同時也是描述符對應特征點(keypoint)的下標,
goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
//int trainIdx –> 是樣本影像的特征點描述符的下標,同樣也是相應的特征點的下標,
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計算單應性矩陣至少需要4個點
/*
計算多個二維點對之間的最優單映射變換矩陣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;
//進行透視變換
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函式進行拼接
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;
}
總結
本文使用OpenCV C++進行影像全景拼接,關鍵步驟有以下幾點,
1、使用特征檢測算子提取兩幅影像的關鍵點,然后進行特征描述子匹配,
2、篩選出匹配程度高的關鍵點計算兩幅圖的單應性矩陣,
3、利用計算出來的單應性矩陣對其中一張圖片進行透視變換,
4、將透視變換的圖片與另一張圖片進行拼接,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/401502.html
標籤:其他
上一篇:003-OpenCV簡單讀取視頻
下一篇:神經網路開發食譜
