• Aucun résultat trouvé

4.4 Primitives du calcul comme skeletons algorithmiques

4.4.5 Concept des "superpixels"

Dans certains de nos algorithmes, nous allons travailler avec un groupe de pixels et avec les pixels voisins de ce groupe, plutôt que de travailler avec un seul pixel et son voisinage. Il serait donc convenable de décrire à cette place la manière dont on va travailler avec un tel groupe et de donner les bases formelles à ce travail.

L’idée de travailler avec des groupes de pixels et des pixels voisins de ce groupe est propre à toutes les implémentations sur les architectures parallèles où on procède à la division de l’image et on effectue le traitement de ces parties par la distribution sur différents processeurs. Cette idée est explorée par Jin Yang qui utilise, q.v. page 57 de sa thèseYan97 doctorale, les bords partagés (également appelés les

halos) qui sont ajoutés aux arrays parallèles ou aux graphes. Notons que ces bords partagés ont dans la morphologie mathématique un sens plus large que celui du voisinage proche sur une grille donnée car ils doivent refléter le travail avec des éléments structurants dans leur forme générale et souvent d’une taille importante, pas nécessairement celle qui définit le voisinage de taille 1.

D’un point de vue formel, c’est le travail sur le voisinage qui nous empêche d’exprimer un tel groupe de pixels par un array découpé régulièrement sur les vecteurs paquetés ou sur les macro blocs car dans la perception des arrays comme nous la décrivons par le formalisme fonctionnel, les voisins d’une donnée qui est du type α sont les données du même type. Par exemple, le voisinage d’un macro bloc concret est constitué d’un ou plusieurs macro blocs voisins.

Ceci ne correspond pas à la philosophie de travail que nous voulons employer pour les groupes de pixels dont le voisinage (proche ou dans le sens large) est constitué également des pixels et non des groupes de pixels. Vu que l’utilisation de ces groupes de pixels dans les fonctions et la façon de travailler avec leurs pixels voisins sont semblables à celles que nous employons lors du travail à l’échelle des pixels non-groupés, il nous semble approprié de désigner ces groupes comme des superpixels, c’est-à-dire des pixels qui ont une notion élargie d’une entité de données qui peut contenir plus d’un seul pixel.

Le concept des superpixels que nous introduisons est pratique pour deux raisons :

• il est cohérent avec l’idée du sens du parcours implémentée par la fonction génératrice des index

et avec l’idée d’une fonction d’extraction des pixels (ici de tout un groupe) à partir de l’image en utilisant un seul index, comme nous l’avons présenté dans la section 4.4.4, page 70.

• les superpixels conservent le caractère des pixels. Ainsi, nous n’avons pas besoin de percevoir

les groupes de pixels très différemment (e.g. comme des macro blocs), de définir un autre type qui serait spécifique aux groupes de pixels et qui nous aurait conduit à un travail très différent de

celui pour les pixels. Un travail très différent surtout parce que nous aurions besoin d’introduire des fonctions plus élaborées d’extraction des pixels voisins (ordinaires) à partir d’un groupe qui serait de ce nouveau type. C’est pourquoi nous préférons introduire des superpixels pour lesquels nous pouvons utiliser, lors de la construction de nos algorithmes, la même charpente que celle utilisée pour les pixels ordinaires ; même si, bien sûr, nous aurons besoin de modifier certains points spécifiques.

Remarquons que l’idée des superpixels n’est pas restreinte uniquement aux arrays définis sur les grilles régulières mais peut être transposée au traitement général des graphes. Pour ces derniers, un superpixel serait défini comme un groupe des sommets du graphe qui peuvent être traités en même temps.

4.4.5.1 Travail avec des superpixels

La position d’un superpixel dans l’array est définie par un index que nous allons appeler l’index d’ancrage. Sa valeur doit être incluse dans les bornes minimales et maximales de cet array. De plus, l’élément de l’array qui est désigné par l’index d’ancrage d’un superpixel doit appartenir au groupe des éléments constituant ce superpixel.

