Как легко применить фильтр Гаусса к списку / массиву двойников
Мой код уже работает, но ИМХО некрасиво:
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 также необходимо обновить вручную.
Все примеры, которые я нашел до сих пор, основаны на изображениях для их фильтрации. Применение разглаживающей формулы EG guassian.
Применение фильтра с библиотеками изображений с двумя строками кода выглядит намного лучше по сравнению с моим уродливым циклом for.
GaussianBlur filter = new GaussianBlur(4, 11);
filter.ApplyInPlace(graph);
0 ответов
Я пытался получить
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[y, 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;
}
и это гарантирует, что значение на границе ваших данных будет использоваться вместо любых значений, лежащих за этой границей. Это один из способов обработки того, что вы называете "двумя самыми внешними значениями массива".