Search code examples
c#gaussiangaussianblur

How to calculate normal distribution kernal for 1d gaussian filter


I need to apply a 1d gaussian filter to a list of floats in c#, ie, to smooth a graph.

I got as far as simply averaging each value with n neighbors, but the result wasn't quite right and so I discovered that I need to apply a normal distribution weight to the contributions of the values per iteration.

I can't find a library like scipy that has a function for this, and I don't quite understand the algebraic formulas I have found for computing a gaussian kernal. Examples are generally geared towards a 2D implementation for images.

Can anyone suggest the modifications that would need to be made to the following code to achieve the proper gaussian effect?

    public static List<float> MeanFloats(List<float> floats, int width)
    {
        List<float> results = new List<float>();
    
        if (width % 2 == 0) 
            width -= 1;     // make sure width is odd

        int halfWidthMinus1 = width / 2; // width is known to be odd, divide by 2 will round down
    
        for (int i = 0; i < floats.Count; i++) // iterate through all floats in list
        {
            float result = 0;
        
            for (int j = 0; j < width; j++)
            {
                var index = i - halfWidthMinus1 + j;
                
                index = math.max(index, 0);                 // clamp index - the first and last elements of the list will be used when the algorithm tries to access outside the bounds of the list 
                index = math.min(index, floats.Count-1);
                
                result += floats[index];  // multiply with kernal here??
            }
            
            result /= width;    // calculate mean

            results.Add(result);
        }
        return results;
    }

If relevant this is for use in a Unity game.


Solution

  • A 1-dimensional Gaussian Kernel is defined as

    equation

    where sigma is the standard deviation of your list, and x is the index distance.

    You then create a kernel by filling each of its array slots with a multiplier. Here is an (untested) example:

    private static float[] GaussianKernel(int width, float sigma)
        {
            float[] kernel = new float[width + 1 + width];
            
            for (int i = -width; i <= width; i++)
            {
                kernel[width + i] = Mathf.Exp(-(i * i) / (2 * sigma * sigma)) / (Math.PI * 2 * sigma * sigma);
            }
    
            return kernel;
        }
    

    In your smoothing function you apply this multiplier to the floats[index] value. Finally, before adding the result, instead of dividing it by the width, you divide it by the total sum of the kernel weights (the values of the kernel array). You could compile the values of the current kernel weight during each iteration in your j-loop weightSum += kernel[j].