Example #1
0
        public void ApplyTest()
        {
            var img1 = Properties.Resources.image2;
            var img2 = Properties.Resources.image2;

            MatrixH homography = new MatrixH(1, 0, 32,
                                             0, 1, 0,
                                             0, 0);

            Blend blend = new Blend(homography, img1);
            var actual = blend.Apply(img2);

            Assert.AreEqual(64, actual.Size.Width);
            Assert.AreEqual(32, actual.Size.Height);



            homography = new MatrixH(1, 0, 32,
                                     0, 1, 32,
                                     0, 0);

            blend = new Blend(homography, img1);
            actual = blend.Apply(img2);


            Assert.AreEqual(64, actual.Size.Width);
            Assert.AreEqual(64, actual.Size.Height);

            // ImageBox.Show(img3, PictureBoxSizeMode.Zoom);
        }
Example #2
0
        /// <summary>
        /// The blending filter is able to blend two images using a homography matrix.
        /// A linear alpha gradient is used to smooth out differences between the two
        /// images, effectively blending them in two images. The gradient is computed
        /// considering the distance between the centers of the two images.
        /// </summary>
        /// <param name="im">Image.</param>
        /// <param name="overlayIm">The overlay image (also called the anchor).</param>
        /// <param name="homography">Homography matrix used to map a image passed to
        /// the filter to the overlay image specified at filter creation.</param>
        /// <param name="fillColor">The filling color used to fill blank spaces. The filling color will only be visible after the image is converted
        /// to 24bpp. The alpha channel will be used internally by the filter.</param>
        /// <param name="gradient">A value indicating whether to blend using a linear
        ///  gradient or just superimpose the two images with equal weights.</param>
        /// <param name="alphaOnly">A value indicating whether only the alpha channel
        /// should be blended. This can be used together with a transparency
        /// mask to selectively blend only portions of the image.</param>
        /// <returns>Blended image.</returns>
        public static Bgra<byte>[,] Blend(this Gray<byte>[,] im, Gray<byte>[,] overlayIm, MatrixH homography, Bgra<byte> fillColor, bool gradient = true, bool alphaOnly = false)
        {
            Bgra<byte>[,] resultImage = null;

            using (var uOverlayIm = overlayIm.Lock())
            {
                Blend blend = new Blend(homography, uOverlayIm.AsBitmap());
                blend.AlphaOnly = alphaOnly;
                blend.Gradient = gradient;
                blend.FillColor = fillColor.ToColor();

                resultImage = im.ApplyBaseTransformationFilter<Gray<byte>, Bgra<byte>>(blend);
            }

            return resultImage;
        }
Example #3
0
        public void MultiplyTest()
        {
            MatrixH A = new MatrixH();
            MatrixH B = new MatrixH();

            MatrixH expected = new MatrixH();
            MatrixH actual = A.Multiply(B);

            Assert.IsTrue(Accord.Math.Matrix.IsEqual(
                expected.Elements, actual.Elements));


            double[,] a = 
            {
                { 2, 3, 1 },
                { 2, 1, 7 },
                { 1, 2, 2 }
            };

            double[,] b = 
            {
                {  1,  3,  1 },
                { -2,  1, -4 },
                {  1, -1,  3 }
            };

            A = new MatrixH(a);
            B = new MatrixH(b);

            actual = A.Multiply(B);
            expected = new MatrixH(Accord.Math.Matrix.Multiply(a, b));

            Assert.IsTrue(Accord.Math.Matrix.IsEqual(
                (double[,])actual, (double[,])expected,
                0.001));
        }
Example #4
0
 /// <summary>
 ///   Normalizes a set of homogeneous points so that the origin is located
 ///   at the centroid and the mean distance to the origin is sqrt(2).
 /// </summary>
 public static PointF[] Normalize(this PointF[] points, out MatrixH transformation)
 {
     float[,] H;
     var result = Normalize(points, out H);
     transformation = new MatrixH(H);
     return result;
 }
