OpenCV Stitching图像拼接算法详解

798 阅读9分钟

OpenCV-4.x Stitching

流程图

StitchingPipeline.jpg

模块详解

核心模块:

  1. 特征点检测与图像匹配(matchers.hpp、matchers.cpp)
  2. 估计相机运动,计算相机内外参(motion_estimators.hpp、motion_estimators.cpp)
  3. 图像变形(warpers.hpp、warpers.cpp)
  4. 计算接缝(seam_finders.hpp、seam_finders.cpp)
  5. 补偿曝光(exposure_compensate.hpp、exposure_compensate.cpp)
  6. 图像融合(blenders.hpp、blenders.hpp)

图像拼接模式:

  1. PANORAMA(全景模式),此模式假设各个图像对应的相机光心位置一致,各相机光轴之间存在旋转关系。

panorama-2images.png

  1. SCANS(扫描模式),此模式假设各个图像对应的相机光心位于同一平面,各相机光轴与此平面垂直,各图像之间符合仿射变换的约束,各相机拍摄的是与光心所在平面平行的平面上的场景。

    **注:**官方文档要求图像之间满足仿射变换,假设同一空间点PP在图像A和在图像B中的像素坐标分别为(u1,v1)(u_1,v_1)(u2,v2)(u_2,v_2),内参分别为K1\mathbf{K_1}K2\mathbf{K_2},在图像A对应的相机坐标系下的坐标和在图像B对应的相机坐标系下的坐标分别为(X1,Y1,Z1)(X_1,Y_1,Z_1)(Z2,Y2,Z2)(Z_2,Y_2,Z_2),矩阵R\mathbf{R}和平移t\mathbf{t}描述图像B对应的相机相对于图像A对应的相机的位姿。则有

    [u1v11]=Z2Z1K1RK21[u2v21]+1Z1K1t\begin{bmatrix} u_1\\ v_1\\ 1 \end{bmatrix} = \frac{Z_2}{Z_1}\mathbf{K_1}\mathbf{R}\mathbf{K_2}^{-1} \begin{bmatrix} u_2\\ v_2\\ 1 \end{bmatrix} + \frac{1}{Z_1}\mathbf{K_1}\mathbf{t}

    根据官方要求,矩阵Z2Z1K1RK21\frac{Z_2}{Z_1}\mathbf{K_1}\mathbf{R}\mathbf{K_2}^{-1}第三行应为[0,0,1][0,0,1],平移1Z1K1t\frac{1}{Z_1}\mathbf{K_1}\mathbf{t}第三维应为00. 由此可知Z1=Z2Z_1=Z_2,且对于任意点PPZ1Z_1为常数,因此可知SCANS模式下要求:各相机光轴与此平面垂直,各相机拍摄的是与光心所在平面平行的平面上。

特征点检测与图像匹配

模块过程:

  1. 计算图像集合中每个图像的特征点集合及其描述子集合;
  2. 根据对应图像的特征点和描述子,对图像集合进行两两配对;
  3. 对匹配上的两张图像,根据对应的特征点集合,计算其变换矩阵H\mathbf{H}

PANORAMA模式:

图像匹配的class:BestOf2NearestMatcher
H是两个特征点集之间的单应变换矩阵,利用RANSAC算法,拟合出H,计算矩阵H的函数为:
cv::Mat cv::findHomography( InputArray _points1, InputArray _points2,
                           OutputArray _mask, int method, double ransacReprojThreshold )

SCANS模式:

图像匹配的class:AffineBestOf2NearestMatcher
H是两个特征点集之间的仿射变换矩阵,opencv的具体实现中,只包含旋转、缩放和平移,利用RANSAC算法,拟合出H,计算矩阵H的函数为:
CV_EXPORTS_W cv::Mat estimateAffinePartial2D(InputArray from, InputArray to, OutputArray inliers = noArray(),
                                  int method = RANSAC, double ransacReprojThreshold = 3,
                                  size_t maxIters = 2000, double confidence = 0.99,
                                  size_t refineIters = 10);
估计相机运动,计算相机内外参

