IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 14272


Ignore:
Timestamp:
Jul 17, 2007, 1:33:09 PM (19 years ago)
Author:
Paul Price
Message:

Adding input/output for weight maps (variances). Using local
functions instead of psVectorStats because it is slow, and it assumes
that the input weights are errors, and I don't want to have to do
square-roots all over the place, etc.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/imcombine/pmStack.c

    r14126 r14272  
    88 *  @author GLG, MHPCC
    99 *
    10  *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
    11  *  @date $Date: 2007-07-11 01:32:41 $
     10 *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
     11 *  @date $Date: 2007-07-17 23:33:09 $
    1212 *
    1313 *  Copyright 2004-2007 Institute for Astronomy, University of Hawaii
     
    4141    psVector *pixels;                   // Pixel values
    4242    psVector *masks;                    // Pixel masks
    43     psStats *stats;                     // Statistics
     43    psVector *weights;                  // Pixel weights
     44    psVector *sort;                     // Buffer for sorting (to get a robust estimator of the standard dev)
    4445} combineBuffer;
    4546
     
    4849    psFree(buffer->pixels);
    4950    psFree(buffer->masks);
    50     psFree(buffer->stats);
     51    psFree(buffer->weights);
     52    psFree(buffer->sort);
    5153    return;
    5254}
     
    6163    buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32);
    6264    buffer->masks = psVectorAlloc(numImages, PS_TYPE_MASK);
    63     buffer->stats = psStatsAlloc(stat);
     65    buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32);
     66    buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32);
    6467
    6568    return buffer;
     
    7780
    7881
     82// Generate a mean value for the combination
     83// Not using psVectorStats because it assumes that the weights are errors, and weights by 1/error^2
     84static bool combinationMean(float *mean, // Mean value, to return
     85                            const psVector *values, // Values to combine
     86                            const psVector *weights, // Weights to apply
     87                            const psVector *masks, // Mask to apply
     88                            psMaskType maskVal // Value to mask
     89                            )
     90{
     91    assert(values && weights && masks);
     92    assert(values->n == weights->n);
     93    assert(values->n == masks->n);
     94    assert(values->type.type == PS_TYPE_F32);
     95    assert(weights->type.type == PS_TYPE_F32);
     96    assert(masks->type.type == PS_TYPE_MASK);
     97
     98    float sumValueWeight = 0.0; // Sum of the value multiplied by the weight
     99    float sumWeight = 0.0;      // Sum of the weights
     100    for (int i = 0; i < values->n; i++) {
     101        if (!(masks->data.PS_TYPE_MASK_DATA[i] & maskVal)) {
     102            sumValueWeight += values->data.F32[i] * weights->data.F32[i];
     103            sumWeight += weights->data.F32[i];
     104        }
     105    }
     106    if (sumWeight <= 0) {
     107        return false;
     108    }
     109
     110    *mean = sumValueWeight / sumWeight;
     111    return true;
     112}
     113
     114// Generate a standard deviation for the combination
     115// Not using psVectorStats because it has additional allocations which slow things down
     116static bool combinationStdev(float *stdev, // Mean value, to return
     117                             const psVector *values, // Values to combine
     118                             const psVector *masks, // Mask to apply
     119                             psMaskType maskVal, // Value to mask
     120                             psVector *sortBuffer // Buffer for sorting
     121                             )
     122{
     123    assert(values && masks);
     124    assert(values->n == masks->n);
     125    assert(values->type.type == PS_TYPE_F32);
     126    assert(masks->type.type == PS_TYPE_MASK);
     127    assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32);
     128
     129    int num = 0;            // Number of valid values
     130    for (int i = 0; i < values->n; i++) {
     131        if (!(masks->data.PS_TYPE_MASK_DATA[i] & maskVal)) {
     132            sortBuffer->data.F32[num++] = values->data.F32[i];
     133        }
     134    }
     135    sortBuffer->n = num;
     136    if (!psVectorSortInPlace(sortBuffer)) {
     137        return false;
     138    }
     139    *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] - sortBuffer->data.F32[(int)(0.25 * num)]);
     140
     141    return true;
     142}
     143
     144// Generate a variance value for the combination
     145static bool combinationVariance(float *variance, // Variance value, to return
     146                                const psVector *variances, // Pixel variances to combine
     147                                const psVector *weights, // Image weights to apply
     148                                const psVector *masks, // Mask to apply
     149                                psMaskType maskVal // Value to mask
     150                                )
     151{
     152    assert(variances && weights && masks);
     153    assert(variances->n == weights->n);
     154    assert(variances->n == masks->n);
     155    assert(variances->type.type == PS_TYPE_F32);
     156    assert(weights->type.type == PS_TYPE_F32);
     157    assert(masks->type.type == PS_TYPE_MASK);
     158
     159    // Get the variance in the combination.  We're not using the input pixel variances to generate a
     160    // weighted average for the pixel flux (because that introduces systematic biases), so the variance
     161    // of the output pixel value should simply be:
     162    //     simga^2 = sum(weight_i^2 * sigma_i^2) / (sum(weight_i))^2
     163    // This reduces, when the weights are all identically unity, to:
     164    //     variance_combination = sum(variance_i) / N^2
     165    // and if the variances are all equal:
     166    //     variance_combination = variance_individual / N
     167    // which makes sense --- the standard deviation of the combination is reduced by a factor of sqrt(N).
     168
     169    float sumWeights = 0.0;             // Sum of the global weights
     170    float sumVarianceWeights = 0.0;     // Sum of the pixel variances multiplied by the global weights
     171    for (int i = 0; i < variances->n; i++) {
     172        if (!(masks->data.PS_TYPE_MASK_DATA[i] & maskVal)) {
     173            sumVarianceWeights += variances->data.F32[i] * PS_SQR(weights->data.F32[i]);
     174            sumWeights += weights->data.F32[i];
     175        }
     176    }
     177
     178    if (sumWeights <= 0) {
     179        return false;
     180    }
     181
     182    *variance = sumVarianceWeights / PS_SQR(sumWeights);
     183    return true;
     184}
     185
    79186// Given a stack of images, combine with optional rejection.
    80187// Pixels in the stack that are rejected are marked for subsequent
    81188static bool combinePixels(psImage *image, // Combined image, for output
    82189                          psImage *mask, // Combined mask, for output
     190                          psImage *weight, // Combined variance map, for output
    83191                          const psArray *inputs, // Stack data
    84                           const psVector *weights, // Weights for data, or NULL
     192                          const psVector *weights, // Global (single value) weights for data, or NULL
    85193                          const psVector *reject, // Indices of pixels to reject, or NULL
    86194                          int x, int y, // Coordinates of interest
     
    104212    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
    105213    psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest
    106     psStats *stats = buffer->stats;     // Statistics
    107 
     214    psVector *pixelWeights = buffer->weights; // Weights for the pixel of interest
     215    psVector *sort = buffer->sort;      // Sort buffer
    108216
    109217    // Extract the pixel and mask data
     
    112220        pmStackData *data = inputs->data[i]; // Stack data of interest
    113221        psImage *image = data->sky->image; // Image of interest
     222        psImage *weight = data->sky->weight; // Weight map of interest
    114223        psImage *mask = data->sky->mask; // Mask of interest
    115224        pixelData->data.F32[i] = image->data.F32[y][x];
     225        pixelWeights->data.F32[i] = weight->data.F32[y][x];
    116226        pixelMasks->data.PS_TYPE_MASK_DATA[i] = mask->data.PS_TYPE_MASK_DATA[y][x];
    117227        if (pixelMasks->data.PS_TYPE_MASK_DATA[i] & maskVal) {
     
    122232        // Catch the case where everything is masked
    123233        image->data.F32[y][x] = NAN;
     234        if (weight) {
     235            weight->data.F32[y][x] = NAN;
     236        }
    124237        mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
    125238        return false;
     
    134247    // Record the value derived with no clipping, because pixels rejected using the harsh clipping applied in
    135248    // the first pass might later be accepted.
    136     stats->options |= PS_STAT_SAMPLE_MEAN;
    137     if (!psVectorStats(stats, pixelData, weights, pixelMasks, maskVal)) {
    138         // Can't do anything about an error except give it a NAN and mask
    139         psErrorClear();
     249    float mean, variance;               // Mean and variance of the combination
     250    if (!combinationMean(&mean, pixelData, weights, pixelMasks, maskVal) ||
     251        (weight && !combinationVariance(&variance, weights, pixelWeights, pixelMasks, maskVal))) {
    140252        image->data.F32[y][x] = NAN;
    141253        mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
    142         return false;
    143     }
    144     image->data.F32[y][x] = stats->sampleMean;
     254        if (weight) {
     255            weight->data.F32[y][x] = NAN;
     256        }
     257        return false;
     258    }
     259
     260    image->data.F32[y][x] = mean;
     261    if (weight) {
     262        weight->data.F32[y][x] = variance;
     263    }
    145264    mask->data.PS_TYPE_MASK_DATA[y][x] = 0;
    146265
    147     stats->options &= ~ PS_STAT_SAMPLE_MEAN;
    148 
     266    // The clipping that follows is solely to identify suspect pixels.
     267    // These suspect pixels will be inspected in more detail by other functions.
    149268    long numClipped = LONG_MAX;         // Number of pixels clipped
    150269    psMaskType ignore = maskVal | bad;  // Ignore values with this mask value
    151270    for (int i = 0; i < numIter && numClipped > 0; i++) {
    152 #if 1
    153         float mean = stats->sampleMedian;
    154         float stdev = 0.74 * (stats->sampleUQ - stats->sampleLQ); // Rough estimate of the standard deviation
    155 #else
    156         float mean = stats->robustMedian;
    157         float stdev = stats->robustStdev;
    158 #endif
     271        float stdev;                    // Standard deviation of the combination, for rejection
     272        // Only get the mean if it's not the first iteration (because we got the mean earlier)
     273        if ((i != 0 && !combinationMean(&mean, pixelData, weights, pixelMasks, maskVal)) ||
     274            !combinationStdev(&stdev, pixelData, pixelMasks, maskVal, sort)) {
     275            image->data.F32[y][x] = NAN;
     276            mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
     277            weight->data.F32[y][x] = NAN;
     278            return false;
     279        }
     280
    159281        float limit = rej * stdev; // Rejection limit
    160282        numClipped = 0;
     
    165287            float diff = pixelData->data.F32[j] - mean;
    166288            if (fabsf(diff) > limit) {
     289                // Add the pixel as one to inspect
    167290                pmStackData *data = inputs->data[j]; // Stack data of interest
    168291                data->pixels = psPixelsAdd(data->pixels, PIXEL_LIST_BUFFER, x, y);
     292                // Mask it so it's not considered in other iterations within this function
    169293                pixelMasks->data.PS_TYPE_MASK_DATA[j] |= bad;
    170294                numClipped++;
    171295            }
    172296        }
    173         if (!psVectorStats(stats, pixelData, weights, pixelMasks, maskVal)) {
    174             // Can't do anything about an error except give it a NAN and mask
    175             psErrorClear();
    176             image->data.F32[y][x] = NAN;
    177             mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
    178             return false;
    179         }
    180297    }
    181298
     
    186303// Ensure the input array of pmStackData is valid, and get some details out of it
    187304static bool validateInputData(bool *haveDetector, // Do we have the detector images?
    188                            bool *haveSky, // Do we have the sky images?
    189                            bool *havePixels, // Do we have lists of pixels?
    190                            int *num,    // Number of inputs
    191                            int *numCols, int *numRows, // Size of (sky) images
    192                            psArray *input // Input array of pmStackData to validate
     305                              bool *haveSky, // Do we have the sky images?
     306                              bool *haveSkyWeights, // Do we have weights in the sky images?
     307                              bool *havePixels, // Do we have lists of pixels?
     308                              int *num,    // Number of inputs
     309                              int *numCols, int *numRows, // Size of (sky) images
     310                              psArray *input // Input array of pmStackData to validate
    193311    )
    194312{
     
    203321            PS_ASSERT_IMAGE_NON_NULL(data->detector->image, false);
    204322            PS_ASSERT_IMAGE_NON_NULL(data->detector->mask, false);
     323            PS_ASSERT_IMAGES_SIZE_EQUAL(data->detector->image, data->detector->mask, false);
     324            PS_ASSERT_IMAGE_TYPE(data->detector->image, PS_TYPE_F32, false);
     325            PS_ASSERT_IMAGE_TYPE(data->detector->mask, PS_TYPE_MASK, false);
    205326        }
    206327        *haveSky = (data->sky != NULL);
     328        *haveSkyWeights = false;
    207329        if (*haveSky) {
    208330            PS_ASSERT_IMAGE_NON_NULL(data->sky->image, false);
     331            PS_ASSERT_IMAGE_TYPE(data->sky->image, PS_TYPE_F32, false);
    209332            PS_ASSERT_IMAGE_NON_NULL(data->sky->mask, false);
     333            PS_ASSERT_IMAGE_TYPE(data->sky->mask, PS_TYPE_MASK, false);
     334            PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->mask, false);
    210335            *numCols = data->sky->image->numCols;
    211336            *numRows = data->sky->image->numRows;
     337            if (data->sky->weight) {
     338                *haveSkyWeights = true;
     339                PS_ASSERT_IMAGE_NON_NULL(data->sky->weight, false);
     340                PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->weight, false);
     341                PS_ASSERT_IMAGE_TYPE(data->sky->weight, PS_TYPE_F32, false);
     342            }
    212343        }
    213344        *havePixels = (data->pixels != NULL);
     
    243374            PS_ASSERT_IMAGE_SIZE(data->sky->image, *numCols, *numRows, false);
    244375            PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->mask, false);
     376            if (*haveSkyWeights) {
     377                PS_ASSERT_IMAGE_NON_NULL(data->sky->weight, false);
     378                PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->weight, false);
     379                PS_ASSERT_IMAGE_TYPE(data->sky->weight, PS_TYPE_F32, false);
     380            }
    245381        }
    246382    }
     
    412548    bool haveDetector;                  // Do we have the detector images?
    413549    bool haveSky;                       // Do we have the sky images?
     550    bool haveSkyWeights;                // Do we have the sky weight maps?
    414551    bool havePixels;                    // Do we have lists of pixels?
    415552    int num;                            // Number of inputs
    416553    int numCols, numRows;               // Size of (sky) images
    417     if (!validateInputData(&haveDetector, &haveSky, &havePixels, &num, &numCols, &numRows, input)) {
     554    if (!validateInputData(&haveDetector, &haveSky, &haveSkyWeights, &havePixels, &num,
     555                           &numCols, &numRows, input)) {
    418556        return false;
    419557    }
     
    461599        psImage *combinedImage = combined->image; // Combined image
    462600        psImage *combinedMask = combined->mask; // Combined mask
     601        psImage *combinedWeight = combined->weight; // Combined mask
    463602
    464603        psArray *pixelMap = pixelMapGenerate(input, numCols, numRows); // Map of pixels to source
     
    472611            int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of interest
    473612            psVector *reject = pixelMapQuery(pixelMap, x, y); // Inspect these images closely
    474             combinePixels(combinedImage, combinedMask, input, weights, reject, x, y,
     613            combinePixels(combinedImage, combinedMask, combinedWeight, input, weights, reject, x, y,
    475614                          maskVal, bad, numIter, rej, buffer);
    476615        }
     
    491630        }
    492631
     632        psImage *combinedWeights = combined->weight; // Combined weight map
     633        if (haveSkyWeights && !combinedWeights) {
     634            combined->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     635            combinedWeights = combined->weight;
     636        }
     637
    493638        // Generate the pixel lists in which to place the rejected pixels
    494639        if (numIter != 0) {
     
    501646        for (int y = 0; y < numRows; y++) {
    502647            for (int x = 0; x < numCols; x++) {
    503                 combinePixels(combinedImage, combinedMask, input, weights, NULL, x, y,
     648                combinePixels(combinedImage, combinedMask, combinedWeights, input, weights, NULL, x, y,
    504649                              maskVal, bad, numIter, rej, buffer);
    505650            }
     
    538683    bool haveDetector;                  // Do we have the detector images?
    539684    bool haveSky;                       // Do we have the sky images?
     685    bool haveSkyWeights;                // Do we have the sky weight maps?
    540686    bool havePixels;                    // Do we have lists of pixels?
    541687    int num;                            // Number of inputs
    542688    int numCols, numRows;               // Size of (sky) images
    543     if (!validateInputData(&haveDetector, &haveSky, &havePixels, &num, &numCols, &numRows, input)) {
     689    if (!validateInputData(&haveDetector, &haveSky, &haveSkyWeights, &havePixels,
     690                           &num, &numCols, &numRows, input)) {
    544691        return false;
    545692    }
Note: See TracChangeset for help on using the changeset viewer.