Example #5
0
        /// <summary>
        ///   Creates an homography matrix matching points
        ///   from a set of points to another.
        /// </summary>
        /// 
        public static MatrixH Homography(PointF[] points1, PointF[] points2)
        {
            // Initial argument checks
            if (points1.Length != points2.Length)
                throw new ArgumentException("The number of points should be equal.");

            if (points1.Length < 4)
                throw new ArgumentException("At least four points are required to fit an homography");


            int N = points1.Length;

            MatrixH T1, T2; // Normalize input points
            points1 = Tools.Normalize(points1, out T1);
            points2 = Tools.Normalize(points2, out T2);

            // Create the matrix A
            var A = new float[3 * N, 9];
            for (int i = 0; i < N; i++)
            {
                PointF X = points1[i];
                float x = points2[i].X;
                float y = points2[i].Y;
                int r = 3 * i;

                A[r, 0] = 0;
                A[r, 1] = 0;
                A[r, 2] = 0;
                A[r, 3] = -X.X;
                A[r, 4] = -X.Y;
                A[r, 5] = -1;
                A[r, 6] = y * X.X;
                A[r, 7] = y * X.Y;
                A[r, 8] = y;

                r++;
                A[r, 0] = X.X;
                A[r, 1] = X.Y;
                A[r, 2] = 1;
                A[r, 3] = 0;
                A[r, 4] = 0;
                A[r, 5] = 0;
                A[r, 6] = -x * X.X;
                A[r, 7] = -x * X.Y;
                A[r, 8] = -x;

                r++;
                A[r, 0] = -y * X.X;
                A[r, 1] = -y * X.Y;
                A[r, 2] = -y;
                A[r, 3] = x * X.X;
                A[r, 4] = x * X.Y;
                A[r, 5] = x;
                A[r, 6] = 0;
                A[r, 7] = 0;
                A[r, 8] = 0;
            }


            // Create the singular value decomposition
            SingularValueDecompositionF svd = new SingularValueDecompositionF(A,
                computeLeftSingularVectors: false, computeRightSingularVectors: true,
                autoTranspose: false, inPlace: true);

            float[,] V = svd.RightSingularVectors;


            // Extract the homography matrix
            MatrixH H = new MatrixH(V[0, 8], V[1, 8], V[2, 8],
                                    V[3, 8], V[4, 8], V[5, 8],
                                    V[6, 8], V[7, 8], V[8, 8]);

            // Denormalize
            H = T2.Inverse().Multiply(H.Multiply(T1));

            return H;
        }
Example #6
0
        public void ApplyTest2()
        {
            var img1 = Properties.Resources.image2;
            var img2 = Properties.Resources.image2;

            var img3 = Properties.Resources.image2;
            var img4 = Properties.Resources.image2;


            MatrixH homography;
            Blend blend;

            homography = new MatrixH(1, 0, 32,
                                     0, 1, 0,
                                     0, 0);


            blend = new Blend(homography, img1);
            var img12 = blend.Apply(img2);

            //ImageBox.Show("Blend of 1 and 2", img12, PictureBoxSizeMode.Zoom);
            Assert.AreEqual(img12.PixelFormat, PixelFormat.Format32bppArgb);

            blend = new Blend(homography, img3);
            var img34 = blend.Apply(img4);

            //ImageBox.Show("Blend of 3 and 4", img34, PictureBoxSizeMode.Zoom);
            Assert.AreEqual(img34.PixelFormat, PixelFormat.Format32bppArgb);


            homography = new MatrixH(1, 0, 64,
                                     0, 1, 0,
                                     0, 0);


            blend = new Blend(homography, img12);
            var img1234 = blend.Apply(img34);

            //ImageBox.Show("Blend of 1, 2, 3, 4", img1234, PictureBoxSizeMode.Zoom);
            Assert.AreEqual(img1234.PixelFormat, PixelFormat.Format32bppArgb);



            // Blend of 1 and 5 (8bpp and 32bpp)
            homography = new MatrixH(1, 0, 0,
                                     0, 1, 32,
                                     0, 0);


            //ImageBox.Show("Image 1", img1, PictureBoxSizeMode.Zoom);
            blend = new Blend(homography, img1234);
            var img15 = blend.Apply(img1);
            //ImageBox.Show("Blend of 1 and 5", img15, PictureBoxSizeMode.Zoom);

            Assert.AreEqual(img1234.PixelFormat, PixelFormat.Format32bppArgb);
            Assert.AreEqual(img1.PixelFormat, PixelFormat.Format8bppIndexed);
            Assert.AreEqual(img15.PixelFormat, PixelFormat.Format32bppArgb);
            Assert.AreEqual(128, img15.Width);
            Assert.AreEqual(64, img15.Height);

        }
