【OpenCV】 全景拼接——两张图像拼接
程序员文章站
2022-07-13 14:10:48
...
上回文章写道opencv_contrib安装,为什么安装呢?为了使用SIFT函数。为什么使用SIFT函数?为了进行特征点检测。为什么进行特征点检测?为了图像拼接。所以呢,这段时间我在学习全景拼接。
全景拼接可以分为两张图像拼接和多张图像拼接(大于两张)。因为每幅图像是相机在不同角度下拍摄得到的,它们并不在同一个投影平面上,如果对重叠部分直接进行拼接,则会破坏实际场景的视觉一致性。所以对于多张图像我们需要在拼接之前,对图像进行投影变换,即对图像进行扭曲变形。
两张图像拼接:可以采用单应性矩阵(cv:: findHomography)。例如以左图为基础将右图进行单应性变换,与左图拼接,这样一来右图就会因为单应性变换分辨率降低变得模糊(详见:运行结果(由于图像上传大小限制,效果不明显))。多张图并不是不可以使用单应性,而是图像越多,拼接扭曲越厉害,惨不忍睹啊。
多张图像拼接:
【开发环境】:
软件版本:OpenCV-4.1.1、opencv_contrib-4.1.1、Visual Studio 2015
操作系统:Win10
【源代码】:
#include <opencv2\opencv.hpp>
#include <vector>
#include <opencv2\xfeatures2d.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img1, img2, img_gray1, img_gray2;
Mat L_Rmatcher;
Mat left_view, result;
img1 = imread("C:\\Users\\Administrator\\Desktop\\p2.jpg", 1); //右图
img2 = imread("C:\\Users\\Administrator\\Desktop\\p1.jpg", 1); //左图
vector<Mat> pic{ img1,img2 };
//灰度图
cvtColor(img1, img_gray1, COLOR_BGR2GRAY);
cvtColor(img2, img_gray2, COLOR_BGR2GRAY);
//SIFT特征检测器,计算关键点
Ptr<Feature2D> sift = xfeatures2d::SIFT::create();
//检测关键点
vector<KeyPoint> keypoints1, keypoints2;
sift->detect(img_gray1, keypoints1);
sift->detect(img_gray2, keypoints2);
//计算描述子
Mat descriptors1, descriptors2;
sift->compute(img_gray1, keypoints1, descriptors1);
sift->compute(img_gray2, keypoints2, descriptors2);
//快速近似最邻近 匹配方法寻找匹配点
FlannBasedMatcher matcher;
vector<vector<DMatch> > matchePoints;
vector<DMatch> GoodMatchePoints;
vector<Mat> train_desc(1, descriptors1);
matcher.add(train_desc); //添加训练描述符
matcher.knnMatch(descriptors2, matchePoints, 2);
cout << "total match points: " << matchePoints.size() << endl;
// Lowe's algorithm,获取优秀匹配点
for (int i = 0; i < matchePoints.size(); i++)
{
if (matchePoints[i][0].distance < 0.4 * matchePoints[i][1].distance) //atio=0. 4:对于准确度要求高的匹配; ratio = 0. 6:对于匹配点数目要求比较多的匹配; ratio = 0. 5:一般情况下。
{
GoodMatchePoints.push_back(matchePoints[i][0]);
}
}
//显示关键点
drawKeypoints(img1, keypoints1, descriptors1, cv::Scalar::all(-1));
drawKeypoints(img2, keypoints2, descriptors2, cv::Scalar::all(-1));
//关键点匹配
drawMatches(img2, keypoints2, img1, keypoints1, GoodMatchePoints, L_Rmatcher, cv::Scalar::all(-1), cv::Scalar(0, 0, 255));
vector<Point2f>kp1, kp2;
for (int i = 0; i < GoodMatchePoints.size(); i++)
{
kp2.push_back(keypoints2[GoodMatchePoints[i].queryIdx].pt);
kp1.push_back(keypoints1[GoodMatchePoints[i].trainIdx].pt);
}
Mat homography = findHomography(kp1, kp2, cv::noArray(), RANSAC);
cout << "变换矩阵为:\n" << homography << endl << endl;
//歪曲右图到左图
// 右边的图像经过矩阵H转换到stitchedImage中对应的位置
warpPerspective(img1, result, homography, Size(img2.cols + img1.cols, img2.rows));
//warpPerspective(输入图像,输出图像,3*3矩阵,最终图像尺寸,)
imshow("左图视角的有图", result);
//赋值右图到整幅图像的前半部分
//把左边的图像放进来
Mat half(result, Rect(0, 0, img2.cols, img2.rows));
//复制图2到图1的ROI区域
img2.copyTo(half);
imshow("左图-img2", descriptors2);
imshow("右图-img1", descriptors1);
imshow("匹配线", L_Rmatcher);
imshow("Result", result);
cv::imwrite("Result.jpg", result);
waitKey(0);
return 0;
}
【运行结果】
You believe in fate?
你相信命运吗?