【问题标题】:How to easily apply a Gauss filter to a list/array of doubles如何轻松地将高斯滤波器应用于双精度列表/数组
【发布时间】:2019-12-10 08:34:40
【问题描述】:

我的代码已经可以工作了,但是恕我直言,很丑:

public IList<OxyPlot.DataPoint> Points { get; private set; }
public IList<OxyPlot.DataPoint> Points2 { get; private set; }

void ApplyGaussFilter()
{
    var gauss = MathNet.Numerics.Window.Gauss(5, 0.8);
    Points2 = new List<OxyPlot.DataPoint>();
    var inputArray = Points.ToArray();

    for (int i=2; i< inputArray.Length-2; ++i)
    {
        double smoothedVal = (inputArray[i - 2].Y * gauss[0] +
                      inputArray[i - 1].Y * gauss[1] +
                      inputArray[i].Y * gauss[2] +
                      inputArray[i + 1].Y * gauss[3] +
                      inputArray[i + 2].Y * gauss[4]) / gauss.Sum();

        Points2.Add(new DataPoint(inputArray[i].X, smoothedVal));
    }
}

有没有更简单的方法?另外我不知道如何正确处理两个最外面的数组值。为了将高斯宽度从 5 更改为任何其他值,for 循环也需要手动更新。

到目前为止,我发现的所有示例都是基于图像来过滤这些的。例如。 guassian smoothening formula application
与我丑陋的 for 循环相比,使用带有两行代码的图像库应用过滤器看起来要好得多。

GaussianBlur filter = new GaussianBlur(4, 11);
filter.ApplyInPlace(graph);

【问题讨论】:

    标签: c# mathnet-numerics mathnet-filtering


    【解决方案1】:

    我试图让MathNet.Numerics.Window.Gauss() 工作,但我做不到。也许我做错了什么,但我找到了其他适合我的解决方案,例如这个:

    private static double[] gaussianKernel1d( int kernelRadius, double sigma )
    {
        double[] kernel = new double[kernelRadius + 1 + kernelRadius];
        for( int xx = -kernelRadius; xx <= kernelRadius; xx++ )
            kernel[kernelRadius + xx] = Math.Exp( -(xx * xx) / (2 * sigma * sigma) ) /
                    (Math.PI * 2 * sigma * sigma);
        return kernel;
    }
    

    一旦你有了它,一维高斯滤波就可以实现如下:

    double[]  array        = (your array)
    double    sigma        = (your sigma)
    int       kernelRadius = (int)Math.Ceiling( sigma * 2.57 ); // significant radius
    double[]  kernel       = gaussianKernel1d( kernelRadius, sigma );
    double[]  result       = filter( array, kernel );
    
    static double[] filter( double[] array, double[] kernel )
    {
        Assert( kernel.Length % 2 == 1 ); //kernel size must be odd.
        int       kernelRadius = kernel.Length / 2;
        int       width  = array.GetLength( 0 );
        double[] result = new double[width];
        for( int x = 0; x < width; x++ )
        {
            double sumOfValues  = 0;
            double sumOfWeights = 0;
            for( int i = -kernelRadius; i <= kernelRadius; i++ )
            {
                double value = array[clamp( x + i, 0, width - 1 )];
                double weight = kernel[kernelRadius + i];
                sumOfValues  += value * weight;
                sumOfWeights += weight;
            }
            result[x] = sumOfValues / sumOfWeights;
        }
        return result;
    }
    

    clamp() 函数就是这个小东西:

    private static int clamp( int value, int min, int max )
    {
        if( value < min )
            return min;
        if( value > max )
            return max;
        return value;
    }
    

    并确保使用数据边界处的值代替超出该边界的任何值。这是处理您所说的“两个最外层数组值”的一种方法。

    【讨论】:

    • 如果您使用的是 .Net Core(或 5),则有一个内置的 Math.Clamp 方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 2013-03-29
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多