fspecial c++ matlab filter blur gaussian

c++ - fspecial - Implementación de desenfoque gaussiano: cómo calcular la matriz de convolución(kernel)



gaussian filter matlab (6)

Mi pregunta es muy similar a esta pregunta: ¿cómo difumino Gauss una imagen sin utilizar ninguna función gaussiana incorporada?

La respuesta a esta pregunta es muy buena, pero no da un ejemplo de calcular realmente un kernel de filtro gaussiano real. La respuesta proporciona un kernel arbitrario y muestra cómo aplicar el filtro utilizando ese núcleo, pero no cómo calcular un kernel real. Estoy tratando de implementar un desenfoque gaussiano en C ++ o Matlab desde cero, así que necesito saber cómo calcular el kernel desde cero.

Agradecería que alguien pudiera calcular un núcleo de filtro Gaussiano real usando cualquier matriz de imagen de ejemplo pequeña.


Desenfoque gaussiano en python utilizando la biblioteca de imágenes PIL. Para obtener más información, lea esto: 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


Es tan simple como suena:

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;


Para implementar el en.wikipedia.org/wiki/Gaussian_blur , simplemente toma la en.wikipedia.org/wiki/Gaussian_function y calcula un valor para cada uno de los elementos en tu kernel.

Por lo general, desea asignar el peso máximo al elemento central en su kernel y valores cercanos a cero para los elementos en los bordes del kernel. Esto implica que el núcleo debe tener una altura impar (o ancho) para garantizar que realmente exista un elemento central.

Para calcular los elementos reales del núcleo, puede escalar la campana gaussiana a la cuadrícula del kernel (elija un arbitrario, por ejemplo, sigma = 1 y un rango arbitrario, por ejemplo, -2*sigma ... 2*sigma ) y normalícelo, st los elementos suman uno . Para lograr esto, si desea admitir tamaños de kernel arbitrarios, es posible que desee adaptar sigma al tamaño de kernel requerido.

Aquí hay un ejemplo de 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''; } }

El resultado es:

$ 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

Como simplificación, no necesita usar un kernel 2d. Más fácil de implementar y también más eficiente de calcular es usar dos núcleos 1d ortogonales. Esto es posible debido a la asociatividad de este tipo de convolución lineal (separabilidad lineal). También es posible que desee ver esta sección del artículo de wikipedia correspondiente.

Esto es lo mismo en Python (con la esperanza de que alguien lo encuentre útil):

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 el 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'']


Puede crear un núcleo gaussiano desde cero, como se indica en la documentación de MATLAB de fspecial . Lea la fórmula de creación de kernel de Gauss en la parte de algoritmos de esa página y siga el código que se muestra a continuación. El código es para crear una matriz 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

Observe que esto se puede hacer con el fspecial siguiente manera:

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

Creo que es sencillo implementar esto en el idioma que desee.

EDITAR: Permítame también agregar los valores de h1 y h2 para el caso dado, ya que puede que no esté familiarizado con meshgrid si codifica 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


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)


// 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://.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); } }