如何缝合重叠很少的图像?

2024-02-21

我正在尝试使用重叠很少的图像创建全景图,但我知道相机的角度,因此我确切地知道有多少重叠,并且我知道图像的顺序,因此我知道每个图像在全景图中的位置。 作为第一步,我只是将图像连接在一起,但结果不够好。 有没有办法将位图裁剪为梯形以消除(大部分)重叠,然后在串联之前将位图拉伸回矩形? 我知道这会在拉伸过程中产生失真,并且梯形只是位图实际需要裁剪的近似值,但我希望这足够好。


您正在寻找的技术称为图像配准 http://en.wikipedia.org/wiki/Image_registration使用仿射变换。这可以在软件中通过计算图像 B 的矩阵变换将其映射到图像 A 来实现。我假设您正在尝试使用 Windows 窗体和 GDI+ 在软件中执行此操作?那么您可用的矩阵是 3x3 矩阵,能够缩放、平移、旋转和倾斜。这通常足以创建简单的图像配准,并且我在商业软件包中成功地使用了该技术(但是 WPF)。

要使用仿射变换实现图像配准,首先需要配准一对图像中的控制点集合。由此我们可以计算 2D 变换来配准图像?我已经在 WPF 中完成了此操作,它可以使用 System.Windows.Media.Matrix 类定义 3x3 矩阵,该类具有以下构造函数:

Matrix(double m11, double m12, double m21, double m22, 
         double offsetX, double offsetY)

注意:GDI+有一个Matrix类,其构造函数可能不同,但原理是相同的

构造函数参数形成矩阵,如下所示:



M11     M12     0
M21     M22     0
OffsetX OffsetY 1
  

现在,如果输入点称为 X、Y,输出点称为 U、V,则将 X、Y 映射到 U、V 的仿射矩阵变换 T 可以计算如下:



U        =  X         *  T

[U1 V1 1] = [X1 Y1 1]   [A  B  0]
[U2 V2 1] = [X2 Y2 1] * [C  D  0]
[U3 V3 1] = [X3 Y3 1]   [Tx Ty 1]
  

这也可以简化如下:



U      =  X         *  T

[U1 V1] = [X1 Y1 1]   [A  B ]
[U2 V2] = [X2 Y2 1] * [C  D ]
[U3 V3] = [X3 Y3 1]   [Tx Ty]
  

or



X^-1         * U       = T
[X1 Y1 1]^-1   [U1 V1]   [A  B ]
[X2 Y2 1]    * [U2 V2] = [C  D ]
[X3 Y3 1]      [U3 V3]   [Tx Ty]
  

用英语来说这意味着,给定图像 1 中对应于图像 2 的点 X、Y 的列表,包含 XY 点的矩阵 X 的逆矩阵乘以图像 2 中对应点的矩阵即可得出从图像 1 到图像 2 的矩阵变换

输出变换 T 包含 A、B、C、D 和 Tx、Ty,它们对应于 3x3 仿射矩阵类(上面的构造函数)中的 M11、M12、M21、M22、OffsetX、OffsetY。但是,如果 X 矩阵和 U 矩阵的点超过 3 个,则解是超定的,必须找到最小二乘拟合。这是使用 Moores-Penrose 伪逆求 X^-1 来实现的。

这在代码中意味着什么?我编写了自己的 Matrix3x3、Matrix3x2 类和控制点(x,y 点)来处理转换,然后将其应用于元素上的 WPF MatrixTransform。在 GDI+ 中,您可以通过在调用 Graphics.DrawImage 之前将 Matrix 应用到图形管道来执行相同的操作。让我们看看如何计算变换矩阵。

我们需要的第一个类是 Matrix3x3 类:

public class Matrix3x3 : ICloneable
{
    #region Local Variables

    private double [] coeffs;

    private const int _M11 = 0;
    private const int _M12 = 1;
    private const int _M13 = 2;
    private const int _M21 = 3;
    private const int _M22 = 4;
    private const int _M23 = 5;
    private const int _M31 = 6;
    private const int _M32 = 7;
    private const int _M33 = 8;

    #endregion

    #region Construction