La fonction d’échantillonnage des superpixels décrit la manière dont les éléments d’un superpixel sont extraits à partir de l’image. Étant donné un array ar et l’index d’ancrage i d’un superpixel, les élé- ments de ce dernier peuvent être obtenus par l’application d’une fonction concrète sampFncSP d’ex- traction de superpixels :

sampFncSPar i

L’ensemble de tous les superpixels d’un array doit obligatoirement composer l’array entier. Le nombre précis des éléments composant un superpixel peut être variable d’un superpixel à l’autre. Deux cas spé- ciaux peuvent être distingués, celui d’un superpixel composé d’un seul élément et celui d’un superpixel composé de tous les éléments d’un array. La définition exacte des superpixels n’est pas restreinte par d’autres conditions, on n’exige pas une forme géométrique particulière ni que cette forme soit convexe. 4.4.5.2 Sens du parcours, passage d’un array à un flux de superpixels et vice versa

Cependant, pour le travail pratique, il est préférable de définir des superpixels d’une manière unifiée comme des ensembles d’éléments définissant le pavage de l’array aux zones rectangulaires de mêmes dimensions dans lesquelles nous choisissons par convention les index les plus petits comme les index d’ancrage. La figure 4.8 illustre cette situation.

i1 superpixel index d’ancrage i2 i3 i4 i5 i6 i7 i8 i9 i10 array fs t snd

FIG. 4.8 : Décomposition d’un array aux superpixels rectangulaires de mêmes dimensions

Dans ce but, nous définissons la fonction streamAr2DSP du sens du parcours pour les superpixels qui nous retourne un stream des index d’ancrage des superpixels et dont le fonctionnement est très semblable à celui de la fonction streamAr2D, page 72 :

streamAr2DSP :: [ Char] → I → I → Ar ( I , I ) α → [ ( I , I ) ] streamAr2DSP how m n ar

| how == "FWFst" = [ ( flo +m∗ i , slo +n ∗ j ) | j ←[0 .. smax], i←[0 .. fmax] ] | how == "FWSnd" = [ ( flo +m∗ i , slo +n ∗ j ) | i←[0 .. fmax], j ←[0 .. smax] ] | how == "BWFst" = [ ( fhi −m∗i, shi−n∗j ) | j ←[0 .. smax], i←[0 .. fmax] ] | how == "BWSnd" = [ ( fhi −m∗i, shi−n∗j ) | i←[0 .. fmax], j ←[0 .. smax] ] where

( ( flo , slo ) , ( fhi , shi ) ) = bounds $ ar ;

fmax = div (rangeSize(flo,fhi)−1) m smax = div (rangeSize(slo,shi)−1) n

Le premier argument est la clé avec laquelle nous désignons la fonctionnalité exacte de cette fonction. Le deuxième / troisième argument de cette fonction désigne les dimensions d’un superpixel dans la première/deuxième coordonnée. Le quatrième paramètre est l’array d’entrée que nous voulons parcourir. Nous définissons le type StreamizeSP qui va désigner des fonctions pour le passage d’un array à un stream des index d’ancrage des superpixels. La signature de ce type est, en effet, identique aux fonctions désignées par le type Streamize. Tandis que les fonction étant du type Streamize travaillent avec les index ordinaires, le type StreamizeSP porte, de plus, une information syntactique du type de retour [(I, I)], car nous le définissons comme la liste des index d’ancrage des superpixels :

typeStreamizeSP α = Ar ( I , I ) α → [ ( I , I ) ]

Ainsi, la signature de type de la fonction streamAr2DSP que nous venons de présenter :

streamAr2DSP :: [ Char] → I → I → Ar ( I , I ) α → [ ( I , I ) ]

peut être récrite comme :

streamAr2DSP :: [ Char] → I → I → StreamizeSPα

Ayant obtenu un stream des index d’ancrage par la fonction streamAr2DSP, nous avons encore besoin des fonctions qui extrairaient à partir de ce stream les éléments de bases de l’image qui composent les superpixels correspondants. Ces fonction seront du type SampFncSP :

SampFncSPα :: ( Ix β) ⇒ Ar β α → β → [ α ]