模块过程:

  1. 利用图像匹配过程中计算得到的H\mathbf{H},估计相机外参R\mathbf{R},并初始化相机内参K\mathbf{K}。其中H\mathbf{H}R\mathbf{R}是相对值,首先由多个图像为节点,各个图像之间的匹配关系为边,构造Spanning Tree,然后树中心节点对应的图像外参为单位矩阵,其他图像外参是基于此图像的位姿变化。
  2. 利用Bundle Adjustment方法优化相机内外参;
  3. 波形矫正,进一步优化相机外参。

PANORAMA模式:

估计相机外参的class:HomographyBasedEstimator
根据H计算R的过程:CalcRotation结构体及其operator()成员函数
优化相机参数的clas:BundleAdjusterRay
波形矫正优化策略:WAVE_CORRECT_HORIZ

PANORAMA模式下,根据两个图像像素之间的单应变换矩阵H\mathbf{H},计算相机旋转矩阵R\mathbf{R}的过程如下:

假设同一空间点PP在图像A和在图像B中的像素坐标分别为(u1,v1)(u_1,v_1)(u2,v2)(u_2,v_2),在图像A对应的相机坐标系下的坐标和在图像B对应的相机坐标系下的坐标分别为(X1,Y1,Z1)(X_1,Y_1,Z_1)(Z2,Y2,Z2)(Z_2,Y_2,Z_2),则根据opencv stitching模块中的定义,H\mathbf{H}满足

[u2v21]=H[u1v11]\begin{bmatrix} u_2\\ v_2\\ 1 \end{bmatrix} = \mathbf{H} \begin{bmatrix} u_1\\ v_1\\ 1 \end{bmatrix}

而根据相机成像模型,有

[u1v11]=1Z1K1[X1Y1Z1],   [u2v21]=1Z2K2[X2Y2Z2]\begin{bmatrix} u_1\\ v_1\\ 1 \end{bmatrix} = \frac{1}{Z_1}\cdot\mathbf{K_1} \begin{bmatrix} X_1\\ Y_1\\ Z_1 \end{bmatrix} ,\ \ \ \begin{bmatrix} u_2\\ v_2\\ 1 \end{bmatrix} = \frac{1}{Z_2}\cdot\mathbf{K_2} \begin{bmatrix} X_2\\ Y_2\\ Z_2 \end{bmatrix}

其中K1\mathbf{K_1}K2\mathbf{K_2}分别为图像A对应的相机的内参和图像B对应的相机内参。而R\mathbf{R}为图像B对应的相机相对于图像A对应的相机的旋转,因此有

[X1Y1Z1]=R[X2Y2Z2]\begin{bmatrix} X_1\\ Y_1\\ Z_1 \end{bmatrix} = \mathbf{R} \begin{bmatrix} X_2\\ Y_2\\ Z_2 \end{bmatrix}

由上述等式,可以求出R\mathbf{R}满足

R=Z1Z2K11H1K2\mathbf{R}=\frac{Z_1}{Z_2}\mathbf{K_1}^{-1}\mathbf{H}^{-1}\mathbf{K_2}

图像B对应的相机相对于图像A对应的相机的旋转矩阵R\mathbf{R}由上式给出。

(注:opencv stitching模块的具体实现中旋转矩阵用K11H1K2\mathbf{K_1}^{-1}\cdot\mathbf{H}^{-1}\mathbf{K_2}来表示,相当于在实际上的旋转矩阵R\mathbf{R}基础上多一个系数Z1Z2\frac{Z_1}{Z_2}. 此系数在利用Bundle Adjustment方法优化相机参数的过程中,通过cv::Rodrigues()函数将旋转矩阵转换为旋转向量的时候,去除了尺度信息)

SCANS模式:

估计相机外参的class:AffineBasedEstimator
根据H计算R的过程:CalcAffineTransform结构体及其operator()成员函数
优化相机参数的class:BundleAdjusterAffinePartial
波形矫正策略:不进行波形矫正

SCANS模式下,根据两个图像像素之间的仿射变换矩阵H\mathbf{H},估计相机旋转矩阵R\mathbf{R}和平移t\mathbf{t}的过程如下:

假设同一空间点PP在图像A和在图像B中的像素坐标分别为(u1,v1)(u_1,v_1)(u2,v2)(u_2,v_2),则根据opencv stitching模块中的定义,H\mathbf{H}是仿射矩阵,满足

