OpenCV总结3——图像拼接Stitching

本文深入探讨了OpenCV的图像拼接库,详细解析了其内部实现原理,包括特征查找、图像匹配、相机参数估计、曝光补偿及图像混合等关键步骤。OpenCV通过高度封装的算法,提供了一套完整的全景图像拼接解决方案。

之前折腾过一段时间配准发现自己写的一点都不准,最近需要进行图像的拼接,偶然的机会查到了opencv原来有拼接的库,发现opencv处理配准之外还做了许多的操作,就这个机会查找了相关的资料,同时也研究了以下他的源代码,做一个简单的总结。

Stitching

因为OpenCV已经将算法进行了高度的封装,所以用起来跟OpenGL类似,遵循了一条管线进行处理。
在这里插入图片描述
上图是OpenCV官方网站中提供的流程图。

从这个图中我们也能看出这是一个很复杂的过程,源代码中提到了七个部分:

  • Features Finding and Images Matching:功能查找和图像匹配(感觉与配准的内容比较接近,都是计算特征点和匹配的过程)
  • Rotation Estimation:旋转估计
  • Autocalibration:自动校准
  • Images Warping:图像变形
  • Seam Estimation:接缝估计
  • Exposure Compensation:曝光补偿
  • Image Blenders :图像混合器

之前我做的只考虑了特征和变形,OpenCV这里的要素非常多,所以它的效果也很好。

看英文比较费事,自己做了个翻译:
在这里插入图片描述
写个最简单的调用:

	vector<Mat> ss;
	Ptr<Stitcher> stitcher = Stitcher::create();
	ss.push_back(source_image);
	ss.push_back(target_image);
	Mat pano;
	Stitcher::Status status = stitcher->stitch(ss, pano);
	if (status != Stitcher::OK)
	{
   
   
		cout << "Can't stitch images, error code = " << int(status) << endl;
		return -1;
	}

通过上述的调用就可以得到一个效果不错的拼接全景图像。(这里调用的时候采用的是指针,surf和sift也是同样,因为OpenCV在2后有比较大的改版,所以调用全部改成了指针调用的形式),我在调用这个接口的时候图像只能是用三通道的,单通道会报错。

因为我这里使用的是OpenCV4,所以没有使用2的写法,有一个博客关于OpenCV2的拼接部分源码解析的特别好,很详细。https://blog.csdn.net/zhaocj/article/details/78798687

这里根据自己的理解对OpenCV4的源代码进行简单的解析:

创建接口

Ptr<Stitcher> Stitcher::create(Mode mode)
{
   
   
    Ptr<Stitcher> stitcher = makePtr<Stitcher>();//获取指针

    stitcher->setRegistrationResol(0.6);//配准分辨率,最大是1,如果想要获得较多的特征点要将这个值调大,因为它是缩放时乘以的系数之一
    stitcher->setSeamEstimationResol(0.1);//接缝处的分辨率
    stitcher->setCompositingResol(ORIG_RESOL);//合成分辨率
    stitcher->setPanoConfidenceThresh(1);//匹配置信度阈值
    stitcher->setSeamFinder(makePtr<detail::GraphCutSeamFinder>(detail::GraphCutSeamFinderBase::COST_COLOR));//基于最小图割的接缝查找
    stitcher->setBlender(makePtr<detail::MultiBandBlender>(false));//采用多段混合算法进行混合,细节(高频)采用较小的平滑渐变,宏观(低频)采用较大的平滑渐变
    stitcher->setFeaturesFinder(ORB::create());//设置特征查找的方法
    stitcher->setInterpolationFlags(INTER_LINEAR);//设置插值的方法

    stitcher->work_scale_ = 1;//计算是的尺度
    stitcher->seam_scale_ = 1;//接缝尺度
    stitcher->seam_work_aspect_ = 1;//接缝与计算的尺度比例
    stitcher->warped_image_scale_ = 1;//图像变换的尺度

    switch (mode)
    {
   
   
    case PANORAMA: // PANORAMA is the default
        // mostly already setup
        stitcher->setEstimator(makePtr<detail::HomographyBasedEstimator>());//单应矩阵估计
        stitcher->setWaveCorrection(true);//波校正
        stitcher->setWaveCorrectKind(detail::WAVE_CORRECT_HORIZ);//波校正的方法
        stitcher->setFeaturesMatcher(makePtr<detail::BestOf2NearestMatcher>(false));//特征点匹配方法knn
        stitcher->setBundleAdjuster(makePtr<detail::BundleAdjusterRay>());//相机光线修正,估计焦距
        stitcher->setWarper(makePtr<SphericalWarper>());//变换模式:球
        stitcher->setExposureCompensator(makePtr<detail::BlocksGainCompensator>());//曝光补偿
    break;

    case SCANS:
        stitcher->setEstimator(makePtr<detail::AffineBasedEstimator>());//仿射变换
        stitcher->setWaveCorrection(false);//不采用波修正
        stitcher->setFeaturesMatcher(makePtr<detail::AffineBestOf2NearestMatcher>(false, false));//knn
        stitcher->setBundleAdjuster(makePtr<detail::BundleAdjusterAffinePartial>());//估计仿射变换的参数
        stitcher->setWarper(makePtr<AffineWarper>());//仿射变换
        stitcher->setExposureCompensator(makePtr<detail::NoExposureCompensator>());//不进行曝光补偿
    break;

    default:
        CV_Error(Error::StsBadArg, "Invalid stitching mode. Must be one of Stitcher::Mode");
    break;
    }

    return stitcher;
}

