问题
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
回答1:
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]
appliesLinearFractionalTransform[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
]
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:
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 */
}
}
来源:https://stackoverflow.com/questions/3540001/perspective-transformation-with-gd