do wdrożenia gaussian blur wystarczy wziąć gaussian function i obliczyć jedną wartość dla każdego z elementów w jądrze.
Zazwyczaj do głównego elementu jądra przypisuje się maksymalną masę, a wartości bliskie zeru dla elementów na krawędziach jądra. Oznacza to, że jądro powinno mieć nieparzystą wysokość (lub szerokość), aby upewnić się, że rzeczywiście jest element centralny.
Aby obliczyć rzeczywiste elementy jądra, można przeskalować dzwon Gaussa do siatki jądra (wybierz dowolny, np. sigma = 1
i dowolny zakres, np. -2*sigma ... 2*sigma
) i znormalizuj go, s.t. elementy sumują się do jednego. Aby to osiągnąć, jeśli chcesz wspierać dowolne rozmiary jądra, możesz dostosować sigmę do wymaganego rozmiaru jądra.
Oto przykład 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';
}
}
Wyjście jest:
$ 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
Jako uproszczenia nie trzeba używać 2D-jądro. Łatwiejsze do wdrożenia, a także bardziej wydajne do obliczenia jest użycie dwóch ortogonalnych rdzeni 1d. Jest to możliwe dzięki asocjatywności tego typu liniowego splotu (separacja liniowa). Możesz także chcieć zobaczyć this section odpowiedniego artykułu w Wikipedii.
Oto samo w Pythonie (w nadziei, że ktoś może go znaleźć przydatne):
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]
produkuje jądro:
['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']
Czy to czytasz: http://en.wikipedia.org/wiki/Gaussian_function? –
Albo nawet to: http://en.wikipedia.org/wiki/Gaussian_blur – Bart
Tak, spędziłem dużo czasu próbując je zrozumieć. Potrzebuję przykładu "krok po kroku". Po jego zrozumieniu prawdopodobnie dodam przykład do strony Rozmycie gaussowskie. – gsingh2011