• Aucun résultat trouvé

2.3 Nouvel algorithme pour des signaux 1-D

Cette section introduit un algorithme permettant de calculer un arbre de composantes connexes, une granulométrie ou une ouverture par attribut de longueur à partir d'un signal 1-D. Outre la exibilité, la vitesse d'exécution de cet algorithme est un élément crucial qui sera précisément décrit et commenté dans cette section. À la base de cet algorithme rapide, on retrouve une décomposition compacte et ecace du signal 1-D en une structure appelée cordes.

2.3.1 Décomposition du signal en cordes maximales

On considère un signal 1-D, g : E → V , avec V = R ou Z. On rappelle que Xh décrit la binarisation

de g au niveau h et que {Xh

i} est l'ensemble des CCs à ce niveau h (on réindexe {Xih} en {Xj}).

On remarque qu'une même composante connexe peut être dénie pour plusieurs seuils successifs. Néanmoins, on souhaite avoir une représentation de g la plus compacte possible et la réduction des doublons passe par trouver h le plus haut possible, permettant de dénir et d'extraire une CC. Définition 2.3

On appelle corde tous les couples c = (Xj,k)appartenant à {Xj} × V, tel que :

k = min

x∈Xj

g(x), (2.9)

où k correspond à l'altitude de la corde. Comme Xj est un intervalle de Z, on peut l'écrire [sp,fp]

avec sp et fp respectivement la position initiale et nale de la corde. Ainsi, la longueur d'une corde est égale à Length(c) = fp − sp + 1. La gure 2.3(a) illustre la décomposition d'un signal 1-D en un ensemble de cordes : c4

c

3 rp 7 6 5 4 3 2 1 0

c

2

c

c5

c

6 1 (a)

c

1

c

2

c

3 (b) (c)

c

1

c

2

c

4

c

3

c

5

c

6

Figure 2.3  (a) signal 1-D et les cordes associées. La lecture du signal se fait séquentiellement de la gauche vers la droite et la position de la tête de lecture est représentée par rp (les lignes continues (resp. en pointillées) représentent les éléments connus (resp. inconnus)) ; (b) arbre de composantes connexes ; (c) état courant de la pile (en fonction de rp).

La longueur des cordes respecte la propriété d'inclusion. En eet, pour deux cordes ci = {Xi,ki}

et cj = {Xj,kj} telles que Xi ⊂ Xj, alors on aura ki > kj. On dit alors que cj est l'ancêtre de ci et

réciproquement, ci est le descendant de cj. Si on admet que ci est le plus grand descendant de cj au

sens de la longueur de la corde, alors on dit que ci est le ls de cj ou que cj est le père de ci. Cette

d'une corde ci :

V (ci) = (ki− kj)Length(ci). (2.10)

Définition 2.4

L'ensemble des cordes d'un signal, noté C = {(Xj,kj)}, est une représentation strictement équi-

valente au signal g et la reconstruction de ce signal au point x, est calculée en prenant l'altitude maximale de toutes les cordes de C contenant x :

g(x) = max

(Xj,kj)∈C, x∈Xj

kj. (2.11)

À partir de cet ensemble de cordes C, le calcul des ouvertures et des granulométries est réalisé d'une manière très ecace :

γL(C) = {cj|Length(cj) ≥ L}, (2.12)

(P S(C))(L) = X

Length(cj)=L

V (cj). (2.13)

Grâce à l'équation 2.11, γL(C) et P S(C) sont deux opérateurs strictement équivalents à γL(g) et à

P S(g). On verra par la suite, que cette décomposition du signal en cordes peut être faite d'une manière ecace et rapide, en temps constant par pixel.

L'ecacité de l'algorithme vient de plusieurs caractéristiques : le signal est lu séquentiellement de gauche à droite, chaque corde est analysée une unique fois, et toutes les opérations peuvent être réalisées en O(1) par pixel en moyenne y compris la reconstruction du signal 1-D à partir de l'ensemble des cordes (équation 2.11). Le principe de l'algorithme est présenté dans la section suivante.

