2011-12-30 16 views
14

Estoy tratando de crear un panorama utilizando imágenes con muy poca superposición, pero conozco el ángulo de la cámara, así sé exactamente cuánta superposición hay y sé el orden de las imágenes para saber a qué pertenece cada una de ellas. panorama. Como primer paso simplemente cocineto las imágenes, pero el resultado no es suficiente. ¿Hay alguna manera de recortar los mapas de bits en trapezoides para eliminar (la mayoría de) la superposición, luego estirar los mapas de bits de nuevo a rectángulos antes de la connación? Sé que esto producirá distorsión durante el estiramiento, y que un trapezoide es solo una aproximación cercana a la forma en que el mapa de bits realmente necesita ser recortado, pero espero que esto sea lo suficientemente bueno.Cómo unir imágenes con muy poca superposición?

Respuesta

25

La técnica que está buscando se llama Image Registration usando una Transformada afín. Esto se puede lograr en el software calculando la transformación de la matriz para la imagen B que se correlaciona con la imagen A. Supongo que está intentando hacer esto en el software usando Windows Forms & GDI +? Entonces las matrices que tienes disponibles son matrices de 3x3, capaces de Escalar, Traducir, Rotar y Sesgar. Esto es a menudo suficiente para crear un registro de imagen simple y utilicé esta técnica con éxito en un paquete de software comercial (sin embargo, fue WPF).

Para lograr el registro de la imagen utilizando una Transformación afín, primero necesita una colección de puntos de control en un par de imágenes para registrarse. De esto podemos calcular la transformación 2D para registrar las imágenes? He hecho esto en WPF, que tiene una Matriz 3x3 que se puede definir usando System.Windows.Media.clase Matrix, que tiene la siguiente constructor:

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

Nota: GDI + tiene una clase Matrix cuyo constructor puede ser diferente, pero el principio es el mismo

Los argumentos de constructor forman la matriz como sigue:

 
M11  M12  0 
M21  M22  0 
OffsetX OffsetY 1 

Ahora bien, si los puntos de entrada se denominan X, y y de salida U, V, la matriz afín transformar, T, que mapas X, y en U, V se pueden calcular como sigue:

 
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] 

Esto también se puede simplificar como sigue:

 
U  = X   * T 

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

o

 
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] 

En Inglés lo que esto significa es, dado una lista de los puntos X, Y en la imagen 1 que corresponden a imagen 2, la inversa de la matriz X que contiene puntos XY multiplicados por la matriz de puntos correspondientes en la imagen 2 le da la transformación de su matriz de la Imagen 1 a 2

La transformación de salida T contiene A, B, C, D y Tx, Ty que corresponde a M11, M12, M21, M22, OffsetX, OffsetY en la clase de matriz afín 3x3 (constructor anterior). Sin embargo, si la matriz X y la matriz U tienen más de 3 puntos, la solución está sobredeterminada y se debe encontrar un ajuste por mínimos cuadrados. Esto se logra usando el Moores-Penrose Psuedo-inverso para encontrar X^-1.

¿Qué significa esto en el código? Bueno, codifiqué mis propias clases Matrix3x3, Matrix3x2 y el Punto de control (punto x, y) para manejar la transformación y luego apliqué esto a una matriz de transformación WPF en un elemento. En GDI +, puede hacer lo mismo al aplicar Matrix a la canalización de gráficos antes de llamar a Graphics.DrawImage. Veamos cómo podemos calcular la matriz de transformación.

La primera clase que necesitamos es la clase 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 
} 

y una clase 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 
} 

De estos podemos llevar a cabo el registro de imágenes con una lista de puntos que corresponden a las dos imágenes . Para aclarar lo que esto significa, digamos que sus fotos panorámicas tienen ciertas características que son las mismas. Ambos tienen una aguja de catedral, ambos tienen un árbol. Los puntos que registran las imágenes A a B serían las ubicaciones X, Y en cada imagen que corresponda, es decir: la ubicación XY de la aguja en ambas imágenes sería un par de puntos.

Ahora, con esta lista de puntos podemos calcular nuestra transformación:

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; 
} 

Finalmente, lo anterior se puede llamar de la siguiente manera:

// donde xy1, xy2, xy3 son puntos de control en la primera imagen, y uv1, uv2, uv3 son // pares correspondientes en la segunda imagen Resultado Matrix3x2 = ComputeForwardTransform (new [] {xy1, xy2, xy3}. new [] {uv1, uv2, uv3});

De todos modos, espero que esto sea útil para usted.Me doy cuenta de que no es específico de GDI +, pero sí analiza cómo registrar imágenes con transformaciones 3x3 en detalle, que se pueden usar tanto en GDI + como en WPF. De hecho, tengo un ejemplo de código en algún lugar de mi disco duro y me gustaría hablar más si necesita una aclaración sobre lo anterior.

continuación: prueba donde se muestran imágenes stiched Image registration - choosing control points

Image registration result - panoramas have been stiched

+0

+1 Me gusta! Esperando el código. :) – rfmodulator

+0

¡Cómo llegar! Lol –

+0

Muy bien, ese es el código de la matriz. ¿Qué piensas? Estoy buscando una aplicación de prueba que escribí en mi disco duro para ver si puedo poner una captura de pantalla de panoramas cosidos usando este método ... –

6

Lo que quiere se conoce como transformación matricial.

Here son algunos ejemplos simples en C#/GDI +.

MSDN tiene algunas descripciones más en profundidad.

Creo que al final buscará una "Transformación de Perspectiva", here es una ASEJA que podría llevarlo por el camino correcto.

No me preocupa la recompensa, este es un tema complejo (y divertido) y no tengo tiempo para buscar una solución, solo espero que esta información sea útil. :)

+0

Por favor, no vote esto. Hasta vote Andrew en cambio. – rfmodulator

+0

Eso es muy deportivo de su parte, simplemente no voté y deduje 10 puntos de usted. Me temo que también soy un tipo deportivo, así que volveré a votar. ¡Perdón por la confusion! lol –

+0

Lol, supongo ... pero esto no pretende ser una respuesta en absoluto, es demasiado grande para un comentario. :) Gracias, sin embargo. – rfmodulator

Cuestiones relacionadas