Skia 中的透视变换

问题描述 投票:0回答:2

我有一个四边形,其坐标已知。我需要使用 Skia 进行透视变换并将图像扭曲到这些坐标。我参考了 Skia 页面中的链接,如 https://developer.xamarin.com/guides/xamarin-forms/advanced/skiasharp/transforms/non-affine/ 但无法使用坐标。如何使用坐标而不是矩阵?

c# transform perspective skia
2个回答
3
投票

这是一个函数,它获取目标图像上四个角的坐标和源图像的大小。然后它生成一个可以传递给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);
}

这是我如何获得此功能的:

从您链接的页面,skia 计算转换后的坐标,如下所示:

x' = ScaleX·x + SkewX·y + TransX
y' = SkewY·x + ScaleY·y + TransY
z` = Persp0·x + Persp1·y + Persp2

xFinal = x' / z'
yFinal = y' / z'

其中

(x, y)
是像素的原始位置,
(xFinal, yFinal)
是其新位置,
Scale/Skew/Trans/Persp
的变体是所提供矩阵中的值。

首先,我们稍微重写一下公式:

xFinal = (ScaleX·x + SkewX·y + TransX) / (Persp0·x + Persp1·y + Persp2)
yFinal = (SkewY·x + ScaleY·y + TransY) / (Persp0·x + Persp1·y + Persp2)

给定四边形

(x1,y1)
(x2,y2)
(x3,y3)
(x4,y4)
的角点位置以及图像
(w,h)
的大小,我们需要找到
ScaleX
SkewX
的值,
TransX
SkewY
ScaleY
TransY
Persp0
Persp1
Persp2
,应用上述公式后:

  • (0,0)
    变为
    (x1,y1)
    (图像左上角 -> 四边形左上角)
  • (w,0)
    变为
    (x2,y2)
    (图像右上角 -> 四边形右上角)
  • (w,h)
    变为
    (x3,y3)
    (图像右下角 -> 四边形右下角)
  • (0,h)
    变为
    (x4,y4)
    (图像左下角 -> 四边形左下角)

现在我们可以用

(x,y)
代替
(0,0)
(w,0)
(w,h)
(0,h)
(xFinal,yFinal)
表示
(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)

通过简化和重新排列,我们最终得到一个线性方程组。

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

剩下的就是解决

ScaleX
SkewX
TransX
SkewY
ScaleY
TransY
Persp0
Persp1
Persp2
》。换句话说,我们想要 找到一种方法来用我们知道的所有其他变量来表达这些变量。我使用this计算器来完成这项工作。它的输出是从一开始的公式。


0
投票

Python版本:

def create_matrix_from_points(top_left, top_right, bottom_right, bottom_left, width, height):
    x1, y1 = top_left.x, top_left.y
    x2, y2 = top_right.x, top_right.y
    x3, y3 = bottom_right.x, bottom_right.y
    x4, y4 = bottom_left.x, bottom_left.y

    scale_x = (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 * width + y2 * x4 * width - y3 * x4 * width - x2 * y4 * width - y2 * width * x3 + y4 * width * x3)
    skew_x = (-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 * height + y2 * x4 * height - y3 * x4 * height - x2 * y4 * height - y2 * height * x3 + y4 * height * x3)
    translation_x = x1
    skew_y = (-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 * width + y2 * x4 * width - y3 * x4 * width - x2 * y4 * width - y2 * width * x3 + y4 * width * x3)
    scale_y = (-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 * height + y2 * x4 * height - y3 * x4 * height - x2 * y4 * height - y2 * height * x3 + y4 * height * x3)
    translation_y = y1
    perspective0 = (x1 * y3 - x2 * y3 + y1 * x4 - y2 * x4 - x1 * y4 + x2 * y4 - y1 * x3 + y2 * x3) / (x2 * y3 * width + y2 * x4 * width - y3 * x4 * width - x2 * y4 * width - y2 * width * x3 + y4 * width * x3)
    perspective1 = (-y1 * x2 + x1 * y2 - x1 * y3 - y2 * x4 + y3 * x4 + x2 * y4 + y1 * x3 - y4 * x3) / (x2 * y3 * height + y2 * x4 * height - y3 * x4 * height - x2 * y4 * height - y2 * height * x3 + y4 * height * x3)
    perspective2 = 1

    return skia.Matrix(scale_x, skew_x, translation_x, skew_y, scale_y, translation_y, perspective0, perspective1, perspective2)
© www.soinside.com 2019 - 2024. All rights reserved.