本文参考:/zhaocj/article/details/78798687
多张图像拼接:参考OpenCV4.1.1帮助文档中Examples—<samples/cpp/stitching_detailed.cpp>
【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_warpsizes[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;}
Standing up to your own...That would take a real hero.
能勇敢地坚持自己,那才是真正的英雄。
如果觉得《【OpenCV】 全景拼接——多张图像拼接》对你有帮助,请点赞、收藏,并留下你的观点哦!