Changeset 16624
- Timestamp:
- Feb 22, 2008, 4:33:59 PM (18 years ago)
- File:
-
- 1 edited
-
trunk/psModules/src/imcombine/pmStack.c (modified) (15 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules/src/imcombine/pmStack.c
r16608 r16624 8 8 * @author GLG, MHPCC 9 9 * 10 * @version $Revision: 1.2 0$ $Name: not supported by cvs2svn $11 * @date $Date: 2008-02-2 2 20:05:17$10 * @version $Revision: 1.21 $ $Name: not supported by cvs2svn $ 11 * @date $Date: 2008-02-23 02:33:59 $ 12 12 * Copyright 2004-2007 Institute for Astronomy, University of Hawaii 13 13 * … … 34 34 #define MASK_BAD 0x01 // Mask value for pixels that have formerly been masked as bad 35 35 #define MASK_SUSPECT 0x02 // Mask value for pixels that are suspected bad (by pmStackCombine) 36 37 #define NUM_DIRECT_STDEV 5 // For less than this number of values, measure stdev directly 36 38 37 39 … … 42 44 psVector *masks; // Pixel masks 43 45 psVector *weights; // Pixel weights 46 psVector *sources; // Pixel sources (which image did they come from?) 44 47 psVector *sort; // Buffer for sorting (to get a robust estimator of the standard dev) 45 48 } combineBuffer; … … 50 53 psFree(buffer->masks); 51 54 psFree(buffer->weights); 55 psFree(buffer->sources); 52 56 psFree(buffer->sort); 53 57 return; … … 64 68 buffer->masks = psVectorAlloc(numImages, PS_TYPE_MASK); 65 69 buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32); 70 buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16); 66 71 buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32); 67 72 return buffer; … … 110 115 } 111 116 112 // Generate a standard deviation for the combination117 // Return the median and standard deviation for the pixels 113 118 // Not using psVectorStats because it has additional allocations which slow things down 114 static bool combination Stdev(float *median, // Median value, to return115 float *stdev, // Standard deviation value, to return116 const psVector *values, // Values to combine117 const psVector *masks, // Mask to apply118 psMaskType maskVal, // Value to mask119 psVector *sortBuffer // Buffer for sorting120 )119 static bool combinationMedianStdev(float *median, // Median value, to return 120 float *stdev, // Standard deviation value, to return 121 const psVector *values, // Values to combine 122 const psVector *masks, // Mask to apply 123 psMaskType maskVal, // Value to mask 124 psVector *sortBuffer // Buffer for sorting 125 ) 121 126 { 122 127 assert(values && masks); … … 126 131 assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32); 127 132 133 // Need to filter out clipped values 128 134 int num = 0; // Number of valid values 129 135 for (int i = 0; i < values->n; i++) { 130 if (!(masks->data.PS_TYPE_MASK_DATA[i] & maskVal)) {136 if (!(masks->data.PS_TYPE_MASK_DATA[i])) { 131 137 sortBuffer->data.F32[num++] = values->data.F32[i]; 132 138 } … … 136 142 return false; 137 143 } 138 *median = num % 2 ? sortBuffer->data.F32[num / 2] : 139 (sortBuffer->data.F32[num / 2] + sortBuffer->data.F32[num / 2 + 1]) / 2.0 ; 140 #if 0 141 if (num < NUM_DIRECT_STDEV) { 142 #endif 143 // If there are not many values, the direct standard deviation is better 144 double sum = 0.0; 145 for (int i = 0; i < num; i++) { 146 sum += PS_SQR(sortBuffer->data.F32[i] - *median); 147 } 148 *stdev = sqrt(sum / (float)(num - 1)); 149 #if 0 144 145 if (num == 3) { 146 // Attempt to measure standard deviation with only three values (and one of those possibly corrupted) 147 *median = sortBuffer->data.F32[1]; 148 if (stdev) { 149 float diff1 = sortBuffer->data.F32[0] - *median; 150 float diff2 = sortBuffer->data.F32[2] - *median; 151 // This factor of sqrt(2) might not be exact, but it's about right 152 *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2)); 153 } 150 154 } else { 151 *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] - sortBuffer->data.F32[(int)(0.25 * num)]); 152 } 153 #endif 155 *median = num % 2 ? sortBuffer->data.F32[num / 2] : 156 (sortBuffer->data.F32[num / 2] + sortBuffer->data.F32[num / 2 + 1]) / 2.0; 157 if (stdev) { 158 if (num <= NUM_DIRECT_STDEV) { 159 // If there are not many values, the direct standard deviation is better 160 double sum = 0.0; 161 for (int i = 0; i < num; i++) { 162 sum += PS_SQR(sortBuffer->data.F32[i] - *median); 163 } 164 *stdev = sqrt(sum / (float)(num - 1)); 165 } else { 166 // Standard deviation from the interquartile range 167 *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] - 168 sortBuffer->data.F32[(int)(0.25 * num)]); 169 } 170 } 171 } 154 172 155 173 return true; … … 198 216 } 199 217 218 // Common path for a bad combined pixel 219 static inline bool combineBad(psImage *image, // Combined image 220 psImage *mask, // Combined mask 221 psImage *weight, // Combined variance map 222 int x, int y, // Coordinates of interest 223 psMaskType bad // Value for bad pixels 224 ) 225 { 226 image->data.F32[y][x] = NAN; 227 if (weight) { 228 weight->data.F32[y][x] = NAN; 229 } 230 mask->data.PS_TYPE_MASK_DATA[y][x] = bad; 231 return false; 232 } 233 234 // Mark a pixel for inspection 235 static inline void combineInspect(const psArray *inputs, // Stack data 236 int x, int y, // Pixel 237 int source // Source image index 238 ) 239 { 240 pmStackData *data = inputs->data[source]; // Stack data of interest 241 if (!data) { 242 return; 243 } 244 data->pixels = psPixelsAdd(data->pixels, PIXEL_LIST_BUFFER, x, y); 245 return; 246 } 247 200 248 // Given a stack of images, combine with optional rejection. 201 249 // Pixels in the stack that are rejected are marked for subsequent inspection 202 static boolcombinePixels(psImage *image, // Combined image, for output250 static void combinePixels(psImage *image, // Combined image, for output 203 251 psImage *mask, // Combined mask, for output 204 252 psImage *weight, // Combined variance map, for output … … 211 259 int numIter, // Number of rejection iterations 212 260 float rej, // Number of standard deviations at which to reject 261 bool useVariance, // Use variance for rejection when combining? 262 bool safe, // Combine safely? 213 263 combineBuffer *buffer // Buffer for combination; to avoid multiple allocations 214 264 ) … … 221 271 assert(rej > 0); 222 272 assert(buffer); 223 224 int num = inputs->n; // Number of images to combine 273 assert((useVariance && weight) || !useVariance); 225 274 226 275 psVector *pixelData = buffer->pixels; // Values for the pixel of interest 227 276 psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest 228 277 psVector *pixelWeights = buffer->weights; // Weights for the pixel of interest 278 psVector *pixelSources = buffer->sources; // Sources for the pixel of interest 229 279 psVector *sort = buffer->sort; // Sort buffer 230 280 231 281 // Extract the pixel and mask data 232 int numBad = 0; // Number of bad pixels 233 for (int i = 0; i < num; i++) { 282 int num = 0; // Number of good images 283 for (int i = 0, j = 0; i < inputs->n; i++) { 284 // Check if this pixel has been rejected. Assumes that the rejection pixel list is sorted --- it 285 // should be because of how pixelMapGenerate works 286 if (reject && reject->data.U16[j] == i) { 287 j++; 288 continue; 289 } 290 234 291 pmStackData *data = inputs->data[i]; // Stack data of interest 235 292 if (!data) { 236 293 continue; 237 294 } 295 238 296 int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout 297 psImage *mask = data->readout->mask; // Mask of interest 298 if (mask->data.PS_TYPE_MASK_DATA[yIn][xIn] & maskVal) { 299 continue; 300 } 301 239 302 psImage *image = data->readout->image; // Image of interest 240 303 psImage *weight = data->readout->weight; // Weight map of interest 241 psImage *mask = data->readout->mask; // Mask of interest 242 pixelData->data.F32[i] = image->data.F32[yIn][xIn]; 304 pixelData->data.F32[num] = image->data.F32[yIn][xIn]; 243 305 if (weight) { 244 pixelWeights->data.F32[i] = weight->data.F32[yIn][xIn]; 245 } 246 pixelMasks->data.PS_TYPE_MASK_DATA[i] = mask->data.PS_TYPE_MASK_DATA[yIn][xIn]; 247 if (pixelMasks->data.PS_TYPE_MASK_DATA[i] & maskVal) { 248 numBad++; 249 } 250 } 251 if (numBad == num) { 252 // Catch the case where everything is masked 253 image->data.F32[y][x] = NAN; 254 if (weight) { 255 weight->data.F32[y][x] = NAN; 256 } 257 mask->data.PS_TYPE_MASK_DATA[y][x] = bad; 258 return false; 259 } 260 261 if (reject) { 262 for (int i = 0; i < reject->n; i++) { 263 pixelMasks->data.PS_TYPE_MASK_DATA[reject->data.U16[i]] |= maskVal; 264 } 265 } 266 267 // Record the value derived with no clipping, because pixels rejected using the harsh clipping applied in 268 // the first pass might later be accepted. 269 float mean, variance; // Mean and variance of the combination 270 if (!combinationMean(&mean, pixelData, weights, pixelMasks, maskVal) || 271 (weight && !combinationVariance(&variance, pixelWeights, weights, pixelMasks, maskVal))) { 272 image->data.F32[y][x] = NAN; 273 mask->data.PS_TYPE_MASK_DATA[y][x] = bad; 274 if (weight) { 275 weight->data.F32[y][x] = NAN; 276 } 277 return false; 278 } 279 280 image->data.F32[y][x] = mean; 306 pixelWeights->data.F32[num] = weight->data.F32[yIn][xIn]; 307 } 308 pixelSources->data.U16[num] = i; 309 num++; 310 } 311 pixelData->n = num; 281 312 if (weight) { 282 weight->data.F32[y][x] = variance; 283 } 284 mask->data.PS_TYPE_MASK_DATA[y][x] = 0; 285 286 // The clipping that follows is solely to identify suspect pixels. 287 // These suspect pixels will be inspected in more detail by other functions. 288 long numClipped = LONG_MAX; // Number of pixels clipped 289 psMaskType ignore = maskVal | bad; // Ignore values with this mask value 290 for (int i = 0; i < numIter && numClipped > 0; i++) { 291 float median, stdev; // Median and stdev of the combination, for rejection 292 // Only get the mean if it's not the first iteration (because we got the mean earlier) 293 if (!combinationStdev(&median, &stdev, pixelData, pixelMasks, maskVal, sort)) { 294 image->data.F32[y][x] = NAN; 295 mask->data.PS_TYPE_MASK_DATA[y][x] = bad; 296 weight->data.F32[y][x] = NAN; 297 return false; 298 } 299 300 float limit = rej * stdev; // Rejection limit 301 numClipped = 0; 302 for (int j = 0; j < num; j++) { 303 if (pixelMasks->data.PS_TYPE_MASK_DATA[j] & ignore) { 304 continue; 305 } 306 float diff = pixelData->data.F32[j] - median; 307 if (fabsf(diff) > limit) { 308 // Add the pixel as one to inspect 309 pmStackData *data = inputs->data[j]; // Stack data of interest 310 if (!data) { 311 continue; 312 } 313 data->pixels = psPixelsAdd(data->pixels, PIXEL_LIST_BUFFER, x, y); 314 // Mask it so it's not considered in other iterations within this function 315 pixelMasks->data.PS_TYPE_MASK_DATA[j] |= bad; 316 numClipped++; 317 } 318 } 319 } 320 321 return true; 313 pixelWeights->n = num; 314 } 315 pixelSources->n = num; 316 317 // The sensible thing to do varies according to how many good pixels there are. 318 // Default option is that the pixel is bad 319 float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map 320 psMaskType maskValue = bad; // Value for combined mask 321 switch (num) { 322 case 0: 323 // Nothing to combine: it's bad 324 break; 325 case 1: { 326 // Accept the single pixel unless we have to be safe 327 if (!safe) { 328 imageValue = pixelData->data.F32[0]; 329 if (weight) { 330 varianceValue = pixelWeights->data.F32[0]; 331 } 332 maskValue = 0; 333 } 334 break; 335 } 336 case 2: { 337 // Accept the mean of the pixels only if we're going to reject based on the variance, or we're not 338 // playing safe 339 if (useVariance || !safe) { 340 float mean, variance; // Mean and variance from combination 341 if (combinationMean(&mean, pixelData, weights, pixelMasks, maskVal) && 342 (!weight || combinationVariance(&variance, pixelWeights, weights, pixelMasks, maskVal))) { 343 imageValue = mean; 344 varianceValue = variance; 345 maskValue = 0; 346 } 347 } 348 if (useVariance) { 349 // Use variance to check that the two are consistent 350 float diff = pixelData->data.F32[0] - pixelData->data.F32[1]; 351 float sigma = sqrtf(pixelWeights->data.F32[0] + pixelWeights->data.F32[1]); 352 if (fabs(diff) > rej * sigma) { 353 // Not consistent: mark both for inspection 354 combineInspect(inputs, x, y, pixelSources->data.U16[0]); 355 combineInspect(inputs, x, y, pixelSources->data.U16[1]); 356 } 357 } 358 break; 359 } 360 default: { 361 // Record the value derived with no clipping, because pixels rejected using the harsh clipping 362 // applied in the first pass might later be accepted. 363 float mean, variance; // Mean and variance of the combination 364 if (!combinationMean(&mean, pixelData, weights, pixelMasks, maskVal) || 365 (weight && !combinationVariance(&variance, pixelWeights, weights, pixelMasks, maskVal))) { 366 break; 367 } 368 imageValue = mean; 369 varianceValue = variance; 370 maskValue = 0; 371 372 // Prepare for clipping iteration 373 if (numIter > 0) { 374 pixelMasks->n = num; 375 psVectorInit(pixelMasks, 0); 376 if (useVariance) { 377 // Convert to rejection limits 378 for (int i = 0; i < num; i++) { 379 pixelWeights->data.F32[i] = rej * sqrtf(pixelWeights->data.F32[i]); 380 } 381 } 382 } 383 384 // The clipping that follows is solely to identify suspect pixels. 385 // These suspect pixels will be inspected in more detail by other functions. 386 int numClipped = INT_MAX; // Number of pixels clipped per iteration 387 int totalClipped = 0; // Total number of pixels clipped 388 for (int i = 0; i < numIter && numClipped > 0 && num - totalClipped > 2; i++) { 389 numClipped = 0; 390 float median, stdev; // Median and stdev of the combination, for rejection 391 392 if (!combinationMedianStdev(&median, useVariance ? NULL : &stdev, pixelData, pixelMasks, 393 maskVal, sort)) { 394 psWarning("Bad median/stdev at %d,%d", x, y); 395 break; 396 } 397 398 float limit; // Rejection limit, when rejecting based on data properties 399 if (!useVariance) { 400 limit = rej * stdev; 401 } 402 403 for (int j = 0; j < num; j++) { 404 if (pixelMasks->data.PS_TYPE_MASK_DATA[j]) { 405 continue; 406 } 407 float diff = fabsf(pixelData->data.F32[j] - median); // Difference from expected 408 if (diff > (useVariance ? pixelWeights->data.F32[i] : limit)) { 409 pixelMasks->data.PS_TYPE_MASK_DATA[j] = 0xff; 410 combineInspect(inputs, x, y, pixelSources->data.U16[j]); 411 numClipped++; 412 totalClipped++; 413 } 414 } 415 } 416 break; 417 } 418 } 419 420 return; 322 421 } 323 422 … … 549 648 /// Stack input images 550 649 bool pmStackCombine(pmReadout *combined, psArray *input, psMaskType maskVal, psMaskType bad, 551 int numIter, float rej )650 int numIter, float rej, bool useVariance, bool safe) 552 651 { 553 652 PS_ASSERT_PTR_NON_NULL(combined, false); … … 570 669 PS_ASSERT_IMAGES_SIZE_EQUAL(combined->image, combined->mask, false); 571 670 } 671 if (useVariance && !haveWeights) { 672 psWarning("Unable to use variance in rejection if no variance maps supplied --- option turned off"); 673 useVariance = false; 674 } 572 675 573 676 // Pull out the weightings … … 629 732 psVector *reject = pixelMapQuery(pixelMap, x, y); // Inspect these images closely 630 733 combinePixels(combinedImage, combinedMask, combinedWeight, input, weights, reject, x, y, 631 maskVal, bad, numIter, rej, buffer);734 maskVal, bad, numIter, rej, useVariance, safe, buffer); 632 735 } 633 736 psTrace("psModules.imcombine", 5, "Additional %ld pixels fixed.\n", pixels->n); … … 667 770 for (int x = minInputCols; x < maxInputCols; x++) { 668 771 combinePixels(combinedImage, combinedMask, combinedWeights, input, weights, NULL, x, y, 669 maskVal, bad, numIter, rej, buffer);772 maskVal, bad, numIter, rej, useVariance, safe, buffer); 670 773 } 671 774 }
Note:
See TracChangeset
for help on using the changeset viewer.
