IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 3, 2010, 8:50:52 AM (16 years ago)
Author:
eugene
Message:

updates from trunk

Location:
branches/simtest_nebulous_branches
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • branches/simtest_nebulous_branches

  • branches/simtest_nebulous_branches/psModules

  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmStack.c

    r23775 r27840  
    3030#define PIXEL_LIST_BUFFER 100           // Number of entries to add to pixel list at a time
    3131#define PIXEL_MAP_BUFFER 2              // Number of entries to add to pixel map at a time
    32 #define ADD_VARIANCE                    // Allow additional variance (besides variance factor)?
     32//#define ADD_VARIANCE                    // Allow additional variance (besides variance factor)?
    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 843-1                     // x coordinate to examine
     38//#define TEST_Y 813-1                     // y coordinate to examine
     39//#define TEST_RADIUS 0                    // Radius to examine
    3840
    3941
     
    4244typedef struct {
    4345    psVector *pixels;                   // Pixel values
    44     psVector *masks;                    // Pixel masks
    4546    psVector *variances;                // Pixel variances
    4647    psVector *weights;                  // Pixel weightings
     48    psVector *exps;                     // Pixel exposures
    4749    psVector *sources;                  // Pixel sources (which image did they come from?)
    4850    psVector *limits;                   // Rejection limits
     51    psVector *suspects;                 // Pixel is suspect?
    4952    psVector *sort;                     // Buffer for sorting (to get a robust estimator of the standard dev)
    5053} combineBuffer;
     
    5356{
    5457    psFree(buffer->pixels);
    55     psFree(buffer->masks);
    5658    psFree(buffer->variances);
    5759    psFree(buffer->weights);
     60    psFree(buffer->exps);
    5861    psFree(buffer->sources);
    5962    psFree(buffer->limits);
     63    psFree(buffer->suspects);
    6064    psFree(buffer->sort);
    6165    return;
     
    6973
    7074    buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32);
    71     buffer->masks = psVectorAlloc(numImages, PS_TYPE_VECTOR_MASK);
    7275    buffer->variances = psVectorAlloc(numImages, PS_TYPE_F32);
    7376    buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32);
     77    buffer->exps = psVectorAlloc(numImages, PS_TYPE_F32);
    7478    buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16);
    7579    buffer->limits = psVectorAlloc(numImages, PS_TYPE_F32);
     80    buffer->suspects = psVectorAlloc(numImages, PS_TYPE_U8);
    7681    buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32);
    7782    return buffer;
     
    9398static bool combinationMeanVariance(float *mean, // Mean value, to return
    9499                                    float *var, // Variance value, to return
     100                                    float *exp, // Exposure time, to return
     101                                    float *expWeight,          // Weighted exposure time, to return
    95102                                    const psVector *values, // Values to combine
    96103                                    const psVector *variances, // Pixel variances to combine
     104                                    const psVector *exps,      // Exposure times to combine
    97105                                    const psVector *weights // Weights to apply
    98106                                    )
     
    119127    float sumVarianceWeight = 0.0;     // Sum of the pixel variances multiplied by the global weights
    120128    float sumWeight = 0.0;              // Sum of the image weights
     129    float sumExp = 0.0;                 // Sum of the exposure time
     130    float sumExpWeight = 0.0;           // Sum of the exposure time multiplied by the global weights
     131    int numGood = 0;                    // Number of good exposures
    121132    for (int i = 0; i < values->n; i++) {
    122133        sumValueWeight += values->data.F32[i] * weights->data.F32[i];
     
    125136            sumVarianceWeight += variances->data.F32[i] * PS_SQR(weights->data.F32[i]);
    126137        }
     138        if (exps) {
     139            sumExp += exps->data.F32[i];
     140            sumExpWeight += exps->data.F32[i] * weights->data.F32[i];
     141            numGood++;
     142        }
    127143    }
    128144
     
    134150    if (var) {
    135151        *var = sumVarianceWeight / PS_SQR(sumWeight);
     152    }
     153    if (exp) {
     154        *exp = sumExp;
     155    }
     156    if (expWeight) {
     157        *expWeight = sumExpWeight;
    136158    }
    137159    return true;
     
    143165                                   float *stdev, // Standard deviation value, to return
    144166                                   const psVector *values, // Values to combine
    145                                    const psVector *masks, // Mask to apply
    146167                                   psVector *sortBuffer // Buffer for sorting
    147168                                   )
    148169{
    149170    assert(values);
    150     assert(!masks || values->n == masks->n);
    151171    assert(values->type.type == PS_TYPE_F32);
    152     assert(!masks || masks->type.type == PS_TYPE_VECTOR_MASK);
    153172    assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32);
    154173
    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)) {
     174    int num = values->n;                // Number of values
     175    sortBuffer = psVectorSortIndex(sortBuffer, values);
     176    if (!sortBuffer) {
     177        *median = NAN;
     178        *stdev = NAN;
    164179        return false;
    165180    }
     
    167182    if (num == 3) {
    168183        // Attempt to measure standard deviation with only three values (and one of those possibly corrupted)
    169         *median = sortBuffer->data.F32[1];
     184        *median = values->data.F32[sortBuffer->data.S32[1]];
    170185        if (stdev) {
    171             float diff1 = sortBuffer->data.F32[0] - *median;
    172             float diff2 = sortBuffer->data.F32[2] - *median;
     186            float diff1 = values->data.F32[sortBuffer->data.S32[0]] - *median;
     187            float diff2 = values->data.F32[sortBuffer->data.S32[2]] - *median;
    173188            // This factor of sqrt(2) might not be exact, but it's about right
    174189            *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2));
    175190        }
    176191    } else {
    177         *median = num % 2 ? sortBuffer->data.F32[num / 2] :
    178             (sortBuffer->data.F32[num / 2 - 1] + sortBuffer->data.F32[num / 2]) / 2.0;
     192        *median = num % 2 ? values->data.F32[sortBuffer->data.S32[num / 2]] :
     193            (values->data.F32[sortBuffer->data.S32[num / 2 - 1]] +
     194             values->data.F32[sortBuffer->data.S32[num / 2]]) / 2.0;
    179195        if (stdev) {
    180196            if (num <= NUM_DIRECT_STDEV) {
     
    182198                double sum = 0.0;
    183199                for (int i = 0; i < num; i++) {
    184                     sum += PS_SQR(sortBuffer->data.F32[i] - *median);
     200                    sum += PS_SQR(values->data.F32[sortBuffer->data.S32[i]] - *median);
    185201                }
    186202                *stdev = sqrt(sum / (double)(num - 1));
    187203            } else {
    188204                // 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)]);
     205                *stdev = 0.74 * (values->data.F32[sortBuffer->data.S32[(int)(0.75 * num)]] -
     206                                 values->data.F32[sortBuffer->data.S32[(int)(0.25 * num)]]);
    191207            }
    192208        }
     
    195211    return true;
    196212}
    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
    249213
    250214// Return the weighted Olympic mean for the pixels
    251215static float combinationWeightedOlympic(const psVector *values, // Values to combine
    252216                                        const psVector *weights, // Weights to combine
    253                                         const psVector *masks, // Mask to apply
    254217                                        float frac, // Fraction to discard
    255218                                        psVector *sortBuffer // Buffer for sorting
    256219                                        )
    257220{
    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     }
     221    int numGood = values->n;            // Number of good values
    265222
    266223    int numBad = frac * numGood + 0.5;  // Number of bad values
     
    272229    for (int i = 0, j = 0; i < values->n; i++) {
    273230        int index = sortBuffer->data.S32[i]; // Index of interest
    274         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[index]) {
    275             continue;
    276         }
    277231        j++;
    278232        if (j > high) {
     
    290244
    291245// 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 {
     246// Value in pixel doesn't seem to agree with the stack, so need to look closer
     247static inline void combineMarkInspect(const psArray *inputs, // Stack data
     248                                      int x, int y, // Pixel
     249                                      int source // Source image index
     250                                      )
     251{
     252#ifdef TESTING
     253    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     254        fprintf(stderr, "Marking image %d, pixel %d,%d for inspection\n", source, x, y);
     255    }
     256#endif
    297257    pmStackData *data = inputs->data[source]; // Stack data of interest
    298258    if (!data) {
     
    304264}
    305265
    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                          )
     266// Mark a pixel for rejection
     267// Cannot possibly inspect this pixel and confirm that it's good.
     268// e.g., Only a single input
     269static inline void combineMarkReject(const psArray *inputs, // Stack data
     270                                     int x, int y, // Pixel
     271                                     int source // Source image index
     272                                     )
     273{
     274#ifdef TESTING
     275    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     276        fprintf(stderr, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y);
     277    }
     278#endif
     279    pmStackData *data = inputs->data[source]; // Stack data of interest
     280    if (!data) {
     281        psWarning("Can't find input data for source %d", source);
     282        return;
     283    }
     284    data->reject = psPixelsAdd(data->reject, data->reject->nalloc, x, y);
     285    return;
     286}
     287
     288
     289// Extract vectors for simple combination/rejection operations
     290static void combineExtract(int *num,                        // Number of good pixels
     291                           bool *suspect,                   // Any suspect pixels?
     292                           combineBuffer *buffer, // Buffer with vectors
     293                           psImage *image, // Combined image, for output
     294                           psImage *mask, // Combined mask, for output
     295                           psImage *variance, // Combined variance map, for output
     296                           const psArray *inputs, // Stack data
     297                           const psVector *weights, // Global (single value) weights for data, or NULL
     298                           const psVector *exps,    // Exposures for data, or NULL
     299                           const psVector *addVariance, // Additional variance for data
     300                           const psVector *reject, // Indices of pixels to reject, or NULL
     301                           int x, int y, // Coordinates of interest; frame of output image
     302                           psImageMaskType maskVal, // Value to mask
     303                           psImageMaskType maskSuspect // Value to suspect
     304                           )
    327305{
    328306    // Rudimentary error checking
     307    assert(buffer);
    329308    assert(image);
    330309    assert(mask);
    331310    assert(inputs);
    332     assert(numIter >= 0);
    333     assert(buffer);
    334     assert(addVariance);
    335     assert((useVariance && variance) || !useVariance);
    336311
    337312    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
    338     psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest
    339313    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
    340314    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
     315    psVector *pixelExps = buffer->exps;       // Exposure times
    341316    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
    342317    psVector *pixelLimits = buffer->limits; // Limits for the pixel of interest
    343     psVector *sort = buffer->sort;      // Sort buffer
     318    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     319
     320    if (suspect) {
     321        *suspect = false;
     322    }
    344323
    345324    // Extract the pixel and mask data
    346     int num = 0;                        // Number of good images
     325    int numGood = 0;                    // Number of good pixels
    347326    for (int i = 0, j = 0; i < inputs->n; i++) {
    348327        // Check if this pixel has been rejected.  Assumes that the rejection pixel list is sorted --- it
     
    364343        }
    365344
     345        pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ?
     346            true : false;
     347
    366348        psImage *image = data->readout->image; // Image of interest
    367349        psImage *variance = data->readout->variance; // Variance map of interest
    368         pixelData->data.F32[num] = image->data.F32[yIn][xIn];
     350        pixelData->data.F32[numGood] = image->data.F32[yIn][xIn];
    369351        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;
     352            pixelVariances->data.F32[numGood] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];
     353        }
     354        pixelWeights->data.F32[numGood] = data->weight;
     355        pixelExps->data.F32[numGood] = data->exp;
     356        pixelSources->data.U16[numGood] = i;
     357        numGood++;
     358    }
     359    pixelData->n = numGood;
    377360    if (variance) {
    378         pixelVariances->n = num;
    379     }
    380     pixelWeights->n = num;
    381     pixelSources->n = num;
    382     pixelLimits->n = num;
    383 
    384 #ifdef TESTING
    385     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",
    388                     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.
     361        pixelVariances->n = numGood;
     362    }
     363    pixelWeights->n = numGood;
     364    pixelSources->n = numGood;
     365    pixelLimits->n = numGood;
     366    pixelSuspects->n = numGood;
     367    *num = numGood;
     368
     369#ifdef TESTING
     370    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     371        for (int i = 0; i < numGood; i++) {
     372            fprintf(stderr, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d\n",
     373                    i, x, y, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
     374                    addVariance->data.F32[i], pixelWeights->data.F32[i], pixelExps->data.F32[i],
     375                    pixelSuspects->data.U8[i]);
     376        }
     377    }
     378#endif
     379
     380    return;
     381}
     382
     383
     384// Combine pixels
     385static void combinePixels(psImage *image, // Combined image, for output
     386                          psImage *mask, // Combined mask, for output
     387                          psImage *variance, // Combined variance map, for output
     388                          psImage *exp,   // Exposure map (time), for output
     389                          psImage *expnum,       // Exposure map (number) for output
     390                          psImage *expweight,    // Exposure map (weighted time) for output
     391                          int num,      // Number of good pixels
     392                          combineBuffer *buffer, // Buffer with vectors
     393                          int x, int y, // Coordinates of interest; frame of output image
     394                          psImageMaskType bad, // Value for bad pixels
     395                          bool safe,           // Safe combination?
     396                          float invTotalWeight    // Inverse of total weight for all inputs
     397                          )
     398{
     399    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     400    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
     401    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
     402    psVector *pixelExps = buffer->exps;       // Exposure times
     403
    395404    // Default option is that the pixel is bad
    396405    float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
    397406    psImageMaskType maskValue = bad;    // Value for combined mask
     407    float expValue = 0.0, expWeightValue = NAN; // Exposure value (straight, and weighted)
     408
    398409    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;
     410      case 0: {
     411          // Nothing to combine: it's bad
     412#ifdef TESTING
     413          if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     414              fprintf(stderr, "No inputs to combine, pixel %d,%d is bad.\n", x, y);
     415          }
     416#endif
     417          break;
     418      }
    407419      case 1: {
    408420          // Accept the single pixel unless we have to be safe
    409421          if (!safe) {
    410 #ifdef TESTING
    411               if (x == TEST_X && y == TEST_Y) {
    412                   fprintf(stderr, "Single input to combine, safety off.\n");
    413               }
    414 #endif
    415422              imageValue = pixelData->data.F32[0];
    416423              if (variance) {
    417424                  varianceValue = pixelVariances->data.F32[0];
    418425              }
     426              if (exp) {
     427                  expValue = pixelExps->data.F32[0];
     428                  expWeightValue = pixelExps->data.F32[0];
     429              }
    419430              maskValue = 0;
     431#ifdef TESTING
     432              if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     433                  fprintf(stderr, "Single input to combine, safety off, pixel %d,%d --> %f\n",
     434                          x, y, imageValue);
     435              }
     436#endif
    420437          }
    421438#ifdef TESTING
    422           else if (x == TEST_X && y == TEST_Y) {
    423               fprintf(stderr, "Single input to combine, safety on, pixel is bad.\n");
     439          else if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     440              fprintf(stderr, "Single input to combine, safety on, pixel %d,%d is bad.\n", x, y);
    424441          }
    425442#endif
     
    427444      }
    428445      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) {
    432               float mean, var;   // Mean and variance from combination
    433               if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
    434                   imageValue = mean;
    435                   varianceValue = var;
     446          // Automatically accept the mean of the pixels only if we're not playing safe
     447          if (!safe) {
     448              if (combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue,
     449                                          pixelData, pixelVariances, pixelExps, pixelWeights)) {
    436450                  maskValue = 0;
    437451#ifdef TESTING
    438                   if (x == TEST_X && y == TEST_Y) {
    439                       fprintf(stderr, "Two inputs to combine using variance/unsafe --> %f %f\n",
    440                               mean, var);
     452                  if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     453                      fprintf(stderr, "Two inputs to combine using unsafe, pixel %d,%d --> %f %f\n",
     454                              x, y, imageValue, varianceValue);
    441455                  }
    442456#endif
    443457              }
    444458          }
    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
     459#ifdef TESTING
     460          else {
     461              if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     462                  fprintf(stderr, "Two inputs to combine, safety on, pixel %d,%d is bad\n", x, y);
    471463              }
    472464          }
     465#endif
    473466          break;
    474467      }
    475468      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.
    478           float mean, var;           // Mean and variance of the combination
    479           if (!combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
     469          // Can combine without too much worrying
     470          if (!combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue,
     471                                       pixelData, pixelVariances, pixelExps, pixelWeights)) {
    480472              break;
    481473          }
    482           imageValue = mean;
    483           varianceValue = var;
    484474          maskValue = 0;
    485475#ifdef TESTING
    486           if (x == TEST_X && y == TEST_Y) {
    487               fprintf(stderr, "Combined inputs: %f %f\n", mean, var);
     476          if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     477              fprintf(stderr, "Combined inputs, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    488478          }
    489479#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 
    625480          break;
    626481      }
     
    632487        variance->data.F32[y][x] = varianceValue;
    633488    }
     489    if (exp) {
     490        exp->data.F32[y][x] = expValue;
     491    }
     492    if (expnum) {
     493        expnum->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = num;
     494    }
     495    if (expweight) {
     496        expweight->data.F32[y][x] = expWeightValue * invTotalWeight;
     497    }
    634498
    635499    return;
     500}
     501
     502
     503// Test pixels to be combined
     504// Returns false to repeat without suspect pixels
     505static bool combineTest(int num,      // Number of good pixels
     506                        bool suspect, // Does the stack contain suspect pixels?
     507                        psArray *inputs,       // Original inputs (for flagging)
     508                        combineBuffer *buffer, // Buffer with vectors
     509                        int x, int y, // Coordinates of interest; frame of output image
     510                        float iter, // Number of rejection iterations per input
     511                        float rej, // Number of standard deviations at which to reject
     512                        float sys,    // Relative systematic error
     513                        float olympic,// Fraction of values to discard (Olympic weighted mean)
     514                        bool useVariance, // Use variance for rejection when combining?
     515                        bool safe    // Combine safely?
     516                        )
     517{
     518    if (iter <= 0) {
     519        return true;
     520    }
     521
     522    int numIter = PS_MAX(iter * num, 1); // Number of iterations
     523
     524#ifdef TESTING
     525    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     526        fprintf(stderr, "Testing pixel %d,%d: %d %f %f %f %d %d\n",
     527                x, y, numIter, rej, sys, olympic, useVariance, safe);
     528    }
     529#endif
     530
     531    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     532    psVector *pixelWeights = buffer->weights; // Is the pixel suspect?
     533    psVector *pixelVariances = buffer->variances; // Variances for the pixel of interest
     534    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
     535    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     536    psVector *pixelLimits = buffer->limits; // Is the pixel suspect?
     537
     538    // Set up rejection limits
     539    float rej2 = PS_SQR(rej); // Rejection level squared
     540    if (num > 2 && useVariance) {
     541        // Convert rejection limits --- saves doing it later multiple times
     542        // Using squared rejection limit because it's cheaper than sqrts
     543        double sumWeights = 0.0;
     544        for (int i = 0; i < num; i++) {
     545            sumWeights += pixelWeights->data.F32[i];
     546        }
     547        for (int i = 0; i < num; i++) {
     548            // Systematic error contributes to the rejection level
     549            float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
     550#ifdef TESTING
     551            // Correct variance for comparison against weighted mean including itself
     552            float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
     553            if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     554                fprintf(stderr, "Variance %d (%d), pixel %d,%d: %f %f %f\n", i, pixelSources->data.U16[i],
     555                        x, y, pixelVariances->data.F32[i], sysVar, compare);
     556            }
     557#endif
     558            pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
     559        }
     560    }
     561
     562    int maskIndex = 0;                  // Index of pixel to mask
     563    int totalClipped = 0;               // Total number of pixels clipped
     564    for (int i = 0; i < numIter && maskIndex >= 0; i++) {
     565        maskIndex = -1;                 // Nothing to reject
     566
     567        switch (num) {
     568          case 0:
     569            break;
     570          case 1:
     571            if (i == 0 && safe) {
     572                combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     573            }
     574            break;
     575          case 2: {
     576              if (useVariance) {
     577                  // Use variance to check that the two are consistent
     578                  float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference
     579                  float var1 = pixelVariances->data.F32[0]; // Variance of first
     580                  float var2 = pixelVariances->data.F32[1]; // Variance of second
     581                  // Systematic error contributes to the rejection level
     582                  var1 += PS_SQR(sys * pixelData->data.F32[0]);
     583                  var2 += PS_SQR(sys * pixelData->data.F32[1]);
     584
     585                  float sigma2 = var1 + var2; // Combined variance
     586                  if (PS_SQR(diff) > rej2 * sigma2) {
     587                      // Not consistent: don't believe either!
     588                      if (i == 0 && suspect) {
     589                          combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     590                          combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     591                      } else {
     592                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     593                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     594                      }
     595#ifdef TESTING
     596                      if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     597                          fprintf(stderr, "Flagged both inputs for pixel %d,%d (%f > %f x %f\n)",
     598                                  x, y, diff, rej, sqrtf(sigma2));
     599                      }
     600#endif
     601                  }
     602              } else if (i == 0 && safe) {
     603                  // Can't test them, and we want to be safe, so reject
     604                  combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     605                  combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     606              }
     607              break;
     608          }
     609#if 0
     610          case 3: {
     611              // Want to be a bit careful on the rejection than for a larger number of inputs
     612              if (!useVariance) {
     613                  return combineTestGeneral(num, suspect, inputs, buffer, x, y, numIter, rej, sys,
     614                                            olympic, useVariance, safe, allowSuspect);
     615              }
     616
     617              // Differences between pixel values
     618              float diff01 = pixelData->data.F32[0] - pixelData->data.F32[1];
     619              float diff12 = pixelData->data.F32[1] - pixelData->data.F32[2];
     620              float diff20 = pixelData->data.F32[2] - pixelData->data.F32[0];
     621              // Variance for each pixel
     622              float var0 = pixelVariances->data.F32[0] + PS_SQR(sys * pixelData->data.F32[0]);
     623              float var1 = pixelVariances->data.F32[1] + PS_SQR(sys * pixelData->data.F32[1]);
     624              float var2 = pixelVariances->data.F32[2] + PS_SQR(sys * pixelData->data.F32[2]);
     625              // Errors in pixel differences
     626              float err01 = var0 + var1;
     627              float err12 = var1 + var2;
     628              float err20 = var2 + var0;
     629
     630#ifdef TESTING
     631              if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     632                  fprintf(stderr, "Diff 0-1: %f %f\n", diff01, err01);
     633                  fprintf(stderr, "Diff 1-2: %f %f\n", diff12, err12);
     634                  fprintf(stderr, "Diff 2-0: %f %f\n", diff20, err20);
     635              }
     636#endif
     637
     638              int badPairs = 0;         // Number of bad pairs
     639              bool bad01 = false, bad12 = false, bad20 = false; // Pair is bad?
     640              if (PS_SQR(diff01) > rej2 * err01) {
     641                  bad01 = true;
     642                  badPairs++;
     643              }
     644              if (PS_SQR(diff12) > rej2 * err12) {
     645                  bad12 = true;
     646                  badPairs++;
     647              }
     648              if (PS_SQR(diff20) > rej2 * err20) {
     649                  bad20 = true;
     650                  badPairs++;
     651              }
     652
     653              if (badPairs > 0 && allowSuspect && suspect) {
     654                  return false;
     655              }
     656
     657              switch (badPairs) {
     658                case 0:
     659                  // Nothing to worry about!
     660                  break;
     661                case 1:
     662                  // Can't tell which image is bad, so be sure to get it
     663                  if (bad01) {
     664                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     665                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     666                      break;
     667                  }
     668                  if (bad12) {
     669                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     670                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     671                      break;
     672                  }
     673                  if (bad20) {
     674                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     675                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     676                      break;
     677                  }
     678                  psAbort("Should never get here");
     679                case 2:
     680                  if (bad01 && bad12) {
     681                      // 2 and 0 are good
     682                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     683                      break;
     684                  }
     685                  if (bad12 && bad20) {
     686                      // 0 and 1 are good
     687                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     688                      break;
     689                  }
     690                  if (bad20 && bad01) {
     691                      // 1 and 2 are good
     692                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     693                      break;
     694                  }
     695                  psAbort("Should never get here");
     696                case 3:
     697                  // Everything's bad
     698                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     699                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     700                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     701                  break;
     702              }
     703              break;
     704          }
     705#endif
     706          default: {
     707              if (useVariance) {
     708                  float median = combinationWeightedOlympic(pixelData, pixelWeights,
     709                                                            olympic, buffer->sort); // Median for stack
     710#ifdef TESTING
     711                  if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     712                      fprintf(stderr, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
     713                  }
     714#endif
     715                  float worst = -INFINITY; // Largest deviation
     716                  for (int j = 0; j < num; j++) {
     717                      float diff = pixelData->data.F32[j] - median; // Difference from expected
     718#ifdef TESTING
     719                      if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     720                          fprintf(stderr, "Testing input %d for pixel %d,%d: %f\n", j, x, y, diff);
     721                      }
     722#endif
     723
     724                      // Comparing squares --- cheaper than lots of sqrts
     725                      // pixelVariances includes the rejection limit, from above
     726                      float diff2 = PS_SQR(diff); // Square difference
     727                      if (diff2 > pixelLimits->data.F32[j]) {
     728                          float dev = diff2 / pixelLimits->data.F32[j]; // Deviation
     729                          if (dev > worst) {
     730                              worst = dev;
     731                              maskIndex = j;
     732                          }
     733                      }
     734                  }
     735              } else {
     736                  float median = NAN, stdev = NAN;  // Median and stdev of the combination, for rejection
     737                  combinationMedianStdev(&median, &stdev, pixelData, buffer->sort);
     738                  float limit = rej * stdev; // Rejection limit
     739#ifdef TESTING
     740                  if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     741                      fprintf(stderr,
     742                              "Flag without variance pixel %d,%d; median = %f, stdev = %f, limit = %f\n",
     743                              x, y, median, stdev, limit);
     744                  }
     745#endif
     746                  float worst = -INFINITY; // Largest deviation
     747                  for (int j = 0; j < num; j++) {
     748                      float diff = fabsf(pixelData->data.F32[j] - median); // Difference from expected
     749
     750                      if (diff > limit) {
     751                          float dev = diff / limit; // Deviation
     752                          if (dev > worst) {
     753                              worst = dev;
     754                              maskIndex = j;
     755                          }
     756                      }
     757                  }
     758              }
     759          }
     760        }
     761
     762        // Do the actual rejection of the pixel
     763        if (maskIndex >= 0) {
     764            if (suspect) {
     765#ifdef TESTING
     766                if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     767                    fprintf(stderr, "Throwing out all suspect pixels for %d,%d\n", x, y);
     768                }
     769#endif
     770                // Throw out all suspect pixels
     771                int numGood = 0;        // Number of good pixels
     772                for (int j = 0; j < num; j++) {
     773                    if (pixelSuspects->data.U8[j]) {
     774                        combineMarkReject(inputs, x, y, pixelSources->data.U16[j]);
     775                        continue;
     776                    }
     777                    if (numGood == j) {
     778                        numGood++;
     779                        continue;
     780                    }
     781                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     782                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     783                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     784                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     785                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     786                    numGood++;
     787                }
     788                pixelData->n = numGood;
     789                pixelWeights->n = numGood;
     790                pixelSources->n = numGood;
     791                pixelLimits->n = numGood;
     792                pixelVariances->n = numGood;
     793                totalClipped += num - numGood;
     794                num = numGood;
     795                suspect = false;
     796            } else {
     797                // Throw out masked pixel
     798#ifdef TESTING
     799                if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     800                    fprintf(stderr, "Throwing out input %d for pixel %d,%d\n", maskIndex, x, y);
     801                }
     802#endif
     803                combineMarkInspect(inputs, x, y, pixelSources->data.U16[maskIndex]);
     804                int numGood = 0;        // Number of good pixels
     805                for (int j = 0; j < num; j++) {
     806                    if (j == maskIndex) {
     807                        continue;
     808                    }
     809                    if (numGood == j) {
     810                        numGood++;
     811                        continue;
     812                    }
     813                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     814                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     815                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     816                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     817                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     818                    numGood++;
     819                }
     820                pixelData->n = numGood;
     821                pixelWeights->n = numGood;
     822                pixelSources->n = numGood;
     823                pixelLimits->n = numGood;
     824                pixelVariances->n = numGood;
     825                totalClipped++;
     826                num--;
     827            }
     828        }
     829    }
     830
     831    return true;
    636832}
    637833
     
    639835// Ensure the input array of pmStackData is valid, and get some details out of it
    640836static bool validateInputData(bool *haveVariances, // Do we have variance maps in the sky images?
    641                               bool *haveRejects, // Do we have lists of rejected pixels?
    642837                              int *num,    // Number of inputs
    643838                              int *numCols, int *numRows, // Size of (sky) images
    644                               psArray *input // Input array of pmStackData to validate
     839                              const psArray *input, // Input array of pmStackData to validate
     840                              const pmReadout *output, // Output readout
     841                              const pmReadout *exp    // Exposure map
    645842    )
    646843{
     
    668865        PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
    669866    }
    670     *haveRejects = (data->reject != NULL);
     867    bool haveRejects = (data->reject != NULL); // Do we have rejected pixels?
    671868
    672869    // Make sure the rest correspond with the first
     
    681878            return false;
    682879        }
    683         if ((*haveRejects && !data->reject) || (data->reject && !*haveRejects)) {
     880        if ((haveRejects && !data->reject) || (data->reject && !haveRejects)) {
    684881            psError(PS_ERR_UNEXPECTED_NULL, true,
    685882                    "The rejected pixels are specified in some but not all inputs.");
     
    696893            PS_ASSERT_IMAGES_SIZE_EQUAL(data->readout->image, data->readout->variance, false);
    697894            PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
     895        }
     896    }
     897
     898    PM_ASSERT_READOUT_NON_NULL(output, false);
     899    if (output->image) {
     900        PS_ASSERT_IMAGE_NON_NULL(output->image, false);
     901        PS_ASSERT_IMAGE_TYPE(output->image, PS_TYPE_F32, false);
     902        PS_ASSERT_IMAGE_NON_NULL(output->mask, false);
     903        PS_ASSERT_IMAGE_TYPE(output->mask, PS_TYPE_IMAGE_MASK, false);
     904        PS_ASSERT_IMAGES_SIZE_EQUAL(output->image, output->mask, false);
     905    }
     906
     907    if (exp) {
     908        PM_ASSERT_READOUT_NON_NULL(exp, false);
     909        if (exp->image) {
     910            PS_ASSERT_IMAGES_SIZE_EQUAL(exp->image, output->image, false);
     911        }
     912        if (exp->mask) {
     913            PS_ASSERT_IMAGES_SIZE_EQUAL(exp->mask, output->image, false);
    698914        }
    699915    }
     
    767983
    768984/// Constructor
    769 pmStackData *pmStackDataAlloc(pmReadout *readout, float weight, float addVariance)
     985pmStackData *pmStackDataAlloc(pmReadout *readout, float weight, float exp, float addVariance)
    770986{
    771987    pmStackData *data = psAlloc(sizeof(pmStackData)); // Stack data, to return
     
    776992    data->inspect = NULL;
    777993    data->weight = weight;
     994    data->exp = exp;
    778995    data->addVariance = addVariance;
    779996
     
    782999
    7831000/// 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);
     1001bool pmStackCombine(pmReadout *combined, pmReadout *expmaps, psArray *input,
     1002                    psImageMaskType maskVal, psImageMaskType maskSuspect,
     1003                    psImageMaskType bad, int kernelSize,
     1004                    float iter, float rej, float sys, float olympic,
     1005                    bool useVariance, bool safe, bool rejection)
     1006{
    7891007    bool haveVariances;                 // Do we have the variance maps?
    790     bool haveRejects;                   // Do we have lists of rejected pixels?
    7911008    int num;                            // Number of inputs
    7921009    int numCols, numRows;               // Size of (sky) images
    793     if (!validateInputData(&haveVariances, &haveRejects, &num, &numCols, &numRows, input)) {
     1010    if (!validateInputData(&haveVariances, &num, &numCols, &numRows, input, combined, expmaps)) {
    7941011        return false;
    7951012    }
    7961013    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    7971014    PS_ASSERT_INT_POSITIVE(bad, false);
    798     PS_ASSERT_INT_NONNEGATIVE(numIter, false);
    7991015    if (isnan(rej)) {
    800         PS_ASSERT_INT_EQUAL(numIter, 0, false);
     1016        PS_ASSERT_FLOAT_EQUAL(iter, 0, false);
    8011017    } else {
     1018        PS_ASSERT_FLOAT_LARGER_THAN(iter, 0, false);
    8021019        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);
    8111020    }
    8121021    if (useVariance && !haveVariances) {
     
    8171026    psVector *addVariance = psVectorAlloc(num, PS_TYPE_F32); // Additional variance for each image
    8181027    psVector *weights = psVectorAlloc(num, PS_TYPE_F32); // Relative weighting for each image
     1028    psVector *exps = psVectorAlloc(num, PS_TYPE_F32);    // Exposure times for each image
    8191029    psArray *stack = psArrayAlloc(num); // Stack of readouts
     1030    float totalExpWeight = 0.0;           // Total value of all weighted exposure times
     1031    float totalExp = 0.0;                 // Total exposure time
    8201032    for (int i = 0; i < num; i++) {
    8211033        pmStackData *data = input->data[i]; // Stack data for this input
    8221034        if (!data) {
    8231035            weights->data.F32[i] = 0.0;
     1036            exps->data.F32[i] = NAN;
    8241037            continue;
    8251038        }
    8261039        weights->data.F32[i] = data->weight;
     1040        exps->data.F32[i] = data->exp;
     1041        totalExp += exps->data.F32[i];
     1042        totalExpWeight += exps->data.F32[i] * weights->data.F32[i];
    8271043        pmReadout *ro = data->readout;  // Readout of interest
    8281044        stack->data[i] = psMemIncrRefCounter(ro);
     
    8331049        }
    8341050#endif
    835         if (!haveRejects && !data->inspect) {
    836             data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
    837         }
    838     }
     1051        if (!rejection) {
     1052            // Ensure pixels can be put on the appropriate list
     1053            if (!data->inspect) {
     1054                data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     1055            }
     1056            if (!data->reject) {
     1057                data->reject = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     1058            }
     1059        }
     1060    }
     1061    totalExpWeight = totalExp / totalExpWeight;    // Convert to inverse
    8391062
    8401063    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
     
    8421065    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize,
    8431066                                stack)) {
    844         psError(PS_ERR_UNKNOWN, false, "Input stack is not valid.");
     1067        psError(psErrorCodeLast(), false, "Input stack is not valid.");
    8451068        psFree(stack);
    8461069        return false;
     
    8631086    combineBuffer *buffer = combineBufferAlloc(num);
    8641087
    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
     1088    // Pull the products out, allocate if necessary
     1089    psImage *combinedImage = combined->image; // Combined image
     1090    if (!combinedImage) {
     1091        combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1092        combinedImage = combined->image;
     1093    }
     1094    psImage *combinedMask = combined->mask; // Combined mask
     1095    if (!combinedMask) {
     1096        combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     1097        combinedMask = combined->mask;
     1098    }
     1099
     1100    psImage *combinedVariance = combined->variance; // Combined variance map
     1101    if (haveVariances && !combinedVariance) {
     1102        combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1103        combinedVariance = combined->variance;
     1104    }
     1105
     1106    psImage *exp = NULL, *expnum = NULL, *expweight = NULL; // Exposure map and exposure number
     1107    if (expmaps) {
     1108        if (!expmaps->image) {
     1109            expmaps->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1110        }
     1111        exp = expmaps->image;
     1112
     1113        if (!expmaps->mask) {
     1114            expmaps->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     1115        }
     1116        expnum = expmaps->mask;
     1117
     1118        if (!expmaps->variance) {
     1119            expmaps->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1120        }
     1121        expweight = expmaps->variance;
     1122    }
     1123
     1124    // Set up rejection list
     1125    psArray *pixelMap = NULL;           // Map of pixels to source
     1126    if (rejection) {
     1127        pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols, minInputRows, maxInputRows);
     1128    }
     1129
     1130    // Combine each pixel
     1131    for (int y = minInputRows; y < maxInputRows; y++) {
     1132        for (int x = minInputCols; x < maxInputCols; x++) {
     1133#ifdef TESTING
     1134            if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     1135                fprintf(stderr, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n",
     1136                        x, y, maskVal, bad, iter, rej, sys, olympic, useVariance, safe, rejection);
     1137            }
     1138#endif
     1139            psVector *reject = NULL; // Images to reject for this pixel
     1140            if (rejection) {
     1141                reject = pixelMapQuery(pixelMap, minInputCols, minInputRows, x, y);
     1142#ifdef TESTING
     1143                if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     1144                    fprintf(stderr, "Rejected inputs for pixel %d,%d: ", x, y);
     1145                    if (!reject) {
     1146                        fprintf(stderr, "<none>\n");
     1147                    } else {
     1148                        for (int i = 0; i < reject->n; i++) {
     1149                            fprintf(stderr, "%d ", reject->data.U16[i]);
     1150                        }
     1151                        fprintf(stderr, "\n");
     1152                    }
     1153                }
     1154#endif
     1155            }
     1156
     1157            int num;                    // Number of good pixels
     1158            bool suspect;               // Suspect pixels in stack?
     1159            combineExtract(&num, &suspect, buffer, combinedImage, combinedMask, combinedVariance,
     1160                           input, weights, exps, addVariance, reject, x, y, maskVal, maskSuspect);
     1161            combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight,
     1162                          num, buffer, x, y, bad, safe, totalExpWeight);
     1163
     1164            if (iter > 0) {
     1165                combineTest(num, suspect, input, buffer, x, y, iter, rej, sys, olympic,
     1166                            useVariance, safe);
     1167            }
     1168        }
     1169    }
     1170
     1171    psFree(pixelMap);
     1172    psFree(weights);
     1173    psFree(buffer);
     1174    psFree(addVariance);
     1175
     1176
     1177#ifndef PS_NO_TRACE
     1178    if (!rejection && psTraceGetLevel("psModules.imcombine") >= 5) {
    8731179        for (int i = 0; i < num; i++) {
    874             pmStackData *data = input->data[i]; // Stacking data; contains the list of pixels
    875             if (!data) {
     1180            pmStackData *data = input->data[i]; // Stack data for this input
     1181            if (!data || !data->inspect) {
    8761182                continue;
    8771183            }
    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);
     1184            psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
     1185        }
     1186    }
     1187#endif
    9521188
    9531189    return true;
Note: See TracChangeset for help on using the changeset viewer.