I have a quadrilateral, the coordinates of which are known. I need to do a perspective transform and warp an image to those coordinates using Skia. I have referred to the links in Skia's page like https://developer.xamarin.com/guides/xamarin-forms/advanced/skiasharp/transforms/non-affine/ but not able to use the coordinates. How do I use the coordinates instead of matrix?
1 Answers
Here is a function that takes the coordinates of the four corners on the destination image and the size of the source image. It then generates a matrix that can be passed to skia:
public static SKMatrix CreateMatrixFromPoints(SKPoint topLeft, SKPoint topRight, SKPoint botRight, SKPoint botLeft, float width, float height)
{
(float x1, float y1) = (topLeft.X, topLeft.Y);
(float x2, float y2) = (topRight.X, topRight.Y);
(float x3, float y3) = (botRight.X, botRight.Y);
(float x4, float y4) = (botLeft.X, botLeft.Y);
(float w, float h) = (width, height);
float scaleX = (y1 * x2 * x4 - x1 * y2 * x4 + x1 * y3 * x4 - x2 * y3 * x4 - y1 * x2 * x3 + x1 * y2 * x3 - x1 * y4 * x3 + x2 * y4 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
float skewX = (-x1 * x2 * y3 - y1 * x2 * x4 + x2 * y3 * x4 + x1 * x2 * y4 + x1 * y2 * x3 + y1 * x4 * x3 - y2 * x4 * x3 - x1 * y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
float transX = x1;
float skewY = (-y1 * x2 * y3 + x1 * y2 * y3 + y1 * y3 * x4 - y2 * y3 * x4 + y1 * x2 * y4 - x1 * y2 * y4 - y1 * y4 * x3 + y2 * y4 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
float scaleY = (-y1 * x2 * y3 - y1 * y2 * x4 + y1 * y3 * x4 + x1 * y2 * y4 - x1 * y3 * y4 + x2 * y3 * y4 + y1 * y2 * x3 - y2 * y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
float transY = y1;
float persp0 = (x1 * y3 - x2 * y3 + y1 * x4 - y2 * x4 - x1 * y4 + x2 * y4 - y1 * x3 + y2 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
float persp1 = (-y1 * x2 + x1 * y2 - x1 * y3 - y2 * x4 + y3 * x4 + x2 * y4 + y1 * x3 - y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
float persp2 = 1;
return new SKMatrix(scaleX, skewX, transX, skewY, scaleY, transY, persp0, persp1, persp2);
}
Here is how I got this function:
From the page you've linked, skia calculates the transformed coordinates like this:
x' = ScaleX·x + SkewX·y + TransX
y' = SkewY·x + ScaleY·y + TransY
z` = Persp0·x + Persp1·y + Persp2
xFinal = x' / z'
yFinal = y' / z'
where (x, y)
is the original position of a pixel, (xFinal, yFinal)
is its new position, and variations of Scale/Skew/Trans/Persp
are the values from the supplied matrix.
First, let's slightly rewrite the formulas:
xFinal = (ScaleX·x + SkewX·y + TransX) / (Persp0·x + Persp1·y + Persp2)
yFinal = (SkewY·x + ScaleY·y + TransY) / (Persp0·x + Persp1·y + Persp2)
Given the position of the corners of the quadrilateral (x1,y1)
, (x2,y2)
, (x3,y3)
, (x4,y4)
and the size of the image (w,h)
, we need to find the values of ScaleX
, SkewX
, TransX
, SkewY
, ScaleY
, TransY
, Persp0
, Persp1
, and Persp2
such that after applying the formulas above:
(0,0)
becomes(x1,y1)
(image top left -> quadrilateral top left)(w,0)
becomes(x2,y2)
(image top right -> quadrilateral top right)(w,h)
becomes(x3,y3)
(image bottom right -> quadrilateral bottom right)(0,h)
becomes(x4,y4)
(image bottom left -> quadrilateral bottom left)
Now we can substitute (x,y)
for (0,0)
; (w,0)
; (w,h)
; (0,h)
and (xFinal,yFinal)
for (x1,y1)
; (x2,y2)
; (x3,y3)
; (x4,y4)
.
x1 = (ScaleX·0 + SkewX·0 + TransX) / (Persp0·0 + Persp1·0 + Persp2)
y1 = (SkewY·0 + ScaleY·0 + TransY) / (Persp0·0 + Persp1·0 + Persp2)
x2 = (ScaleX·w + SkewX·0 + TransX) / (Persp0·w + Persp1·0 + Persp2)
y2 = (SkewY·w + ScaleY·0 + TransY) / (Persp0·w + Persp1·0 + Persp2)
x3 = (ScaleX·w + SkewX·h + TransX) / (Persp0·w + Persp1·h + Persp2)
y3 = (SkewY·w + ScaleY·h + TransY) / (Persp0·w + Persp1·h + Persp2)
x4 = (ScaleX·0 + SkewX·h + TransX) / (Persp0·0 + Persp1·h + Persp2)
y4 = (SkewY·0 + ScaleY·h + TransY) / (Persp0·0 + Persp1·h + Persp2)
By simplifying and rearranging that, we end up with a system of linear equations.
x1·Persp2 - TransX = 0
y1·Persp2 - TransY = 0
Persp0·w·x2 + Persp2·x2 - ScaleX·w - TransX = 0
Persp0·w·y2 + Persp2·y2 - SkewY·w - TransY = 0
Persp0·w·x3 + Persp1·h·x3 + Persp2·x3 - ScaleX·w - SkewX·h - TransX = 0
Persp0·w·y3 + Persp1·h·y3 + Persp2·y3 - SkewY·w - ScaleY·h - TransY = 0
Persp1·h·x4 + Persp2·x4 - SkewX·h - TransX = 0
Persp1·h·y4 + Persp2·y4 - ScaleY·h - TransY = 0
All that's left is solving it for ScaleX
, SkewX
, TransX
, SkewY
, ScaleY
, TransY
, Persp0
, Persp1
, and Persp2
. In other words, we want
to find a way to express these variables in terms of all the other ones which we know. I used this calculator to do the job. It's output are the formulas from the very beginning.

- 31
- 2