Le filtre Mean Shift est un lissage de l'image par moyennage qui préserve les contours.
Il possède deux paramètres : un voisinage de pixel et une
valeur d'intensité Imin correspondant au gradient minimum d'un
contour.
Le principe est d'appliquer l'algorithme suivant pour
chaque pixel l'un après l'autre :
Le résultat avec une fenêtre carrée 9x9 pixels et une intensité minimale de gradient de contour de 20 par canal
est présenté ci-dessous. La première ligne donne les images
bruités en entrée, la seconde le résultat, la troisième et la
quatrième montrent un détail avec des couleurs renforçant les contrastes.
Gaussien (sigma=20) | Uniforme (+/-20) | Poivre et sel (3%) | Périodique (jpg) |
Temps d'exécution sur un AMD 1800+ pour l'image 384x288 :
0.95 secondes pour les bruits gaussiens et
uniformes, 0.49 secondes pour le "poivre et sel" et
le périodique (convergence plus rapide).
Première remarque : ce filtre préserve bien les contours de
gradients supérieur à Imin.
Seconde remarque : les résultats sur les bruits gaussiens,
uniformes et périodiques sont excellents au vu du fort
bruit ajouté.
Troisième remarque : le filtre n'est pas du tout adapté au bruit "poivre et
sel". En effet, ce type de bruit est considéré
comme un contour, et donc non filtré.
Le code source en C du coeur de l'algorithme est donné
ci-dessous. Les variables non déclarées et la
gestion de la structure image sont sans ambiguité et
doivent être complétées pour avoir un programme compilable.
// Loop on pixels
for(int v=0; v<sizeV; ++v) {
for(int u=0; u<sizeU; ++u) {
// Compute mean box
int uMin = utMax(u-halfWindowSize, 0);
int uMax = utMin(u+halfWindowSize, sizeU-1);
int vMin = utMax(v-halfWindowSize, 0);
int vMax = utMin(v+halfWindowSize, sizeV-1);
// Initialize pixel filtering
int startOffset = u + v*sizeU;
int currentR = bufferR[startOffset];
int currentG = bufferG[startOffset];
int currentB = bufferB[startOffset];
// Filter the pixel
int iterationQty = 10; // Convergence with integers is not ensured
while(iterationQty--) {
// Compute mean inside noise sphere
int qty = 0;
int sumR = 0;
int sumG = 0;
int sumB = 0;
meanIterationQty += 1;
for(int v1=vMin; v1<=vMax; ++v1) {
int offset = uMin + v1*sizeU;
for(int i=1+uMax-uMin; i; --i, ++offset) {
if(utAbs(currentR-(int)bufferR[offset])+
utAbs(currentG-(int)bufferG[offset])+
utAbs(currentB-(int)bufferB[offset])<noiseIntensity) {
sumR += bufferR[offset];
sumG += bufferG[offset];
sumB += bufferB[offset];
qty += 1;
}
}
}
// Update the color
int oldR = currentR;
int oldG = currentG;
int oldB = currentB;
currentR = sumR/qty;
currentG = sumG/qty;
currentB = sumB/qty;
// Check the convergence
if(oldR==currentR && oldG==currentG && oldB==currentB) {
break;
}
} // End of iterations on the pixel
// Store the result for this pixel
resultBufR[startOffset] = (unsigned char)currentR;
resultBufG[startOffset] = (unsigned char)currentG;
resultBufB[startOffset] = (unsigned char)currentB;
} // End of loop on columns
} // End of loop on rows
|