59 votes

Comment rendre une image floue à la gaussienne sans utiliser les fonctions gaussiennes intégrées ?

Je souhaite rendre mon image floue en utilisant la formule native du flou gaussien. Je lis l'article de Wikipedia mais je ne suis pas sûr de savoir comment le mettre en œuvre.

Comment utiliser la formule pour déterminer les poids ?

Je ne veux pas utiliser de fonctions intégrées comme celles de MATLAB.

2 votes

En fait, vous devez implémenter un opérateur de convolution équivalent à la fonction conv2() dans MATLAB. Cependant, étant donné que les gaussiennes 2D peuvent être séparées en deux gaussiennes 1D, tout ce dont vous avez besoin est une implémentation de la fonction de convolution sur 1D, couplée à la matrice de noyau correcte.

137voto

Goz Points 35007

L'écriture d'un flou gaussien naïf est en fait assez facile. Il est réalisé exactement de la même manière que n'importe quel autre filtre de convolution. La seule différence entre une boîte et un filtre gaussien est la matrice utilisée.

Imaginez que vous ayez une image définie comme suit :

 0  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

Une matrice de filtrage en boîte 3x3 est définie comme suit :

0.111 0.111 0.111
0.111 0.111 0.111
0.111 0.111 0.111

Pour appliquer le flou gaussien, procédez comme suit :

Pour le pixel 11, vous devez charger les pixels 0, 1, 2, 10, 11, 12, 20, 21, 22.

vous multipliez alors le pixel 0 par la partie supérieure gauche du filtre flou 3x3. Le pixel 1 par la partie supérieure du milieu, le pixel 2, le pixel 3 par la partie supérieure droite, le pixel 10 par la partie centrale gauche et ainsi de suite.

Additionnez-les ensuite et inscrivez le résultat au pixel 11. Comme vous pouvez le constater, le pixel 11 est maintenant la moyenne de lui-même et des pixels environnants.

Les cas limites deviennent un peu plus complexes. Quelles valeurs utilisez-vous pour les valeurs du bord de la texture ? L'une des solutions consiste à faire le tour de l'autre côté. Cela convient bien à une image qui sera ensuite mise en carreaux. Une autre solution consiste à pousser le pixel dans les endroits environnants.

Ainsi, pour la partie supérieure gauche, vous pouvez placer les échantillons comme suit :

 0  0  1
 0  0  1
10 10 11

J'espère que vous pouvez voir comment ceci peut facilement être étendu à de grands noyaux de filtres (c'est-à-dire 5x5 ou 9x9, etc.).

La différence entre un filtre gaussien et un filtre en boîte réside dans les nombres qui figurent dans la matrice. Un filtre gaussien utilise une distribution gaussienne sur une ligne et une colonne.

Par exemple, pour un filtre défini arbitrairement comme (ce n'est pas une gaussienne, mais ce n'est probablement pas loin)

0.1 0.8 0.1

la première colonne serait la même, mais multipliée par le premier élément de la ligne ci-dessus.

0.01 0.8 0.1
0.08 
0.01 

La deuxième colonne serait la même, mais les valeurs seraient multipliées par le 0,8 de la ligne précédente (et ainsi de suite).

0.01 0.08 0.01
0.08 0.64 0.08
0.01 0.08 0.01