[u2v21]=H[u1v11]\begin{bmatrix} u_2\\ v_2\\ 1 \end{bmatrix} = \mathbf{H} \begin{bmatrix} u_1\\ v_1\\ 1 \end{bmatrix}

根据仿射变换矩阵的性质,H\mathbf{H}可表示为

H=[h11h12h13h21h22h23001]\mathbf{H} = \begin{bmatrix} h_{11} & h_{12} & h_{13}\\ h_{21} & h_{22} & h_{23}\\ 0 & 0 & 1 \end{bmatrix}

根据仿射变换的性质,可将矩阵H\mathbf{H}拆分为旋转缩放矩阵R\mathbf{R'}和平移t\mathbf{t'},即

[u2v21]=R[u1v11]+t=[h11h120h21h220001][u1v11]+[h13h230]\begin{bmatrix} u_2\\ v_2\\ 1 \end{bmatrix} = \mathbf{R'} \begin{bmatrix} u_1\\ v_1\\ 1 \end{bmatrix} + \mathbf{t'} = \begin{bmatrix} h_{11} & h_{12} & 0\\ h_{21} & h_{22} & 0\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} u_1\\ v_1\\ 1 \end{bmatrix} + \begin{bmatrix} h_{13}\\ h_{23}\\ 0 \end{bmatrix}

根据opencv中的定义,旋转矩阵R\mathbf{R}和平移t\mathbf{t}描述的是图像B对应的相机相对于图像A对应的相机的位姿,是与H\mathbf{H}相反的过程,因此可得

