2010-08-22 11 views
6

Come posso applicare una trasformazione prospettica su un'immagine utilizzando solo la libreria PHP GD?prospettiva trasformazione con GD

Non voglio utilizzare una funzione che qualcun altro ha fatto voglio CAPIRE cosa sta succedendo

+0

A questo è già stata data risposta [qui] [1]. [1]: http://stackoverflow.com/a/2536847/577306 – Jarrod

risposta

9

Sinceramente non so come descrivere matematicamente una distorsione prospettica. Potresti provare a cercare la documentazione per questo (ad esempio Google Scholar). Vedi anche nella documentazione OpenGL, glFrustum.


EDIT: È interessante notare che, a partire dalla versione 8, Mathematica ha un ImagePerspectiveTransformation. Nella parte rilevante, si legge:

per un 3 * 3 matrice m, ImagePerspectiveTransformation[image,m] vale LinearFractionalTransform[m] all'immagine.

Questa è una trasformazione che, per alcuni a (matrice), b (vettore), c (vettore) e d (scalare), trasforma il vettore r a (a.r+b)/(c.r+d). In una situazione 2D, questo dà il homogeneous matrix:

a_11 a_12 b_1 
a_21 a_22 b_2 
c_1 c_2 d 

Per applicare la trasformazione, si moltiplica questa matrice dal vettore colonna esteso con z=1 e poi prendere i primi due elementi del risultato e dividere per il terzo:

{{a11, a12, b1}, {a21, a22, b2}, {c1, c2, d}}.{{x}, {y}, {1}} // #[[ 
    1 ;; 2, All]]/#[[3, 1]] & // First /@ # & 

che dà:

{(b1 + a11 x + a12 y)/(d + c1 x + c2 y), 
    (b2 + a21 x + a22 y)/(d + c1 x + c2 y)} 

Con l'esempio:

a = {{0.9, 0.1}, {0.3, 0.9}} 
b = {0, -0.1} 
c = {0, 0.1} 
d = 1 

Si ottiene questa trasformazione:

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 
] 

Transformation result


Una volta che avete una mappa che descrive la posizione di un punto dell'immagine finale in termini di un punto in originale immagine, è solo questione di trovare il suo valore per ciascuno dei punti nella nuova immagine.

C'è una difficoltà aggiuntiva. Poiché un'immagine è discreta, cioè ha pixel anziché valori continui, devi renderla continua.

Supponiamo di avere una trasformazione che raddoppia le dimensioni di un'immagine. La funzione per calcolare un punto {x,y} nell'immagine finale cercherà il punto {x/2, y/2} nell'originale. Questo punto non esiste, perché le immagini sono discrete. Quindi devi interpolare questo punto. Ci sono diverse possibili strategie per questo.

In questo esempio Mathematica, faccio una semplice rotazione 2D e utilizzare una funzione spline gradi-1 interpolare:

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

Questo dà:

alt text

PHP:

Per l'interpolazione, google per "B-spline". Il resto è come segue.

Prima scegliere un referenziale per l'immagine originale, dire se l'immagine è 200x200, pixel (1,1) mappe (0,0) e pixel (200,200) mappe a (1,1).

Quindi devi indovinare dove verrà posizionata l'immagine finale quando viene applicata la trasformazione. Questo dipende dalla trasformazione, ad es. applicalo agli angoli dell'immagine o semplicemente indovina.

Supponi di considerare la mappatura tra (-5,0) e (1, 1,5) come ho fatto io e che anche l'immagine finale dovrebbe essere 200x200. Quindi:

$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 */ 
    } 
} 
+0

Interessante, ma che non sembra essere il codice PHP (è?) –

+0

@ Mark No, non lo è. È Mathematica. In PHP, dovresti implementare la spline da solo e utilizzare due cicli (nidificati) per costruire la trama parametrica. Ma il principio è lo stesso. – Artefacto

+0

Come lo implementeresti in PHP con i loop? –