IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 10173


Ignore:
Timestamp:
Nov 22, 2006, 7:33:28 PM (20 years ago)
Author:
magnier
Message:

several important changes:

  • simplified code for sampleMean and MinMax : a single copy with if statements

is only marginally slower than separate loops

  • added local maskVal to two functions so external user can safely provide a 0 mask
  • moved fittedStats to independent function: called multiple times if sigma changes alot
  • gaussian now fits for var not sigma (avoid +/- flips)
  • restrict to 2 sigma by default, but user setable
  • some significant re-work of the implementation of robustStats
    • no more smoothing of the vector
    • a bit clearer choice for binsize
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/math/psStats.c

    r9982 r10173  
    1616 * use ->min and ->max (PS_STAT_USE_RANGE)
    1717 *
    18  *  @version $Revision: 1.189 $ $Name: not supported by cvs2svn $
    19  *  @date $Date: 2006-11-15 00:36:14 $
     18 *  @version $Revision: 1.190 $ $Name: not supported by cvs2svn $
     19 *  @date $Date: 2006-11-23 05:33:28 $
    2020 *
    2121 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
     
    5555/*****************************************************************************/
    5656#define PS_GAUSS_WIDTH 3                // The width of the Gaussian smoothing.
    57 // This corresponds to N in the ADD.
    58 #define PS_CLIPPED_NUM_ITER_LB 1
     57#define PS_CLIPPED_NUM_ITER_LB 1 // This corresponds to N in the ADD.
    5958#define PS_CLIPPED_NUM_ITER_UB 10
    6059#define PS_CLIPPED_SIGMA_LB 1.0
    6160#define PS_CLIPPED_SIGMA_UB 10.0
    6261#define PS_POLY_MEDIAN_MAX_ITERATIONS 30
     62#define MASK_MARK 0x80   // bit to use internally to mark data as bad
    6363
    6464#define PS_BIN_MIDPOINT(HISTOGRAM, BIN_NUM) \
     
    104104this routine sets stats->sampleMean to NAN.
    105105 *****************************************************************************/
    106 static bool vectorSampleMean(const psVector* myVector,
    107                              const psVector* errors,
    108                              const psVector* maskVector,
    109                              psMaskType maskVal,
    110                              psStats* stats)
     106# if (0)
     107    static bool vectorSampleMean(const psVector* myVector,
     108                                 const psVector* errors,
     109                                 const psVector* maskVector,
     110                                 psMaskType maskVal,
     111                                 psStats* stats)
    111112{
    112113    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     
    228229    return true;
    229230}
     231#endif
     232
     233/******************************************************************************
     234vectorSampleMean(myVector, maskVector, maskVal, stats): calculates the
     235mean of the input vector.  If there was a problem with the mean calculation,
     236this routine sets stats->sampleMean to NAN.
     237 
     238XXX : using the method below, with a single loop for various options
     239      costs only a small amount and is much easier to debug. running 10000 tests
     240      of 1000 point vectors for the two methods gives:
     241                     separate    single
     242(mask: 0, range: 0): 0.067 sec   0.073 sec
     243(mask: 1, range: 0): 0.098 sec  0.102 sec
     244(mask: 0, range: 0): 0.136 sec  0.162 sec
     245(mask: 1, range: 0): 0.177 sec  0.181 sec
     246 *****************************************************************************/
     247static bool vectorSampleMean(const psVector* myVector,
     248                             const psVector* errors,
     249                             const psVector* maskVector,
     250                             psMaskType maskVal,
     251                             psStats* stats)
     252{
     253    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     254
     255    psF32 mean = 0.0;                   // The mean
     256    long count = 0;                     // Number of points contributing to this mean
     257
     258    psF32 *data = myVector->data.F32;   // Dereference
     259    int numData = myVector->n;          // Number of data points
     260
     261    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8;
     262    bool useRange = stats->options & PS_STAT_USE_RANGE;
     263
     264    // If errors exist, we enter a slightly different loop.
     265    // XXX collapse to a single loop?
     266    if (!errors) {
     267        for (long i = 0; i < numData; i++) {
     268            // Check if the data is with the specified range
     269            if (useRange && (data[i] < stats->min))
     270                continue;
     271            if (useRange && (data[i] > stats->max))
     272                continue;
     273            if (maskData && (maskData[i] & maskVal))
     274                continue;
     275            mean += data[i];
     276            count++;
     277        }
     278        mean = (count > 0) ? mean / (psF32) count : NAN;
     279    } else {
     280        psF32 sumWeights = 0.0;         // The sum of the weights
     281        psF32 *errorsData = errors->data.F32;
     282        for (long i = 0; i < numData; i++) {
     283            // Check if the data is with the specified range
     284            if (useRange && (data[i] < stats->min))
     285                continue;
     286            if (useRange && (data[i] > stats->max))
     287                continue;
     288            if (maskData && (maskData[i] & maskVal))
     289                continue;
     290
     291            float weight = 1.0 / PS_SQR(errorsData[i]);
     292            mean += data[i] * weight;
     293            sumWeights += weight;
     294            count++;
     295        }
     296        mean = (count > 0) ? mean / sumWeights : NAN;
     297    }
     298
     299    stats->sampleMean = mean;
     300    if (isnan(mean)) {
     301        psTrace("psLib.math", 4, "---- %s(false) end ----\n", __func__);
     302        return false;
     303    }
     304
     305    psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
     306    return true;
     307}
    230308
    231309/******************************************************************************
     
    233311If there was a problem with the calculation, this routine sets stats->max and stats->min to NAN.  Return the
    234312number of valid values in the vector (those not masked or outside the specified range.
     313XXX : using the method below, with a single loop for various options
     314      costs only a small amount and is much easier to debug. running 10000 tests
     315      of 1000 point vectors for the two methods gives:
     316                     separate    single
     317(mask: 0, range: 0):  0.101 sec  0.149 sec
     318(mask: 1, range: 0):  0.125 sec  0.160 sec
     319(mask: 0, range: 0):  0.179 sec  0.208 sec
     320(mask: 1, range: 0):  0.200 sec  0.244 sec
    235321 *****************************************************************************/
    236322static long vectorMinMax(const psVector* myVector,
     
    244330    psF32 min = PS_MAX_F32;             // The calculated minimum
    245331    psF32 *vector = myVector->data.F32; // Dereference the vector
    246     psU8 *mask = NULL;                  // Dereference the mask
    247     if (maskVector) {
    248         mask = maskVector->data.U8;
    249     }
     332
    250333    int num = myVector->n;              // Number of values
    251334    int numValid = 0;                   // Number of valid values
    252335
    253     // If PS_STAT_USE_RANGE is requested, then we enter a different loop.
    254     if (stats->options & PS_STAT_USE_RANGE) {
    255         if (mask) {
    256             // Need to check range and mask
    257             for (long i = 0; i < num; i++) {
    258                 if (!(maskVal & mask[i]) && vector[i] >= stats->min && vector[i] <= stats->max) {
    259                     numValid++;
    260                     if (vector[i] > max) {
    261                         max = vector[i];
    262                     }
    263                     if (vector[i] < min) {
    264                         min = vector[i];
    265                     }
    266                 }
    267             }
    268         } else {
    269             // Need to check range
    270             for (long i = 0; i < num; i++) {
    271                 if (vector[i] >= stats->min && vector[i] <= stats->max) {
    272                     numValid++;
    273                     if (vector[i] > max) {
    274                         max = vector[i];
    275                     }
    276                     if (vector[i] < min) {
    277                         min = vector[i];
    278                     }
    279                 }
    280             }
    281         }
    282     } else {
    283         if (mask) {
    284             // Need to check mask
    285             for (long i = 0; i < num; i++) {
    286                 if (!(maskVal & mask[i])) {
    287                     numValid++;
    288                     if (vector[i] > max) {
    289                         max = vector[i];
    290                     }
    291                     if (vector[i] < min) {
    292                         min = vector[i];
    293                     }
    294                 }
    295             }
    296         } else {
    297             // No checks
    298             for (long i = 0; i < num; i++) {
    299                 if (vector[i] > max) {
    300                     max = vector[i];
    301                 }
    302                 if (vector[i] < min) {
    303                     min = vector[i];
    304                 }
    305             }
    306             numValid = num;
    307         }
    308     }
    309 
     336    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8;
     337    bool useRange = stats->options & PS_STAT_USE_RANGE;
     338
     339    for (long i = 0; i < num; i++) {
     340        // Check if the data is with the specified range
     341        if (useRange && (vector[i] < stats->min))
     342            continue;
     343        if (useRange && (vector[i] > stats->max))
     344            continue;
     345        if (maskData && (maskData[i] & maskVal))
     346            continue;
     347        numValid++;
     348        max = PS_MAX (vector[i], max);
     349        min = PS_MIN (vector[i], min);
     350    }
    310351
    311352    if (numValid == 0) {
     
    685726
    686727/******************************************************************************
    687 vectorClippedStats(myVector, errors, maskVector, maskVal, stats): calculates the
     728vectorClippedStats(myVector, errors, maskInput, maskValInput, stats): calculates the
    688729clipped stats (mean or stdev) of the input vector.
    689730 
    690731Inputs
    691732    myVector
    692     maskVector
    693     maskVal
     733    maskInput
     734    maskValInput
    694735    stats
    695736Returns
     
    698739static bool vectorClippedStats(const psVector* myVector,
    699740                               const psVector* errors,
    700                                psVector* maskVector,
    701                                psMaskType maskVal,
     741                               psVector* maskInput,
     742                               psMaskType maskValInput,
    702743                               psStats* stats
    703744                              )
     
    721762
    722763    // We copy the mask vector, to preserve the original
     764    psMaskType maskVal = MASK_MARK | maskValInput;
    723765    psVector *tmpMask = psVectorAlloc(myVector->n, PS_TYPE_U8);
    724766    psVectorInit(tmpMask, 0);
    725     if (maskVector) {
     767    if (maskInput) {
    726768        for (long i = 0; i < myVector->n; i++) {
    727             if (maskVector->data.U8[i] & maskVal) {
    728                 tmpMask->data.U8[i] = 0xff;
     769            if (maskInput->data.U8[i] & maskValInput) {
     770                tmpMask->data.U8[i] = maskVal;
    729771            }
    730772        }
     
    10121054    psF32 x = coords->data.F32[0];
    10131055    psF32 mean = params->data.F32[0];
    1014     psF32 stdev = params->data.F32[1];
    1015 
    1016     psF32 gauss = psGaussian(x, mean, stdev, false);
     1056    psF32 var = params->data.F32[1];
     1057    psF32 dx = (x - mean);
     1058
     1059    psF32 gauss = exp (-0.5*PS_SQR(dx)/var);
    10171060    if (deriv) {
    10181061        PS_ASSERT_VECTOR_SIZE(deriv, (long)2, NAN);
    10191062        PS_ASSERT_VECTOR_TYPE(deriv, PS_TYPE_F32, NAN);
    1020         psF32 tmp = (x - mean) * gauss;
    1021         deriv->data.F32[0] = tmp / (stdev * stdev);
    1022         deriv->data.F32[1] = (x - mean) * tmp / (stdev * stdev * stdev);
     1063        psF32 tmp = dx * gauss;
     1064        deriv->data.F32[0] = tmp / var;
     1065        deriv->data.F32[1] = tmp * dx / (var * var);
    10231066    }
    10241067
     
    10281071}
    10291072
     1073/*
     1074 * vectorFittedStats requires guess for fittedMean and fittedStdev
     1075 * robustN50 should also be set
     1076 */
     1077static bool vectorFittedStats (const psVector* myVector,
     1078                               const psVector* errors,
     1079                               psVector* mask,
     1080                               psMaskType maskVal,
     1081                               psStats* stats)
     1082{
     1083
     1084    psTrace("psLib.math", 6, "The guess mean  is %f.\n", stats->fittedMean);
     1085    psTrace("psLib.math", 6, "The guess stdev is %f.\n", stats->fittedStdev);
     1086
     1087    psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
     1088
     1089    psScalar tmpScalar;                 // Temporary scalar variable, for p_psVectorBinDisect
     1090    tmpScalar.type.type = PS_TYPE_F32;
     1091
     1092    // construct a histogram with (sigma/10 < binsize < sigma)
     1093    // set roughly so that the lowest bins have about 2 cnts
     1094    // Nsmallest ~ N50 / (4*dN))
     1095    psF32 dN = PS_MAX (1, PS_MIN (10, stats->robustN50 / 8));
     1096    psF32 newBinSize = stats->fittedStdev / dN;
     1097
     1098    // Determine the min/max of the vector (which prior outliers masked out)
     1099    int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
     1100    float min = statsMinMax->min;
     1101    float max = statsMinMax->max;
     1102    if (numValid == 0 || isnan(min) || isnan(max)) {
     1103        psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
     1104        psFree(statsMinMax);
     1105        psFree(mask);
     1106        psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1107        return false;
     1108    }
     1109
     1110    // Calculate the number of bins.
     1111    long numBins = (max - min) / newBinSize;
     1112    psTrace("psLib.math", 6, "The new min/max values are (%f, %f).\n", min, max);
     1113    psTrace("psLib.math", 6, "The new bin size is %f.\n", newBinSize);
     1114    psTrace("psLib.math", 6, "The numBins is %ld\n", numBins);
     1115
     1116    psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
     1117    histogram = psVectorHistogram(histogram, myVector, errors, mask, maskVal);
     1118    if (psTraceGetLevel("psLib.math") >= 8) {
     1119        PS_VECTOR_PRINT_F32(histogram->nums);
     1120    }
     1121
     1122    long binMin = 0;                // Low bin to check
     1123    long binMax = 0;                // High bin to check
     1124
     1125    // Fit a Gaussian to the bins in the requested range about the guess mean
     1126    // min,max overrides clipSigma
     1127    psF32 maxFitSigma = 2.0;
     1128    if (isfinite(stats->clipSigma)) {
     1129        maxFitSigma = fabs(stats->clipSigma);
     1130    }
     1131    if (isfinite(stats->max)) {
     1132        maxFitSigma = fabs(stats->max);
     1133    }
     1134
     1135    psF32 minFitSigma = 2.0;
     1136    if (isfinite(stats->clipSigma)) {
     1137        minFitSigma = fabs(stats->clipSigma);
     1138    }
     1139    if (isfinite(stats->min)) {
     1140        minFitSigma = fabs(stats->min);
     1141    }
     1142
     1143    tmpScalar.data.F32 = stats->fittedMean - minFitSigma*stats->fittedStdev;
     1144    if (tmpScalar.data.F32 < min) {
     1145        binMin = 0;
     1146    } else {
     1147        binMin = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
     1148    }
     1149    tmpScalar.data.F32 = stats->fittedMean + maxFitSigma*stats->fittedStdev;
     1150    if (tmpScalar.data.F32 > max) {
     1151        binMax = histogram->bounds->n - 1;
     1152    } else {
     1153        binMax = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
     1154    }
     1155    if (binMin < 0 || binMax < 0) {
     1156        psError(PS_ERR_UNKNOWN, false, "Failed to calculate the -%f sigma : +%f sigma bins\n", minFitSigma, maxFitSigma);
     1157        psFree(statsMinMax);
     1158        psFree(histogram);
     1159        psFree(mask);
     1160        psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1161        return false;
     1162    }
     1163
     1164    // Generate the variables that will be used in the Gaussian fitting
     1165    // XXX EAM : we should test / guarantee that there are at least 3 (right?) bins
     1166    psVector *y = psVectorAlloc((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of coordinates
     1167    psArray *x = psArrayAlloc((1 + (binMax - binMin))); // Array of ordinates
     1168    for (long i = binMin, j = 0; i <= binMax ; i++, j++) {
     1169        y->data.F32[j] = histogram->nums->data.F32[i];
     1170        psVector *ordinate = psVectorAlloc(1, PS_TYPE_F32); // The ordinate value
     1171        ordinate->data.F32[0] = PS_BIN_MIDPOINT(histogram, i);
     1172        x->data[j] = ordinate;
     1173    }
     1174    if (psTraceGetLevel("psLib.math") >= 8) {
     1175        // XXX: Print the x array somehow.
     1176        PS_VECTOR_PRINT_F32(y);
     1177    }
     1178    psTrace("psLib.math", 6, "The clipped numBins is %ld\n", y->n);
     1179
     1180    // Normalize y to [0.0:1.0] (since the psMinimizeLMChi2Gauss1D() functions is [0.0:1.0])
     1181    // XXX EAM : why not just fit the normalization as well??
     1182    p_psNormalizeVectorRange(y, 0.0, 1.0);
     1183
     1184    // Fit a Gaussian to the data.
     1185    psMinimization *minimizer = psMinimizationAlloc(100, 0.01); // The minimizer information
     1186    psVector *params = psVectorAlloc(2, PS_TYPE_F32); // Parameters for the Gaussian
     1187    // Initial guess for the mean (index 0) and var (index 1).
     1188    params->data.F32[0] = stats->fittedMean;
     1189    params->data.F32[1] = PS_SQR(stats->fittedStdev);
     1190    if (psTraceGetLevel("psLib.math") >= 8) {
     1191        PS_VECTOR_PRINT_F32(params);
     1192        PS_VECTOR_PRINT_F32(y);
     1193    }
     1194    if (!psMinimizeLMChi2(minimizer, NULL, params, NULL, x, y, NULL, minimizeLMChi2Gauss1D)) {
     1195        psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
     1196        psFree(statsMinMax);
     1197        psFree(x);
     1198        psFree(y);
     1199        psFree(minimizer);
     1200        psFree(params);
     1201        psFree(mask);
     1202        psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1203        return false;
     1204    }
     1205    if (psTraceGetLevel("psLib.math") >= 8) {
     1206        PS_VECTOR_PRINT_F32(params);
     1207    }
     1208
     1209    // The fitted mean is the Gaussian mean.
     1210    stats->fittedMean = params->data.F32[0];
     1211    psTrace("psLib.math", 6, "The fitted mean is %f.\n", stats->fittedMean);
     1212
     1213    // The fitted standard deviation
     1214    stats->fittedStdev = sqrt(params->data.F32[1]);
     1215    psTrace("psLib.math", 6, "The fitted stdev is %f.\n", stats->fittedStdev);
     1216
     1217    // Clean up after fitting
     1218    psFree (histogram);
     1219    psFree (x);
     1220    psFree (y);
     1221    psFree (minimizer);
     1222    psFree (params);
     1223    psFree (statsMinMax);
     1224    return true;
     1225}
     1226
     1227
    10301228/******************************************************************************
    1031 vectorRobustStats(myVector, maskVector, maskVal, stats): This is the new
     1229vectorRobustStats(myVector, maskInput, maskValInput, stats): This is the new
    10321230version of the robust stats routine.
    10331231 
     
    10491247static bool vectorRobustStats(const psVector* myVector,
    10501248                              const psVector* errors,
    1051                               psVector* maskVector,
    1052                               psMaskType maskVal,
     1249                              psVector* maskInput,
     1250                              psMaskType maskValInput,
    10531251                              psStats* stats)
    10541252{
     
    10611259    tmpScalar.type.type = PS_TYPE_F32;
    10621260
     1261    // we need to generate an internal mask and maskVal which ensures a bit is set
     1262    // and tested even if there is no supplied mask (and/or the maskVal is 0)
     1263    // XXX this would be better if we had globally defined mask values
     1264    psMaskType maskVal = MASK_MARK | maskValInput;
    10631265    psVector *mask = psVectorAlloc(myVector->n, PS_TYPE_MASK); // The actual mask we will use
    10641266    psVectorInit(mask, 0);
    1065     if (maskVector) {
     1267    if (maskInput) {
    10661268        for (long i = 0; i < myVector->n; i++) {
    1067             if (maskVector->data.U8[i] & maskVal) {
    1068                 mask->data.U8[i] = 0xff;
     1269            if (maskInput->data.U8[i] & maskValInput) {
     1270                mask->data.U8[i] = maskVal;
    10691271            }
    10701272        }
     
    10981300        psTrace("psLib.math", 6, "Data min/max is (%.2f, %.2f)\n", min, max);
    10991301
    1100         // If all data points have the same value, then we set the appropiate members of stats and return.
     1302        // If all data points have the same value, then we set the appropriate members of stats and return.
    11011303        if (fabs(max - min) <= FLT_EPSILON) {
    11021304            psTrace("psLib.math", 5, "All data points have the same value: %f.\n", min);
     
    12261428            return false;
    12271429        }
    1228 
     1430        // XXXX we can probably just use the bin values to get sigma without interpolating...
    12291431        // ADD step 4b: Interpolate Sigma to find these two positions exactly: these are the 1sigma positions.
    12301432        #if 0
     
    13041506            for (long i = 0 ; i < myVector->n ; i++) {
    13051507                if ((myVector->data.F32[i] < medianLo) || (myVector->data.F32[i] > medianHi)) {
    1306                     mask->data.U8[i] = 0xff;
     1508                    mask->data.U8[i] |= MASK_MARK;
    13071509                    psTrace("psLib.math", 6, "Masking element %ld is %f\n", i, myVector->data.F32[i]);
    13081510                }
     
    13781580    psTrace("psLib.math", 6, "The robustN50 is %ld.\n", N50);
    13791581
    1380 
    13811582    // Fitted statistics
    13821583    if (stats->options & PS_STAT_FITTED_MEAN || stats->options & PS_STAT_FITTED_STDEV) {
    1383         // New algorithm for calculated mean and stdev.
    1384 
    1385         // XXX: Where is this documented?
    1386         psF32 dN = 0.17 * N50;
    1387         if (dN < 1.0) {
    1388             dN = 1.0;
    1389         } else if (dN > 4.0) {
    1390             dN = 4.0;
    1391         }
    1392         psF32 newBinSize = sigma / dN;
    1393 
    1394         // Determine the min/max of the vector (which prior outliers masked out)
    1395         int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
    1396         min = statsMinMax->min;
    1397         max = statsMinMax->max;
    1398         if (numValid == 0 || isnan(min) || isnan(max)) {
    1399             psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
    1400             psFree(statsMinMax);
    1401             psFree(mask);
    1402             psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1403             return false;
    1404         }
    1405 
    1406         // Calculate the number of bins.
    1407         long numBins = (max - min) / newBinSize;
    1408         psTrace("psLib.math", 6, "The new min/max values are (%f, %f).\n", min, max);
    1409         psTrace("psLib.math", 6, "The new bin size is %f.\n", newBinSize);
    1410         psTrace("psLib.math", 6, "The numBins is %ld\n", numBins);
    1411 
    1412         psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
    1413         histogram = psVectorHistogram(histogram, myVector, errors, mask, maskVal);
    1414         if (psTraceGetLevel("psLib.math") >= 8) {
    1415             PS_VECTOR_PRINT_F32(histogram->nums);
    1416         }
    1417 
    1418         // Smooth the resulting histogram with a Gaussian with sigma_s = 1 bin.
    1419         psVector *smoothed = vectorSmoothHistGaussian(histogram, newBinSize); // Smoothed histogram
    1420         if (psTraceGetLevel("psLib.math") >= 8) {
    1421             PS_VECTOR_PRINT_F32(smoothed);
    1422         }
    1423 
    1424         // Find the bin with the peak value in the range 2 sigma of the robust histogram median.
    1425         long binMin = 0;                // Low bin to check
    1426         long binMax = 0;                // High bin to check
    1427         tmpScalar.data.F32 = stats->robustMedian - (2.0 * sigma);
    1428         if (tmpScalar.data.F32 <= histogram->bounds->data.F32[0]) {
    1429             binMin = 0;
    1430         } else {
    1431             binMin = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
    1432         }
    1433 
    1434         tmpScalar.data.F32 = stats->robustMedian + (2.0 + sigma);
    1435         if (tmpScalar.data.F32 >= histogram->bounds->data.F32[histogram->bounds->n-1]) {
    1436             binMax = histogram->bounds->n-1;
    1437         } else {
    1438             binMax = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
    1439         }
    1440         if ((binMin < 0) || (binMax < 0)) {
    1441             psError(PS_ERR_UNKNOWN, false, "Failed to calculate the +/- 2.0 * sigma bins\n");
    1442             psFree(statsMinMax);
    1443             psFree(histogram);
    1444             psFree(mask);
    1445             psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1446             return false;
    1447         }
    1448 
    1449         long binNum = binMin;           // Bin number containing the peak
    1450         psF32 binMaxNums = smoothed->data.F32[binNum]; // The peak value
    1451         for (psS32 i = binMin+1 ; i <= binMax ; i++) {
    1452             if (smoothed->data.F32[i] > binMaxNums) {
    1453                 binNum = i;
    1454                 binMaxNums = smoothed->data.F32[i];
    1455             }
    1456         }
    1457         psTrace("psLib.math", 6, "The peak bin is %ld, with %f data.n", binNum, binMaxNums);
    1458 
    1459         // Fit a Gaussian to the bins in the range 20 sigma of the robust histogram median.
    1460         tmpScalar.data.F32 = stats->robustMedian - (20.0 * sigma);
    1461         if (tmpScalar.data.F32 < min) {
    1462             binMin = 0;
    1463         } else {
    1464             binMin = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
    1465         }
    1466         tmpScalar.data.F32 = stats->robustMedian + (20.0 * sigma);
    1467         if (tmpScalar.data.F32 > max) {
    1468             binMax = smoothed->n - 1;
    1469         } else {
    1470             binMax = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
    1471         }
    1472         if (binMin < 0 || binMax < 0) {
    1473             psError(PS_ERR_UNKNOWN, false, "Failed to calculate the +/- 20.0 * sigma bins\n");
    1474             psFree(statsMinMax);
    1475             psFree(histogram);
    1476             psFree(mask);
    1477             psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1478             return false;
    1479         }
    1480 
    1481         // Generate the variables that will be used in the Gaussian fitting
    1482         psVector *y = psVectorAlloc((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of coordinates
    1483         psArray *x = psArrayAlloc((1 + (binMax - binMin))); // Array of ordinates
    1484         for (long i = binMin, j = 0; i <= binMax ; i++, j++) {
    1485             y->data.F32[j] = smoothed->data.F32[i];
    1486             psVector *ordinate = psVectorAlloc(1, PS_TYPE_F32); // The ordinate value
    1487             ordinate->data.F32[0] = PS_BIN_MIDPOINT(histogram, i);
    1488             x->data[j] = ordinate;
    1489         }
    1490         if (psTraceGetLevel("psLib.math") >= 8) {
    1491             // XXX: Print the x array somehow.
    1492             PS_VECTOR_PRINT_F32(y);
    1493         }
    1494         psFree(smoothed);
    1495         psFree(histogram);
    1496 
    1497         numValid = vectorMinMax(y, NULL, 0, statsMinMax); // Number of valid values
    1498         min = statsMinMax->min;
    1499         max = statsMinMax->max;
    1500         if (numValid == 0 || isnan(min) || isnan(max)) {
    1501             psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
    1502             psFree(mask);
    1503             psFree(histogram);
    1504             psFree(statsMinMax);
    1505             psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1506             return false;
    1507         }
    1508 
    1509         // Normalize y to [0.0:1.0] (since the psMinimizeLMChi2Gauss1D() functions is [0.0:1.0])
    1510         p_psNormalizeVectorRange(y, 0.0, 1.0);
    1511 
    1512         // Fit a Gaussian to the data.
    1513         psMinimization *minimizer = psMinimizationAlloc(100, 0.01); // The minimizer information
    1514         psVector *params = psVectorAlloc(2, PS_TYPE_F32); // Parameters for the Gaussian
    1515         // Initial guess for the mean (index 0) and standard dev (index 1).
    1516         params->data.F32[0] = stats->robustMedian;
    1517         params->data.F32[1] = sigma;
    1518         if (psTraceGetLevel("psLib.math") >= 8) {
    1519             PS_VECTOR_PRINT_F32(params);
    1520             PS_VECTOR_PRINT_F32(y);
    1521         }
    1522         if (!psMinimizeLMChi2(minimizer, NULL, params, NULL, x, y, NULL, minimizeLMChi2Gauss1D)) {
    1523             psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
    1524             psFree(statsMinMax);
    1525             psFree(x);
    1526             psFree(y);
    1527             psFree(minimizer);
    1528             psFree(params);
    1529             psFree(mask);
    1530             psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1531             return false;
    1532         }
    1533         if (psTraceGetLevel("psLib.math") >= 8) {
    1534             PS_VECTOR_PRINT_F32(params);
    1535         }
    1536 
    1537         // The fitted mean is the Gaussian mean.
    1538         stats->fittedMean = params->data.F32[0];
    1539         psTrace("psLib.math", 6, "The fitted mean is %f.\n", params->data.F32[0]);
    1540 
    1541         // The fitted standard deviation, SIGMA_r is determined by subtracting the smoothing scale in
    1542         // quadrature: SIGMA_r^2 = SIGMA^2 - sigma_s^2
    1543         stats->fittedStdev = sqrt(PS_SQR(params->data.F32[1]) - PS_SQR(newBinSize));
    1544         psTrace("psLib.math", 6, "The fitted stdev is %f.\n", stats->fittedStdev);
    1545 
    1546         // Clean up after fitting
    1547         psFree(x);
    1548         psFree(y);
    1549         psFree(minimizer);
    1550         psFree(params);
     1584        stats->fittedStdev = stats->robustStdev;  // pass the guess sigma
     1585        stats->fittedMean = stats->robustMedian;  // pass the guess mean
     1586        vectorFittedStats (myVector, errors, mask, maskVal, stats);
     1587        // if there is a large swing in sigma, try a second time
     1588        if ((stats->fittedStdev/stats->robustStdev) < 0.75) {
     1589            vectorFittedStats (myVector, errors, mask, maskVal, stats);
     1590        }
    15511591    }
    15521592
     
    19141954
    19151955/******************************************************************************
    1916 psVectorStats(myVector, maskVector, maskVal, stats): this is the public API
     1956psVectorStats(in, mask, maskVal, stats): this is the public API
    19171957function which calls the above private stats functions based on what bits
    19181958were set in stats->options.
    19191959 
    19201960Inputs
    1921     myVector
    1922     maskVector
     1961    in
     1962    mask
    19231963    maskVal
    19241964    stats
Note: See TracChangeset for help on using the changeset viewer.