2.3.2 Principe de l'algorithme

En analysant le signal de la gure 2.3, ainsi que son ensemble de cordes C, on remarque plusieurs phénomènes que l'on décrit ci dessous :

1. Chaque front montant du signal est le point de départ d'au moins une corde. 2. Chaque front descendant du signal représente la n d'au moins une corde.

3. La longueur d'une corde n'est connue que lorsque l'on arrive à sa n. Dans la gure 2.3, les portions connues de chaque cordes sont représentées par un trait continu jusqu'à la position de lecture rp. Les cordes (ou partie de corde) non connues pour l'instant sont en pointillées. 4. Une corde ne pourra être traitée que lorsque la position de lecture rp arrivera à sa n.

5. Les cordes naissantes, attendant d'être traitées, peuvent être stockées dans une pile (LIFO : last in, rst out). Une pile est une structure ordonnée qui s'adapte parfaitement à nos données puisque les cordes sont elles-mêmes ordonnées en fonction de leurs tailles et seront donc visitées dans l'ordre d'inclusion.

L'utilisation d'une pile est donc nécessaire pour obtenir un algorithme rapide et la section suivante présente son pseudo code.

2.3 Nouvel algorithme pour des signaux 1-D

2.3.3 Pseudo code de l'algorithme

L'algorithme 1prend pour entrée un signal 1-D ainsi que les paramètres du traitement. op corres- pond à l'opérateur que l'on souhaite calculer, il peut prendre comme valeurs {Ouverture, Granulomé- trie, Arbre de composantes connexes}. Pour le calcul des ouvertures, un paramètre supplémentaire est nécessaire : le paramètre de longueur L.

En sortie, on aura le résultat de l'opérateur sélectionné : soit γL(g), soit le P S soit CompT ree pour

l'ouverture, la distribution de tailles et l'arbre de composantes connexes respectivement.

rp correspond à la position de la tête de lecture et le signal est lu séquentiellement de gauche à droite, lignes 4 et 5.

Algorithme 1: (gout or PS or CompTree) ← ProcessSignal1D (g, L, op)

Input : g : [1 . . . N] → R - signal d'entrée ; op, opérateur sélectionné

L, paramètre de l'ouverture Result : gout = γL(g)or CompT ree or P S

1 Stack ← ∅

2 CompT ree ← ∅

3 P S[1..N ] ← 0

4 for rp = 1..N do

5 (gout or PS or CompTree)← ProcessPixel(g(rp), rp, L, Stack, op) 6 (gout or PS or CompTree)← ProcessPixel(−∞, N + 1, L, Stack, op)

Chaque corde est codée par un couple de deux variables (sp,k) : la position de départ sp et l'altitude kde la corde. Les cordes naissantes dont on ne connaît pas encore la taille sont stockées dans une pile appelée Stack et qui supporte les opérations suivantes :

 push(sp,k) ajout d'une nouvelle corde dans la pile avec une position de départ sp et une altitude k,

 pop() pour supprimer de la pile la dernière corde insérée,  empty() pour tester si la pile est vide,

 top().att pour lire l'état d'une variable de la dernière corde insérée dans la pile (att = sp ou att = k).

Au début du traitement, la pile est vide et la variable PS est initialisée à zéro (lignes 1 à 3). Tous les pixels à la position rp sont traités par l'algorithme 2 et à chaque nouveau pixel, trois situations peuvent se produire : le pixel introduit un front montant, un front descendant ou une zone plate :

 Même altitude : si le pixel courant est à la même altitude que la dernière corde ajoutée dans la pile, alors ce pixel fait partie de cette corde et n'apporte aucune information complémentaire. On passe au pixel suivant.

 Front montant, ligne 1 : un front montant signie que l'on débute une nouvelle corde. On l'ajoute dans la pile en stockant son altitude et sa position de départ, ligne 2.

Algorithme 2: (gout or PS or CompTree) ← ProcessPixel (k, L, rp, Stack, op)

Input : k = g(rp)

rp, position de la tête de lecture L, paramètre de l'ouverture op, opérateur sélectionné

