namespace VisualMath.Accord.Imaging { using System; using System.Drawing; /// /// Encapsulates a 3-by-3 general transformation matrix that represents /// a (possibly) non-linear transform. /// /// /// /// Linear transformations are not the only ones that can be represented by /// matrices. Using homogeneous coordinates, both affine transformations and /// perspective projections on R^n can be represented as linear transformations /// on R^n+1 (that is, n+1-dimensional real projective space). /// /// The general transformation matrix has 8 degrees of freedom, as the last element is just a scale parameter. /// /// [Serializable] public class MatrixH { private float[] elements; /// /// Creates a new projective matrix. /// public MatrixH() { // Start as the identity matrix this.elements = new float[] { 1, 0, 0, 0, 1, 0, 0, 0 }; } /// /// Creates a new projective matrix. /// public MatrixH(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32) { this.elements = new float[8]; this.elements[0] = m11; this.elements[1] = m12; this.elements[2] = m13; this.elements[3] = m21; this.elements[4] = m22; this.elements[5] = m23; this.elements[6] = m31; this.elements[7] = m32; } /// /// Creates a new projective matrix. /// public MatrixH(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33) : this(m11, m12, m13, m21, m22, m23, m31, m32) { for (int i = 0; i < 8; i++) elements[i] /= m33; } /// /// Creates a new projective matrix. /// public MatrixH(double[,] H) { this.elements = new float[8]; for (int i = 0, k = 0; i < 3; i++) for (int j = 0; j < 3 && k < 8; j++, k++) this.elements[k] = (float)(H[i, j] / H[2, 2]); } /// /// Gets the elements of this matrix. /// public float[] Elements { get { return elements; } } /// /// Gets the offset x /// public float OffsetX { get { return elements[2]; } } /// /// Gets the offset y /// public float OffsetY { get { return elements[5]; } } /// /// Gets whether this matrix is invertible. /// public bool IsInvertible { get { float det = elements[0] * (elements[4] - elements[5] * elements[7]) - elements[1] * (elements[3] - elements[5] * elements[6]) + elements[2] * (elements[3] * elements[7] - elements[4] * elements[6]); return det > 0; } } /// /// Gets whether this is an Affine transformation matrix. /// public bool IsAffine { get { return (elements[6] == 0 && elements[7] == 0); } } /// /// Gets whether this is the identity transformation. /// public bool IsIdentity { get { return elements[0] == 1 && elements[1] == 0 && elements[2] == 0 && elements[3] == 0 && elements[4] == 1 && elements[5] == 0 && elements[6] == 0 && elements[7] == 0; } } /// /// Resets this matrix to be the identity. /// public void Reset() { elements[0] = 1; elements[1] = 0; elements[2] = 0; elements[3] = 0; elements[4] = 1; elements[5] = 0; elements[6] = 0; elements[7] = 0; } /// /// Returns the inverse matrix, if this matrix is invertible. /// public MatrixH Inverse() { // m = 1 / [a(ei-fh) - b(di-fg) + c(dh-eg)] // // (ei-fh) (ch-bi) (bf-ce) // inv(A) = m x (fg-di) (ai-cg) (cd-af) // (dh-eg) (bg-ah) (ae-bd) // float a = this.elements[0], b = this.elements[1], c = this.elements[2]; float d = this.elements[3], e = this.elements[4], f = this.elements[5]; float g = this.elements[6], h = this.elements[7]; float m = 1f / (a * (e - f * h) - b * (d - f * g) + c * (d * h - e * g)); float na = m * (e - f * h); float nb = m * (c * h - b); float nc = m * (b * f - c * e); float nd = m * (f * g - d); float ne = m * (a - c * g); float nf = m * (c * d - a * f); float ng = m * (d * h - e * g); float nh = m * (b * g - a * h); float nj = m * (a * e - b * d); return new MatrixH(na, nb, nc, nd, ne, nf, ng, nh, nj); } /// /// Transforms the given points using this transformation matrix. /// public PointH[] TransformPoints(params PointH[] points) { PointH[] r = new PointH[points.Length]; for (int j = 0; j < points.Length; j++) { r[j].X = elements[0] * points[j].X + elements[1] * points[j].Y + elements[2] * points[j].W; r[j].Y = elements[3] * points[j].X + elements[4] * points[j].Y + elements[5] * points[j].W; r[j].W = elements[6] * points[j].X + elements[7] * points[j].Y + points[j].W; } return r; } /// /// Transforms the given points using this transformation matrix. /// public PointF[] TransformPoints(params PointF[] points) { PointF[] r = new PointF[points.Length]; for (int j = 0; j < points.Length; j++) { float w = elements[6] * points[j].X + elements[7] * points[j].Y + 1f; r[j].X = (elements[0] * points[j].X + elements[1] * points[j].Y + elements[2]) / w; r[j].Y = (elements[3] * points[j].X + elements[4] * points[j].Y + elements[5]) / w; } return r; } /// /// Multiplies this matrix, returning a new matrix as result. /// public MatrixH Multiply(MatrixH matrix) { float na = elements[0] * matrix.elements[0] + elements[1] * matrix.elements[3] + elements[2] * matrix.elements[6]; float nb = elements[0] * matrix.elements[1] + elements[1] * matrix.elements[4] + elements[2] * matrix.elements[7]; float nc = elements[0] * matrix.elements[2] + elements[1] * matrix.elements[5] + elements[2]; float nd = elements[3] * matrix.elements[0] + elements[4] * matrix.elements[3] + elements[5] * matrix.elements[6]; float ne = elements[3] * matrix.elements[1] + elements[4] * matrix.elements[4] + elements[5] * matrix.elements[7]; float nf = elements[3] * matrix.elements[2] + elements[4] * matrix.elements[5] + elements[5]; float ng = elements[6] * matrix.elements[0] + elements[7] * matrix.elements[3] + matrix.elements[6]; float nh = elements[6] * matrix.elements[1] + elements[7] * matrix.elements[4] + matrix.elements[7]; float ni = elements[6] * matrix.elements[2] + elements[7] * matrix.elements[5] + 1f; return new MatrixH(na, nb, nc, nd, ne, nf, ng, nh, ni); } /// /// Compares two objects for equality. /// public override bool Equals(object obj) { if (obj is MatrixH) { MatrixH m = obj as MatrixH; return this == m; } else { return false; } } /// /// Returns the hash code for this instance. /// public override int GetHashCode() { return elements.GetHashCode(); } /// /// Double[,] conversion. /// public static explicit operator double[,](MatrixH matrix) { return new double[,] { { matrix.elements[0], matrix.elements[1], matrix.elements[2] }, { matrix.elements[3], matrix.elements[4], matrix.elements[5] }, { matrix.elements[6], matrix.elements[7], 1.0 }, }; } /// /// Single[,] conversion. /// public static explicit operator float[,](MatrixH matrix) { return new float[,] { { matrix.elements[0], matrix.elements[1], matrix.elements[2] }, { matrix.elements[3], matrix.elements[4], matrix.elements[5] }, { matrix.elements[6], matrix.elements[7], 1.0f }, }; } /// /// Matrix multiplication. /// public static MatrixH operator *(MatrixH matrix1, MatrixH matrix2) { return matrix1.Multiply(matrix2); } /// /// Equality /// public static bool operator ==(MatrixH a, MatrixH b) { for (int i = 0; i < 8; i++) if (a.elements[i] != b.elements[i]) return false; return true; } /// /// Inequality /// public static bool operator !=(MatrixH a, MatrixH b) { for (int i = 0; i < 8; i++) if (a.elements[i] == b.elements[i]) return true; return false; } } }