Bonjour,

Etant donné que la documentation sur les opérateurs morphologiques d'érosion et de dilatation sur des images en couleurs est assez difficile à trouver, je vais vous expliquer le principe.

La diffusion et l'érosion s'effectue suivant un noyau, en général, quand on voit ces opérateurs, on travaille avec des images binaires :

Par exemple, l'érosion :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
 
Avant          Apres
0 0 0 0 0      0 0 0 0 0
0 1 1 0 0      0 0 0 0 0
0 1 1 1 0      0 0 1 0 0
0 0 1 0 0      0 0 0 0 0
La dilatation :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
 
Avant          Apres
0 0 0 0 0      0 1 1 0 0
0 1 1 0 0      1 1 1 1 0
0 1 1 1 0      1 1 1 1 1
0 0 1 0 0      0 1 1 1 0
A chaque pixel, on pose un "noyau" dessus composé de 0 et de 1. Typiquement, le noyau classique est :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
 
0 1 0
1 1 1
0 1 0
Pour la dilatation,
On centre le noyau de tel façon à ce qu'il recouvre le pixel de destination (x,y) à changer, si l'un des pixels de l'image d'origine dispose d'un 1 là où il y a un 1 dans le noyau, alors l'image de destination (x,y) vaudra 1.

On fait l'inverse pour l'érosion.

On suppose que l'on dispose d'un objet Kernel qui dispose des opérations suivantes :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
 
getWidth() : donne la largeur
getHeight() : donne la hauteur
getLeftPadding() : donne le nombre de pixel à gauche à partir du centre (ici, c'est 1)
getRightPadding() : idem mais pour la droite
getBottomPadding() : idem mais pour en bas
getTopPadding() : idem mais pour en haut
getOriginX() : donne le centre horizontal du noyau en commencant à 0 (donc ici 1)
getOriginY() : donne le centre vertical
L'algorithme naif pour une image binaire va s'écrire comme ça :
Pour la dilatation par exemple :
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
 
fonction dilatation(Image in, Kernel k)
 /*on ne traite pas les bords*/
 Image out de taille : largeur -(k.getRightPadding() + k.getLeftPadding()
                             hauteur - (k.getTopPadding() + k.getBottomPadding())
 
 Pour chaque canaux de out et de in faire :
   Pour j = k.getTopPadding() à  hauteur - k.getBottomPadding()-1
    Pour  i = leftpadding à largeur - k.getRightPadding()-1  
    {
     float value = 0; 
     Pour kj = 0 à k.getHeight()-1 
       Pour ki =0 à k.getWidth()-1
       {        
           out.setPixel(  i - leftpadding, j - toppadding, 0);
 
           /*si l'un des pixels du noyau à 1 correspond à un pixel à 1 de l'image, la futur valeur sera égale à 1*/
           Si kernel(ki, kj) * image.getPixel(i + ki - kernel.getXOrigin(), j+ kj - kernel.getYOrigin()) == 1
           Alors value = 1;
            out.setPixel(i - leftpadding, j - toppadding,
                             value);
        }
 
      }

Cette méthode fonctionne bien pour des images binaires, mais pas pour des images qui dispose de plusieurs niveaux de gris, la méthode général est la suivante :

Pour l'érosion :
1. Pour chaque pixel de la source, placer le noyau sur l'image centré sur le pixel (i,j)
2. Pour chaque pixel du noyau (positionné), soustraire la valeur du noyau * le pixel décalé au pixel central (i,j)
3. Déterminer le minimum de toutes ces valeurs
4. Ajouter le minimum au pixel central (i,j)

Algorithme avec une notation C++ :
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
 
  out.resize(largeur - (rightpadding + leftpadding), hauteur - (toppadding+bottompadding));
 
    for(int j = toppadding; j< hauteur - bottompadding; j++)
    {
      for(int i = leftpadding; i< largeur - rightpadding; i++)
      {
        for(int kj = 0; kj< khauteur; kj++)
          for(int ki = 0; ki< klargeur; ki++)
          {
            buffer[klargeur * kj + ki] =  image.getPixel(i,j, canal) - kernel(ki, kj) * image.getPixel(i + ki - kernel.getXOrigin(), j+ kj - kernel.getYOrigin(), canal);
          }
 
            float mini = getMinimum(buffer);
 
            out.setPixel(i - leftpadding, j - toppadding,
                             canal, mini + image.getPixel(i,j, canal));
 
      }
Pour la dilatation :
1. Pour chaque pixel de la source (i,j), placer le noyau sur l'image centré sur le pixel
2. Pour chaque pixel du noyau (positionné), soustraire la valeur du pixel central (i,j) au produit noyau * le pixel décalé
3. Déterminer le maximum de toutes ces valeurs
4. Ajouter le maximum au pixel central (i,j)

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
 out.resize(largeur - (rightpadding + leftpadding), hauteur - (toppadding+bottompadding));
 
    for(int j = toppadding; j< hauteur - bottompadding; j++)
    {
      for(int i = leftpadding; i< largeur - rightpadding; i++)
      {
        for(int kj = 0; kj< khauteur; kj++)
          for(int ki = 0; ki< klargeur; ki++)
          {
            buffer[klargeur * kj + ki] =  -image.getPixel(i,j, canal) + kernel(ki, kj) * image.getPixel(i + ki - kernel.getXOrigin(), j+ kj - kernel.getYOrigin(), canal);
          }
 
            float maxi = getMaximum(buffer);
 
            out.setPixel(i - leftpadding, j - toppadding,
                             canal, maxi + image.getPixel(i,j, canal));
 
      }

Classiquement, on utilise le noyau :
Voici un exemple :

Source :


Erosion :


Dilatation :