Changeset 26076 for trunk/psModules/src/imcombine/pmStack.c
- Timestamp:
- Nov 9, 2009, 2:53:12 PM (17 years ago)
- File:
-
- 1 edited
-
trunk/psModules/src/imcombine/pmStack.c (modified) (23 diffs, 1 prop)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules/src/imcombine/pmStack.c
-
Property svn:mergeinfo
set to (toggle deleted branches)
/branches/czw_branch/cleanup/psModules/src/imcombine/pmStack.c merged eligible /branches/eam_branches/20090522/psModules/src/imcombine/pmStack.c merged eligible /branches/eam_branches/20090715/psModules/src/imcombine/pmStack.c merged eligible /branches/eam_branches/20090820/psModules/src/imcombine/pmStack.c merged eligible /branches/pap/psModules/src/imcombine/pmStack.c merged eligible /branches/pap_mops/psModules/src/imcombine/pmStack.c 25137-25255
r25380 r26076 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 examine37 //#define TEST_Y 3371 // y coordinate to examine37 //#define TEST_X 3122-1 // x coordinate to examine 38 //#define TEST_Y 1028-1 // y coordinate to examine 38 39 39 40 … … 42 43 typedef struct { 43 44 psVector *pixels; // Pixel values 44 psVector *masks; // Pixel masks45 45 psVector *variances; // Pixel variances 46 46 psVector *weights; // Pixel weightings 47 47 psVector *sources; // Pixel sources (which image did they come from?) 48 48 psVector *limits; // Rejection limits 49 psVector *suspects; // Pixel is suspect? 49 50 psVector *sort; // Buffer for sorting (to get a robust estimator of the standard dev) 50 51 } combineBuffer; … … 53 54 { 54 55 psFree(buffer->pixels); 55 psFree(buffer->masks);56 56 psFree(buffer->variances); 57 57 psFree(buffer->weights); 58 58 psFree(buffer->sources); 59 59 psFree(buffer->limits); 60 psFree(buffer->suspects); 60 61 psFree(buffer->sort); 61 62 return; … … 69 70 70 71 buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32); 71 buffer->masks = psVectorAlloc(numImages, PS_TYPE_VECTOR_MASK);72 72 buffer->variances = psVectorAlloc(numImages, PS_TYPE_F32); 73 73 buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32); 74 74 buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16); 75 75 buffer->limits = psVectorAlloc(numImages, PS_TYPE_F32); 76 buffer->suspects = psVectorAlloc(numImages, PS_TYPE_U8); 76 77 buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32); 77 78 return buffer; … … 143 144 float *stdev, // Standard deviation value, to return 144 145 const psVector *values, // Values to combine 145 const psVector *masks, // Mask to apply146 146 psVector *sortBuffer // Buffer for sorting 147 147 ) 148 148 { 149 149 assert(values); 150 assert(!masks || values->n == masks->n);151 150 assert(values->type.type == PS_TYPE_F32); 152 assert(!masks || masks->type.type == PS_TYPE_VECTOR_MASK);153 151 assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32); 154 152 155 // Need to filter out clipped values 156 int num = 0; // Number of valid values 157 for (int i = 0; i < values->n; i++) { 158 if (!masks || !masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) { 159 sortBuffer->data.F32[num++] = values->data.F32[i]; 160 } 161 } 162 sortBuffer->n = num; 163 if (!psVectorSortInPlace(sortBuffer)) { 153 int num = values->n; // Number of values 154 sortBuffer = psVectorSortIndex(sortBuffer, values); 155 if (!sortBuffer) { 156 *median = NAN; 157 *stdev = NAN; 164 158 return false; 165 159 } … … 167 161 if (num == 3) { 168 162 // Attempt to measure standard deviation with only three values (and one of those possibly corrupted) 169 *median = sortBuffer->data.F32[1];163 *median = values->data.F32[sortBuffer->data.S32[1]]; 170 164 if (stdev) { 171 float diff1 = sortBuffer->data.F32[0] - *median;172 float diff2 = sortBuffer->data.F32[2] - *median;165 float diff1 = values->data.F32[sortBuffer->data.S32[0]] - *median; 166 float diff2 = values->data.F32[sortBuffer->data.S32[2]] - *median; 173 167 // This factor of sqrt(2) might not be exact, but it's about right 174 168 *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2)); 175 169 } 176 170 } else { 177 *median = num % 2 ? sortBuffer->data.F32[num / 2] : 178 (sortBuffer->data.F32[num / 2 - 1] + sortBuffer->data.F32[num / 2]) / 2.0; 171 *median = num % 2 ? values->data.F32[sortBuffer->data.S32[num / 2]] : 172 (values->data.F32[sortBuffer->data.S32[num / 2 - 1]] + 173 values->data.F32[sortBuffer->data.S32[num / 2]]) / 2.0; 179 174 if (stdev) { 180 175 if (num <= NUM_DIRECT_STDEV) { … … 182 177 double sum = 0.0; 183 178 for (int i = 0; i < num; i++) { 184 sum += PS_SQR( sortBuffer->data.F32[i] - *median);179 sum += PS_SQR(values->data.F32[sortBuffer->data.S32[i]] - *median); 185 180 } 186 181 *stdev = sqrt(sum / (double)(num - 1)); 187 182 } else { 188 183 // Standard deviation from the interquartile range 189 *stdev = 0.74 * ( sortBuffer->data.F32[(int)(0.75 * num)] -190 sortBuffer->data.F32[(int)(0.25 * num)]);184 *stdev = 0.74 * (values->data.F32[sortBuffer->data.S32[(int)(0.75 * num)]] - 185 values->data.F32[sortBuffer->data.S32[(int)(0.25 * num)]]); 191 186 } 192 187 } … … 195 190 return true; 196 191 } 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 192 250 193 // Return the weighted Olympic mean for the pixels 251 194 static float combinationWeightedOlympic(const psVector *values, // Values to combine 252 195 const psVector *weights, // Weights to combine 253 const psVector *masks, // Mask to apply254 196 float frac, // Fraction to discard 255 197 psVector *sortBuffer // Buffer for sorting 256 198 ) 257 199 { 258 int numGood = 0; // Number of good values 259 for (int i = 0; i < values->n; i++) { 260 if (masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) { 261 continue; 262 } 263 numGood++; 264 } 200 int numGood = values->n; // Number of good values 265 201 266 202 int numBad = frac * numGood + 0.5; // Number of bad values … … 272 208 for (int i = 0, j = 0; i < values->n; i++) { 273 209 int index = sortBuffer->data.S32[i]; // Index of interest 274 if (masks->data.PS_TYPE_VECTOR_MASK_DATA[index]) {275 continue;276 }277 210 j++; 278 211 if (j > high) { … … 290 223 291 224 // Mark a pixel for inspection 292 static inline void combineInspect(const psArray *inputs, // Stack data 293 int x, int y, // Pixel 294 int source // Source image index 295 ) 296 { 225 // Value in pixel doesn't seem to agree with the stack, so need to look closer 226 static inline void combineMarkInspect(const psArray *inputs, // Stack data 227 int x, int y, // Pixel 228 int source // Source image index 229 ) 230 { 231 #ifdef TESTING 232 if (x == TEST_X && y == TEST_Y) { 233 fprintf(stderr, "Marking image %d for inspection\n", source); 234 } 235 #endif 297 236 pmStackData *data = inputs->data[source]; // Stack data of interest 298 237 if (!data) { … … 304 243 } 305 244 306 // Given a stack of images, combine with optional rejection. 307 // Pixels in the stack that are rejected are marked for subsequent inspection 308 static void combinePixels(psImage *image, // Combined image, for output 309 psImage *mask, // Combined mask, for output 310 psImage *variance, // Combined variance map, for output 311 const psArray *inputs, // Stack data 312 const psVector *weights, // Global (single value) weights for data, or NULL 313 const psVector *addVariance, // Additional variance for data 314 const psVector *reject, // Indices of pixels to reject, or NULL 315 int x, int y, // Coordinates of interest; frame of output image 316 psImageMaskType maskVal, // Value to mask 317 psImageMaskType bad, // Value to give bad pixels 318 int numIter, // Number of rejection iterations 319 float rej, // Number of standard deviations at which to reject 320 float sys, // Relative systematic error 321 float discard,// Fraction of values to discard (Olympic weighted mean) 322 bool useVariance, // Use variance for rejection when combining? 323 bool safe, // Combine safely? 324 bool rejectInspect, // Reject values marked for inspection from combination? 325 combineBuffer *buffer // Buffer for combination; to avoid multiple allocations 326 ) 245 // Mark a pixel for rejection 246 // Cannot possibly inspect this pixel and confirm that it's good. 247 // e.g., Only a single input 248 static inline void combineMarkReject(const psArray *inputs, // Stack data 249 int x, int y, // Pixel 250 int source // Source image index 251 ) 252 { 253 #ifdef TESTING 254 if (x == TEST_X && y == TEST_Y) { 255 fprintf(stderr, "Marking pixel image %d for rejection\n", source); 256 } 257 #endif 258 pmStackData *data = inputs->data[source]; // Stack data of interest 259 if (!data) { 260 psWarning("Can't find input data for source %d", source); 261 return; 262 } 263 data->reject = psPixelsAdd(data->reject, data->reject->nalloc, x, y); 264 return; 265 } 266 267 268 // Extract vectors for simple combination/rejection operations 269 static void combineExtract(int *num, // Number of good pixels 270 bool *suspect, // Any suspect pixels? 271 combineBuffer *buffer, // Buffer with vectors 272 psImage *image, // Combined image, for output 273 psImage *mask, // Combined mask, for output 274 psImage *variance, // Combined variance map, for output 275 const psArray *inputs, // Stack data 276 const psVector *weights, // Global (single value) weights for data, or NULL 277 const psVector *addVariance, // Additional variance for data 278 const psVector *reject, // Indices of pixels to reject, or NULL 279 int x, int y, // Coordinates of interest; frame of output image 280 psImageMaskType maskVal, // Value to mask 281 psImageMaskType maskSuspect // Value to suspect 282 ) 327 283 { 328 284 // Rudimentary error checking 285 assert(buffer); 329 286 assert(image); 330 287 assert(mask); 331 288 assert(inputs); 332 assert(numIter >= 0);333 assert(buffer);334 assert(addVariance);335 assert((useVariance && variance) || !useVariance);336 289 337 290 psVector *pixelData = buffer->pixels; // Values for the pixel of interest 338 psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest339 291 psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest 340 292 psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest 341 293 psVector *pixelSources = buffer->sources; // Sources for the pixel of interest 342 294 psVector *pixelLimits = buffer->limits; // Limits for the pixel of interest 343 psVector *sort = buffer->sort; // Sort buffer 295 psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect? 296 297 if (suspect) { 298 *suspect = false; 299 } 344 300 345 301 // Extract the pixel and mask data 346 int num = 0; // Number of good images302 int numGood = 0; // Number of good pixels 347 303 for (int i = 0, j = 0; i < inputs->n; i++) { 348 304 // Check if this pixel has been rejected. Assumes that the rejection pixel list is sorted --- it … … 364 320 } 365 321 322 pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ? 323 true : false; 324 366 325 psImage *image = data->readout->image; // Image of interest 367 326 psImage *variance = data->readout->variance; // Variance map of interest 368 pixelData->data.F32[num ] = image->data.F32[yIn][xIn];327 pixelData->data.F32[numGood] = image->data.F32[yIn][xIn]; 369 328 if (variance) { 370 pixelVariances->data.F32[num ] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];371 } 372 pixelWeights->data.F32[num ] = data->weight;373 pixelSources->data.U16[num ] = i;374 num ++;375 } 376 pixelData->n = num ;329 pixelVariances->data.F32[numGood] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i]; 330 } 331 pixelWeights->data.F32[numGood] = data->weight; 332 pixelSources->data.U16[numGood] = i; 333 numGood++; 334 } 335 pixelData->n = numGood; 377 336 if (variance) { 378 pixelVariances->n = num; 379 } 380 pixelWeights->n = num; 381 pixelSources->n = num; 382 pixelLimits->n = num; 337 pixelVariances->n = numGood; 338 } 339 pixelWeights->n = numGood; 340 pixelSources->n = numGood; 341 pixelLimits->n = numGood; 342 pixelSuspects->n = numGood; 343 *num = numGood; 383 344 384 345 #ifdef TESTING 385 346 if (x == TEST_X && y == TEST_Y) { 386 for (int i = 0; i < num ; i++) {387 fprintf(stderr, "Input %d (%" PRIu16 "): %f %f %f\n",347 for (int i = 0; i < numGood; i++) { 348 fprintf(stderr, "Input %d (%" PRIu16 "): %f %f (%f) %f %d\n", 388 349 i, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i], 389 pixelWeights->data.F32[i]); 390 } 391 } 392 #endif 393 394 // The sensible thing to do varies according to how many good pixels there are. 350 addVariance->data.F32[i], pixelWeights->data.F32[i], pixelSuspects->data.U8[i]); 351 } 352 } 353 #endif 354 355 return; 356 } 357 358 359 // Combine pixels 360 static void combinePixels(psImage *image, // Combined image, for output 361 psImage *mask, // Combined mask, for output 362 psImage *variance, // Combined variance map, for output 363 int num, // Number of good pixels 364 combineBuffer *buffer, // Buffer with vectors 365 int x, int y, // Coordinates of interest; frame of output image 366 psImageMaskType bad, // Value for bad pixels 367 bool safe // Safe combination? 368 ) 369 { 370 psVector *pixelData = buffer->pixels; // Values for the pixel of interest 371 psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest 372 psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest 373 395 374 // Default option is that the pixel is bad 396 375 float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map 397 376 psImageMaskType maskValue = bad; // Value for combined mask 398 377 switch (num) { 399 case 0: 400 // Nothing to combine: it's bad 401 #ifdef TESTING 402 if (x == TEST_X && y == TEST_Y) { 403 fprintf(stderr, "No inputs to combine, pixel is bad.\n"); 404 } 405 #endif 406 break; 378 case 0: { 379 // Nothing to combine: it's bad 380 #ifdef TESTING 381 if (x == TEST_X && y == TEST_Y) { 382 fprintf(stderr, "No inputs to combine, pixel is bad.\n"); 383 } 384 #endif 385 break; 386 } 407 387 case 1: { 408 388 // Accept the single pixel unless we have to be safe … … 427 407 } 428 408 case 2: { 429 // Accept the mean of the pixels only if we're going to reject based on the variance, or we're not 430 // playing safe 431 if (useVariance || !safe) { 409 // Automatically accept the mean of the pixels only if we're not playing safe 410 if (!safe) { 432 411 float mean, var; // Mean and variance from combination 433 412 if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) { … … 437 416 #ifdef TESTING 438 417 if (x == TEST_X && y == TEST_Y) { 439 fprintf(stderr, "Two inputs to combine using variance/unsafe --> %f %f\n", 440 mean, var); 418 fprintf(stderr, "Two inputs to combine using unsafe --> %f %f\n", mean, var); 441 419 } 442 420 #endif 443 421 } 444 422 } 445 if (useVariance && numIter > 0) { 446 // Use variance to check that the two are consistent 447 float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference 448 float var1 = pixelVariances->data.F32[0]; // Variance of first 449 float var2 = pixelVariances->data.F32[1]; // Variance of second 450 // Systematic error contributes to the rejection level 451 var1 += PS_SQR(sys * pixelData->data.F32[0]); 452 var2 += PS_SQR(sys * pixelData->data.F32[1]); 453 454 float sigma2 = var1 + var2; // Combined variance 455 if (PS_SQR(diff) > PS_SQR(rej) * sigma2) { 456 // Not consistent: mark both for inspection 457 if (rejectInspect) { 458 imageValue = NAN; 459 varianceValue = NAN; 460 maskValue = bad; 461 } else { 462 combineInspect(inputs, x, y, pixelSources->data.U16[0]); 463 combineInspect(inputs, x, y, pixelSources->data.U16[1]); 464 } 465 #ifdef TESTING 466 if (x == TEST_X && y == TEST_Y) { 467 fprintf(stderr, "Both pixels marked for inspection (%f > %f x %f\n)", 468 diff, rej, sqrtf(sigma2)); 469 } 470 #endif 423 #ifdef TESTING 424 else { 425 if (x == TEST_X && y == TEST_Y) { 426 fprintf(stderr, "Two inputs to combine, safety on, pixel is bad\n"); 471 427 } 472 428 } 429 #endif 473 430 break; 474 431 } 475 432 default: { 476 // Record the value derived with no clipping, because pixels rejected using the harsh clipping 477 // applied in the first pass might later be accepted. 433 // Can combine without too much worrying 478 434 float mean, var; // Mean and variance of the combination 479 435 if (!combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) { … … 488 444 } 489 445 #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 446 break; 626 447 } … … 633 454 } 634 455 456 #ifdef TESTING 457 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = num; 458 #endif 459 460 635 461 return; 462 } 463 464 465 // Test pixels to be combined 466 // Returns false to repeat without suspect pixels 467 static bool combineTest(int num, // Number of good pixels 468 bool suspect, // Does the stack contain suspect pixels? 469 psArray *inputs, // Original inputs (for flagging) 470 combineBuffer *buffer, // Buffer with vectors 471 int x, int y, // Coordinates of interest; frame of output image 472 float iter, // Number of rejection iterations per input 473 float rej, // Number of standard deviations at which to reject 474 float sys, // Relative systematic error 475 float olympic,// Fraction of values to discard (Olympic weighted mean) 476 bool useVariance, // Use variance for rejection when combining? 477 bool safe // Combine safely? 478 ) 479 { 480 if (iter <= 0) { 481 return true; 482 } 483 484 int numIter = PS_MAX(iter * num, 1); // Number of iterations 485 486 #ifdef TESTING 487 if (x == TEST_X && y == TEST_Y) { 488 fprintf(stderr, "Testing pixel %d,%d: %d %f %f %f %d %d\n", 489 x, y, numIter, rej, sys, olympic, useVariance, safe); 490 } 491 #endif 492 493 psVector *pixelData = buffer->pixels; // Values for the pixel of interest 494 psVector *pixelWeights = buffer->weights; // Is the pixel suspect? 495 psVector *pixelVariances = buffer->variances; // Variances for the pixel of interest 496 psVector *pixelSources = buffer->sources; // Sources for the pixel of interest 497 psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect? 498 psVector *pixelLimits = buffer->limits; // Is the pixel suspect? 499 500 // Set up rejection limits 501 float rej2 = PS_SQR(rej); // Rejection level squared 502 if (num > 2 && useVariance) { 503 // Convert rejection limits --- saves doing it later multiple times 504 // Using squared rejection limit because it's cheaper than sqrts 505 double sumWeights = 0.0; 506 for (int i = 0; i < num; i++) { 507 sumWeights += pixelWeights->data.F32[i]; 508 } 509 for (int i = 0; i < num; i++) { 510 // Systematic error contributes to the rejection level 511 float sysVar = PS_SQR(sys * pixelData->data.F32[i]); 512 #ifdef TESTING 513 // Correct variance for comparison against weighted mean including itself 514 float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights; 515 if (x == TEST_X && y == TEST_Y) { 516 fprintf(stderr, "Variance %d (%d): %f %f %f\n", i, pixelSources->data.U16[i], 517 pixelVariances->data.F32[i], sysVar, compare); 518 } 519 #endif 520 pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar); 521 } 522 } 523 524 int maskIndex = 0; // Index of pixel to mask 525 int totalClipped = 0; // Total number of pixels clipped 526 for (int i = 0; i < numIter && maskIndex >= 0; i++) { 527 maskIndex = -1; // Nothing to reject 528 529 switch (num) { 530 case 0: 531 break; 532 case 1: 533 if (i == 0 && safe) { 534 combineMarkReject(inputs, x, y, pixelSources->data.U16[0]); 535 } 536 break; 537 case 2: { 538 if (useVariance) { 539 // Use variance to check that the two are consistent 540 float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference 541 float var1 = pixelVariances->data.F32[0]; // Variance of first 542 float var2 = pixelVariances->data.F32[1]; // Variance of second 543 // Systematic error contributes to the rejection level 544 var1 += PS_SQR(sys * pixelData->data.F32[0]); 545 var2 += PS_SQR(sys * pixelData->data.F32[1]); 546 547 float sigma2 = var1 + var2; // Combined variance 548 if (PS_SQR(diff) > rej2 * sigma2) { 549 // Not consistent: don't believe either! 550 if (i == 0 && suspect) { 551 combineMarkReject(inputs, x, y, pixelSources->data.U16[0]); 552 combineMarkReject(inputs, x, y, pixelSources->data.U16[1]); 553 } else { 554 combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]); 555 combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]); 556 } 557 #ifdef TESTING 558 if (x == TEST_X && y == TEST_Y) { 559 fprintf(stderr, "Flagged both pixels (%f > %f x %f\n)", 560 diff, rej, sqrtf(sigma2)); 561 } 562 #endif 563 } 564 } else if (i == 0 && safe) { 565 // Can't test them, and we want to be safe, so reject 566 combineMarkReject(inputs, x, y, pixelSources->data.U16[0]); 567 combineMarkReject(inputs, x, y, pixelSources->data.U16[1]); 568 } 569 break; 570 } 571 #if 0 572 case 3: { 573 // Want to be a bit careful on the rejection than for a larger number of inputs 574 if (!useVariance) { 575 return combineTestGeneral(num, suspect, inputs, buffer, x, y, numIter, rej, sys, 576 olympic, useVariance, safe, allowSuspect); 577 } 578 579 // Differences between pixel values 580 float diff01 = pixelData->data.F32[0] - pixelData->data.F32[1]; 581 float diff12 = pixelData->data.F32[1] - pixelData->data.F32[2]; 582 float diff20 = pixelData->data.F32[2] - pixelData->data.F32[0]; 583 // Variance for each pixel 584 float var0 = pixelVariances->data.F32[0] + PS_SQR(sys * pixelData->data.F32[0]); 585 float var1 = pixelVariances->data.F32[1] + PS_SQR(sys * pixelData->data.F32[1]); 586 float var2 = pixelVariances->data.F32[2] + PS_SQR(sys * pixelData->data.F32[2]); 587 // Errors in pixel differences 588 float err01 = var0 + var1; 589 float err12 = var1 + var2; 590 float err20 = var2 + var0; 591 592 #ifdef TESTING 593 if (x == TEST_X && y == TEST_Y) { 594 fprintf(stderr, "Diff 0-1: %f %f\n", diff01, err01); 595 fprintf(stderr, "Diff 1-2: %f %f\n", diff12, err12); 596 fprintf(stderr, "Diff 2-0: %f %f\n", diff20, err20); 597 } 598 #endif 599 600 int badPairs = 0; // Number of bad pairs 601 bool bad01 = false, bad12 = false, bad20 = false; // Pair is bad? 602 if (PS_SQR(diff01) > rej2 * err01) { 603 bad01 = true; 604 badPairs++; 605 } 606 if (PS_SQR(diff12) > rej2 * err12) { 607 bad12 = true; 608 badPairs++; 609 } 610 if (PS_SQR(diff20) > rej2 * err20) { 611 bad20 = true; 612 badPairs++; 613 } 614 615 if (badPairs > 0 && allowSuspect && suspect) { 616 return false; 617 } 618 619 switch (badPairs) { 620 case 0: 621 // Nothing to worry about! 622 break; 623 case 1: 624 // Can't tell which image is bad, so be sure to get it 625 if (bad01) { 626 combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]); 627 combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]); 628 break; 629 } 630 if (bad12) { 631 combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]); 632 combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]); 633 break; 634 } 635 if (bad20) { 636 combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]); 637 combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]); 638 break; 639 } 640 psAbort("Should never get here"); 641 case 2: 642 if (bad01 && bad12) { 643 // 2 and 0 are good 644 combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]); 645 break; 646 } 647 if (bad12 && bad20) { 648 // 0 and 1 are good 649 combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]); 650 break; 651 } 652 if (bad20 && bad01) { 653 // 1 and 2 are good 654 combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]); 655 break; 656 } 657 psAbort("Should never get here"); 658 case 3: 659 // Everything's bad 660 combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]); 661 combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]); 662 combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]); 663 break; 664 } 665 break; 666 } 667 #endif 668 default: { 669 if (useVariance) { 670 float median = combinationWeightedOlympic(pixelData, pixelWeights, 671 olympic, buffer->sort); // Median for stack 672 #ifdef TESTING 673 if (x == TEST_X && y == TEST_Y) { 674 fprintf(stderr, "Flag with variance, median = %f\n", median); 675 } 676 #endif 677 float worst = -INFINITY; // Largest deviation 678 for (int j = 0; j < num; j++) { 679 float diff = pixelData->data.F32[j] - median; // Difference from expected 680 #ifdef TESTING 681 if (x == TEST_X && y == TEST_Y) { 682 fprintf(stderr, "Testing input %d: %f\n", j, diff); 683 } 684 #endif 685 686 // Comparing squares --- cheaper than lots of sqrts 687 // pixelVariances includes the rejection limit, from above 688 float diff2 = PS_SQR(diff); // Square difference 689 if (diff2 > pixelLimits->data.F32[j]) { 690 float dev = diff2 / pixelLimits->data.F32[j]; // Deviation 691 if (dev > worst) { 692 worst = dev; 693 maskIndex = j; 694 } 695 } 696 } 697 } else { 698 float median = NAN, stdev = NAN; // Median and stdev of the combination, for rejection 699 combinationMedianStdev(&median, &stdev, pixelData, buffer->sort); 700 float limit = rej * stdev; // Rejection limit 701 #ifdef TESTING 702 if (x == TEST_X && y == TEST_Y) { 703 fprintf(stderr, "Flag without variance; median = %f, stdev = %f, limit = %f\n", 704 median, stdev, limit); 705 } 706 #endif 707 float worst = -INFINITY; // Largest deviation 708 for (int j = 0; j < num; j++) { 709 float diff = fabsf(pixelData->data.F32[j] - median); // Difference from expected 710 711 if (diff > limit) { 712 float dev = diff / limit; // Deviation 713 if (dev > worst) { 714 worst = dev; 715 maskIndex = j; 716 } 717 } 718 } 719 } 720 } 721 } 722 723 // Do the actual rejection of the pixel 724 if (maskIndex >= 0) { 725 if (suspect) { 726 #ifdef TESTING 727 if (x == TEST_X && y == TEST_Y) { 728 fprintf(stderr, "Throwing out all suspect pixels\n"); 729 } 730 #endif 731 // Throw out all suspect pixels 732 int numGood = 0; // Number of good pixels 733 for (int j = 0; j < num; j++) { 734 if (pixelSuspects->data.U8[j]) { 735 combineMarkReject(inputs, x, y, pixelSources->data.U16[j]); 736 continue; 737 } 738 if (numGood == j) { 739 numGood++; 740 continue; 741 } 742 pixelData->data.F32[numGood] = pixelData->data.F32[j]; 743 pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j]; 744 pixelSources->data.U16[numGood] = pixelSources->data.U16[j]; 745 pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j]; 746 pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j]; 747 numGood++; 748 } 749 pixelData->n = numGood; 750 pixelWeights->n = numGood; 751 pixelSources->n = numGood; 752 pixelLimits->n = numGood; 753 pixelVariances->n = numGood; 754 totalClipped += num - numGood; 755 num = numGood; 756 suspect = false; 757 } else { 758 // Throw out masked pixel 759 #ifdef TESTING 760 if (x == TEST_X && y == TEST_Y) { 761 fprintf(stderr, "Throwing out input %d\n", maskIndex); 762 } 763 #endif 764 combineMarkInspect(inputs, x, y, pixelSources->data.U16[maskIndex]); 765 int numGood = 0; // Number of good pixels 766 for (int j = 0; j < num; j++) { 767 if (j == maskIndex) { 768 continue; 769 } 770 if (numGood == j) { 771 numGood++; 772 continue; 773 } 774 pixelData->data.F32[numGood] = pixelData->data.F32[j]; 775 pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j]; 776 pixelSources->data.U16[numGood] = pixelSources->data.U16[j]; 777 pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j]; 778 pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j]; 779 numGood++; 780 } 781 pixelData->n = numGood; 782 pixelWeights->n = numGood; 783 pixelSources->n = numGood; 784 pixelLimits->n = numGood; 785 pixelVariances->n = numGood; 786 totalClipped++; 787 num--; 788 } 789 } 790 } 791 792 return true; 636 793 } 637 794 … … 639 796 // Ensure the input array of pmStackData is valid, and get some details out of it 640 797 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 798 int *num, // Number of inputs 643 799 int *numCols, int *numRows, // Size of (sky) images 644 psArray *input // Input array of pmStackData to validate 800 const psArray *input, // Input array of pmStackData to validate 801 const pmReadout *output // Output readout 645 802 ) 646 803 { … … 668 825 PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false); 669 826 } 670 *haveRejects = (data->reject != NULL);827 bool haveRejects = (data->reject != NULL); // Do we have rejected pixels? 671 828 672 829 // Make sure the rest correspond with the first … … 681 838 return false; 682 839 } 683 if (( *haveRejects && !data->reject) || (data->reject && !*haveRejects)) {840 if ((haveRejects && !data->reject) || (data->reject && !haveRejects)) { 684 841 psError(PS_ERR_UNEXPECTED_NULL, true, 685 842 "The rejected pixels are specified in some but not all inputs."); … … 697 854 PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false); 698 855 } 856 } 857 858 PM_ASSERT_READOUT_NON_NULL(output, false); 859 if (output->image) { 860 PS_ASSERT_IMAGE_NON_NULL(output->image, false); 861 PS_ASSERT_IMAGE_TYPE(output->image, PS_TYPE_F32, false); 862 PS_ASSERT_IMAGE_NON_NULL(output->mask, false); 863 PS_ASSERT_IMAGE_TYPE(output->mask, PS_TYPE_IMAGE_MASK, false); 864 PS_ASSERT_IMAGES_SIZE_EQUAL(output->image, output->mask, false); 699 865 } 700 866 … … 782 948 783 949 /// 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); 950 bool pmStackCombine(pmReadout *combined, psArray *input, psImageMaskType maskVal, psImageMaskType maskSuspect, 951 psImageMaskType bad, int kernelSize, float iter, float rej, float sys, float olympic, 952 bool useVariance, bool safe, bool rejection) 953 { 789 954 bool haveVariances; // Do we have the variance maps? 790 bool haveRejects; // Do we have lists of rejected pixels?791 955 int num; // Number of inputs 792 956 int numCols, numRows; // Size of (sky) images 793 if (!validateInputData(&haveVariances, & haveRejects, &num, &numCols, &numRows, input)) {957 if (!validateInputData(&haveVariances, &num, &numCols, &numRows, input, combined)) { 794 958 return false; 795 959 } 796 960 PS_ASSERT_INT_NONNEGATIVE(kernelSize, false); 797 961 PS_ASSERT_INT_POSITIVE(bad, false); 798 PS_ASSERT_INT_NONNEGATIVE(numIter, false);799 962 if (isnan(rej)) { 800 PS_ASSERT_ INT_EQUAL(numIter, 0, false);963 PS_ASSERT_FLOAT_EQUAL(iter, 0, false); 801 964 } else { 965 PS_ASSERT_FLOAT_LARGER_THAN(iter, 0, false); 802 966 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 967 } 812 968 if (useVariance && !haveVariances) { … … 833 989 } 834 990 #endif 835 if (!haveRejects && !data->inspect) { 836 data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER); 991 if (!rejection) { 992 // Ensure pixels can be put on the appropriate list 993 if (!data->inspect) { 994 data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER); 995 } 996 if (!data->reject) { 997 data->reject = psPixelsAllocEmpty(PIXEL_LIST_BUFFER); 998 } 837 999 } 838 1000 } … … 863 1025 combineBuffer *buffer = combineBufferAlloc(num); 864 1026 865 if (haveRejects) { 866 psImage *combinedImage = combined->image; // Combined image 867 psImage *combinedMask = combined->mask; // Combined mask 868 psImage *combinedVariance = combined->variance; // Combined variance map 869 870 psArray *pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols, 871 minInputRows, maxInputRows); // Map of pixels to source 872 psPixels *pixels = NULL; // Total list of pixels, with no duplicates 1027 // Pull the products out, allocate if necessary 1028 psImage *combinedImage = combined->image; // Combined image 1029 if (!combinedImage) { 1030 combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32); 1031 combinedImage = combined->image; 1032 } 1033 psImage *combinedMask = combined->mask; // Combined mask 1034 if (!combinedMask) { 1035 combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK); 1036 combinedMask = combined->mask; 1037 } 1038 1039 psImage *combinedVariance = combined->variance; // Combined variance map 1040 if (haveVariances && !combinedVariance) { 1041 combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32); 1042 combinedVariance = combined->variance; 1043 } 1044 1045 // Set up rejection list 1046 psArray *pixelMap = NULL; // Map of pixels to source 1047 if (rejection) { 1048 pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols, minInputRows, maxInputRows); 1049 } 1050 1051 // Combine each pixel 1052 for (int y = minInputRows; y < maxInputRows; y++) { 1053 for (int x = minInputCols; x < maxInputCols; x++) { 1054 #ifdef TESTING 1055 if (x == TEST_X && y == TEST_Y) { 1056 fprintf(stderr, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n", 1057 x, y, maskVal, bad, iter, rej, sys, olympic, useVariance, safe, rejection); 1058 } 1059 #endif 1060 psVector *reject = NULL; // Images to reject for this pixel 1061 if (rejection) { 1062 reject = pixelMapQuery(pixelMap, minInputCols, minInputRows, x, y); 1063 #ifdef TESTING 1064 if (x == TEST_X && y == TEST_Y) { 1065 fprintf(stderr, "Rejected inputs: "); 1066 if (!reject) { 1067 fprintf(stderr, "<none>\n"); 1068 } else { 1069 for (int i = 0; i < reject->n; i++) { 1070 fprintf(stderr, "%d ", reject->data.U16[i]); 1071 } 1072 fprintf(stderr, "\n"); 1073 } 1074 } 1075 #endif 1076 } 1077 1078 int num; // Number of good pixels 1079 bool suspect; // Suspect pixels in stack? 1080 combineExtract(&num, &suspect, buffer, combinedImage, combinedMask, combinedVariance, 1081 input, weights, addVariance, reject, x, y, maskVal, maskSuspect); 1082 combinePixels(combinedImage, combinedMask, combinedVariance, num, buffer, x, y, bad, safe); 1083 1084 if (iter > 0) { 1085 combineTest(num, suspect, input, buffer, x, y, iter, rej, sys, olympic, 1086 useVariance, safe); 1087 } 1088 } 1089 } 1090 1091 psFree(pixelMap); 1092 psFree(weights); 1093 psFree(buffer); 1094 1095 1096 1097 #ifndef PS_NO_TRACE 1098 if (!rejection && psTraceGetLevel("psModules.imcombine") >= 5) { 873 1099 for (int i = 0; i < num; i++) { 874 pmStackData *data = input->data[i]; // Stack ing data; contains the list of pixels875 if (!data ) {1100 pmStackData *data = input->data[i]; // Stack data for this input 1101 if (!data || !data->inspect) { 876 1102 continue; 877 1103 } 878 pixels = psPixelsConcatenate(pixels, data->reject); 879 } 880 pixels = psPixelsDuplicates(pixels, pixels); 881 882 if (entire) { 883 // Combine entire image 884 for (int y = minInputRows; y < maxInputRows; y++) { 885 for (int x = minInputCols; x < maxInputCols; x++) { 886 psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows, 887 x, y); // Reject these images 888 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights, 889 addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard, 890 useVariance, safe, rejectInspect, buffer); 891 } 892 } 893 } else { 894 // Only combine previously rejected pixels 895 for (int i = 0; i < pixels->n; i++) { 896 // Pixel coordinates are in the frame of the original image 897 int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of interest 898 if (x < minInputCols || x >= maxInputCols || y < minInputRows || y >= maxInputRows) { 899 continue; 900 } 901 psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows, 902 x, y); // Reject these images 903 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights, 904 addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard, 905 useVariance, safe, rejectInspect, buffer); 906 } 907 } 908 psFree(pixels); 909 psFree(pixelMap); 910 } else { 911 // Pull the products out, allocate if necessary 912 psImage *combinedImage = combined->image; // Combined image 913 if (!combinedImage) { 914 combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32); 915 combinedImage = combined->image; 916 } 917 psImage *combinedMask = combined->mask; // Combined mask 918 if (!combinedMask) { 919 combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK); 920 combinedMask = combined->mask; 921 } 922 923 psImage *combinedVariance = combined->variance; // Combined variance map 924 if (haveVariances && !combinedVariance) { 925 combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32); 926 combinedVariance = combined->variance; 927 } 928 929 for (int y = minInputRows; y < maxInputRows; y++) { 930 for (int x = minInputCols; x < maxInputCols; x++) { 931 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights, 932 addVariance, NULL, x, y, maskVal, bad, numIter, rej, sys, discard, 933 useVariance, safe, rejectInspect, buffer); 934 } 935 } 936 937 #ifndef PS_NO_TRACE 938 if (psTraceGetLevel("psModules.imcombine") >= 5) { 939 for (int i = 0; i < num; i++) { 940 pmStackData *data = input->data[i]; // Stack data for this input 941 if (!data || !data->inspect) { 942 continue; 943 } 944 psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n); 945 } 946 } 947 #endif 948 } 949 950 psFree(weights); 951 psFree(buffer); 1104 psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n); 1105 } 1106 } 1107 #endif 952 1108 953 1109 return true; -
Property svn:mergeinfo
set to (toggle deleted branches)
Note:
See TracChangeset
for help on using the changeset viewer.
