web-dev-qa-db-fra.com

Comment gaussian brouiller une image sans utiliser de fonctions gaussiennes intégrées?

Je souhaite brouiller mon image à l'aide de la formule de flou gaussien native. J'ai lu ceci , mais je ne sais pas comment le mettre en œuvre.

Comment utiliser la formule pour décider des poids?

Je ne veux utiliser aucune fonction intégrée comme celle de MATLAB

52
Moeb

Écrire un flou gaussien naïf est en fait assez facile. Cela se fait exactement de la même manière que tout autre filtre à convolution. La seule différence entre une boîte et un filtre gaussien est la matrice que vous utilisez.

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 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 devez ensuite multiplier le pixel 0 par la partie supérieure gauche du filtre de flou 3x3. Pixel 1 en haut au centre, pixel 2, pixel 3 en haut à droite, pixel 10 en milieu à gauche, etc.

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

Les cas extrêmes deviennent un peu plus complexes. Quelles valeurs utilisez-vous pour les valeurs de la bordure de la texture? Une façon peut être d'enrouler de l'autre côté. Cela semble bon pour une image qui est ensuite carrelée. Une autre façon consiste à pousser le pixel dans les lieux environnants.

Donc, en haut à gauche, vous pouvez placer les échantillons comme suit:

 0  0  1
 0  0  1
10 10 11

J'espère que vous pourrez voir comment cela peut facilement être étendu aux noyaux de filtres volumineux (à savoir 5x5 ou 9x9, etc.). 

La différence entre un filtre gaussien et un filtre de type boîte réside dans les nombres entrés 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 (c'est-à-dire que ce n'est pas un gaussien, mais probablement pas très loin)

0.1 0.8 0.1

la première colonne serait la même mais multipliée dans 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 0.8 dans la ligne ci-dessus (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’ajout de tous les éléments ci-dessus doit être égal à 1. La différence entre le filtre ci-dessus et le filtre de boîte d'origine réside dans le fait que le pixel de fin écrit aurait une pondération beaucoup plus lourde par rapport au pixel central déjà). Le flou est dû au fait que les pixels environnants sont flous dans ce pixel, mais pas autant. En utilisant ce type de filtre, vous obtenez un flou qui ne détruit pas autant les informations haute fréquence (changement rapide de couleur d'un pixel à l'autre).

Ces types de filtres peuvent faire beaucoup de choses intéressantes. Vous pouvez effectuer une détection de contour à l'aide de ce type de filtre en soustrayant les pixels environnants du pixel actuel. Cela ne laissera que les très grands changements de couleur (hautes fréquences).

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 dans leur par le premier élément pour former une colonne, puis multipliez chaque par le deuxième élément pour former la deuxième colonne, vous obtiendrez un filtre. 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 des positions arbitraires, vous pouvez voir que la position 0, 0 est simple 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 assez bonne explication.

125
Goz

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

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 * sqrtTwoPiTimesRadiusRecip);
    r++;
}

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

J'espère que cela peut aider.

12
Cecil Has a Name

Pour utiliser le noyau de filtre décrit dans l'article de Wikipedia, vous devez implémenter (discret) convolution . L'idée est que vous avez une petite matrice de valeurs (le noyau), vous déplacez ce noyau de pixel en pixel dans l'image (c'est-à-dire que le centre de la matrice est sur le pixel), multipliez les éléments de la matrice par l'image superposée éléments, additionnez toutes les valeurs du résultat et remplacez l'ancienne valeur de 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 également un peu les choses.

9
Markus Johnsson

Je ne sais pas si vous voulez restreindre cette à certaines technologies, mais sinon SVG (ScalableVectorGraphics) a une implémentation de Gaussian Blur. Je crois que cela s'applique à toutes les primitives, y compris les pixels. SVG a l'avantage d'être un standard ouvert et largement implémenté.

3

Bien, le noyau gaussien est un noyau séparable.
Par conséquent, tout ce dont vous avez besoin est d’une fonction prenant en charge la convolution 2D séparable telle que - ImageConvolutionSeparableKernel() .

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

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

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

0
Royi