原本在之前的版本中会有createDefault这个函数,但是在新版中没有了,只有一个调用的接口,所以以前的调用方式不能再使用。

以上代码中,接缝查找器具有不同的四种算法:NoSeamFinder(无需接缝查找)、PairwiseSeamFinder(逐点查找)、DpSeamFinder(动态规划查找)、GraphCutSeamFinder(最小图割查找),其中逐点查找只实现了voronoi图法(泰森多边形),接缝的具体内容查看接缝部分的解析。

混合采用多段混合,将图像按照频率展开金字塔,先按照不同频率平滑加权,然后把各个频率的分量加权,得到最后的结果,具体查看混合部分的解析。

特征查找的方法也有许多中:BRAISK(一种二进制的特征描述算子,适合较大模糊图像的配准)、ORB(快速特征点提取的算法,是FAST和BRIEF的结合,在此基础上进行优化改进,比surf快十倍,是sift的百倍)、MSER(最大极值稳定区域,利用分水岭算法得到形状稳定的区域)、FAST(如果像素与邻域内的大量像素点的差距较大,则认为是特征点)、AGAST(自适应通用加速分割检测,比surf和sift都快)、shi_tomas(为避免harris出现聚簇现象特出的一种算法)、blob(斑点特征,blob是一块联通区域,通过对前后背景的二值化,进行联通区域的提取与标记)、KAZE、AKAZE(非线性尺度空间检测,保证图像边缘尺度变化中信息损失较少,保留细节信息),具体查看配准相关信息。

图像的变换方式分为两种:单应变换和仿射变换,单应变换会将一张图中的特征点映射到另一张图的特征点上面,仿射变换是整张图像的变换,并且仿射变换会保留线的并行性。

波校正分为水平和垂直方向。

拼接接口

Stitcher::Status Stitcher::stitch(InputArrayOfArrays images, OutputArray pano)
{
   
   
	//默认是需要遮罩的,如果没有遮罩,则传入空的图像
    return stitch(images, noArray(), pano);
}


Stitcher::Status Stitcher::stitch(InputArrayOfArrays images, InputArrayOfArrays masks, OutputArray pano)
{
   
   
    CV_INSTRUMENT_REGION();

    Status status = estimateTransform(images, masks);//估计旋转矩阵也就是计算特征进行匹配以及旋转等相关内容,也就是流程图中的上半部分
    if (status != OK)
        return status;
    return composePanorama(pano);
}

上面的代码是我们调用的接口代码,将整个过程分为了两个大的部分,一部分进行配准等操作,另一部分进行融合操作。

配准

Stitcher::Status Stitcher::estimateTransform(InputArrayOfArrays images, InputArrayOfArrays masks)
{
   
   
    CV_INSTRUMENT_REGION();

    images.getUMatVector(imgs_);//这里将图像转化为GPU中可以使用的图像,也就是说使图像支持并行处理,遵循opencl标准
    masks.getUMatVector(masks_);

    Status status;

    if ((status = matchImages()) != OK)//匹配图像
        return status;

    if ((status = estimateCameraParams()) != OK)//估计相机参数
        return status;

    return OK;
}

这里的估计变换部分又分成了两部分,一部分用于匹配图像,另一部分用于相机参数的估计。

匹配图像

以下是图像匹配部分的代码:

Stitcher::Status Stitcher::matchImages()
{
   
   
    if ((int)imgs_.size() < 2)//因为配准需要两幅图以上,所以在调用接口的时候也可以看到传入的是图像的数组
    {
   
   
        LOGLN("Need more images");
        return ERR_NEED_MORE_IMGS;
    }

    work_scale_ = 1;//匹配尺度
    seam_work_aspect_ = 1;//匹配尺度和接缝尺度的比值
    seam_scale_ = 1;//接缝尺度
    bool is_work_scale_set = false;//匹配尺度是否被设置
    bool is_seam_scale_set = false;//接缝尺度是否被设置
    features_.resize(imgs_.size());//features是保存所有图像特征的一个数组,所以数量上是一一对应的,所以此处对特征数组的数量进行设定
    seam_est_imgs_.resize(imgs_.size());//接缝估计数组,因为在整个处理过程中还有接缝误差的估计,所以这里有接缝相关的内容
    full_img_sizes_.resize(imgs_.size());//保存每个图像的尺寸

    LOGLN("Finding features...");
#if ENABLE_LOG
    int64 t = getTickCount();//返回系统启动经过的时间
#endif

    std::vector<UMat> feature_find_imgs(imgs_.size());//保存进行过尺度处理的图像
    std::vector<UMat> feature_find_masks(masks_.size());//保存图像的遮罩

    for (size_t i = 0; i < imgs_.size(); ++i)
    {
   
   
        full_img_sizes_[i] = imgs_[i].size();//保存图像的大小
        if (registr_resol_ 
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值