高斯生成的内核和书中给出的不一样。为什么?

时间:2016-09-28 10:17:28

标签: c++ image-processing

为什么高斯核值不是由公式生成并在书中给出?

我使用以下代码创建了高斯内核。

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

结果是

0.01134 0.08382 0.01134
0.08382 0.61935 0.08382
0.01134 0.08382 0.01134
Press any key to continue . . .

本书中给出了这个3x3高斯内核

{1 / 16.0f, 2 / 16.0f, 1 / 16.0f,
2 / 16.0f, 4 / 16.0f, 2 / 16.0f,
1 / 16.0f, 2 / 16.0f, 1 / 16.0f };

我徘徊为什么两个系数都不相同。以及西格玛的哪个值,高斯内核(书中给出)掩码生成? 注意:我使用高斯方程生成高斯核

已编辑:我在代码中添加了高斯函数。

2 个答案:

答案 0 :(得分:2)

好的,这本书的内核是一个二项式加权内核,可以在Pascal的三角形中找到,在3行系数的行上1 2 1。矩阵中的每个单元格是与其行索引对应的系数,乘以与其列索引对应的系数,然后整个事物被标准化。

很遗憾,我无法找到gaussian功能的来源。我可以将你的函数输出与我对它的作用的猜测进行比较,但我真的不想花时间去做这个。

为什么这很重要,实际上有两种类型的高斯内核。我怀疑你的内核使用熟悉的基于指数的高斯,一个标准化的exp(-x*x/(2*s*s))或类似的东西。但是像这样的内核只在涉及连续函数的卷积中有效,而不是像图像数据那样的离散样本集。

所以另一个被称为“离散高斯核”。对于大sigma,它非常接近连续的Gausian内核,但对于小于4像素的sigma,离散版本与连续版本略有不同。

以下是维基百科文章的链接,其中包含离散高斯核:https://en.wikipedia.org/wiki/Scale_space_implementation#The_discrete_Gaussian_kernel 不幸的是,它很难计算,因为它依赖于修改的贝塞尔函数而不是标准数学库中提供的指数函数。

修改

我继续挖掘我的代码来计算离散高斯核。我不能证明这个计算的算法是正确的,因为几年前我写了它,我不记得我是怎么想到的。对于固定的sigma和几个输入,下面的代码将来自Descrete高斯核的值与从高斯函数中采样的值进行比较。

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

// x is expected to be an integer value
double DiscreteGaussian(double x, double Sigma) {
    x = std::fabs(x);

    if(x > Sigma * 10) return 0;

    double k = 0;

    const double LnSigma_x_2 = std::log(Sigma) * 2;

    const double Ca = x * (LnSigma_x_2 - std::log(2.0)) - (Sigma * Sigma);

    double Ra = 0;                  // accumulated LnGamma(k + 1)
    double Rb = std::lgamma(x + 1); // accumulated LnGamma(x + k + 1)
    double Rc = 0;                  // accumulated k * (4*Ln(Sigma)-2*Ln(2))

    const double Cc = 2.0 * (LnSigma_x_2 - std::log(2.0));   // const for Rc

    double Sum;
    double Next = std::exp(-Rb);

    do {
        Sum = Next;
        k += 1;
        Ra += std::log(k);
        Rb += std::log(x + k);
        Rc += Cc;
        const double ExpTerm = Rc - Ra - Rb;
        Next = Sum + std::exp(ExpTerm);
    } while(Next != Sum);

    return Sum * std::exp(Ca); 
}

double ContinuousGaussian(double x, double Sigma) {
    static const double NORMER = 0.3989422804014327;  // 1/sqrt(2*pi)
    const double x_as_sigmas = x / Sigma;
    return std::exp(-0.5 * x_as_sigmas * x_as_sigmas) * NORMER / Sigma;
}

int main() {
    // t is sigma squared, so for a t of 2, use a sigma of sqrt(2)
    const double sigma = std::sqrt(0.5);
    std::cout << "Sigma " << sigma << '\n';
    std::cout << " x    Discrete          Sampled\n";
    for(int x = -5; x<=5; ++x) {
        const double yd = DiscreteGaussian(x, sigma);
        const double yc = ContinuousGaussian(x, sigma);
        std::cout << (x < 0 ? "" : " ") << x << "    "
            << std::setw(14) << std::setprecision(8) << std::fixed << std::left
            << yd << "    " << yc << '\n';
    }
}

输出看起来像这样

Sigma 0.707107
 x    Discrete          Sampled
-5    0.00000499        0.00000000
-4    0.00009996        0.00000006
-3    0.00160434        0.00006963
-2    0.01935206        0.01033349
-1    0.15642080        0.20755375
 0    0.64503527        0.56418958
 1    0.15642080        0.20755375
 2    0.01935206        0.01033349
 3    0.00160434        0.00006963
 4    0.00009996        0.00000006
 5    0.00000499        0.00000000

正如您所看到的,即使是小sigma,差异也很小,取决于应用,可能不足以从使用采样连续高斯函数切换。

答案 1 :(得分:0)

double sigma = kernelRadius / 2.;

这是问题所在。该计算是近似值。你需要确切地知道他们在书中使用的西格玛是什么。

本书中使用的西格玛是~0.8