Opencv2.4.9源码分析——Stitching(八)

8、完整的拼接程序

 

下面给出完整的拼接程序:

[cpp]  view plain  copy
  1. #include "opencv2/core/core.hpp"  
  2. #include "highgui.h"  
  3. #include "opencv2/imgproc/imgproc.hpp"  
  4. #include "opencv2/features2d/features2d.hpp"  
  5. #include "opencv2/nonfree/nonfree.hpp"  
  6. #include "opencv2/legacy/legacy.hpp"  
  7.   
  8. #include "opencv2/stitching/detail/autocalib.hpp"  
  9. #include "opencv2/stitching/detail/blenders.hpp"  
  10. #include "opencv2/stitching/detail/camera.hpp"  
  11. #include "opencv2/stitching/detail/exposure_compensate.hpp"  
  12. #include "opencv2/stitching/detail/matchers.hpp"  
  13. #include "opencv2/stitching/detail/motion_estimators.hpp"  
  14. #include "opencv2/stitching/detail/seam_finders.hpp"  
  15. #include "opencv2/stitching/detail/util.hpp"  
  16. #include "opencv2/stitching/detail/warpers.hpp"  
  17. #include "opencv2/stitching/warpers.hpp"  
  18.   
  19. #include <iostream>  
  20. #include <fstream>   
  21. #include <string>  
  22. #include <iomanip>   
  23. using namespace cv;  
  24. using namespace std;  
  25. using namespace detail;  
  26.   
  27. int main(int argc, char** argv)  
  28. {  
  29.    vector<Mat> imgs;    //输入9幅图像  
  30.    Mat img;  
  31.    img = imread("1.jpg");     
  32.    imgs.push_back(img);  
  33.    img = imread("2.jpg");     
  34.    imgs.push_back(img);  
  35.    img = imread("3.jpg");     
  36.    imgs.push_back(img);  
  37.    img = imread("4.jpg");     
  38.    imgs.push_back(img);  
  39.    img = imread("5.jpg");     
  40.    imgs.push_back(img);  
  41.    img = imread("6.jpg");     
  42.    imgs.push_back(img);   
  43.    img = imread("7.jpg");     
  44.    imgs.push_back(img);  
  45.    img = imread("8.jpg");     
  46.    imgs.push_back(img);  
  47.    img = imread("9.jpg");     
  48.    imgs.push_back(img);  
  49.   
  50.    int num_images = 9;    //图像数量  
  51.   
  52.    Ptr<FeaturesFinder> finder;    //定义特征寻找器  
  53.    finder = new SurfFeaturesFinder();    //应用SURF方法寻找特征  
  54.    //finder = new OrbFeaturesFinder();    //应用ORB方法寻找特征  
  55.    vector<ImageFeatures> features(num_images);    //表示图像特征  
  56.    for (int i =0 ;i<num_images;i++)  
  57.       (*finder)(imgs[i], features[i]);    //特征检测  
  58.   
  59.    vector<MatchesInfo> pairwise_matches;    //表示特征匹配信息变量  
  60.    BestOf2NearestMatcher matcher(false, 0.3f, 6, 6);    //定义特征匹配器,2NN方法  
  61.    matcher(features, pairwise_matches);    //进行特征匹配  
  62.   
  63.    HomographyBasedEstimator estimator;    //定义参数评估器  
  64.    vector<CameraParams> cameras;    //表示相机参数  
  65.    estimator(features, pairwise_matches, cameras);    //进行相机参数评估  
  66.   
  67.    for (size_t i = 0; i < cameras.size(); ++i)    //转换相机旋转参数的数据类型  
  68.    {  
  69.       Mat R;  
  70.       cameras[i].R.convertTo(R, CV_32F);  
  71.       cameras[i].R = R;    
  72.    }  
  73.   
  74.    Ptr<detail::BundleAdjusterBase> adjuster;    //光束平差法,精确相机参数  
  75.    adjuster = new detail::BundleAdjusterReproj();    //重映射误差方法  
  76.    //adjuster = new detail::BundleAdjusterRay();    //射线发散误差方法  
  77.       
  78.    adjuster->setConfThresh(1);    //设置匹配置信度,该值设为1  
  79.    (*adjuster)(features, pairwise_matches, cameras);    //精确评估相机参数  
  80.   
  81.    vector<Mat> rmats;  
  82.    for (size_t i = 0; i < cameras.size(); ++i)    //复制相机的旋转参数  
  83.       rmats.push_back(cameras[i].R.clone());  
  84.    waveCorrect(rmats, WAVE_CORRECT_HORIZ);    //进行波形校正  
  85.    for (size_t i = 0; i < cameras.size(); ++i)    //相机参数赋值  
  86.       cameras[i].R = rmats[i];  
  87.    rmats.clear();    //清变量  
  88.   
  89.    vector<Point> corners(num_images);    //表示映射变换后图像的左上角坐标  
  90.    vector<Mat> masks_warped(num_images);    //表示映射变换后的图像掩码  
  91.    vector<Mat> images_warped(num_images);    //表示映射变换后的图像  
  92.    vector<Size> sizes(num_images);    //表示映射变换后的图像尺寸  
  93.    vector<Mat> masks(num_images);    //表示源图的掩码  
  94.   
  95.    for (int i = 0; i < num_images; ++i)    //初始化源图的掩码  
  96.    {  
  97.       masks[i].create(imgs[i].size(), CV_8U);    //定义尺寸大小  
  98.       masks[i].setTo(Scalar::all(255));    //全部赋值为255,表示源图的所有区域都使用  
  99.    }  
  100.   
  101.    Ptr<WarperCreator> warper_creator;    //定义图像映射变换创造器  
  102.    warper_creator = new cv::PlaneWarper();    //平面投影  
  103.    //warper_creator = new cv::CylindricalWarper();    //柱面投影  
  104.    //warper_creator = new cv::SphericalWarper();    //球面投影  
  105.    //warper_creator = new cv::FisheyeWarper();    //鱼眼投影  
  106.    //warper_creator = new cv::StereographicWarper();    //立方体投影  
  107.   
  108.    //定义图像映射变换器,设置映射的尺度为相机的焦距,所有相机的焦距都相同  
  109.    Ptr<RotationWarper> warper = warper_creator->create(static_cast<float>(cameras[0].focal));  
  110.    for (int i = 0; i < num_images; ++i)  
  111.     {  
  112.       Mat_<float> K;  
  113.       cameras[i].K().convertTo(K, CV_32F);    //转换相机内参数的数据类型  
  114.       //对当前图像镜像投影变换,得到变换后的图像以及该图像的左上角坐标  
  115.       corners[i] = warper->warp(imgs[i], K, cameras[i].R, INTER_LINEAR, BORDER_REFLECT, images_warped[i]);  
  116.       sizes[i] = images_warped[i].size();    //得到尺寸  
  117.       //得到变换后的图像掩码  
  118.       warper->warp(masks[i], K, cameras[i].R, INTER_NEAREST, BORDER_CONSTANT, masks_warped[i]);  
  119.    }  
  120.       
  121.    imgs.clear();    //清变量  
  122.    masks.clear();  
  123.   
  124.    //创建曝光补偿器,应用增益补偿方法  
  125.    Ptr<ExposureCompensator> compensator =         
  126.                      ExposureCompensator::createDefault(ExposureCompensator::GAIN);  
  127.    compensator->feed(corners, images_warped, masks_warped);    //得到曝光补偿器  
  128.    for(int i=0;i<num_images;++i)    //应用曝光补偿器,对图像进行曝光补偿  
  129.    {  
  130.       compensator->apply(i, corners[i], images_warped[i], masks_warped[i]);  
  131.    }  
  132.   
  133.    //在后面,我们还需要用到映射变换图的掩码masks_warped,因此这里为该变量添加一个副本masks_seam  
  134.    vector<Mat> masks_seam(num_images);   
  135.    for(int i = 0; i<num_images;i++)  
  136.       masks_warped[i].copyTo(masks_seam[i]);  
  137.   
  138.     Ptr<SeamFinder> seam_finder;    //定义接缝线寻找器  
  139.    //seam_finder = new NoSeamFinder();    //无需寻找接缝线  
  140.    //seam_finder = new VoronoiSeamFinder();    //逐点法  
  141.    //seam_finder = new DpSeamFinder(DpSeamFinder::COLOR);    //动态规范法  
  142.    //seam_finder = new DpSeamFinder(DpSeamFinder::COLOR_GRAD);  
  143.    //图割法  
  144.    //seam_finder = new GraphCutSeamFinder(GraphCutSeamFinder::COST_COLOR);  
  145.    seam_finder = new GraphCutSeamFinder(GraphCutSeamFinder::COST_COLOR_GRAD);  
  146.   
  147.    vector<Mat> images_warped_f(num_images);  
  148.    for (int i = 0; i < num_images; ++i)    //图像数据类型转换  
  149.       images_warped[i].convertTo(images_warped_f[i], CV_32F);  
  150.   
  151.    images_warped.clear();    //清内存  
  152.   
  153.    //得到接缝线的掩码图像masks_seam  
  154.    seam_finder->find(images_warped_f, corners, masks_seam);   
  155.       
  156.    vector<Mat> images_warped_s(num_images);  
  157.    Ptr<Blender> blender;    //定义图像融合器  
  158.      
  159.    //blender = Blender::createDefault(Blender::NO, false);    //简单融合方法  
  160.    //羽化融合方法  
  161.    //blender = Blender::createDefault(Blender::FEATHER, false);  
  162.    //FeatherBlender* fb = dynamic_cast<FeatherBlender*>(static_cast<Blender*>(blender));  
  163.    //fb->setSharpness(0.005);    //设置羽化锐度  
  164.   
  165.    blender = Blender::createDefault(Blender::MULTI_BAND, false);    //多频段融合  
  166.    MultiBandBlender* mb = dynamic_cast<MultiBandBlender*>(static_cast<Blender*>(blender));  
  167.    mb->setNumBands(8);   //设置频段数,即金字塔层数  
  168.   
  169.    blender->prepare(corners, sizes);    //生成全景图像区域  
  170.   
  171.    //在融合的时候,最重要的是在接缝线两侧进行处理,而上一步在寻找接缝线后得到的掩码的边界就是接缝线处,因此我们还需要在接缝线两侧开辟一块区域用于融合处理,这一处理过程对羽化方法尤为关键  
  172.    //应用膨胀算法缩小掩码面积  
  173.    vector<Mat> dilate_img(num_images);    
  174.    Mat element = getStructuringElement(MORPH_RECT, Size(20, 20));    //定义结构元素  
  175.    for(int k=0;k<num_images;k++)  
  176.    {  
  177.       images_warped_f[k].convertTo(images_warped_s[k], CV_16S);    //改变数据类型  
  178.       dilate(masks_seam[k], masks_seam[k], element);    //膨胀运算  
  179.       //映射变换图的掩码和膨胀后的掩码相“与”,从而使扩展的区域仅仅限于接缝线两侧,其他边界处不受影响  
  180.       masks_seam[k] = masks_seam[k] & masks_warped[k];  
  181.       blender->feed(images_warped_s[k], masks_seam[k], corners[k]);    //初始化数据  
  182.    }  
  183.   
  184.    masks_seam.clear();    //清内存  
  185.    images_warped_s.clear();  
  186.    masks_warped.clear();  
  187.    images_warped_f.clear();  
  188.   
  189.    Mat result, result_mask;  
  190.    //完成融合操作,得到全景图像result和它的掩码result_mask  
  191.    blender->blend(result, result_mask);  
  192.   
  193.    imwrite("pano.jpg", result);    //存储全景图像  
  194.   
  195.    return 0;  
  196. }  

