多张图像拼接:参考OpenCV4.1.1帮助文档中Examples—<samples/cpp/stitching_detailed.cpp>
stitching_detailed 图像拼接流程
【stitching_detailed程序运行流程】
1.命令行调用程序,输入源图像以及程序的参数
2.特征点检测,判断是使用surf还是orb,默认是surf。
3.对图像的特征点进行匹配,使用最近邻方法,将最优的匹配的置信度保存下来,同时保存两幅图像匹配特征点的单应性矩阵。
4.删除置信度比较低的图像间的匹配,利用并查集算法,确保匹配图像的拼接集。
5.对所有拼接集图像进行相机参数粗略估计,然后求出旋转矩阵。
6.使用光束平均法进一步精准的估计出旋转矩阵。
7.波形校正,水平或者垂直。
8.曝光补偿器、接缝寻找器。
9.融合,多频段融合,全景图。
通过对【stitching_detailed源代码】的调整,加入拼接图片得到了全景图。具体代码请移步:【OpenCV】全景拼接_多张图像拼接
【运行结果】
没有使用 曝光补偿器 和 接缝拼接器。
【程序代码】
#include <iostream>
#include <fstream>
#include <string>
#include "opencv2/opencv_modules.hpp"
#include <opencv2/core/utility.hpp>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/stitching/detail/autocalib.hpp"
#include "opencv2/stitching/detail/blenders.hpp"
#include "opencv2/stitching/detail/timelapsers.hpp"
#include "opencv2/stitching/detail/camera.hpp"
#include "opencv2/stitching/detail/exposure_compensate.hpp"
#include "opencv2/stitching/detail/matchers.hpp"
#include "opencv2/stitching/detail/motion_estimators.hpp"
#include "opencv2/stitching/detail/seam_finders.hpp"
#include "opencv2/stitching/detail/warpers.hpp"
#include "opencv2/stitching/warpers.hpp"
#include "opencv2/xfeatures2d/nonfree.hpp"
using namespace cv;
using namespace std;
using namespace cv::detail;
int main()
{
//获取图片路径
vector<String>image_names; //所有图片名字
String filepath = "C:\\Users\\Desktop\\photo\\*.jpg"; //图片存储路径
glob(filepath, image_names, false);
size_t num_images = image_names.size(); //图片数量
cout << "检索到的图片为:" << endl;
for (int i = 0; i < num_images; ++i)
{
cout << "Image #" <<i+1<<": "<< image_names[i]<<endl;
}
cout << endl;
//存储图像、尺寸、特征点
vector<ImageFeatures> features(num_images); //存储图像特征点
vector<Mat> images(num_images); //存储所有图像
vector<Size> images_sizes(num_images); //存储图像的尺寸
Ptr<Feature2D> featurefinder = xfeatures2d::SIFT::create();//特征点检测方法
for (int i = 0; i < num_images; ++i)
{
images[i] = cv::imread(samples::findFile(image_names[i]));//读取每一张图片
images_sizes[i] = images[i].size();
computeImageFeatures(featurefinder, images[i], features[i]); //计算图像特征
//features[i].img_idx = i;
cout << "image #" << i + 1 << "特征点为: " << features[i].keypoints.size() << " 个"<<" "<< "尺寸为: " << images_sizes[i] << endl;
}
cout << endl;
//图像特征点匹配
vector<MatchesInfo> pairwise_matches; //表示特征匹配信息变量
Ptr<FeaturesMatcher> matcher = makePtr<BestOf2NearestMatcher>(false, 0.3f, 6, 6); //定义特征匹配器,2NN方法
(*matcher)(features, pairwise_matches); //进行特征匹配
//预估相机参数
Ptr<Estimator> estimator;
estimator = makePtr<HomographyBasedEstimator>(); //水平估计
vector<CameraParams> cameras; //相机参数素组
(*estimator)(features, pairwise_matches, cameras); //得到相机参数
cout << "预估相机参数:" << endl;
for (size_t i = 0; i < cameras.size(); ++i)
{
Mat R;
cameras[i].R.convertTo(R, CV_32F);
cameras[i].R = R;
cout << "camera #" << i + 1 << ":\n内参数矩阵K:\n" << cameras[i].K() << "\n旋转矩阵R:\n" << cameras[i].R << "\n焦距focal: " << cameras[i].focal << endl;
}
cout << endl;
//光束平差,精确相机参数
Ptr<detail::BundleAdjusterBase> adjuster;
adjuster = makePtr<detail::BundleAdjusterRay>();
(*adjuster)(features, pairwise_matches, cameras);
cout << "精确相机参数" << endl;
for (size_t i = 0; i < cameras.size(); ++i)
{
Mat R;
cameras[i].R.convertTo(R, CV_32F);
cameras[i].R = R;
cout << "camera #" << i + 1 << ":\n内参数矩阵K:\n" << cameras[i].K() << "\n旋转矩阵R:\n" << cameras[i].R << "\n焦距focal: " << cameras[i].focal << endl;
}
cout << endl;
//波形矫正
vector<Mat> mat;
for (size_t i = 0; i < cameras.size(); ++i)
mat.push_back(cameras[i].R);
waveCorrect(mat, WAVE_CORRECT_HORIZ); //水平校正
for (size_t i = 0; i < cameras.size(); ++i)
cameras[i].R = mat[i];
cout << endl;
//创建mask图像
vector<Mat> masks(num_images);
for (int i = 0; i < num_images; ++i)
{
masks[i].create(images[i].size(), CV_8U);
masks[i].setTo(Scalar::all(255));
}
//图像、掩码变换
vector<Mat> masks_warp(num_images); //mask扭曲
vector<Mat> images_warp(num_images); //图像扭曲
vector<Point> corners(num_images); //图像左角点
vector<Size> sizes(num_images); //图像尺寸
Ptr<WarperCreator> warper_creator=makePtr<cv::CylindricalWarper>(); //柱面投影
Ptr<RotationWarper> warper = warper_creator->create(static_cast<float>(cameras[0].focal)); //因为图像焦距都一样
for (int i = 0; i < num_images; ++i)
{
Mat K;
cameras[i].K().convertTo(K, CV_32F);
corners[i] = warper->warp(images[i], K, cameras[i].R, INTER_LINEAR, BORDER_REFLECT, images_warp[i]); //扭曲图像images->images_warp
sizes[i] = images_warp[i].size();
warper->warp(masks[i], K, cameras[i].R, INTER_NEAREST, BORDER_CONSTANT, masks_warp[i]); //扭曲masks->masks_warped
}
for (int i = 0; i < num_images; ++i)
{
cout << "Image #" << i + 1 << " corner: " << corners[i] << " " << "size: " << sizes[i] << endl;
}
cout << endl;
//图像融合
Ptr<Blender> blender; //定义图像融合器
blender = Blender::createDefault(Blender::NO, false); //简单融合方法
blender->prepare(corners, sizes); //生成全景图像区域
for (int i = 0; i < num_images; ++i)
{
images_warp[i].convertTo(images_warp[i], CV_16S);
blender->feed(images_warp[i], masks_warp[i], corners[i]); //处理图像 初始化数据
}
Mat result, result_mask;
blender->blend(result, result_mask); //blend( InputOutputArray dst, InputOutputArray dst_mask )混合并返回最后的pano。
imwrite("result.jpg", result);
return 0;
}