et elle diffèrent des versions classiques (non-superpixeliques) des fonctions d’échantillonnage dans le type de retours qui est dans ce cas une liste des éléments.

Ce procédé est assuré, dans la version générale, par la fonction sampSPGen. Elle va nous retourner, pour un index d’ancrage donné, la liste de tous les éléments de l’array appartenant au superpixel qui est désigné par cet index.

sampSPGen :: I → I → Ar ( I , I ) α → ( I , I ) → [ α ]

sampSPGen m n ar ( ixf , ixs ) = map (ar ! ) ( range(( ixf , ixs ) , ( ixf +m−1, ixs+n−1)) )

Notons que la signature de type de cette fonction est compatible avec celle qui utilise le type SampFncSP :

sampSPGen :: I → I → SampFncSPα

et nous pouvons l’utiliser, après l’application partielle de ses deux premiers paramètres, comme la fonc- tion d’entrée dans les algorithmes exigeants les fonction du type SampFncSP.

Nous utiliserons encore une autre catégorie de fonctions qui est connexe aux superpixels et qui sera utilisée lors de la recomposition de l’array de sortie. Il s’agit des fonctions qui, à partir d’un tuple com- posé de l’index d’ancrage et de la liste des éléments résultants d’un superpixel, créent la liste des tuples (index, élément). Cette dernière liste sera utilisée dans les algorithmes comme un moyen pour pouvoir recomposer le stream d’entrée de la fonction standard array de Haskell afin de créer l’array de sortie. Ces fonctions seront du type ZipSP :

typeZipSP α = ( ( I , I ) , [ α ] ) → [ ( ( I , I ) , α) ]

La fonction qui sera de cette catégorie, qui complète le passage au flux de superpixels, comme défini par la fonction StreamAr2DSP et leur extraction, comme défini par la fonction sampSPGen, c’est la fonction zipSPGen

zipSPGen :: I → I → ( ( I , I ) , [ α ] ) → [ ( ( I , I ) , α) ]

zipSPGen m n ( ( ixf , ixs ) , ss) = zip ( range(( ixf , ixs ) , ( ixf +m−1, ixs+n−1)) ) ss

Elle prend deux paramètres supplémentaires, m et n définissant les dimensions des superpixels dans l’axe de la première et la deuxième coordonnée, respectivement. En effet, la signature de type de cette fonction est compatible avec la suivante :

zipSPGen :: I → I → ZipSP

qui utilise le type ZipSP et démontre plus explicitement sa désignation.

Regardons maintenant comment nous allons utiliser les fonctions de travail avec les superpixels sur un exemple trivial. Dans cet exemple, en dehors du passage d’un array ar à un stream des superpixels de dimensions m × n et de la recomposition d’un nouvel array à partir de ces derniers, nous n’employons aucune fonction de traitement des superpixels.

array (bounds ar ) ( ( foldl1 (++)) ◦ (map (zipSPGen m n) ) ◦ ( zip ixs ) ◦ (map (sampSPGen m n ar ) ) $ ixs ) where ixs = streamAr2DSP "FWFst" mn ar

Tout d’abord, nous choisissons le parcours de l’image par la fonction streamAr2DSP et nous obtenons un stream des index d’ancrage. Sur ce stream, nous appliquons la fonction d’extraction des superpixels sampSPGen pour obtenir le stream des superpixels qui est du type liste des listes, [[α]]. Lors de la recomposition, nous ajoutons à chacun des superpixels son index d’ancrage par la fonction zip pour obtenir un stream des tuples (index d’ancrage, superpixel). Sur tous les éléments de ce stream, nous appliquons, par la fonction map, la fonction zipSP retournant une liste des tuples (index, élément). Nous connectons ces listes distinctes par l’application de la fonction foldl1 de réduction d’un stream par la fonction ++ de jonction des listes. La figure 4.9 illustre graphiquement le fonctionnement de ce procédé.

ar (Array

d’entrée) de sortieArray

bounds

Création de l’array de sortie

streamAr2DSP extrSPGen zip zipSPGen ++

stream des indexes d’ancrage stream des superpixels

FIG. 4.9 : Exemple de travail avec les streams des superpixels