最终的输出图像为:

图18 平面映射全景图像

输入的9幅图像的尺寸都为979×550,最终共耗时10分钟左右。全景拼接程序十分消耗内存空间,如果图像的尺寸较大,而且图像数量较多的话,不仅耗时较长,而且很可能由于内存不足而报错。另外,经过多次实验看出,如果图像尺寸大、数量多,还会引起拼接不正确的现象。

下面几幅图像分别为不同映射得到的全景图像。

图19 柱面映射全景图像

图20 球面映射全景图像

图21 鱼眼映射全景图像(该图调整了角度,以便于显示观看)

图22 立体映射全景图像(该图调整了角度,以便于显示观看)

原文地址:https://blog.csdn.net/zhaocj/article/details/78967041


opencv是一个开源的计算机视觉库,opencv2.4.9是其中的一个版本。在opencv2.4.9中,有一个模块叫做stitching,用于图像拼接。 图像拼接是将多张图像按照一定的顺序和方式进行合并,形成一张更大视野覆盖范围的图像。拼接的过程需要解决图像间的重叠区域匹配、图像变换与叠加等问题。 在opencv2.4.9stitching模块中,主要有以下几个重要的类: 1. Stitcher类:拼接器类,用于执行拼接的主要操作。它提供了一系列的方法,如设置拼接的模式、添加要拼接的图像等。 2. FeaturesFinder类:特征点检测类,用于在图像中寻找特征点。该类利用SIFT、SURF等算法来检测图像中的关键点,以便进行匹配。 3. FeaturesMatcher类:特征点匹配类,用于对图像中的特征点进行匹配。该类使用KNN算法进行特征点的匹配,并利用RANSAC算法进一步筛选特征点,剔除误匹配。 4. Estimator类:变换估计类,用于估计图像间的变换参数。该类可以通过特征点的对应关系,计算图像间的旋转矩阵、平移矩阵等变换参数。 5. Blender类:图像融合类,用于将拼接后的图像进行融合。该类可以进行多种融合方式,如线性融合、多频融合等。 通过以上的类和方法,opencv2.4.9stitching模块能够完成图像拼接的过程。整个过程包括特征点检测、特征点匹配、变换参数估计和图像融合等步骤。 需要指出的是,本文只是对opencv2.4.9stitching模块进行了初步的介绍,具体的源码分析需要深入研究。整个源码工程庞大,包含很多细节和算法,需要对计算机视觉和图像处理有较深入的理解才能进行分析和改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值