- Timestamp:
- May 3, 2010, 8:50:52 AM (16 years ago)
- Location:
- branches/simtest_nebulous_branches
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/simtest_nebulous_branches
- Property svn:mergeinfo changed
-
branches/simtest_nebulous_branches/psModules
-
Property svn:mergeinfo
set to (toggle deleted branches)
/trunk/psModules merged eligible /branches/eam_branches/stackphot.20100406/psModules 27623-27653 /branches/pap_delete/psModules 27530-27595
-
Property svn:mergeinfo
set to (toggle deleted branches)
-
branches/simtest_nebulous_branches/psModules/src/imcombine/pmStack.c
r23775 r27840 30 30 #define PIXEL_LIST_BUFFER 100 // Number of entries to add to pixel list at a time 31 31 #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)? 33 33 #define NUM_DIRECT_STDEV 5 // For less than this number of values, measure stdev directly 34 34 35 35 36 //#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 38 40 39 41 … … 42 44 typedef struct { 43 45 psVector *pixels; // Pixel values 44 psVector *masks; // Pixel masks45 46 psVector *variances; // Pixel variances 46 47 psVector *weights; // Pixel weightings 48 psVector *exps; // Pixel exposures 47 49 psVector *sources; // Pixel sources (which image did they come from?) 48 50 psVector *limits; // Rejection limits 51 psVector *suspects; // Pixel is suspect? 49 52 psVector *sort; // Buffer for sorting (to get a robust estimator of the standard dev) 50 53 } combineBuffer; … … 53 56 { 54 57 psFree(buffer->pixels); 55 psFree(buffer->masks);56 58 psFree(buffer->variances); 57 59 psFree(buffer->weights); 60 psFree(buffer->exps); 58 61 psFree(buffer->sources); 59 62 psFree(buffer->limits); 63 psFree(buffer->suspects); 60 64 psFree(buffer->sort); 61 65 return; … … 69 73 70 74 buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32); 71 buffer->masks = psVectorAlloc(numImages, PS_TYPE_VECTOR_MASK);72 75 buffer->variances = psVectorAlloc(numImages, PS_TYPE_F32); 73 76 buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32); 77 buffer->exps = psVectorAlloc(numImages, PS_TYPE_F32); 74 78 buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16); 75 79 buffer->limits = psVectorAlloc(numImages, PS_TYPE_F32); 80 buffer->suspects = psVectorAlloc(numImages, PS_TYPE_U8); 76 81 buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32); 77 82 return buffer; … … 93 98 static bool combinationMeanVariance(float *mean, // Mean value, to return 94 99 float *var, // Variance value, to return 100 float *exp, // Exposure time, to return 101 float *expWeight, // Weighted exposure time, to return 95 102 const psVector *values, // Values to combine 96 103 const psVector *variances, // Pixel variances to combine 104 const psVector *exps, // Exposure times to combine 97 105 const psVector *weights // Weights to apply 98 106 ) … … 119 127 float sumVarianceWeight = 0.0; // Sum of the pixel variances multiplied by the global weights 120 128 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 121 132 for (int i = 0; i < values->n; i++) { 122 133 sumValueWeight += values->data.F32[i] * weights->data.F32[i]; … … 125 136 sumVarianceWeight += variances->data.F32[i] * PS_SQR(weights->data.F32[i]); 126 137 } 138 if (exps) { 139 sumExp += exps->data.F32[i]; 140 sumExpWeight += exps->data.F32[i] * weights->data.F32[i]; 141 numGood++; 142 } 127 143 } 128 144 … … 134 150 if (var) { 135 151 *var = sumVarianceWeight / PS_SQR(sumWeight); 152 } 153 if (exp) { 154 *exp = sumExp; 155 } 156 if (expWeight) { 157 *expWeight = sumExpWeight; 136 158 } 137 159 return true; … … 143 165 float *stdev, // Standard deviation value, to return 144 166 const psVector *values, // Values to combine 145 const psVector *masks, // Mask to apply146 167 psVector *sortBuffer // Buffer for sorting 147 168 ) 148 169 { 149 170 assert(values); 150 assert(!masks || values->n == masks->n);151 171 assert(values->type.type == PS_TYPE_F32); 152 assert(!masks || masks->type.type == PS_TYPE_VECTOR_MASK);153 172 assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32); 154 173 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; 164 179 return false; 165 180 } … … 167 182 if (num == 3) { 168 183 // 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]]; 170 185 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; 173 188 // This factor of sqrt(2) might not be exact, but it's about right 174 189 *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2)); 175 190 } 176 191 } 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; 179 195 if (stdev) { 180 196 if (num <= NUM_DIRECT_STDEV) { … … 182 198 double sum = 0.0; 183 199 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); 185 201 } 186 202 *stdev = sqrt(sum / (double)(num - 1)); 187 203 } else { 188 204 // 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)]]); 191 207 } 192 208 } … … 195 211 return true; 196 212 } 197 198 #if 0199 // Return the weighted median for the pixels200 // This does not appear to produce as clean images as the weighted Olympic mean201 static float combinationWeightedMedian(const psVector *values, // Values to combine202 const psVector *weights, // Weights to combine203 const psVector *masks, // Mask to apply204 psVector *sortBuffer // Buffer for sorting205 )206 {207 double sumWeight = 0.0; // Sum of weights208 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 weight217 218 int dominant = -1; // Index of dominant value, if any219 double cumulativeWeight = 0.0; // Sum of weights220 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 interest225 float weight = weights->data.F32[index]; // Weight for value of interest226 if (weight >= target) {227 // Get the weighted median of the rest228 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 median236 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 #endif249 213 250 214 // Return the weighted Olympic mean for the pixels 251 215 static float combinationWeightedOlympic(const psVector *values, // Values to combine 252 216 const psVector *weights, // Weights to combine 253 const psVector *masks, // Mask to apply254 217 float frac, // Fraction to discard 255 218 psVector *sortBuffer // Buffer for sorting 256 219 ) 257 220 { 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 265 222 266 223 int numBad = frac * numGood + 0.5; // Number of bad values … … 272 229 for (int i = 0, j = 0; i < values->n; i++) { 273 230 int index = sortBuffer->data.S32[i]; // Index of interest 274 if (masks->data.PS_TYPE_VECTOR_MASK_DATA[index]) {275 continue;276 }277 231 j++; 278 232 if (j > high) { … … 290 244 291 245 // 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 247 static 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 297 257 pmStackData *data = inputs->data[source]; // Stack data of interest 298 258 if (!data) { … … 304 264 } 305 265 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 269 static 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 290 static 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 ) 327 305 { 328 306 // Rudimentary error checking 307 assert(buffer); 329 308 assert(image); 330 309 assert(mask); 331 310 assert(inputs); 332 assert(numIter >= 0);333 assert(buffer);334 assert(addVariance);335 assert((useVariance && variance) || !useVariance);336 311 337 312 psVector *pixelData = buffer->pixels; // Values for the pixel of interest 338 psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest339 313 psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest 340 314 psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest 315 psVector *pixelExps = buffer->exps; // Exposure times 341 316 psVector *pixelSources = buffer->sources; // Sources for the pixel of interest 342 317 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 } 344 323 345 324 // Extract the pixel and mask data 346 int num = 0; // Number of good images325 int numGood = 0; // Number of good pixels 347 326 for (int i = 0, j = 0; i < inputs->n; i++) { 348 327 // Check if this pixel has been rejected. Assumes that the rejection pixel list is sorted --- it … … 364 343 } 365 344 345 pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ? 346 true : false; 347 366 348 psImage *image = data->readout->image; // Image of interest 367 349 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]; 369 351 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; 377 360 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 385 static 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 395 404 // Default option is that the pixel is bad 396 405 float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map 397 406 psImageMaskType maskValue = bad; // Value for combined mask 407 float expValue = 0.0, expWeightValue = NAN; // Exposure value (straight, and weighted) 408 398 409 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 } 407 419 case 1: { 408 420 // Accept the single pixel unless we have to be safe 409 421 if (!safe) { 410 #ifdef TESTING411 if (x == TEST_X && y == TEST_Y) {412 fprintf(stderr, "Single input to combine, safety off.\n");413 }414 #endif415 422 imageValue = pixelData->data.F32[0]; 416 423 if (variance) { 417 424 varianceValue = pixelVariances->data.F32[0]; 418 425 } 426 if (exp) { 427 expValue = pixelExps->data.F32[0]; 428 expWeightValue = pixelExps->data.F32[0]; 429 } 419 430 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 420 437 } 421 438 #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); 424 441 } 425 442 #endif … … 427 444 } 428 445 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)) { 436 450 maskValue = 0; 437 451 #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); 441 455 } 442 456 #endif 443 457 } 444 458 } 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); 471 463 } 472 464 } 465 #endif 473 466 break; 474 467 } 475 468 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)) { 480 472 break; 481 473 } 482 imageValue = mean;483 varianceValue = var;484 474 maskValue = 0; 485 475 #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); 488 478 } 489 479 #endif 490 491 // Prepare for clipping iteration492 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 sqrts498 float rej2 = PS_SQR(rej); // Rejection level squared499 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 level505 float sysVar = PS_SQR(sys * pixelData->data.F32[i]);506 #ifdef TESTING507 // Correct variance for comparison against weighted mean including itself508 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 #endif514 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 iteration523 int totalClipped = 0; // Total number of pixels clipped524 for (int i = 0; i < numIter && numClipped > 0 && num - totalClipped > 2; i++) {525 numClipped = 0;526 float median = NAN; // Middle of distribution527 float limit = NAN; // Rejection limit528 if (!useVariance) {529 float stdev; // Median and stdev of the combination, for rejection530 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 TESTING537 if (x == TEST_X && y == TEST_Y) {538 fprintf(stderr, "Rejecting without variance; rejection limit: %f\n", limit);539 }540 #endif541 } else {542 #ifdef TESTING543 if (x == TEST_X && y == TEST_Y) {544 fprintf(stderr, "Rejecting with variance...\n");545 }546 #endif547 median = combinationWeightedOlympic(pixelData, pixelWeights, pixelMasks, discard, sort);548 }549 550 #ifdef TESTING551 if (x == TEST_X && y == TEST_Y) {552 fprintf(stderr, "Median: %f\n", median);553 }554 #endif555 556 557 // Mask a pixel for inspection558 #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 expected571 if (useVariance) {572 // Comparing squares --- cheaper than lots of sqrts573 // pixelVariances includes the rejection limit, from above574 if (PS_SQR(diff) > pixelLimits->data.F32[j]) {575 MASK_PIXEL_FOR_INSPECTION();576 #ifdef TESTING577 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 #endif582 }583 } else if (fabsf(diff) > limit) {584 MASK_PIXEL_FOR_INSPECTION();585 #ifdef TESTING586 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 #endif591 }592 }593 }594 595 if (rejectInspect && totalClipped > 0) {596 // Get rid of the masked values597 // The alternative to this is to make combinationMeanVariance() accept a mask598 int good = 0; // Index of good value599 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 625 480 break; 626 481 } … … 632 487 variance->data.F32[y][x] = varianceValue; 633 488 } 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 } 634 498 635 499 return; 500 } 501 502 503 // Test pixels to be combined 504 // Returns false to repeat without suspect pixels 505 static 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; 636 832 } 637 833 … … 639 835 // Ensure the input array of pmStackData is valid, and get some details out of it 640 836 static bool validateInputData(bool *haveVariances, // Do we have variance maps in the sky images? 641 bool *haveRejects, // Do we have lists of rejected pixels?642 837 int *num, // Number of inputs 643 838 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 645 842 ) 646 843 { … … 668 865 PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false); 669 866 } 670 *haveRejects = (data->reject != NULL);867 bool haveRejects = (data->reject != NULL); // Do we have rejected pixels? 671 868 672 869 // Make sure the rest correspond with the first … … 681 878 return false; 682 879 } 683 if (( *haveRejects && !data->reject) || (data->reject && !*haveRejects)) {880 if ((haveRejects && !data->reject) || (data->reject && !haveRejects)) { 684 881 psError(PS_ERR_UNEXPECTED_NULL, true, 685 882 "The rejected pixels are specified in some but not all inputs."); … … 696 893 PS_ASSERT_IMAGES_SIZE_EQUAL(data->readout->image, data->readout->variance, false); 697 894 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); 698 914 } 699 915 } … … 767 983 768 984 /// Constructor 769 pmStackData *pmStackDataAlloc(pmReadout *readout, float weight, float addVariance)985 pmStackData *pmStackDataAlloc(pmReadout *readout, float weight, float exp, float addVariance) 770 986 { 771 987 pmStackData *data = psAlloc(sizeof(pmStackData)); // Stack data, to return … … 776 992 data->inspect = NULL; 777 993 data->weight = weight; 994 data->exp = exp; 778 995 data->addVariance = addVariance; 779 996 … … 782 999 783 1000 /// 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); 1001 bool 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 { 789 1007 bool haveVariances; // Do we have the variance maps? 790 bool haveRejects; // Do we have lists of rejected pixels?791 1008 int num; // Number of inputs 792 1009 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)) { 794 1011 return false; 795 1012 } 796 1013 PS_ASSERT_INT_NONNEGATIVE(kernelSize, false); 797 1014 PS_ASSERT_INT_POSITIVE(bad, false); 798 PS_ASSERT_INT_NONNEGATIVE(numIter, false);799 1015 if (isnan(rej)) { 800 PS_ASSERT_ INT_EQUAL(numIter, 0, false);1016 PS_ASSERT_FLOAT_EQUAL(iter, 0, false); 801 1017 } else { 1018 PS_ASSERT_FLOAT_LARGER_THAN(iter, 0, false); 802 1019 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 exist806 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);811 1020 } 812 1021 if (useVariance && !haveVariances) { … … 817 1026 psVector *addVariance = psVectorAlloc(num, PS_TYPE_F32); // Additional variance for each image 818 1027 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 819 1029 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 820 1032 for (int i = 0; i < num; i++) { 821 1033 pmStackData *data = input->data[i]; // Stack data for this input 822 1034 if (!data) { 823 1035 weights->data.F32[i] = 0.0; 1036 exps->data.F32[i] = NAN; 824 1037 continue; 825 1038 } 826 1039 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]; 827 1043 pmReadout *ro = data->readout; // Readout of interest 828 1044 stack->data[i] = psMemIncrRefCounter(ro); … … 833 1049 } 834 1050 #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 839 1062 840 1063 int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine … … 842 1065 if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize, 843 1066 stack)) { 844 psError( PS_ERR_UNKNOWN, false, "Input stack is not valid.");1067 psError(psErrorCodeLast(), false, "Input stack is not valid."); 845 1068 psFree(stack); 846 1069 return false; … … 863 1086 combineBuffer *buffer = combineBufferAlloc(num); 864 1087 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) { 873 1179 for (int i = 0; i < num; i++) { 874 pmStackData *data = input->data[i]; // Stack ing data; contains the list of pixels875 if (!data ) {1180 pmStackData *data = input->data[i]; // Stack data for this input 1181 if (!data || !data->inspect) { 876 1182 continue; 877 1183 } 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 952 1188 953 1189 return true;
Note:
See TracChangeset
for help on using the changeset viewer.