Example #7
0
        /// <summary>
        ///   Multiplies this matrix, returning a new matrix as result.
        /// </summary>
        /// 
        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);
        }
Example #8
0
        /// <summary>
        /// Matches detected keypoints.
        /// </summary>
        private void MatchKeypoints()
        {
            // matching keypoints step needs at least two images
            if (input_images.Count < 2) return;

            // using RANSAC homography estimator
            RansacHomographyEstimator ransac = new RansacHomographyEstimator(0.001, 0.99);
            CorrelationMatching matcher = new CorrelationMatching(9);

            Bitmap mergedImage = new Bitmap(input_images.Count * input_images[0].Width, input_images[0].Height); //it is assumed the images are of same size!
            Graphics graphics = Graphics.FromImage(mergedImage);

            IntPoint[][] matches;

            int cumulativeWidth = 0;
            int keypoints_cntr = 0;

            // draw all images
            for (int i = 0; i < input_images.Count; i++) {
                graphics.DrawImage(input_images[i], cumulativeWidth, 0, input_images[i].Width, input_images[i].Height);
                cumulativeWidth += input_images[i].Width;
            }

            // iterate through all images
            for (int i = 0; i < input_images.Count; i++)
            {

                if (i != input_images.Count - 1)
                {
                    // match detected keypoints with maximum cross-correlation algorithm
                    matches = matcher.Match(new Bitmap(input_images[i]), new Bitmap(input_images[i + 1]), keypoints[keypoints_cntr].ToArray(), keypoints[keypoints_cntr + 1].ToArray());

                    IntPoint[] correlationPoints1 = matches[0];
                    IntPoint[] correlationPoints2 = matches[1];

                    // Plot RANSAC results against correlation results
                    homography = ransac.Estimate(correlationPoints1, correlationPoints2);

                    // take only inliers - good matches
                    IntPoint[] inliers1 = correlationPoints1.Submatrix(ransac.Inliers);
                    IntPoint[] inliers2 = correlationPoints2.Submatrix(ransac.Inliers);

                    PairsMarker pairs = new PairsMarker(correlationPoints1, correlationPoints2.Apply(p => new IntPoint(p.X + input_images[i].Width, p.Y)));

                    // draw all matching pairs
                    for (int j = 0; j < pairs.Points1.Count(); j++) {
                        if (i % 2 == 0) graphics.DrawLine(new Pen(Color.Red, 1.5f), new System.Drawing.Point(correlationPoints1[j].X + i * input_images[i].Width, correlationPoints1[j].Y), new System.Drawing.Point(correlationPoints2[j].X + (i + 1) * input_images[i].Width, correlationPoints2[j].Y));
                    }

                    // draw inliers
                    for (int j = 0; j < inliers1.Count(); j++) {
                        if (i % 2 == 0) graphics.DrawLine(new Pen(Color.GreenYellow, 1.5f), new System.Drawing.Point(inliers1[j].X + i * input_images[i].Width, inliers1[j].Y), new System.Drawing.Point(inliers2[j].X + (i + 1) * input_images[i].Width, inliers2[j].Y));
                        else            graphics.DrawLine(new Pen(Color.Blue, 1.5f), new System.Drawing.Point(inliers1[j].X + i * input_images[i].Width, inliers1[j].Y), new System.Drawing.Point(inliers2[j].X + (i + 1) * input_images[i].Width, inliers2[j].Y));
                    }

                    keypoints_cntr += 2;
                }
            }

            ShowThumbnail(mergedImage, true, PanoramaPhase.MatchKeypoints);
        }
