全景拼接

        图像和视频缝合 在全景图生成、360°全景相机以及VR全景领域有非常多的应用,常用的图像缝合工具有Microsoft的ICE、PTGui、开源软件Hugin等,基于视频的拼接可以参考VideoStitch、StitcHD (github.com/lukeyeager/StitcHD)以及stitching_with_cuda。

        图像缝合的算法步骤可以描述为:

Step1. 定义映射模型,常用的包括:球面、柱面、平面,其中球面映射应用最为广泛;

Step2. 根据输入图像,提取特征点,对特征进行匹配,得到输入图像之间的映射关系T;

Step3. 根据映射关系T进行图像的Warp变换,对齐图像;

Step4. 包括利用颜色调整来消除图像间的色差,和采用图像融合来消除拼缝。

        算法流程图描述为:


        我们对算法每一步进行展开,并在此基础上进行讨论。

•   映射模型

        映射模型可以看作是用于图像映射的载体,相当于二维图像映射到三维空间的一种变换,如下图所示:


        其对应缝合效果如下图所示:

        选择合适的映射模型非常重要,需要与你的图像采集场景以及应用方式相匹配,一般对于水平拼接,采用柱面映射描述性最佳,而对于360°全景,球面映射或者立方体(多面体)映射的效果更好。

        上图中我们采用的即是三张平行拍摄图片,由于垂直方向张角较小,因此球面模型与柱面模型的效果实际差异不大,大家可以用OpenCV的Stitching模块自己感觉一下。


•   特征点提取与匹配

        对于自动拼接来讲,特征点提取是必不可少的一步操作,结合前面所提到的特征,选取常用的特征描述子(可以选择SIFT、Surf、Orb等),大部分图像缝合不涉及尺度问题,从速度考虑ORB通常是比较好的选择。

        特征匹配用来计算图像之间的映射关系(采用RANSAC或者概率模型),得到每个匹配图像对之间的单应矩阵,结合上一步的映射模型,我们可以得到最终的图像变换序列 T1T2T3…。

        在计算映射变换之后,实际上我们就得到了图像之间的全景变换关系,或者叫做相机变换参数(参照OpenCV里的detail::CameraParams),对于视频拼接来讲,这个变换参数通常是不变的。

        另外也可以利用手动的方式进行调整,只要确保图像之间的对齐即可,因此特征的提取与匹配并不是必选项。


•   图像Warp变换

        采用矩阵的形式描述为:

        矩阵H 被称为投影变换矩阵。对上式进行分解化简,即是通常所谓的单应性变换:


        图像Warp变换是一项耗时的操作,相当于对里面每一个像素点进行一次变换,像素之间的操作相互独立,因此操作通常放在GPU上来并行处理,对于CUDA来讲,透视变换相当于将输入图像的索引坐标值映射到纹理坐标。


•   色差调整

        关于色差调整文献较多,可以从自动白平衡方向入手,也可以手动调整色温,其基本思路都是通过统计每幅图的颜色区间分布,对于不同的颜色进行调整到与参考颜色一致的空间内。

        比较常用的是Reinhard方法,将图像 I 转换到lab 空间(降低三原色之间的相关性),通过图像的统计分析,利用目标图像 I' 的 均值 及 标准差 进行线性调整,公式描述为:


        线性变换(方差作为斜率)能够保证源图像能够与目标图像在 lab 颜色空间具有近似相同的均值和方差。通常我们可以选定一副图像作为调整基准,当然也可以计算需要变换的所有图像的均值作为一个目标值,需要由使用者来决定。


•   图像融合

        融合目的在于拼缝消除,这里考虑常用的三种方式,Alpha平均、羽化融合和Multi-Band融合方法。从效果上来讲,Multi-Band能够达到比较好的融合效果,当然效率也低,这里我们采用的方法称为Laplacian(拉普拉斯)金字塔。可以理解为通过对相邻两层的高斯金字塔进行差分,将原图分解(Reduce)成不同尺度(频率)的子图,对每一个之图(对应不同频带)进行加权平均,得到每一层的融合结果,最后进行金字塔的反向重建(Expand),得到最终融合效果。

        关于图像融合,可以搜到很多现成的代码,这里不再过多描述。对于融合的关键在于选择用于融合的子图像区域部分,要注意图像过大导致的效率问题,也要避免图像较小带来的信息缺失。

        OpenCV的代码是视频缝合比较好的入门材料,大家可以前期参考,当然后面可以根据自己的侧重对代码进行效率或者效果上的优化,比如打造一个轻量级的Stitching库、采用CUDA或者OpenCL进行高效的实现等等。

        OpenCV的流程框架图如下:

        OpenCV代码实现及注释:

[cpp]  view plain  copy
  1. // 拼接,use gpu  
  2.     Stitcher stitcher = Stitcher::createDefault(true);  
  3.   
  4.     // 默认是0.6,最大值1最慢,此方法用于特征点检测阶段,如果找不到特征点,要调高  
  5.     stitcher.setRegistrationResol(0.6);  
  6.   
  7.     //stitcher.setSeamEstimationResol(0.1); // 默认是0.1  
  8.     //stitcher.setCompositingResol(-1);     // 默认是-1,用于特征点检测阶段,找不到特征点的话,改-1  
  9.     stitcher.setPanoConfidenceThresh(1);  // 默认是1,见过有设0.6和0.4的  
  10.     stitcher.setWaveCorrection(false);    // 默认是true,为加速选false,表示跳过WaveCorrection步骤  
  11.     //stitcher.setWaveCorrectKind(detail::WAVE_CORRECT_HORIZ);//还可以选detail::WAVE_CORRECT_VERT ,波段修正(wave correction)功能(水平方向/垂直方向修正)。因为setWaveCorrection设的false,此语句没用  
  12.   
  13.     // 找特征点surf算法,此算法计算量大,但对刚体运动、缩放、环境影响等情况下较为稳定  
  14.     //stitcher.setFeaturesFinder(new detail::SurfFeaturesFinder);  
  15.     stitcher.setFeaturesFinder(new detail::OrbFeaturesFinder);// ORB  
  16.   
  17.     // Features matcher which finds two best matches for each feature and leaves the best one only if the ratio between descriptor distances is greater than the threshold match_conf.  
  18.     // match_conf默认是0.65,选太大了没特征点  
  19.     detail::BestOf2NearestMatcher* matcher = new detail::BestOf2NearestMatcher(false, 0.5f);  
  20.     stitcher.setFeaturesMatcher(matcher);  
  21.   
  22.     // Rotation Estimation,It takes features of all images, pairwise matches between all images and estimates rotations of all cameras.  
  23.     //Implementation of the camera parameters refinement algorithm which minimizes sum of the distances between the rays passing through the camera center and a feature,这个耗时短  
  24.     stitcher.setBundleAdjuster(new detail::BundleAdjusterRay);  
  25.     //Implementation of the camera parameters refinement algorithm which minimizes sum of the reprojection error squares.  
  26.     //stitcher.setBundleAdjuster(new detail::BundleAdjusterReproj);  
  27.   
  28.     //Seam Estimation  
  29.     //Minimum graph cut-based seam estimator  
  30.     //stitcher.setSeamFinder(new detail::GraphCutSeamFinder(detail::GraphCutSeamFinderBase::COST_COLOR));//默认就是这个  
  31.     //stitcher.setSeamFinder(new detail::GraphCutSeamFinder(detail::GraphCutSeamFinderBase::COST_COLOR_GRAD));//GraphCutSeamFinder的第二种形式  
  32.     //啥SeamFinder也不用,Stub seam estimator which does nothing.  
  33.     stitcher.setSeamFinder(new detail::NoSeamFinder);  
  34.     //Voronoi diagram-based seam estimator.  
  35.     //stitcher.setSeamFinder(new detail::VoronoiSeamFinder);  
  36.   
  37.     //exposure compensators曝光补偿  
  38.     //stitcher.setExposureCompensator(new detail::BlocksGainCompensator);//默认的就是这个  
  39.     //不要曝光补偿  
  40.     stitcher.setExposureCompensator(new detail::NoExposureCompensator);  
  41.     //Exposure compensator which tries to remove exposure related artifacts by adjusting image intensities  
  42.     //stitcher.setExposureCompensator(new detail::detail::GainCompensator);  
  43.     //Exposure compensator which tries to remove exposure related artifacts by adjusting image block intensities   
  44.     //stitcher.setExposureCompensator(new detail::detail::BlocksGainCompensator);   
  45.   
  46.     // 边缘Blending  
  47.     //stitcher.setBlender( new detail::MultiBandBlender(false) );// 默认使用这个,use gpu  
  48.     //Simple blender which mixes images at its borders  
  49.     stitcher.setBlender(new detail::FeatherBlender);// 这个简单,耗时少  
  50.   
  51.     // 拼接方式,柱面?球面OR平面?默认为球面  
  52.     //stitcher.setWarper(new PlaneWarper);  
  53.     stitcher.setWarper(new SphericalWarper);  
  54.     //stitcher.setWarper(new CylindricalWarper);  
  55.   
  56.     // 开始计算变换  
  57.     Stitcher::Status status = stitcher.estimateTransform(imgs);  
  58.     if (status != Stitcher::OK)  
  59.     {  
  60.         std::cout << "Can't stitch images, error code = " << int(status) << std::endl;  
  61.         return -1;  
  62.     }  
  63.     else  
  64.     {  
  65.         std::cout << "Estimate transform complete" << std::endl;  
  66.     }  
  67.   
  68.     Mat pano;  
  69.     status = stitcher.composePanorama(imgs,pano);  
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值