开发者

Array data normalization

I have an array of values (between -1.0 and 1.0) that represent intensity (Black to White). I need a way to map the double values from -1.0 through 1.0 to 0 through 255 and back.

More generalized, I have an array of data and I need to map from the min and max value of the data to a supplied min and max. Basic struc开发者_Python百科ture should be like:

private static int[] NormalizeData(double[] data, int min, int max)
{
    var sorted = data.OrderBy(d => d);
    double dataMax = sorted.First();
    double dataMin = sorted.Last();
    int[] ret = new int[data.Length];

    for (int i = 0; i < data.Length; i++)
    {
        ret[i] = (int)data[i];  // Normalization here
    }

    return ret;
}


This works:

private static int[] NormalizeData(IEnumerable<double> data, int min, int max)
{
    double dataMax = data.Max();
    double dataMin = data.Min();
    double range = dataMax - dataMin;

    return data
        .Select(d => (d - dataMin) / range)
        .Select(n => (int)((1 - n) * min + n * max))
        .ToArray();
}

The first select normalizes the input to be from 0 to 1 (0 being minimum, 1 being the maximum). The second select takes that normalized number, and maps it to the new minimum and maximum.

Note that using the LINQ Min() and Max() functions are faster than sorting the input for larger datasets: O(n) vs. O(n * lg(n)).

Also, if you want to go the other way, then you'll want it to return doubles instead of ints.


public static double Scale(this double elementToScale,
              double rangeMin, double rangeMax, 
              double scaledRangeMin, double scaledRangeMax)
{
    var scaled = scaledRangeMin + ((elementToScale - rangeMin) * (scaledRangeMax - scaledRangeMin) / (rangeMax - rangeMin));
    return scaled;
}

Usage:

// double [-1,1] to int [0-255]
int[] integers = doubles.Select(x => x.Scale(-1,1,0,255)).ToArray();

//  int [0-255] to double [-1,1]
double[] doubles = integers.Select(x => ((double)x).Scale(0,255,-1,1)).ToArray();

If you don't know the min and max in advance ([0-255] and [-1,1] in the example), you can use LINQ Min() and Max()


private static int[] NormalizeData(double[] data, int min, int max) {
    int[] ret = new int[data.Length];
    for (int i = 0; i < data.Length; i++) {
        ret[i] = (int)((max * (data[i] + 1)) / 2);
    }
    return ret;
}

static void Main(string[] args) {
    double[] data = { 1.0, -1, 0, -.5, .5 };
    int[] normalized = NormalizeData(data, 0, 255);
    foreach (var v in normalized) {
        Console.WriteLine(v);
    }
}


EDIT: How about this:

private static int[] NormalizeData(double[] data, int min, int max)
{
    var sorted = data.OrderBy(d => d);
    double dataMax = sorted.First();
    double dataMin = sorted.Last();
    int[] ret = new int[data.Length];

    double avgIn = (double)((min + max) / 2.0);
    double avgOut = (dataMax + dataMin) / 2.0);

    for (int i = 0; i < data.Length; i++)
    {
        ret[i] = (int) Math.Round(avgOut * (data[i] + avgIn) / 2);
    }

    return ret;
}


Assuming a strictly linear transformation and that you want dataMin to map to min and dataMax to map to max:

double dataRange = dataMax - dataMin;
int newRange = max - min;

double pct = (data[i] - dataMin) / dataRange;

int newValue = Math.Round(min + (pct * newRange));

That can certainly be optimized, but it shows the basic idea. Basically, you figure out the position (as a percentage) of the value in the original range and then map that percentage to the target range.

Note that if dataMin is -0.5 and dataMax is 0.5, this might not produce the results that you're looking for because -0.5 will map to 0 and 0.5 will map to 255. If you want things to map exactly as stated, you'll have to define the source range as well.

As an aside, there's no particular reason to sort the items just to get the min and max. You can write:

double dataMax = data.Max();
double dataMin = data.Min();


To be able to normalize your array which in this example acts a vector mathematically you need to define what length the vector is in (how many dimensions). It's not really clear from the example if you want to normalize the entire array taking all elements in the array into account. If so then you calculate the dot product of the array, store the dot products square root as the length of the array. then you divide every term with that length to normalize the array to a length of 1.0.

In the case above you did not actually describe a normalization of the data but a conversion. To solve that you could use something like the following:

private static double[] convertToScale(double[] data, double oldMin, double oldMax,double min, double max)
{
    double oldDiff = 0 - oldMin;
    double oldScale = oldMax - oldMin;
    double diff = 0 - min;
    double scale = max - min;
    int[] ret = new double[data.Length];

    for (int i = 0; i < data.Length; i++)
    {
        double scaledFromZeroToOne = (oldDiff+data[i])/oldScale; // Normalization here [0,1]
        double value = (scaledFromZeroToOne*scale)-diff;
        ret[i] = value;  
    }

    return ret;
}

This function i believe would solve the problem described above. You can call it like following row:

double[] result = convertToScale(input,-1.0,1.0,0,255);

And then cast everything to int if you'd rather have the values represented as ints.

Hope it helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