如何创建任意宽度的高斯核?
How to create a Gaussian kernel of arbitrary width?
如何通过仅指定其宽度 w
(3,5,7,9...) 而不指定其方差 sigma
来创建高斯核?
换句话说,如何适应sigma
使得高斯分布'fits well' w
?
我会对 C++ 实现感兴趣:
void create_gaussian_kernel(int w, std::vector<std::vector<float>>& kernel)
{
kernel = std::vector<std::vector<float>>(w, std::vector<float>(w, 0.f)); // 2D array of size w x w
const Scalar sigma = 1.0; // how to adapt sigma to w ???
const int hw = (w-1)/2; // half width
for(int di = -hw; di <= +hw; ++di)
{
const int i = hw + di;
for(int dj = -hw; dj <= +hw; ++dj)
{
const int j = hw + dj;
kernel[i][j] = gauss2D(di, dj, sigma);
}
}
}
我在 Internet 上看到的所有内容都使用固定大小 w
和固定方差 sigma
:
- geeksforgeeks.org/gaussian-filter-generation-c/
- tutorialspoint.com/gaussian-filter-generation-in-cplusplus
- whosebug.com/a/8204880/5317819
我发现了 sigma
和 w
之间的简单(任意)关系。
我希望核外的下一个值(沿一个轴)低于一个非常小的值 epsilon
:
exp( - (half_width + 1)^2 / (2 * sigma^2) ) < epsilon
使用 half_width
内核 'half width'.
结果是
sigma^2 = - (half_width + 1)^2 / (2 * log(epsilon))
我使用以下 C++ 代码:
#include <vector>
#include <cmath>
#include <cassert>
using Matrix = std::vector<std::vector<float>>;
// compute sigma^2 that 'fit' the kernel half width
float compute_squared_variance(int half_width, float epsilon = 0.001)
{
assert(0 < epsilon && epsilon < 1); // small value required
return - (half_width + 1.0) * (half_width + 1.0) / 2.0 / std::log(epsilon);
}
float gaussian_exp(float y, float x, float sigma2)
{
assert(0 < sigma2);
return std::exp( - (x*x + y*y) / (2 * sigma2) );
}
// create a Gaussian kernel of size 2*half_width+1 x 2*half_width+1
Matrix make_gaussian_kernel(int half_width)
{
if(half_width <= 0)
{
// kernel of size 1 x 1
Matrix kernel(1, std::vector<float>(1, 1.0));
return kernel;
}
Matrix kernel(2*half_width+1, std::vector<float>(2*half_width+1, 0.0));
const float sigma2 = compute_squared_variance(half_width, 0.1);
float sum = 0;
for(int di = -half_width; di <= +half_width; ++di)
{
const int i = half_width + di;
for(int dj = -half_width; dj <= +half_width; ++dj)
{
const int j = half_width + dj;
kernel[i][j] = gaussian_exp(di, dj, sigma2);
sum += kernel[i][j];
}
}
assert(0 < sum);
// normalize
for(int i=0; i<2*half_width+1; ++i)
{
for(int j=0; j<2*half_width+1; ++j)
{
kernel[i][j] /= sum;
}
}
return kernel;
}
如何通过仅指定其宽度 w
(3,5,7,9...) 而不指定其方差 sigma
来创建高斯核?
换句话说,如何适应sigma
使得高斯分布'fits well' w
?
我会对 C++ 实现感兴趣:
void create_gaussian_kernel(int w, std::vector<std::vector<float>>& kernel)
{
kernel = std::vector<std::vector<float>>(w, std::vector<float>(w, 0.f)); // 2D array of size w x w
const Scalar sigma = 1.0; // how to adapt sigma to w ???
const int hw = (w-1)/2; // half width
for(int di = -hw; di <= +hw; ++di)
{
const int i = hw + di;
for(int dj = -hw; dj <= +hw; ++dj)
{
const int j = hw + dj;
kernel[i][j] = gauss2D(di, dj, sigma);
}
}
}
我在 Internet 上看到的所有内容都使用固定大小 w
和固定方差 sigma
:
- geeksforgeeks.org/gaussian-filter-generation-c/
- tutorialspoint.com/gaussian-filter-generation-in-cplusplus
- whosebug.com/a/8204880/5317819
我发现了 sigma
和 w
之间的简单(任意)关系。
我希望核外的下一个值(沿一个轴)低于一个非常小的值 epsilon
:
exp( - (half_width + 1)^2 / (2 * sigma^2) ) < epsilon
使用 half_width
内核 'half width'.
结果是
sigma^2 = - (half_width + 1)^2 / (2 * log(epsilon))
我使用以下 C++ 代码:
#include <vector>
#include <cmath>
#include <cassert>
using Matrix = std::vector<std::vector<float>>;
// compute sigma^2 that 'fit' the kernel half width
float compute_squared_variance(int half_width, float epsilon = 0.001)
{
assert(0 < epsilon && epsilon < 1); // small value required
return - (half_width + 1.0) * (half_width + 1.0) / 2.0 / std::log(epsilon);
}
float gaussian_exp(float y, float x, float sigma2)
{
assert(0 < sigma2);
return std::exp( - (x*x + y*y) / (2 * sigma2) );
}
// create a Gaussian kernel of size 2*half_width+1 x 2*half_width+1
Matrix make_gaussian_kernel(int half_width)
{
if(half_width <= 0)
{
// kernel of size 1 x 1
Matrix kernel(1, std::vector<float>(1, 1.0));
return kernel;
}
Matrix kernel(2*half_width+1, std::vector<float>(2*half_width+1, 0.0));
const float sigma2 = compute_squared_variance(half_width, 0.1);
float sum = 0;
for(int di = -half_width; di <= +half_width; ++di)
{
const int i = half_width + di;
for(int dj = -half_width; dj <= +half_width; ++dj)
{
const int j = half_width + dj;
kernel[i][j] = gaussian_exp(di, dj, sigma2);
sum += kernel[i][j];
}
}
assert(0 < sum);
// normalize
for(int i=0; i<2*half_width+1; ++i)
{
for(int j=0; j<2*half_width+1; ++j)
{
kernel[i][j] /= sum;
}
}
return kernel;
}