web-dev-qa-db-fra.com

Mise en œuvre du flou gaussien - Comment calculer la matrice de convolution (noyau)

Ma question est très proche de celle-ci: Comment gaussian brouiller une image sans utiliser de fonctions gaussiennes intégrées?

La réponse à cette question est très bonne, mais elle ne donne pas d'exemple de calcul d'un véritable noyau de filtre gaussien. La réponse donne un noyau arbitraire et montre comment appliquer le filtre à l'aide de ce noyau, mais pas comment calculer un véritable noyau lui-même. J'essaie d'implémenter un flou gaussien en C++ ou Matlab, il me faut donc savoir calculer le noyau à partir de rien.

J'apprécierais que quelqu'un puisse calculer un véritable noyau de filtre gaussien en utilisant n'importe quel petit exemple de matrice d'image.

31
gsingh2011

Vous pouvez créer un noyau gaussien à partir de zéro, comme indiqué dans la documentation MATLAB de fspecial . Veuillez lire la formule de création du noyau gaussien dans la partie algorithmes de cette page et suivre le code ci-dessous. Le code consiste à créer une matrice m-par-n avec sigma = 1.

m = 5; n = 5;
sigma = 1;
[h1, h2] = meshgrid(-(m-1)/2:(m-1)/2, -(n-1)/2:(n-1)/2);
hg = exp(- (h1.^2+h2.^2) / (2*sigma^2));
h = hg ./ sum(hg(:));

h =

    0.0030    0.0133    0.0219    0.0133    0.0030
    0.0133    0.0596    0.0983    0.0596    0.0133
    0.0219    0.0983    0.1621    0.0983    0.0219
    0.0133    0.0596    0.0983    0.0596    0.0133
    0.0030    0.0133    0.0219    0.0133    0.0030

Observez que ceci peut être fait par la fspecial intégrée comme suit:

fspecial('gaussian', [m n], sigma)
ans =

    0.0030    0.0133    0.0219    0.0133    0.0030
    0.0133    0.0596    0.0983    0.0596    0.0133
    0.0219    0.0983    0.1621    0.0983    0.0219
    0.0133    0.0596    0.0983    0.0596    0.0133
    0.0030    0.0133    0.0219    0.0133    0.0030

Je pense qu’il est simple d’appliquer cela dans la langue de votre choix.

EDIT: Permettez-moi également d'ajouter les valeurs de h1 et h2 pour le cas donné, car vous pouvez ne pas connaître meshgrid si vous codez en C++.

h1 =

    -2    -1     0     1     2
    -2    -1     0     1     2
    -2    -1     0     1     2
    -2    -1     0     1     2
    -2    -1     0     1     2

h2 =

    -2    -2    -2    -2    -2
    -1    -1    -1    -1    -1
     0     0     0     0     0
     1     1     1     1     1
     2     2     2     2     2
35
petrichor

C'est aussi simple que ça en a l'air:

double sigma = 1;
int W = 5;
double kernel[W][W];
double mean = W/2;
double sum = 0.0; // For accumulating the kernel values
for (int x = 0; x < W; ++x) 
    for (int y = 0; y < W; ++y) {
        kernel[x][y] = exp( -0.5 * (pow((x-mean)/sigma, 2.0) + pow((y-mean)/sigma,2.0)) )
                         / (2 * M_PI * sigma * sigma);

        // Accumulate the kernel values
        sum += kernel[x][y];
    }

// Normalize the kernel
for (int x = 0; x < W; ++x) 
    for (int y = 0; y < W; ++y)
        kernel[x][y] /= sum;
25
thiton

Pour implémenter le flou gaussian , prenez simplement la fonction gaussian et calculez une valeur pour chacun des éléments de votre noyau.

Habituellement, vous souhaitez attribuer le poids maximal à l'élément central de votre noyau et des valeurs proches de zéro pour les éléments situés aux limites du noyau. Cela implique que le noyau doit avoir une hauteur impaire (largeur resp.) Pour qu’il existe réellement un élément central.

Pour calculer les éléments du noyau réels, vous pouvez redimensionner la cloche gaussienne en fonction de la grille du noyau (choisissez un code arbitraire, par exemple sigma = 1 et une plage arbitraire, par exemple -2*sigma ... 2*sigma) et normalisez-le, par ex. la somme des éléments est égale à un . Pour cela, si vous souhaitez prendre en charge des tailles de noyau arbitraires, vous pouvez adapter le sigma à la taille de noyau requise.

