实现高斯模糊 \- 如何计算卷积矩阵(内核)

新手上路,请多包涵

我的问题非常接近这个问题: 如何在不使用任何内置高斯函数的情况下对图像进行高斯模糊处理?

这个问题的答案很好,但是并没有给出实际计算一个真正的高斯滤波器内核的例子。答案给出了一个任意内核,并展示了如何使用该内核应用过滤器,而不是如何计算真正的内核本身。我正在尝试从头开始在 C++ 或 Matlab 中实现高斯模糊,所以我需要知道如何从头开始计算内核。

如果有人可以使用任何小的示例图像矩阵计算出真正的高斯滤波器内核,我将不胜感激。

原文由 gsgx 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 868
2 个回答

您可以按照 fspecial 的 MATLAB 文档中的说明从头开始创建高斯内核。请阅读该页面算法部分中的高斯核创建公式,并按照以下代码进行操作。代码是创建一个 sigma = 1 的 m×n 矩阵。

 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

请注意,这可以通过内置的 fspecial 来完成,如下所示:

 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

我认为用你喜欢的任何语言来实现它都很简单。

编辑:让我也添加 h1h2 对于给定的情况,因为你可能不熟悉 meshgrid 如果你在 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

原文由 petrichor 发布,翻译遵循 CC BY-SA 3.0 许可协议

这是 C# 中的一个计算,它不从高斯(或另一个内核)函数中获取单个样本,而是在一个小网格中计算大量样本,并将样本整合到所需数量的部分中。

计算是针对一维的,但它可以很容易地扩展到二维。

这个计算使用了一些其他的函数,我没有在这里添加,但是我已经添加了函数签名,以便您知道它们的作用。

此计算为 +/- 3 的限值生成以下离散值(积分之和 areaSum 为 0.997300):

 kernel size: normalized kernel values, rounded to 6 decimals
3:                     0.157731, 0.684538, 0.157731
5:           0.034674, 0.238968, 0.452716, 0.238968, 0.034674
7: 0.014752, 0.083434, 0.235482, 0.332663, 0.235482, 0.083434, 0.014752

此计算为 +/- 2 的限值生成以下离散值(积分之和 areaSum 为 0.954500):

 kernel size: normalized kernel values, rounded to 6 decimals
3:                     0.240694, 0.518612, 0.240694
5:           0.096720, 0.240449, 0.325661, 0.240449, 0.096720
7: 0.056379, 0.124798, 0.201012, 0.235624, 0.201012, 0.124798, 0.056379

代码:

 using System.Linq;

private static void Main ()
{
  int    positionCount    = 1024;  // Number of samples in the range 0..1.
  double positionStepSize = 1.0 / positionCount;
  double limit            = 3;  // The calculation range of the kernel. +/- 3 is sufficient for gaussian.
  int    sectionCount     = 3;  // The number of elements in the kernel. May be 1, 3, 5, 7, ... (n*2+1)

  // calculate the x positions for each kernel value to calculate.
  double[] positions = CreateSeries (-limit, positionStepSize, (int)(limit * 2 * positionCount + 1));
  // calculate the gaussian function value for each position
  double[] values    = positions.Select (pos => Gaussian (pos)).ToArray ();
  // split the values into equal-sized sections and calculate the integral of each section.
  double[] areas     = IntegrateInSections (values, positionStepSize, sectionCount);
  double   areaSum   = areas.Sum ();
  // normalize to 1
  double[] areas1    = areas.Select (a => a / areaSum).ToArray ();
  double   area1Sum  = areas1.Sum ();  // just to check it's 1 now
}

///-------------------------------------------------------------------
/// <summary>
/// Create a series of <paramref name="i_count"/> numbers, starting at <paramref name="i_start"/> and increasing by <paramref name="i_stepSize"/>.
/// </summary>
/// <param name="i_start">The start value of the series.</param>
/// <param name="i_stepSize">The step size between values in the series.</param>
/// <param name="i_count">The number of elements in the series.</param>
///-------------------------------------------------------------------
public static double[] CreateSeries (double i_start,
                                     double i_stepSize,
                                     int    i_count)
{ ... }

private static readonly double s_gaussian_Divisor = Math.Sqrt (Math.PI * 2.0);

/// ------------------------------------------------------------------
/// <summary>
///   Calculate the value for the given position in a Gaussian kernel.
/// </summary>
/// <param name="i_position"> The position in the kernel for which the value will be calculated. </param>
/// <param name="i_bandwidth"> The width factor of the kernel. </param>
/// <returns> The value for the given position in a Gaussian kernel. </returns>
/// ------------------------------------------------------------------
public static double Gaussian (double i_position,
                               double i_bandwidth = 1)
{
  double position = i_position / i_bandwidth;
  return Math.Pow (Math.E, -0.5 * position * position) / s_gaussian_Divisor / i_bandwidth;
}

/// ------------------------------------------------------------------
/// <summary>
///   Calculate the integrals in the given number of sections of all given values with the given distance between the values.
/// </summary>
/// <param name="i_values"> The values for which the integral will be calculated. </param>
/// <param name="i_distance"> The distance between the values. </param>
/// <param name="i_sectionCount"> The number of sections in the integration. </param>
/// ------------------------------------------------------------------
public static double[] IntegrateInSections (IReadOnlyCollection<double> i_values,
                                            double                      i_distance,
                                            int                         i_sectionCount)
{ ... }

原文由 Tobias Knauss 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题