2011-11-20 12 views
29

La mia domanda è molto vicino a questa domanda: How do I gaussian blur an image without using any in-built gaussian functions?Implementazione Controllo sfocatura - Come si calcola matrice di convoluzione (kernel)

La risposta a questa domanda è molto buona, ma non dà un esempio di calcolo di una realtà vero kernel del filtro gaussiano. La risposta fornisce un kernel arbitrario e mostra come applicare il filtro usando quel kernel ma non come calcolare un vero kernel. Sto provando a implementare una sfocatura gaussiana in C++ o Matlab da zero, quindi ho bisogno di sapere come calcolare il kernel da zero.

Apprezzerei se qualcuno potesse calcolare un vero kernel di filtro gaussiano usando una qualsiasi piccola matrice immagine di esempio.

+0

Avere leggete questo: http://en.wikipedia.org/wiki/Gaussian_function? –

+0

O anche questo: http://en.wikipedia.org/wiki/Gaussian_blur – Bart

+0

Sì, ho passato un sacco di tempo a cercare di capirli. Quello di cui ho bisogno è un esempio graduale. Dopo averlo capito, probabilmente aggiungerò l'esempio alla pagina Sfocatura gaussiana. – gsingh2011

risposta

33

si può creare un kernel gaussiano da zero, come indicato nella documentazione di MATLAB di fspecial. Si prega di leggere la formula di creazione del kernel gaussiana nella parte degli algoritmi in quella pagina e seguire il codice sottostante. Il codice è quello di creare una matrice m-by-n con 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 

osservi che questo può essere fatto incorporata fspecial come segue:

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 

ritengo sia semplice da implementare in qualsiasi lingua che ti piace.

EDIT: Vorrei inoltre aggiungere i valori di h1 e h2 per il caso specifico, dal momento che non conoscete con meshgrid se il codice in 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 
+1

Ho digitato [h1, h2] = meshgrid (- (m-1)/2: (m-1)/2, - (n-1)/2: (n-1)/2) e ho ottenuto un h1 che va da -2 a 2, non da -1.5 a 1.5. Lo stesso problema con h2. Ma il mio risultato è lo stesso. Inoltre, perché hai utilizzato i valori della griglia della mesh come valori nella formula? Cosa rappresenta questo se stavi calcolando questo per un'immagine? – gsingh2011

+0

Hai ragione! Ho cambiato 'm' e' n' a 4 per vedere se il codice funziona e poi ho copiato i valori per questo caso invece di dargli il valore 5. L'ho risolto, grazie. – petrichor

+0

I valori sono calcolati su una griglia in cui quello al centro è l'origine, che nel nostro caso è h1 == 0 e h2 == 0. Tutte le altre coppie rappresentano le altre coordinate quando si osservano i valori h1, h2 elemento per elemento. Durante il filtraggio, puoi pensare che questa griglia verrà posizionata su un pixel dell'immagine in cui l'origine della griglia si adatta esattamente al pixel. Puoi leggere la risposta di Goz nel link che hai fornito nella tua domanda per i dettagli. – petrichor

22

E 'così semplice come sembra:

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; 
+11

Questo è imperfetto: è necessario normalizzare anche il kernel, oppure l'immagine diventa più scura a seconda di W e sigma. In poche parole: ottieni la somma dei valori del kernel e dividi ogni valore del kernel per quella somma. – Rookie

+2

@Rookie - Ho deciso di modificare questo post e aggiungere la normalizzazione. Questo per consentire a chi vuole una soluzione C/C++ di usarlo direttamente. Buona pesca! – rayryeng

+0

Sembra non corretto quando m, n sono pari, confrontando con il risultato di 'fspecial'. –

13

Per l'attuazione del gaussian blur è sufficiente prendere il gaussian function e calcolare un valore per ciascuno degli elementi nel vostro kernel.

In genere si desidera assegnare il peso massimo all'elemento centrale nel kernel e valori prossimi allo zero per gli elementi ai bordi del kernel. Ciò implica che il kernel dovrebbe avere un'altezza dispari (larghezza) per garantire che ci sia effettivamente un elemento centrale.

Per calcolare gli elementi effettivi kernel si può scalare la campana gaussiana alla rete kernel (scegliere un arbitrario esempio sigma = 1 e un intervallo arbitrario esempio -2*sigma ... 2*sigma) e normalizzare esso, S.T. gli elementi sommano a uno. Per raggiungere questo obiettivo, se si desidera supportare dimensioni del kernel arbitrarie, è possibile adattare il sigma alla dimensione del kernel richiesta.

Ecco un esempio C++:

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

double gaussian (double x, double mu, double sigma) { 
    return std::exp(-(((x-mu)/(sigma))*((x-mu)/(sigma)))/2.0); 
} 

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'; 
    } 
} 

L'output è:

$ g++ test.cc && ./a.out 
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 
0.00408 0..02412 0.03012 0.02412 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..02412 0.03012 0.02412 0..00408 
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 

Come una semplificazione non è necessario utilizzare un 2d-kernel. Più semplice da implementare e anche più efficiente da calcolare è utilizzare due kernel 1d ortogonali. Ciò è possibile a causa dell'associatività di questo tipo di convoluzione lineare (separabilità lineare). Si potrebbe anche voler vedere this section del corrispondente articolo di Wikipedia.


Ecco lo stesso in Python (con la speranza che qualcuno potrebbe trovare 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] 

produce il kernel:

['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'] 
+0

Dov'è il PI qui? – user2023370

+0

@ user2023370 Perché pensi che ci dovrebbe essere un IP in là? – moooeeeep

0

sfocatura gaussiana in Python utilizzando PIL libreria di immagini. Per maggiori informazioni leggere questo: 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
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

Aggiungi alcuni commenti al tuo codice, sarà utile per le altre persone. – HDJEMAI

Problemi correlati