Voici un exemple C++:

#include <cmath>
#include <vector>
#include <iostream>
#include <iomanip>

double gaussian( double x, double mu, double sigma ) {
    const double a = ( x - mu ) / sigma;
    return std::exp( -0.5 * a * a );
}

typedef std::vector<double> kernel_row;
typedef std::vector<kernel_row> kernel_type;

kernel_type produce2dGaussianKernel (int kernelRadius) {
  double sigma = kernelRadius/2.;
  kernel_type kernel2d(2*kernelRadius+1, kernel_row(2*kernelRadius+1));
  double sum = 0;
  // compute values
  for (int row = 0; row < kernel2d.size(); row++)
    for (int col = 0; col < kernel2d[row].size(); col++) {
      double x = gaussian(row, kernelRadius, sigma)
               * gaussian(col, kernelRadius, sigma);
      kernel2d[row][col] = x;
      sum += x;
    }
  // normalize
  for (int row = 0; row < kernel2d.size(); row++)
    for (int col = 0; col < kernel2d[row].size(); col++)
      kernel2d[row][col] /= sum;
  return kernel2d;
}

int main() {
  kernel_type kernel2d = produce2dGaussianKernel(3);
  std::cout << std::setprecision(5) << std::fixed;
  for (int row = 0; row < kernel2d.size(); row++) {
    for (int col = 0; col < kernel2d[row].size(); col++)
      std::cout << kernel2d[row][col] << ' ';
    std::cout << '\n';
  }
}

La sortie est:

$ g++ test.cc && ./a.out
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 
0.00408 0.01238 0.02412 0.03012 0.02412 0.01238 0.00408 
0.00794 0.02412 0.04698 0.05867 0.04698 0.02412 0.00794 
0.00992 0.03012 0.05867 0.07327 0.05867 0.03012 0.00992 
0.00794 0.02412 0.04698 0.05867 0.04698 0.02412 0.00794 
0.00408 0.01238 0.02412 0.03012 0.02412 0.01238 0.00408 
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 

Pour simplifier, vous n'avez pas besoin d'utiliser un noyau 2d. Plus facile à mettre en œuvre et plus efficace à calculer, il faut utiliser deux noyaux 1d orthogonaux. Ceci est possible en raison de l’associativité de ce type de convolution linéaire (séparabilité linéaire) . Vous pouvez également vouloir voir cette section de l’article de Wikipédia correspondant.


Voici la même chose en Python (dans l'espoir que quelqu'un le trouvera utile):

from math import exp

def gaussian(x, mu, sigma):
  return exp( -(((x-mu)/(sigma))**2)/2.0 )

#kernel_height, kernel_width = 7, 7
kernel_radius = 3 # for an 7x7 filter
sigma = kernel_radius/2. # for [-2*sigma, 2*sigma]

# compute the actual kernel elements
hkernel = [gaussian(x, kernel_radius, sigma) for x in range(2*kernel_radius+1)]
vkernel = [x for x in hkernel]
kernel2d = [[xh*xv for xh in hkernel] for xv in vkernel]

# normalize the kernel elements
kernelsum = sum([sum(row) for row in kernel2d])
kernel2d = [[x/kernelsum for x in row] for row in kernel2d]

for line in kernel2d:
  print ["%.3f" % x for x in line]

produit le noyau:

['0.001', '0.004', '0.008', '0.010', '0.008', '0.004', '0.001']
['0.004', '0.012', '0.024', '0.030', '0.024', '0.012', '0.004']
['0.008', '0.024', '0.047', '0.059', '0.047', '0.024', '0.008']
['0.010', '0.030', '0.059', '0.073', '0.059', '0.030', '0.010']
['0.008', '0.024', '0.047', '0.059', '0.047', '0.024', '0.008']
['0.004', '0.012', '0.024', '0.030', '0.024', '0.012', '0.004']
['0.001', '0.004', '0.008', '0.010', '0.008', '0.004', '0.001']
16
moooeeeep

Flou gaussien en python à l'aide de la bibliothèque d'images PIL. Pour plus d'informations, lisez ceci: http://blog.ivank.net/fastest-gaussian-blur.html

from PIL import Image
import math

