Честно говоря, я не знаю, как математически описать перспективное искажение. Вы можете попробовать поискать литературу для этого (например, 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)
. В 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 я делаю простое двухмерное вращение и использую сплайн-функцию степени-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:
Для интерполяции, Google для "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 */
}
}