6

PHP GDライブラリのみを使用して画像に遠近法変換を適用するにはどうすればよい ですか?

他の人が作った機能を使いたくない何が起こっているのか理解したい

4

1 に答える 1

9

正直なところ、遠近法の歪みを数学的に説明する方法がわかりません。そのための文献を検索してみてください (例: Google Scholar )。OpenGL ドキュメントの も参照してくださいglFrustum


編集: 興味深いことに、バージョン 8 以降、Mathematica にはImagePerspectiveTransformation. 関連する部分では、次のように述べています。

3*3 行列の場合、画像mImagePerspectiveTransformation[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から、結果の最初の 2 つの要素を取り、それらを 3 番目の要素で割ります。

{{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
 ]

変換結果


元の画像のポイントに関して最終的な画像のポイントの位置を記述するマップを取得したら、新しい画像の各ポイントの値を見つけるだけです。

もう1つ難点があります。画像は離散的であるため、つまり、連続値ではなくピクセルを持っているため、画像を連続にする必要があります。

画像のサイズを 2 倍にする変換があるとします。最終的な画像のポイントを計算する関数は、オリジナル{x,y}のポイントを探します。{x/2, y/2}画像は離散的であるため、この点は存在しません。したがって、この点を補間する必要があります。これにはいくつかの可能な戦略があります。

この Mathematica の例では、単純な 2D 回転を行い、次数 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 */
    }
}
于 2010-08-22T04:19:27.603 に答える