如何使用PHP GD库对图像进行透视变换?
我不想使用别人编写的函数,我想理解其中的原理。
如何使用PHP GD库对图像进行透视变换?
我不想使用别人编写的函数,我想理解其中的原理。
glFrustum
。
ImagePerspectiveTransformation
。在相关部分中,它说:
这是一种变换,对于某些对于一个3*3矩阵
m
,ImagePerspectiveTransformation[image,m]
将LinearFractionalTransform[m]
应用于图像。
a
(矩阵)、b
(向量)、c
(向量)和d
(标量),将向量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
]
一旦你有了一个描述最终图像中某个点在原始图像中的位置的映射,只需要为新图像中的每个点找到它的值。
还有一个额外的困难。由于图像是离散的,即具有像素而不是连续值,因此必须使其连续。
假设你有一个将图像大小加倍的变换。计算最终图像中点{x,y}
的函数将在原始图像中查找点{x/2,y/2}
。但是这个点不存在,因为图像是离散的。因此,必须对这个点进行插值。有几种可能的策略。
在这个Mathematica示例中,我做了一个简单的二维旋转,并使用一阶样条函数进行插值:
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}}]
这将得到:
PHP:
对于插值,可以搜索“B样条”。剩下的步骤如下。
首先为原始图像选择一个参考系,比如如果图像是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 */
}
}