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