IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 3, 2010, 8:50:52 AM (16 years ago)
Author:
eugene
Message:

updates from trunk

Location:
branches/simtest_nebulous_branches
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • branches/simtest_nebulous_branches

  • branches/simtest_nebulous_branches/psphot

  • branches/simtest_nebulous_branches/psphot/src

    • Property svn:ignore
      •  

        old new  
        1818psphotVersionDefinitions.h
        1919psphotMomentsStudy
         20psphotPetrosianStudy
         21psphotForced
         22psphotMakePSF
         23psphotStack
  • branches/simtest_nebulous_branches/psphot/src/psphotSourceSize.c

    r21519 r27840  
    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    int xtest, ytest;
     16    bool apply; // apply CR mask?
     17} psphotSourceSizeOptions;
     18
     19// local functions:
     20bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf);
     21bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options);
     22bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options);
     23bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options);
     24bool psphotMaskCosmicRay (pmReadout *readout, pmSource *source, psImageMaskType maskVal);
     25bool psphotMaskCosmicRayFootprintCheck (psArray *sources);
     26int  psphotMaskCosmicRayConnected (int xPeak, int yPeak, psImage *mymask, psImage *myvar, psImage *edges, int binning, float sigma_thresh);
    927
    1028// we need to call this function after sources have been fitted to the PSF model and
     
    1432// deviation from the psf model at the r = FWHM/2 position
    1533
    16 bool psphotSourceSize(pmConfig *config, pmReadout *readout, psArray *sources, psMetadata *recipe, long first)
     34// for now, let's store the detections on the readout->analysis for each readout
     35bool psphotSourceSize (pmConfig *config, const pmFPAview *view, bool getPSFsize)
     36{
     37    bool status = true;
     38
     39    // select the appropriate recipe information
     40    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
     41    psAssert (recipe, "missing recipe?");
     42
     43    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
     44    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
     45
     46    // skip the chisq image (optionally?)
     47    int chisqNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.CHISQ.NUM");
     48    if (!status) chisqNum = -1;
     49
     50    // loop over the available readouts
     51    for (int i = 0; i < num; i++) {
     52        if (i == chisqNum) continue; // skip chisq image
     53        if (!psphotSourceSizeReadout (config, view, "PSPHOT.INPUT", i, recipe, getPSFsize)) {
     54            psError (PSPHOT_ERR_CONFIG, false, "failed on source size analysis for PSPHOT.INPUT entry %d", i);
     55            return false;
     56        }
     57    }
     58    return true;
     59}
     60
     61// this function use an internal flag to mark sources which have already been measured
     62bool psphotSourceSizeReadout(pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe, bool getPSFsize)
    1763{
    1864    bool status;
     65    psphotSourceSizeOptions options;
    1966
    2067    psTimerStart ("psphot.size");
    2168
     69    // find the currently selected readout
     70    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filename, index); // File of interest
     71    psAssert (file, "missing file?");
     72
     73    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
     74    psAssert (readout, "missing readout?");
     75
     76    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     77    psAssert (detections, "missing detections?");
     78
     79    psArray *sources = detections->allSources;
     80    psAssert (sources, "missing sources?");
     81
     82    if (!sources->n) {
     83        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping source size");
     84        return true;
     85    }
     86
     87    pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
     88    psAssert (psf, "missing psf?");
     89
    2290    // 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);
     91    options.maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
     92    assert (options.maskVal);
     93
     94    options.markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT"); // Mask value for bad pixels
     95    assert (options.markVal);
    2596
    2697    // 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");
     98    options.crMask  = pmConfigMaskGet("CR", config); // Mask value for cosmic rays
     99
     100    options.nSigmaCR = psMetadataLookupF32 (&status, recipe, "PSPHOT.CR.NSIGMA.LIMIT");
    30101    assert (status);
    31102
    32     float EXT_NSIGMA_LIMIT = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.LIMIT");
     103    // XXX recipe name is not great
     104    options.nSigmaApResid = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.LIMIT");
    33105    assert (status);
    34106
    35     int grow = psMetadataLookupS32(&status, recipe, "PSPHOT.CR.GROW"); // Growth size for CRs
    36     if (!status || grow < 0) {
     107    // XXX recipe name is not great
     108    options.nSigmaMoments = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.MOMENTS");
     109    assert (status);
     110
     111    // XXX recipe name is not great
     112    options.xtest = psMetadataLookupS32 (&status, recipe, "PSPHOT.CRMASK.XTEST");
     113    options.ytest = psMetadataLookupS32 (&status, recipe, "PSPHOT.CRMASK.YTEST");
     114    assert (status);
     115
     116    options.grow = psMetadataLookupS32(&status, recipe, "PSPHOT.CR.GROW"); // Growth size for CRs
     117    if (!status || options.grow < 0) {
    37118        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "PSPHOT.CR.GROW is not positive.");
    38119        return false;
    39120    }
    40121
    41     float soft = psMetadataLookupF32(&status, recipe, "PSPHOT.CR.NSIGMA.SOFTEN"); // Softening parameter
    42     if (!status || !isfinite(soft) || soft < 0.0) {
     122    options.soft = psMetadataLookupF32(&status, recipe, "PSPHOT.CR.NSIGMA.SOFTEN"); // Softening parameter
     123    if (!status || !isfinite(options.soft) || options.soft < 0.0) {
    43124        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++) {
     125        options.soft = 0.0;
     126    }
     127
     128    options.apply = psMetadataLookupBool(&status, recipe, "PSPHOT.CRMASK.APPLY"); // Growth size for CRs
     129    if (!status) {
     130        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "PSPHOT.CRMASK.APPLY is not defined.");
     131        return false;
     132    }
     133
     134    // We are using the value psfMag - 2.5*log10(moment.Sum) as a measure of the extendedness
     135    // of an object.  We need to model this distribution for the PSF stars before we can test
     136    // the significance for a specific object
     137    // XXX move this to the code that generates the PSF?
     138    // XXX store the results on pmPSF?
     139
     140    // XXX this should only be done on the first pass (ie, if we have newSources or allSources?)
     141    if (getPSFsize) {
     142        psphotSourceSizePSF (&options, sources, psf);
     143    }
     144
     145    // classify the sources based on ApResid and Moments (extended sources)
     146    // NOTE: only sources not already measured !(source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED)
     147    psphotSourceClass(readout, sources, recipe, psf, &options);
     148
     149    // NOTE: only sources not already measured !(source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED)
     150    psphotSourceSizeCR (readout, sources, &options);
     151
     152    // XXX fix this (was source->n  - first)
     153    psLogMsg ("psphot.size", PS_LOG_INFO, "measure source sizes for %ld sources: %f sec\n", sources->n, psTimerMark ("psphot.size"));
     154
     155    psphotVisualPlotSourceSize (recipe, readout->analysis, sources);
     156    psphotVisualShowSourceSize (readout, sources);
     157    psphotVisualPlotApResid (sources, options.ApResid, options.ApSysErr);
     158    psphotVisualShowSatStars (recipe, psf, sources);
     159
     160    return true;
     161}
     162
     163// model the apmifit distribution for the psf stars:
     164bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf) {
     165
     166    // select stats from the psf stars
     167    psVector *Ap = psVectorAllocEmpty (100, PS_TYPE_F32);
     168    psVector *ApErr = psVectorAllocEmpty (100, PS_TYPE_F32);
     169
     170    psImageMaskType maskVal = options->maskVal | options->markVal;
     171
     172    // XXX  why PHOT_WEIGHT??
     173    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
     174
     175    int num = 0;                        // Number of sources measured
     176    for (int i = 0; i < sources->n; i++) {
    49177        pmSource *source = sources->data[i];
     178        if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;
     179        num++;
     180
     181        // replace object in image
     182        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
     183            pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
     184        }
     185
     186        // clear the mask bit and set the circular mask pixels
     187        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     188        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
     189
     190        // XXX can we test if psfMag is set and calculate only if needed?
     191        pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
     192
     193        // clear the mask bit
     194        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     195
     196        // re-subtract the object, leave local sky
     197        pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
     198
     199        float apMag = -2.5*log10(source->moments->Sum);
     200        float dMag = source->psfMag - apMag;
     201
     202        psVectorAppend (Ap, dMag);
     203        psVectorAppend (ApErr, source->errMag);
     204    }
     205    if (num == 0) {
     206        // Not raising an error, because errors aren't being checked elsewhere in this function
     207        psFree(Ap);
     208        psFree(ApErr);
     209        return false;
     210    }
     211
     212    // model the distribution as a mean or median value and a systematic error from that value:
     213    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
     214    psVectorStats (stats, Ap, NULL, NULL, 0);
     215
     216    psVector *dAp = psVectorAlloc (Ap->n, PS_TYPE_F32);
     217    for (int i = 0; i < Ap->n; i++) {
     218        dAp->data.F32[i] = Ap->data.F32[i] - stats->robustMedian;
     219    }
     220
     221    options->ApResid = stats->robustMedian;
     222    options->ApSysErr = psVectorSystematicError(dAp, ApErr, 0.05);
     223    // XXX this is quite arbitrary...
     224    if (!isfinite(options->ApSysErr)) options->ApSysErr = 0.01;
     225    psLogMsg ("psphot", PS_LOG_DETAIL, "psf - Sum: %f +/- %f\n", options->ApResid, options->ApSysErr);
     226
     227    psFree (Ap);
     228    psFree (ApErr);
     229    psFree (stats);
     230    psFree (dAp);
     231
     232    return true;
     233}
     234
     235// classify sources based on the combination of psf-mag, Mxx, Myy
     236bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
     237
     238    bool status;
     239    pmPSFClump psfClump;
     240    char regionName[64];
     241
     242    psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4s %4s %4s %4s %4s %4s", "Npsf", "Next", "Nsat", "Ncr", "Nmiss", "Nskip");
     243
     244    int nRegions = psMetadataLookupS32 (&status, readout->analysis, "PSF.CLUMP.NREGIONS");
     245    for (int i = 0; i < nRegions; i ++) {
     246        snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
     247        psMetadata *regionMD = psMetadataLookupPtr (&status, readout->analysis, regionName);
     248        psAssert (regionMD, "regions must be defined by earlier call to psphotRoughClassRegion");
     249
     250        psRegion *region = psMetadataLookupPtr (&status, regionMD, "REGION");
     251        psAssert (region, "regions must be defined by earlier call to psphotRoughClassRegion");
     252
     253        // pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
     254        psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   psAssert (status, "missing PSF.CLUMP.X");
     255        psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   psAssert (status, "missing PSF.CLUMP.Y");
     256        psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  psAssert (status, "missing PSF.CLUMP.DX");
     257        psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  psAssert (status, "missing PSF.CLUMP.DY");
     258
     259        if ((psfClump.X < 0) || (psfClump.Y < 0) || !psfClump.X || !psfClump.Y || isnan(psfClump.X) || isnan(psfClump.Y)) {
     260            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);
     261            continue;
     262        }
     263
     264        if (!psphotSourceClassRegion (region, &psfClump, sources, recipe, psf, options)) {
     265            psLogMsg ("psphot", 4, "Failed to determine source classification for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);
     266            continue;
     267        }
     268        // psphotVisualPlotSourceSize (recipe, readout->analysis, sources);
     269    }
     270
     271    return true;
     272}
     273
     274# define SIZE_SN_LIM 10
     275bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
     276
     277    PS_ASSERT_PTR_NON_NULL(sources, false);
     278    PS_ASSERT_PTR_NON_NULL(recipe, false);
     279
     280    int Nsat  = 0;
     281    int Next  = 0;
     282    int Npsf  = 0;
     283    int Ncr   = 0;
     284    int Nmiss = 0;
     285    int Nskip = 0;
     286
     287    pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;
     288    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
     289
     290    psImageMaskType maskVal = options->maskVal | options->markVal;
     291
     292    for (psS32 i = 0 ; i < sources->n ; i++) {
     293
     294        pmSource *source = (pmSource *) sources->data[i];
     295
     296        // psfClumps are found for image subregions:
     297        // skip sources not in this region
     298        if (source->peak->x <  region->x0) continue;
     299        if (source->peak->x >= region->x1) continue;
     300        if (source->peak->y <  region->y0) continue;
     301        if (source->peak->y >= region->y1) continue;
    50302
    51303        // skip source if it was already measured
    52         if (isfinite(source->crNsigma)) {
    53             psTrace("psphot", 7, "Not calculating extNsigma,crNsigma since already measured\n");
     304        if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
     305            psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
    54306            continue;
    55307        }
     
    57309        // source must have been subtracted
    58310        if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
    59             source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
    60             psTrace("psphot", 7, "Not calculating extNsigma,crNsigma since source is not subtracted\n");
    61             continue;
    62         }
    63 
    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) {
     311            source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
     312            psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");
     313            continue;
     314        }
     315
     316        // we are basically classifying by moments
     317        psAssert (source->moments, "why is this source missing moments?");
     318        if (source->mode & noMoments) {
     319            Nskip ++;
     320            continue;
     321        }
     322
     323        // convert to Mmaj, Mmin:
     324        psF32 Mxx = source->moments->Mxx;
     325        psF32 Myy = source->moments->Myy;
     326
     327        // replace object in image
     328        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
     329            pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
     330        }
     331
     332        // clear the mask bit and set the circular mask pixels
     333        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     334        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
     335
     336        // XXX can we test if psfMag is set and calculate only if needed?
     337        pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
     338
     339        // clear the mask bit
     340        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     341
     342        // re-subtract the object, leave local sky
     343        pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
     344
     345        float apMag = -2.5*log10(source->moments->Sum);
     346        float dMag = source->psfMag - apMag;
     347
     348        // set nSigma to include both systematic and poisson error terms
     349        // XXX the 'poisson error' contribution for size is probably wrong...
     350        float nSigmaMAG = (dMag - options->ApResid) / hypot(source->errMag, options->ApSysErr);
     351        float nSigmaMXX = (Mxx - psfClump->X) / hypot(psfClump->dX, psfClump->X*psfClump->X*source->errMag);
     352        float nSigmaMYY = (Myy - psfClump->Y) / hypot(psfClump->dY, psfClump->Y*psfClump->Y*source->errMag);
     353
     354        // partially-masked sources are more likely to be mis-measured PSFs
     355        float sizeBias = 1.0;
     356        if (source->pixWeight < 0.9) {
     357            sizeBias = 3.0;
     358        }
     359
     360        float minMxx = psfClump->X - sizeBias*options->nSigmaMoments*psfClump->dX;
     361        float minMyy = psfClump->Y - sizeBias*options->nSigmaMoments*psfClump->dY;
     362
     363        // include MAG, MXX, and MYY?
     364        source->extNsigma = nSigmaMAG;
     365
     366        // notes to clarify the source size classification rules:
     367        // * a defect should be functionally equivalent to a cosmic ray
     368        // * CR & defect should have a faintess limit (min S/N)
     369        // * SAT stars should not be faint, but defects may?
     370
     371        // Anything within this region is a probably PSF-like object. Saturated stars may land
     372        // in this region, but are detected elsewhere on the basis of their peak value.
     373        bool isPSF = (fabs(nSigmaMAG) < options->nSigmaApResid) && (fabs(nSigmaMXX) < sizeBias*options->nSigmaMoments) && (fabs(nSigmaMYY) < sizeBias*options->nSigmaMoments);
     374        if (isPSF) {
     375          psTrace("psphotSourceClassRegion.PSF",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g PSF\t%g %g\n",
     376                  source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigmaMAG,
     377                  options->nSigmaApResid,sizeBias*options->nSigmaMoments);
     378          source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     379          Npsf ++;
     380          continue;
     381        }
     382
     383        // Defects may not always match CRs from peak curvature analysis
     384        // Defects may also be marked as SATSTAR -- XXX deactivate this flag?
     385        // XXX this rule is not great
     386        // XXX only accept brightish detections as CRs
     387        // (nSigmaMAG < -options->nSigmaApResid) ||
     388        bool isCR = isCR = (source->errMag < 1.0 / SIZE_SN_LIM) && ((Mxx < minMxx) || (Myy < minMyy));
     389        if (isCR) {
     390            psTrace("psphotSourceClassRegion.CR",4,"CLASS: %g %g %f\t%g %g  %g %g  %g %g\t%g %g\t%g CR\t%g %g\n",
     391                    source->peak->xf,source->peak->yf,source->pixWeight,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigmaMAG,
     392                    options->nSigmaApResid,sizeBias*options->nSigmaMoments);
     393            source->mode |= PM_SOURCE_MODE_DEFECT;
     394            source->tmpFlags |= PM_SOURCE_TMPF_SIZE_CR_CANDIDATE;
     395            Ncr ++;
     396            continue;
     397        }
     398
     399        // saturated star (determined in PSF fit).  These may also be saturated galaxies, or
     400        // just large saturated regions.
     401        if (source->mode & PM_SOURCE_MODE_SATSTAR) {
     402            psTrace("psphotSourceClassRegion.SAT",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g SAT\t%g %g\n",
     403                    source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigmaMAG,
     404                    options->nSigmaApResid,sizeBias*options->nSigmaMoments);
     405            source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     406            Nsat ++;
     407            continue;
     408        }
     409
     410        // XXX allow the Mxx, Myy to be less than psfClump->X,Y (by some nSigma)?
     411        bool isEXT = (nSigmaMAG > options->nSigmaApResid) || (Mxx > minMxx) || (Myy > minMyy);
     412        if (isEXT) {
     413          psTrace("psphotSourceClassRegion.EXT",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g Ext\t%g %g\n",
     414                  source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigmaMAG,
     415                  options->nSigmaApResid,sizeBias*options->nSigmaMoments);
     416
    74417            source->mode |= PM_SOURCE_MODE_EXT_LIMIT;
    75         }
     418            source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     419            Next ++;
     420            continue;
     421        }
     422        psTrace("psphotSourceClassRegion.MISS",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g Unk\t%g %g\n",
     423                source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigmaMAG,
     424                options->nSigmaApResid,sizeBias*options->nSigmaMoments);
     425
     426        // sources that reach here are probably too faint for a reasonable source size measurement
     427        // psWarning ("sourse size was missed for %f,%f : %f %f -- %f\n", source->peak->xf, source->peak->yf, Mxx, Myy, nSigmaMAG);
     428        source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     429        Nmiss ++;
     430    }
     431
     432    psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4d %4d %4d %4d %4d %4d", Npsf, Next, Nsat, Ncr, Nmiss, Nskip);
     433
     434    return true;
     435}
     436
     437// given an object suspected to be a defect, generate a pixel mask using the Lapacian transform
     438// if enough of the object is detected as 'sharp', consider the object a cosmic ray
     439bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) {
     440
     441    psTimerStart ("psphot.cr");
     442
     443    int nMasked = 0;
     444    for (int i = 0; i < sources->n; i++) {
     445        pmSource *source = sources->data[i];
     446
     447        // skip source if it was already measured
     448        if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
     449            psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
     450            continue;
     451        }
     452
     453        // only check candidates marked above
     454        if (!(source->tmpFlags & PM_SOURCE_TMPF_SIZE_CR_CANDIDATE)) {
     455            psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
     456            continue;
     457        }
     458
     459        // skip unless this source is thought to be a cosmic ray.  flag the detection and mask the pixels
     460        // XXX this may be degenerate with the above test
     461        if (!(source->mode & PM_SOURCE_MODE_DEFECT)) continue;
    76462
    77463        // Integer position of peak
     
    79465        int yPeak = source->peak->yf - source->pixels->row0 + 0.5;
    80466
    81         // XXX for now, skip sources which are too close to a boundary
    82         // XXX raise a flag?
     467        // Skip sources which are too close to a boundary.  These are mostly caught as DEFECT
    83468        if (xPeak < 1 || xPeak > source->pixels->numCols - 2 ||
    84469            yPeak < 1 || yPeak > source->pixels->numRows - 2) {
    85             source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
    86470            psTrace("psphot", 7, "Not calculating crNsigma due to edge\n");
    87471            continue;
    88472        }
    89473
    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         }
     474        // XXX for testing, only CRMASK a single source:
     475        if (options->xtest && (fabs(source->peak->xf - options->xtest) > 5)) continue;
     476        if (options->ytest && (fabs(source->peak->yf - options->ytest) > 5)) continue;
     477
     478        // replace object in image
     479        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
     480            pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
     481        }
     482
     483        // XXX this is running slowly and is too agressive, but it more-or-less works
     484        psTrace("psphot", 6, "mask cosmic ray at %f, %f\n", source->peak->xf, source->peak->yf);
     485        if (options->apply) {
     486            psphotMaskCosmicRay(readout, source, options->crMask);
     487        } else {
     488            source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     489        }
     490        nMasked ++;
     491
     492        // re-subtract the object, leave local sky
     493        pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
    170494    }
    171495
    172496    // now that we have masked pixels associated with CRs, we can grow the mask
    173     if (grow > 0) {
     497    if (options->grow > 0) {
    174498        bool oldThreads = psImageConvolveSetThreads(true); // Old value of threading for psImageConvolveMask
    175         psImage *newMask = psImageConvolveMask(NULL, readout->mask, crMask, crMask, -grow, grow, -grow, grow);
     499        psImage *newMask = psImageConvolveMask(NULL, readout->mask, options->crMask, options->crMask, -options->grow, options->grow, -options->grow, options->grow);
    176500        psImageConvolveSetThreads(oldThreads);
    177501        if (!newMask) {
     
    183507    }
    184508
    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);
    190 
     509    psLogMsg ("psphot.cr", PS_LOG_INFO, "mask CR: %d masked in %f sec\n", nMasked, psTimerMark ("psphot.cr"));
     510
     511    // XXX test : save the mask image
     512    if (0) {
     513        psphotSaveImage (NULL, readout->mask,   "mask.fits");
     514    }
     515
     516    return true;
     517}
     518
     519# define DUMPPICS 0
     520# define LIMIT_XRANGE(X, IMAGE) { X = PS_MIN(PS_MAX(0, X), IMAGE->numCols); }
     521# define LIMIT_YRANGE(Y, IMAGE) { Y = PS_MIN(PS_MAX(0, Y), IMAGE->numRows); }
     522
     523// Comments by CZW 20091209 : Mechanics of how to identify CR pixels taken from "Cosmic-Ray
     524// Rejection by Laplacian Edge Detection" by Pieter van Dokkum, arXiv:astro-ph/0108003.  This
     525// does no repair or recovery of the CR pixels, it only masks them out.  My test code can be
     526// found at /data/ipp031.0/watersc1/psphot.20091209/algo_check.c
     527bool psphotMaskCosmicRay (pmReadout *readout, pmSource *source, psImageMaskType maskVal) {
     528
     529    // Get the actual images and information about the peak.
     530    psImage *mask = readout->mask;
     531    pmPeak *peak = source->peak;
     532    pmFootprint *footprint = peak->footprint;
     533
     534    // Bounding boxes are inclusive of final pixel
     535    int xs = footprint->bbox.x0;
     536    int xe = footprint->bbox.x1 + 1;
     537    int ys = footprint->bbox.y0;
     538    int ye = footprint->bbox.y1 + 1;
     539
     540    LIMIT_XRANGE(xs, mask);
     541    LIMIT_XRANGE(xe, mask);
     542    LIMIT_YRANGE(ys, mask);
     543    LIMIT_YRANGE(ye, mask);
     544
     545    int dx = xe - xs;
     546    int dy = ye - ys;
     547
     548    psImage *image= readout->image;
     549    psImage *variance = readout->variance;
     550
     551    int binning = 2;
     552    float sigma_thresh = 3.0;
     553    int max_iter = 1; // XXX with isophot masking, we only want to do a single pass
     554
     555    // Temporary images.
     556    psImage *mypix  = psImageAlloc(dx,dy,image->type.type);
     557    psImage *myfix  = psImageAlloc(dx,dy,image->type.type);
     558    psImage *myvar  = psImageAlloc(dx,dy,image->type.type);
     559    psImage *binned = psImageAlloc(dx * binning,dy * binning,image->type.type);
     560    psImage *conved = psImageAlloc(dx * binning,dy * binning,image->type.type);
     561    psImage *edges  = psImageAlloc(dx,dy,image->type.type);
     562    psImage *mymask = psImageAlloc(dx,dy,PS_TYPE_IMAGE_MASK);
     563
     564    // Load my copy of things.
     565    for (int y = 0; y < dy; y++) {
     566        for (int x = 0; x < dx; x++) {
     567            mypix->data.F32[y][x] = image->data.F32[y+ys][x+xs];
     568            myvar->data.F32[y][x] = variance->data.F32[y+ys][x+xs];
     569            mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = 0x00;
     570        }
     571    }
     572    // Mask so I can see on the output image where the footprint is.
     573    for (int i = 0; i < footprint->spans->n; i++) {
     574        pmSpan *sp = footprint->spans->data[i];
     575        for (int j = sp->x0; j <= sp->x1; j++) {
     576            int y = sp->y - ys;
     577            int x = j - xs;
     578            mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= 0x01;
     579        }
     580    }
     581
     582    int nCRpix = 1; // force at least one pass...
     583    for (int iteration = 0; (iteration < max_iter) && (nCRpix > 0); iteration++) {
     584        nCRpix = 0;
     585        psImageInit (binned, 0.0);
     586        psImageInit (conved, 0.0);
     587        psImageInit (edges, 0.0);
     588
     589        // Make subsampled image. Maybe this should be called "unbinned" or something
     590        for (int y = 0; y < binning * dy; y++) {
     591            int yraw = y / binning;
     592            for (int x = 0; x < binning * dx; x++) {
     593                int xraw = x / binning;
     594                binned->data.F32[y][x] = mypix->data.F32[yraw][xraw];
     595            }
     596        }
     597
     598        // Apply Laplace transform (kernel = [[0 -0.25 0][-0.25 1 -0.25][0 -0.25 0]]), clipping at zero
     599        for (int y = 1; y < binning * dy - 1; y++) {
     600            for (int x = 1; x < binning * dx - 1; x++) {
     601                float value = binned->data.F32[y][x] - 0.25 *
     602                    (binned->data.F32[y+0][x-1] + binned->data.F32[y+0][x+1] +
     603                     binned->data.F32[y-1][x+0] + binned->data.F32[y+1][x+0]);
     604                value = PS_MAX(0.0, value);
     605
     606                conved->data.F32[y][x] = value;
     607            }
     608        }
     609
     610        // Create an edge map by rebinning
     611        for (int y = 0; y < binning * dy; y++) {
     612            int yraw = y / binning;
     613            for (int x = 0; x < binning * dx; x++) {
     614                int xraw = x / binning;
     615                edges->data.F32[yraw][xraw] += conved->data.F32[y][x];
     616            }
     617        }
     618
     619        // coordinate of peak in subimage pixels:
     620        int xPeak = peak->x - xs;
     621        int yPeak = peak->y - ys;
     622
     623        // Modify my mask if we're above the significance threshold, but only for connected pixels
     624        nCRpix = psphotMaskCosmicRayConnected (xPeak, yPeak, mymask, myvar, edges, binning, sigma_thresh);
     625
     626# if DUMPPICS
     627        psphotSaveImage (NULL, mypix,   "crmask.pix.fits");
     628# endif
     629
     630// XXX do not repair the pixels in isophot version
     631# if 0
     632        // "Repair" Masked pixels for the next round.
     633        for (int y = 1; y < dy - 1; y++) {
     634            for (int x = 1; x < dx - 1; x++) {
     635                if (!(mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & 0x40)) {
     636                    myfix->data.F32[y][x] = mypix->data.F32[y][x];
     637                    continue;
     638                }
     639                myfix->data.F32[y][x] = 0.25 *
     640                    (mypix->data.F32[y+0][x-1] + mypix->data.F32[y+0][x+1] +
     641                     mypix->data.F32[y-1][x+0] + mypix->data.F32[y+1][x+0]);
     642            }
     643        }
     644
     645        // "Repair" Masked pixels for the next round.
     646        for (int y = 1; y < dy - 1; y++) {
     647            for (int x = 1; x < dx - 1; x++) {
     648                mypix->data.F32[y][x] = myfix->data.F32[y][x];
     649            }
     650        }
     651# endif
     652
     653# if DUMPPICS
     654        fprintf (stderr, "CRMASK %d %d %d %d %d\n", xs, ys, dx, dy, iteration);
     655        psphotSaveImage (NULL, mypix,   "crmask.fix.fits");
     656        psphotSaveImage (NULL, myvar,   "crmask.var.fits");
     657        psphotSaveImage (NULL, binned,  "crmask.binn.fits");
     658        psphotSaveImage (NULL, conved,  "crmask.conv.fits");
     659        psphotSaveImage (NULL, edges,   "crmask.edge.fits");
     660        psphotSaveImage (NULL, mymask,  "crmask.mask.fits");
     661# endif
     662        psTrace("psphot.czw",2,"Iter: %d Count: %d",iteration, nCRpix);
     663    }
     664
     665# if 0
     666    // A solitary masked pixel is likely a lie. Remove those
     667    // XXX can't we use nCRpix == 1 to test for these?
     668    for (int x = 0; x < dx; x++) {
     669        for (int y = 0; y < dy; y++) {
     670            if (!(mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & 0x40)) continue;
     671            if ((x-1 >= 0) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x-1] & 0x40)) {
     672                continue;
     673            }
     674            if ((y-1 >= 0) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y-1][x] & 0x40)) {
     675                continue;
     676            }
     677            if ((x+1 < dx) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x+1] & 0x40)) {
     678                continue;
     679            }
     680            if ((y+1 < dy) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y+1][x] & 0x40)) {
     681                continue;
     682            }
     683            mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] ^= 0x40;
     684        }
     685    }
     686# endif
     687
     688    // transfer temporary mask to real mask & count masked pixels
     689    nCRpix = 0;
     690    for (int x = 0; x < dx; x++) {
     691        for (int y = 0; y < dy; y++) {
     692            if (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & 0x40) {
     693                mask->data.PS_TYPE_IMAGE_MASK_DATA[y+ys+mask->row0][x+xs+mask->col0] |= maskVal;
     694                nCRpix ++;
     695            }
     696        }
     697    }
     698
     699    // XXX if we decide this REALLY is a cosmic ray, set the CR_LIMIT bit
     700    if (nCRpix > 1) {
     701        source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     702        source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     703    }
     704    // fprintf (stderr, "CRMASK %d %d %d %d %d\n", peak->x, peak->y, dx, dy, nCRpix);
     705
     706    psFree(mypix);
     707    psFree(myfix);
     708    psFree(myvar);
     709    psFree(binned);
     710    psFree(conved);
     711    psFree(edges);
     712    psFree(mymask);
     713
     714    return true;
     715}
     716
     717bool psphotMaskCosmicRayFootprintCheck (psArray *sources) {
     718
     719    for (int i = 0; i < sources->n; i++) {
     720        pmSource *source = sources->data[i];
     721        pmPeak *peak = source->peak;
     722        pmFootprint *footprint = peak->footprint;
     723        if (!footprint) continue;
     724        for (int j = 0; j < footprint->spans->n; j++) {
     725            pmSpan *sp = footprint->spans->data[j];
     726            psAssert (sp, "missing span");
     727        }
     728    }
     729    return true;
     730}
     731
     732/**** ------ old versions of cosmic ray masking ----- ****/
     733
     734bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
     735
     736// This attempt to mask the cosmic rays used the isophotal boundary
     737bool psphotMaskCosmicRay_V1 (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
     738
     739    // replace the source flux
     740    pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
     741
     742    // flag this as a CR
     743    source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     744    pmPeak *peak = source->peak;
     745    psAssert (peak, "NULL peak");
     746
     747    // grab the matching footprint
     748    pmFootprint *footprint = peak->footprint;
     749    if (!footprint) {
     750      psTrace("psphot.czw",2,"Using isophot CR mask code.");
     751
     752        // if we have not footprint, use the old code to mask by isophot
     753        psphotMaskCosmicRayIsophot (source, maskVal, crMask);
     754        return true;
     755    }
     756
     757    if (!footprint->spans) {
     758      psTrace("psphot.czw",2,"Using isophot CR mask code.");
     759
     760        // if we have no footprint, use the old code to mask by isophot
     761        psphotMaskCosmicRayIsophot (source, maskVal, crMask);
     762        return true;
     763    }
     764    psphotMaskCosmicRayIsophot (source, maskVal, crMask);
     765    // mask all of the pixels covered by the spans of the footprint
     766    for (int j = 1; j < footprint->spans->n; j++) {
     767        pmSpan *span1 = footprint->spans->data[j];
     768
     769        int iy = span1->y;
     770        int xs = span1->x0;
     771        int xe = span1->x1;
     772
     773        for (int ix = xs; ix < xe; ix++) {
     774            mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
     775        }
     776    }
     777    return true;
     778}
     779
     780# define VERBOSE 0
     781int psphotMaskCosmicRayConnected (int xPeak, int yPeak, psImage *mymask, psImage *myvar, psImage *edges, int binning, float sigma_thresh) {
     782
     783    int xLo, xRo;
     784    int nCRpix = 0;
     785
     786    float noise_factor = 5.0 / 4.0;  // Intrinsic to the Laplacian making noise spikes spikier.
     787
     788    // mark the pixels in this row to the left, then the right. stay within footprint
     789    int xL = xPeak; // find the range of valid pixels in this row
     790    int xR = xPeak;
     791    for (int ix = xPeak; (ix >= 0) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[yPeak][ix] & 0x01); ix--) {
     792        float noise = binning * sqrt(noise_factor * myvar->data.F32[yPeak][ix]);
     793        float value = edges->data.F32[yPeak][ix] / noise;
     794        if (value < sigma_thresh ) break;
     795        mymask->data.PS_TYPE_IMAGE_MASK_DATA[yPeak][ix] |= 0x40;
     796        xL = ix;
     797        nCRpix ++;
     798        if (VERBOSE) fprintf (stderr, "mark %d,%d (%d) : %d - %d\n", ix, yPeak, nCRpix, xL, xR);
     799    }
     800    for (int ix = xPeak; (ix < mymask->numCols) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[yPeak][ix] & 0x01); ix++) {
     801        float noise = binning * sqrt(noise_factor * myvar->data.F32[yPeak][ix]);
     802        float value = edges->data.F32[yPeak][ix] / noise;
     803        if (value < sigma_thresh ) break;
     804        mymask->data.PS_TYPE_IMAGE_MASK_DATA[yPeak][ix] |= 0x40;
     805        xR = ix;
     806        nCRpix ++;
     807        if (VERBOSE) fprintf (stderr, "mark %d,%d (%d) : %d - %d\n", ix, yPeak, nCRpix, xL, xR);
     808    }
     809    // xL and xR mark the first and last valid pixel in the row
     810
     811    // for each of the neighboring rows, mark the high pixels if they touch the range xL to xR
     812    xLo = PS_MAX(xL - 1, 0);
     813    xRo = PS_MIN(xR + 1, mymask->numCols);
     814
     815    // first go down:
     816    for (int iy = yPeak - 1; iy >= 0; iy--) {
     817
     818        int xLn = -1;
     819        int xRn = -1;
     820        int newPix = 0;
     821
     822        // mark the pixels in the good range
     823        for (int ix = xLo; ix < xRo; ix++) {
     824            if (!(mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & 0x01)) continue; // only use pixels in the footprint
     825            float noise = binning * sqrt(noise_factor * myvar->data.F32[iy][ix]);
     826            float value = edges->data.F32[iy][ix] / noise;
     827            if (value < sigma_thresh ) continue;
     828            mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= 0x40;
     829            if (xLn == -1) xLn = ix; // first valid pixel in this row
     830            xRn = ix;                // last valid pixel in this row
     831            nCRpix ++;
     832            newPix ++;
     833            if (VERBOSE) fprintf (stderr, "mark C %d,%d (%d) : %d - %d | %d - %d | %d - %d \n", ix, iy, nCRpix, xL, xR, xLo, xRo, xLn, xRn);
     834        }
     835
     836        // mark the pixels to the left of the good range
     837        for (int ix = xLo; (ix >= 0) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & 0x01); ix--) {
     838            float noise = binning * sqrt(noise_factor * myvar->data.F32[iy][ix]);
     839            float value = edges->data.F32[iy][ix] / noise;
     840            if (value < sigma_thresh ) break;
     841            mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= 0x40;
     842            if (xRn == -1) xRn = ix; // last valid pixel in this row
     843            xLn = ix;
     844            nCRpix ++;
     845            newPix ++;
     846            if (VERBOSE) fprintf (stderr, "mark L %d,%d (%d) : %d - %d | %d - %d | %d - %d \n", ix, iy, nCRpix, xL, xR, xLo, xRo, xLn, xRn);
     847        }
     848
     849        // mark the pixels to the right of the good range
     850        for (int ix = xRo; (ix < mymask->numCols) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & 0x01); ix++) {
     851            float noise = binning * sqrt(noise_factor * myvar->data.F32[iy][ix]);
     852            float value = edges->data.F32[iy][ix] / noise;
     853            if (value < sigma_thresh ) break;
     854            mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= 0x40;
     855            if (xLn == -1) xLn = ix; // first valid pixel in this row
     856            xRn = ix;
     857            nCRpix ++;
     858            newPix ++;
     859            if (VERBOSE) fprintf (stderr, "mark R %d,%d (%d) : %d - %d | %d - %d | %d - %d \n", ix, iy, nCRpix, xL, xR, xLo, xRo, xLn, xRn);
     860        }
     861        if (newPix == 0) break;
     862        xLo = PS_MAX(xLn - 1, 0);
     863        xRo = PS_MIN(xRn + 1, mymask->numCols);
     864    }
     865
     866    xLo = PS_MAX(xL - 1, 0);
     867    xRo = PS_MIN(xR + 1, mymask->numCols);
     868
     869    // next go up:
     870    for (int iy = yPeak + 1; iy < mymask->numRows; iy++) {
     871
     872        int xLn = -1;
     873        int xRn = -1;
     874        int newPix = 0;
     875
     876        // mark the pixels in the good range
     877        for (int ix = xLo; ix < xRo; ix++) {
     878            if (!(mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & 0x01)) continue; // only use pixels in the footprint
     879            float noise = binning * sqrt(noise_factor * myvar->data.F32[iy][ix]);
     880            float value = edges->data.F32[iy][ix] / noise;
     881            if (value < sigma_thresh ) continue;
     882            mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= 0x40;
     883            if (xLn == -1) xLn = ix; // first valid pixel in this row
     884            xRn = ix;                // last valid pixel in this row
     885            nCRpix ++;
     886            newPix ++;
     887            if (VERBOSE) fprintf (stderr, "mark C %d,%d (%d) : %d - %d | %d - %d | %d - %d \n", ix, iy, nCRpix, xL, xR, xLo, xRo, xLn, xRn);
     888        }
     889
     890        // mark the pixels to the left of the good range
     891        for (int ix = xLo; (ix >= 0) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & 0x01); ix--) {
     892            float noise = binning * sqrt(noise_factor * myvar->data.F32[iy][ix]);
     893            float value = edges->data.F32[iy][ix] / noise;
     894            if (value < sigma_thresh ) break;
     895            mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= 0x40;
     896            if (xRn == -1) xRn = ix; // last valid pixel in this row
     897            xLn = ix;
     898            nCRpix ++;
     899            newPix ++;
     900            if (VERBOSE) fprintf (stderr, "mark L %d,%d (%d) : %d - %d | %d - %d | %d - %d \n", ix, iy, nCRpix, xL, xR, xLo, xRo, xLn, xRn);
     901        }
     902
     903        // mark the pixels to the right of the good range
     904        for (int ix = xRo; (ix < mymask->numCols) && (mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & 0x01); ix++) {
     905            float noise = binning * sqrt(noise_factor * myvar->data.F32[iy][ix]);
     906            float value = edges->data.F32[iy][ix] / noise;
     907            if (value < sigma_thresh ) break;
     908            mymask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= 0x40;
     909            if (xLn == -1) xLn = ix; // first valid pixel in this row
     910            xRn = ix;
     911            nCRpix ++;
     912            newPix ++;
     913            if (VERBOSE) fprintf (stderr, "mark R %d,%d (%d) : %d - %d | %d - %d | %d - %d \n", ix, iy, nCRpix, xL, xR, xLo, xRo, xLn, xRn);
     914        }
     915        if (newPix == 0) break;
     916        xLo = PS_MAX(xLn - 1, 0);
     917        xRo = PS_MIN(xRn + 1, mymask->numCols);
     918    }
     919
     920    return nCRpix;
     921}
     922
     923bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
     924
     925    source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     926    pmPeak *peak = source->peak;
     927    psAssert (peak, "NULL peak");
     928
     929    psImage *mask   = source->maskView;
     930    psImage *pixels = source->pixels;
     931    psImage *variance = source->variance;
     932
     933    // XXX This should be a recipe variable
     934# define SN_LIMIT 5.0
     935
     936    int xo = peak->x - pixels->col0;
     937    int yo = peak->y - pixels->row0;
     938
     939    // mark the pixels in this row to the left, then the right
     940    for (int ix = xo; ix >= 0; ix--) {
     941        float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
     942        if (SN > SN_LIMIT) {
     943            mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
     944        }
     945    }
     946    for (int ix = xo + 1; ix < pixels->numCols; ix++) {
     947        float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
     948        if (SN > SN_LIMIT) {
     949            mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
     950        }
     951    }
     952
     953    // for each of the neighboring rows, mark the high pixels if they have a marked neighbor
     954    // first go up:
     955    for (int iy = PS_MIN(yo, mask->numRows-2); iy >= 0; iy--) {
     956        // mark the pixels in this row to the left, then the right
     957        for (int ix = 0; ix < pixels->numCols; ix++) {
     958            float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
     959            if (SN < SN_LIMIT) continue;
     960
     961            bool valid = false;
     962            valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix] & crMask);
     963            valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix-1] & crMask) : 0;
     964            valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix+1] & crMask) : 0;
     965
     966            if (!valid) continue;
     967            mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
     968        }
     969    }
     970    // next go down:
     971    for (int iy = PS_MIN(yo+1, mask->numRows-1); iy < pixels->numRows; iy++) {
     972        // mark the pixels in this row to the left, then the right
     973        for (int ix = 0; ix < pixels->numCols; ix++) {
     974            float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
     975            if (SN < SN_LIMIT) continue;
     976
     977            bool valid = false;
     978            valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix] & crMask);
     979            valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix-1] & crMask) : 0;
     980            valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix+1] & crMask) : 0;
     981
     982            if (!valid) continue;
     983            mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
     984        }
     985    }
    191986    return true;
    192987}
     
    194989// given the PSF ellipse parameters, navigate around the 1sigma contour, return the total
    195990// 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)
     991// deviations?  NOTE: This function was an early attempt to classify extended objects, and is
     992// no longer used by psphot.
     993float psphotModelContour(const psImage *image, const psImage *variance, const psImage *mask,
     994                         psImageMaskType maskVal, const pmModel *model, float Ro)
    199995{
    200996    psF32 *PAR = model->params->data.F32; // Model parameters
     
    2651061}
    2661062
    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         }
     1063// this was an old attempt to identify cosmic rays based on the peak curvature
     1064bool psphotSourcePeakCurvature (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) {
     1065
     1066    // classify the sources based on the CR test (place this in a function?)
     1067    // XXX use an internal flag to mark sources which have already been measured
     1068    for (int i = 0; i < sources->n; i++) {
     1069        pmSource *source = sources->data[i];
     1070
     1071        // skip source if it was already measured
     1072        if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
     1073            psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
     1074            continue;
     1075        }
     1076
     1077        // source must have been subtracted
     1078        if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
     1079            source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
     1080            psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");
     1081            continue;
     1082        }
     1083
     1084        psF32 **resid  = source->pixels->data.F32;
     1085        psF32 **variance = source->variance->data.F32;
     1086        psImageMaskType **mask = source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
     1087
     1088        // Integer position of peak
     1089        int xPeak = source->peak->xf - source->pixels->col0 + 0.5;
     1090        int yPeak = source->peak->yf - source->pixels->row0 + 0.5;
     1091
     1092        // Skip sources which are too close to a boundary.  These are mostly caught as DEFECT
     1093        if (xPeak < 1 || xPeak > source->pixels->numCols - 2 ||
     1094            yPeak < 1 || yPeak > source->pixels->numRows - 2) {
     1095            psTrace("psphot", 7, "Not calculating crNsigma due to edge\n");
     1096            continue;
     1097        }
     1098
     1099        // Skip sources with masked pixels.  These are mostly caught as DEFECT
     1100        bool keep = true;
     1101        for (int iy = -1; (iy <= +1) && keep; iy++) {
     1102            for (int ix = -1; (ix <= +1) && keep; ix++) {
     1103                if (mask[yPeak+iy][xPeak+ix] & options->maskVal) {
     1104                    keep = false;
     1105                }
     1106            }
     1107        }
     1108        if (!keep) {
     1109            psTrace("psphot", 7, "Not calculating crNsigma due to masked pixels\n");
     1110            continue;
     1111        }
     1112
     1113        // Compare the central pixel with those on either side, for the four possible lines through it.
     1114
     1115        // Soften variances (add systematic error)
     1116        float softening = options->soft * PS_SQR(source->peak->flux); // Softening for variances
     1117
     1118        // Across the middle: y = 0
     1119        float cX = 2*resid[yPeak][xPeak]   - resid[yPeak+0][xPeak-1]  - resid[yPeak+0][xPeak+1];
     1120        float dcX = 4*variance[yPeak][xPeak] + variance[yPeak+0][xPeak-1] + variance[yPeak+0][xPeak+1];
     1121        float nX = cX / sqrtf(dcX + softening);
     1122
     1123        // Up the centre: x = 0
     1124        float cY = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak+0]  - resid[yPeak+1][xPeak+0];
     1125        float dcY = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak+0] + variance[yPeak+1][xPeak+0];
     1126        float nY = cY / sqrtf(dcY + softening);
     1127
     1128        // Diagonal: x = y
     1129        float cL = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak-1]  - resid[yPeak+1][xPeak+1];
     1130        float dcL = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak-1] + variance[yPeak+1][xPeak+1];
     1131        float nL = cL / sqrtf(dcL + softening);
     1132
     1133        // Diagonal: x = - y
     1134        float cR = 2*resid[yPeak][xPeak]   - resid[yPeak+1][xPeak-1]  - resid[yPeak-1][xPeak+1];
     1135        float dcR = 4*variance[yPeak][xPeak] + variance[yPeak+1][xPeak-1] + variance[yPeak-1][xPeak+1];
     1136        float nR = cR / sqrtf(dcR + softening);
     1137
     1138        // P(chisq > chisq_obs; Ndof) = gamma_Q (Ndof/2, chisq/2)
     1139        // Ndof = 4 ? (four measurements, no free parameters)
     1140        // XXX this value is going to be biased low because of systematic errors.
     1141        // we need to calibrate it somehow
     1142        // source->psfProb = gsl_sf_gamma_inc_Q (2, 0.5*chisq);
     1143
     1144        // not strictly accurate: overcounts the chisq contribution from the center pixel (by
     1145        // factor of 4); also biases a bit low if any pixels are masked
     1146        // XXX I am not sure I want to keep this value...
     1147        source->psfChisq = PS_SQR(nX) + PS_SQR(nY) + PS_SQR(nL) + PS_SQR(nR);
     1148
     1149        float fCR = 0.0;
     1150        int nCR = 0;
     1151        if (nX > 0.0) {
     1152            fCR += nX;
     1153            nCR ++;
     1154        }
     1155        if (nY > 0.0) {
     1156            fCR += nY;
     1157            nCR ++;
     1158        }
     1159        if (nL > 0.0) {
     1160            fCR += nL;
     1161            nCR ++;
     1162        }
     1163        if (nR > 0.0) {
     1164            fCR += nR;
     1165            nCR ++;
     1166        }
     1167        source->crNsigma  = (nCR > 0)  ? fCR / nCR : 0.0;
     1168        source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     1169
     1170        if (!isfinite(source->crNsigma)) {
     1171            continue;
     1172        }
     1173
     1174        // this source is thought to be a cosmic ray.  flag the detection and mask the pixels
     1175        if (source->crNsigma > options->nSigmaCR) {
     1176            source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     1177            // XXX still testing... : psphotMaskCosmicRay (readout->mask, source, maskVal, crMask);
     1178            // XXX acting strange... psphotMaskCosmicRay_Old (source, maskVal, crMask);
     1179        }
     1180    }
     1181
     1182    // now that we have masked pixels associated with CRs, we can grow the mask
     1183    if (options->grow > 0) {
     1184        bool oldThreads = psImageConvolveSetThreads(true); // Old value of threading for psImageConvolveMask
     1185        psImage *newMask = psImageConvolveMask(NULL, readout->mask, options->crMask, options->crMask, -options->grow, options->grow, -options->grow, options->grow);
     1186        psImageConvolveSetThreads(oldThreads);
     1187        if (!newMask) {
     1188            psError(PS_ERR_UNKNOWN, false, "Unable to grow CR mask");
     1189            return false;
     1190        }
     1191        psFree(readout->mask);
     1192        readout->mask = newMask;
    3031193    }
    3041194    return true;
    3051195}
    3061196
    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.