Changeset 41892 for trunk/psModules/src/detrend/pmPattern.c
- Timestamp:
- Nov 4, 2021, 6:05:18 PM (5 years ago)
- Location:
- trunk/psModules
- Files:
-
- 2 edited
-
. (modified) (1 prop)
-
src/detrend/pmPattern.c (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules
-
trunk/psModules/src/detrend/pmPattern.c
r41743 r41892 9 9 #define PATTERN_ROW_BKG_FIX 1 10 10 11 /* some in-line notes: 12 13 patternMaskRow sets the data value to NAN, inconsistent with new plan 14 15 here is the outline of pmPatternRow 16 17 * at this point, we have already done overscan subtraction, right? 18 19 * measure stats on the full cell (MEDIAN, STDEV) 20 ** subsample? 21 ** if it fails, it masks the entire cell and set the value to NAN 22 23 * calculate an upper and lower threshold (median +/- T * sigma) 24 * define a normalized x-coordinate ('index') : 25 ** see note below on chebys 26 27 * each row is treated independently 28 * pixels are masked for the fit if they are out-of-range 29 or if they are already masked 30 31 ** note that the clipping threshold will be larger if there 32 are pixels which have astronomical structures 33 34 a possible better option would be to set the threshold based on the median 35 and a sigma calculated from Poisson stats (do we know the gain?) 36 37 ** fit is allowed to proceed if even N+1 pixels exist, which is clearly too low 38 39 ** Remaining pixels are fitted with clip-fit 40 41 ** solution is subtracted from the data 42 (this is implemented with psPolynomial1DEvalVector) 43 perhaps faster if we fixed the order to 2 and hardwired the result 44 45 * after each row is fitted, the intercept (A value) is fitted 46 as a function of the y-coordinate and the result is subtracted 47 48 * the slope value is also fitted as a function of the column 49 and added back in -- I'm not sure I understand this step. 50 51 ***************** 52 53 ** what we calculate are related to chebychevs (domain is -1 : +1) 54 *** T0(x) = 1 55 *** T1(x) = x 56 *** T2(x) = 2x^2 - 1 57 58 *** we calculate y = A + Bx + Cx^2 59 60 a_0 + a_1 x + a_2 (2x^2 - 1) = A + B + Cx^2 61 62 a_1 = B 63 a_0 - a_2 = A 64 2 a_2 = C 65 66 a_0 = A + C/2 67 a_1 = B 68 a_2 = C/2 69 70 ***************** 71 72 I have 3 goals in re-working the code: 73 74 1) improve overall speed 75 2) improve reliability of the fit 76 3) skip fit if we can 77 78 Let's assume the signal in the cell is light + bias drift 79 80 The bias drift has an amplitude of ~5 - 10 DN 81 82 That makes a detectable source with ~N * a few counts (multiple pixels in a row) 83 84 So, the effective flux is ~10 * 5 = 50 DN 85 for which sky level is this value - 3 sigma? 86 87 50 / sqrt(sky sigma^2 * effective area) 88 89 area ~ 5pixels, sky sigma^2 = sky 90 91 10 * Npix / sqrt(sky * Npix) = 3 92 93 Npix = sky * (S/N)^2 / (peak^2) 94 95 sky = Npix * peak^2 / SN^2 96 97 if (sky < Npix * peak^2 / SN^2), we should skip: 98 99 Npix ~ 5 100 peak ~ 10 101 SN ~ 3 (or even less) 102 103 sky < 5 * 100 / 9 = 55 or so 104 105 106 To address these in order: 107 108 1) speed: 109 * the analysis threaded is not threaded: thread across cells 110 111 2) 112 113 */ 11 114 12 115 // Mask a row as bad … … 33 136 } 34 137 138 // Comparison and swap functions for sorting values directly 139 #define SORT_COMPARE(A,B) (sampleArray[A] < sampleArray[B]) 140 #define SORT_SWAP(TYPE,A,B) { \ 141 if (A != B) { \ 142 TYPE temp = sampleArray[A]; \ 143 sampleArray[A] = sampleArray[B]; \ 144 sampleArray[B] = temp; \ 145 } \ 146 } 147 35 148 ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 36 149 // Measurement and application 37 150 ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 38 151 152 bool pmPatternRowUnbinned(pmReadout *ro, int order, int iter, float rej, float thresh, 153 psStatsOptions clipMean, psStatsOptions clipStdev, 154 psImageMaskType maskVal, psImageMaskType maskBad); 155 156 157 bool pmPatternRowBinned(pmReadout *ro, int order, int iter, float rej, float thresh, 158 psStatsOptions clipMean, psStatsOptions clipStdev, 159 psImageMaskType maskVal, psImageMaskType maskBad); 160 161 162 // XXX allow user choice of binned vs unbinned analysis? 39 163 bool pmPatternRow(pmReadout *ro, int order, int iter, float rej, float thresh, 164 psStatsOptions clipMean, psStatsOptions clipStdev, 165 psImageMaskType maskVal, psImageMaskType maskBad) { 166 167 bool status = false; 168 if (true) { 169 status = pmPatternRowBinned(ro, order, iter, rej, thresh, clipMean, clipStdev, maskVal, maskBad); 170 } else { 171 status = pmPatternRowUnbinned(ro, order, iter, rej, thresh, clipMean, clipStdev, maskVal, maskBad); 172 } 173 return status; 174 } 175 176 // USE_BACKGROUND_STDEV: if TRUE, the analysis will use the measured robust stdev to clip the out-of-range pixles 177 // if FALSE, the stdev will be estimated based on the Poisson statistics of the median (sky level). 178 # define USE_BACKGROUND_STDEV 0 179 180 bool pmPatternRowUnbinned(pmReadout *ro, int order, int iter, float rej, float thresh, 40 181 psStatsOptions clipMean, psStatsOptions clipStdev, 41 182 psImageMaskType maskVal, psImageMaskType maskBad) … … 48 189 PS_ASSERT_FLOAT_LARGER_THAN(thresh, 0.0, false); 49 190 191 bool mdok; // Status of MD lookup 192 193 pmCell *cell = ro->parent; 194 pmChip *chip = cell->parent; 195 const char *chipName = psMetadataLookupStr(&mdok, chip->concepts, "CHIP.NAME"); // Name of chip 196 const char *cellName = psMetadataLookupStr(&mdok, cell->concepts, "CELL.NAME"); // Name of cell 197 50 198 psImage *image = ro->image; // Image to correct 51 199 psImage *mask = ro->mask; // Mask for image … … 55 203 psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator 56 204 if (!psImageBackground(stats, NULL, ro->image, ro->mask, maskVal, rng)) { 57 psWarning("Unable to calculate statistics on readout; masking entire readout.");205 psWarning("Unable to calculate statistics on readout; skipping pattern correction for %s, %s.", chipName, cellName); 58 206 psErrorClear(); 59 207 psFree(stats); 60 208 psFree(rng); 61 psImageInit(image, NAN);62 if (mask) {63 psBinaryOp(mask, mask, "|", psScalarAlloc(maskBad, PS_TYPE_IMAGE_MASK));64 }65 if (ro->variance) {66 psImageInit(image, NAN);67 }68 209 return true; 69 210 } 211 212 # if (USE_BACKGROUND_STDEV) 70 213 float lower = stats->robustMedian - thresh * stats->robustStdev; // Lower bound for data 71 214 float upper = stats->robustMedian + thresh * stats->robustStdev; // Upper bound for data 72 215 float background = stats->robustMedian; 216 # else 217 // the signal we are looking for is a small variation on top of the background. if 218 // the background is uniform with only read noise + sky noise, then the pixel-to-pixel 219 // stdev should only be due to known noise sources and predictable. If the 220 // pixel-to-pixel variations are from other features, then those variations will 221 // probably dominate the row-by-row bias variations. 222 223 // instead of using the image pixel statistics to measure the stdev, lets assume only 224 // dark noise plus poisson sky noise. we are not carrying in the read noise, but it is 225 // fairly modest for GPC1 (~10 DN) 226 227 // if we assume a gain of 1 and the read noise of 10 DN, then a sky of 200 would have 228 // a noise of N = sqrt (1 * 200 + 10^2) = sqrt (300) ~ 17 229 230 // if the gain were as much as 2, then the noise in DN would be N = sqrt(2 * (200 + 100)) / 2 = sqrt(300) / sqrt(2) 231 // so smaller by a factor of 1.4 than what we predict, which is not very large 232 233 // find the nominal signal amplitude (check the ghost and/or crosstalk recipe file) 234 float nominalAmplitude = psMetadataLookupF32 (&mdok, cell->analysis, "PTN.ROW.AMP"); 235 if (!mdok) nominalAmplitude = 20; // XXX EAM : somewhat arbitrary number 236 // If we cannot determine the nominal amplitude, we fall-back on the worst case 237 238 // XXX retrieve noise and gain from 'concepts' if possible 239 # define READNOISE 10 /* arbitrary number */ 240 float sigma = sqrt(stats->robustMedian + PS_SQR(READNOISE)); 241 float delta = PS_MIN (thresh * sigma, 2*nominalAmplitude); 242 float lower = stats->robustMedian - delta; // Lower bound for data 243 float upper = stats->robustMedian + delta; // Upper bound for data 244 float background = stats->robustMedian; 245 246 // if the noise from the background is too large 247 float significance = nominalAmplitude / sigma; 248 249 // XXX EAM : arbitrary number 250 if (isfinite(nominalAmplitude) && (significance < 1.0/6.0)) { 251 psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 252 return true; 253 } 254 fprintf (stderr, "correcting pattern row background %s: %f - %f - %f : %f %f %f\n", cellName, lower, background, upper, sigma, nominalAmplitude, significance); 255 psLogMsg("ppImage", PS_LOG_INFO, "Performing row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 256 # endif 257 73 258 psFree(stats); 74 259 psFree(rng); … … 109 294 psVectorInit(yaxisMask, 0); 110 295 #endif 296 297 // we really need more than order + 1 points (= 4). 298 // this should be tunable, but let's try 5 - 10% 299 int validNmin = numCols * 0.1; 300 111 301 for (int y = 0; y < numRows; y++) { 112 302 psVectorInit(clipMask, 0); 113 303 data = psImageRow(data, image, y); 114 304 int num = 0; // Number of good pixels 305 306 // if the unmasked pixels only span a small range in x then we cannot fit the 307 // 2nd order polynomial variations very well. Require a minimum fractional range 308 float validXmin = +1; 309 float validXmax = -1; 310 311 // XXX can we do just as well fitting 1/3 of the pixels? (NOT REALLY) 312 // (x % 3) || 115 313 for (int x = 0; x < numCols; x++) { 116 if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) ||117 data->data.F32[x] < lower || data->data.F32[x] > upper) {118 clipMask->data.PS_TYPE_VECTOR_MASK_DATA[x] = 0xFF;314 if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) || 315 data->data.F32[x] < lower || data->data.F32[x] > upper) { 316 clipMask->data.PS_TYPE_VECTOR_MASK_DATA[x] = 0xFF; 119 317 } else { 120 318 clipMask->data.PS_TYPE_VECTOR_MASK_DATA[x] = 0; 121 319 num++; 320 validXmin = PS_MIN(indices->data.F32[x], validXmin); 321 validXmax = PS_MAX(indices->data.F32[x], validXmax); 122 322 } 123 323 } 124 if (num < order + 1) { 324 325 // XXX how much time is spent in the fitting 326 if (num < validNmin) { 125 327 // Not enough points to fit 126 328 patternMaskRow(ro, y, maskBad); … … 131 333 continue; 132 334 } 335 // XXX does this need to be a clipped fit if we are clipping based on the median poisson noise? 133 336 if (!psVectorClipFitPolynomial1D(poly, clip, clipMask, 0xFF, data, NULL, indices)) { 134 337 psWarning("Unable to fit polynomial to row %d", y); … … 215 418 for (int x = 0; x < numCols; x++) { 216 419 image->data.F32[y][x] += solution->data.F32[y]; 217 psTrace("pattern",5,"B: %d %d %g\n",x,y,solution->data.F32[ x]);420 psTrace("pattern",5,"B: %d %d %g\n",x,y,solution->data.F32[y]); 218 421 } 219 422 corr->data.F64[y][0] -= solution->data.F32[y]; … … 241 444 for (int x = 0; x < numCols; x++) { 242 445 image->data.F32[y][x] += solution->data.F32[y] * indices->data.F32[x]; 243 psTrace("pattern",5,"C: %d %d %g %g\n",x,y,solution->data.F32[x],indices->data.F32[x]); 446 // XXX EAM : this was [x] which is wrong 447 psTrace("pattern",5,"C: %d %d %g %g\n",x,y,solution->data.F32[y],indices->data.F32[x]); 244 448 } 245 449 corr->data.F64[y][1] -= solution->data.F32[y] ; … … 262 466 263 467 psFree(indices); 468 psFree(clip); 469 psFree(clipMask); 470 psFree(poly); 471 psFree(data); 472 473 return true; 474 } 475 476 # define NPIX 15 477 478 // bin by NPIX in the x-direction to reduce the number of calculations needed to measure 479 // the pattern 480 bool pmPatternRowBinned(pmReadout *ro, int order, int iter, float rej, float thresh, 481 psStatsOptions clipMean, psStatsOptions clipStdev, 482 psImageMaskType maskVal, psImageMaskType maskBad) 483 { 484 PM_ASSERT_READOUT_NON_NULL(ro, false); 485 PM_ASSERT_READOUT_IMAGE(ro, false); 486 PS_ASSERT_INT_NONNEGATIVE(order, false); 487 PS_ASSERT_INT_NONNEGATIVE(iter, false); 488 PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false); 489 PS_ASSERT_FLOAT_LARGER_THAN(thresh, 0.0, false); 490 491 bool mdok; // Status of MD lookup 492 493 pmCell *cell = ro->parent; 494 pmChip *chip = cell->parent; 495 const char *chipName = psMetadataLookupStr(&mdok, chip->concepts, "CHIP.NAME"); // Name of chip 496 const char *cellName = psMetadataLookupStr(&mdok, cell->concepts, "CELL.NAME"); // Name of cell 497 498 psImage *image = ro->image; // Image to correct 499 psImage *mask = ro->mask; // Mask for image 500 int numCols = image->numCols, numRows = image->numRows; // Size of image 501 502 psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); 503 psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator 504 if (!psImageBackground(stats, NULL, ro->image, ro->mask, maskVal, rng)) { 505 psWarning("Unable to calculate statistics on readout; skipping pattern correction for %s, %s.", chipName, cellName); 506 psErrorClear(); 507 psFree(stats); 508 psFree(rng); 509 510 // EAM 20211011 : we used to mask cells which fail the above, but this seems excessive 511 // psImageInit(image, NAN); 512 // if (mask) { 513 // psBinaryOp(mask, mask, "|", psScalarAlloc(maskBad, PS_TYPE_IMAGE_MASK)); 514 // } 515 // if (ro->variance) { 516 // psImageInit(image, NAN); 517 // } 518 return true; 519 } 520 521 // if USE_BACKGROUND_STDEV is TRUE, the observed standard deviation is used to set the 522 // thresholds. this is going to be an overestimate if there is any structure in the 523 // image. If FALSE, the thresholds are set based on poisson stats for the background 524 // level. We assume the gain is 1, so this is an overestimate if the gain is > 1 525 526 # if (USE_BACKGROUND_STDEV) 527 float lower = stats->robustMedian - thresh * stats->robustStdev; // Lower bound for data 528 float upper = stats->robustMedian + thresh * stats->robustStdev; // Upper bound for data 529 float background = stats->robustMedian; 530 # else 531 // the signal we are looking for is a small variation on top of the background. if 532 // the background is uniform with only read noise + sky noise, then the pixel-to-pixel 533 // stdev should only be due to known noise sources and predictable. If the 534 // pixel-to-pixel variations are from other features, then those variations will 535 // probably dominate the row-by-row bias variations. 536 537 // instead of using the image pixel statistics to measure the stdev, lets assume only 538 // dark noise plus poisson sky noise. we are not carrying in the read noise, but it is 539 // fairly modest for GPC1 (~10 DN) 540 541 // if we assume a gain of 1 and the read noise of 10 DN, then a sky of 200 would have 542 // a noise of N = sqrt (1 * 200 + 10^2) = sqrt (300) ~ 17 543 544 // if the gain were as much as 2, then the noise in DN would be N = sqrt(2 * (200 + 100)) / 2 = sqrt(300) / sqrt(2) 545 // so smaller by a factor of 1.4 than what we predict, which is not very large 546 547 // find the nominal signal amplitude (check the ghost and/or crosstalk recipe file) 548 float nominalAmplitude = psMetadataLookupF32 (&mdok, cell->analysis, "PTN.ROW.AMP"); 549 if (!mdok) nominalAmplitude = 20; // XXX EAM : somewhat arbitrary number 550 if (!isfinite(nominalAmplitude)) nominalAmplitude = 20; // XXX EAM : somewhat arbitrary number 551 // If we cannot determine the nominal amplitude, we fall-back on the worst case 552 553 // retrieve noise and gain from 'concepts' if possible 554 # define READNOISE 10 /* arbitrary number */ 555 float sigma = sqrt(stats->robustMedian + PS_SQR(READNOISE)); 556 float delta = PS_MIN (thresh * sigma, 5*nominalAmplitude); 557 float lower = stats->robustMedian - delta; // Lower bound for data 558 float upper = stats->robustMedian + delta; // Upper bound for data 559 float background = stats->robustMedian; 560 561 // if the noise from the background is too large 562 float significance = nominalAmplitude / sigma; 563 564 // XXX EAM : arbitrary number 565 if (isfinite(nominalAmplitude) && (significance < 1.0/6.0)) { 566 psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 567 psFree(stats); 568 psFree(rng); 569 return true; 570 } 571 psLogMsg("ppImage", PS_LOG_INFO, "Performing row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 572 # endif 573 574 psFree(stats); 575 psFree(rng); 576 577 // the vector 'indices' maps the x-coordinate to a range [-1:1]. the element number (i) of indices 578 // related to the x-coordinate (column number) by x = (i + 0.5) * NPIX 579 580 int nSamples = numCols / NPIX; 581 582 psVector *indices = psVectorAlloc(numCols, PS_TYPE_F32); // Indices for fit solutions 583 psVector *xFit = psVectorAlloc(nSamples, PS_TYPE_F32); // x-coordinate for fitting 584 psVector *yFit = psVectorAlloc(nSamples, PS_TYPE_F32); // flux values for fitting 585 586 // xFit elements run from 0 - nSamples, element 'sample' corresponds to the middle of the bin sample*NPIX + 0.5*NPIX 587 588 float norm = 2.0 / (float)numCols; // Normalisation for indices 589 for (int sample = 0; sample < nSamples; sample ++) { 590 int x = (sample + 0.5)*NPIX; 591 xFit->data.F32[sample] = x * norm - 1.0; 592 } 593 for (int x = 0; x < numCols; x ++) { 594 indices->data.F32[x] = x * norm - 1.0; 595 } 596 597 psStats *clip = psStatsAlloc(clipMean | clipStdev); // Clipping statistics 598 clip->clipIter = iter; 599 clip->clipSigma = rej; 600 psVector *clipMask = psVectorAlloc(nSamples, PS_TYPE_VECTOR_MASK); // Mask for clipping 601 psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, order); // Polynomial to fit 602 psVector *data = psVectorAlloc(numCols, PS_TYPE_F32); // Data to fit 603 604 psImage *corr = psImageAlloc(order + 1, numRows, PS_TYPE_F64); // Corrections applied 605 psImageInit(corr, NAN); 606 607 // CZW: 2011-11-30 608 // Define the vectors to hold the "x" and "y" slope trends. 609 // Briefly, the slope trend in the y-axis is a due to variations in the 0-th order term 610 // of the PATTERN.ROW fit between individual rows across the cell. Similarly, the 1-st 611 // order term of the PATTERN.ROW fit defines the trend in the x-axis (as that's what we 612 // are fitting with PATTERN.ROW in the first place). However, the thing we're trying to 613 // fix with PATTERN.ROW is the detector level bias wiggles. These should be overlaid on 614 // the true sky level. Therefore, simply applying the PATTERN.ROW correction will 615 // introduce cell-to-cell sky variations as these two trends are removed. To avoid this, 616 // We store the 0th and 1st order values used for each row, and then fit a polynomial to 617 // these results. By re-adding these systematic trends back, we can remove the row-to-row 618 // variations without improperly removing the real sky trend. 619 psVector *yaxisData = psVectorAlloc(numRows, PS_TYPE_F32); // Data to fit to the constant term 620 psVector *yaxisMask = psVectorAlloc(numRows, PS_TYPE_VECTOR_MASK); // Mask for rows with no fit 621 psVector *xaxisData = psVectorAlloc(numRows, PS_TYPE_F32); // Data to fit to the linear term 622 psVectorInit(yaxisMask, 0); 623 624 // validNmin is the minimum number of samples needed to measure the trend. 625 // this should be tunable, but let's try 5 - 10% 626 int validNmin = PS_MAX (nSamples * 0.1, order + 2); 627 628 for (int y = 0; y < numRows; y++) { 629 psVectorInit(clipMask, 0); 630 data = psImageRow(data, image, y); 631 int num = 0; // Number of good pixels 632 633 // if the unmasked pixels only span a small range in x then we cannot fit the 634 // 2nd order polynomial variations very well. Require a minimum fractional range 635 float validXmin = +1; 636 float validXmax = -1; 637 638 for (int sample = 0; sample < nSamples; sample ++) { 639 640 // store valid samples in the array to be sorted 641 float sampleArray[NPIX]; 642 int seq = 0; 643 for (int j = 0; j < NPIX; j++) { 644 int pix = sample * NPIX + j; // real pixel elements in x-dir 645 psAssert (pix >= 0, "invalid pix value"); 646 psAssert (pix < numCols, "invalid pix value"); 647 if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][pix] & maskVal)) continue; 648 if (data->data.F32[pix] < lower || data->data.F32[pix] > upper) continue; 649 sampleArray[seq] = data->data.F32[pix]; // store the value to be sorted 650 seq ++; 651 } 652 if (seq < 1) { 653 clipMask->data.PS_TYPE_VECTOR_MASK_DATA[sample] = 0xFF; 654 yFit->data.F32[sample] = NAN; 655 continue; 656 } 657 // note that we are treating the x-coordinate as the center 658 // of the binned pixel group, even if some or most pixels have 659 // been masked. compared to the amplitude of the slope, this 660 // error is small 661 clipMask->data.PS_TYPE_VECTOR_MASK_DATA[sample] = 0; 662 validXmin = PS_MIN(xFit->data.F32[sample], validXmin); 663 validXmax = PS_MAX(xFit->data.F32[sample], validXmax); 664 num++; 665 666 // PSSORT operates on sampleArray (see define of macro SORT_SWAP above) 667 PSSORT (seq, SORT_COMPARE, SORT_SWAP, float); 668 669 int midPt = 0.5 * seq; 670 if (seq % 2 == 1) { psAssert (midPt >= 0, "invalid midPt"); } 671 if (seq % 2 == 0) { psAssert (midPt >= 1, "invalid midPt"); } 672 psAssert (midPt < NPIX, "invalid midPt"); 673 674 float medValue = (seq % 2) ? sampleArray[midPt] : 0.5*(sampleArray[midPt] + sampleArray[midPt-1]); 675 yFit->data.F32[sample] = medValue; 676 } 677 678 // If not enough points are valid, skip 679 if (num < validNmin) { 680 // Ignore this row in our subsequent fits, because the fit failed. 681 yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF; 682 continue; 683 } 684 // XXX does this need to be a clipped fit if we are clipping based on the median poisson noise? 685 if (!psVectorClipFitPolynomial1D(poly, clip, clipMask, 0xFF, yFit, NULL, xFit)) { 686 psWarning("Unable to fit polynomial to row %d", y); 687 psErrorClear(); 688 // Ignore this row in our subsequent fits, because the fit failed. 689 yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF; 690 continue; 691 } 692 // Store the results we found for this row. 693 yaxisData->data.F32[y] = poly->coeff[0]; 694 xaxisData->data.F32[y] = poly->coeff[1]; 695 psTrace("pattern",1,"%d %g %g\n",y,poly->coeff[0],poly->coeff[1]); 696 697 memcpy(corr->data.F64[y], poly->coeff, (order + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F64)); 698 psVector *solution = psPolynomial1DEvalVector(poly, indices); // Solution vector 699 if (!solution) { 700 psWarning("Unable to evaluate polynomial for row %d", y); 701 psErrorClear(); 702 yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF; 703 continue; 704 } 705 706 psAssert (solution->n == numCols, "oops"); 707 for (int x = 0; x < numCols; x++) { 708 image->data.F32[y][x] -= solution->data.F32[x]; 709 psTrace("pattern",5,"A: %d %d %g\n",x,y,solution->data.F32[x]); 710 } 711 psFree(solution); 712 } 713 714 // Put the global trends back that were removed by the PATTERN.ROW correction. 715 // Set up the indices for the polynomial 716 psVector *yaxisIndices = psVectorAlloc(numRows, PS_TYPE_F32); 717 norm = 2.0 / (float)numRows; 718 for (int y = 0; y < numRows; y++) { 719 yaxisIndices->data.F32[y] = y * norm - 1.0; 720 psTrace("psModules.detrend.pattern",10,"%d %f %f\n",y,yaxisIndices->data.F32[y],yaxisData->data.F32[y]); 721 } 722 723 // Fit the trend of the constant term, producing the y-axis global trend 724 psStatsInit(clip); 725 psPolynomial1D *yaxisPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Polynomial to fit. 726 if (!psVectorClipFitPolynomial1D(yaxisPoly,clip,yaxisMask,0xFF,yaxisData, NULL, yaxisIndices)) { 727 psWarning("Unable to fit polynomial to y-axis trend"); 728 psErrorClear(); 729 // If we've failed, we need to do something, so add back in the background level, and 730 // expect that the final image will have background mismatches. 731 for (int y = 0; y < numRows; y++) { 732 for (int x = 0; x < numCols; x++) { 733 image->data.F32[y][x] += background; 734 } 735 corr->data.F64[y][0] -= background; 736 } 737 } else { 738 psVector *solution = psPolynomial1DEvalVector(yaxisPoly,yaxisIndices); 739 if (!solution) { 740 psWarning("Unable to evaluate polynomial"); 741 psErrorClear(); 742 // If we've failed, we need to do something, so add back in the background level, and 743 // expect that the final image will have background mismatches. 744 for (int y = 0; y < numRows; y++) { 745 for (int x = 0; x < numCols; x++) { 746 image->data.F32[y][x] += background; 747 } 748 corr->data.F64[y][0] -= background; 749 } 750 } else { 751 psAssert (solution->n == numRows, "oops"); 752 for (int y = 0; y < numRows; y++) { 753 for (int x = 0; x < numCols; x++) { 754 image->data.F32[y][x] += solution->data.F32[y]; 755 // XXX EAM : this was [x], which is wrong 756 psTrace("pattern",5,"B: %d %d %g\n",x,y,solution->data.F32[y]); 757 } 758 corr->data.F64[y][0] -= solution->data.F32[y]; 759 } 760 } 761 psFree(solution); 762 } 763 764 // Fit the trend of the linear term, producing the x-axis global trend 765 // We can use the same mask vector, as the same rows failed the row-fit earlier. 766 psStatsInit(clip); 767 psPolynomial1D *xaxisPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Polynomial to fit. 768 if (!psVectorClipFitPolynomial1D(xaxisPoly,clip,yaxisMask,0xFF,xaxisData, NULL, yaxisIndices)) { 769 psWarning("Unable to fit polynomial to x-axis trend"); 770 psErrorClear(); 771 } else { 772 psVector *solution = psPolynomial1DEvalVector(xaxisPoly,yaxisIndices); 773 if (!solution) { 774 psWarning("Unable to evaluate polynomial"); 775 psErrorClear(); 776 } else { 777 psAssert (solution->n == numRows, "oops"); 778 for (int y = 0; y < numRows; y++) { 779 for (int x = 0; x < numCols; x++) { 780 image->data.F32[y][x] += solution->data.F32[y] * indices->data.F32[x]; 781 // XXX EAM : this was set to [x] which is wrong (numCols > numRows) 782 psTrace("pattern",5,"C: %d %d %g %g\n",x,y,solution->data.F32[y],indices->data.F32[x]); 783 } 784 corr->data.F64[y][1] -= solution->data.F32[y] ; 785 } 786 } 787 psFree(solution); 788 } 789 psFree(yaxisPoly); 790 psFree(xaxisPoly); 791 psFree(yaxisIndices); 792 psFree(yaxisMask); 793 psFree(yaxisData); 794 psFree(xaxisData); 795 796 psMetadataAddImage(ro->analysis, PS_LIST_TAIL, PM_PATTERN_ROW_CORRECTION, PS_META_REPLACE, 797 "Pattern row correction", corr); 798 psFree(corr); 799 800 psFree(indices); 801 psFree(xFit); 802 psFree(yFit); 264 803 psFree(clip); 265 804 psFree(clipMask); … … 1316 1855 return true; 1317 1856 } 1857
Note:
See TracChangeset
for help on using the changeset viewer.
