Changeset 14272 for trunk/psModules/src/imcombine/pmStack.c
- Timestamp:
- Jul 17, 2007, 1:33:09 PM (19 years ago)
- File:
-
- 1 edited
-
trunk/psModules/src/imcombine/pmStack.c (modified) (19 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules/src/imcombine/pmStack.c
r14126 r14272 8 8 * @author GLG, MHPCC 9 9 * 10 * @version $Revision: 1. 7$ $Name: not supported by cvs2svn $11 * @date $Date: 2007-07-1 1 01:32:41$10 * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $ 11 * @date $Date: 2007-07-17 23:33:09 $ 12 12 * 13 13 * Copyright 2004-2007 Institute for Astronomy, University of Hawaii … … 41 41 psVector *pixels; // Pixel values 42 42 psVector *masks; // Pixel masks 43 psStats *stats; // Statistics 43 psVector *weights; // Pixel weights 44 psVector *sort; // Buffer for sorting (to get a robust estimator of the standard dev) 44 45 } combineBuffer; 45 46 … … 48 49 psFree(buffer->pixels); 49 50 psFree(buffer->masks); 50 psFree(buffer->stats); 51 psFree(buffer->weights); 52 psFree(buffer->sort); 51 53 return; 52 54 } … … 61 63 buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32); 62 64 buffer->masks = psVectorAlloc(numImages, PS_TYPE_MASK); 63 buffer->stats = psStatsAlloc(stat); 65 buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32); 66 buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32); 64 67 65 68 return buffer; … … 77 80 78 81 82 // Generate a mean value for the combination 83 // Not using psVectorStats because it assumes that the weights are errors, and weights by 1/error^2 84 static bool combinationMean(float *mean, // Mean value, to return 85 const psVector *values, // Values to combine 86 const psVector *weights, // Weights to apply 87 const psVector *masks, // Mask to apply 88 psMaskType maskVal // Value to mask 89 ) 90 { 91 assert(values && weights && masks); 92 assert(values->n == weights->n); 93 assert(values->n == masks->n); 94 assert(values->type.type == PS_TYPE_F32); 95 assert(weights->type.type == PS_TYPE_F32); 96 assert(masks->type.type == PS_TYPE_MASK); 97 98 float sumValueWeight = 0.0; // Sum of the value multiplied by the weight 99 float sumWeight = 0.0; // Sum of the weights 100 for (int i = 0; i < values->n; i++) { 101 if (!(masks->data.PS_TYPE_MASK_DATA[i] & maskVal)) { 102 sumValueWeight += values->data.F32[i] * weights->data.F32[i]; 103 sumWeight += weights->data.F32[i]; 104 } 105 } 106 if (sumWeight <= 0) { 107 return false; 108 } 109 110 *mean = sumValueWeight / sumWeight; 111 return true; 112 } 113 114 // Generate a standard deviation for the combination 115 // Not using psVectorStats because it has additional allocations which slow things down 116 static bool combinationStdev(float *stdev, // Mean value, to return 117 const psVector *values, // Values to combine 118 const psVector *masks, // Mask to apply 119 psMaskType maskVal, // Value to mask 120 psVector *sortBuffer // Buffer for sorting 121 ) 122 { 123 assert(values && masks); 124 assert(values->n == masks->n); 125 assert(values->type.type == PS_TYPE_F32); 126 assert(masks->type.type == PS_TYPE_MASK); 127 assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32); 128 129 int num = 0; // Number of valid values 130 for (int i = 0; i < values->n; i++) { 131 if (!(masks->data.PS_TYPE_MASK_DATA[i] & maskVal)) { 132 sortBuffer->data.F32[num++] = values->data.F32[i]; 133 } 134 } 135 sortBuffer->n = num; 136 if (!psVectorSortInPlace(sortBuffer)) { 137 return false; 138 } 139 *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] - sortBuffer->data.F32[(int)(0.25 * num)]); 140 141 return true; 142 } 143 144 // Generate a variance value for the combination 145 static bool combinationVariance(float *variance, // Variance value, to return 146 const psVector *variances, // Pixel variances to combine 147 const psVector *weights, // Image weights to apply 148 const psVector *masks, // Mask to apply 149 psMaskType maskVal // Value to mask 150 ) 151 { 152 assert(variances && weights && masks); 153 assert(variances->n == weights->n); 154 assert(variances->n == masks->n); 155 assert(variances->type.type == PS_TYPE_F32); 156 assert(weights->type.type == PS_TYPE_F32); 157 assert(masks->type.type == PS_TYPE_MASK); 158 159 // Get the variance in the combination. We're not using the input pixel variances to generate a 160 // weighted average for the pixel flux (because that introduces systematic biases), so the variance 161 // of the output pixel value should simply be: 162 // simga^2 = sum(weight_i^2 * sigma_i^2) / (sum(weight_i))^2 163 // This reduces, when the weights are all identically unity, to: 164 // variance_combination = sum(variance_i) / N^2 165 // and if the variances are all equal: 166 // variance_combination = variance_individual / N 167 // which makes sense --- the standard deviation of the combination is reduced by a factor of sqrt(N). 168 169 float sumWeights = 0.0; // Sum of the global weights 170 float sumVarianceWeights = 0.0; // Sum of the pixel variances multiplied by the global weights 171 for (int i = 0; i < variances->n; i++) { 172 if (!(masks->data.PS_TYPE_MASK_DATA[i] & maskVal)) { 173 sumVarianceWeights += variances->data.F32[i] * PS_SQR(weights->data.F32[i]); 174 sumWeights += weights->data.F32[i]; 175 } 176 } 177 178 if (sumWeights <= 0) { 179 return false; 180 } 181 182 *variance = sumVarianceWeights / PS_SQR(sumWeights); 183 return true; 184 } 185 79 186 // Given a stack of images, combine with optional rejection. 80 187 // Pixels in the stack that are rejected are marked for subsequent 81 188 static bool combinePixels(psImage *image, // Combined image, for output 82 189 psImage *mask, // Combined mask, for output 190 psImage *weight, // Combined variance map, for output 83 191 const psArray *inputs, // Stack data 84 const psVector *weights, // Weights for data, or NULL192 const psVector *weights, // Global (single value) weights for data, or NULL 85 193 const psVector *reject, // Indices of pixels to reject, or NULL 86 194 int x, int y, // Coordinates of interest … … 104 212 psVector *pixelData = buffer->pixels; // Values for the pixel of interest 105 213 psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest 106 ps Stats *stats = buffer->stats; // Statistics107 214 psVector *pixelWeights = buffer->weights; // Weights for the pixel of interest 215 psVector *sort = buffer->sort; // Sort buffer 108 216 109 217 // Extract the pixel and mask data … … 112 220 pmStackData *data = inputs->data[i]; // Stack data of interest 113 221 psImage *image = data->sky->image; // Image of interest 222 psImage *weight = data->sky->weight; // Weight map of interest 114 223 psImage *mask = data->sky->mask; // Mask of interest 115 224 pixelData->data.F32[i] = image->data.F32[y][x]; 225 pixelWeights->data.F32[i] = weight->data.F32[y][x]; 116 226 pixelMasks->data.PS_TYPE_MASK_DATA[i] = mask->data.PS_TYPE_MASK_DATA[y][x]; 117 227 if (pixelMasks->data.PS_TYPE_MASK_DATA[i] & maskVal) { … … 122 232 // Catch the case where everything is masked 123 233 image->data.F32[y][x] = NAN; 234 if (weight) { 235 weight->data.F32[y][x] = NAN; 236 } 124 237 mask->data.PS_TYPE_MASK_DATA[y][x] = bad; 125 238 return false; … … 134 247 // Record the value derived with no clipping, because pixels rejected using the harsh clipping applied in 135 248 // the first pass might later be accepted. 136 stats->options |= PS_STAT_SAMPLE_MEAN; 137 if (!psVectorStats(stats, pixelData, weights, pixelMasks, maskVal)) { 138 // Can't do anything about an error except give it a NAN and mask 139 psErrorClear(); 249 float mean, variance; // Mean and variance of the combination 250 if (!combinationMean(&mean, pixelData, weights, pixelMasks, maskVal) || 251 (weight && !combinationVariance(&variance, weights, pixelWeights, pixelMasks, maskVal))) { 140 252 image->data.F32[y][x] = NAN; 141 253 mask->data.PS_TYPE_MASK_DATA[y][x] = bad; 142 return false; 143 } 144 image->data.F32[y][x] = stats->sampleMean; 254 if (weight) { 255 weight->data.F32[y][x] = NAN; 256 } 257 return false; 258 } 259 260 image->data.F32[y][x] = mean; 261 if (weight) { 262 weight->data.F32[y][x] = variance; 263 } 145 264 mask->data.PS_TYPE_MASK_DATA[y][x] = 0; 146 265 147 stats->options &= ~ PS_STAT_SAMPLE_MEAN;148 266 // The clipping that follows is solely to identify suspect pixels. 267 // These suspect pixels will be inspected in more detail by other functions. 149 268 long numClipped = LONG_MAX; // Number of pixels clipped 150 269 psMaskType ignore = maskVal | bad; // Ignore values with this mask value 151 270 for (int i = 0; i < numIter && numClipped > 0; i++) { 152 #if 1 153 float mean = stats->sampleMedian; 154 float stdev = 0.74 * (stats->sampleUQ - stats->sampleLQ); // Rough estimate of the standard deviation 155 #else 156 float mean = stats->robustMedian; 157 float stdev = stats->robustStdev; 158 #endif 271 float stdev; // Standard deviation of the combination, for rejection 272 // Only get the mean if it's not the first iteration (because we got the mean earlier) 273 if ((i != 0 && !combinationMean(&mean, pixelData, weights, pixelMasks, maskVal)) || 274 !combinationStdev(&stdev, pixelData, pixelMasks, maskVal, sort)) { 275 image->data.F32[y][x] = NAN; 276 mask->data.PS_TYPE_MASK_DATA[y][x] = bad; 277 weight->data.F32[y][x] = NAN; 278 return false; 279 } 280 159 281 float limit = rej * stdev; // Rejection limit 160 282 numClipped = 0; … … 165 287 float diff = pixelData->data.F32[j] - mean; 166 288 if (fabsf(diff) > limit) { 289 // Add the pixel as one to inspect 167 290 pmStackData *data = inputs->data[j]; // Stack data of interest 168 291 data->pixels = psPixelsAdd(data->pixels, PIXEL_LIST_BUFFER, x, y); 292 // Mask it so it's not considered in other iterations within this function 169 293 pixelMasks->data.PS_TYPE_MASK_DATA[j] |= bad; 170 294 numClipped++; 171 295 } 172 296 } 173 if (!psVectorStats(stats, pixelData, weights, pixelMasks, maskVal)) {174 // Can't do anything about an error except give it a NAN and mask175 psErrorClear();176 image->data.F32[y][x] = NAN;177 mask->data.PS_TYPE_MASK_DATA[y][x] = bad;178 return false;179 }180 297 } 181 298 … … 186 303 // Ensure the input array of pmStackData is valid, and get some details out of it 187 304 static bool validateInputData(bool *haveDetector, // Do we have the detector images? 188 bool *haveSky, // Do we have the sky images? 189 bool *havePixels, // Do we have lists of pixels? 190 int *num, // Number of inputs 191 int *numCols, int *numRows, // Size of (sky) images 192 psArray *input // Input array of pmStackData to validate 305 bool *haveSky, // Do we have the sky images? 306 bool *haveSkyWeights, // Do we have weights in the sky images? 307 bool *havePixels, // Do we have lists of pixels? 308 int *num, // Number of inputs 309 int *numCols, int *numRows, // Size of (sky) images 310 psArray *input // Input array of pmStackData to validate 193 311 ) 194 312 { … … 203 321 PS_ASSERT_IMAGE_NON_NULL(data->detector->image, false); 204 322 PS_ASSERT_IMAGE_NON_NULL(data->detector->mask, false); 323 PS_ASSERT_IMAGES_SIZE_EQUAL(data->detector->image, data->detector->mask, false); 324 PS_ASSERT_IMAGE_TYPE(data->detector->image, PS_TYPE_F32, false); 325 PS_ASSERT_IMAGE_TYPE(data->detector->mask, PS_TYPE_MASK, false); 205 326 } 206 327 *haveSky = (data->sky != NULL); 328 *haveSkyWeights = false; 207 329 if (*haveSky) { 208 330 PS_ASSERT_IMAGE_NON_NULL(data->sky->image, false); 331 PS_ASSERT_IMAGE_TYPE(data->sky->image, PS_TYPE_F32, false); 209 332 PS_ASSERT_IMAGE_NON_NULL(data->sky->mask, false); 333 PS_ASSERT_IMAGE_TYPE(data->sky->mask, PS_TYPE_MASK, false); 334 PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->mask, false); 210 335 *numCols = data->sky->image->numCols; 211 336 *numRows = data->sky->image->numRows; 337 if (data->sky->weight) { 338 *haveSkyWeights = true; 339 PS_ASSERT_IMAGE_NON_NULL(data->sky->weight, false); 340 PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->weight, false); 341 PS_ASSERT_IMAGE_TYPE(data->sky->weight, PS_TYPE_F32, false); 342 } 212 343 } 213 344 *havePixels = (data->pixels != NULL); … … 243 374 PS_ASSERT_IMAGE_SIZE(data->sky->image, *numCols, *numRows, false); 244 375 PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->mask, false); 376 if (*haveSkyWeights) { 377 PS_ASSERT_IMAGE_NON_NULL(data->sky->weight, false); 378 PS_ASSERT_IMAGES_SIZE_EQUAL(data->sky->image, data->sky->weight, false); 379 PS_ASSERT_IMAGE_TYPE(data->sky->weight, PS_TYPE_F32, false); 380 } 245 381 } 246 382 } … … 412 548 bool haveDetector; // Do we have the detector images? 413 549 bool haveSky; // Do we have the sky images? 550 bool haveSkyWeights; // Do we have the sky weight maps? 414 551 bool havePixels; // Do we have lists of pixels? 415 552 int num; // Number of inputs 416 553 int numCols, numRows; // Size of (sky) images 417 if (!validateInputData(&haveDetector, &haveSky, &havePixels, &num, &numCols, &numRows, input)) { 554 if (!validateInputData(&haveDetector, &haveSky, &haveSkyWeights, &havePixels, &num, 555 &numCols, &numRows, input)) { 418 556 return false; 419 557 } … … 461 599 psImage *combinedImage = combined->image; // Combined image 462 600 psImage *combinedMask = combined->mask; // Combined mask 601 psImage *combinedWeight = combined->weight; // Combined mask 463 602 464 603 psArray *pixelMap = pixelMapGenerate(input, numCols, numRows); // Map of pixels to source … … 472 611 int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of interest 473 612 psVector *reject = pixelMapQuery(pixelMap, x, y); // Inspect these images closely 474 combinePixels(combinedImage, combinedMask, input, weights, reject, x, y,613 combinePixels(combinedImage, combinedMask, combinedWeight, input, weights, reject, x, y, 475 614 maskVal, bad, numIter, rej, buffer); 476 615 } … … 491 630 } 492 631 632 psImage *combinedWeights = combined->weight; // Combined weight map 633 if (haveSkyWeights && !combinedWeights) { 634 combined->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32); 635 combinedWeights = combined->weight; 636 } 637 493 638 // Generate the pixel lists in which to place the rejected pixels 494 639 if (numIter != 0) { … … 501 646 for (int y = 0; y < numRows; y++) { 502 647 for (int x = 0; x < numCols; x++) { 503 combinePixels(combinedImage, combinedMask, input, weights, NULL, x, y,648 combinePixels(combinedImage, combinedMask, combinedWeights, input, weights, NULL, x, y, 504 649 maskVal, bad, numIter, rej, buffer); 505 650 } … … 538 683 bool haveDetector; // Do we have the detector images? 539 684 bool haveSky; // Do we have the sky images? 685 bool haveSkyWeights; // Do we have the sky weight maps? 540 686 bool havePixels; // Do we have lists of pixels? 541 687 int num; // Number of inputs 542 688 int numCols, numRows; // Size of (sky) images 543 if (!validateInputData(&haveDetector, &haveSky, &havePixels, &num, &numCols, &numRows, input)) { 689 if (!validateInputData(&haveDetector, &haveSky, &haveSkyWeights, &havePixels, 690 &num, &numCols, &numRows, input)) { 544 691 return false; 545 692 }
Note:
See TracChangeset
for help on using the changeset viewer.
