IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Oct 2, 2009, 3:12:47 PM (17 years ago)
Author:
eugene
Message:

check in changes from gene@development branch : extensive changes to moments calculation, psf model generation, aperture residuals; improvements to extended source analysis

Location:
trunk/psphot/src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/psphot/src

    • Property svn:ignore
      •  

        old new  
        1818psphotVersionDefinitions.h
        1919psphotMomentsStudy
         20psphotPetrosianStudy
  • trunk/psphot/src/psphotSourceSize.c

    r21519 r25755  
    22# include <gsl/gsl_sf_gamma.h>
    33
    4 static float psphotModelContour(const psImage *image, const psImage *variance, const psImage *mask,
    5                                 psImageMaskType maskVal, const pmModel *model, float Ro);
    6 
    7 bool psphotMaskCosmicRay_Old (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
    8 bool psphotMaskCosmicRay_New (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
     4typedef struct {
     5    psImageMaskType maskVal;
     6    psImageMaskType markVal;
     7    psImageMaskType crMask;
     8    float ApResid;
     9    float ApSysErr;
     10    float nSigmaApResid;
     11    float nSigmaMoments;
     12    float nSigmaCR;
     13    float soft;
     14    int grow;
     15} psphotSourceSizeOptions;
     16
     17bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf);
     18bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options);
     19bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options);
     20bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options);
     21bool psphotMaskCosmicRay (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
     22bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
    923
    1024// we need to call this function after sources have been fitted to the PSF model and
     
    1428// deviation from the psf model at the r = FWHM/2 position
    1529
    16 bool psphotSourceSize(pmConfig *config, pmReadout *readout, psArray *sources, psMetadata *recipe, long first)
     30// XXX use an internal flag to mark sources which have already been measured
     31bool psphotSourceSize(pmConfig *config, pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, long first)
    1732{
    1833    bool status;
     34    psphotSourceSizeOptions options;
    1935
    2036    psTimerStart ("psphot.size");
    2137
    2238    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
    23     psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
    24     assert (maskVal);
     39    options.maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
     40    assert (options.maskVal);
     41
     42    options.markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT"); // Mask value for bad pixels
     43    assert (options.markVal);
    2544
    2645    // bit to mask the cosmic-ray pixels
    27     psImageMaskType crMask  = pmConfigMaskGet("CR", config); // Mask value for cosmic rays
    28 
    29     float CR_NSIGMA_LIMIT = psMetadataLookupF32 (&status, recipe, "PSPHOT.CR.NSIGMA.LIMIT");
     46    options.crMask  = pmConfigMaskGet("CR", config); // Mask value for cosmic rays
     47
     48    options.nSigmaCR = psMetadataLookupF32 (&status, recipe, "PSPHOT.CR.NSIGMA.LIMIT");
    3049    assert (status);
    3150
    32     float EXT_NSIGMA_LIMIT = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.LIMIT");
     51    // XXX recipe name is not great
     52    options.nSigmaApResid = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.LIMIT");
    3353    assert (status);
    3454
    35     int grow = psMetadataLookupS32(&status, recipe, "PSPHOT.CR.GROW"); // Growth size for CRs
    36     if (!status || grow < 0) {
     55    // XXX recipe name is not great
     56    options.nSigmaMoments = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.MOMENTS");
     57    assert (status);
     58
     59    options.grow = psMetadataLookupS32(&status, recipe, "PSPHOT.CR.GROW"); // Growth size for CRs
     60    if (!status || options.grow < 0) {
    3761        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "PSPHOT.CR.GROW is not positive.");
    3862        return false;
    3963    }
    4064
    41     float soft = psMetadataLookupF32(&status, recipe, "PSPHOT.CR.NSIGMA.SOFTEN"); // Softening parameter
    42     if (!status || !isfinite(soft) || soft < 0.0) {
     65    options.soft = psMetadataLookupF32(&status, recipe, "PSPHOT.CR.NSIGMA.SOFTEN"); // Softening parameter
     66    if (!status || !isfinite(options.soft) || options.soft < 0.0) {
    4367        psWarning("PSPHOT.CR.NSIGMA.SOFTEN not set; defaulting to zero.");
    44         soft = 0.0;
    45     }
    46 
    47     // loop over all source
    48     for (int i = first; i < sources->n; i++) {
    49         pmSource *source = sources->data[i];
    50 
    51         // skip source if it was already measured
    52         if (isfinite(source->crNsigma)) {
    53             psTrace("psphot", 7, "Not calculating extNsigma,crNsigma since already measured\n");
    54             continue;
     68        options.soft = 0.0;
     69    }
     70
     71    // We are using the value psfMag - 2.5*log10(moment.Sum) as a measure of the extendedness
     72    // of and object.  We need to model this distribution for the PSF stars before we can test
     73    // the significance for a specific object
     74    // XXX move this to the code that generates the PSF?
     75    // XXX store the results on pmPSF?
     76    psphotSourceSizePSF (&options, sources, psf);
     77
     78    // classify the sources based on ApResid and Moments (extended sources)
     79    psphotSourceClass(readout, sources, recipe, psf, &options);
     80
     81    psphotSourceSizeCR (readout, sources, &options);
     82
     83    psLogMsg ("psphot.size", PS_LOG_INFO, "measure source sizes for %ld sources: %f sec\n", sources->n - first, psTimerMark ("psphot.size"));
     84
     85    psphotVisualPlotSourceSize (recipe, sources);
     86    psphotVisualShowSourceSize (readout, sources);
     87    psphotVisualPlotApResid (sources, options.ApResid, options.ApSysErr);
     88
     89    return true;
     90}
     91
     92bool psphotMaskCosmicRay (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
     93
     94    // replace the source flux
     95    pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
     96
     97    // flag this as a CR
     98    source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     99    pmPeak *peak = source->peak;
     100    psAssert (peak, "NULL peak");
     101
     102    // grab the matching footprint
     103    pmFootprint *footprint = peak->footprint;
     104    if (!footprint) {
     105        // if we have not footprint, use the old code to mask by isophot
     106        psphotMaskCosmicRayIsophot (source, maskVal, crMask);
     107        return true;
     108    }
     109
     110    if (!footprint->spans) {
     111        // if we have no footprint, use the old code to mask by isophot
     112        psphotMaskCosmicRayIsophot (source, maskVal, crMask);
     113        return true;
     114    }
     115
     116    // mask all of the pixels covered by the spans of the footprint
     117    for (int j = 1; j < footprint->spans->n; j++) {
     118        pmSpan *span1 = footprint->spans->data[j];
     119
     120        int iy = span1->y;
     121        int xs = span1->x0;
     122        int xe = span1->x1;
     123
     124        for (int ix = xs; ix < xe; ix++) {
     125            mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
     126        }
     127    }
     128    return true;
     129}
     130
     131bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
     132
     133    source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     134    pmPeak *peak = source->peak;
     135    psAssert (peak, "NULL peak");
     136
     137    psImage *mask   = source->maskView;
     138    psImage *pixels = source->pixels;
     139    psImage *variance = source->variance;
     140
     141    // XXX This should be a recipe variable
     142# define SN_LIMIT 5.0
     143
     144    int xo = peak->x - pixels->col0;
     145    int yo = peak->y - pixels->row0;
     146
     147    // mark the pixels in this row to the left, then the right
     148    for (int ix = xo; ix >= 0; ix--) {
     149        float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
     150        if (SN > SN_LIMIT) {
     151            mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
     152        }
     153    }
     154    for (int ix = xo + 1; ix < pixels->numCols; ix++) {
     155        float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
     156        if (SN > SN_LIMIT) {
     157            mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
     158        }
     159    }
     160
     161    // for each of the neighboring rows, mark the high pixels if they have a marked neighbor
     162    // first go up:
     163    for (int iy = PS_MIN(yo, mask->numRows-2); iy >= 0; iy--) {
     164        // mark the pixels in this row to the left, then the right
     165        for (int ix = 0; ix < pixels->numCols; ix++) {
     166            float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
     167            if (SN < SN_LIMIT) continue;
     168
     169            bool valid = false;
     170            valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix] & crMask);
     171            valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix-1] & crMask) : 0;
     172            valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix+1] & crMask) : 0;
     173
     174            if (!valid) continue;
     175            mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
     176        }
     177    }
     178    // next go down:
     179    for (int iy = PS_MIN(yo+1, mask->numRows-1); iy < pixels->numRows; iy++) {
     180        // mark the pixels in this row to the left, then the right
     181        for (int ix = 0; ix < pixels->numCols; ix++) {
     182            float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
     183            if (SN < SN_LIMIT) continue;
     184
     185            bool valid = false;
     186            valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix] & crMask);
     187            valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix-1] & crMask) : 0;
     188            valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix+1] & crMask) : 0;
     189
     190            if (!valid) continue;
     191            mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
     192        }
     193    }
     194    return true;
     195}
     196
     197// model the apmifit distribution for the psf stars:
     198bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf) {
     199
     200    // select stats from the psf stars
     201    psVector *Ap = psVectorAllocEmpty (100, PS_TYPE_F32);
     202    psVector *ApErr = psVectorAllocEmpty (100, PS_TYPE_F32);
     203   
     204    psImageMaskType maskVal = options->maskVal | options->markVal;
     205
     206    // XXX  why PHOT_WEIGHT??
     207    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
     208
     209    for (int i = 0; i < sources->n; i++) {
     210        pmSource *source = sources->data[i];
     211        if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;
     212
     213        // replace object in image
     214        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
     215            pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
    55216        }
    56217
    57         // source must have been subtracted
    58         if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
     218        // clear the mask bit and set the circular mask pixels
     219        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     220        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
     221
     222        // XXX can we test if psfMag is set and calculate only if needed?
     223        pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
     224       
     225        // clear the mask bit
     226        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     227
     228        // re-subtract the object, leave local sky
     229        pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
     230
     231        float apMag = -2.5*log10(source->moments->Sum);
     232        float dMag = source->psfMag - apMag;
     233       
     234        psVectorAppend (Ap, 100, dMag);
     235        psVectorAppend (ApErr, 100, source->errMag);
     236    }
     237
     238    // model the distribution as a mean or median value and a systematic error from that value:
     239    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
     240    psVectorStats (stats, Ap, NULL, NULL, 0);
     241
     242    psVector *dAp = psVectorAlloc (Ap->n, PS_TYPE_F32);
     243    for (int i = 0; i < Ap->n; i++) {
     244        dAp->data.F32[i] = Ap->data.F32[i] - stats->robustMedian;
     245    }
     246
     247    options->ApResid = stats->robustMedian;
     248    options->ApSysErr = psVectorSystematicError(dAp, ApErr, 0.05);
     249    psLogMsg ("psphot", PS_LOG_DETAIL, "psf - Sum: %f +/- %f\n", options->ApResid, options->ApSysErr);
     250
     251    psFree (Ap);
     252    psFree (ApErr);
     253    psFree (stats);
     254    psFree (dAp);
     255
     256    return true;
     257}
     258
     259// classify sources based on the combination of psf-mag, Mxx, Myy
     260bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
     261
     262    bool status;
     263    pmPSFClump psfClump;
     264    char regionName[64];
     265
     266    psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4s %4s %4s %4s %4s %4s", "Npsf", "Next", "Nsat", "Ncr", "Nmiss", "Nskip");
     267
     268    int nRegions = psMetadataLookupS32 (&status, recipe, "PSF.CLUMP.NREGIONS");
     269    for (int i = 0; i < nRegions; i ++) {
     270        snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
     271        psMetadata *regionMD = psMetadataLookupPtr (&status, recipe, regionName);
     272        psAssert (regionMD, "regions must be defined by earlier call to psphotRoughClassRegion");
     273
     274        psRegion *region = psMetadataLookupPtr (&status, regionMD, "REGION");
     275        psAssert (region, "regions must be defined by earlier call to psphotRoughClassRegion");
     276
     277        // pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
     278        psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   psAssert (status, "missing PSF.CLUMP.X");
     279        psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   psAssert (status, "missing PSF.CLUMP.Y");
     280        psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  psAssert (status, "missing PSF.CLUMP.DX");
     281        psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  psAssert (status, "missing PSF.CLUMP.DY");
     282
     283        if ((psfClump.X < 0) || (psfClump.Y < 0) || !psfClump.X || !psfClump.Y || isnan(psfClump.X) || isnan(psfClump.Y)) {
     284            psLogMsg ("psphot", 4, "Failed to find a valid PSF clump for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);
     285            continue;
     286        }
     287       
     288        if (!psphotSourceClassRegion (region, &psfClump, sources, recipe, psf, options)) {
     289            psLogMsg ("psphot", 4, "Failed to determine source classification for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);
     290            continue;
     291        }
     292    }   
     293
     294    return true;
     295}
     296
     297bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
     298
     299    PS_ASSERT_PTR_NON_NULL(sources, false);
     300    PS_ASSERT_PTR_NON_NULL(recipe, false);
     301
     302    int Nsat  = 0;
     303    int Next  = 0;
     304    int Npsf  = 0;
     305    int Ncr   = 0;
     306    int Nmiss = 0;
     307    int Nskip = 0;
     308
     309    pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;
     310    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
     311
     312    psImageMaskType maskVal = options->maskVal | options->markVal;
     313
     314    for (psS32 i = 0 ; i < sources->n ; i++) {
     315
     316        pmSource *source = (pmSource *) sources->data[i];
     317
     318        // psfClumps are found for image subregions:
     319        // skip sources not in this region
     320        if (source->peak->x <  region->x0) continue;
     321        if (source->peak->x >= region->x1) continue;
     322        if (source->peak->y <  region->y0) continue;
     323        if (source->peak->y >= region->y1) continue;
     324
     325        // skip source if it was already measured
     326        if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
     327            psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
     328            continue;
     329        }
     330
     331        // source must have been subtracted
     332        if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
    59333            source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
    60             psTrace("psphot", 7, "Not calculating extNsigma,crNsigma since source is not subtracted\n");
    61             continue;
     334            psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");
     335            continue;
     336        }
     337
     338        // we are basically classifying by moments; use the default if not found
     339        psAssert (source->moments, "why is this source missing moments?");
     340        if (source->mode & noMoments) {
     341            Nskip ++;
     342            continue;
     343        }
     344
     345        psF32 Mxx = source->moments->Mxx;
     346        psF32 Myy = source->moments->Myy;
     347
     348        // replace object in image
     349        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
     350            pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
    62351        }
    63352
    64         psF32 **resid  = source->pixels->data.F32;
    65         psF32 **variance = source->variance->data.F32;
    66         psImageMaskType **mask = source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
    67 
    68         // check for extendedness: measure the delta flux significance at the 1 sigma contour
    69         source->extNsigma = psphotModelContour(source->pixels, source->variance, source->maskObj, maskVal,
    70                                                source->modelPSF, 1.0);
    71 
    72         // XXX prevent a source from being both CR and EXT?
    73         if (source->extNsigma > EXT_NSIGMA_LIMIT) {
    74             source->mode |= PM_SOURCE_MODE_EXT_LIMIT;
    75         }
    76 
    77         // Integer position of peak
    78         int xPeak = source->peak->xf - source->pixels->col0 + 0.5;
    79         int yPeak = source->peak->yf - source->pixels->row0 + 0.5;
    80 
    81         // XXX for now, skip sources which are too close to a boundary
    82         // XXX raise a flag?
    83         if (xPeak < 1 || xPeak > source->pixels->numCols - 2 ||
    84             yPeak < 1 || yPeak > source->pixels->numRows - 2) {
    85             source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
    86             psTrace("psphot", 7, "Not calculating crNsigma due to edge\n");
    87             continue;
    88         }
    89 
    90         // XXX for now, just skip any sources with masked pixels
    91         // XXX raise a flag?
    92         bool keep = true;
    93         for (int iy = -1; (iy <= +1) && keep; iy++) {
    94             for (int ix = -1; (ix <= +1) && keep; ix++) {
    95                 if (mask[yPeak+iy][xPeak+ix] & maskVal) {
    96                     keep = false;
    97                 }
    98             }
    99         }
    100         if (!keep) {
    101             psTrace("psphot", 7, "Not calculating crNsigma due to masked pixels\n");
    102             source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
    103             continue;
    104         }
    105 
    106         // Compare the central pixel with those on either side, for the four possible lines through it.
    107 
    108         // Soften variances (add systematic error)
    109         float softening = soft * PS_SQR(source->peak->flux); // Softening for variances
    110 
    111         // Across the middle: y = 0
    112         float cX = 2*resid[yPeak][xPeak]   - resid[yPeak+0][xPeak-1]  - resid[yPeak+0][xPeak+1];
    113         float dcX = 4*variance[yPeak][xPeak] + variance[yPeak+0][xPeak-1] + variance[yPeak+0][xPeak+1];
    114         float nX = cX / sqrtf(dcX + softening);
    115 
    116         // Up the centre: x = 0
    117         float cY = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak+0]  - resid[yPeak+1][xPeak+0];
    118         float dcY = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak+0] + variance[yPeak+1][xPeak+0];
    119         float nY = cY / sqrtf(dcY + softening);
    120 
    121         // Diagonal: x = y
    122         float cL = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak-1]  - resid[yPeak+1][xPeak+1];
    123         float dcL = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak-1] + variance[yPeak+1][xPeak+1];
    124         float nL = cL / sqrtf(dcL + softening);
    125 
    126         // Diagonal: x = - y
    127         float cR = 2*resid[yPeak][xPeak]   - resid[yPeak+1][xPeak-1]  - resid[yPeak-1][xPeak+1];
    128         float dcR = 4*variance[yPeak][xPeak] + variance[yPeak+1][xPeak-1] + variance[yPeak-1][xPeak+1];
    129         float nR = cR / sqrtf(dcR + softening);
    130 
    131         // P(chisq > chisq_obs; Ndof) = gamma_Q (Ndof/2, chisq/2)
    132         // Ndof = 4 ? (four measurements, no free parameters)
    133         // XXX this value is going to be biased low because of systematic errors.
    134         // we need to calibrate it somehow
    135         // source->psfProb = gsl_sf_gamma_inc_Q (2, 0.5*chisq);
    136 
    137         // not strictly accurate: overcounts the chisq contribution from the center pixel (by
    138         // factor of 4); also biases a bit low if any pixels are masked
    139         // XXX I am not sure I want to keep this value...
    140         source->psfChisq = PS_SQR(nX) + PS_SQR(nY) + PS_SQR(nL) + PS_SQR(nR);
    141 
    142         float fCR = 0.0;
    143         int nCR = 0;
    144         if (nX > 0.0) {
    145             fCR += nX;
    146             nCR ++;
    147         }
    148         if (nY > 0.0) {
    149             fCR += nY;
    150             nCR ++;
    151         }
    152         if (nL > 0.0) {
    153             fCR += nL;
    154             nCR ++;
    155         }
    156         if (nR > 0.0) {
    157             fCR += nR;
    158             nCR ++;
    159         }
    160         source->crNsigma  = (nCR > 0)  ? fCR / nCR : 0.0;
    161         if (!isfinite(source->crNsigma)) {
    162             continue;
    163         }
    164 
    165         // this source is thought to be a cosmic ray.  flag the detection and mask the pixels
    166         if (source->crNsigma > CR_NSIGMA_LIMIT) {
    167             // XXX still testing... : psphotMaskCosmicRay_New (readout->mask, source, maskVal, crMask);
    168             psphotMaskCosmicRay_Old (source, maskVal, crMask);
    169         }
    170     }
    171 
    172     // now that we have masked pixels associated with CRs, we can grow the mask
    173     if (grow > 0) {
    174         bool oldThreads = psImageConvolveSetThreads(true); // Old value of threading for psImageConvolveMask
    175         psImage *newMask = psImageConvolveMask(NULL, readout->mask, crMask, crMask, -grow, grow, -grow, grow);
    176         psImageConvolveSetThreads(oldThreads);
    177         if (!newMask) {
    178             psError(PS_ERR_UNKNOWN, false, "Unable to grow CR mask");
    179             return false;
    180         }
    181         psFree(readout->mask);
    182         readout->mask = newMask;
    183     }
    184 
    185     psLogMsg ("psphot.size", PS_LOG_INFO, "measure source sizes for %ld sources: %f sec\n",
    186               sources->n - first, psTimerMark ("psphot.size"));
    187 
    188     psphotVisualPlotSourceSize (sources);
    189     psphotVisualShowSourceSize (readout, sources);
     353        // clear the mask bit and set the circular mask pixels
     354        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     355        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
     356
     357        // XXX can we test if psfMag is set and calculate only if needed?
     358        pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
     359
     360        // clear the mask bit
     361        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     362
     363        // re-subtract the object, leave local sky
     364        pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
     365
     366        float apMag = -2.5*log10(source->moments->Sum);
     367        float dMag = source->psfMag - apMag;
     368        float nSigma = (dMag - options->ApResid) / hypot(source->errMag, options->ApSysErr);
     369
     370        source->extNsigma = nSigma;
     371        source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     372
     373        // Anything within this region is a probably PSF-like object. Saturated stars may land
     374        // in this region, but are detected elsewhere on the basis of their peak value.
     375        bool isPSF = (fabs(nSigma) < options->nSigmaApResid) && (fabs(Mxx - psfClump->X) < options->nSigmaMoments*psfClump->dX) && (fabs(Myy - psfClump->Y) < options->nSigmaMoments*psfClump->dY);
     376        if (isPSF) {
     377            Npsf ++;
     378            continue;
     379        }
     380
     381        // Defects may not always match CRs from peak curvature analysis
     382        // Defects may also be marked as SATSTAR -- XXX deactivate this flag?
     383        // XXX this rule is not great
     384        if ((Mxx < psfClump->X) || (Myy < psfClump->Y)) {
     385            source->mode |= PM_SOURCE_MODE_DEFECT;
     386            Ncr ++;
     387            continue;
     388        }
     389
     390        // saturated star (determined in PSF fit).  These may also be saturated galaxies, or
     391        // just large saturated regions.
     392        if (source->mode & PM_SOURCE_MODE_SATSTAR) {
     393            Nsat ++;
     394            continue;
     395        }
     396
     397        // XXX allow the Mxx, Myy to be less than psfClump->X,Y (by some nSigma)?
     398        bool isEXT = (nSigma > options->nSigmaApResid) || ((Mxx > psfClump->X) && (Myy > psfClump->Y));
     399        if (isEXT) {
     400            source->mode |= PM_SOURCE_MODE_EXT_LIMIT;
     401            Next ++;
     402            continue;
     403        }
     404
     405        psWarning ("sourse size was missed for %f,%f : %f %f -- %f\n", source->peak->xf, source->peak->yf, Mxx, Myy, nSigma);
     406        Nmiss ++;
     407    }
     408
     409    psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4d %4d %4d %4d %4d %4d", Npsf, Next, Nsat, Ncr, Nmiss, Nskip);
    190410
    191411    return true;
     
    194414// given the PSF ellipse parameters, navigate around the 1sigma contour, return the total
    195415// deviation in sigmas.  This is measured on the residual image - should we ignore negative
    196 // deviations?
    197 static float psphotModelContour(const psImage *image, const psImage *variance, const psImage *mask,
    198                                 psImageMaskType maskVal, const pmModel *model, float Ro)
     416// deviations?  NOTE: This function was an early attempt to classify extended objects, and is
     417// no longer used by psphot.
     418float psphotModelContour(const psImage *image, const psImage *variance, const psImage *mask,
     419                         psImageMaskType maskVal, const pmModel *model, float Ro)
    199420{
    200421    psF32 *PAR = model->params->data.F32; // Model parameters
     
    211432    float Q = Ro * PS_SQR(sxx) / (1.0 - PS_SQR(sxx * syy * sxy) / 4.0);
    212433    if (Q < 0.0) {
    213         // ellipse is imaginary
    214         return NAN;
     434        // ellipse is imaginary
     435        return NAN;
    215436    }
    216437
     
    220441
    221442    for (int x = -radius; x <= radius; x++) {
    222         // Polynomial coefficients
    223         // XXX Should we be using the centre of the pixel as x or x+0.5?
    224         float A = PS_SQR (1.0 / syy);
    225         float B = x * sxy;
    226         float C = PS_SQR (x / sxx) - Ro;
    227         float T = PS_SQR(B) - 4*A*C;
    228         if (T < 0.0) {
    229             continue;
    230         }
    231 
    232         // y position in source frame
    233         float yP = (-B + sqrt (T)) / (2.0 * A);
    234         float yM = (-B - sqrt (T)) / (2.0 * A);
    235 
    236         // Get the closest pixel positions (image frame)
    237         int xPix  = x  + PAR[PM_PAR_XPOS] - image->col0 + 0.5;
    238         int yPixM = yM + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
    239         int yPixP = yP + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
    240 
    241         if (xPix < 0 || xPix >= image->numCols) {
    242             continue;
    243         }
    244 
    245         if (yPixM >= 0 && yPixM < image->numRows &&
    246             !(mask && (mask->data.PS_TYPE_IMAGE_MASK_DATA[yPixM][xPix] & maskVal))) {
    247             float dSigma = image->data.F32[yPixM][xPix] / sqrtf(variance->data.F32[yPixM][xPix]);
    248             nSigma += dSigma;
    249             nPts++;
    250         }
    251 
    252         if (yPixM == yPixP) {
    253             continue;
    254         }
    255 
    256         if (yPixP >= 0 && yPixP < image->numRows &&
    257             !(mask && (mask->data.PS_TYPE_IMAGE_MASK_DATA[yPixP][xPix] & maskVal))) {
    258             float dSigma = image->data.F32[yPixP][xPix] / sqrtf(variance->data.F32[yPixP][xPix]);
    259             nSigma += dSigma;
    260             nPts++;
    261         }
     443        // Polynomial coefficients
     444        // XXX Should we be using the centre of the pixel as x or x+0.5?
     445        float A = PS_SQR (1.0 / syy);
     446        float B = x * sxy;
     447        float C = PS_SQR (x / sxx) - Ro;
     448        float T = PS_SQR(B) - 4*A*C;
     449        if (T < 0.0) {
     450            continue;
     451        }
     452
     453        // y position in source frame
     454        float yP = (-B + sqrt (T)) / (2.0 * A);
     455        float yM = (-B - sqrt (T)) / (2.0 * A);
     456
     457        // Get the closest pixel positions (image frame)
     458        int xPix  = x  + PAR[PM_PAR_XPOS] - image->col0 + 0.5;
     459        int yPixM = yM + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
     460        int yPixP = yP + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
     461
     462        if (xPix < 0 || xPix >= image->numCols) {
     463            continue;
     464        }
     465
     466        if (yPixM >= 0 && yPixM < image->numRows &&
     467            !(mask && (mask->data.PS_TYPE_IMAGE_MASK_DATA[yPixM][xPix] & maskVal))) {
     468            float dSigma = image->data.F32[yPixM][xPix] / sqrtf(variance->data.F32[yPixM][xPix]);
     469            nSigma += dSigma;
     470            nPts++;
     471        }
     472
     473        if (yPixM == yPixP) {
     474            continue;
     475        }
     476
     477        if (yPixP >= 0 && yPixP < image->numRows &&
     478            !(mask && (mask->data.PS_TYPE_IMAGE_MASK_DATA[yPixP][xPix] & maskVal))) {
     479            float dSigma = image->data.F32[yPixP][xPix] / sqrtf(variance->data.F32[yPixP][xPix]);
     480            nSigma += dSigma;
     481            nPts++;
     482        }
    262483    }
    263484    nSigma /= nPts;
     
    265486}
    266487
    267 bool psphotMaskCosmicRay_New (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
    268 
    269     // replace the source flux
    270     pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
    271     source->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
    272 
    273     // flag this as a CR
    274     source->mode |= PM_SOURCE_MODE_CR_LIMIT;
    275     pmPeak *peak = source->peak;
    276     psAssert (peak, "NULL peak");
    277 
    278     // grab the matching footprint
    279     pmFootprint *footprint = peak->footprint;
    280     if (!footprint) {
    281         // if we have not footprint, use the old code to mask by isophot
    282         psphotMaskCosmicRay_Old (source, maskVal, crMask);
    283         return true;
    284     }
    285 
    286     if (!footprint->spans) {
    287         // if we have not footprint, use the old code to mask by isophot
    288         psphotMaskCosmicRay_Old (source, maskVal, crMask);
    289         return true;
    290     }
    291 
    292     // mask all of the pixels covered by the spans of the footprint
    293     for (int j = 1; j < footprint->spans->n; j++) {
    294         pmSpan *span1 = footprint->spans->data[j];
    295 
    296         int iy = span1->y;
    297         int xs = span1->x0;
    298         int xe = span1->x1;
    299 
    300         for (int ix = xs; ix < xe; ix++) {
    301             mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
    302         }
     488bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) {
     489
     490    // classify the sources based on the CR test (place this in a function?)
     491    // XXX use an internal flag to mark sources which have already been measured
     492    for (int i = 0; i < sources->n; i++) {
     493        pmSource *source = sources->data[i];
     494
     495        // skip source if it was already measured
     496        if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
     497            psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
     498            continue;
     499        }
     500
     501        // source must have been subtracted
     502        if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
     503            source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
     504            psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");
     505            continue;
     506        }
     507
     508        psF32 **resid  = source->pixels->data.F32;
     509        psF32 **variance = source->variance->data.F32;
     510        psImageMaskType **mask = source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
     511
     512        // Integer position of peak
     513        int xPeak = source->peak->xf - source->pixels->col0 + 0.5;
     514        int yPeak = source->peak->yf - source->pixels->row0 + 0.5;
     515
     516        // Skip sources which are too close to a boundary.  These are mostly caught as DEFECT
     517        if (xPeak < 1 || xPeak > source->pixels->numCols - 2 ||
     518            yPeak < 1 || yPeak > source->pixels->numRows - 2) {
     519            psTrace("psphot", 7, "Not calculating crNsigma due to edge\n");
     520            continue;
     521        }
     522
     523        // Skip sources with masked pixels.  These are mostly caught as DEFECT
     524        bool keep = true;
     525        for (int iy = -1; (iy <= +1) && keep; iy++) {
     526            for (int ix = -1; (ix <= +1) && keep; ix++) {
     527                if (mask[yPeak+iy][xPeak+ix] & options->maskVal) {
     528                    keep = false;
     529                }
     530            }
     531        }
     532        if (!keep) {
     533            psTrace("psphot", 7, "Not calculating crNsigma due to masked pixels\n");
     534            continue;
     535        }
     536
     537        // Compare the central pixel with those on either side, for the four possible lines through it.
     538
     539        // Soften variances (add systematic error)
     540        float softening = options->soft * PS_SQR(source->peak->flux); // Softening for variances
     541
     542        // Across the middle: y = 0
     543        float cX = 2*resid[yPeak][xPeak]   - resid[yPeak+0][xPeak-1]  - resid[yPeak+0][xPeak+1];
     544        float dcX = 4*variance[yPeak][xPeak] + variance[yPeak+0][xPeak-1] + variance[yPeak+0][xPeak+1];
     545        float nX = cX / sqrtf(dcX + softening);
     546
     547        // Up the centre: x = 0
     548        float cY = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak+0]  - resid[yPeak+1][xPeak+0];
     549        float dcY = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak+0] + variance[yPeak+1][xPeak+0];
     550        float nY = cY / sqrtf(dcY + softening);
     551
     552        // Diagonal: x = y
     553        float cL = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak-1]  - resid[yPeak+1][xPeak+1];
     554        float dcL = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak-1] + variance[yPeak+1][xPeak+1];
     555        float nL = cL / sqrtf(dcL + softening);
     556
     557        // Diagonal: x = - y
     558        float cR = 2*resid[yPeak][xPeak]   - resid[yPeak+1][xPeak-1]  - resid[yPeak-1][xPeak+1];
     559        float dcR = 4*variance[yPeak][xPeak] + variance[yPeak+1][xPeak-1] + variance[yPeak-1][xPeak+1];
     560        float nR = cR / sqrtf(dcR + softening);
     561
     562        // P(chisq > chisq_obs; Ndof) = gamma_Q (Ndof/2, chisq/2)
     563        // Ndof = 4 ? (four measurements, no free parameters)
     564        // XXX this value is going to be biased low because of systematic errors.
     565        // we need to calibrate it somehow
     566        // source->psfProb = gsl_sf_gamma_inc_Q (2, 0.5*chisq);
     567
     568        // not strictly accurate: overcounts the chisq contribution from the center pixel (by
     569        // factor of 4); also biases a bit low if any pixels are masked
     570        // XXX I am not sure I want to keep this value...
     571        source->psfChisq = PS_SQR(nX) + PS_SQR(nY) + PS_SQR(nL) + PS_SQR(nR);
     572
     573        float fCR = 0.0;
     574        int nCR = 0;
     575        if (nX > 0.0) {
     576            fCR += nX;
     577            nCR ++;
     578        }
     579        if (nY > 0.0) {
     580            fCR += nY;
     581            nCR ++;
     582        }
     583        if (nL > 0.0) {
     584            fCR += nL;
     585            nCR ++;
     586        }
     587        if (nR > 0.0) {
     588            fCR += nR;
     589            nCR ++;
     590        }
     591        source->crNsigma  = (nCR > 0)  ? fCR / nCR : 0.0;
     592        source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     593
     594        if (!isfinite(source->crNsigma)) {
     595            continue;
     596        }
     597
     598        // this source is thought to be a cosmic ray.  flag the detection and mask the pixels
     599        if (source->crNsigma > options->nSigmaCR) {
     600            source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     601            // XXX still testing... : psphotMaskCosmicRay (readout->mask, source, maskVal, crMask);
     602            // XXX acting strange... psphotMaskCosmicRay_Old (source, maskVal, crMask);
     603        }
     604    }
     605
     606    // now that we have masked pixels associated with CRs, we can grow the mask
     607    if (options->grow > 0) {
     608        bool oldThreads = psImageConvolveSetThreads(true); // Old value of threading for psImageConvolveMask
     609        psImage *newMask = psImageConvolveMask(NULL, readout->mask, options->crMask, options->crMask, -options->grow, options->grow, -options->grow, options->grow);
     610        psImageConvolveSetThreads(oldThreads);
     611        if (!newMask) {
     612            psError(PS_ERR_UNKNOWN, false, "Unable to grow CR mask");
     613            return false;
     614        }
     615        psFree(readout->mask);
     616        readout->mask = newMask;
    303617    }
    304618    return true;
    305619}
    306 
    307 bool psphotMaskCosmicRay_Old (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
    308 
    309     source->mode |= PM_SOURCE_MODE_CR_LIMIT;
    310     pmPeak *peak = source->peak;
    311     psAssert (peak, "NULL peak");
    312 
    313     psImage *mask   = source->maskView;
    314     psImage *pixels = source->pixels;
    315     psImage *variance = source->variance;
    316 
    317     // XXX This should be a recipe variable
    318 # define SN_LIMIT 5.0
    319 
    320     int xo = peak->x - pixels->col0;
    321     int yo = peak->y - pixels->row0;
    322 
    323     // mark the pixels in this row to the left, then the right
    324     for (int ix = xo; ix >= 0; ix--) {
    325         float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
    326         if (SN > SN_LIMIT) {
    327             mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
    328         }
    329     }
    330     for (int ix = xo + 1; ix < pixels->numCols; ix++) {
    331         float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
    332         if (SN > SN_LIMIT) {
    333             mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
    334         }
    335     }
    336 
    337     // for each of the neighboring rows, mark the high pixels if they have a marked neighbor
    338     // first go up:
    339     for (int iy = PS_MIN(yo, mask->numRows-2); iy >= 0; iy--) {
    340         // mark the pixels in this row to the left, then the right
    341         for (int ix = 0; ix < pixels->numCols; ix++) {
    342             float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
    343             if (SN < SN_LIMIT) continue;
    344 
    345             bool valid = false;
    346             valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix] & crMask);
    347             valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix-1] & crMask) : 0;
    348             valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix+1] & crMask) : 0;
    349 
    350             if (!valid) continue;
    351             mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
    352         }
    353     }
    354     // next go down:
    355     for (int iy = PS_MIN(yo+1, mask->numRows-1); iy < pixels->numRows; iy++) {
    356         // mark the pixels in this row to the left, then the right
    357         for (int ix = 0; ix < pixels->numCols; ix++) {
    358             float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
    359             if (SN < SN_LIMIT) continue;
    360 
    361             bool valid = false;
    362             valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix] & crMask);
    363             valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix-1] & crMask) : 0;
    364             valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix+1] & crMask) : 0;
    365 
    366             if (!valid) continue;
    367             mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
    368         }
    369     }
    370     return true;
    371 }
Note: See TracChangeset for help on using the changeset viewer.