Example #9
0
        private void button2_Click(object sender, EventArgs e)
        {
            // Step 3: Create the homography matrix using a robust estimator
            RansacHomographyEstimator ransac = new RansacHomographyEstimator(0.001, 0.99);
            homography = ransac.Estimate(correlationPoints1, correlationPoints2);

            // Plot RANSAC results against correlation results
            IntPoint[] inliers1 = correlationPoints1.Submatrix(ransac.Inliers);
            IntPoint[] inliers2 = correlationPoints2.Submatrix(ransac.Inliers);

            // Concatenate the two images in a single image (just to show on screen)
            Concatenate concat = new Concatenate(img1);
            Bitmap img3 = concat.Apply(img2);

            // Show the marked correlations in the concatenated image
            PairsMarker pairs = new PairsMarker(
                inliers1, // Add image1's width to the X points to show the markings correctly
                inliers2.Apply(p => new IntPoint(p.X + img1.Width, p.Y)));

            pictureBox1.Image = pairs.Apply(img3);

            numA.Value = (decimal)homography.Elements[0];
            numB.Value = (decimal)homography.Elements[1];
            numC.Value = (decimal)homography.Elements[2];

            numD.Value = (decimal)homography.Elements[3];
            numE.Value = (decimal)homography.Elements[4];
            numF.Value = (decimal)homography.Elements[5];

            numG.Value = (decimal)homography.Elements[6];
            numH.Value = (decimal)homography.Elements[7];
        }
Example #10
0
        public void MultiplyTest()
        {
            MatrixH matrix = new MatrixH(Matrix.Identity(3));

            PointH[] points = new PointH[] 
            {
                new PointH(1, 2),
                new PointH(5, 2),
                new PointH(12, 2),
                new PointH(1, 2),
                new PointH(10, 2),
            };


            PointH[] expected = new PointH[] 
            {
                new PointH(1, 2),
                new PointH(5, 2),
                new PointH(12, 2),
                new PointH(1, 2),
                new PointH(10, 2),
            };

            PointH[] actual = (PointH[])points.Clone();
            matrix.TransformPoints(actual);

            Assert.AreEqual(expected[0], actual[0]);
            Assert.AreEqual(expected[1], actual[1]);
            Assert.AreEqual(expected[2], actual[2]);
            Assert.AreEqual(expected[3], actual[3]);
            Assert.AreEqual(expected[4], actual[4]);

        }
Example #11
0
        private bool GenerateKeypointMatches(Bitmap target, Bitmap frame, Rectangle prevTarget)
        {
            HarrisCornersDetector harrisDetector = new HarrisCornersDetector(HarrisCornerMeasure.Harris, 1000f, 1f, 2);
            targetKeyPoints=harrisDetector.ProcessImage(target).ToArray();
            frameKeyPoints=harrisDetector.ProcessImage(frame).ToArray();
            //Console.WriteLine("tr={0} fr={1}", targetKeyPoints.Length, frameKeyPoints.Length);
            if (targetKeyPoints.Length==0||frameKeyPoints.Length==0)
            {
                return false;
            }

            CorrelationMatching matcher=new CorrelationMatching(15);
            IntPoint[][] matches=matcher.Match(target, frame, targetKeyPoints, frameKeyPoints);
            targetMacthes=matches[0];
            frameMatches=matches[1];
            if (targetMacthes.Length<4||frameMatches.Length<4)
            {
                return false;
            }
            RansacHomographyEstimator ransac=new RansacHomographyEstimator(0.1, 0.50);
            MatrixH estiment = new MatrixH();
            try
            {

                estiment = ransac.Estimate(targetMacthes, frameMatches);
            }
            catch
            {
                return false;
            }
            IntPoint[] targetGoodMatch=targetMacthes.Submatrix(ransac.Inliers);
            IntPoint[] frameGoodMatch=frameMatches.Submatrix(ransac.Inliers);
            CalculatePosChange(targetGoodMatch, frameGoodMatch, prevTarget);
            return true;
        }
