使用 GD 进行透视变换

发布于 2024-09-15 10:07:09 字数 106 浏览 12 评论 0原文

如何在图像上应用透视变换 仅使用 PHP GD 库?

我不想使用别人制作的功能我想了解发生了什么

How can I apply a perspective transformation on an image
using only the PHP GD library?

I don't want to use a function someone else made I want to UNDERSTAND what's going on

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情深已缘浅 2024-09-22 10:07:09

老实说,我不知道如何从数学上描述透视变形。您可以尝试搜索相关文献(例如 Google Scholar)。另请参阅 OpenGL 文档,glFrustum< /a>.


编辑:有趣的是,从版本 8 开始,Mathematica 有一个 <代码>ImagePerspectiveTransformation。在相关部分,它说:

对于 3*3 矩阵 mImagePerspectiveTransformation[image,m]LinearFractionalTransform[m] 应用于图像。

对于某些a(矩阵)、b(向量)、c(向量)和d,这是一种变换code>(标量),将向量 r 转换为 (a.r+b)/(c.r+d)。在 2D 情况下,这给出了 齐次矩阵

a_11 a_12 b_1
a_21 a_22 b_2
c_1  c_2  d

要应用变换,请乘以该矩阵通过用 z=1 扩展的列向量,然后将结果的前两个元素除以第三个: 得到

{{a11, a12, b1}, {a21, a22, b2}, {c1, c2, d}}.{{x}, {y}, {1}} // #[[
     1 ;; 2, All]]/#[[3, 1]] & // First /@ # &

{(b1 + a11 x + a12 y)/(d + c1 x + c2 y),
  (b2 + a21 x + a22 y)/(d + c1 x + c2 y)}

通过示例:

a = {{0.9, 0.1}, {0.3, 0.9}}
b = {0, -0.1}
c = {0, 0.1}
d = 1

您将得到以下转换:

im = Import["/home/cataphract/Downloads/so_q.png"];
orfun = BSplineFunction[ImageData[im], SplineDegree -> 1];

