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