Stack, pile stockant les cordes (LIFO) Result : gout or P S or CompT ree en fonction de op 1 if Stack.empty() or k > Stack.top().k then

2 Stack.push(k,rp,F ALSE) 3 else 4 while k < Stack.top().k do 5 cordOut = Stack.pop() 6 if op == Granulometrie then 7 Length = rp − cordOut.sp

8 P S[Length]+ = Length × (cordOut.k − max(k,Stack.top().k))

9 if op ==Ouverture then

10 if cordOut.P assed or rp − cordOut.sp ≥ L then

11 gout← W riteCords(cordOut,Stack,rp)

12 Stack.push(k,rp,T RU E)

13 break

14 if op ==Arbre de composantes connexes then

15 if Stack.empty() or Stack.top().k < k then

16 currentCord.k = k 17 currentCord.Children.push(cordOut) 18 Stack.push(currentCord) 19 break 20 else 21 Stack.top().Children.push(cordOut)

22 if Stack.empty() or k > Stack.top().k then

23 Stack.push(k,cordOut.sp,F ALSE)

2.3 Nouvel algorithme pour des signaux 1-D

Algorithme 3: gout ←WriteCords(cordOut, Stack, rp)

Input : cordOut, Dernière corde sortie de la pile Stack, pile stockant les cordes (LIFO) rp, position de la tête de lecture Result : gout = γL(g)

1 gout[cordOut.sp : rp] = cordOut.k 2 while not Stack.empty() do

3 end = cordOut.sp

4 cordOut = Stack.pop()

5 gout[cordOut.sp : end] = cordOut.k

 Front descendant : un front descendant signie qu'au moins une corde se termine. Ainsi, la boucle while (lignes 4 à 24) dépile toutes les cordes insérées qui viennent de se terminer et les analyse une par une (cordOut). À ce niveau, le traitement dépend de l'opérateur sélectionné op : • Granulométrie : pour la corde qui vient d'être dépilée, on calcule sa longueur, ligne 7 puis on ajoute son volume dans l'histogramme indexé par sa longueur, ligne 8. On remarque que si la pile est vide, alors un front() sur la pile retourne −∞.

