Как применить преобразование перспективы к изображению, используя только библиотеку PHP GD?
Я не хочу использовать функцию, созданную кем-то другим Я хочу ПОНИМАТЬ, что происходит
Как применить преобразование перспективы к изображению, используя только библиотеку PHP GD?
Я не хочу использовать функцию, созданную кем-то другим Я хочу ПОНИМАТЬ, что происходит
Честно говоря, я не знаю, как математически описать искажение перспективы. Вы можете попробовать найти литературу по этому вопросу (например, Google Scholar). См. также в документации OpenGL glFrustum
.
EDIT: интересно, что начиная с версии 8 Mathematica имеет ImagePerspectiveTransformation
а>. В соответствующей части говорится:
Для матрицы 3*3
m
,ImagePerspectiveTransformation[image,m]
применяетLinearFractionalTransform[m]
к изображению.
Это преобразование, которое для некоторых a
(матрица), b
(вектор), c
(вектор) и d
(скаляр) преобразует вектор r
в (a.r+b)/(c.r+d)
. В двумерной ситуации это дает однородную матрицу:
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 я выполняю простое двумерное вращение и использую сплайн-функцию степени 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}}]
Это дает:
PHP:
Для интерполяции погуглите "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 */
}
}