2010-02-28 13 views
30

Comment incliner une image? Par exemple, chaque coin a un CGPoint avec des coords - p1, p2, p3, p4. Ensuite, je dois définir - p4.x + = 50, p4.y + = 30. Donc ce coin (p4) devrait être étiré dans une perspective 2D et l'image devrait être déformée.Étirement de l'image de l'iPhone (inclinaison)

alt text http://www.polar-b.com/scew.png

J'ai essayé d'utiliser CATransform3D, mais il semble que cela ne peut se faire de telle sorte, car il est seulement un changement de la perspective de vue (rotation, rapprocher/plus d'un côté). Peut-être que CGAffineTransform peut être utile?

Si vous connaissez la réponse, veuillez écrire un code d'exemple.

Merci d'avance

+1

Voici une autre façon d'y parvenir: http://stackoverflow.com/questions/9470493/transforming-a-rectangle-image-into- a-quadrilateral-using-a-catransform3d Faites-moi savoir si cela vous aide. – MonsieurDart

+0

Pour 2016: grâce à tous les éléments de base ici: voici une solution rapide Swift http://stackoverflow.com/a/39981054/294884 – Fattie

Répondre

79

Impossible avec CGAffineTransform. Une transformation affine peut toujours être décomposée en translations, rotations, cisaillement et mise à l'échelle. Ils cartographient tous des parallélogrammes en parallélogrammes, ce que votre transformatrice ne fait pas.

Pour votre transformation, cela peut se faire en deux étapes. Un pour convertir le carré en trapèze.

p1-----p2  p1-----p2 
|  | --> |  \ 
p3-----p4  p3--------p4' 

Un autre à la direction verticale. Une règle de transformation est naïve

    y - c 
x' = (x - p1.x) * ———————— + p1.x 
        p1.y - c 
y' = y 

où c est la coordonnée y du point d'intersection des lignes de jonction P1 et P3 et P2 et P4.

Notez maintenant le facteur x * y dans la transformation. Cela indique qu'une telle transformation est et non linéaire. Par conséquent, CATransform3D ne peut pas effectuer cela comme une transformation 2D non plus.

Cependant, le vecteur

[x, y, z, w=1] 

seront converties au vecteur 3D réelle

(x/w, y/w, z/w) 

avant la projection si CA suit les règles habituelles graphiques de calcul 3D, de sorte que vous pouvez « tricher » en utilisant le transformer

[ P . . Q ] [ x ] [ x' ] 
[ . R . S ] [ y ] = [ y' ] 
[ . . 1 . ] [ z ] [ z' ] 
[ . T . U ] [ 1 ] [ w' ] 

avec P approprié, Q, R, S, T, U qui mappe les 4 points aux endroits prévus. (6 coordonnées uniques et 6 variables doivent avoir exactement 1 solution dans la plupart des cas.)

Lorsque vous avez trouvé ces 6 constantes, vous pouvez créer un CATransform3D. Notez la définition de la structure est

struct CATransform3D 
    { 
    CGFloat m11, m12, m13, m14; 
    CGFloat m21, m22, m23, m24; 
    CGFloat m31, m32, m33, m34; 
    CGFloat m41, m42, m43, m44; 
}; 
typedef struct CATransform3D CATransform3D; 

vous pouvez donc modifier directement les éléments de la matrice, au lieu de compter sur les fonctions de CATransform3DMake. (Vous devrez peut-être effectuer une transposition en raison de la convention d'utilisation de vecteurs de ligne ou de colonne.)


Pour obtenir la transformée de convertir un rectangle ((X, Y), (W, H)) à un quadrilatère ((X1A, Y1A), (X2A, Y2A); (X3A, Y3a) (X4a, Y4a)), utilisez cette fonction (vous devrez peut-être une transposition):

function compute_transform_matrix(X, Y, W, H, x1a, y1a, x2a, y2a, x3a, y3a, x4a, y4a) { 
    var y21 = y2a - y1a, 
     y32 = y3a - y2a, 
     y43 = y4a - y3a, 
     y14 = y1a - y4a, 
     y31 = y3a - y1a, 
     y42 = y4a - y2a; 

    var a = -H*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42); 
    var b = W*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43); 
    var c = H*X*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42) - H*W*x1a*(x4a*y32 - x3a*y42 + x2a*y43) - W*Y*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43); 

    var d = H*(-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a); 
    var e = W*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42); 
    var f = -(W*(x4a*(Y*y2a*y31 + H*y1a*y32) - x3a*(H + Y)*y1a*y42 + H*x2a*y1a*y43 + x2a*Y*(y1a - y3a)*y4a + x1a*Y*y3a*(-y2a + y4a)) - H*X*(x4a*y21*y3a - x2a*y1a*y43 + x3a*(y1a - y2a)*y4a + x1a*y2a*(-y3a + y4a))); 

    var g = H*(x3a*y21 - x4a*y21 + (-x1a + x2a)*y43); 
    var h = W*(-x2a*y31 + x4a*y31 + (x1a - x3a)*y42); 
    var i = W*Y*(x2a*y31 - x4a*y31 - x1a*y42 + x3a*y42) + H*(X*(-(x3a*y21) + x4a*y21 + x1a*y43 - x2a*y43) + W*(-(x3a*y2a) + x4a*y2a + x2a*y3a - x4a*y3a - x2a*y4a + x3a*y4a)); 

    return [[a,b,0,c],[d,e,0,f],[0,0,1,0],[g,h,0,i]]; 
} 
+0

Hi Kenny. Je pense que je comprends votre point de vue. mais j'ai un problème avec la réalisation: CATransform3D sublayerTransform = CATransform3DIdentity; // sublayerTransform.m11 = P; // !!!!! ici j'ai besoin de faire quelque chose avec m11, m14, m22 ... pas vrai ??? et comment dois-je trouver P, Q, R .. imageLayer.contents = (id) [[UIImage imageNamed: @ "scene.jpg"] CGImage]; [couche addSublayer: imageLayer]; layer.sublayerTransform = sous-coucheTransform; – Dmitry

+0

@Dmitry: Oui. Vous devez résoudre 6 équations avec 6 inconnues (P, Q, R, etc.) – kennytm

+0

Je suis terriblement désolé pour l'importunité, mais comment ??? – Dmitry

3
struct CATransform3D 
{ 
    CGFloat m11, m12, m13, m14; 
    CGFloat m21, m22, m23, m24; 
    CGFloat m31, m32, m33, m34; 
    CGFloat m41, m42, m43, m44; 
}; 

vous devez régler m24 et m14 pour obtenir une telle forme.

+5

mention 0.000567 pour m24 pour obtenir la forme trapézoid – jothikenpachi

7

3D sur transformez UIImage/CGImageRef

Vous devriez être en mesure de calculer la cartographie de chaque pixel vous .. Pas parfait, mais il fait l'affaire ...

Il est disponible sur cette dépôt http://github.com/hfossli/AGGeometryKit/

Les fichiers intéressants est

https://github.com/hfossli/AGGeometryKit/blob/master/Source/AGTransformPixelMapper.m

https://github.com/hfossli/AGGeometryKit/blob/master/Source/CGImageRef%2BCATransform3D.m

https://github.com/hfossli/AGGeometryKit/blob/master/Source/UIImage%2BCATransform3D.m


transformation 3D sur UIView/UIImageView

https://stackoverflow.com/a/12820877/202451

Ensuite, vous aurez le contrôle total sur chaque point de la quadrilatérale. :)

