IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Nov 4, 2021, 6:05:18 PM (5 years ago)
Author:
eugene
Message:

merge changes from eam_branches/ipp-dev-20210817 (pmPattern updates, new inverse transform extra orders api, forward transform uses ORD, pmSourceIO_CMF.c.in conversion to pmFitsTableNew)

Location:
trunk/psModules
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules

  • trunk/psModules/src/detrend/pmPattern.c

    r41743 r41892  
    99#define PATTERN_ROW_BKG_FIX 1
    1010
     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 */
    11114
    12115// Mask a row as bad
     
    33136}
    34137
     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
    35148//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    36149// Measurement and application
    37150//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    38151
     152bool pmPatternRowUnbinned(pmReadout *ro, int order, int iter, float rej, float thresh,
     153                          psStatsOptions clipMean, psStatsOptions clipStdev,
     154                          psImageMaskType maskVal, psImageMaskType maskBad);
     155
     156
     157bool 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?
    39163bool 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
     180bool pmPatternRowUnbinned(pmReadout *ro, int order, int iter, float rej, float thresh,
    40181                  psStatsOptions clipMean, psStatsOptions clipStdev,
    41182                  psImageMaskType maskVal, psImageMaskType maskBad)
     
    48189    PS_ASSERT_FLOAT_LARGER_THAN(thresh, 0.0, false);
    49190
     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
    50198    psImage *image = ro->image;         // Image to correct
    51199    psImage *mask = ro->mask;           // Mask for image
     
    55203    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    56204    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);
    58206        psErrorClear();
    59207        psFree(stats);
    60208        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         }
    68209        return true;
    69210    }
     211
     212# if (USE_BACKGROUND_STDEV)
    70213    float lower = stats->robustMedian - thresh * stats->robustStdev; // Lower bound for data
    71214    float upper = stats->robustMedian + thresh * stats->robustStdev; // Upper bound for data
    72215    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   
    73258    psFree(stats);
    74259    psFree(rng);
     
    109294    psVectorInit(yaxisMask, 0);
    110295#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
    111301    for (int y = 0; y < numRows; y++) {
    112302        psVectorInit(clipMask, 0);
    113303        data = psImageRow(data, image, y);
    114304        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) ||
    115313        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;
    119317            } else {
    120318                clipMask->data.PS_TYPE_VECTOR_MASK_DATA[x] = 0;
    121319                num++;
     320                validXmin = PS_MIN(indices->data.F32[x], validXmin);
     321                validXmax = PS_MAX(indices->data.F32[x], validXmax);
    122322            }
    123323        }
    124         if (num < order + 1) {
     324
     325        // XXX how much time is spent in the fitting
     326        if (num < validNmin) {
    125327            // Not enough points to fit
    126328            patternMaskRow(ro, y, maskBad);
     
    131333            continue;
    132334        }
     335        // XXX does this need to be a clipped fit if we are clipping based on the median poisson noise?
    133336        if (!psVectorClipFitPolynomial1D(poly, clip, clipMask, 0xFF, data, NULL, indices)) {
    134337            psWarning("Unable to fit polynomial to row %d", y);
     
    215418          for (int x = 0; x < numCols; x++) {
    216419            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]);
    218421          }
    219422          corr->data.F64[y][0]  -= solution->data.F32[y];
     
    241444          for (int x = 0; x < numCols; x++) {
    242445            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]);
    244448          }
    245449          corr->data.F64[y][1]  -= solution->data.F32[y] ;
     
    262466
    263467    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
     480bool 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);
    264803    psFree(clip);
    265804    psFree(clipMask);
     
    13161855    return true;
    13171856}
     1857
Note: See TracChangeset for help on using the changeset viewer.