(*transf=TransformationFunction[{{0.9, 0.1, 0.}, {0.3, 
   0.9, -0.1}, {0., 0.1, 1.}}] -- let's expand this:*)

transf = {(0.9 x + 0.1 y)/(1.+ 0.1 y), (-0.1 + 0.3 x + 0.9 y)/(
     1. + 0.1 y)} /. {x -> #[[1]], y -> #[[2]]} &;

ParametricPlot[transf[{x, y}], {x, 0, 1}, {y, 0, 1},
 ColorFunction -> (orfun[1 - #4, #3] &),
 Mesh -> None,
 FrameTicks -> None,
 Axes -> False,
 ImageSize -> 200,
 PlotRange -> All,
 Frame -> False
 ]

Transformation result


一旦你有了一张用原始图像中的点来描述最终图像的点的位置的地图,它就只是一个找到新图像中每个点的值的问题。

还有一个额外的困难。由于图像是离散的,即具有像素而不是连续值,因此必须使其连续。

假设您有一个将图像大小加倍的转换。用于计算最终图像中的点 {x,y} 的函数将在原始图像中查找点 {x/2, y/2}。这一点不存在,因为图像是离散的。所以你必须对这一点进行插值。为此有几种可能的策略。

在此 Mathematica 示例中,我执行简单的 2D 旋转并使用 1 度样条函数进行插值:

im = Import["d:\\users\\cataphract\\desktop\\img.png"]
orfun = BSplineFunction[ImageData[im], SplineDegree -> 1];
transf = Function[{coord}, RotationMatrix[20. Degree].coord];
ParametricPlot[transf[{x, y}], {x, 0, 1}, {y, 0, 1}, 
 ColorFunction -> (orfun[1 - #4, #3] &), Mesh -> None, 
 FrameTicks -> None, Axes -> None, ImageSize -> 200, 
 PlotRange -> {{-0.5, 1}, {0, 1.5}}]

这给出:

alt text

PHP:

对于插值,请在 google 上搜索“B-spline”。其余部分如下。

首先选择原始图像的参考,假设图像为 200x200,则像素 (1,1) 映射 (0,0),像素 (200,200) 映射到 (1,1)。

然后你必须猜测应用变换时最终图像将落在哪里。这取决于变换,您可以将其应用于图像的角点或只是猜测。

假设您像我一样考虑 (-.5,0) 和 (1, 1.5) 之间的映射,并且您的最终图像也应该是 200x200。然后:

$sizex = 200;
$sizey = 200;
$x = array("min"=>-.5, "max" => 1);
$y = array("min"=>0, "max" => 1.5);
// keep $sizex/$sizey == $rangex/$rangey
$rangex = $x["max"] - $x["min"];
$rangey = $y["max"] - $y["min"];
for ($xp = 1; $xp <= $sizex; $xp++) {
    for ($yp = 1; $yp <= $sizey; $yp++) {
        $value = transf(
             (($xp-1)/($sizex-1)) * $rangex + $x["min"],
             (($yp-1)/($sizey-1)) * $rangey + $y["min"]);
        /* $value should be in the form array(r, g, b), for instance */
    }
}

I honestly don't know how to describe mathematically a perspective distortion. You could try searching the literature for that (e.g. Google Scholar). See also in the OpenGL documentation, glFrustum.


EDIT: Interestingly, starting with version 8, Mathematica has a ImagePerspectiveTransformation. In the relevant part, it says:

For a 3*3 matrix m, ImagePerspectiveTransformation[image,m] applies LinearFractionalTransform[m] to image.

This is a transformation that, for some a (matrix), b (vector), c (vector) and d (scalar), transforms the vector r to (a.r+b)/(c.r+d). In a 2D situation, this gives the homogeneous matrix:

a_11 a_12 b_1
a_21 a_22 b_2
c_1  c_2  d

To apply the transformation, you multiply this matrix by the column vector extended with z=1 and then take the first two elements of the result and divide them by the third:

{{a11, a12, b1}, {a21, a22, b2}, {c1, c2, d}}.{{x}, {y}, {1}} // #[[
     1 ;; 2, All]]/#[[3, 1]] & // First /@ # &

which gives:

{(b1 + a11 x + a12 y)/(d + c1 x + c2 y),
  (b2 + a21 x + a22 y)/(d + c1 x + c2 y)}

With the example:

a = {{0.9, 0.1}, {0.3, 0.9}}
b = {0, -0.1}
c = {0, 0.1}
d = 1

You get this transformation:

im = Import["/home/cataphract/Downloads/so_q.png"];
orfun = BSplineFunction[ImageData[im], SplineDegree -> 1];

(*transf=TransformationFunction[{{0.9, 0.1, 0.}, {0.3, 
   0.9, -0.1}, {0., 0.1, 1.}}] -- let's expand this:*)

transf = {(0.9 x + 0.1 y)/(1.+ 0.1 y), (-0.1 + 0.3 x + 0.9 y)/(
     1. + 0.1 y)} /. {x -> #[[1]], y -> #[[2]]} &;

ParametricPlot[transf[{x, y}], {x, 0, 1}, {y, 0, 1},
 ColorFunction -> (orfun[1 - #4, #3] &),
 Mesh -> None,
 FrameTicks -> None,
 Axes -> False,
 ImageSize -> 200,
 PlotRange -> All,
 Frame -> False
 ]

Transformation result


Once you have a map that describes the position of a point of the final image in terms of a point in the original image, it's just a matter of finding its value for each of the points in the new image.

There's one additional difficulty. Since an image is discrete, i.e., has pixels instead of continuous values, you have to make it continuous.

Say you have a transformation that doubles the size of an image. The function to calculate a point {x,y} in the final image will look for point {x/2, y/2} in the original. This point doesn't exist, because images are discrete. So you have to interpolate this point. There are several possible strategies for this.

In this Mathematica example, I do a simple 2D rotation and use a degree-1 spline function to interpolate:

im = Import["d:\\users\\cataphract\\desktop\\img.png"]
orfun = BSplineFunction[ImageData[im], SplineDegree -> 1];
transf = Function[{coord}, RotationMatrix[20. Degree].coord];
ParametricPlot[transf[{x, y}], {x, 0, 1}, {y, 0, 1}, 
 ColorFunction -> (orfun[1 - #4, #3] &), Mesh -> None, 
 FrameTicks -> None, Axes -> None, ImageSize -> 200, 
 PlotRange -> {{-0.5, 1}, {0, 1.5}}]

This gives:

alt text

PHP:

For the interpolation, google for "B-spline". The rest is as follows.

First choose a referential for the original image, say if the image is 200x200, pixel (1,1) maps (0,0) and pixel (200,200) maps to (1,1).

Then you have to guess where your final image will land when the transformation is applied. This depends on the transformation, you can e.g. apply it to the corners of the image or just guess.

Say you consider the mapped between (-.5,0) and (1, 1.5) like I did and that your final image should be 200x200 also. Then:

$sizex = 200;
$sizey = 200;
$x = array("min"=>-.5, "max" => 1);
$y = array("min"=>0, "max" => 1.5);
// keep $sizex/$sizey == $rangex/$rangey
$rangex = $x["max"] - $x["min"];
$rangey = $y["max"] - $y["min"];
for ($xp = 1; $xp <= $sizex; $xp++) {
    for ($yp = 1; $yp <= $sizey; $yp++) {
        $value = transf(
             (($xp-1)/($sizex-1)) * $rangex + $x["min"],
             (($yp-1)/($sizey-1)) * $rangey + $y["min"]);
        /* $value should be in the form array(r, g, b), for instance */
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文