Code Optimization

Interesting things in software development and code optimization

C#.Net - Perspective Image Transform

Hello,


Today, I'm going to share one interesting thing that everyone may need - perspective image transformation.

Its 2D transformation that makes image look like in perspective view like 3D but without any 3D:


So, the main thing there is math. We take an image, specify four points and math will do the trick.

First step is to create and calculate matrices from the new four points:

//original points

Point p1 = new Point(0, 0);

Point p2 = new Point(width, 0);

Point p3 = new Point(width, height);

Point p4 = new Point(0, height);

//create matrix

A = Matrix3x3.Homogenous(p1, p2, p3, p4);

//new points

Point n1 = points[0];

Point n2 = points[1];

Point n3 = points[2];

Point n4 = points[3];

B = Matrix3x3.Homogenous(n1, n2, n3, n4);

A.Inverse();

C = B.MultMat(A);

C.Inverse();

Second step is to go through each pixel on your image and calculate new pixel:

Point ptDest = new Point(0, 0);

PointF ptOriginF = new Point(0, 0);

int iOrigX = 0;

Color pix = new Color();

for (int x = 0; x < width; ++x)

{

for (int y = 0; y < height; ++y)

{

ptDest.X = x;

ptDest.Y = y;

ptOriginF = C.Update(ptDest);

if (ptOriginF.X >= -5 && ptOriginF.X < width && ptOriginF.Y >= -5 && ptOriginF.Y < height)

{


iOrigX = (int)ptOriginF.X; // round to lowest integer

int iOrigY = (int)ptOriginF.Y; // round to lowest integer

double dx = ptOriginF.X - iOrigX;

double dy = ptOriginF.Y - iOrigY;

Point ptOrigin = new Point(iOrigX, iOrigY);

if (dx != 0.0f || dy != 0.0)

{

Color pix1 = Color.FromArgb(0, 255, 255, 255);

Color pix2 = Color.FromArgb(0, 255, 255, 255);

Color pix3 = Color.FromArgb(0, 255, 255, 255);

Color pix4 = Color.FromArgb(0, 255, 255, 255);

//

// Correct square's direction

//

int idx = (dx >= 0.0) ? 1 : -1;

int idy = (dy >= 0.0) ? 1 : -1;


dx = Math.Abs(dx);

dy = Math.Abs(dy);

//

// Get pixels of square

//

if (ptOrigin.X >= 0 && ptOrigin.X < width && ptOrigin.Y >= 0 && ptOrigin.Y < height)

pix1 = src.GetPixel(ptOrigin.X, ptOrigin.Y);

if (ptOrigin.X + idx >= 0 && ptOrigin.X + idx < width && ptOrigin.Y >= 0 && ptOrigin.Y < height)

pix2 = src.GetPixel(ptOrigin.X + idx, ptOrigin.Y);

if (ptOrigin.X >= 0 && ptOrigin.X < width && ptOrigin.Y + idy >= 0 && ptOrigin.Y + idy < height)

pix3 = src.GetPixel(ptOrigin.X, ptOrigin.Y + idy);

if (ptOrigin.X + idx >= 0 && ptOrigin.X + idx < width && ptOrigin.Y + idy >= 0 && ptOrigin.Y + idy < height)

pix4 = src.GetPixel(ptOrigin.X + idx, ptOrigin.Y + idy);

//

// Use bilinear interpolation

//

double r = pix1.R + (pix2.R - pix1.R) * dx + (pix3.R - pix1.R) * dy + (pix1.R - pix2.R - pix3.R + pix4.R) * dx * dy;

double g = pix1.G + (pix2.G - pix1.G) * dx + (pix3.G - pix1.G) * dy + (pix1.G - pix2.G - pix3.G + pix4.G) * dx * dy;

double b = pix1.B + (pix2.B - pix1.B) * dx + (pix3.B - pix1.B) * dy + (pix1.B - pix2.B - pix3.B + pix4.B) * dx * dy;

double a = pix1.A + (pix2.A - pix1.A) * dx + (pix3.A - pix1.A) * dy + (pix1.A - pix2.A - pix3.A + pix4.A) * dx * dy;

pix = Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b);

}

else

{

pix = src.GetPixel(ptOrigin.X, ptOrigin.Y);

}

dst.SetPixel(ptDest.X, ptDest.Y, pix);

}

}

}

So this is bilinear interpolation is the main thing to transform your image.


Thank you.

PS

will extend this and provide source code if you will request.

