IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Jan 25, 2010, 7:52:41 PM (16 years ago)
Author:
eugene
Message:

various API updates to work with multiple INPUT files (not yet finished)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/psphot.stack.20100120/src/psphotSourceSize.c

    r26643 r26681  
    1515} psphotSourceSizeOptions;
    1616
     17// local functions:
     18bool psphotSourceSizeReadout(pmConfig *config, const pmFPAview *view, const char *filename, int index);
    1719bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf);
    1820bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options);
    1921bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options);
    2022bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options);
    21 bool psphotMaskCosmicRay (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
    22 bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
     23bool psphotMaskCosmicRay (pmReadout *readout, pmSource *source, psImageMaskType maskVal);
     24bool psphotMaskCosmicRayFootprintCheck (psArray *sources);
    2325
    2426// we need to call this function after sources have been fitted to the PSF model and
     
    3335    bool status = true;
    3436
     37    // select the appropriate recipe information
     38    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
     39    psAssert (recipe, "missing recipe?");
     40
    3541    int num = psMetadataAddS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
    3642    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
     
    3844    // loop over the available readouts
    3945    for (int i = 0; i < num; i++) {
    40         if (!psphotSourceSizeReadout (config, view, "PSPHOT.INPUT", i)) {
     46        if (!psphotSourceSizeReadout (config, view, "PSPHOT.INPUT", i, recipe)) {
    4147            psError (PSPHOT_ERR_CONFIG, false, "failed on saturated star deblend analysis for PSPHOT.INPUT entry %d", i);
    4248            return false;
     
    4753
    4854// XXX use an internal flag to mark sources which have already been measured
    49 // how to track the sources already measured?
    50 bool psphotSourceSize(pmConfig *config, const pmFPAview *view, const char *filename, int index)
     55bool psphotSourceSizeReadout(pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe)
    5156{
    5257    bool status;
     
    6570    psAssert (sources, "missing sources?");
    6671
     72    if (!sources->n) {
     73        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping source size");
     74        return true;
     75    }
     76
    6777    pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
    6878    psAssert (psf, "missing psf?");
    69 
    70     // select the appropriate recipe information
    71     psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
    72     psAssert (recipe, "missing recipe?");
    7379
    7480    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
     
    106112
    107113    // We are using the value psfMag - 2.5*log10(moment.Sum) as a measure of the extendedness
    108     // of and object.  We need to model this distribution for the PSF stars before we can test
     114    // of an object.  We need to model this distribution for the PSF stars before we can test
    109115    // the significance for a specific object
    110116    // XXX move this to the code that generates the PSF?
     
    122128    psphotVisualShowSourceSize (readout, sources);
    123129    psphotVisualPlotApResid (sources, options.ApResid, options.ApSysErr);
    124 
    125     return true;
    126 }
    127 
    128 bool psphotMaskCosmicRay (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
     130    psphotVisualShowSatStars (recipe, psf, sources);
     131
     132    return true;
     133}
     134
     135// model the apmifit distribution for the psf stars:
     136bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf) {
     137
     138    // select stats from the psf stars
     139    psVector *Ap = psVectorAllocEmpty (100, PS_TYPE_F32);
     140    psVector *ApErr = psVectorAllocEmpty (100, PS_TYPE_F32);
     141
     142    psImageMaskType maskVal = options->maskVal | options->markVal;
     143
     144    // XXX  why PHOT_WEIGHT??
     145    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
     146
     147    for (int i = 0; i < sources->n; i++) {
     148        pmSource *source = sources->data[i];
     149        if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;
     150
     151        // replace object in image
     152        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
     153            pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
     154        }
     155
     156        // clear the mask bit and set the circular mask pixels
     157        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     158        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
     159
     160        // XXX can we test if psfMag is set and calculate only if needed?
     161        pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
     162
     163        // clear the mask bit
     164        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     165
     166        // re-subtract the object, leave local sky
     167        pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
     168
     169        float apMag = -2.5*log10(source->moments->Sum);
     170        float dMag = source->psfMag - apMag;
     171
     172        psVectorAppend (Ap, 100, dMag);
     173        psVectorAppend (ApErr, 100, source->errMag);
     174    }
     175
     176    // model the distribution as a mean or median value and a systematic error from that value:
     177    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
     178    psVectorStats (stats, Ap, NULL, NULL, 0);
     179
     180    psVector *dAp = psVectorAlloc (Ap->n, PS_TYPE_F32);
     181    for (int i = 0; i < Ap->n; i++) {
     182        dAp->data.F32[i] = Ap->data.F32[i] - stats->robustMedian;
     183    }
     184
     185    options->ApResid = stats->robustMedian;
     186    options->ApSysErr = psVectorSystematicError(dAp, ApErr, 0.05);
     187    psLogMsg ("psphot", PS_LOG_DETAIL, "psf - Sum: %f +/- %f\n", options->ApResid, options->ApSysErr);
     188
     189    psFree (Ap);
     190    psFree (ApErr);
     191    psFree (stats);
     192    psFree (dAp);
     193
     194    return true;
     195}
     196
     197// classify sources based on the combination of psf-mag, Mxx, Myy
     198bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
     199
     200    bool status;
     201    pmPSFClump psfClump;
     202    char regionName[64];
     203
     204    psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4s %4s %4s %4s %4s %4s", "Npsf", "Next", "Nsat", "Ncr", "Nmiss", "Nskip");
     205
     206    int nRegions = psMetadataLookupS32 (&status, readout->analysis, "PSF.CLUMP.NREGIONS");
     207    for (int i = 0; i < nRegions; i ++) {
     208        snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
     209        psMetadata *regionMD = psMetadataLookupPtr (&status, readout->analysis, regionName);
     210        psAssert (regionMD, "regions must be defined by earlier call to psphotRoughClassRegion");
     211
     212        psRegion *region = psMetadataLookupPtr (&status, regionMD, "REGION");
     213        psAssert (region, "regions must be defined by earlier call to psphotRoughClassRegion");
     214
     215        // pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
     216        psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   psAssert (status, "missing PSF.CLUMP.X");
     217        psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   psAssert (status, "missing PSF.CLUMP.Y");
     218        psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  psAssert (status, "missing PSF.CLUMP.DX");
     219        psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  psAssert (status, "missing PSF.CLUMP.DY");
     220
     221        if ((psfClump.X < 0) || (psfClump.Y < 0) || !psfClump.X || !psfClump.Y || isnan(psfClump.X) || isnan(psfClump.Y)) {
     222            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);
     223            continue;
     224        }
     225
     226        if (!psphotSourceClassRegion (region, &psfClump, sources, recipe, psf, options)) {
     227            psLogMsg ("psphot", 4, "Failed to determine source classification for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);
     228            continue;
     229        }
     230    }
     231
     232    return true;
     233}
     234
     235bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
     236
     237    PS_ASSERT_PTR_NON_NULL(sources, false);
     238    PS_ASSERT_PTR_NON_NULL(recipe, false);
     239
     240    int Nsat  = 0;
     241    int Next  = 0;
     242    int Npsf  = 0;
     243    int Ncr   = 0;
     244    int Nmiss = 0;
     245    int Nskip = 0;
     246
     247    pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;
     248    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
     249
     250    psImageMaskType maskVal = options->maskVal | options->markVal;
     251
     252    for (psS32 i = 0 ; i < sources->n ; i++) {
     253
     254        pmSource *source = (pmSource *) sources->data[i];
     255
     256        // psfClumps are found for image subregions:
     257        // skip sources not in this region
     258        if (source->peak->x <  region->x0) continue;
     259        if (source->peak->x >= region->x1) continue;
     260        if (source->peak->y <  region->y0) continue;
     261        if (source->peak->y >= region->y1) continue;
     262
     263        // skip source if it was already measured
     264        if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
     265            psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
     266            continue;
     267        }
     268
     269        // source must have been subtracted
     270        if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
     271            source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
     272            psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");
     273            continue;
     274        }
     275
     276        // we are basically classifying by moments; use the default if not found
     277        psAssert (source->moments, "why is this source missing moments?");
     278        if (source->mode & noMoments) {
     279            Nskip ++;
     280            continue;
     281        }
     282
     283        psF32 Mxx = source->moments->Mxx;
     284        psF32 Myy = source->moments->Myy;
     285
     286        // replace object in image
     287        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
     288            pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
     289        }
     290
     291        // clear the mask bit and set the circular mask pixels
     292        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     293        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
     294
     295        // XXX can we test if psfMag is set and calculate only if needed?
     296        pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
     297
     298        // clear the mask bit
     299        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
     300
     301        // re-subtract the object, leave local sky
     302        pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
     303
     304        float apMag = -2.5*log10(source->moments->Sum);
     305        float dMag = source->psfMag - apMag;
     306        float nSigma = (dMag - options->ApResid) / hypot(source->errMag, options->ApSysErr);
     307
     308        source->extNsigma = nSigma;
     309        source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
     310
     311        // Anything within this region is a probably PSF-like object. Saturated stars may land
     312        // in this region, but are detected elsewhere on the basis of their peak value.
     313        bool isPSF = ((fabs(nSigma) < options->nSigmaApResid) &&
     314                      (fabs(Mxx - psfClump->X) < options->nSigmaMoments*psfClump->dX) &&
     315                      (fabs(Myy - psfClump->Y) < options->nSigmaMoments*psfClump->dY));
     316        if (isPSF) {
     317          psTrace("psphot.czw",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g PSF\t%g %g\n",
     318                  source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigma,
     319                  options->nSigmaApResid,options->nSigmaMoments);
     320          Npsf ++;
     321          continue;
     322        }
     323
     324        // Defects may not always match CRs from peak curvature analysis
     325        // Defects may also be marked as SATSTAR -- XXX deactivate this flag?
     326        // XXX this rule is not great
     327        if ((Mxx < psfClump->X) || (Myy < psfClump->Y)) {
     328
     329          psTrace("psphot.czw",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g CR\t%g %g\n",
     330                  source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigma,
     331                  options->nSigmaApResid,options->nSigmaMoments);
     332            source->mode |= PM_SOURCE_MODE_DEFECT;
     333            Ncr ++;
     334            continue;
     335        }
     336
     337        // saturated star (determined in PSF fit).  These may also be saturated galaxies, or
     338        // just large saturated regions.
     339        if (source->mode & PM_SOURCE_MODE_SATSTAR) {
     340          psTrace("psphot.czw",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g SAT\t%g %g\n",
     341                  source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigma,
     342                  options->nSigmaApResid,options->nSigmaMoments);
     343         
     344            Nsat ++;
     345            continue;
     346        }
     347
     348        // XXX allow the Mxx, Myy to be less than psfClump->X,Y (by some nSigma)?
     349        bool isEXT = (nSigma > options->nSigmaApResid) || ((Mxx > psfClump->X) && (Myy > psfClump->Y));
     350        if (isEXT) {
     351          psTrace("psphot.czw",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g Ext\t%g %g\n",
     352                  source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigma,
     353                  options->nSigmaApResid,options->nSigmaMoments);
     354         
     355            source->mode |= PM_SOURCE_MODE_EXT_LIMIT;
     356            Next ++;
     357            continue;
     358        }
     359        psTrace("psphot.czw",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g Unk\t%g %g\n",
     360                source->peak->xf,source->peak->yf,Mxx,Myy,psfClump->X,psfClump->Y,psfClump->dX,psfClump->dY,apMag,dMag,nSigma,
     361                options->nSigmaApResid,options->nSigmaMoments);
     362       
     363        psWarning ("sourse size was missed for %f,%f : %f %f -- %f\n", source->peak->xf, source->peak->yf, Mxx, Myy, nSigma);
     364        Nmiss ++;
     365    }
     366
     367    psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4d %4d %4d %4d %4d %4d", Npsf, Next, Nsat, Ncr, Nmiss, Nskip);
     368
     369    return true;
     370}
     371
     372// given an object suspected to be a defect, generate a pixel mask using the Lapacian transform
     373// if enough of the object is detected as 'sharp', consider the object a cosmic ray
     374bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) {
     375
     376    for (int i = 0; i < sources->n; i++) {
     377        pmSource *source = sources->data[i];
     378
     379        // Integer position of peak
     380        int xPeak = source->peak->xf - source->pixels->col0 + 0.5;
     381        int yPeak = source->peak->yf - source->pixels->row0 + 0.5;
     382
     383        // Skip sources which are too close to a boundary.  These are mostly caught as DEFECT
     384        if (xPeak < 1 || xPeak > source->pixels->numCols - 2 ||
     385            yPeak < 1 || yPeak > source->pixels->numRows - 2) {
     386            psTrace("psphot", 7, "Not calculating crNsigma due to edge\n");
     387            //      psTrace("psphot.czw", 2, "Not calculating crNsigma due to edge\n");
     388            continue;
     389        }
     390       
     391        // this source is thought to be a cosmic ray.  flag the detection and mask the pixels
     392        if (source->mode & PM_SOURCE_MODE_DEFECT) {
     393            // XXX this is running slowly and is too agressive, but it more-or-less works
     394            // psphotMaskCosmicRayCZW(readout, source, options->crMask);
     395           
     396            // XXX these are the old versions which we are not using
     397            // psphotMaskCosmicRay (readout->mask, source, maskVal, crMask);
     398            // psphotMaskCosmicRay_Old (source, maskVal, crMask);
     399        }
     400    }
     401   
     402    // now that we have masked pixels associated with CRs, we can grow the mask
     403    if (options->grow > 0) {
     404        bool oldThreads = psImageConvolveSetThreads(true); // Old value of threading for psImageConvolveMask
     405        psImage *newMask = psImageConvolveMask(NULL, readout->mask, options->crMask, options->crMask, -options->grow, options->grow, -options->grow, options->grow);
     406        psImageConvolveSetThreads(oldThreads);
     407        if (!newMask) {
     408            psError(PS_ERR_UNKNOWN, false, "Unable to grow CR mask");
     409            return false;
     410        }
     411        psFree(readout->mask);
     412        readout->mask = newMask;
     413    }
     414
     415    // XXX test : save the mask image
     416    if (0) {
     417        psphotSaveImage (NULL, readout->mask,   "mask.fits");
     418    }
     419    return true;
     420}
     421
     422// Comments by CZW 20091209 : Mechanics of how to identify CR pixels taken from "Cosmic-Ray
     423// Rejection by Laplacian Edge Detection" by Pieter van Dokkum, arXiv:astro-ph/0108003.  This
     424// does no repair or recovery of the CR pixels, it only masks them out.  My test code can be
     425// found at /data/ipp031.0/watersc1/psphot.20091209/algo_check.c
     426bool psphotMaskCosmicRay (pmReadout *readout, pmSource *source, psImageMaskType maskVal) {
     427
     428    // Get the actual images and information about the peak.
     429    psImage *mask = readout->mask;
     430    pmPeak *peak = source->peak;
     431    pmFootprint *footprint = peak->footprint;
     432
     433    int xm = footprint->bbox.x0;
     434    int xM = footprint->bbox.x1;
     435    int ym = footprint->bbox.y0;
     436    int yM = footprint->bbox.y1;
     437
     438    if (xm < 0) { xm = 0; }
     439    if (ym < 0) { ym = 0; }
     440    if (xM > mask->numCols) { xM = mask->numCols; }
     441    if (yM > mask->numRows) { yM = mask->numRows; }
     442    int dx = xM - xm;
     443    int dy = yM - ym;
     444
     445    // Bounding boxes are inclusive of final pixel
     446    // XXX ensure dx,dy do not become too large here
     447    dx++;
     448    dy++;
     449
     450    psImage *image= readout->image;
     451    psImage *variance = readout->variance;
     452     
     453    int binning = 1;
     454    float sigma_thresh = 2.0;
     455    int iteration = 0;
     456    int max_iter = 5;
     457    float noise_factor = 5.0 / 4.0;  // Intrinsic to the Laplacian making noise spikes spikier.
     458
     459    // Temporary images.
     460    psImage *mypix  = psImageAlloc(dx,dy,image->type.type);
     461    psImage *myvar  = psImageAlloc(dx,dy,image->type.type);
     462    psImage *binned = psImageAlloc(dx * binning,dy * binning,image->type.type);
     463    psImage *conved = psImageAlloc(dx * binning,dy * binning,image->type.type);
     464    psImage *edges  = psImageAlloc(dx,dy,image->type.type);
     465    psImage *mymask = psImageAlloc(dx,dy,PS_TYPE_IMAGE_MASK);
     466     
     467    int x,y;
     468    // Load my copy of things.
     469    for (x = 0; x < dx; x++) {
     470        for (y = 0; y < dy; y++) {
     471            psImageSet(mypix,x,y,psImageGet(image,x+xm,y+ym));
     472            psImageSet(myvar,x,y,psImageGet(variance,x+xm,y+ym));
     473            mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = 0x00;
     474        }
     475    }
     476    // Mask so I can see on the output image where the footprint is.
     477    for (int i = 0; i < footprint->spans->n; i++) {
     478        pmSpan *sp = footprint->spans->data[i];
     479        for (int j = sp->x0; j <= sp->x1; j++) {
     480            y = sp->y - ym;
     481            x = j - xm;
     482            mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= 0x01;
     483        }
     484    }
     485
     486    int CRpix_count = 0;     
     487    do {
     488        CRpix_count = 0;
     489        // Zero out my temp images.
     490        for (x = 0; x < binning * dx; x++) {
     491            for (y = 0; y < binning * dy; y++) {
     492                psImageSet(binned,x,y,0.0);
     493                psImageSet(conved,x,y,0.0);
     494                psImageSet(edges,x/binning,y/ binning,0.0);
     495            }
     496        }     
     497        // Make subsampled image. Maybe this should be called "unbinned" or something
     498        for (x = 0; x < binning * dx; x++) {
     499            for (y = 0; y < binning * dy; y++) {
     500                psImageSet(binned,x,y,psImageGet(mypix,x / binning,y / binning));
     501            }
     502        }
     503        // Apply Laplace transform (kernel = [[0 -0.25 0][-0.25 1 -0.25][0 -0.25 0]]), clipping at zero
     504        for (x = 1; x < dx - 1; x++) {
     505            for (y = 1; y < dy - 1; y++) {
     506                psImageSet(conved,x,y,psImageGet(binned,x,y) - 0.25 *
     507                           (psImageGet(binned,x-1,y) + psImageGet(binned,x+1,y) +
     508                            psImageGet(binned,x,y-1) + psImageGet(binned,x,y+1)));
     509                if (psImageGet(conved,x,y) < 0.0) {
     510                    psImageSet(conved,x,y,0.0);
     511                }
     512            }
     513        }
     514        // Create an edge map by rebinning
     515        for (x = 0; x < binning * dx; x++) {
     516            for (y = 0; y < binning * dy; y++) {
     517                psImageSet(edges,x / binning, y / binning,
     518                           psImageGet(edges, x / binning, y / binning) +
     519                           psImageGet(conved,x,y));
     520            }
     521        }
     522        // Modify my mask if we're above the significance threshold
     523        for (x = 0; x < dx; x++) {
     524            for (y = 0; y < dy; y++) {
     525                if ( psImageGet(edges,x,y) / (binning * sqrt(noise_factor * psImageGet(myvar,x,y))) > sigma_thresh ) {
     526                    if (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & 0x01) {
     527                        mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= 0x40;
     528                        CRpix_count++;
     529                    }
     530                }
     531            }
     532        }
     533
     534        // "Repair" Masked pixels for the next round.
     535        for (x = 1; x < dx - 1; x++) {
     536            for (y = 1; y < dy - 1; y++) {
     537                if (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & 0x40) {
     538                    psImageSet(mypix,x,y,0.25 *
     539                               (psImageGet(mypix,x-1,y) + psImageGet(mypix,x+1,y) +
     540                                psImageGet(mypix,x,y-1) + psImageGet(mypix,x,y+1)));
     541                }
     542            }
     543        }
     544       
     545# if 0
     546        if ((psTraceGetLevel("psphot.czw") >= 2)&&(abs(xm - 2770) < 5)&&(abs(ym - 2581) < 5)&&(iteration == 0)) {
     547          psTrace("psphot.czw",2,"TRACEMOTRON %d %d %d %d %d\n",xm,ym,dx,dy,iteration);
     548          psphotSaveImage (NULL, mypix,   "czw.pix.fits");
     549          psphotSaveImage (NULL, myvar,   "czw.var.fits");
     550          psphotSaveImage (NULL, binned,  "czw.binn.fits");
     551          psphotSaveImage (NULL, conved,  "czw.conv.fits");
     552          psphotSaveImage (NULL, edges,   "czw.edge.fits");
     553          psphotSaveImage (NULL, mymask,  "czw.mask.fits");
     554        }
     555# endif
     556
     557        psTrace("psphot.czw",2,"Iter: %d Count: %d",iteration,CRpix_count);
     558        iteration++;
     559    } while ((iteration < max_iter)&&(CRpix_count > 0));
     560
     561    // A solitary masked pixel is likely a lie. Remove those.
     562    for (x = 0; x < dx; x++) {
     563        for (y = 0; y < dy; y++) {
     564            if (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & 0x40) {
     565                if ((x-1 >= 0)&&(mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x-1] & 0x40)) {
     566                    continue;
     567                }
     568                if ((y-1 >= 0)&&(mymask->data.PS_TYPE_IMAGE_MASK_DATA[y-1][x] & 0x40)) {
     569                    continue;
     570                }
     571                if ((x+1 < dx)&&(mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x+1] & 0x40)) {
     572                    continue;
     573                }
     574                if ((y+1 < dy)&&(mymask->data.PS_TYPE_IMAGE_MASK_DATA[y+1][x] & 0x40)) {
     575                    continue;
     576                }
     577                mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] ^= 0x40;
     578            }
     579        }
     580    }
     581
     582    // transfer temporary mask to real mask
     583    for (x = 0; x < dx; x++) {
     584        for (y = 0; y < dy; y++) {
     585            if (mymask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & 0x40) {
     586                mask->data.PS_TYPE_IMAGE_MASK_DATA[y+ym+mask->row0][x+xm+mask->col0] |= maskVal;
     587            }
     588        }
     589    }
     590     
     591    // XXX if we decide this REALLY is a cosmic ray, set the CR_LIMIT bit
     592    source->mode |= PM_SOURCE_MODE_CR_LIMIT;
     593
     594    psFree(mypix);
     595    psFree(myvar);
     596    psFree(binned);
     597    psFree(conved);
     598    psFree(edges);
     599    psFree(mymask);
     600     
     601    return true;
     602}
     603
     604bool psphotMaskCosmicRayFootprintCheck (psArray *sources) {
     605
     606    for (int i = 0; i < sources->n; i++) {
     607        pmSource *source = sources->data[i];
     608        pmPeak *peak = source->peak;
     609        pmFootprint *footprint = peak->footprint;
     610        if (!footprint) continue;
     611        for (int j = 0; j < footprint->spans->n; j++) {
     612            pmSpan *sp = footprint->spans->data[j];
     613            psAssert (sp, "missing span");
     614        }
     615    }
     616    return true;
     617}
     618
     619/**** ------ old versions of cosmic ray masking ----- ****/
     620
     621// This attempt to mask the cosmic rays used the isophotal boundary
     622bool psphotMaskCosmicRay_V1 (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
    129623
    130624    // replace the source flux
     
    139633    pmFootprint *footprint = peak->footprint;
    140634    if (!footprint) {
     635      psTrace("psphot.czw",2,"Using isophot CR mask code.");
     636     
    141637        // if we have not footprint, use the old code to mask by isophot
    142638        psphotMaskCosmicRayIsophot (source, maskVal, crMask);
     
    145641
    146642    if (!footprint->spans) {
     643      psTrace("psphot.czw",2,"Using isophot CR mask code.");
     644     
    147645        // if we have no footprint, use the old code to mask by isophot
    148646        psphotMaskCosmicRayIsophot (source, maskVal, crMask);
    149647        return true;
    150648    }
    151 
     649    psphotMaskCosmicRayIsophot (source, maskVal, crMask);
    152650    // mask all of the pixels covered by the spans of the footprint
    153     for (int j = 1; j < footprint->spans->n; j++) {
    154         pmSpan *span1 = footprint->spans->data[j];
    155 
    156         int iy = span1->y;
    157         int xs = span1->x0;
    158         int xe = span1->x1;
    159 
    160         for (int ix = xs; ix < xe; ix++) {
    161             mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
    162         }
    163     }
     651    for (int j = 1; j < footprint->spans->n; j++) { 
     652        pmSpan *span1 = footprint->spans->data[j];
     653
     654        int iy = span1->y;
     655        int xs = span1->x0;
     656        int xe = span1->x1;
     657
     658        for (int ix = xs; ix < xe; ix++) {
     659            mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
     660        }
     661    } 
    164662    return true;
    165663}
     
    228726        }
    229727    }
    230     return true;
    231 }
    232 
    233 // model the apmifit distribution for the psf stars:
    234 bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf) {
    235 
    236     // select stats from the psf stars
    237     psVector *Ap = psVectorAllocEmpty (100, PS_TYPE_F32);
    238     psVector *ApErr = psVectorAllocEmpty (100, PS_TYPE_F32);
    239 
    240     psImageMaskType maskVal = options->maskVal | options->markVal;
    241 
    242     // XXX  why PHOT_WEIGHT??
    243     pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
    244 
    245     for (int i = 0; i < sources->n; i++) {
    246         pmSource *source = sources->data[i];
    247         if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;
    248 
    249         // replace object in image
    250         if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
    251             pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
    252         }
    253 
    254         // clear the mask bit and set the circular mask pixels
    255         psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
    256         psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
    257 
    258         // XXX can we test if psfMag is set and calculate only if needed?
    259         pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
    260 
    261         // clear the mask bit
    262         psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
    263 
    264         // re-subtract the object, leave local sky
    265         pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
    266 
    267         float apMag = -2.5*log10(source->moments->Sum);
    268         float dMag = source->psfMag - apMag;
    269 
    270         psVectorAppend (Ap, dMag);
    271         psVectorAppend (ApErr, source->errMag);
    272     }
    273 
    274     // model the distribution as a mean or median value and a systematic error from that value:
    275     psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    276     psVectorStats (stats, Ap, NULL, NULL, 0);
    277 
    278     psVector *dAp = psVectorAlloc (Ap->n, PS_TYPE_F32);
    279     for (int i = 0; i < Ap->n; i++) {
    280         dAp->data.F32[i] = Ap->data.F32[i] - stats->robustMedian;
    281     }
    282 
    283     options->ApResid = stats->robustMedian;
    284     options->ApSysErr = psVectorSystematicError(dAp, ApErr, 0.05);
    285     // XXX this is quite arbitrary...
    286     if (!isfinite(options->ApSysErr)) options->ApSysErr = 0.01;
    287     psLogMsg ("psphot", PS_LOG_DETAIL, "psf - Sum: %f +/- %f\n", options->ApResid, options->ApSysErr);
    288 
    289     psFree (Ap);
    290     psFree (ApErr);
    291     psFree (stats);
    292     psFree (dAp);
    293 
    294     return true;
    295 }
    296 
    297 // classify sources based on the combination of psf-mag, Mxx, Myy
    298 bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
    299 
    300     bool status;
    301     pmPSFClump psfClump;
    302     char regionName[64];
    303 
    304     psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4s %4s %4s %4s %4s %4s", "Npsf", "Next", "Nsat", "Ncr", "Nmiss", "Nskip");
    305 
    306     int nRegions = psMetadataLookupS32 (&status, readout->analysis, "PSF.CLUMP.NREGIONS");
    307     for (int i = 0; i < nRegions; i ++) {
    308         snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
    309         psMetadata *regionMD = psMetadataLookupPtr (&status, readout->analysis, regionName);
    310         psAssert (regionMD, "regions must be defined by earlier call to psphotRoughClassRegion");
    311 
    312         psRegion *region = psMetadataLookupPtr (&status, regionMD, "REGION");
    313         psAssert (region, "regions must be defined by earlier call to psphotRoughClassRegion");
    314 
    315         // pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
    316         psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   psAssert (status, "missing PSF.CLUMP.X");
    317         psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   psAssert (status, "missing PSF.CLUMP.Y");
    318         psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  psAssert (status, "missing PSF.CLUMP.DX");
    319         psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  psAssert (status, "missing PSF.CLUMP.DY");
    320 
    321         if ((psfClump.X < 0) || (psfClump.Y < 0) || !psfClump.X || !psfClump.Y || isnan(psfClump.X) || isnan(psfClump.Y)) {
    322             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);
    323             continue;
    324         }
    325 
    326         if (!psphotSourceClassRegion (region, &psfClump, sources, recipe, psf, options)) {
    327             psLogMsg ("psphot", 4, "Failed to determine source classification for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);
    328             continue;
    329         }
    330     }
    331 
    332     return true;
    333 }
    334 
    335 bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {
    336 
    337     PS_ASSERT_PTR_NON_NULL(sources, false);
    338     PS_ASSERT_PTR_NON_NULL(recipe, false);
    339 
    340     int Nsat  = 0;
    341     int Next  = 0;
    342     int Npsf  = 0;
    343     int Ncr   = 0;
    344     int Nmiss = 0;
    345     int Nskip = 0;
    346 
    347     pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;
    348     pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;
    349 
    350     psImageMaskType maskVal = options->maskVal | options->markVal;
    351 
    352     for (psS32 i = 0 ; i < sources->n ; i++) {
    353 
    354         pmSource *source = (pmSource *) sources->data[i];
    355 
    356         // psfClumps are found for image subregions:
    357         // skip sources not in this region
    358         if (source->peak->x <  region->x0) continue;
    359         if (source->peak->x >= region->x1) continue;
    360         if (source->peak->y <  region->y0) continue;
    361         if (source->peak->y >= region->y1) continue;
    362 
    363         // skip source if it was already measured
    364         if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
    365             psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
    366             continue;
    367         }
    368 
    369         // source must have been subtracted
    370         if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
    371             source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
    372             psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");
    373             continue;
    374         }
    375 
    376         // we are basically classifying by moments; use the default if not found
    377         psAssert (source->moments, "why is this source missing moments?");
    378         if (source->mode & noMoments) {
    379             Nskip ++;
    380             continue;
    381         }
    382 
    383         psF32 Mxx = source->moments->Mxx;
    384         psF32 Myy = source->moments->Myy;
    385 
    386         // replace object in image
    387         if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
    388             pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);
    389         }
    390 
    391         // clear the mask bit and set the circular mask pixels
    392         psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
    393         psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);
    394 
    395         // XXX can we test if psfMag is set and calculate only if needed?
    396         pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
    397 
    398         // clear the mask bit
    399         psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));
    400 
    401         // re-subtract the object, leave local sky
    402         pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
    403 
    404         float apMag = -2.5*log10(source->moments->Sum);
    405         float dMag = source->psfMag - apMag;
    406         float nSigma = (dMag - options->ApResid) / hypot(source->errMag, options->ApSysErr);
    407 
    408         source->extNsigma = nSigma;
    409         source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
    410 
    411         // Anything within this region is a probably PSF-like object. Saturated stars may land
    412         // in this region, but are detected elsewhere on the basis of their peak value.
    413         bool isPSF = (fabs(nSigma) < options->nSigmaApResid) && (fabs(Mxx - psfClump->X) < options->nSigmaMoments*psfClump->dX) && (fabs(Myy - psfClump->Y) < options->nSigmaMoments*psfClump->dY);
    414         if (isPSF) {
    415             Npsf ++;
    416             continue;
    417         }
    418 
    419         // Defects may not always match CRs from peak curvature analysis
    420         // Defects may also be marked as SATSTAR -- XXX deactivate this flag?
    421         // XXX this rule is not great
    422         if ((Mxx < psfClump->X) || (Myy < psfClump->Y)) {
    423             source->mode |= PM_SOURCE_MODE_DEFECT;
    424             Ncr ++;
    425             continue;
    426         }
    427 
    428         // saturated star (determined in PSF fit).  These may also be saturated galaxies, or
    429         // just large saturated regions.
    430         if (source->mode & PM_SOURCE_MODE_SATSTAR) {
    431             Nsat ++;
    432             continue;
    433         }
    434 
    435         // XXX allow the Mxx, Myy to be less than psfClump->X,Y (by some nSigma)?
    436         bool isEXT = (nSigma > options->nSigmaApResid) || ((Mxx > psfClump->X) && (Myy > psfClump->Y));
    437         if (isEXT) {
    438             source->mode |= PM_SOURCE_MODE_EXT_LIMIT;
    439             Next ++;
    440             continue;
    441         }
    442 
    443         psWarning ("sourse size was missed for %f,%f : %f %f -- %f\n", source->peak->xf, source->peak->yf, Mxx, Myy, nSigma);
    444         Nmiss ++;
    445     }
    446 
    447     psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4d %4d %4d %4d %4d %4d", Npsf, Next, Nsat, Ncr, Nmiss, Nskip);
    448 
    449728    return true;
    450729}
     
    524803}
    525804
    526 bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) {
     805// this was an old attempt to identify cosmic rays based on the peak curvature
     806bool psphotSourcePeakCurvature (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) {
    527807
    528808    // classify the sources based on the CR test (place this in a function?)
     
    656936    return true;
    657937}
     938
Note: See TracChangeset for help on using the changeset viewer.