R=R1t=R1t\begin{aligned} &\mathbf{R}=\mathbf{R'}^{-1}\\ &\mathbf{t}=-\mathbf{R'}^{-1}\cdot\mathbf{t'} \end{aligned}

此时得到的R\mathbf{R}t\mathbf{t}都是基于像素坐标,即对于点PP在图像A和在图像B中的像素坐标(u1,v1)(u_1,v_1)(u2,v2)(u_2,v_2),有

[u1v11]=R[u2v21]+t\begin{bmatrix} u_1\\ v_1\\ 1 \end{bmatrix} = \mathbf{R} \begin{bmatrix} u_2\\ v_2\\ 1 \end{bmatrix} + \mathbf{t}

**经过bundle adjustment过程优化之后,由H\mathbf{H}得到与真实的相机之间的位姿R\mathbf{R}t\mathbf{t}。**对于PP点在图像A对应的相机坐标系下的坐标和在图像B对应的相机坐标系下的坐标(X1,Y1,Z1)(X_1,Y_1,Z_1)(Z2,Y2,Z2)(Z_2,Y_2,Z_2),因此有

[X1Z1Y1Z11]=R[X2Z2Y2Z21]+t\begin{bmatrix} \frac{X_1}{Z_1}\\ \frac{Y_1}{Z_1}\\ 1 \end{bmatrix} = \mathbf{R} \begin{bmatrix} \frac{X_2}{Z_2}\\ \frac{Y_2}{Z_2}\\ 1 \end{bmatrix} + \mathbf{t}
图像变形

模块过程:

  1. 建立原图像和其对应的投影面之间的像素对应关系;
  2. 将图像本身的像素范围变换为对应到投影面上的ROI框;
  3. 对投影面上的ROI框中每个像素坐标,在原图像中找到对应的像素点,将其值赋给投影面上ROI框中对应的坐标。

PANORAMA模式:

图像变形class:SphericalWarper
原图与投影面映射变换class:SphericalProjector

PANORAMA模式下,图像投影方式为球面投影,即将图像投影到以相机光心为球心的球面上。

注1: 以下涉及的齐次坐标,是指相对于相机z\mathbf{z}轴方向上的齐次坐标。

注2: 球面坐标系即为半径为常数的球坐标系,具体定义可参考球坐标系

  1. 正向投影:

    首先,将图像像素坐标转换到相机坐标系下的齐次坐标。选取原图像中像素位置(u,v)(u,v),其对应的空间点PP在当前相机坐标系下的坐标为(X,Y,Z)(X,Y,Z),则根据相机模型有

    [XZYZ1]=K1[uv1]\begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix} =\mathbf{K}^{-1} \begin{bmatrix} u\\ v\\ 1 \end{bmatrix}

    (XZ,YZ,1)(\frac{X}{Z},\frac{Y}{Z},1)即为PP点当前相机坐标系下,投影到单位平面的齐次坐标,其中K\mathbf{K}是当前相机内参。

    然后,将PP点在当前相机坐标系下的齐次坐标,变换为中心相机坐标系下的齐次坐标(X,Y,Z)(X',Y',Z'),即

    [XYZ]=R[XZYZ1]\begin{bmatrix} X'\\ Y'\\ Z' \end{bmatrix} =\mathbf{R} \begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix}

    其中,R\mathbf{R}为当前相机相对于中心相机的旋转。

    最后,根据利用直角坐标系和球面坐标系的关系,将PP点在中心相机坐标系下的齐次坐标(X,Y,Z)(X',Y',Z')投影到,以中心相机光心为球心以ss为半径的球面上得到PP点在球面坐标系下的坐标(u,v)(u',v')

    u=sarctan2(XZ)v=s(πarccosYX2+Y2+Z2)\begin{aligned} &u'=s\cdot \textbf{arctan2}(\frac{X'}{Z'})\\ &v'=s\cdot (\pi - \arccos\frac{Y'}{\sqrt{X'^2+Y'^2+Z'^2}}) \end{aligned}

    其中,函数arctan2()\textbf{arctan2}()与c++中的函数定义一致。

  2. 反向投影:

    反向投影即是正向投影的逆向操作,我们沿用上述符号定义。

    首先,将PP点球面坐标(u,v)(u',v'),转为中心相机坐标系下的齐次坐标

    [XX2+Y2+Z2YX2+Y2+Z2ZX2+Y2+Z2]=[sin(πvs)sin(us)cos(πvs)sin(πvs)cos(us)]\begin{bmatrix} \frac{X'}{\sqrt{X'^2+Y'^2+Z'^2}}\\ \frac{Y'}{\sqrt{X'^2+Y'^2+Z'^2}}\\ \frac{Z'}{\sqrt{X'^2+Y'^2+Z'^2}} \end{bmatrix} = \begin{bmatrix} \sin(\pi-\frac{v'}{s})\cdot\sin(\frac{u'}{s})\\ \cos(\pi-\frac{v'}{s})\\ \sin(\pi-\frac{v'}{s})\cdot\cos(\frac{u'}{s}) \end{bmatrix}

    然后,将中心相机坐标系下的齐次坐标转为原图像相机坐标系下的齐次坐标

    [XX2+Y2+Z2YX2+Y2+Z2ZX2+Y2+Z2]=R1[XX2+Y2+Z2YX2+Y2+Z2ZX2+Y2+Z2]\begin{bmatrix} \frac{X}{\sqrt{X^2+Y^2+Z^2}}\\ \frac{Y}{\sqrt{X^2+Y^2+Z^2}}\\ \frac{Z}{\sqrt{X^2+Y^2+Z^2}} \end{bmatrix} = \mathbf{R}^{-1} \begin{bmatrix} \frac{X'}{\sqrt{X'^2+Y'^2+Z'^2}}\\ \frac{Y'}{\sqrt{X'^2+Y'^2+Z'^2}}\\ \frac{Z'}{\sqrt{X'^2+Y'^2+Z'^2}} \end{bmatrix}

    将原图像相机下的齐次坐标,除去z\mathbf{z}轴方向的长度,得到

    [XZYZ1]=X2+Y2+Z2Z[XX2+Y2+Z2YX2+Y2+Z2ZX2+Y2+Z2]\begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix} = \frac{\sqrt{X^2+Y^2+Z^2}}{Z} \begin{bmatrix} \frac{X}{\sqrt{X^2+Y^2+Z^2}}\\ \frac{Y}{\sqrt{X^2+Y^2+Z^2}}\\ \frac{Z}{\sqrt{X^2+Y^2+Z^2}} \end{bmatrix}

    最后,根据原图像相机的内参,计算出齐次坐标投影到成像平面上的像素坐标(u,v)(u,v),即

    [uv1]=K[XZYZ1]\begin{bmatrix} u\\ v\\ 1 \end{bmatrix} = \mathbf{K} \begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix}

