IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Nov 9, 2009, 2:53:12 PM (17 years ago)
Author:
Paul Price
Message:

Merging branches/pap (unconvolved stacks, reworked combinePixels function into clearer roles, only throw out most variant pixel on each iteration, throw suspect pixels out as first rejection step) stack development. I think I've got everything, but not entirely sure, since I've already merged this branch once before (for dual convolution).

File:
1 edited

Legend:

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

    r25380 r26076  
    3333#define NUM_DIRECT_STDEV 5              // For less than this number of values, measure stdev directly
    3434
     35
    3536//#define TESTING                         // Enable test output
    36 //#define TEST_X 1085                     // x coordinate to examine
    37 //#define TEST_Y 3371                     // y coordinate to examine
     37//#define TEST_X 3122-1                     // x coordinate to examine
     38//#define TEST_Y 1028-1                     // y coordinate to examine
    3839
    3940
     
    4243typedef struct {
    4344    psVector *pixels;                   // Pixel values
    44     psVector *masks;                    // Pixel masks
    4545    psVector *variances;                // Pixel variances
    4646    psVector *weights;                  // Pixel weightings
    4747    psVector *sources;                  // Pixel sources (which image did they come from?)
    4848    psVector *limits;                   // Rejection limits
     49    psVector *suspects;                 // Pixel is suspect?
    4950    psVector *sort;                     // Buffer for sorting (to get a robust estimator of the standard dev)
    5051} combineBuffer;
     
    5354{
    5455    psFree(buffer->pixels);
    55     psFree(buffer->masks);
    5656    psFree(buffer->variances);
    5757    psFree(buffer->weights);
    5858    psFree(buffer->sources);
    5959    psFree(buffer->limits);
     60    psFree(buffer->suspects);
    6061    psFree(buffer->sort);
    6162    return;
     
    6970
    7071    buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32);
    71     buffer->masks = psVectorAlloc(numImages, PS_TYPE_VECTOR_MASK);
    7272    buffer->variances = psVectorAlloc(numImages, PS_TYPE_F32);
    7373    buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32);
    7474    buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16);
    7575    buffer->limits = psVectorAlloc(numImages, PS_TYPE_F32);
     76    buffer->suspects = psVectorAlloc(numImages, PS_TYPE_U8);
    7677    buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32);
    7778    return buffer;
     
    143144                                   float *stdev, // Standard deviation value, to return
    144145                                   const psVector *values, // Values to combine
    145                                    const psVector *masks, // Mask to apply
    146146                                   psVector *sortBuffer // Buffer for sorting
    147147                                   )
    148148{
    149149    assert(values);
    150     assert(!masks || values->n == masks->n);
    151150    assert(values->type.type == PS_TYPE_F32);
    152     assert(!masks || masks->type.type == PS_TYPE_VECTOR_MASK);
    153151    assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32);
    154152
    155     // Need to filter out clipped values
    156     int num = 0;            // Number of valid values
    157     for (int i = 0; i < values->n; i++) {
    158         if (!masks || !masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    159             sortBuffer->data.F32[num++] = values->data.F32[i];
    160         }
    161     }
    162     sortBuffer->n = num;
    163     if (!psVectorSortInPlace(sortBuffer)) {
     153    int num = values->n;                // Number of values
     154    sortBuffer = psVectorSortIndex(sortBuffer, values);
     155    if (!sortBuffer) {
     156        *median = NAN;
     157        *stdev = NAN;
    164158        return false;
    165159    }
     
    167161    if (num == 3) {
    168162        // Attempt to measure standard deviation with only three values (and one of those possibly corrupted)
    169         *median = sortBuffer->data.F32[1];
     163        *median = values->data.F32[sortBuffer->data.S32[1]];
    170164        if (stdev) {
    171             float diff1 = sortBuffer->data.F32[0] - *median;
    172             float diff2 = sortBuffer->data.F32[2] - *median;
     165            float diff1 = values->data.F32[sortBuffer->data.S32[0]] - *median;
     166            float diff2 = values->data.F32[sortBuffer->data.S32[2]] - *median;
    173167            // This factor of sqrt(2) might not be exact, but it's about right
    174168            *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2));
    175169        }
    176170    } else {
    177         *median = num % 2 ? sortBuffer->data.F32[num / 2] :
    178             (sortBuffer->data.F32[num / 2 - 1] + sortBuffer->data.F32[num / 2]) / 2.0;
     171        *median = num % 2 ? values->data.F32[sortBuffer->data.S32[num / 2]] :
     172            (values->data.F32[sortBuffer->data.S32[num / 2 - 1]] +
     173             values->data.F32[sortBuffer->data.S32[num / 2]]) / 2.0;
    179174        if (stdev) {
    180175            if (num <= NUM_DIRECT_STDEV) {
     
    182177                double sum = 0.0;
    183178                for (int i = 0; i < num; i++) {
    184                     sum += PS_SQR(sortBuffer->data.F32[i] - *median);
     179                    sum += PS_SQR(values->data.F32[sortBuffer->data.S32[i]] - *median);
    185180                }
    186181                *stdev = sqrt(sum / (double)(num - 1));
    187182            } else {
    188183                // Standard deviation from the interquartile range
    189                 *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] -
    190                                  sortBuffer->data.F32[(int)(0.25 * num)]);
     184                *stdev = 0.74 * (values->data.F32[sortBuffer->data.S32[(int)(0.75 * num)]] -
     185                                 values->data.F32[sortBuffer->data.S32[(int)(0.25 * num)]]);
    191186            }
    192187        }
     
    195190    return true;
    196191}
    197 
    198 #if 0
    199 // Return the weighted median for the pixels
    200 // This does not appear to produce as clean images as the weighted Olympic mean
    201 static float combinationWeightedMedian(const psVector *values, // Values to combine
    202                                        const psVector *weights, // Weights to combine
    203                                        const psVector *masks, // Mask to apply
    204                                        psVector *sortBuffer // Buffer for sorting
    205                                        )
    206 {
    207     double sumWeight = 0.0;             // Sum of weights
    208     for (int j = 0; j < values->n; j++) {
    209         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    210             continue;
    211         }
    212         sumWeight += weights->data.F32[j];
    213     }
    214 
    215     sortBuffer = psVectorSortIndex(sortBuffer, values);
    216     double target = sumWeight / 2.0;    // Target weight
    217 
    218     int dominant = -1;                  // Index of dominant value, if any
    219     double cumulativeWeight = 0.0;      // Sum of weights
    220     for (int j = 0; j < values->n; j++) {
    221         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    222             continue;
    223         }
    224         int index = sortBuffer->data.S32[j];  // Index of value of interest
    225         float weight = weights->data.F32[index]; // Weight for value of interest
    226         if (weight >= target) {
    227             // Get the weighted median of the rest
    228             dominant = index;
    229             sumWeight -= weight;
    230             target = sumWeight / 2.0;
    231             continue;
    232         }
    233         cumulativeWeight += weight;
    234         if (cumulativeWeight >= target) {
    235             float median = values->data.F32[index]; // Weighted median median
    236             if (dominant != -1) {
    237                 // In the case that a single value contains a disproportionate weight compared to the rest,
    238                 // we use a weighted mean between that dominant value and the weighted median of the rest.
    239                 return (values->data.F32[dominant] * weights->data.F32[dominant] + median * sumWeight) /
    240                     (weights->data.F32[dominant] + sumWeight);
    241             }
    242             return median;
    243         }
    244     }
    245 
    246     return NAN;
    247 }
    248 #endif
    249192
    250193// Return the weighted Olympic mean for the pixels
    251194static float combinationWeightedOlympic(const psVector *values, // Values to combine
    252195                                        const psVector *weights, // Weights to combine
    253                                         const psVector *masks, // Mask to apply
    254196                                        float frac, // Fraction to discard
    255197                                        psVector *sortBuffer // Buffer for sorting
    256198                                        )
    257199{
    258     int numGood = 0;                    // Number of good values
    259     for (int i = 0; i < values->n; i++) {
    260         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    261             continue;
    262         }
    263         numGood++;
    264     }
     200    int numGood = values->n;            // Number of good values
    265201
    266202    int numBad = frac * numGood + 0.5;  // Number of bad values
     
    272208    for (int i = 0, j = 0; i < values->n; i++) {
    273209        int index = sortBuffer->data.S32[i]; // Index of interest
    274         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[index]) {
    275             continue;
    276         }
    277210        j++;
    278211        if (j > high) {
     
    290223
    291224// Mark a pixel for inspection
    292 static inline void combineInspect(const psArray *inputs, // Stack data
    293                                   int x, int y, // Pixel
    294                                   int source // Source image index
    295                                   )
    296 {
     225// Value in pixel doesn't seem to agree with the stack, so need to look closer
     226static inline void combineMarkInspect(const psArray *inputs, // Stack data
     227                                      int x, int y, // Pixel
     228                                      int source // Source image index
     229                                      )
     230{
     231#ifdef TESTING
     232    if (x == TEST_X && y == TEST_Y) {
     233        fprintf(stderr, "Marking image %d for inspection\n", source);
     234    }
     235#endif
    297236    pmStackData *data = inputs->data[source]; // Stack data of interest
    298237    if (!data) {
     
    304243}
    305244
    306 // Given a stack of images, combine with optional rejection.
    307 // Pixels in the stack that are rejected are marked for subsequent inspection
    308 static void combinePixels(psImage *image, // Combined image, for output
    309                           psImage *mask, // Combined mask, for output
    310                           psImage *variance, // Combined variance map, for output
    311                           const psArray *inputs, // Stack data
    312                           const psVector *weights, // Global (single value) weights for data, or NULL
    313                           const psVector *addVariance, // Additional variance for data
    314                           const psVector *reject, // Indices of pixels to reject, or NULL
    315                           int x, int y, // Coordinates of interest; frame of output image
    316                           psImageMaskType maskVal, // Value to mask
    317                           psImageMaskType bad, // Value to give bad pixels
    318                           int numIter, // Number of rejection iterations
    319                           float rej, // Number of standard deviations at which to reject
    320                           float sys,    // Relative systematic error
    321                           float discard,// Fraction of values to discard (Olympic weighted mean)
    322                           bool useVariance, // Use variance for rejection when combining?
    323                           bool safe,    // Combine safely?
    324                           bool rejectInspect, // Reject values marked for inspection from combination?
    325                           combineBuffer *buffer // Buffer for combination; to avoid multiple allocations
    326                          )
     245// Mark a pixel for rejection
     246// Cannot possibly inspect this pixel and confirm that it's good.
     247// e.g., Only a single input
     248static inline void combineMarkReject(const psArray *inputs, // Stack data
     249                                     int x, int y, // Pixel
     250                                     int source // Source image index
     251                                     )
     252{
     253#ifdef TESTING
     254    if (x == TEST_X && y == TEST_Y) {
     255        fprintf(stderr, "Marking pixel image %d for rejection\n", source);
     256    }
     257#endif
     258    pmStackData *data = inputs->data[source]; // Stack data of interest
     259    if (!data) {
     260        psWarning("Can't find input data for source %d", source);
     261        return;
     262    }
     263    data->reject = psPixelsAdd(data->reject, data->reject->nalloc, x, y);
     264    return;
     265}
     266
     267
     268// Extract vectors for simple combination/rejection operations
     269static void combineExtract(int *num,                        // Number of good pixels
     270                           bool *suspect,                   // Any suspect pixels?
     271                           combineBuffer *buffer, // Buffer with vectors
     272                           psImage *image, // Combined image, for output
     273                           psImage *mask, // Combined mask, for output
     274                           psImage *variance, // Combined variance map, for output
     275                           const psArray *inputs, // Stack data
     276                           const psVector *weights, // Global (single value) weights for data, or NULL
     277                           const psVector *addVariance, // Additional variance for data
     278                           const psVector *reject, // Indices of pixels to reject, or NULL
     279                           int x, int y, // Coordinates of interest; frame of output image
     280                           psImageMaskType maskVal, // Value to mask
     281                           psImageMaskType maskSuspect // Value to suspect
     282                           )
    327283{
    328284    // Rudimentary error checking
     285    assert(buffer);
    329286    assert(image);
    330287    assert(mask);
    331288    assert(inputs);
    332     assert(numIter >= 0);
    333     assert(buffer);
    334     assert(addVariance);
    335     assert((useVariance && variance) || !useVariance);
    336289
    337290    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
    338     psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest
    339291    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
    340292    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
    341293    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
    342294    psVector *pixelLimits = buffer->limits; // Limits for the pixel of interest
    343     psVector *sort = buffer->sort;      // Sort buffer
     295    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     296
     297    if (suspect) {
     298        *suspect = false;
     299    }
    344300
    345301    // Extract the pixel and mask data
    346     int num = 0;                        // Number of good images
     302    int numGood = 0;                    // Number of good pixels
    347303    for (int i = 0, j = 0; i < inputs->n; i++) {
    348304        // Check if this pixel has been rejected.  Assumes that the rejection pixel list is sorted --- it
     
    364320        }
    365321
     322        pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ?
     323            true : false;
     324
    366325        psImage *image = data->readout->image; // Image of interest
    367326        psImage *variance = data->readout->variance; // Variance map of interest
    368         pixelData->data.F32[num] = image->data.F32[yIn][xIn];
     327        pixelData->data.F32[numGood] = image->data.F32[yIn][xIn];
    369328        if (variance) {
    370             pixelVariances->data.F32[num] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];
    371         }
    372         pixelWeights->data.F32[num] = data->weight;
    373         pixelSources->data.U16[num] = i;
    374         num++;
    375     }
    376     pixelData->n = num;
     329            pixelVariances->data.F32[numGood] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];
     330        }
     331        pixelWeights->data.F32[numGood] = data->weight;
     332        pixelSources->data.U16[numGood] = i;
     333        numGood++;
     334    }
     335    pixelData->n = numGood;
    377336    if (variance) {
    378         pixelVariances->n = num;
    379     }
    380     pixelWeights->n = num;
    381     pixelSources->n = num;
    382     pixelLimits->n = num;
     337        pixelVariances->n = numGood;
     338    }
     339    pixelWeights->n = numGood;
     340    pixelSources->n = numGood;
     341    pixelLimits->n = numGood;
     342    pixelSuspects->n = numGood;
     343    *num = numGood;
    383344
    384345#ifdef TESTING
    385346    if (x == TEST_X && y == TEST_Y) {
    386         for (int i = 0; i < num; i++) {
    387             fprintf(stderr, "Input %d (%" PRIu16 "): %f %f %f\n",
     347        for (int i = 0; i < numGood; i++) {
     348            fprintf(stderr, "Input %d (%" PRIu16 "): %f %f (%f) %f %d\n",
    388349                    i, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
    389                     pixelWeights->data.F32[i]);
    390         }
    391     }
    392 #endif
    393 
    394     // The sensible thing to do varies according to how many good pixels there are.
     350                    addVariance->data.F32[i], pixelWeights->data.F32[i], pixelSuspects->data.U8[i]);
     351        }
     352    }
     353#endif
     354
     355    return;
     356}
     357
     358
     359// Combine pixels
     360static void combinePixels(psImage *image, // Combined image, for output
     361                          psImage *mask, // Combined mask, for output
     362                          psImage *variance, // Combined variance map, for output
     363                          int num,      // Number of good pixels
     364                          combineBuffer *buffer, // Buffer with vectors
     365                          int x, int y, // Coordinates of interest; frame of output image
     366                          psImageMaskType bad, // Value for bad pixels
     367                          bool safe             // Safe combination?
     368                          )
     369{
     370    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     371    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
     372    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
     373
    395374    // Default option is that the pixel is bad
    396375    float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
    397376    psImageMaskType maskValue = bad;    // Value for combined mask
    398377    switch (num) {
    399       case 0:
    400         // Nothing to combine: it's bad
    401 #ifdef TESTING
    402     if (x == TEST_X && y == TEST_Y) {
    403         fprintf(stderr, "No inputs to combine, pixel is bad.\n");
    404     }
    405 #endif
    406         break;
     378      case 0: {
     379          // Nothing to combine: it's bad
     380#ifdef TESTING
     381          if (x == TEST_X && y == TEST_Y) {
     382              fprintf(stderr, "No inputs to combine, pixel is bad.\n");
     383          }
     384#endif
     385          break;
     386      }
    407387      case 1: {
    408388          // Accept the single pixel unless we have to be safe
     
    427407      }
    428408      case 2: {
    429           // Accept the mean of the pixels only if we're going to reject based on the variance, or we're not
    430           // playing safe
    431           if (useVariance || !safe) {
     409          // Automatically accept the mean of the pixels only if we're not playing safe
     410          if (!safe) {
    432411              float mean, var;   // Mean and variance from combination
    433412              if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
     
    437416#ifdef TESTING
    438417                  if (x == TEST_X && y == TEST_Y) {
    439                       fprintf(stderr, "Two inputs to combine using variance/unsafe --> %f %f\n",
    440                               mean, var);
     418                      fprintf(stderr, "Two inputs to combine using unsafe --> %f %f\n", mean, var);
    441419                  }
    442420#endif
    443421              }
    444422          }
    445           if (useVariance && numIter > 0) {
    446               // Use variance to check that the two are consistent
    447               float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference
    448               float var1 = pixelVariances->data.F32[0]; // Variance of first
    449               float var2 = pixelVariances->data.F32[1]; // Variance of second
    450               // Systematic error contributes to the rejection level
    451               var1 += PS_SQR(sys * pixelData->data.F32[0]);
    452               var2 += PS_SQR(sys * pixelData->data.F32[1]);
    453 
    454               float sigma2 = var1 + var2; // Combined variance
    455               if (PS_SQR(diff) > PS_SQR(rej) * sigma2) {
    456                   // Not consistent: mark both for inspection
    457                   if (rejectInspect) {
    458                       imageValue = NAN;
    459                       varianceValue = NAN;
    460                       maskValue = bad;
    461                   } else {
    462                       combineInspect(inputs, x, y, pixelSources->data.U16[0]);
    463                       combineInspect(inputs, x, y, pixelSources->data.U16[1]);
    464                   }
    465 #ifdef TESTING
    466                   if (x == TEST_X && y == TEST_Y) {
    467                       fprintf(stderr, "Both pixels marked for inspection (%f > %f x %f\n)",
    468                               diff, rej, sqrtf(sigma2));
    469                   }
    470 #endif
     423#ifdef TESTING
     424          else {
     425              if (x == TEST_X && y == TEST_Y) {
     426                  fprintf(stderr, "Two inputs to combine, safety on, pixel is bad\n");
    471427              }
    472428          }
     429#endif
    473430          break;
    474431      }
    475432      default: {
    476           // Record the value derived with no clipping, because pixels rejected using the harsh clipping
    477           // applied in the first pass might later be accepted.
     433          // Can combine without too much worrying
    478434          float mean, var;           // Mean and variance of the combination
    479435          if (!combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
     
    488444          }
    489445#endif
    490 
    491           // Prepare for clipping iteration
    492           if (numIter > 0) {
    493               pixelMasks->n = num;
    494               psVectorInit(pixelMasks, 0);
    495               if (useVariance) {
    496                   // Convert to rejection limits --- saves doing it later.
    497                   // Using squared rejection limit because it's cheaper than sqrts
    498                   float rej2 = PS_SQR(rej); // Rejection level squared
    499                   double sumWeights = 0.0;
    500                   for (int i = 0; i < num; i++) {
    501                       sumWeights += pixelWeights->data.F32[i];
    502                   }
    503                   for (int i = 0; i < num; i++) {
    504                       // Systematic error contributes to the rejection level
    505                       float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
    506 #ifdef TESTING
    507                       // Correct variance for comparison against weighted mean including itself
    508                       float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
    509                       if (x == TEST_X && y == TEST_Y) {
    510                           fprintf(stderr, "Variance %d (%d): %f %f %f\n", i, pixelSources->data.U16[i],
    511                                   pixelVariances->data.F32[i], sysVar, compare);
    512                       }
    513 #endif
    514 
    515                       pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
    516                   }
    517               }
    518           }
    519 
    520           // The clipping that follows is solely to identify suspect pixels.
    521           // These suspect pixels will be inspected in more detail by other functions.
    522           int numClipped = INT_MAX;     // Number of pixels clipped per iteration
    523           int totalClipped = 0;         // Total number of pixels clipped
    524           for (int i = 0; i < numIter && numClipped > 0 && num - totalClipped > 2; i++) {
    525               numClipped = 0;
    526               float median = NAN;       // Middle of distribution
    527               float limit = NAN;        // Rejection limit
    528               if (!useVariance) {
    529                   float stdev;  // Median and stdev of the combination, for rejection
    530                   if (!combinationMedianStdev(&median, useVariance ? NULL : &stdev,
    531                                               pixelData, pixelMasks, sort)) {
    532                       psWarning("Bad median/stdev at %d,%d", x, y);
    533                       break;
    534                   }
    535                   limit = rej * stdev;
    536 #ifdef TESTING
    537                   if (x == TEST_X && y == TEST_Y) {
    538                       fprintf(stderr, "Rejecting without variance; rejection limit: %f\n", limit);
    539                   }
    540 #endif
    541               } else {
    542 #ifdef TESTING
    543                   if (x == TEST_X && y == TEST_Y) {
    544                       fprintf(stderr, "Rejecting with variance...\n");
    545                   }
    546 #endif
    547                   median = combinationWeightedOlympic(pixelData, pixelWeights, pixelMasks, discard, sort);
    548               }
    549 
    550 #ifdef TESTING
    551               if (x == TEST_X && y == TEST_Y) {
    552                   fprintf(stderr, "Median: %f\n", median);
    553               }
    554 #endif
    555 
    556 
    557 // Mask a pixel for inspection
    558 #define MASK_PIXEL_FOR_INSPECTION() \
    559     pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xff; \
    560     if (!rejectInspect) { \
    561         combineInspect(inputs, x, y, pixelSources->data.U16[j]); \
    562     } \
    563     numClipped++; \
    564     totalClipped++;
    565 
    566               for (int j = 0; j < num; j++) {
    567                   if (pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    568                       continue;
    569                   }
    570                   float diff = pixelData->data.F32[j] - median; // Difference from expected
    571                   if (useVariance) {
    572                       // Comparing squares --- cheaper than lots of sqrts
    573                       // pixelVariances includes the rejection limit, from above
    574                       if (PS_SQR(diff) > pixelLimits->data.F32[j]) {
    575                           MASK_PIXEL_FOR_INSPECTION();
    576 #ifdef TESTING
    577                           if (x == TEST_X && y == TEST_Y) {
    578                               fprintf(stderr, "Rejecting input %d based on variance: %f > %f\n",
    579                                       j, diff, sqrtf(pixelLimits->data.F32[j]));
    580                           }
    581 #endif
    582                       }
    583                   } else if (fabsf(diff) > limit) {
    584                       MASK_PIXEL_FOR_INSPECTION();
    585 #ifdef TESTING
    586                       if (x == TEST_X && y == TEST_Y) {
    587                           fprintf(stderr, "Rejecting input %d based on distribution: %f > %f\n",
    588                                   j, diff, limit);
    589                       }
    590 #endif
    591                   }
    592               }
    593           }
    594 
    595           if (rejectInspect && totalClipped > 0) {
    596               // Get rid of the masked values
    597               // The alternative to this is to make combinationMeanVariance() accept a mask
    598               int good = 0;            // Index of good value
    599               for (int i = 0; i < num; i++) {
    600                   if (pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    601                       continue;
    602                   }
    603                   if (i != good) {
    604                       pixelData->data.F32[good] = pixelData->data.F32[i];
    605                       pixelVariances->data.F32[good] = pixelVariances->data.F32[i];
    606                       pixelWeights->data.F32[good] = pixelWeights->data.F32[i];
    607                       pixelData->data.F32[good] = pixelData->data.F32[i];
    608                   }
    609                   good++;
    610               }
    611               pixelData->n = good;
    612               pixelVariances->n = good;
    613               pixelWeights->n = good;
    614               if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
    615                   imageValue = mean;
    616                   varianceValue = var;
    617                   maskValue = 0;
    618               } else {
    619                   imageValue = NAN;
    620                   varianceValue = NAN;
    621                   maskValue = bad;
    622               }
    623           }
    624 
    625446          break;
    626447      }
     
    633454    }
    634455
     456#ifdef TESTING
     457    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = num;
     458#endif
     459
     460
    635461    return;
     462}
     463
     464
     465// Test pixels to be combined
     466// Returns false to repeat without suspect pixels
     467static bool combineTest(int num,      // Number of good pixels
     468                        bool suspect, // Does the stack contain suspect pixels?
     469                        psArray *inputs,       // Original inputs (for flagging)
     470                        combineBuffer *buffer, // Buffer with vectors
     471                        int x, int y, // Coordinates of interest; frame of output image
     472                        float iter, // Number of rejection iterations per input
     473                        float rej, // Number of standard deviations at which to reject
     474                        float sys,    // Relative systematic error
     475                        float olympic,// Fraction of values to discard (Olympic weighted mean)
     476                        bool useVariance, // Use variance for rejection when combining?
     477                        bool safe    // Combine safely?
     478                        )
     479{
     480    if (iter <= 0) {
     481        return true;
     482    }
     483
     484    int numIter = PS_MAX(iter * num, 1); // Number of iterations
     485
     486#ifdef TESTING
     487    if (x == TEST_X && y == TEST_Y) {
     488        fprintf(stderr, "Testing pixel %d,%d: %d %f %f %f %d %d\n",
     489                x, y, numIter, rej, sys, olympic, useVariance, safe);
     490    }
     491#endif
     492
     493    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     494    psVector *pixelWeights = buffer->weights; // Is the pixel suspect?
     495    psVector *pixelVariances = buffer->variances; // Variances for the pixel of interest
     496    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
     497    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     498    psVector *pixelLimits = buffer->limits; // Is the pixel suspect?
     499
     500    // Set up rejection limits
     501    float rej2 = PS_SQR(rej); // Rejection level squared
     502    if (num > 2 && useVariance) {
     503        // Convert rejection limits --- saves doing it later multiple times
     504        // Using squared rejection limit because it's cheaper than sqrts
     505        double sumWeights = 0.0;
     506        for (int i = 0; i < num; i++) {
     507            sumWeights += pixelWeights->data.F32[i];
     508        }
     509        for (int i = 0; i < num; i++) {
     510            // Systematic error contributes to the rejection level
     511            float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
     512#ifdef TESTING
     513            // Correct variance for comparison against weighted mean including itself
     514            float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
     515            if (x == TEST_X && y == TEST_Y) {
     516                fprintf(stderr, "Variance %d (%d): %f %f %f\n", i, pixelSources->data.U16[i],
     517                        pixelVariances->data.F32[i], sysVar, compare);
     518            }
     519#endif
     520            pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
     521        }
     522    }
     523
     524    int maskIndex = 0;                  // Index of pixel to mask
     525    int totalClipped = 0;               // Total number of pixels clipped
     526    for (int i = 0; i < numIter && maskIndex >= 0; i++) {
     527        maskIndex = -1;                 // Nothing to reject
     528
     529        switch (num) {
     530          case 0:
     531            break;
     532          case 1:
     533            if (i == 0 && safe) {
     534                combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     535            }
     536            break;
     537          case 2: {
     538              if (useVariance) {
     539                  // Use variance to check that the two are consistent
     540                  float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference
     541                  float var1 = pixelVariances->data.F32[0]; // Variance of first
     542                  float var2 = pixelVariances->data.F32[1]; // Variance of second
     543                  // Systematic error contributes to the rejection level
     544                  var1 += PS_SQR(sys * pixelData->data.F32[0]);
     545                  var2 += PS_SQR(sys * pixelData->data.F32[1]);
     546
     547                  float sigma2 = var1 + var2; // Combined variance
     548                  if (PS_SQR(diff) > rej2 * sigma2) {
     549                      // Not consistent: don't believe either!
     550                      if (i == 0 && suspect) {
     551                          combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     552                          combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     553                      } else {
     554                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     555                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     556                      }
     557#ifdef TESTING
     558                      if (x == TEST_X && y == TEST_Y) {
     559                          fprintf(stderr, "Flagged both pixels (%f > %f x %f\n)",
     560                                  diff, rej, sqrtf(sigma2));
     561                      }
     562#endif
     563                  }
     564              } else if (i == 0 && safe) {
     565                  // Can't test them, and we want to be safe, so reject
     566                  combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     567                  combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     568              }
     569              break;
     570          }
     571#if 0
     572          case 3: {
     573              // Want to be a bit careful on the rejection than for a larger number of inputs
     574              if (!useVariance) {
     575                  return combineTestGeneral(num, suspect, inputs, buffer, x, y, numIter, rej, sys,
     576                                            olympic, useVariance, safe, allowSuspect);
     577              }
     578
     579              // Differences between pixel values
     580              float diff01 = pixelData->data.F32[0] - pixelData->data.F32[1];
     581              float diff12 = pixelData->data.F32[1] - pixelData->data.F32[2];
     582              float diff20 = pixelData->data.F32[2] - pixelData->data.F32[0];
     583              // Variance for each pixel
     584              float var0 = pixelVariances->data.F32[0] + PS_SQR(sys * pixelData->data.F32[0]);
     585              float var1 = pixelVariances->data.F32[1] + PS_SQR(sys * pixelData->data.F32[1]);
     586              float var2 = pixelVariances->data.F32[2] + PS_SQR(sys * pixelData->data.F32[2]);
     587              // Errors in pixel differences
     588              float err01 = var0 + var1;
     589              float err12 = var1 + var2;
     590              float err20 = var2 + var0;
     591
     592#ifdef TESTING
     593              if (x == TEST_X && y == TEST_Y) {
     594                  fprintf(stderr, "Diff 0-1: %f %f\n", diff01, err01);
     595                  fprintf(stderr, "Diff 1-2: %f %f\n", diff12, err12);
     596                  fprintf(stderr, "Diff 2-0: %f %f\n", diff20, err20);
     597              }
     598#endif
     599
     600              int badPairs = 0;         // Number of bad pairs
     601              bool bad01 = false, bad12 = false, bad20 = false; // Pair is bad?
     602              if (PS_SQR(diff01) > rej2 * err01) {
     603                  bad01 = true;
     604                  badPairs++;
     605              }
     606              if (PS_SQR(diff12) > rej2 * err12) {
     607                  bad12 = true;
     608                  badPairs++;
     609              }
     610              if (PS_SQR(diff20) > rej2 * err20) {
     611                  bad20 = true;
     612                  badPairs++;
     613              }
     614
     615              if (badPairs > 0 && allowSuspect && suspect) {
     616                  return false;
     617              }
     618
     619              switch (badPairs) {
     620                case 0:
     621                  // Nothing to worry about!
     622                  break;
     623                case 1:
     624                  // Can't tell which image is bad, so be sure to get it
     625                  if (bad01) {
     626                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     627                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     628                      break;
     629                  }
     630                  if (bad12) {
     631                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     632                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     633                      break;
     634                  }
     635                  if (bad20) {
     636                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     637                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     638                      break;
     639                  }
     640                  psAbort("Should never get here");
     641                case 2:
     642                  if (bad01 && bad12) {
     643                      // 2 and 0 are good
     644                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     645                      break;
     646                  }
     647                  if (bad12 && bad20) {
     648                      // 0 and 1 are good
     649                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     650                      break;
     651                  }
     652                  if (bad20 && bad01) {
     653                      // 1 and 2 are good
     654                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     655                      break;
     656                  }
     657                  psAbort("Should never get here");
     658                case 3:
     659                  // Everything's bad
     660                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     661                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     662                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     663                  break;
     664              }
     665              break;
     666          }
     667#endif
     668          default: {
     669              if (useVariance) {
     670                  float median = combinationWeightedOlympic(pixelData, pixelWeights,
     671                                                            olympic, buffer->sort); // Median for stack
     672#ifdef TESTING
     673                  if (x == TEST_X && y == TEST_Y) {
     674                      fprintf(stderr, "Flag with variance, median = %f\n", median);
     675                  }
     676#endif
     677                  float worst = -INFINITY; // Largest deviation
     678                  for (int j = 0; j < num; j++) {
     679                      float diff = pixelData->data.F32[j] - median; // Difference from expected
     680#ifdef TESTING
     681                      if (x == TEST_X && y == TEST_Y) {
     682                          fprintf(stderr, "Testing input %d: %f\n", j, diff);
     683                      }
     684#endif
     685
     686                      // Comparing squares --- cheaper than lots of sqrts
     687                      // pixelVariances includes the rejection limit, from above
     688                      float diff2 = PS_SQR(diff); // Square difference
     689                      if (diff2 > pixelLimits->data.F32[j]) {
     690                          float dev = diff2 / pixelLimits->data.F32[j]; // Deviation
     691                          if (dev > worst) {
     692                              worst = dev;
     693                              maskIndex = j;
     694                          }
     695                      }
     696                  }
     697              } else {
     698                  float median = NAN, stdev = NAN;  // Median and stdev of the combination, for rejection
     699                  combinationMedianStdev(&median, &stdev, pixelData, buffer->sort);
     700                  float limit = rej * stdev; // Rejection limit
     701#ifdef TESTING
     702                  if (x == TEST_X && y == TEST_Y) {
     703                      fprintf(stderr, "Flag without variance; median = %f, stdev = %f, limit = %f\n",
     704                              median, stdev, limit);
     705                  }
     706#endif
     707                  float worst = -INFINITY; // Largest deviation
     708                  for (int j = 0; j < num; j++) {
     709                      float diff = fabsf(pixelData->data.F32[j] - median); // Difference from expected
     710
     711                      if (diff > limit) {
     712                          float dev = diff / limit; // Deviation
     713                          if (dev > worst) {
     714                              worst = dev;
     715                              maskIndex = j;
     716                          }
     717                      }
     718                  }
     719              }
     720          }
     721        }
     722
     723        // Do the actual rejection of the pixel
     724        if (maskIndex >= 0) {
     725            if (suspect) {
     726#ifdef TESTING
     727                if (x == TEST_X && y == TEST_Y) {
     728                    fprintf(stderr, "Throwing out all suspect pixels\n");
     729                }
     730#endif
     731                // Throw out all suspect pixels
     732                int numGood = 0;        // Number of good pixels
     733                for (int j = 0; j < num; j++) {
     734                    if (pixelSuspects->data.U8[j]) {
     735                        combineMarkReject(inputs, x, y, pixelSources->data.U16[j]);
     736                        continue;
     737                    }
     738                    if (numGood == j) {
     739                        numGood++;
     740                        continue;
     741                    }
     742                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     743                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     744                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     745                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     746                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     747                    numGood++;
     748                }
     749                pixelData->n = numGood;
     750                pixelWeights->n = numGood;
     751                pixelSources->n = numGood;
     752                pixelLimits->n = numGood;
     753                pixelVariances->n = numGood;
     754                totalClipped += num - numGood;
     755                num = numGood;
     756                suspect = false;
     757            } else {
     758                // Throw out masked pixel
     759#ifdef TESTING
     760                if (x == TEST_X && y == TEST_Y) {
     761                    fprintf(stderr, "Throwing out input %d\n", maskIndex);
     762                }
     763#endif
     764                combineMarkInspect(inputs, x, y, pixelSources->data.U16[maskIndex]);
     765                int numGood = 0;        // Number of good pixels
     766                for (int j = 0; j < num; j++) {
     767                    if (j == maskIndex) {
     768                        continue;
     769                    }
     770                    if (numGood == j) {
     771                        numGood++;
     772                        continue;
     773                    }
     774                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     775                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     776                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     777                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     778                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     779                    numGood++;
     780                }
     781                pixelData->n = numGood;
     782                pixelWeights->n = numGood;
     783                pixelSources->n = numGood;
     784                pixelLimits->n = numGood;
     785                pixelVariances->n = numGood;
     786                totalClipped++;
     787                num--;
     788            }
     789        }
     790    }
     791
     792    return true;
    636793}
    637794
     
    639796// Ensure the input array of pmStackData is valid, and get some details out of it
    640797static bool validateInputData(bool *haveVariances, // Do we have variance maps in the sky images?
    641                               bool *haveRejects, // Do we have lists of rejected pixels?
    642798                              int *num,    // Number of inputs
    643799                              int *numCols, int *numRows, // Size of (sky) images
    644                               psArray *input // Input array of pmStackData to validate
     800                              const psArray *input, // Input array of pmStackData to validate
     801                              const pmReadout *output // Output readout
    645802    )
    646803{
     
    668825        PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
    669826    }
    670     *haveRejects = (data->reject != NULL);
     827    bool haveRejects = (data->reject != NULL); // Do we have rejected pixels?
    671828
    672829    // Make sure the rest correspond with the first
     
    681838            return false;
    682839        }
    683         if ((*haveRejects && !data->reject) || (data->reject && !*haveRejects)) {
     840        if ((haveRejects && !data->reject) || (data->reject && !haveRejects)) {
    684841            psError(PS_ERR_UNEXPECTED_NULL, true,
    685842                    "The rejected pixels are specified in some but not all inputs.");
     
    697854            PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
    698855        }
     856    }
     857
     858    PM_ASSERT_READOUT_NON_NULL(output, false);
     859    if (output->image) {
     860        PS_ASSERT_IMAGE_NON_NULL(output->image, false);
     861        PS_ASSERT_IMAGE_TYPE(output->image, PS_TYPE_F32, false);
     862        PS_ASSERT_IMAGE_NON_NULL(output->mask, false);
     863        PS_ASSERT_IMAGE_TYPE(output->mask, PS_TYPE_IMAGE_MASK, false);
     864        PS_ASSERT_IMAGES_SIZE_EQUAL(output->image, output->mask, false);
    699865    }
    700866
     
    782948
    783949/// Stack input images
    784 bool pmStackCombine(pmReadout *combined, psArray *input, psImageMaskType maskVal, psImageMaskType bad,
    785                     int kernelSize, int numIter, float rej, float sys, float discard,
    786                     bool entire, bool useVariance, bool safe, bool rejectInspect)
    787 {
    788     PS_ASSERT_PTR_NON_NULL(combined, false);
     950bool pmStackCombine(pmReadout *combined, psArray *input, psImageMaskType maskVal, psImageMaskType maskSuspect,
     951                    psImageMaskType bad, int kernelSize, float iter, float rej, float sys, float olympic,
     952                    bool useVariance, bool safe, bool rejection)
     953{
    789954    bool haveVariances;                 // Do we have the variance maps?
    790     bool haveRejects;                   // Do we have lists of rejected pixels?
    791955    int num;                            // Number of inputs
    792956    int numCols, numRows;               // Size of (sky) images
    793     if (!validateInputData(&haveVariances, &haveRejects, &num, &numCols, &numRows, input)) {
     957    if (!validateInputData(&haveVariances, &num, &numCols, &numRows, input, combined)) {
    794958        return false;
    795959    }
    796960    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    797961    PS_ASSERT_INT_POSITIVE(bad, false);
    798     PS_ASSERT_INT_NONNEGATIVE(numIter, false);
    799962    if (isnan(rej)) {
    800         PS_ASSERT_INT_EQUAL(numIter, 0, false);
     963        PS_ASSERT_FLOAT_EQUAL(iter, 0, false);
    801964    } else {
     965        PS_ASSERT_FLOAT_LARGER_THAN(iter, 0, false);
    802966        PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
    803     }
    804     if (haveRejects) {
    805         // This is a subsequent combination, so expect that the image and mask already exist
    806         PS_ASSERT_IMAGE_NON_NULL(combined->image, false);
    807         PS_ASSERT_IMAGE_TYPE(combined->image, PS_TYPE_F32, false);
    808         PS_ASSERT_IMAGE_NON_NULL(combined->mask, false);
    809         PS_ASSERT_IMAGE_TYPE(combined->mask, PS_TYPE_IMAGE_MASK, false);
    810         PS_ASSERT_IMAGES_SIZE_EQUAL(combined->image, combined->mask, false);
    811967    }
    812968    if (useVariance && !haveVariances) {
     
    833989        }
    834990#endif
    835         if (!haveRejects && !data->inspect) {
    836             data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     991        if (!rejection) {
     992            // Ensure pixels can be put on the appropriate list
     993            if (!data->inspect) {
     994                data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     995            }
     996            if (!data->reject) {
     997                data->reject = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     998            }
    837999        }
    8381000    }
     
    8631025    combineBuffer *buffer = combineBufferAlloc(num);
    8641026
    865     if (haveRejects) {
    866         psImage *combinedImage = combined->image; // Combined image
    867         psImage *combinedMask = combined->mask; // Combined mask
    868         psImage *combinedVariance = combined->variance; // Combined variance map
    869 
    870         psArray *pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols,
    871                                              minInputRows, maxInputRows); // Map of pixels to source
    872         psPixels *pixels = NULL;            // Total list of pixels, with no duplicates
     1027    // Pull the products out, allocate if necessary
     1028    psImage *combinedImage = combined->image; // Combined image
     1029    if (!combinedImage) {
     1030        combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1031        combinedImage = combined->image;
     1032    }
     1033    psImage *combinedMask = combined->mask; // Combined mask
     1034    if (!combinedMask) {
     1035        combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     1036        combinedMask = combined->mask;
     1037    }
     1038
     1039    psImage *combinedVariance = combined->variance; // Combined variance map
     1040    if (haveVariances && !combinedVariance) {
     1041        combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1042        combinedVariance = combined->variance;
     1043    }
     1044
     1045    // Set up rejection list
     1046    psArray *pixelMap = NULL;           // Map of pixels to source
     1047    if (rejection) {
     1048        pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols, minInputRows, maxInputRows);
     1049    }
     1050
     1051    // Combine each pixel
     1052    for (int y = minInputRows; y < maxInputRows; y++) {
     1053        for (int x = minInputCols; x < maxInputCols; x++) {
     1054#ifdef TESTING
     1055            if (x == TEST_X && y == TEST_Y) {
     1056                fprintf(stderr, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n",
     1057                        x, y, maskVal, bad, iter, rej, sys, olympic, useVariance, safe, rejection);
     1058            }
     1059#endif
     1060            psVector *reject = NULL; // Images to reject for this pixel
     1061            if (rejection) {
     1062                reject = pixelMapQuery(pixelMap, minInputCols, minInputRows, x, y);
     1063#ifdef TESTING
     1064                if (x == TEST_X && y == TEST_Y) {
     1065                    fprintf(stderr, "Rejected inputs: ");
     1066                    if (!reject) {
     1067                        fprintf(stderr, "<none>\n");
     1068                    } else {
     1069                        for (int i = 0; i < reject->n; i++) {
     1070                            fprintf(stderr, "%d ", reject->data.U16[i]);
     1071                        }
     1072                        fprintf(stderr, "\n");
     1073                    }
     1074                }
     1075#endif
     1076            }
     1077
     1078            int num;                    // Number of good pixels
     1079            bool suspect;               // Suspect pixels in stack?
     1080            combineExtract(&num, &suspect, buffer, combinedImage, combinedMask, combinedVariance,
     1081                           input, weights, addVariance, reject, x, y, maskVal, maskSuspect);
     1082            combinePixels(combinedImage, combinedMask, combinedVariance, num, buffer, x, y, bad, safe);
     1083
     1084            if (iter > 0) {
     1085                combineTest(num, suspect, input, buffer, x, y, iter, rej, sys, olympic,
     1086                            useVariance, safe);
     1087            }
     1088        }
     1089    }
     1090
     1091    psFree(pixelMap);
     1092    psFree(weights);
     1093    psFree(buffer);
     1094
     1095
     1096
     1097#ifndef PS_NO_TRACE
     1098    if (!rejection && psTraceGetLevel("psModules.imcombine") >= 5) {
    8731099        for (int i = 0; i < num; i++) {
    874             pmStackData *data = input->data[i]; // Stacking data; contains the list of pixels
    875             if (!data) {
     1100            pmStackData *data = input->data[i]; // Stack data for this input
     1101            if (!data || !data->inspect) {
    8761102                continue;
    8771103            }
    878             pixels = psPixelsConcatenate(pixels, data->reject);
    879         }
    880         pixels = psPixelsDuplicates(pixels, pixels);
    881 
    882         if (entire) {
    883             // Combine entire image
    884             for (int y = minInputRows; y < maxInputRows; y++) {
    885                 for (int x = minInputCols; x < maxInputCols; x++) {
    886                     psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
    887                                                      x, y); // Reject these images
    888                     combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    889                                   addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
    890                                   useVariance, safe, rejectInspect, buffer);
    891                 }
    892             }
    893         } else {
    894             // Only combine previously rejected pixels
    895             for (int i = 0; i < pixels->n; i++) {
    896                 // Pixel coordinates are in the frame of the original image
    897                 int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of interest
    898                 if (x < minInputCols || x >= maxInputCols || y < minInputRows || y >= maxInputRows) {
    899                     continue;
    900                 }
    901                 psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
    902                                                  x, y); // Reject these images
    903                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    904                               addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
    905                               useVariance, safe, rejectInspect, buffer);
    906             }
    907         }
    908         psFree(pixels);
    909         psFree(pixelMap);
    910     } else {
    911         // Pull the products out, allocate if necessary
    912         psImage *combinedImage = combined->image; // Combined image
    913         if (!combinedImage) {
    914             combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    915             combinedImage = combined->image;
    916         }
    917         psImage *combinedMask = combined->mask; // Combined mask
    918         if (!combinedMask) {
    919             combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    920             combinedMask = combined->mask;
    921         }
    922 
    923         psImage *combinedVariance = combined->variance; // Combined variance map
    924         if (haveVariances && !combinedVariance) {
    925             combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    926             combinedVariance = combined->variance;
    927         }
    928 
    929         for (int y = minInputRows; y < maxInputRows; y++) {
    930             for (int x = minInputCols; x < maxInputCols; x++) {
    931                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    932                               addVariance, NULL, x, y, maskVal, bad, numIter, rej, sys, discard,
    933                               useVariance, safe, rejectInspect, buffer);
    934             }
    935         }
    936 
    937 #ifndef PS_NO_TRACE
    938         if (psTraceGetLevel("psModules.imcombine") >= 5) {
    939             for (int i = 0; i < num; i++) {
    940                 pmStackData *data = input->data[i]; // Stack data for this input
    941                 if (!data || !data->inspect) {
    942                     continue;
    943                 }
    944                 psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
    945             }
    946         }
    947 #endif
    948     }
    949 
    950     psFree(weights);
    951     psFree(buffer);
     1104            psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
     1105        }
     1106    }
     1107#endif
    9521108
    9531109    return true;
Note: See TracChangeset for help on using the changeset viewer.