Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Algorithmes et structures de données Discussion :

Coordonnées polaires vers coordonnées XY (cartésiennes)


Sujet :

Algorithmes et structures de données

  1. #1
    Membre du Club
    Coordonnées polaires vers coordonnées XY (cartésiennes)
    Bonjour,

    J'ai des données d'un capteur que je recois en coordonnées polaires. Les données recues sont des tableaux MxN, les données en X sont des coordonnées polaires de {-theta , theta} avec une résolution de (theta*2/M) .
    Les coordonnées en Y sont les distance de [dmin, dmax] avec une résolution de (dmax-dmin) / N.

    Quelqu'un pourrait me conseiller un algorithme permettant de creer une grille d'occupation à partir de mon jeu de donnée polaire?

    Je ne sais pas comment pondérer la valeur en coordonnée polaire vers la grille en cordoonée XY. Par exemple si pour la coordonnée polaire p = (r,theta) j'obtiens un point pg = (10.66,1.88) quel est la pondération que je dois appliquer au 4 pixels au alentour?

  2. #2
    Membre chevronné
    Coordonnées polaires vers coordonnées XY (cartésiennes)
    Bonjour,

    Citation Envoyé par black_hole Voir le message
    ... Je ne sais pas comment pondérer la valeur en coordonnée polaire vers la grille en cordoonée XY. Par exemple si pour la coordonnée polaire p = (r,theta) j'obtiens un point pg = (10.66,1.88) quel est la pondération que je dois appliquer au 4 pixels au alentour?
    En notant P00, P10, P01 et P11 les pixels de
    coordonnées respectives (10, 1), (11, 1), (10, 2) et (11, 2),
    le pixel "moyen" résultera de la combinaison linéaire:
    Pm = (1-0.66)*(1-0.88).P00 + 0.66*(1-0.88).P10 + (1-0.66)*0.88.P10 + 0.66*0.88.P11 .

    Si l'on veut rationaliser cela,il faut décomposer toute coordonnée réelle en deux parties entière et décimale:
    x = Ex + Dx , avec Ex = Trunc(x) , Dc = Frac(x);
    y = Ey + Dy, avec Ey = Trunc(y) , Dc = Frac(y);
    chaque composante couleur (j = 1, 2 ou 3) du pixel moyen pourra ainsi s'exprimer en fonction des 4 pixels concernés:
    P00 = P(Ex, Ey) , P10 = P(Ex+1, Ey) , P01 = P(Ex, Ey+1) et P11 = P(Ex+1, Ey+1)
    (et en tenant compte de la nécessité de revenir à un résultat entier) par la combinaison linéaire:
    P(x, y)[j] = Round((1-Dx)(1-Dy)*P00[i] + Dx(1-Dy)*P10[j] + (1-Dx)Dy*P01[j] + Dx*Dy*P11[j]) .

    La formule d'interpolation peut se réduire à des sommes de deux termes, mais elle comporte alors deux stades impliquant des vecteurs de R3; il suffit d'écrire dans ce cas:
    Wx0[j] = (1-Dx)*P00[i] + Dx*P10[j] ,
    Wx1[j] = (1-Dx)*P01[j] + Dx*P11[j] ,
    P(x, y)[j] = Round((1-Dy)*Wx0[j] + Dy*Wx1[j]) .

    Formules à vérifier.


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  3. #3
    Membre éclairé
    Vers l'implémentation
    Bonjour,

    La réponse de wiwaxia est complète et il n'y a donc rien à ajouter à l'algorithme éprouvé d'interpolation bilinéaire (même si j'ai toujours été gêné qu'un point qui, en général, sera dans un seul triangle des 4 points voisins altère aussi le quatrième point ).

    Cependant sur cette base, l'implémentation peut être plus ou moins efficace. Des mises en facteurs peuvent diminuer sensiblement le nombre d'opérations (notamment les multiplications en flottants) et il existe une instruction sincos qui divise par deux les temps de projection cartésienne.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  4. #4
    Membre chevronné
    Coordonnées polaires vers coordonnées XY (cartésiennes)
    Bonjour,

    Citation Envoyé par Guesset Voir le message
    ... j'ai toujours été gêné qu'un point qui, en général, sera dans un seul triangle des 4 points voisins altère aussi le quatrième point ) ...
    La valeur locale d'une fonction de deux variables W(x, y) sur le domaine de double interpolation
    [Ex, Ex + 1]×[Ey, Ey + 1]
    est donnée par le polynôme symétrique:
    W(Ex + Dx, Ey + Dy) = (1-Dx)(1-Dy)*P00[i] + Dx(1-Dy)*P10[j] + (1-Dx)Dy*P01[j] + Dx*Dy*P11[j] ,
    auxquelles sont équivalentes les deux étapes précédemment données.

    Le polynôme en question équivaut à la donnée implicite de deux fonctions affines:
    a) à (Dy) constant: W(Ex + Dx, Ey + Dy) = A(Dy) + B(Dy)*Dx ,
    b) à (Dx) constant: W(Ex + Dx, Ey + Dy) = C(Dx) + D(Dx)*Dy .

    La surface d'équation z = F(x, y) = (1-x)(1-y)*K00 + x(1-y)*K10 + (1-x)y*K01 + xy*K11
    contient un double faisceau de droites situées dans des plans parallèles à (xOz) et (yOz).


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  5. #5
    Membre éclairé
    Bonjour Wiwaxia,

    Je te remercie mais j'exprimais seulement une limitation des traitements bilinéaires qui, en appliquant une opération linéaire en x puis en y (ou dans l'ordre inverse, cela n'a pas d'importance si la transformation a, comme ici, seulement une pente et pas de constante), inscrit ses effets dans un carré ce qui ne saurait être optimal.

    Par exemple, pour un point P dans une maille carrée, le point le plus loin dans cette maille peut être plus loin qu'un point d'une autre maille. Pourtant seul le premier sera altéré. Cela traduit une métrique implicite non isotrope (un compas qui trace des carrés ).

    L'avantage essentiel est la simplicité et donc l'efficacité en temps de traitement. Comme tu l'écris, c'est une double interpolation et non une interpolation de surface.

    C'est un joli sujet en soi loin d'être épuisé par quelques algorithmes éprouvés.

    Salutations

    Philippe
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  6. #6
    Membre chevronné
    Coordonnées polaires vers coordonnées XY (cartésiennes)
    Tes remarques suscitent quelques observations.
    Citation Envoyé par Guesset Voir le message
    ... pour un point P dans une maille carrée, le point le plus loin dans cette maille peut être plus loin qu'un point d'une autre maille. Pourtant seul le premier sera altéré. Cela traduit une métrique implicite non isotrope (un compas qui trace des carrés ) ...
    Il importe de distinguer tout point quelconque du plan, pour lequel on cherche une estimation de la couleur locale, des noeuds de la grille où la fonction est parfaitement connue, puisqu'il s'agit des pixels de l'image initiale.
    L'arrangement régulier de ces derniers détermine une partition du plan de l'image, et les sommets (A, B, C, D) d'un carré sont bien les noeuds les plus proches de tout point (M) situé à l'intérieur.
    Les indices (r, v, b) de la couleur locale sont naturellement bornés par les valeurs extrêmes rencontrées aux sommets, et l'on a (sauf configuration très particulière et discutable):
    Min(IA, IB, IC, ID) <~ IM <~ Max(IA, IB, IC, ID) ;
    d'où l'idée d'une combinaison linéaire impliquant des distances, et conduisant à une valeur locale (IM) d'autant plus proche de celle de l'un des sommets que la distance correspondante est plus faible.

    a) Dans le cas d'un assemblage hexagonal de pixels, la maille est un triangle équilatéral (AB = BC = CA = a) et les indices des 3 sommets sont pondérés par les distances séparant le point considéré (M) de chacun des trois côtés:
    a(31/2/2).IM = MI*IA + MJ*IB + MK*IC ;
    la somme des longueurs des 3 hauteurs abaissées depuis le point (M) est en effet constante , et l'on établit facilement l'égalité:
    MI + MJ + MK = AB.(31/2/2)
    à partir de l'expression de l'aire du triangle.
    b) Cependant les pixels constituent le plus souvent un réseau à maille carrée (AB = BC = CD = DA = a), et les distances séparant le point (M) des 4 côtés vérifient les relations:
    MI + MK = MJ + ML = a ;
    on envisage alors la combinaison linéaire:
    a2.IM = MJ.(MK.IA + MI.IC) + ML.(MK.IB + MI.ID)
    d'où découle naturellement la relation donnée au message #02.

    Il s'agit bien, dans les deux cas, de distances euclidiennes.


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  7. #7
    Membre chevronné
    Coordonnées polaires vers coordonnées XY (cartésiennes)
    Suite.
    Citation Envoyé par Guesset Voir le message
    ... j'exprimais seulement une limitation des traitements bilinéaires qui, en appliquant une opération linéaire en x puis en y (ou dans l'ordre inverse, cela n'a pas d'importance si la transformation a, comme ici, seulement une pente et pas de constante), inscrit ses effets dans un carré ce qui ne saurait être optimal. ...
    La pratique de l'interpolation suppose l'intervention d'une loi de dépendance entre la couleur d'un point et celle de ses voisins; or on envisage ici des domaines très étroits de quelques pixels, dont l'étendue correspond à la dimension des plus petits détails picturaux présents dans l'image.
    La présence de chaque pixel est donc en soi une donnée locale â priori indépendante des couleurs voisines, et il n'est pas légitime d'étendre l'interpolation au-delà du rang suivant, dans l'espoir de calculs plus précis. Sinon, on entreprend un calcul de moyenne qui floute l'image en même temps qu'il en estompe les plus petits détails.
    Par ailleurs, en travaillant sur un domaine très étroit (1 pixel, alors que l'image entière en comporte 500 milles à plusieurs millions), on obtiendra d'un procédé à l'autre des résultats probablement très proches - sinon indiscernables, compte tenu de ce qu'il s'agit d'entiers confinés dans l'intervalle [0..255].

    Voici, à titre d'exemple, ce que donne une rotation de 60° de la partie centrale du portrait de mon mamba préféré.


    On recherche dans le premier cas, à l'aide de la fonction Round(.), le pixel dont les coordonnées entières sont les plus proches des valeurs décimales calculées:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     PROCEDURE Calc_Mat_Im2(La, Ha: Z_32; VAR Ma1, Ma2: Tab_Pix);
       CONST P000: Pixel = (0, 0, 0);
             DegRad = Pi / 180; Alpha = 30;
       VAR Xm, Xm1, Ym, Ym1: Z_32;
           CosA, Rho, Ray, SinA, U1, U2, V1, V2, Xcen, Ycen: Reel; Px: Pixel;
       BEGIN
         SinCos(Alpha * DegRad, SinA, CosA);
         Xcen:= (La - 1)/2; Ycen:= (Ha - 1)/2;
         IF (Xcen<Ycen) THEN Ray:= Xcen ELSE Ray:= Ycen;
         ZeroM(Matrice_2);
         FOR Xm:= 0 TO (La - 1) DO
           FOR Ym:= 0 TO (Ha - 1) DO
             BEGIN
               U2:= Sqr(Xm - Xcen); V2:= Sqr(Ym - Ycen); Rho:= Sqrt(U2 + V2);
               IF (Rho>Ray) THEN Px:= P000
                            ELSE BEGIN
                                   U1:= SommAxBy( CosA, SinA, Xm, Ym);
                                   V1:= SommAxBy(-SinA, CosA, Xm, Ym);
                                   Xm1:= Round(Xcen + U1);
                                   Ym1:= Round(Ycen + V1);
                                   Px:= Ma1[Xm1, Ym1]
                                 END;
               Ma2[Xm, Ym]:= Px
             END
       END;



    On recourt, dans le second cas, au procédé d'interpolation simple, décrit auparavant:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    FUNCTION SommAxBy(Ka, Kb, Wx, Wy: Reel): Reel;
       VAR p, q: Reel;
       BEGIN
         p:= Ka * Wx; q:= Kb * Wy; Result:= p + q
       END;
    
     FUNCTION F_Couleur(u, v: Reel; P00, P10, P01, P11: Pixel): Pixel;
       VAR k:Byte; Wx0, Wx1: Ve3R; Px: Pixel;
       BEGIN
         FOR k:= 1 TO 3 DO
           BEGiN
             Wx0[k]:= SommAxBy(1 - u, u, P00[k], P10[k]);
             Wx1[k]:= SommAxBy(1 - u, u, P01[k], P11[k]);
             Px[k]:= Round(SommAxBy(1 - v, v, Wx0[k], Wx1[k]))
           END;
         Result:= Px
       END; 
       
     PROCEDURE Calc_Mat_Im2(La, Ha: Z_32; VAR Ma2: Tab_Pix);
       CONST P000: Pixel = (0, 0, 0);
             DegRad = Pi / 180; Alpha = 30;
       VAR Ex, Ey, Xm, Xm1, Ym, Ym1: Z_32;
           CosA, Dx, Dy, Ray, Ray2, s, SinA, U1, U2, V1, V2, Xcen, Ycen: Reel;
           Px: Pixel;
       BEGIN
         SinCos(Alpha * DegRad, SinA, CosA);
         Xcen:= (La - 1) / 2; Ycen:= (Ha - 1) / 2;
         IF (Xcen<Ycen) THEN Ray:= Xcen - 1 ELSE Ray:= Ycen - 1;
         Ray2:= Sqr(Ray);     ZeroM(Matrice_2);
         FOR Xm:= 0 TO (La - 1) DO
           FOR Ym:= 0 TO (Ha - 1) DO
             BEGIN
               U2:= Sqr(Xm - Xcen); V2:= Sqr(Ym - Ycen); s:= U2 + V2;
               IF (s>=Ray2)
                 THEN Px:= P000
                 ELSE
                   BEGIN
                     U1:= SommAxBy( CosA, SinA, Xm - Xcen, Ym - Ycen);
                     V1:= SommAxBy(-SinA, CosA, Xm - Xcen, Ym - Ycen);
                     s:= U1 + Xcen; Ex:= Trunc(s); Dx:= Frac(s);
                     s:= V1 + Ycen; Ey:= Trunc(s); Dy:= Frac(s);
                     IF ((Ex>La - 2) OR (Ey>Ha - 2)) THEN
                       BEGIN
                         E(0012); Wt(10, 30, 'Ex, Ey =');
                         E(0011); Write(Ex:10, Ey: 10); A_
                       END;
                     Px:= F_Couleur(Dx, Dy, Matrice_1[Ex, Ey],
                                    Matrice_1[Ex + 1, Ey], Matrice_1[Ex, Ey + 1],
                                    Matrice_1[Ex + 1, Ey + 1])
                   END;
               Ma2[Xm, Ym]:= Px     // P00, P10, P01, P11
             END
       END;



    Il n'y a qu'une toute petite différence, à peine visible - cherchez bien, qui donne l'avantage au second procédé - ainsi la morale est sauve ...


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  8. #8
    Membre éclairé
    Bonsoir,

    Citation Envoyé par wiwaxia Voir le message

    ... Il s'agit bien, dans les deux cas, de distances euclidiennes.
    Ce ne sont pas des distances euclidiennes mais les distances aux côtés qui sont parallèles aux axes. Si on prend 2 points au hasard, leur distance ainsi calculée sera différente après rotation. Une distance euclidienne est isotrope et donc indépendante de toute rotation. Avec cette distance, un "cercle" de rayon a/2 s'écrira a = max(abs(dx), abs(dy)) et sera un carré de côtés a // aux axes.

    C'est un choix qui a sa logique, beaucoup d'avantages dont la grande simplicité, mais qui n'est pas le seul possible. Ma réflexion est juste un éclairage pour sortir un peu des sentiers battus. Ce n'est pas une réussite.

    Une question pour le fun. Pourquoi un maillage hexagonal est vu par ses triangles élémentaires et pas le maillage carré ?

    Autre chose, je crois m'être mal exprimé mais je n'ai pas dit qu'il fallait étendre le nombre de points impactés mais au contraire supprimer le point de la maille le plus loin qui semble illégitime puisque souvent (environ dans 35% des cas) il y a un point hors maille qui est plus près. Une autre manière de le dire, c'est considérer la maille carrée comme la combinaison de mailles triangulaires (triangles rectangles isocèles). Ne prendre que 3 points ne saurait augmenter le flou d'interpolation.

    Si le plus proche voisin, le bilinéaire ou autres algorithmes pondérés par des réponses impulsionnelles (en X et Y) répondent aux besoins c'est parfait. Il faut juste savoir que leur anisotropie donne des niveaux de qualité différents selon les axes (bon sur les verticales et horizontales mais moyens sur les diagonales).

    Bonne nuit.
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  9. #9
    Membre chevronné
    Coordonnées polaires vers coordonnées XY (cartésiennes)
    La différence apparaît évidemment à la limite supérieure de la tête, où une frontière peu incurvée sépare une zone relativement claire d'une autre nettement plus sombre: l'aspect pseudo-aléatoire de la fonction Round(u), ainsi que l'absence de toute modulation des teintes, confèrent un aspect crénelé au contour de l'image. Rien de tel par contre dès qu'intervient la pondération de plusieurs pixels.
    Voici iles deux images agrandies 5 fois des deux régions consernées; elles démontrent sans appel la supériorité du second procédé:



    D'autres améliorations son envisageables, comme par exemple:
    # le remplacement de la fonction f(u) = u et de sa complémentaire g(u) = 1 -f(u) par
    f1(u) = 3u2 - 2u3 et g1(u) = 1 - f1(u) ,
    dont les dérivées s'annulent aux extrémités du domaine ([0 ; 1]);
    # l'intervention directe, dans les combinaisons linéaires, des luminances relatives (L) des couleurs au lieu de leurs indices; on pourra prendre, afin de limiter la lourdeur des calculs L = (IC)2 .


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  10. #10
    Membre éclairé
    Ca tourne pas rond
    Bonjour,

    La distance utilisée dans l'interpolation bilinéaire est celle-ci (et non pas max(|dx|, |dy|) comme je l'ai écrit) :


    La distance constante se caractérise par une influence constante d'un point par rapport à un autre c'est à dire un coefficient alpha constant. Comme on peut le voir, ce n'est pas tout à fait un cercle

    L'image sympathique du serpent illustre bien la différence entre le plus proche voisin (degré 0 de l'interpolation) et une interpolation bilinéaire. Souvent on prend une image avec un visage car notre cerveau est plus critique : par exemple la célèbre photo de Lena qui conjugue beaucoup de pièges (par exemple les trames du chapeau et ruban). Le plus proche voisin vérole allègrement la peau.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  11. #11
    Membre chevronné
    Coordonnées polaires vers coordonnées XY (cartésiennes)
    Bonjour,

    Comme il est assez long de répondre, je m'en tiens dans l'immédiat aux 4 versions du programme réalisant une rotation de 120° sur l'image proposée; les instructions sont assez proches:

    1°) pondération des indices de couleur:
    a) au moyen des parties décimales (Dz = Dx ou Dy): procédure Calc_Mat_Im2_Ic1Dz1(La, Ha: Z_32; VAR Ma2: Tab_Pix), qui calcule la matrice du corps de la seconde image (Image_2);
    b) au moyen du polynôme de degré 3:
    C(Dz) = 3.Dz2 - 2.Dz3 : procédure Calc_Mat_Im2_Ic1Dz3(La, Ha: Z_32; VAR Ma2: Tab_Pix);

    2°) pondération portant sur les carrés des indices de couleur:
    a) au moyen des parties décimales (Dz = Dx ou Dy): procédure Calc_Mat_Im2_Ic2Dz1(La, Ha: Z_32; VAR Ma2: Tab_Pix);
    b) au moyen du polynôme de degré (3) C(Dz) : procédure Calc_Mat_Im2_Ic2Dz3(La, Ha: Z_32; VAR Ma2: Tab_Pix).

    Les écarts sont pratiquement indiscernables; les détails les plus fins paraissent présents dans la dernière image (mais cela relève peut-être de l'auto-suggestion ). Les délais d'exécution varient peu.
    Ic1Dz1
    Ic1Dz3

    Ic2Dz1
    Ic2Dz3

    Ci-dessous le code Pascal correspondant; il y avait une erreur (corrigée depuis) concernant l'angle de rotation (Alpha), qui conduisait à une valeur complémentaire de celle attendue, au niveau de l'appel des fonctions trigonométriques:
    Code Pascal :Sélectionner tout -Visualiser dans une fenêtre à part
     SinCos(Alpha * DegRad, SinA, CosA);     // au lieu de SinCos(Alpha * DegRad, CosA, SinA)


    Code Pascal :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
     FUNCTION SommAxBy(Ka, Kb, Wx, Wy: Reel): Reel;
       VAR p, q: Reel;
       BEGIN
         p:= Ka * Wx; q:= Kb * Wy; Result:= p + q
       END;
     
     FUNCTION F_Couleur_Ic2(u, v: Reel; P00, P10, P01, P11: Pixel): Pixel;
       VAR k:Byte; s: Reel; Wx0, Wx1: Ve3R; Px: Pixel;
       BEGIN
         FOR k:= 1 TO 3 DO                  // Calcul moyenne quadratique
           BEGiN
             Wx0[k]:= SommAxBy(1 - u, u, Sqr(P00[k]), Sqr(P10[k]));
             Wx1[k]:= SommAxBy(1 - u, u, Sqr(P01[k]), Sqr(P11[k]));
             s:= SommAxBy(1 - v, v, Wx0[k], Wx1[k]);
             Px[k]:= Round(Sqrt(s))
           END;
         Result:= Px
       END;
     
     FUNCTION F_Couleur(u, v: Reel; P00, P10, P01, P11: Pixel): Pixel;
       VAR k:Byte; Wx0, Wx1: Ve3R; Px: Pixel;
       BEGIN
         FOR k:= 1 TO 3 DO
           BEGiN
             Wx0[k]:= SommAxBy(1 - u, u, P00[k], P10[k]);
             Wx1[k]:= SommAxBy(1 - u, u, P01[k], P11[k]);
             Px[k]:= Round(SommAxBy(1 - v, v, Wx0[k], Wx1[k]))
           END;
         Result:= Px
       END;
     
     FUNCTION C(r: Reel): Reel;             // C = 3r^2 - 2r^3 = r^2(3 - 2r)
       VAR R2, s: Reel;
       BEGIN
         R2:= Sqr(r); s:= 3 - (2 * r); Result:= R2 * s
       END;
     
    (*HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
     
     Calcul de la seconde matrice
     
    HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH*)
     
     PROCEDURE ZeroM(VAR Ma: Tab_Pix);
       CONST Pzero: Pixel = (0, 0, 255);
       VAR i, j: Z_32;
       BEGIN
         FOR i:= 0 TO Dim_Max DO
           FOR j:= 0 TO Dim_Max DO Ma[i, j]:= Pzero
       END;
     
     PROCEDURE Calc_Mat_Im2_Ic2Dz3(La, Ha: Z_32; VAR Ma2: Tab_Pix);
       CONST P000: Pixel = (0, 0, 0);
             DegRad = Pi / 180; Alpha = 120;
       VAR Ex, Ey, Xm, Xm1, Ym, Ym1: Z_32;
           CosA, Dx, Dy, Ray, Ray2, s, SinA, U1, U2, V1, V2, Xcen, Ycen: Reel;
           Px: Pixel;
       BEGIN
         SinCos(Alpha * DegRad, SinA, CosA);
         Xcen:= (La - 1) / 2; Ycen:= (Ha - 1) / 2;
         IF (Xcen<Ycen) THEN Ray:= Xcen ELSE Ray:= Ycen;
         Ray2:= Sqr(Ray);     ZeroM(Matrice_2);
         FOR Xm:= 0 TO (La - 1) DO
           FOR Ym:= 0 TO (Ha - 1) DO
             BEGIN
               U2:= Sqr(Xm - Xcen); V2:= Sqr(Ym - Ycen); s:= U2 + V2;
               IF (s>=Ray2)
                 THEN Px:= P000
                 ELSE
                   BEGIN
                     U1:= SommAxBy( CosA, SinA, Xm - Xcen, Ym - Ycen);
                     V1:= SommAxBy(-SinA, CosA, Xm - Xcen, Ym - Ycen);
                     s:= U1 + Xcen; Ex:= Trunc(s); Dx:= Frac(s);
                     s:= V1 + Ycen; Ey:= Trunc(s); Dy:= Frac(s);
                     IF ((Ex>La - 1) OR (Ey>Ha - 1)) THEN
                       BEGIN
                         E(0012); Wt(10, 30, 'Ex, Ey =');
                         E(0011); Write(Ex:10, Ey: 10); A_
                       END;
                     Px:= F_Couleur_Ic2(C(Dx), C(Dy), Matrice_1[Ex, Ey],
                                    Matrice_1[Ex + 1, Ey], Matrice_1[Ex, Ey + 1],
                                    Matrice_1[Ex + 1, Ey + 1])
                   END;
               Ma2[Xm, Ym]:= Px             // C(Dz): polyn“me de degr‚ 3
             END
       END;
     
     PROCEDURE Calc_Mat_Im2_Ic2Dz1(La, Ha: Z_32; VAR Ma2: Tab_Pix);
       CONST P000: Pixel = (0, 0, 0);
             DegRad = Pi / 180; Alpha = 120;
       VAR Ex, Ey, Xm, Xm1, Ym, Ym1: Z_32;
           CosA, Dx, Dy, Ray, Ray2, s, SinA, U1, U2, V1, V2, Xcen, Ycen: Reel;
           Px: Pixel;
       BEGIN
         SinCos(Alpha * DegRad, SinA, CosA);
         Xcen:= (La - 1) / 2; Ycen:= (Ha - 1) / 2;
         IF (Xcen<Ycen) THEN Ray:= Xcen ELSE Ray:= Ycen;
         Ray2:= Sqr(Ray);     ZeroM(Matrice_2);
         FOR Xm:= 0 TO (La - 1) DO
           FOR Ym:= 0 TO (Ha - 1) DO
             BEGIN
               U2:= Sqr(Xm - Xcen); V2:= Sqr(Ym - Ycen); s:= U2 + V2;
               IF (s>=Ray2)
                 THEN Px:= P000
                 ELSE
                   BEGIN
                     U1:= SommAxBy( CosA, SinA, Xm - Xcen, Ym - Ycen);
                     V1:= SommAxBy(-SinA, CosA, Xm - Xcen, Ym - Ycen);
                     s:= U1 + Xcen; Ex:= Trunc(s); Dx:= Frac(s);
                     s:= V1 + Ycen; Ey:= Trunc(s); Dy:= Frac(s);
                     IF ((Ex>La - 1) OR (Ey>Ha - 1)) THEN
                       BEGIN
                         E(0012); Wt(10, 30, 'Ex, Ey =');
                         E(0011); Write(Ex:10, Ey: 10); A_
                       END;
                     Px:= F_Couleur_Ic2(Dx, Dy, Matrice_1[Ex, Ey],
                                    Matrice_1[Ex + 1, Ey], Matrice_1[Ex, Ey + 1],
                                    Matrice_1[Ex + 1, Ey + 1])
                   END;
               Ma2[Xm, Ym]:= Px     // P00, P10, P01, P11
             END
       END;
     
     PROCEDURE Calc_Mat_Im2_Ic1Dz3(La, Ha: Z_32; VAR Ma2: Tab_Pix);
       CONST P000: Pixel = (0, 0, 0);
             DegRad = Pi / 180; Alpha = 120;
       VAR Ex, Ey, Xm, Xm1, Ym, Ym1: Z_32;
           CosA, Dx, Dy, Ray, Ray2, s, SinA, U1, U2, V1, V2, Xcen, Ycen: Reel;
           Px: Pixel;
       BEGIN
         SinCos(Alpha * DegRad, SinA, CosA);
         Xcen:= (La - 1) / 2; Ycen:= (Ha - 1) / 2;
         IF (Xcen<Ycen) THEN Ray:= Xcen ELSE Ray:= Ycen;
         Ray2:= Sqr(Ray);     ZeroM(Matrice_2);
         FOR Xm:= 0 TO (La - 1) DO
           FOR Ym:= 0 TO (Ha - 1) DO
             BEGIN
               U2:= Sqr(Xm - Xcen); V2:= Sqr(Ym - Ycen); s:= U2 + V2;
               IF (s>=Ray2)
                 THEN Px:= P000
                 ELSE
                   BEGIN
                     U1:= SommAxBy( CosA, SinA, Xm - Xcen, Ym - Ycen);
                     V1:= SommAxBy(-SinA, CosA, Xm - Xcen, Ym - Ycen);
                     s:= U1 + Xcen; Ex:= Trunc(s); Dx:= Frac(s);
                     s:= V1 + Ycen; Ey:= Trunc(s); Dy:= Frac(s);
                     IF ((Ex>La - 1) OR (Ey>Ha - 1)) THEN
                       BEGIN
                         E(0012); Wt(10, 30, 'Ex, Ey =');
                         E(0011); Write(Ex:10, Ey: 10); A_
                       END;
                     Px:= F_Couleur(C(Dx), C(Dy), Matrice_1[Ex, Ey],
                                    Matrice_1[Ex + 1, Ey], Matrice_1[Ex, Ey + 1],
                                    Matrice_1[Ex + 1, Ey + 1])
                   END;
               Ma2[Xm, Ym]:= Px             // C(Dz): polyn“me de degr‚ 3
             END
       END;
     
     PROCEDURE Calc_Mat_Im2_Ic1Dz1(La, Ha: Z_32; VAR Ma2: Tab_Pix);
       CONST P000: Pixel = (0, 0, 0);
             DegRad = Pi / 180; Alpha = 120;
       VAR Ex, Ey, Xm, Xm1, Ym, Ym1: Z_32;
           CosA, Dx, Dy, Ray, Ray2, s, SinA, U1, U2, V1, V2, Xcen, Ycen: Reel;
           Px: Pixel;
       BEGIN
         SinCos(Alpha * DegRad, SinA, CosA);
         Xcen:= (La - 1) / 2; Ycen:= (Ha - 1) / 2;
         IF (Xcen<Ycen) THEN Ray:= Xcen ELSE Ray:= Ycen;
         Ray2:= Sqr(Ray);     ZeroM(Matrice_2);
         FOR Xm:= 0 TO (La - 1) DO
           FOR Ym:= 0 TO (Ha - 1) DO
             BEGIN
               U2:= Sqr(Xm - Xcen); V2:= Sqr(Ym - Ycen); s:= U2 + V2;
               IF (s>=Ray2)
                 THEN Px:= P000
                 ELSE
                   BEGIN
                     U1:= SommAxBy( CosA, SinA, Xm - Xcen, Ym - Ycen);
                     V1:= SommAxBy(-SinA, CosA, Xm - Xcen, Ym - Ycen);
                     s:= U1 + Xcen; Ex:= Trunc(s); Dx:= Frac(s);
                     s:= V1 + Ycen; Ey:= Trunc(s); Dy:= Frac(s);
                     IF ((Ex>La - 1) OR (Ey>Ha - 1)) THEN
                       BEGIN
                         E(0012); Wt(10, 30, 'Ex, Ey =');
                         E(0011); Write(Ex:10, Ey: 10); A_
                       END;
                     Px:= F_Couleur(Dx, Dy, Matrice_1[Ex, Ey],
                                    Matrice_1[Ex + 1, Ey], Matrice_1[Ex, Ey + 1],
                                    Matrice_1[Ex + 1, Ey + 1])
                   END;
               Ma2[Xm, Ym]:= Px     // P00, P10, P01, P11
             END
       END;


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  12. #12
    Membre éclairé
    Tournis
    Bonjour Wiwaxia,

    Tu t'es bien amusé. Toutes ces opérations utilisent le même type de distance et présentent finalement peu de différences qui seront valorisés ou non selon le type d'image. Je les ai utilisé (j'aime bien Lanczos même si les artefacts ne sont jamais loin et qu'il ne se contente pas des 4 plus proches voisins).

    Un jour que j'avais du courage, j'ai réécrit la rotation bilinéaire sans calcul trigonométrique ni réels dans les boucles. Cela consiste à pré-calculer les 4 coins de la source qui correspondent aux 4 coins de la destination (rotation inverse) puis à parcourir la destination en faisant correspondre via des Breshenham (deux pour chaque axe) les points de la source. Les erreurs sur les Breshenham donnent le taux de répartition point courant point suivant. C'est assez laborieux à écrire (et à lire) mais c'est très rapide malgré les quelques 150 lignes nécessaires. Si ça t'intéresse (c'est un vieux code Delphi qui mériterait un coup de balais).

    Ci-joint l'étude préalable en odg (le site refuse l'odg mais pas sa version compressé qui pourtant n'est pas très utile puisqu'un odg est déjà un zip) :

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  13. #13
    Membre éclairé
    Détails
    Les deux dernières demoiselles semblent effectivement plus détaillées (la texture du chapeau).

    Tu savais que cette photo, beaucoup utilisée en traitement d'image, provient d'une image en pied qui prouve que le chapeau est la pièce maîtresse de son habillement ?

    Dans ton code je vois un type reel, ça existe en pascal ?

    L'inversion sinA cosA n'nverse pas l'angle A mais le transforme en PI/2 - A.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  14. #14
    Membre chevronné
    Citation Envoyé par Guesset Voir le message
    ... j'ai réécrit la rotation bilinéaire sans calcul trigonométrique ni réels dans les boucles. Cela consiste à pré-calculer les 4 coins de la source qui correspondent aux 4 coins de la destination (rotation inverse) puis à parcourir la destination en faisant correspondre via des Breshenham (deux pour chaque axe) les points de la source. Les erreurs sur les Breshenham donnent le taux de répartition point courant point suivant. C'est assez laborieux à écrire (et à lire) mais c'est très rapide malgré les quelques 150 lignes nécessaires. Si ça t'intéresse (c'est un vieux code Delphi qui mériterait un coup de balais) ...
    Il m'est arrivé de coder la transformation d'une image rectangulaire en une autre délimitée par un quadrilatère quelconque, et cela aurait pu effectivement se transformer en rotation (avec réduction d'échelle, cette fois).
    Le programme source m'intéresse à priori, et je le suppose lisible (à défaut d'être facilement compréhensible - ça, c'est une autre histoire ...).

    Citation Envoyé par Guesset Voir le message
    Les deux dernières demoiselles semblent effectivement plus détaillées (la texture du chapeau) ...
    Je m'y attendais, mais n'ai pas eu le temps de vérifier; je m'en suis tenu à la comparaison de quelques fils ou cheveux brillants sur fond sombre (ou inversement).

    Citation Envoyé par Guesset Voir le message
    ... Tu savais que cette photo, beaucoup utilisée en traitement d'image, provient d'une image en pied qui prouve que le chapeau est la pièce maîtresse de son habillement ?
    Je connaissais l'origine de la photo, et savais que Mme Söderberg y apparaissait vêtue d'un rayon de soleil (ou plutôt du faisceau d'un projecteur)

    Citation Envoyé par Guesset Voir le message
    ... Dans ton code je vois un type reel, ça existe en pascal ?
    C'est le nom court attribué au type Extended.

    Citation Envoyé par Guesset Voir le message
    ... L'inversion sinA cosA n'nverse pas l'angle A mais le transforme en PI/2 - A.
    C'est ce qui avait été signalé: les deux valeurs sont complémentaires. Cela ne compromettait pas l'exécution du programme.


    Le français, notre affaire à tous
    Grand Dictionnaire Terminologique

  15. #15
    Membre éclairé
    Bonjour,

    Code principal (les codes en assembleur de calcul de lignes et des points hors source ne sont pas là).

    Si on ne veut pas de traitement de hors source on pourrait pré-calculer ligne par ligne les débuts et fins effectifs.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    // Rotation optimisée_________________________________________________________DEFORMATIONS
    procedure RotAuto(bmpIn, bmpOut : TBitmap; Angle : Double; SizeCode : Integer;
       DrawCode : Integer);
    var
       cs, sn           : Double;
       FnLineDelta      : TFnLine2DLimit ;
       LX, LY           : TLine2D;
       pIns             : array of PxRGB;
       pOut, pIn        : PxRGB;
       Clr              : TxRGB;                     // Couleur des points hors image source
       V0, V1, V2, V3   : TxRGB;                     // (X0,Y0), (X1,Y0), (X0,Y1), (X1,Y1)
       Sx, Sy, Ex, Ey, Exnz, Eynz           : Integer;
       Wx, Wy, Hx, Hy, i, XinMax, YinMax    : Integer;
       X0, Y0, X1, Y1, XY0, XY1, Xout, Yout : Integer;
    begin
       if (bmpIn = nil) or (bmpIn.Width < 64) or (bmpIn.Height < 64) then Exit;
       if abs(Angle) < 0.001 then Exit;
       Cs     := Cos(Angle);
       Sn     := Sin(Angle);
       // Image Cible................................//
       if bmpOut = nil then bmpOut := TBitmap.Create;
       if bmpOut.PixelFormat <> pf32bit then bmpOut.PixelFormat := pf32bit;
       case iSign(SizeCode) of                       // Taille de sortie
          -1:begin                                   // Rotation interne à la source (même ratio) 
             X0 := abs(Round(bmpIn.Width*cs - bmpIn.Height*sn));
             Y0 := abs(Round(bmpIn.Width*sn + bmpIn.Height*cs));
             if X0*bmpIn.Height > Y0*bmpIn.Width then begin
                bmpOut.Width := (bmpIn.Width*bmpIn.Width ) div X0;
                bmpOut.Height:= (bmpIn.Width*bmpIn.Height) div X0;
                end
             else begin
                bmpOut.Width := (bmpIn.Width *bmpIn.Height) div Y0;
                bmpOut.Height:= (bmpIn.Height*bmpIn.Height) div Y0;
                end;
             end;
          +1:begin                                   // Destination englobante
             bmpOut.Width := Max(abs(Round(bmpIn.Width*cs + bmpIn.Height*sn)),
                                 abs(Round(bmpIn.Width*cs - bmpIn.Height*sn)));
             bmpOut.Height:= Max(abs(Round(bmpIn.Width*sn + bmpIn.Height*cs)),
                                 abs(Round(bmpIn.Width*sn - bmpIn.Height*cs)));
             end;
          else begin
             bmpOut.Width := bmpIn.Width;
             bmpOut.Height:= bmpIn.Height;
             end;
          end;
       // Image Source...............................//
       if bmpIn.PixelFormat <> pf32bit then bmpIn.PixelFormat := pf32bit;
       SetLength(pIns, bmpIn.Height);
       for i := 0 to bmpIn.Height-1 do pIns[i] := bmpIn.ScanLine[i];
       XinMax := bmpIn.Width - 1;
       YinMax := bmpIn.Height- 1;
       Wx     := Round(bmpOut.Width*cs);
       Wy     := Round(bmpOut.Width*sn);
       Hx     :=-Round(bmpOut.Height*sn);
       Hy     := Round(bmpOut.Height*cs);
       // Initialisations............................//
       Line2DInit(LY, 0, bmpOut.Height-1,            // Initialiser le tacé correspondant
          (bmpIn.Width - Wx - Hx) div 2, (bmpIn.Height - Wy - Hy) div 2, 0, XinMax,
          (bmpIn.Width - Wx + Hx) div 2, (bmpIn.Height - Wy + Hy) div 2, 0, YinMax);
       Sx := (LY.Sgn[0] - iSign(Wx)) div 2;
       Sy := (LY.Sgn[1] - iSign(Wy)) div 2;
       if Sx <> 0 then Ex := $7FFFFFFF else Ex := 0;
       if Sy <> 0 then Ey := $7FFFFFFF else Ey := 0;
       case DrawCode of                              // Représentation des points hors source
          -1 :  FnLineDelta := Line2DSphere;         // z < 0 => Lz + z, z >= Lz => z - Lz
          -2 :  FnLineDelta := Line2DMiror;          // z < 0 => -z,     z >= Lz => 2*Lz - z
          else  begin
                FnLineDelta := Line2DClip;           // z < 0 => 0,     z >= Lz => Lz - 1
                Clr.Color   := QuadOf(DrawCode and $FFFFFF);
          end;
       end;
       // Boucles....................................//
       for Yout := 0 to bmpOut.Height-1 do begin     //------------------------------Verticale
          pOut := bmpOut.ScanLine[Yout];
          Exnz := -Ord(LY.E[0] <> 0);                // Ex_Not_Zero = LY.E0.d0 <> 0 ? -1 : 0
          Eynz := -Ord(LY.E[1] <> 0);                // Ey_Not_Zero = LY.E0.d1 <> 0 ? -1 : 0
          Line2DInit(LX, 0, bmpOut.Width -1,
             LY.V[0] + (Sx and Exnz), LY.V[0] + (Sx and Exnz) + Wx, 0, XinMax,
             LY.V[1] + (Sy and Eynz), LY.V[1] + (Sy and Eynz) + Wy, 0, YinMax,
             ((Ex and Exnz) xor LY.E[0]) - Exnz,
             ((Ey and Eynz) xor LY.E[1]) - Eynz);
          for Xout := 0 to bmpOut.Width-1 do begin   //--------------------Horizontale
             XY0 := FnLineDelta(LX, 0);              // Limite inférieure
             X0  := LX.VdU[0];
             Y0  := LX.VdU[1];
             XY1 := FnLineDelta(LX, 1);              // Limite supérieure
             X1  := LX.VdU[0];
             Y1  := LX.VdU[1];
             // Filtrage bilinéaire..................//
             if XY0 < 0 then                         // Une limite dépassée non corrigée ?
                if XY0 and $30000 <> 0 then begin    // Y0 non OK  (Y est le 2ème argument)
                   V0 := Clr;
                   V1 := Clr;
                end
                else begin                           // Y0 OK
                   if XY0 and 3 <> 0 then V0 := Clr  // X0 non OK  (X est le 1er argument)
                   else begin  pIn := pIns[Y0];  Inc(pIn, X0);  V0 := pIn^;  end;
                   if XY1 and 3 <> 0 then V1 := Clr  // X1 non OK
                   else begin  pIn := pIns[Y0];  Inc(pIn, X1);  V1 := pIn^;  end;
                end
             else begin
                pIn := pIns[Y0];  Inc(pIn, X0);  V0 := pIn^;
                pIn := pIns[Y0];  Inc(pIn, X1);  V1 := pIn^;
             end;
             if XY0 < 0 then                         // Une limite dépassée non corrigée ?
                if XY1 and $30000 <> 0 then begin    // Y1 non OK
                   V2 := Clr;
                   V3 := Clr;
                end
                else begin                              // Y1 OK
                   if XY0 and 3 <> 0 then V2 := Clr     // X0 non OK
                   else begin  pIn := pIns[Y1];  Inc(pIn, X0);  V2  := pIn^;  end;
                   if XY1 and 3 <> 0 then V3 := Clr     // X1 non OK
                   else begin  pIn := pIns[Y1];  Inc(pIn, X1);  V3  := pIn^;  end;
                end
             else begin
                pIn := pIns[Y1];  Inc(pIn, X0);  V2  := pIn^;
                pIn := pIns[Y1];  Inc(pIn, X1);  V3  := pIn^;
             end;
             asm                                     // Moyenne pondérée des 4 points adjacents
                lea       eax,     LX
                pxor      mm0,     mm0
                movq      mm1,     [eax+8]           // LX.E0  -> E0
                pshufw    mm2,     mm1, $55          // E0.d[0]/ 2^16 -> mm2.w[0..3] = Ax
                pshufw    mm3,     mm1, $FF          // E0.d[1]/ 2^16 -> mm3.w[0..3] = Ay
                movd      mm4,     V0
                movd      mm5,     V1
                punpcklbw mm4,     mm0
                punpcklbw mm5,     mm0
                movd      mm6,     V2
                psubw     mm5,     mm4
                movd      mm7,     V3
                psllw     mm5,     1
                punpcklbw mm6,     mm0
                pmulhw    mm5,     mm2
                punpcklbw mm7,     mm0
                paddw     mm4,     mm5
                psubw     mm7,     mm6
                psllw     mm7,     1
                pmulhw    mm7,     mm2
                paddw     mm6,     mm7
                psubw     mm6,     mm4
                psllw     mm6,     1
                pmulhw    mm6,     mm3
                paddw     mm4,     mm6
                pmaxsw    mm4,     mm0
                packuswb  mm4,     mm0
                movd      V0,      mm4
             end;
             pOut.int := V0.int and $FFFFFF;
             // Point suivant........................//
             Inc(pOut);
             Line2DInc(LX);
          end;
          Line2DInc(LY);
       end;
       asm emms end;
       SetLength(pIns, 0);
       bmpOut.Modified := True;
    end;


    Ce que ça peut donner (sphérique):

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)