Example #12
0
 /// <summary>
 ///   Constructs a new Blend filter.
 /// </summary>
 /// 
 /// <param name="homography">The homography matrix mapping a second image to the overlay image.</param>
 /// <param name="overlayImage">The overlay image (also called the anchor).</param>
 /// 
 public Blend(MatrixH homography, Bitmap overlayImage)
 {
     this.homography = homography;
     this.overlayImage = overlayImage;
     formatTranslations[PixelFormat.Format8bppIndexed] = PixelFormat.Format32bppArgb;
     formatTranslations[PixelFormat.Format24bppRgb] = PixelFormat.Format32bppArgb;
     formatTranslations[PixelFormat.Format32bppArgb] = PixelFormat.Format32bppArgb;
 }
Example #13
0
        private void BtnRansac_OnClick(object sender, RoutedEventArgs e)
        {
            // Step 3: Create the homography matrix using a robust estimator
            RansacHomographyEstimator ransac = new RansacHomographyEstimator(0.001, 0.99);
            homography = ransac.Estimate(correlationPoints1, correlationPoints2);

            // Plot RANSAC results against correlation results
            IntPoint[] inliers1 = correlationPoints1.Submatrix(ransac.Inliers);
            IntPoint[] inliers2 = correlationPoints2.Submatrix(ransac.Inliers);

            // Concatenate the two images in a single image (just to show on screen)
            Concatenate concat = new Concatenate(img1);
            Bitmap img3 = concat.Apply(img2);

            // Show the marked correlations in the concatenated image
            PairsMarker pairs = new PairsMarker(
                inliers1, // Add image1's width to the X points to show the markings correctly
                inliers2.Apply(p => new IntPoint(p.X + img1.Width, p.Y)));

            PictureBox.Source = pairs.Apply(img3);
        }
        public void ApplyTest()
        {
            Bitmap img1 = Properties.Resources.image2;


            MatrixH homography = new MatrixH(1, 0, 32,
                                              0, 1, 0,
                                              0, 0);

            ProjectiveTransform transform = new ProjectiveTransform(homography);
            transform.FillColor = Color.Red;
            Bitmap actual = transform.Apply(img1);

            ImageBox.Show(actual, PictureBoxSizeMode.Zoom);

            Assert.AreEqual(64, actual.Size.Width);
            Assert.AreEqual(32, actual.Size.Height);



            homography = new MatrixH(2, 0, 0,
                                     0, 2, 0,
                                     0, 0);

            transform = new ProjectiveTransform(homography);
            transform.FillColor = Color.Red;
            actual = transform.Apply(img1);

            ImageBox.Show(actual, PictureBoxSizeMode.Zoom);

            Assert.AreEqual(32, actual.Size.Width);
            Assert.AreEqual(32, actual.Size.Height);



            homography = new MatrixH(2, 0, 32,
                                     0, 0.5f, 32,
                                     0, 0);

            transform = new ProjectiveTransform(homography);
            transform.FillColor = Color.Red;
            actual = transform.Apply(img1);

            ImageBox.Show(actual, PictureBoxSizeMode.Zoom);

            Assert.AreEqual(32 / 2 + 32, actual.Size.Width);
            Assert.AreEqual(32 / 0.5 + 32, actual.Size.Height);



            homography = new MatrixH(1, 0, -32,
                                     0, 1,   0,
                                     0, 0);

            transform = new ProjectiveTransform(homography);
            transform.FillColor = Color.Red;
            actual = transform.Apply(img1);

            ImageBox.Show(actual, PictureBoxSizeMode.Zoom);

            Assert.AreEqual(64, actual.Size.Width);
            Assert.AreEqual(32, actual.Size.Height);
        }
        /// <summary>
        ///   Compute inliers using the Symmetric Transfer Error,
        /// </summary>
        /// 
        private int[] distance(MatrixH H, double t)
        {
            // Compute the projections (both directions)
            PointF[] p1 = H.TransformPoints(pointSet1);
            PointF[] p2 = H.Inverse().TransformPoints(pointSet2);

            // Compute the distances
            for (int i = 0; i < pointSet1.Length; i++)
            {
                // Compute the distance as
                float ax = pointSet1[i].X - p2[i].X;
                float ay = pointSet1[i].Y - p2[i].Y;
                float bx = pointSet2[i].X - p1[i].X;
                float by = pointSet2[i].Y - p1[i].Y;
                d2[i] = (ax * ax) + (ay * ay) + (bx * bx) + (by * by);
            }

            // Find and return the inliers
            return Matrix.Find(d2, z => z < t);
        }
