Преобразование перспективы с помощью GD

Как применить преобразование перспективы к изображению, используя только библиотеку PHP GD?

Я не хочу использовать функцию, созданную кем-то другим Я хочу ПОНИМАТЬ, что происходит


person Mark Lalor    schedule 22.08.2010    source источник
comment
На это уже был дан ответ [здесь][1]. [1]: stackoverflow.com/a/2536847/577306   -  person Jarrod    schedule 23.07.2012


Ответы (1)


Честно говоря, я не знаю, как математически описать искажение перспективы. Вы можете попробовать найти литературу по этому вопросу (например, 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 */
    }
}
person Artefacto    schedule 22.08.2010
comment
@ Марк Нет, это не так. Это Математика. В PHP вам придется реализовать сплайн самостоятельно и использовать два цикла (вложенных) для построения параметрического графика. Но принцип тот же. - person Artefacto; 22.08.2010
comment
Как бы вы реализовали это в PHP с помощью циклов? - person Mark Lalor; 22.08.2010
comment
@geon Вот о чем говорит эта фраза. Это относится к отображению (функция, которая переводит новое в старое). - person Artefacto; 08.09.2010