SCANS模式:

图像变形class:AffineWarper
原图与投影面映射变换class:PlaneProjector

SCANS模式下,图像投影方式仿射变换,即将图像投影到与其平行的平面,并利用相对于中心相机图像的仿射变换关系对图像进行变换。

  1. 正向投影:

    首先,将图像像素坐标转换到相机坐标系下的齐次坐标。选取原图像中像素位置(u,v)(u,v),其对应的空间点PP在当前相机坐标系下的坐标为(X,Y,Z)(X,Y,Z),则根据相机模型有

    [XZYZ1]=K1[uv1]\begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix} =\mathbf{K}^{-1} \begin{bmatrix} u\\ v\\ 1 \end{bmatrix}

    (XZ,YZ,1)(\frac{X}{Z},\frac{Y}{Z},1)即为PP点当前相机坐标系下,投影到单位平面的齐次坐标,其中K\mathbf{K}是当前相机内参。

    然后,根据R\mathbf{R}t\mathbf{t}​,将PP点在当前相机坐标系下的齐次坐标,变换为中心相机坐标系下的齐次坐标(X,Y,Z)(X',Y',Z'),即

    [XZYZ1]=R[XZYZ1]+t\begin{bmatrix} \frac{X'}{Z'}\\ \frac{Y'}{Z'}\\ 1 \end{bmatrix} =\mathbf{R} \begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix} + \mathbf{t}

    其中Z=1Z'=1.

    最后,将投影面进行尺度ss的缩放,得到PP点在投影面上的像素坐标(u,v)(u',v')u=sXZ,v=sYZu'=s\frac{X'}{Z'},v'=s\frac{Y'}{Z'}. 因此正向投影,相当于把所有图像投影到距离相机光心所在平面为11的平面上,再将图像缩放ss倍。

  2. 反向投影:

    首先,PP点将投影面图像坐标进行1s\frac{1}{s}尺度缩放,得到在中心相机下的齐次坐标(XZ,YZ,1)(\frac{X'}{Z'},\frac{Y'}{Z'},1),然后进行正向投影的逆操作

    [XZYZ1]=R1[XZYZ1]+R1t\begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix} =\mathbf{R}^{-1} \begin{bmatrix} \frac{X'}{Z'}\\ \frac{Y'}{Z'}\\ 1 \end{bmatrix} + \mathbf{R}^{-1}\mathbf{t}

    最后,根据相机内参K\mathbf{K},得到原图像像素坐标(u,v)(u,v),满足

    [uv1]=K[XZYZ1]\begin{bmatrix} u\\ v\\ 1 \end{bmatrix} =\mathbf{K} \begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix}
计算接缝、补偿曝光

PANORAMA模式:

计算接缝class:GraphCutSeamFinder,使用图形切割算法,可以查找图像中最佳的拼接缝隙
补偿曝光class:SphericalWarper,对图像中不同区域进行增益补偿

SCANS模式:

计算接缝class:GraphCutSeamFinder,使用图形切割算法,可以查找图像中最佳的拼接缝隙
补偿曝光class:BlocksGainCompensator,一种不使用曝光补偿的方法来校正拍摄照片中的一些失真
图像融合

模块过程:

  1. 根据投影之后各个的图像尺寸和位置,确定最终拼接完成之后的图像尺寸;
  2. 利用对应模式下的图像变形方式,将图像投影到最终图像上;
  3. 将拼接好的图像中重叠的部分,利用多频带混合算法进行融合。
图像融合class:MultiBandBlender,图像拼接的多频带混合算法

多频带混合算法:

多频带混合的基本思想是图像可以分解为不同频率的图像的叠加,在不同的频率上,使用不同的权重来进行融合,在低频部分应该使用波长较宽的加权信号,在高频部分使用较窄的加权信号,其算法过程如下:

  1. 计算输入图像的高斯金字塔;
  2. 计算输入图像的拉普拉斯金字塔;
  3. 将处于同一级的拉普拉斯金字塔进行融合;
  4. 将高层的拉普拉斯金字塔依次扩展直至和相同分辨率;
  5. 将上一步中得到的图像依次叠加,则得到最终的输出图像。