- Timestamp:
- May 3, 2010, 8:50:52 AM (16 years ago)
- Location:
- branches/simtest_nebulous_branches
- Files:
-
- 4 edited
-
. (modified) (1 prop)
-
psphot (modified) (1 prop)
-
psphot/src (modified) (1 prop)
-
psphot/src/psphotSourceSize.c (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/simtest_nebulous_branches
- Property svn:mergeinfo changed
-
branches/simtest_nebulous_branches/psphot
-
Property svn:mergeinfo
set to (toggle deleted branches)
/trunk/psphot merged eligible /branches/eam_branches/stackphot.20100406/psphot 27622-27655 /branches/pap_delete/psphot 27530-27595
-
Property svn:mergeinfo
set to (toggle deleted branches)
-
branches/simtest_nebulous_branches/psphot/src
- Property svn:ignore
-
old new 18 18 psphotVersionDefinitions.h 19 19 psphotMomentsStudy 20 psphotPetrosianStudy 21 psphotForced 22 psphotMakePSF 23 psphotStack
-
- Property svn:ignore
-
branches/simtest_nebulous_branches/psphot/src/psphotSourceSize.c
r21519 r27840 2 2 # include <gsl/gsl_sf_gamma.h> 3 3 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); 4 typedef 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: 20 bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf); 21 bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options); 22 bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options); 23 bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options); 24 bool psphotMaskCosmicRay (pmReadout *readout, pmSource *source, psImageMaskType maskVal); 25 bool psphotMaskCosmicRayFootprintCheck (psArray *sources); 26 int psphotMaskCosmicRayConnected (int xPeak, int yPeak, psImage *mymask, psImage *myvar, psImage *edges, int binning, float sigma_thresh); 9 27 10 28 // we need to call this function after sources have been fitted to the PSF model and … … 14 32 // deviation from the psf model at the r = FWHM/2 position 15 33 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 35 bool 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 62 bool psphotSourceSizeReadout(pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe, bool getPSFsize) 17 63 { 18 64 bool status; 65 psphotSourceSizeOptions options; 19 66 20 67 psTimerStart ("psphot.size"); 21 68 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 22 90 // 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); 25 96 26 97 // bit to mask the cosmic-ray pixels 27 psImageMaskTypecrMask = pmConfigMaskGet("CR", config); // Mask value for cosmic rays28 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"); 30 101 assert (status); 31 102 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"); 33 105 assert (status); 34 106 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) { 37 118 psError(PS_ERR_BAD_PARAMETER_VALUE, true, "PSPHOT.CR.GROW is not positive."); 38 119 return false; 39 120 } 40 121 41 floatsoft = psMetadataLookupF32(&status, recipe, "PSPHOT.CR.NSIGMA.SOFTEN"); // Softening parameter42 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) { 43 124 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: 164 bool 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++) { 49 177 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 236 bool 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 275 bool 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; 50 302 51 303 // skip source if it was already measured 52 if ( isfinite(source->crNsigma)) {53 psTrace("psphot", 7, "Not calculating extNsigma,crNsigma since alreadymeasured\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"); 54 306 continue; 55 307 } … … 57 309 // source must have been subtracted 58 310 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 74 417 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 439 bool 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; 76 462 77 463 // Integer position of peak … … 79 465 int yPeak = source->peak->yf - source->pixels->row0 + 0.5; 80 466 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 83 468 if (xPeak < 1 || xPeak > source->pixels->numCols - 2 || 84 469 yPeak < 1 || yPeak > source->pixels->numRows - 2) { 85 source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;86 470 psTrace("psphot", 7, "Not calculating crNsigma due to edge\n"); 87 471 continue; 88 472 } 89 473 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); 170 494 } 171 495 172 496 // now that we have masked pixels associated with CRs, we can grow the mask 173 if ( grow > 0) {497 if (options->grow > 0) { 174 498 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); 176 500 psImageConvolveSetThreads(oldThreads); 177 501 if (!newMask) { … … 183 507 } 184 508 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 527 bool 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 717 bool 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 734 bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask); 735 736 // This attempt to mask the cosmic rays used the isophotal boundary 737 bool 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 781 int 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 923 bool 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 } 191 986 return true; 192 987 } … … 194 989 // given the PSF ellipse parameters, navigate around the 1sigma contour, return the total 195 990 // 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. 993 float psphotModelContour(const psImage *image, const psImage *variance, const psImage *mask, 994 psImageMaskType maskVal, const pmModel *model, float Ro) 199 995 { 200 996 psF32 *PAR = model->params->data.F32; // Model parameters … … 265 1061 } 266 1062 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 1064 bool 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; 303 1193 } 304 1194 return true; 305 1195 } 306 1196 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 variable318 # define SN_LIMIT 5.0319 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 right324 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 neighbor338 // 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 right341 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 right357 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.