# img = Image.open('input.jpg').convert('L')
# r = radiuss
def gauss_blur(img, r):
    imgData = list(img.getdata())

    bluredImg = Image.new(img.mode, img.size)
    bluredImgData = list(bluredImg.getdata())

    rs = int(math.ceil(r * 2.57))

    for i in range(0, img.height):
        for j in range(0, img.width):
            val = 0
            wsum = 0
            for iy in range(i - rs, i + rs + 1):
                for ix in range(j - rs, j + rs + 1):
                    x = min(img.width - 1, max(0, ix))
                    y = min(img.height - 1, max(0, iy))
                    dsq = (ix - j) * (ix - j) + (iy - i) * (iy - i)
                    weight = math.exp(-dsq / (2 * r * r)) / (math.pi * 2 * r * r)
                    val += imgData[y * img.width + x] * weight
                    wsum += weight 
            bluredImgData[i * img.width + j] = round(val / wsum)

    bluredImg.putdata(bluredImgData)
    return bluredImg
0
Vlad
// my_test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <cmath>
#include <vector>
#include <iostream>
#include <iomanip>
#include <string>

//https://stackoverflow.com/questions/8204645/implementing-gaussian-blur-how-to-calculate-convolution-matrix-kernel
//https://docs.opencv.org/2.4/modules/imgproc/doc/filtering.html#getgaussiankernel
//http://dev.theomader.com/gaussian-kernel-calculator/

double gaussian(double x, double mu, double sigma) {
    const double a = (x - mu) / sigma;
    return std::exp(-0.5 * a * a);
}

typedef std::vector<double> kernel_row;
typedef std::vector<kernel_row> kernel_type;

kernel_type produce2dGaussianKernel(int kernelRadius, double sigma) {
    kernel_type kernel2d(2 * kernelRadius + 1, kernel_row(2 * kernelRadius + 1));
    double sum = 0;
    // compute values
    for (int row = 0; row < kernel2d.size(); row++)
        for (int col = 0; col < kernel2d[row].size(); col++) {
            double x = gaussian(row, kernelRadius, sigma)
                * gaussian(col, kernelRadius, sigma);
            kernel2d[row][col] = x;
            sum += x;
        }
    // normalize
    for (int row = 0; row < kernel2d.size(); row++)
        for (int col = 0; col < kernel2d[row].size(); col++)
            kernel2d[row][col] /= sum;
    return kernel2d;
}

char* gMatChar[10] = {
    "          ",
    "         ",
    "        ",
    "       ",
    "      ",
    "     ",
    "    ",
    "   ",
    "  ",
    " "
};

static int countSpace(float aValue)
{
    int count = 0;
    int value = (int)aValue;
    while (value > 9)
    {
        count++;
        value /= 10;
    }
    return count;
}