Example #16
0
        private void btnRansac_Click(object sender, EventArgs e)
        {
            if (correlationPoints1 == null)
            {
                MessageBox.Show("Please, click Nearest Neighbor button first! :-)");
                return;
            }

            if (correlationPoints1.Length < 4 || correlationPoints2.Length < 4)
            {
                MessageBox.Show("Insufficient points to attempt a fit.");
                return;
            }

            // Step 3: Create the homography matrix using a robust estimator
            RansacHomographyEstimator ransac = new RansacHomographyEstimator(0.001, 0.99);
            homography = ransac.Estimate(correlationPoints1, correlationPoints2);

            // Plot RANSAC results against correlation results
            IntPoint[] inliers1 = correlationPoints1.Submatrix(ransac.Inliers);
            IntPoint[] inliers2 = correlationPoints2.Submatrix(ransac.Inliers);

            // Concatenate the two images in a single image (just to show on screen)
            Concatenate concat = new Concatenate(img1);
            Bitmap img3 = concat.Apply(img2);

            // Show the marked correlations in the concatenated image
            PairsMarker pairs = new PairsMarker(
                inliers1, // Add image1's width to the X points to show the markings correctly
                inliers2.Apply(p => new IntPoint(p.X + img1.Width, p.Y)));

            pictureBox.Image = pairs.Apply(img3);
        }
Example #17
0
        /// <summary>
        ///   Creates an homography matrix matching points
        ///   from a set of points to another.
        /// </summary>
        public static MatrixH Homography(PointH[] points1, PointH[] points2)
        {
            // Initial argument checkings
            if (points1.Length != points2.Length)
            {
                throw new ArgumentException("The number of points should be equal.");
            }

            if (points1.Length < 4)
            {
                throw new ArgumentException("At least four points are required to fit an homography");
            }


            int N = points1.Length;

            MatrixH T1, T2; // Normalize input points

            points1 = Tools.Normalize(points1, out T1);
            points2 = Tools.Normalize(points2, out T2);

            // Create the matrix A
            double[,] A = new double[3 * N, 9];
            for (int i = 0; i < N; i++)
            {
                PointH X = points1[i];
                double x = points2[i].X;
                double y = points2[i].Y;
                double w = points2[i].W;
                int    r = 3 * i;

                A[r, 0] = 0;
                A[r, 1] = 0;
                A[r, 2] = 0;
                A[r, 3] = -w * X.X;
                A[r, 4] = -w * X.Y;
                A[r, 5] = -w * X.W;
                A[r, 6] = y * X.X;
                A[r, 7] = y * X.Y;
                A[r, 8] = y * X.W;

                r++;
                A[r, 0] = w * X.X;
                A[r, 1] = w * X.Y;
                A[r, 2] = w * X.W;
                A[r, 3] = 0;
                A[r, 4] = 0;
                A[r, 5] = 0;
                A[r, 6] = -x * X.X;
                A[r, 7] = -x * X.Y;
                A[r, 8] = -x * X.W;

                r++;
                A[r, 0] = -y * X.X;
                A[r, 1] = -y * X.Y;
                A[r, 2] = -y * X.W;
                A[r, 3] = x * X.X;
                A[r, 4] = x * X.Y;
                A[r, 5] = x * X.W;
                A[r, 6] = 0;
                A[r, 7] = 0;
                A[r, 8] = 0;
            }


            // Create the singular value decomposition
            SingularValueDecomposition svd = new SingularValueDecomposition(A, false, true);

            double[,] V = svd.RightSingularVectors;


            // Extract the homography matrix
            MatrixH H = new MatrixH((float)V[0, 8], (float)V[1, 8], (float)V[2, 8],
                                    (float)V[3, 8], (float)V[4, 8], (float)V[5, 8],
                                    (float)V[6, 8], (float)V[7, 8], (float)V[8, 8]);

            // Denormalize
            H = T2.Inverse().Multiply(H.Multiply(T1));

            return(H);
        }