Le résultat de l'addition de tous les éléments ci-dessus doit être égal à 1. La différence entre le filtre ci-dessus et le filtre en boîte d'origine est que le pixel final écrit aura une pondération beaucoup plus forte vers le pixel central (c'est-à-dire celui qui se trouve déjà dans cette position). Le flou est dû au fait que les pixels environnants se confondent avec ce pixel, mais pas autant. En utilisant ce type de filtre, vous obtenez un flou qui ne détruit pas autant d'informations à haute fréquence (c'est-à-dire le changement rapide de couleur d'un pixel à l'autre).

Ce type de filtre permet de réaliser de nombreuses choses intéressantes. Vous pouvez effectuer une détection des contours à l'aide de ce type de filtre en soustrayant les pixels environnants du pixel actuel. Seuls les changements de couleur les plus importants (hautes fréquences) seront conservés.

Edit : Un noyau de filtre 5x5 est défini exactement comme ci-dessus.

Par exemple, si votre ligne est 0.1 0.2 0.4 0.2 0.1, si vous multipliez chaque valeur par le premier élément pour former une colonne, puis multipliez chaque valeur par le deuxième élément pour former la deuxième colonne, et ainsi de suite, vous obtiendrez un filtre composé de

0.01 0.02 0.04 0.02 0.01
0.02 0.04 0.08 0.04 0.02
0.04 0.08 0.16 0.08 0.04
0.02 0.04 0.08 0.04 0.02
0.01 0.02 0.04 0.02 0.01

En prenant quelques positions arbitraires, vous pouvez voir que la position 0, 0 est simplement 0,1 * 0,1. La position 0, 2 est 0,1 * 0,4, la position 2, 2 est 0,4 * 0,4 et la position 1, 2 est 0,2 * 0,4.

J'espère que cela vous donne une explication suffisante.

0 votes

@Goz Supposons que je veuille utiliser un noyau de filtre 5x5, comment calculer les poids qui devraient être utilisés dans le filtre ?

0 votes

Il aurait pu mentionner le mot-clé : réseaux neuronaux respectivement réseaux neuronaux artificiels .

0 votes

@bitterblue : Pouvez-vous expliquer pourquoi les réseaux neuronaux ont quelque chose à voir avec le filtrage de l'espace image ?

11voto

Cecil Has a Name Points 3385

Voici le pseudo-code du code que j'ai utilisé en C# pour calculer le noyau. Je n'ose pas dire que je traite les conditions finales correctement, cependant :

double[] kernel = new double[radius * 2 + 1];
double twoRadiusSquaredRecip = 1.0 / (2.0 * radius * radius);
double sqrtTwoPiTimesRadiusRecip = 1.0 / (sqrt(2.0 * Math.PI) * radius);
double radiusModifier = 1.0;

int r = -radius;
for (int i = 0; i < kernel.Length; i++)
{
    double x = r * radiusModifier;
    x *= x;
    kernel[i] = sqrtTwoPiTimesRadiusRecip * Exp(-x * twoRadiusSquaredRecip);
    r++;
}

double div = Sum(kernel);
for (int i = 0; i < kernel.Length; i++)
{
    kernel[i] /= div;
}

J'espère que cela vous aidera.

3 votes

Je crois que cette ligne : sqrtTwoPiTimesRadiusRecip * Exp(-x * sqrtTwoPiTimesRadiusRecip); doit être : sqrtTwoPiTimesRadiusRecip * Exp(-x * twoRadiusSquaredRecip);

4 votes

La multiplication par sqrtTwoPiTimesRadiusRecip n'est pas du tout nécessaire car vous normalisez le noyau de toute façon.

0 votes

@Simson, je ne suis pas sûr de ce que votre édition a fait, à part m'enlever le mérite de l'avoir formellement suggéré après avoir rencontré le même problème avec le code de mon collègue.

9voto

Markus Johnsson Points 2157

Pour utiliser le noyau filtrant dont il est question dans l'article de Wikipedia, vous devez implémenter (discrètement) convolution . L'idée est de disposer d'une petite matrice de valeurs (le noyau), de déplacer ce noyau d'un pixel à l'autre de l'image (c'est-à-dire de manière à ce que le centre de la matrice se trouve sur le pixel), de multiplier les éléments de la matrice avec les éléments de l'image qui se chevauchent, d'additionner toutes les valeurs du résultat et de remplacer l'ancienne valeur du pixel par cette somme.

Le flou gaussien peut être séparé en deux convolutions 1D (une verticale et une horizontale) au lieu d'une convolution 2D, ce qui accélère un peu les choses.

3voto

peter.murray.rust Points 13406

Je ne sais pas si vous souhaitez restreindre cette possibilité. à certaines technologies, mais si ce n'est pas le cas SVG (ScalableVectorGraphics) dispose d'une implémentation du flou gaussien. Je crois qu'il s'applique à toutes les primitives, y compris les pixels. SVG a l'avantage d'être une norme ouverte et largement mise en œuvre.

0 votes

Je veux utiliser la définition de base d'un filtre gaussien, et aucune implémentation intégrée. Je veux l'implémenter moi-même.

1voto

Drazick Points 1059

Le noyau gaussien est un noyau séparable.
Par conséquent, tout ce dont vous avez besoin, c'est d'une fonction qui supporte la convolution 2D séparable comme - ImageConvolutionSeparableKernel() .

Une fois que vous l'avez, tout ce dont vous avez besoin est un wrapper pour générer un noyau gaussien 1D et l'envoyer à la fonction, comme cela est fait dans ImageConvolutionGaussianKernel() .

Le code est une implémentation C directe de la convolution d'images 2D accélérée par SIMD (SSE) et Multi Threading (OpenMP).

L'ensemble du projet est donné par - Convolution d'images - GitHub .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X