int main() {
    while (1)
    {
        char str1[80]; // window size
        char str2[80]; // sigma
        char str3[80]; // coefficient
        int space;

        int i, ch;
        printf("\n-----------------------------------------------------------------------------\n");
        printf("Start generate Gaussian matrix\n");
        printf("-----------------------------------------------------------------------------\n");
        // input window size
        printf("\nPlease enter window size (from 3 to 10) It should be odd (ksize/mod 2 = 1 ) and positive: Exit enter q \n");
        for (i = 0; (i < 80) && ((ch = getchar()) != EOF)
            && (ch != '\n'); i++)
        {
            str1[i] = (char)ch;
        }

        // Terminate string with a null character
        str1[i] = '\0';
        if (str1[0] == 'q')
        {
            break;
        }
        int input1 = atoi(str1);
        int window_size = input1 / 2;
        printf("Input window_size was: %d\n", input1);

        // input sigma
        printf("Please enter sigma. Use default press Enter . Exit enter q \n");
        str2[0] = '0';
        for (i = 0; (i < 80) && ((ch = getchar()) != EOF)
            && (ch != '\n'); i++)
        {
            str2[i] = (char)ch;
        }

        // Terminate string with a null character
        str2[i] = '\0';
        if (str2[0] == 'q')
        {
            break;
        }
        float input2 = atof(str2);
        float sigma;
        if (input2 == 0)
        {
            // Open-CV sigma � Gaussian standard deviation. If it is non-positive, it is computed from ksize as sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8 .
            sigma = 0.3*((input1 - 1)*0.5 - 1) + 0.8;
        }
        else
        {
            sigma = input2;
        }
        printf("Input sigma was: %f\n", sigma);

        // input Coefficient K
        printf("Please enter Coefficient K. Use default press Enter . Exit enter q \n");
        str3[0] = '0';
        for (i = 0; (i < 80) && ((ch = getchar()) != EOF)
            && (ch != '\n'); i++)
        {
            str3[i] = (char)ch;
        }

        // Terminate string with a null character
        str3[i] = '\0';
        if (str3[0] == 'q')
        {
            break;
        }
        int input3 = atoi(str3);
        int cK;
        if (input3 == 0)
        {
            cK = 1;
        }
        else
        {
            cK = input3;
        }
        float sum_f = 0;
        float temp_f;
        int sum = 0;
        int temp;
        printf("Input Coefficient K was: %d\n", cK);

        printf("\nwindow size=%d | Sigma = %f Coefficient K = %d\n\n\n", input1, sigma, cK);

        kernel_type kernel2d = produce2dGaussianKernel(window_size, sigma);
        std::cout << std::setprecision(input1) << std::fixed;
        for (int row = 0; row < kernel2d.size(); row++) {
            for (int col = 0; col < kernel2d[row].size(); col++)
            {
                temp_f = cK* kernel2d[row][col];
                sum_f += temp_f;
                space = countSpace(temp_f);
                std::cout << gMatChar[space] << temp_f << ' ';
            }
            std::cout << '\n';
        }
        printf("\n Sum array = %f | delta = %f", sum_f, sum_f - cK);

        // rounding
        printf("\nRecommend use round(): window size=%d | Sigma = %f Coefficient K = %d\n\n\n", input1, sigma, cK);
        sum = 0;
        std::cout << std::setprecision(0) << std::fixed;
        for (int row = 0; row < kernel2d.size(); row++) {
            for (int col = 0; col < kernel2d[row].size(); col++)
            {
                temp = round(cK* kernel2d[row][col]);
                sum += temp;
                space = countSpace((float)temp);
                std::cout << gMatChar[space] << temp << ' ';
            }
            std::cout << '\n';
        }
        printf("\n Sum array = %d | delta = %d", sum, sum - cK);

        // recommented
        sum_f = 0;
        int cK_d = 1 / kernel2d[0][0];
        cK_d = cK_d / 2 * 2;
        printf("\nRecommend: window size=%d | Sigma = %f Coefficient K = %d\n\n\n", input1, sigma, cK_d);
        std::cout << std::setprecision(input1) << std::fixed;
        for (int row = 0; row < kernel2d.size(); row++) {
            for (int col = 0; col < kernel2d[row].size(); col++)
            {
                temp_f = cK_d* kernel2d[row][col];
                sum_f += temp_f;
                space = countSpace(temp_f);
                std::cout << gMatChar[space] << temp_f << ' ';
            }
            std::cout << '\n';
        }
        printf("\n Sum array = %f | delta = %f", sum_f, sum_f - cK_d);

        // rounding
        printf("\nRecommend use round(): window size=%d | Sigma = %f Coefficient K = %d\n\n\n", input1, sigma, cK_d);
        sum = 0;
        std::cout << std::setprecision(0) << std::fixed;
        for (int row = 0; row < kernel2d.size(); row++) {
            for (int col = 0; col < kernel2d[row].size(); col++)
            {
                temp = round(cK_d* kernel2d[row][col]);
                sum += temp;
                space = countSpace((float)temp);
                std::cout << gMatChar[space] << temp << ' ';
            }
            std::cout << '\n';
        }
        printf("\n Sum array = %d | delta = %d", sum, sum - cK_d);

    }
}
0
Đào Anh Xuyên
 function kernel = gauss_kernel(m, n, sigma)
 % Generating Gauss Kernel

 x = -(m-1)/2 : (m-1)/2;
 y = -(n-1)/2 : (n-1)/2;

 for i = 1:m
     for j = 1:n
         xx(i,j) = x(i);
         yy(i,j) = y(j);
     end
 end

 kernel = exp(-(xx.*xx + yy.*yy)/(2*sigma*sigma));

 % Normalize the kernel
 kernel  = kernel/sum(kernel(:));

 % Corresponding function in MATLAB
 % fspecial('gaussian', [m n], sigma)
0
Panfeng Li