2

J'ai essayé la merveilleuse réponse @KennyTM dans Swift, et j'ai eu une erreur "L'expression était trop complexe pour être résolue dans un délai raisonnable".

Voici donc une version simplifiée pour Swift:

let y21 = y2a - y1a 
let y32 = y3a - y2a 
let y43 = y4a - y3a 
let y14 = y1a - y4a 
let y31 = y3a - y1a 
let y42 = y4a - y2a 

let a = -H*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42) 
let b = W*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43) 
let c0 = -H*W*x1a*(x4a*y32 - x3a*y42 + x2a*y43) 
let cx = H*X*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42) 
let cy = -W*Y*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43) 
let c = c0 + cx + cy 

let d = H*(-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a) 
let e = W*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42) 
let f0 = -W*H*(x4a*y1a*y32 - x3a*y1a*y42 + x2a*y1a*y43) 
let fx = H*X*(x4a*y21*y3a - x2a*y1a*y43 - x3a*y21*y4a + x1a*y2a*y43) 
let fy = -W*Y*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42) 
let f = f0 + fx + fy; 

let g = H*(x3a*y21 - x4a*y21 + (-x1a + x2a)*y43) 
let h = W*(-x2a*y31 + x4a*y31 + (x1a - x3a)*y42) 
let i0 = H*W*(x3a*y42 - x4a*y32 - x2a*y43) 
let ix = H*X*(x4a*y21 - x3a*y21 + x1a*y43 - x2a*y43) 
let iy = W*Y*(x2a*y31 - x4a*y31 - x1a*y42 + x3a*y42) 
var i = i0 + ix + iy 


let epsilon = CGFloat(0.0001); 
if fabs(i) < epsilon { 
    i = epsilon * (i > 0 ? 1 : -1); 
} 

return CATransform3D(m11: a/i, m12: d/i, m13: 0, m14: g/i, m21: b/i, m22: e/i, m23: 0, m24: h/i, m31: 0, m32: 0, m33: 1, m34: 0, m41: c/i, m42: f/i, m43: 0, m44: 1.0) 
+0

vous avez raison de merveilleux! J'ai utilisé vos merveilleuses ruptures de calcul Swift là pour la version Swift ici: http://stackoverflow.com/a/18606029/294884, faites un saut et appelez avec une ligne, bravo! Alors merci beaucoup! – Fattie