Changeset 27838 for branches/tap_branches/psphot/src/psphotSourceSize.c
- Timestamp:
- May 3, 2010, 8:41:49 AM (16 years ago)
- Location:
- branches/tap_branches
- Files:
-
- 4 edited
-
. (modified) (1 prop)
-
psphot (modified) (1 prop)
-
psphot/src (modified) (1 prop)
-
psphot/src/psphotSourceSize.c (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/tap_branches
- Property svn:mergeinfo changed
-
branches/tap_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/tap_branches/psphot/src
- Property svn:ignore
-
old new 19 19 psphotMomentsStudy 20 20 psphotPetrosianStudy 21 psphotForced 22 psphotMakePSF 23 psphotStack
-
- Property svn:ignore
-
branches/tap_branches/psphot/src/psphotSourceSize.c
r25852 r27838 13 13 float soft; 14 14 int grow; 15 int xtest, ytest; 16 bool apply; // apply CR mask? 15 17 } psphotSourceSizeOptions; 16 18 19 // local functions: 17 20 bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf); 18 21 bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options); 19 22 bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options); 20 23 bool 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); 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); 23 27 24 28 // we need to call this function after sources have been fitted to the PSF model and … … 28 32 // deviation from the psf model at the r = FWHM/2 position 29 33 30 // XXX use an internal flag to mark sources which have already been measured 31 bool psphotSourceSize(pmConfig *config, pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, 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) 32 63 { 33 64 bool status; … … 36 67 psTimerStart ("psphot.size"); 37 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 38 90 // user-defined masks to test for good/bad pixels (build from recipe list if not yet set) 39 91 options.maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels … … 55 107 // XXX recipe name is not great 56 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"); 57 114 assert (status); 58 115 … … 69 126 } 70 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 71 134 // We are using the value psfMag - 2.5*log10(moment.Sum) as a measure of the extendedness 72 // of an dobject. We need to model this distribution for the PSF stars before we can test135 // of an object. We need to model this distribution for the PSF stars before we can test 73 136 // the significance for a specific object 74 137 // XXX move this to the code that generates the PSF? 75 138 // XXX store the results on pmPSF? 76 psphotSourceSizePSF (&options, sources, psf); 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 } 77 144 78 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) 79 147 psphotSourceClass(readout, sources, recipe, psf, &options); 80 148 149 // NOTE: only sources not already measured !(source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) 81 150 psphotSourceSizeCR (readout, sources, &options); 82 151 83 psLogMsg ("psphot.size", PS_LOG_INFO, "measure source sizes for %ld sources: %f sec\n", sources->n - first, psTimerMark ("psphot.size")); 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")); 84 154 85 155 psphotVisualPlotSourceSize (recipe, readout->analysis, sources); 86 156 psphotVisualShowSourceSize (readout, sources); 87 157 psphotVisualPlotApResid (sources, options.ApResid, options.ApSysErr); 158 psphotVisualShowSatStars (recipe, psf, sources); 88 159 89 160 return true; 90 161 } 91 162 92 bool psphotMaskCosmicRay (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) { 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++) { 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; 302 303 // skip source if it was already measured 304 if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) { 305 psTrace("psphot", 7, "Not calculating source size since it has already been measured\n"); 306 continue; 307 } 308 309 // source must have been subtracted 310 if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) { 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 417 source->mode |= PM_SOURCE_MODE_EXT_LIMIT; 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; 462 463 // Integer position of peak 464 int xPeak = source->peak->xf - source->pixels->col0 + 0.5; 465 int yPeak = source->peak->yf - source->pixels->row0 + 0.5; 466 467 // Skip sources which are too close to a boundary. These are mostly caught as DEFECT 468 if (xPeak < 1 || xPeak > source->pixels->numCols - 2 || 469 yPeak < 1 || yPeak > source->pixels->numRows - 2) { 470 psTrace("psphot", 7, "Not calculating crNsigma due to edge\n"); 471 continue; 472 } 473 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); 494 } 495 496 // now that we have masked pixels associated with CRs, we can grow the mask 497 if (options->grow > 0) { 498 bool oldThreads = psImageConvolveSetThreads(true); // Old value of threading for psImageConvolveMask 499 psImage *newMask = psImageConvolveMask(NULL, readout->mask, options->crMask, options->crMask, -options->grow, options->grow, -options->grow, options->grow); 500 psImageConvolveSetThreads(oldThreads); 501 if (!newMask) { 502 psError(PS_ERR_UNKNOWN, false, "Unable to grow CR mask"); 503 return false; 504 } 505 psFree(readout->mask); 506 readout->mask = newMask; 507 } 508 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) { 93 738 94 739 // replace the source flux … … 103 748 pmFootprint *footprint = peak->footprint; 104 749 if (!footprint) { 750 psTrace("psphot.czw",2,"Using isophot CR mask code."); 751 105 752 // if we have not footprint, use the old code to mask by isophot 106 753 psphotMaskCosmicRayIsophot (source, maskVal, crMask); … … 109 756 110 757 if (!footprint->spans) { 758 psTrace("psphot.czw",2,"Using isophot CR mask code."); 759 111 760 // if we have no footprint, use the old code to mask by isophot 112 761 psphotMaskCosmicRayIsophot (source, maskVal, crMask); 113 762 return true; 114 763 } 115 764 psphotMaskCosmicRayIsophot (source, maskVal, crMask); 116 765 // mask all of the pixels covered by the spans of the footprint 117 766 for (int j = 1; j < footprint->spans->n; j++) { … … 127 776 } 128 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; 129 921 } 130 922 … … 192 984 } 193 985 } 194 return true;195 }196 197 // model the apmifit distribution for the psf stars:198 bool psphotSourceSizePSF (psphotSourceSizeOptions *options, psArray *sources, pmPSF *psf) {199 200 // select stats from the psf stars201 psVector *Ap = psVectorAllocEmpty (100, PS_TYPE_F32);202 psVector *ApErr = psVectorAllocEmpty (100, PS_TYPE_F32);203 204 psImageMaskType maskVal = options->maskVal | options->markVal;205 206 // XXX why PHOT_WEIGHT??207 pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;208 209 for (int i = 0; i < sources->n; i++) {210 pmSource *source = sources->data[i];211 if (!(source->mode & PM_SOURCE_MODE_PSFSTAR)) continue;212 213 // replace object in image214 if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {215 pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);216 }217 218 // clear the mask bit and set the circular mask pixels219 psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));220 psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);221 222 // XXX can we test if psfMag is set and calculate only if needed?223 pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal224 225 // clear the mask bit226 psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));227 228 // re-subtract the object, leave local sky229 pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);230 231 float apMag = -2.5*log10(source->moments->Sum);232 float dMag = source->psfMag - apMag;233 234 psVectorAppend (Ap, 100, dMag);235 psVectorAppend (ApErr, 100, source->errMag);236 }237 238 // model the distribution as a mean or median value and a systematic error from that value:239 psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);240 psVectorStats (stats, Ap, NULL, NULL, 0);241 242 psVector *dAp = psVectorAlloc (Ap->n, PS_TYPE_F32);243 for (int i = 0; i < Ap->n; i++) {244 dAp->data.F32[i] = Ap->data.F32[i] - stats->robustMedian;245 }246 247 options->ApResid = stats->robustMedian;248 options->ApSysErr = psVectorSystematicError(dAp, ApErr, 0.05);249 psLogMsg ("psphot", PS_LOG_DETAIL, "psf - Sum: %f +/- %f\n", options->ApResid, options->ApSysErr);250 251 psFree (Ap);252 psFree (ApErr);253 psFree (stats);254 psFree (dAp);255 256 return true;257 }258 259 // classify sources based on the combination of psf-mag, Mxx, Myy260 bool psphotSourceClass (pmReadout *readout, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {261 262 bool status;263 pmPSFClump psfClump;264 char regionName[64];265 266 psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4s %4s %4s %4s %4s %4s", "Npsf", "Next", "Nsat", "Ncr", "Nmiss", "Nskip");267 268 int nRegions = psMetadataLookupS32 (&status, readout->analysis, "PSF.CLUMP.NREGIONS");269 for (int i = 0; i < nRegions; i ++) {270 snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);271 psMetadata *regionMD = psMetadataLookupPtr (&status, readout->analysis, regionName);272 psAssert (regionMD, "regions must be defined by earlier call to psphotRoughClassRegion");273 274 psRegion *region = psMetadataLookupPtr (&status, regionMD, "REGION");275 psAssert (region, "regions must be defined by earlier call to psphotRoughClassRegion");276 277 // pull FWHM_X,Y from the recipe, use to define psfClump.X,Y278 psfClump.X = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X"); psAssert (status, "missing PSF.CLUMP.X");279 psfClump.Y = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y"); psAssert (status, "missing PSF.CLUMP.Y");280 psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX"); psAssert (status, "missing PSF.CLUMP.DX");281 psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY"); psAssert (status, "missing PSF.CLUMP.DY");282 283 if ((psfClump.X < 0) || (psfClump.Y < 0) || !psfClump.X || !psfClump.Y || isnan(psfClump.X) || isnan(psfClump.Y)) {284 psLogMsg ("psphot", 4, "Failed to find a valid PSF clump for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);285 continue;286 }287 288 if (!psphotSourceClassRegion (region, &psfClump, sources, recipe, psf, options)) {289 psLogMsg ("psphot", 4, "Failed to determine source classification for region %f,%f - %f,%f\n", region->x0, region->y0, region->x1, region->y1);290 continue;291 }292 }293 294 return true;295 }296 297 bool psphotSourceClassRegion (psRegion *region, pmPSFClump *psfClump, psArray *sources, psMetadata *recipe, pmPSF *psf, psphotSourceSizeOptions *options) {298 299 PS_ASSERT_PTR_NON_NULL(sources, false);300 PS_ASSERT_PTR_NON_NULL(recipe, false);301 302 int Nsat = 0;303 int Next = 0;304 int Npsf = 0;305 int Ncr = 0;306 int Nmiss = 0;307 int Nskip = 0;308 309 pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;310 pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_WEIGHT;311 312 psImageMaskType maskVal = options->maskVal | options->markVal;313 314 for (psS32 i = 0 ; i < sources->n ; i++) {315 316 pmSource *source = (pmSource *) sources->data[i];317 318 // psfClumps are found for image subregions:319 // skip sources not in this region320 if (source->peak->x < region->x0) continue;321 if (source->peak->x >= region->x1) continue;322 if (source->peak->y < region->y0) continue;323 if (source->peak->y >= region->y1) continue;324 325 // skip source if it was already measured326 if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {327 psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");328 continue;329 }330 331 // source must have been subtracted332 if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {333 source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;334 psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");335 continue;336 }337 338 // we are basically classifying by moments; use the default if not found339 psAssert (source->moments, "why is this source missing moments?");340 if (source->mode & noMoments) {341 Nskip ++;342 continue;343 }344 345 psF32 Mxx = source->moments->Mxx;346 psF32 Myy = source->moments->Myy;347 348 // replace object in image349 if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {350 pmSourceAdd (source, PM_MODEL_OP_FULL, options->maskVal);351 }352 353 // clear the mask bit and set the circular mask pixels354 psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));355 psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", options->markVal);356 357 // XXX can we test if psfMag is set and calculate only if needed?358 pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal359 360 // clear the mask bit361 psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(options->markVal));362 363 // re-subtract the object, leave local sky364 pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);365 366 float apMag = -2.5*log10(source->moments->Sum);367 float dMag = source->psfMag - apMag;368 float nSigma = (dMag - options->ApResid) / hypot(source->errMag, options->ApSysErr);369 370 source->extNsigma = nSigma;371 source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;372 373 // Anything within this region is a probably PSF-like object. Saturated stars may land374 // in this region, but are detected elsewhere on the basis of their peak value.375 bool isPSF = (fabs(nSigma) < options->nSigmaApResid) && (fabs(Mxx - psfClump->X) < options->nSigmaMoments*psfClump->dX) && (fabs(Myy - psfClump->Y) < options->nSigmaMoments*psfClump->dY);376 if (isPSF) {377 Npsf ++;378 continue;379 }380 381 // Defects may not always match CRs from peak curvature analysis382 // Defects may also be marked as SATSTAR -- XXX deactivate this flag?383 // XXX this rule is not great384 if ((Mxx < psfClump->X) || (Myy < psfClump->Y)) {385 source->mode |= PM_SOURCE_MODE_DEFECT;386 Ncr ++;387 continue;388 }389 390 // saturated star (determined in PSF fit). These may also be saturated galaxies, or391 // just large saturated regions.392 if (source->mode & PM_SOURCE_MODE_SATSTAR) {393 Nsat ++;394 continue;395 }396 397 // XXX allow the Mxx, Myy to be less than psfClump->X,Y (by some nSigma)?398 bool isEXT = (nSigma > options->nSigmaApResid) || ((Mxx > psfClump->X) && (Myy > psfClump->Y));399 if (isEXT) {400 source->mode |= PM_SOURCE_MODE_EXT_LIMIT;401 Next ++;402 continue;403 }404 405 psWarning ("sourse size was missed for %f,%f : %f %f -- %f\n", source->peak->xf, source->peak->yf, Mxx, Myy, nSigma);406 Nmiss ++;407 }408 409 psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4d %4d %4d %4d %4d %4d", Npsf, Next, Nsat, Ncr, Nmiss, Nskip);410 411 986 return true; 412 987 } … … 486 1061 } 487 1062 488 bool psphotSourceSizeCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) { 1063 // this was an old attempt to identify cosmic rays based on the peak curvature 1064 bool psphotSourcePeakCurvature (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) { 489 1065 490 1066 // classify the sources based on the CR test (place this in a function?) … … 618 1194 return true; 619 1195 } 1196
Note:
See TracChangeset
for help on using the changeset viewer.