• Ouverture : on teste la longueur de la corde avec l'équation 2.12 qui correspond à la ligne 10 de l'algorithme. Dès qu'une corde est plus grande que L, on reconstruit immédiatement le signal gout = γL(g) jusqu'à la position de lecture (avec l'équation 2.11). En eet, la pile est

ordonnée grâce à la relation d'inclusion et dès que la longueur d'une corde satisfait le critère de longueur, alors l'ensemble des cordes présent dans la pile satisfait aussi le critère. Il n'est donc pas utile d'attendre leurs ns pour les écrire dans le signal de sortie. D'un point de vue algorithmique, il est même obligatoire d'écrire ces cordes dans goutà ce moment, si on souhaite

que chaque pixel ne soit écrit qu'une seule et unique fois dans le signal de sortie. La fonction WriteCords fait ce travail : la ligne 1 de l'algorithme 3, écrit la fraction de corde qui vient d'être dépilée dans gout, puis, la boucle while (lignes 2 à 5) va vider la pile. Pour chaque corde,

l'écriture se fera au niveau k, entre le point de départ de cette corde et le point de départ de la corde précédente (ligne 5). L'algorithme3 vidant la pile et écrivant le signal, il est nécessaire d'ajouter à la structure corde un drapeau1 que l'on nommera P assed et qui permettra de se

souvenir si la corde courante satisfait le critère de longueur. Enn, cette corde courante sera placée dans la pile avec une position de départ volontairement erronée (rp) an de ne par réécrire deux fois la même partie du signal. On voit ici tout l'intérêt de ce drapeau puisque la modication de la position de départ d'une corde entrainera la sous-estimation du calcul de sa longueur. On rappelle que pour des ouvertures, on ne souhaite pas connaitre la taille exacte des cordes, mais on souhaite seulement savoir si celles-ci sont plus grande que L.

• Arbre de composantes connexes : on enrichit la structure corde en ajoutant une nouvelle variable appelée Children qui correspond à une liste de pointeurs sur la structure corde. Le rôle de cette nouvelle variable est de représenter la relation parents/enfants de l'arbre. La corde qui se termine, cordOut, est une corde enfant qui doit être liée à son parent. Elle peut être liée à deux cordes diérentes, la corde courante currentCord ou la dernière corde de la pile :

 Si la pile est vide ou si l'altitude de la dernière corde de la pile est inférieure à l'altitude de la corde courante, alors on lie cordOut avec currentCord (lignes 16, 17 et 18).

 Sinon, on lie cordOut avec la dernière corde de la pile (ligne 21).

Que ce soit pour le calcul des ouvertures, des granulométries ou des arbres de composantes connexes, au moins une corde est présente dans la pile à la n du signal 1-D. Il est donc nécessaire de traiter ces cordes en fonction de l'opérateur op et de la gestion des bords désirée. Nous verrons plus précisément, dans la section2.4.3, cette gestion des bords. On notera ici que pour construire l'arbre de composantes connexes, il faut dépiler les cordes en les liants à la précédente. La dernière corde restante dans la pile sera appelée la racine1 de l'arbre. Cette gestion des bords peut être gérée très facilement en ajoutant

articiellement un pixel à −∞ à la n du signal (voir la ligne 6 de l'algorithme1) ce qui permettra de dépiler toutes les cordes restantes dans la pile.

Remarque : Cet algorithme ne réalise que des opérations de comparaison et de soustraction permettant de l'appliquer sur des données de diérentes précisions : 8-bits, 16-bits, 32-bits ou même des ottants simples et doubles précision. Plus généralement en algèbre, cet algorithme peut donc gérer n'importe quel type de données à partir du moment où les valeurs font partie d'un groupe ordonné (pour les trois opérateurs) et muni de la soustraction (seulement pour le calcul des granulométries).

La exibilité de cet algorithme permet de calculer avec le même pseudo code, des ouvertures avec un attribut de longueur, des granulométries ou l'arbre de composantes connexes. La section suivante présente l'extension de cet algorithme à d'autres opérateurs morphologiques : la reconstruction et l'ouverture par reconstruction. Ces opérateurs nécessitent tous deux, une image marqueur comme entrée, rendant dicile l'intégration des modications dans le pseudo code2.

2.3.4 Extension à l'opérateur de reconstruction

Choisir des attributs diérents permet de dénir d'autres opérations morphologiques de base telles que la reconstruction morphologique ou l'ouverture par reconstruction. Avant de présenter les modi- cations apportées à l'algorithme précédent, nous dénissions ces deux opérateurs.

Soit deux signaux mono dimensionnels g et h ayant le même support D ⊆ Z et le même ensemble de valeurs V = Z ou R avec ∀x ∈ D, g(x) ≥ h(x). La reconstruction morphologique du signal h sous g revient en pratique à dilater le signal h, prendre l'intersection avec le signal g et à itérer ce processus jusqu'à stabilisation. On note cet opérateur Rech :

Rech(g) = (δh ∧ g)(∞). (2.14)

La notation (.)(∞) signie que l'on répète le processus jusqu'à idempotence, le symbole ∧ correspond

à l'inmum et le symbole δ à une dilatation de taille unitaire.

Avec des cordes maximales, l'équation2.14revient à conserver l'ensemble des cordes de g tels que, pour une corde c, il existe au moins un élément de h dans le support de cette corde, dont le niveau de gris est supérieur ou égale à l'altitude de c.

L'ouverture par reconstruction ajoute un critère de taille à la contrainte précédente. La corde c doit avoir une longueur supérieure à L pour survivre et il doit exister au moins un élément de h dans le support de cette corde, dont le niveau de gris est supérieur ou égale à l'altitude de c.

Ces deux opérateurs sont notés Rech et γh,Lrec et la gure2.4 illustre leurs résultats sur des signaux

g et h quelconques.

2.4 Extension aux signaux 2-D