    /// <summary>
    /// Initializes a new instance of the <see cref="Matrix3x3"/> class.
    /// </summary>
    public Matrix3x3()
    {
        coeffs = new double[9];
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Matrix3x3"/> class.
    /// </summary>
    /// <param name="coefficients">The coefficients to initialise. The number of elements of the array should
    /// be equal to 9, else an exception will be thrown</param>
    public Matrix3x3(double[] coefficients)
    {
        if (coefficients.GetLength(0) != 9)
            throw new Exception("Matrix3x3.Matrix3x3()", "The number of coefficients passed in to the constructor must be 9");

        coeffs = coefficients;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Matrix3x3"/> class. 
    /// </summary>
    /// <param name="m11">The M11 coefficient</param>
    /// <param name="m12">The M12 coefficien</param>
    /// <param name="m13">The M13 coefficien</param>
    /// <param name="m21">The M21 coefficien</param>
    /// <param name="m22">The M22 coefficien</param>
    /// <param name="m23">The M23 coefficien</param>
    /// <param name="m31">The M31 coefficien</param>
    /// <param name="m32">The M32 coefficien</param>
    /// <param name="m33">The M33 coefficien</param>
    public Matrix3x3(double m11, double m12, double m13, double m21, double m22, double m23, double m31, double m32, double m33)
    {
        // The 3x3 matrix is constructed as follows
        //
        // | M11 M12 M13 | 
        // | M21 M22 M23 | 
        // | M31 M32 M33 | 

        coeffs = new double[] { m11, m12, m13, m21, m22, m23, m31, m32, m33 };
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Matrix3x3"/> class. The IAffineTransformCoefficients
    /// passed in is used to populate coefficients M11, M12, M21, M22, M31, M32. The remaining column (M13, M23, M33)
    /// is populated with homogenous values 0 0 1.
    /// </summary>
    /// <param name="affineMatrix">The IAffineTransformCoefficients used to populate M11, M12, M21, M22, M31, M32</param>
    public Matrix3x3(IAffineTransformCoefficients affineTransform)
    {
        coeffs = new double[] { affineTransform.M11, affineTransform.M12, 0, 
                                affineTransform.M21, affineTransform.M22, 0, 
                                affineTransform.OffsetX, affineTransform.OffsetY, 1};
    }

    #endregion

    #region Public Properties

    /// <summary>
    /// Gets or sets the M11 coefficient
    /// </summary>
    /// <value>The M11</value>
    public double M11
    {
        get
        {
            return coeffs[_M11];
        }
        set
        {
            coeffs[_M11] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M12 coefficient
    /// </summary>
    /// <value>The M12</value>
    public double M12
    {
        get
        {
            return coeffs[_M12];
        }
        set
        {
            coeffs[_M12] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M13 coefficient
    /// </summary>
    /// <value>The M13</value>
    public double M13
    {
        get
        {
            return coeffs[_M13];
        }
        set
        {
            coeffs[_M13] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M21 coefficient
    /// </summary>
    /// <value>The M21</value>
    public double M21
    {
        get
        {
            return coeffs[_M21];
        }
        set
        {
            coeffs[_M21] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M22 coefficient
    /// </summary>
    /// <value>The M22</value>
    public double M22
    {
        get
        {
            return coeffs[_M22];
        }
        set
        {
            coeffs[_M22] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M23 coefficient
    /// </summary>
    /// <value>The M23</value>
    public double M23
    {
        get
        {
            return coeffs[_M23];
        }
        set
        {
            coeffs[_M23] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M31 coefficient
    /// </summary>
    /// <value>The M31</value>
    public double M31
    {
        get
        {
            return coeffs[_M31];
        }
        set
        {
            coeffs[_M31] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M32 coefficient
    /// </summary>
    /// <value>The M32</value>
    public double M32
    {
        get
        {
            return coeffs[_M32];
        }
        set
        {
            coeffs[_M32] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M33 coefficient
    /// </summary>
    /// <value>The M33</value>
    public double M33
    {
        get
        {
            return coeffs[_M33];
        }
        set
        {
            coeffs[_M33] = value;
        }
    }

    /// <summary>
    /// Gets the determinant of the matrix
    /// </summary>
    /// <value>The determinant</value>
    public double Determinant
    {
        get
        {
            //                                |a b c|
            // In general, for a 3X3 matrix   |d e f|
            //                                |g h i|
            //
            // The determinant can be found as follows:
            // a(ei-fh) - b(di-fg) + c(dh-eg)

            // Get coeffs
            double a = coeffs[_M11];
            double b = coeffs[_M12];
            double c = coeffs[_M13];
            double d = coeffs[_M21];
            double e = coeffs[_M22];
            double f = coeffs[_M23];
            double g = coeffs[_M31];
            double h = coeffs[_M32];
            double i = coeffs[_M33];
            double ei = e * i;
            double fh = f * h;
            double di = d * i;
            double fg = f * g;
            double dh = d * h;
            double eg = e * g;

            // Compute the determinant
            return (a * (ei - fh)) - (b * (di - fg)) + (c * (dh - eg));
        }
    }

    /// <summary>
    /// Gets a value indicating whether this matrix is singular. If it is singular, it cannot be inverted
    /// </summary>
    /// <value>
    ///     <c>true</c> if this instance is singular; otherwise, <c>false</c>.
    /// </value>
    public bool IsSingular
    {
        get
        {
            return Determinant == 0;
        }
    }

    /// <summary>
    /// Gets the inverse of this matrix. If the matrix is singular, this method will throw an exception
    /// </summary>
    /// <value>The inverse</value>
    public Matrix3x3 Inverse
    {
        get
        {
            // Taken from http://everything2.com/index.pl?node_id=1271704
            //                                                  a b c
            //In general, the inverse matrix of a 3X3 matrix    d e f
            //                                                  g h i

            //is 

            //        1                              (ei-fh)   (bi-ch)   (bf-ce)
            // -----------------------------   x     (fg-di)   (ai-cg)   (cd-af)
            // a(ei-fh) - b(di-fg) + c(dh-eg)        (dh-eg)   (bg-ah)   (ae-bd)

            // Get coeffs
            double a = coeffs[_M11];
            double b = coeffs[_M12];
            double c = coeffs[_M13];
            double d = coeffs[_M21];
            double e = coeffs[_M22];
            double f = coeffs[_M23];
            double g = coeffs[_M31];
            double h = coeffs[_M32];
            double i = coeffs[_M33];

            //// Compute often used components
            double ei = e * i;
            double fh = f * h;
            double di = d * i;
            double fg = f * g;
            double dh = d * h;
            double eg = e * g;
            double bi = b * i;
            double ch = c * h;
            double ai = a * i;
            double cg = c * g;
            double cd = c * d;
            double bg = b * g;
            double ah = a * h;
            double ae = a * e;
            double bd = b * d;
            double bf = b * f;
            double ce = c * e;
            double cf = c * d;
            double af = a * f;

            // Construct the matrix using these components
            Matrix3x3 tempMat = new Matrix3x3(ei - fh, ch - bi, bf - ce, fg - di, ai - cg, cd - af, dh - eg, bg - ah, ae - bd);

            // Compute the determinant
            double det = Determinant;

            if (det == 0.0)
            {
                throw new Exception("Matrix3x3.Inverse", "Unable to invert the matrix as it is singular");
            }

            // Scale the matrix by 1/determinant
            tempMat.Scale(1.0 / det);

            return tempMat;
        }
    }

    /// <summary>
    /// Gets a value indicating whether this matrix is affine. This will be true if the right column 
    /// (M13, M23, M33) is 0 0 1
    /// </summary>
    /// <value><c>true</c> if this instance is affine; otherwise, <c>false</c>.</value>
    public bool IsAffine
    {
        get
        {
            return (coeffs[_M13] == 0 && coeffs[_M23] == 0 && coeffs[_M33] == 1);
        }
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Multiplies the current matrix by the 3x3 matrix passed in
    /// </summary>
    /// <param name="rhs"></param>
    public void Multiply(Matrix3x3 rhs)
    {
        // Get coeffs
        double a = coeffs[_M11];
        double b = coeffs[_M12];
        double c = coeffs[_M13];
        double d = coeffs[_M21];
        double e = coeffs[_M22];
        double f = coeffs[_M23];
        double g = coeffs[_M31];
        double h = coeffs[_M32];
        double i = coeffs[_M33];

        double j = rhs.M11;
        double k = rhs.M12;
        double l = rhs.M13;
        double m = rhs.M21;
        double n = rhs.M22;
        double o = rhs.M23;
        double p = rhs.M31;
        double q = rhs.M32;
        double r = rhs.M33;

        // Perform multiplication. Formula taken from
        // http://www.maths.surrey.ac.uk/explore/emmaspages/option1.html

        coeffs[_M11] = a * j + b * m + c * p;
        coeffs[_M12] = a * k + b * n + c * q;
        coeffs[_M13] = a * l + b * o + c * r;
        coeffs[_M21] = d * j + e * m + f * p;
        coeffs[_M22] = d * k + e * n + f * q;
        coeffs[_M23] = d * l + e * o + f * r;
        coeffs[_M31] = g * j + h * m + i * p;
        coeffs[_M32] = g * k + h * n + i * q;
        coeffs[_M33] = g * l + h * o + i * r;
    }

    /// <summary>
    /// Scales the matrix by the specified scalar value
    /// </summary>
    /// <param name="scalar">The scalar.</param>
    public void Scale(double scalar)
    {
        coeffs[0] *= scalar;
        coeffs[1] *= scalar;
        coeffs[2] *= scalar;
        coeffs[3] *= scalar;
        coeffs[4] *= scalar;
        coeffs[5] *= scalar;
        coeffs[6] *= scalar;
        coeffs[7] *= scalar;
        coeffs[8] *= scalar;
    }

    /// <summary>
    /// Makes the matrix an affine matrix by setting the right column (M13, M23, M33) to 0 0 1
    /// </summary>
    public void MakeAffine()
    {
        coeffs[_M13] = 0;
        coeffs[_M23] = 0;
        coeffs[_M33] = 1;
    }

    #endregion

    #region ICloneable Members

    /// <summary>
    /// Creates a new object that is a copy of the current instance.
    /// </summary>
    /// <returns>
    /// A new object that is a copy of this instance.
    /// </returns>
    public object Clone()
    {
        double[] coeffCopy = (double[])coeffs.Clone();
        return new Matrix3x3(coeffCopy);
    }

    #endregion

    #region IAffineTransformCoefficients Members

    //
    // NB: M11, M12, M21, M22 members of IAffineTransformCoefficients are implemented within the
    // #region Public Properties directive
    //

    /// <summary>
    /// Gets or sets the Translation Offset in the X Direction
    /// </summary>
    /// <value>The M31</value>
    public double OffsetX
    {
        get
        {
            return coeffs[_M31];
        }
        set
        {
            coeffs[_M31] = value;
        }
    }

    /// <summary>
    /// Gets or sets the Translation Offset in the Y Direction
    /// </summary>
    /// <value>The M32</value>
    public double OffsetY
    {
        get
        {
            return coeffs[_M32];
        }
        set
        {
            coeffs[_M32] = value;
        }
    }

    #endregion
}

还有一个 Matrix3x2 类

public class Matrix3x2 : ICloneable
{
    #region Local Variables

    private double[] coeffs;

    private const int _M11 = 0;
    private const int _M12 = 1;
    private const int _M21 = 2;
    private const int _M22 = 3;
    private const int _M31 = 4;
    private const int _M32 = 5;

    #endregion

    #region Construction

    /// <summary>
    /// Initializes a new instance of the <see cref="Matrix3x2"/> class.
    /// </summary>
    public Matrix3x2()
    {
        coeffs = new double[6];
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Matrix3x2"/> class.
    /// </summary>
    /// <param name="coefficients">The coefficients to initialise. The number of elements of the array should
    /// be equal to 6, else an exception will be thrown</param>
    public Matrix3x2(double[] coefficients)
    {
        if (coefficients.GetLength(0) != 6)
            throw new Exception("Matrix3x2.Matrix3x2()", 
                "The number of coefficients passed in to the constructor must be 6");

        coeffs = coefficients;
    }

    public Matrix3x2(double m11, double m12, double m21, double m22, double m31, double m32)
    {
        coeffs = new double[] { m11, m12, m21, m22, m31, m32 };
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Matrix3x2"/> class. The IAffineTransformCoefficients
    /// passed in is used to populate coefficients M11, M12, M21, M22, M31, M32.
    /// </summary>
    /// <param name="affineMatrix">The IAffineTransformCoefficients used to populate M11, M12, M21, M22, M31, M32</param>
    public Matrix3x2(IAffineTransformCoefficients affineTransform)
    {
        coeffs = new double[] { affineTransform.M11, affineTransform.M12, 
                                affineTransform.M21, affineTransform.M22, 
                                affineTransform.OffsetX, affineTransform.OffsetY};
    }

    #endregion

    #region Public Properties

    /// <summary>
    /// Gets or sets the M11 coefficient
    /// </summary>
    /// <value>The M11</value>
    public double M11
    {
        get
        {
            return coeffs[_M11];
        }
        set
        {
            coeffs[_M11] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M12 coefficient
    /// </summary>
    /// <value>The M12</value>
    public double M12
    {
        get
        {
            return coeffs[_M12];
        }
        set
        {
            coeffs[_M12] = value;
        }
    }


    /// <summary>
    /// Gets or sets the M21 coefficient
    /// </summary>
    /// <value>The M21</value>
    public double M21
    {
        get
        {
            return coeffs[_M21];
        }
        set
        {
            coeffs[_M21] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M22 coefficient
    /// </summary>
    /// <value>The M22</value>
    public double M22
    {
        get
        {
            return coeffs[_M22];
        }
        set
        {
            coeffs[_M22] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M31 coefficient
    /// </summary>
    /// <value>The M31</value>
    public double M31
    {
        get
        {
            return coeffs[_M31];
        }
        set
        {
            coeffs[_M31] = value;
        }
    }

    /// <summary>
    /// Gets or sets the M32 coefficient
    /// </summary>
    /// <value>The M32</value>
    public double M32
    {
        get
        {
            return coeffs[_M32];
        }
        set
        {
            coeffs[_M32] = value;
        }
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Transforms the the ILocation passed in and returns the result in a new ILocation
    /// </summary>
    /// <param name="location">The location to transform</param>
    /// <returns>The transformed location</returns>
    public ILocation Transform(ILocation location)
    {
        // Perform the following equation:
        //
        // | x y 1 |   | M11 M12 |   |(xM11 + yM21 + M31) (xM12 + yM22 + M32)|
        //           * | M21 M22 | = 
        //             | M31 M32 | 

        double x = location.X * coeffs[_M11] + location.Y * coeffs[_M21] + coeffs[_M31];
        double y = location.X * coeffs[_M12] + location.Y * coeffs[_M22] + coeffs[_M32];

        return new Location(x, y);
    }

    /// <summary>
    /// Multiplies the 3x3 matrix passed in with the current 3x2 matrix
    /// </summary>
    /// <param name="x">The 3x3 Matrix X</param>
    public void Multiply(Matrix3x3 lhs)
    {
        // Multiply the 3x3 matrix with the 3x2 matrix and store inside the current 2x3 matrix
        // 
        // [a b c]   [j k]   [(aj + bl + cn) (ak + bm + co)]
        // [d e f] * [l m] = [(dj + el + fn) (dk + em + fo)]
        // [g h i]   [n o]   [(gj + hl + in) (gk + hm + io)]

        // Get coeffs
        double a = lhs.M11;
        double b = lhs.M12;
        double c = lhs.M13;
        double d = lhs.M21;
        double e = lhs.M22;
        double f = lhs.M23;
        double g = lhs.M31;
        double h = lhs.M32;
        double i = lhs.M33;

        double j = coeffs[_M11];
        double k = coeffs[_M12];
        double l = coeffs[_M21];
        double m = coeffs[_M22];
        double n = coeffs[_M31];
        double o = coeffs[_M32];

        coeffs[_M11] = a * j + b * l + c * n;
        coeffs[_M12] = a * k + b * m + c * o;
        coeffs[_M21] = d * j + e * l + f * n;
        coeffs[_M22] = d * k + e * m + f * o;
        coeffs[_M31] = g * j + h * l + i * n;
        coeffs[_M32] = g * k + h * m + i * o;
    }

    #endregion

    #region ICloneable Members

    /// <summary>
    /// Creates a new object that is a copy of the current instance.
    /// </summary>
    /// <returns>
    /// A new object that is a copy of this instance.
    /// </returns>
    public object Clone()
    {
        double[] coeffCopy = (double[])coeffs.Clone();
        return new Matrix3x2(coeffCopy);
    }

    #endregion

    #region IAffineTransformCoefficients Members

    //
    // NB: M11, M12, M21, M22 members of IAffineTransformCoefficients are implemented within the
    // #region Public Properties directive
    //

    /// <summary>
    /// Gets or sets the Translation Offset in the X Direction
    /// </summary>
    /// <value>The M31</value>
    public double OffsetX
    {
        get
        {
            return coeffs[_M31];
        }
        set
        {
            coeffs[_M31] = value;
        }
    }

    /// <summary>
    /// Gets or sets the Translation Offset in the Y Direction
    /// </summary>
    /// <value>The M32</value>
    public double OffsetY
    {
        get
        {
            return coeffs[_M32];
        }
        set
        {
            coeffs[_M32] = value;
        }
    }

    #endregion
}

由此,我们可以使用与两个图像相对应的点列表来执行图像配准。为了澄清这意味着什么,假设您的全景照片具有某些相同的功能。两者都有大教堂尖顶,都有一棵树。配准图像 A 到 B 的点将是每个图像中对应的 X、Y 位置,即:两个图像中尖塔的 XY 位置将是一对点。

现在有了这个点列表,我们可以计算我们的变换:

    public Matrix3x2 ComputeForwardTransform(IList<Point> baselineLocations, IList<Point> registerLocations)
{
    if (baselineLocations.Count < 3 || registerLocations.Count < 3)
    {
        throw new Exception("ComputeForwardTransform()",
            "Unable to compute the forward transform. A minimum of 3 control point pairs are required");
    }

    if (baselineLocations.Count != registerLocations.Count)
    {
        throw new Exception("ComputeForwardTransform()",
            "Unable to compute the forward transform. The number of control point pairs in baseline and registration results must be equal");
    }

    // To compute 
    //    Transform = ((X^T * X)^-1 * X^T)U = (X^T * X)^-1 (X^T * U)

    //    X^T * X =
    //    [ Sum(x_i^2)   Sum(x_i*y_i) Sum(x_i) ]
    //    [ Sum(x_i*y_i) Sum(y_i^2)   Sum(y_i) ]
    //    [ Sum(x_i)     Sum(y_i)     Sum(1)=n ]

    //    X^T * U =
    //    [ Sum(x_i*u_i) Sum(x_i*v_i) ]
    //    [ Sum(y_i*u_i) Sum(y_i*v_i) ]
    //    [ Sum(u_i)     Sum(v_i) ]

    IList<Point> xy = baselineLocations;
    IList<Point> uv = registerLocations;

    double a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, n = xy.Count;
    double p = 0, q = 0, r = 0, s = 0, t = 0, u = 0;

    for (int i = 0; i < n; i++)
    {
        // Compute sum of squares for X^T * X
        a += xy[i].X * xy[i].X;
        b += xy[i].X * xy[i].Y;
        c += xy[i].X;
        d += xy[i].X * xy[i].Y;
        e += xy[i].Y * xy[i].Y;
        f += xy[i].Y;
        g += xy[i].X;
        h += xy[i].Y;

        // Compute sum of squares for X^T * U
        p += xy[i].X * uv[i].X;
        q += xy[i].X * uv[i].Y;
        r += xy[i].Y * uv[i].X;
        s += xy[i].Y * uv[i].Y;
        t += uv[i].X;
        u += uv[i].Y;
    }

    // Create matrices from the coefficients
    Matrix3x2 uMat = new Matrix3x2(p, q, r, s, t, u);
    Matrix3x3 xMat = new Matrix3x3(a, b, c, d, e, f, g, h, n);

    // Invert X
    Matrix3x3 xInv = xMat.Inverse;

    // Perform the multiplication to get the transform
    uMat.Multiply(xInv);

    // Matrix uMat now holds the image registration transform to go from the current result to baseline
    return uMat;
}

最后,上面的内容可以这样调用:

// 其中 xy1、xy2、xy3 是第一张图像中的控制点,uv1、uv2、uv3 是 // 第二张图片中的对应对 Matrix3x2 结果 = ComputeForwardTransform(new [] {xy1, xy2, xy3}.new [] {uv1, uv2, uv3});

无论如何,我希望这对你有帮助。我意识到它不是 GDI+ 特定的,但确实详细讨论了如何使用 3x3 变换来注册图像,这可以在 GDI+ 和 WPF 中使用。实际上,我的硬盘驱动器深处有一个代码示例,如果您需要对上述内容进行澄清,我很乐意与您进行更多讨论。

Below: Demo showing stiched images Image registration - choosing control points

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何缝合重叠很少的图像? 的相关文章

  • GCC C++ (ARM) 和指向结构体字段的 const 指针

    假设有一个简单的测试代码 typedef struct int first int second int third type t define ADDRESS 0x12345678 define REGISTER type t ADDRE
  • 获取两个字符串之间的公共部分c# [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我需要的是获取两个单词之间的共同部分并获取差异 例子 场景1 word1 感言 word2 Test 将返回 公共部分Test 不同之
  • 处理 fanart.tv Web 服务响应 JSON 和 C#

    我正在尝试使用 fanart tv Webservice API 但有几个问题 我正在使用 Json Net Newtonsoft Json 并通过其他 Web 服务将 JSON 响应直接反序列化为 C 对象 这里的问题是元素名称正在更改
  • 有什么工具可以说明每种方法运行需要多长时间?

    我的程序的某些部分速度很慢 我想知道是否有我可以使用的工具 例如它可以告诉我可以运行 methodA 花了 100ms 等等 或者类似的有用信息 如果您使用的是 Visual Studio Team System 性能工具 中有一个内置分析
  • ZLIB 解压缩

    我编写了一个小型应用程序 该应用程序应该解压缩以 gzip deflate 格式编码的数据 为了实现这一点 我使用 ZLIB 库 使用解压缩功能 问题是这个功能不起作用 换句话说 数据不是未压缩的 我在这里发布代码 int decompre
  • 如何用 kevent() 替换 select() 以获得更高的性能?

    来自Kqueue 维基百科页面 http en wikipedia org wiki Kqueue Kqueue 在内核和用户空间之间提供高效的输入和输出事件管道 因此 可以修改事件过滤器以及接收待处理事件 同时每次主事件循环迭代仅使用对
  • 为什么 std::allocator 在 C++17 中丢失成员类型/函数?

    一边看着std 分配器 http en cppreference com w cpp memory allocator 我看到成员 value type pointer const pointer reference const refer
  • 禁用 LINQ 上下文的所有延迟加载或强制预先加载

    我有一个文档生成器 目前包含约 200 个项目的查询 但完成后可能会超过 500 个 我最近注意到一些映射表示延迟加载 这给文档生成器带来了一个问题 因为它需要根据生成的文档来访问所有这些属性 虽然我知道DataLoadOptions可以指
  • 单元测试失败,异常代码为 c0000005

    我正在尝试使用本机单元测试项目在 Visual Studios 2012 中创建单元测试 这是我的测试 TEST METHOD CalculationsRoundTests int result Calculations Round 1 0
  • 为什么 FTPWebRequest 或 WebRequest 通常不接受 /../ 路径?

    我正在尝试从 ftp Web 服务器自动执行一些上传 下载任务 当我通过客户端甚至通过 Firefox 连接到服务器时 为了访问我的目录 我必须指定如下路径 ftp ftpserver com AB00000 incoming files
  • 事件日志写入错误

    很简单 我想向事件日志写入一些内容 protected override void OnStop TODO Add code here to perform any tear down necessary to stop your serv
  • 用于从字符串安全转换的辅助函数

    回到 VB6 我编写了一些函数 让我在编码时无需关心字符串的 null 和 数字的 null 和 0 等之间的区别 编码时 没有什么比添加特殊情况更能降低我的工作效率了用于处理可能导致一些不相关错误的数据的代码 9999 10000 如果我
  • 如何排列表格中的项目 - MVC3 视图 (Index.cshtml)

    我想使用 ASP NET MVC3 显示特定类型食品样本中存在的不同类型维生素的含量 如何在我的视图 Index cshtml 中显示它 an example 这些是我的代码 table tr th th foreach var m in
  • C# 搜索目录中包含字符串的所有文件,然后返回该字符串

    使用用户在文本框中输入的内容 我想搜索目录中的哪个文件包含该文本 然后我想解析出信息 但我似乎找不到该字符串或至少返回信息 任何帮助将不胜感激 我当前的代码 private void btnSearchSerial Click object
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 运行代码首先迁移更新数据库时出错

    我在迁移到数据库时遇到问题 并且似乎找不到我遇到的错误的答案 System MissingMethodException Method not found System Data Entity Migrations Builders Tab
  • 同时从多个流中捕获、最佳方法以及如何减少 CPU 使用率

    我目前正在编写一个应用程序 该应用程序将捕获大量 RTSP 流 在我的例子中为 12 个 并将其显示在 QT 小部件上 当我超过大约 6 7 个流时 问题就会出现 CPU 使用率激增并且出现明显的卡顿 我认为它不是 QT 绘制函数的原因是因
  • Swagger 为 ASP.CORE 3 中的字典生成错误的 URL

    当从查询字符串中提取的模型将字典作为其属性之一时 Swagger 会生成不正确的 URL 如何告诉 Swagger 更改 URL 中字典的格式或手动定义输入参数模式而不自动生成 尝试使用 Swashbuckle 和 NSwag 控制器 pu
  • Azure函数版本2.0-应用程序blobTrigger不工作

    我有一个工作功能应用程序 它有一个 blob 输入和一个事件中心输出 在测试版中工作 随着最新的更改 我的功能不再起作用 我尝试根据发行说明更新 host json 文件 但它没有引用 blob 触发器 version 2 0 extens
  • 如何创建向后兼容 Windows 7 的缩放和尺寸更改每显示器 DPI 感知应用程序?

    我是 WPF 和 DPI 感知 API 的新手 正在编写一个在 Windows 7 8 1 和 10 中运行的应用程序 我使用具有不同每个显示器 DPI 设置的多个显示器 并且有兴趣将我的应用程序制作为跨桌面配置尽可能兼容 我已经知道可以将

随机推荐

  • Mapbox 中路线的多色折线

    我用我的应用程序记录路线 主要是摩托车路线 并希望根据路线每个点的速度为其着色 基本上我知道如何在 Mapbox 中做到这一点 这就是问题所在 由于您只能为一条折线设置颜色 因此我必须将速度部分中的路线分开 并使用其特定的颜色打印每个路线部
  • WSO2ESB 4.5.1 axi2 属性 ssl.client.auth.cert.X509 始终为 NULL

    当使用NIO SSL监听器 org apache synapse transport nhttp HttpCoreNIOSSLListener 时 没有HttpServletContext不再可用 显然 因此深入研究可能解决我发现的客户端证
  • 如何在 aws elastic beanstalk 环境实例启动上运行 shell 脚本

    我正在使用 Terraform 脚本创建 aws elastic beanstalk 环境 我需要在实例启动时启动 shell 脚本 我已经尝试过以下操作 resource aws elastic beanstalk environment
  • 无边框 TabControl wpf (XP)

    我在 WPF 中设置 TabControl 样式时遇到视觉问题 因此 即使我将选项卡控件的边框设置为 0px 且透明 右侧和底部边框上仍然有一条非常细的线 我没有找到可以解决这个问题的属性 所以也许有人遇到了同样的问题并可以分享 提前致谢
  • LibGDX指导-精灵追踪2D无限随机贝塞尔曲线

    我已经能够将平滑的动画应用于我的精灵并使用加速度计控制它 我的精灵固定为沿 x 轴左右移动 从这里开始 我需要弄清楚如何创建一条垂直的无限波浪线供精灵尝试追踪 我的游戏的目的是让用户用加速度计控制精灵的左 右移动 试图尽可能地追踪永无止境的
  • MVC4 部分视图中的淘汰赛绑定

    简而言之 我相信我所追求的是一种在 MVC4 部分视图中为剔除绑定 javascript 对象提供范围 上下文的方法 这样我就可以重用相同的部分而不会让它们互相干扰 但仍然能够在客户端引用父子视图模型 作为一个出色的菜鸟 更广泛地说 是真正
  • 查找 html 块中最宽单词的宽度

    目标是找到这里最宽单词的宽度 文本是由不同字体的单词组成的句子 如图所示 the html looks like span style font bold 14px Verdana LONGESTW span span style font
  • WSO2 API Manager - 移动应用程序如何连接到 API Manager?

    我们有一个移动应用程序 需要通过 WSO2 API Manager 来访问一些 API 由于oauth2身份验证 我们需要在移动应用程序中存储用户名和密码 安全吗 例如 可以使用用户名和密码登录API Store 对于这种情况有其他解决方案
  • Rails cron 与无论何时,设置环境

    如果您了解用于创建 cron 作业的whengem 这个问题可能才有意义 我的 Schedule rb 中有一个任务 例如 every 1 day at gt 4am do command cd RAILS ROOT rake thinki
  • SQL Server 存储过程能否以比其调用者更高的权限执行?

    我们的 SQL Server 数据库具有报告功能 允许调用者读取但不能写入任何表 因为用户 或者更准确地说 代表用户操作的 Web 应用程序打开的连接 仅具有以下数据读取器权限 数据库 我们希望能够编写一个存储过程 它是一个特殊的 清理报告
  • 为什么使用setTimeout函数会立即执行?

    我正在尝试编写简单的代码setTimeout 但是setTimeout只是不会等待它应该等待的时间并且代码会立即执行 我究竟做错了什么 setTimeout testfunction 2000 您将立即调用该函数并安排其返回值 Use se
  • 神秘的阴谋集团安装问题

    全新安装 Haskell Platform OS X Snow Leopard Platform 2010 1 0 1 这样做会导致简单的序列导致非常奇怪cabal install行为 cabal install time cabal in
  • 如何在 Visual Studio C++ Express 版本中启用自动完成功能?

    请指导我 如何在 VS C 中启用自动完成功能 通过自动完成 我的意思是 当我在控件名称后面加点时 编辑器应该显示一个下拉菜单以供选择 谢谢 开始写作 然后只需按 CTRL SPACE 即可
  • 如何在 Heroku.com 上托管的 Redmine 上安装插件

    刚刚把redmine推送到heroku平台 虽然我无法让积压插件工作 但它在本地工作得很好 我已将本地数据库推送到heroku aswwell Heroku 的公共目录不可写 但默认的 Redmine 假设 要求如此 因为在应用程序服务器启
  • 在 apache Spark 中替换 groupByKey 的方法

    我想知道更换的最佳方法按键分组与另一个操作 基本上我想获得一个RDD int 列表 测量 我的情况 consider measures like RDD of objects measures keyBy getId groupByKey
  • 为什么在自定义 WinRT C++/CX 控件中默认添加 [Windows::Foundation::Metadata::WebHostHidden]?

    当我在 WinRT C CX 项目中创建新控件时 类属性 Windows Foundation Metadata WebHostHidden Visual Studio 2012 默认添加 Example namespace Windows
  • Java:使用 Swing 进行游戏编程

    我对游戏开发比较陌生 我现在已经开发游戏并学习游戏开发2 3个月了 我使用Java 我一直使用 Swing 来制作图形 也就是说 整个游戏都显示在JPanel 用一个Graphics2D目的 到目前为止我没有遇到任何问题 最近 我在最近的项
  • 如何使用 gradle 'api' 依赖项

    我尝试在项目中使用 api 依赖关键字 但收到此错误 指出找不到方法 api 我在一个新项目上尝试过 这是 build gradle 文件 plugins id java group com test version 1 0 SNAPSHO
  • 如何在 wordpress ul 容器周围添加 div

    WordPress 在这个 ul 标签内输出我的子菜单 ul class sub menu 我怎样才能在它周围包裹一个简单的div 最好是通过functions php 来完成此操作 但jquery 也可以工作 虽然使用 jQuery 之类
  • 如何缝合重叠很少的图像?

    我正在尝试使用重叠很少的图像创建全景图 但我知道相机的角度 因此我确切地知道有多少重叠 并且我知道图像的顺序 因此我知道每个图像在全景图中的位置 作为第一步 我只是将图像连接在一起 但结果不够好 有没有办法将位图裁剪为梯形以消除 大部分 重