PPS

    public class Matrix3x3
    {
        double [,] m_val = new double [3,3];

        public Matrix3x3()
        {
        }
        public Matrix3x3(Point p1, Point p2, Point p3)
        {
            m_val = new double[3, 3];
            m_val[0, 0] = p1.X;
            m_val[0, 1] = p2.X;
            m_val[0, 2] = p3.X;

            m_val[1, 0] = p1.Y;
            m_val[1, 1] = p2.Y;
            m_val[1, 2] = p3.Y;

            m_val[2, 0] = 1.0;
            m_val[2, 1] = 1.0;
            m_val[2, 2] = 1.0;
        }

        public double Determinant()
        {
            double result = 0;
            result = m_val[0, 0] * (m_val[1, 1] * m_val[2, 2] - m_val[1, 2] * m_val[2, 1]);
            result-= m_val[0, 1] * (m_val[1, 0] * m_val[2, 2] - m_val[1, 2] * m_val[2, 0]);
            result+= m_val[0, 2] * (m_val[1, 0] * m_val[2, 1] - m_val[1, 1] * m_val[2, 0]);

            return result;
        }
        public double a(int i, int j)
        {
            return m_val[i-1, j-1];
        }

        public void Inverse()
        {
            double a11 = a(2, 2) * a(3, 3) - a(2, 3) * a(3, 2);
            double a12 = a(2, 1) * a(3, 3) - a(2, 3) * a(3, 1);
            double a13 = a(2, 1) * a(3, 2) - a(2, 2) * a(3, 1);

            double a21 = a(1, 2) * a(3, 3) - a(1, 3) * a(3, 2);
            double a22 = a(1, 1) * a(3, 3) - a(1, 3) * a(3, 1);
            double a23 = a(1, 1) * a(3, 2) - a(1, 2) * a(3, 1);

            double a31 = a(1, 2) * a(2, 3) - a(1, 3) * a(2, 2);
            double a32 = a(1, 1) * a(2, 3) - a(1, 3) * a(2, 1);
            double a33 = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1);

            double od = 1.0/Determinant();

            m_val[0, 0] = od * a11;
            m_val[0, 1] = -od * a21;
            m_val[0, 2] = od * a31;

            m_val[1, 0] = -od * a12;
            m_val[1, 1] = od * a22;
            m_val[1, 2] = -od * a32;

            m_val[2, 0] = od * a13;
            m_val[2, 1] = -od * a23;
            m_val[2, 2] = od * a33;
        }

        public void MultByVec(double [] vector)
        {
            for (int row = 0; row < 3; ++row)
            {
                m_val[row, 0] *= vector[0];
                m_val[row, 1] *= vector[1];
                m_val[row, 2] *= vector[2];
            }
        }
        public PointD Update(PointD vector)
        {
            double x = a(1, 1) * vector.X + a(1, 2) * vector.Y + a(1, 3) * 1.0;
            double y = a(2, 1) * vector.X + a(2, 2) * vector.Y + a(2, 3) * 1.0;
            double z = a(3, 1) * vector.X + a(3, 2) * vector.Y + a(3, 3) * 1.0;
            //
            PointD result = new PointD();
            result.X = (x / z);
            result.Y = (y / z);
            return result;
        }

        public Matrix3x3 MultMat(Matrix3x3 B)
        {
            Matrix3x3 result = new Matrix3x3();

            result.m_val[0, 0] = a(1, 1) * B.a(1, 1) + a(1, 2) * B.a(2, 1) + a(1, 3) * B.a(3, 1);
            result.m_val[0, 1] = a(1, 1) * B.a(1, 2) + a(1, 2) * B.a(2, 2) + a(1, 3) * B.a(3, 2);
            result.m_val[0, 2] = a(1, 1) * B.a(1, 3) + a(1, 2) * B.a(2, 3) + a(1, 3) * B.a(3, 3);

            result.m_val[1, 0] = a(2, 1) * B.a(1, 1) + a(2, 2) * B.a(2, 1) + a(2, 3) * B.a(3, 1);
            result.m_val[1, 1] = a(2, 1) * B.a(1, 2) + a(2, 2) * B.a(2, 2) + a(2, 3) * B.a(3, 2);
            result.m_val[1, 2] = a(2, 1) * B.a(1, 3) + a(2, 2) * B.a(2, 3) + a(2, 3) * B.a(3, 3);

            result.m_val[2, 0] = a(3, 1) * B.a(1, 1) + a(3, 2) * B.a(2, 1) + a(3, 3) * B.a(3, 1);
            result.m_val[2, 1] = a(3, 1) * B.a(1, 2) + a(3, 2) * B.a(2, 2) + a(3, 3) * B.a(3, 2);
            result.m_val[2, 2] = a(3, 1) * B.a(1, 3) + a(3, 2) * B.a(2, 3) + a(3, 3) * B.a(3, 3);

            return result;
        }

        public static Matrix3x3 Homogenous(Point p1, Point p2, Point p3, Point p4)
        {
            Matrix3x3 Major = new Matrix3x3(p1, p2, p3);
            Matrix3x3 Minor1 = new Matrix3x3(p4, p2, p3);
            Matrix3x3 Minor2 = new Matrix3x3(p1, p4, p3);
            Matrix3x3 Minor3 = new Matrix3x3(p1, p2, p4);
            double MajorD = Major.Determinant();
            double Minor1D = Minor1.Determinant();
            double Minor2D = Minor2.Determinant();
            double Minor3D = Minor3.Determinant();

            double[] coeff = new double[3];
            coeff[0] = Minor1D / MajorD;
            coeff[1] = Minor2D / MajorD;
            coeff[2] = Minor3D / MajorD;
            //
            Major.MultByVec(coeff);

            return Major;
        }
    }





1vqHSTrq1GEoEF7QsL8dhmJfRMDVxhv2y