24 from lsstDebug
import getDebugFrame
26 import lsst.pex.config
as pexConfig
37 from lsst.meas.base import SingleFrameMeasurementTask, ApplyApCorrTask, CatalogCalculationTask
39 from .measurePsf
import MeasurePsfTask
40 from .repair
import RepairTask
42 __all__ = [
"CharacterizeImageConfig",
"CharacterizeImageTask"]
46 dimensions=(
"instrument",
"visit",
"detector")):
48 doc=
"Input exposure data",
50 storageClass=
"ExposureF",
51 dimensions=[
"instrument",
"visit",
"detector"],
53 characterized = cT.Output(
54 doc=
"Output characterized data.",
56 storageClass=
"ExposureF",
57 dimensions=[
"instrument",
"visit",
"detector"],
59 sourceCat = cT.Output(
60 doc=
"Output source catalog.",
62 storageClass=
"SourceCatalog",
63 dimensions=[
"instrument",
"visit",
"detector"],
65 backgroundModel = cT.Output(
66 doc=
"Output background model.",
67 name=
"icExpBackground",
68 storageClass=
"Background",
69 dimensions=[
"instrument",
"visit",
"detector"],
71 outputSchema = cT.InitOutput(
72 doc=
"Schema of the catalog produced by CharacterizeImage",
74 storageClass=
"SourceCatalog",
79 pipelineConnections=CharacterizeImageConnections):
81 """!Config for CharacterizeImageTask""" 82 doMeasurePsf = pexConfig.Field(
85 doc=
"Measure PSF? If False then for all subsequent operations use either existing PSF " 86 "model when present, or install simple PSF model when not (see installSimplePsf " 89 doWrite = pexConfig.Field(
92 doc=
"Persist results?",
94 doWriteExposure = pexConfig.Field(
97 doc=
"Write icExp and icExpBackground in addition to icSrc? Ignored if doWrite False.",
99 psfIterations = pexConfig.RangeField(
103 doc=
"Number of iterations of detect sources, measure sources, " 104 "estimate PSF. If useSimplePsf is True then 2 should be plenty; " 105 "otherwise more may be wanted.",
107 background = pexConfig.ConfigurableField(
108 target=SubtractBackgroundTask,
109 doc=
"Configuration for initial background estimation",
111 detection = pexConfig.ConfigurableField(
112 target=SourceDetectionTask,
115 doDeblend = pexConfig.Field(
118 doc=
"Run deblender input exposure" 120 deblend = pexConfig.ConfigurableField(
121 target=SourceDeblendTask,
122 doc=
"Split blended source into their components" 124 measurement = pexConfig.ConfigurableField(
125 target=SingleFrameMeasurementTask,
126 doc=
"Measure sources" 128 doApCorr = pexConfig.Field(
131 doc=
"Run subtasks to measure and apply aperture corrections" 133 measureApCorr = pexConfig.ConfigurableField(
134 target=MeasureApCorrTask,
135 doc=
"Subtask to measure aperture corrections" 137 applyApCorr = pexConfig.ConfigurableField(
138 target=ApplyApCorrTask,
139 doc=
"Subtask to apply aperture corrections" 143 catalogCalculation = pexConfig.ConfigurableField(
144 target=CatalogCalculationTask,
145 doc=
"Subtask to run catalogCalculation plugins on catalog" 147 useSimplePsf = pexConfig.Field(
150 doc=
"Replace the existing PSF model with a simplified version that has the same sigma " 151 "at the start of each PSF determination iteration? Doing so makes PSF determination " 152 "converge more robustly and quickly.",
154 installSimplePsf = pexConfig.ConfigurableField(
155 target=InstallGaussianPsfTask,
156 doc=
"Install a simple PSF model",
158 refObjLoader = pexConfig.ConfigurableField(
159 target=LoadIndexedReferenceObjectsTask,
160 doc=
"reference object loader",
162 ref_match = pexConfig.ConfigurableField(
164 doc=
"Task to load and match reference objects. Only used if measurePsf can use matches. " 165 "Warning: matching will only work well if the initial WCS is accurate enough " 166 "to give good matches (roughly: good to 3 arcsec across the CCD).",
168 measurePsf = pexConfig.ConfigurableField(
169 target=MeasurePsfTask,
172 repair = pexConfig.ConfigurableField(
174 doc=
"Remove cosmic rays",
176 checkUnitsParseStrict = pexConfig.Field(
177 doc=
"Strictness of Astropy unit compatibility check, can be 'raise', 'warn' or 'silent'",
187 self.
detection.includeThresholdMultiplier = 10.0
188 self.
detection.doTempLocalBackground =
False 201 "base_CircularApertureFlux",
206 raise RuntimeError(
"Must measure PSF to measure aperture correction, " 207 "because flags determined by PSF measurement are used to identify " 208 "sources used to measure aperture correction")
219 r"""!Measure bright sources and use this to estimate background and PSF of an exposure 221 @anchor CharacterizeImageTask_ 223 @section pipe_tasks_characterizeImage_Contents Contents 225 - @ref pipe_tasks_characterizeImage_Purpose 226 - @ref pipe_tasks_characterizeImage_Initialize 227 - @ref pipe_tasks_characterizeImage_IO 228 - @ref pipe_tasks_characterizeImage_Config 229 - @ref pipe_tasks_characterizeImage_Debug 232 @section pipe_tasks_characterizeImage_Purpose Description 234 Given an exposure with defects repaired (masked and interpolated over, e.g. as output by IsrTask): 235 - detect and measure bright sources 237 - measure and subtract background 240 @section pipe_tasks_characterizeImage_Initialize Task initialisation 242 @copydoc \_\_init\_\_ 244 @section pipe_tasks_characterizeImage_IO Invoking the Task 246 If you want this task to unpersist inputs or persist outputs, then call 247 the `runDataRef` method (a thin wrapper around the `run` method). 249 If you already have the inputs unpersisted and do not want to persist the output 250 then it is more direct to call the `run` method: 252 @section pipe_tasks_characterizeImage_Config Configuration parameters 254 See @ref CharacterizeImageConfig 256 @section pipe_tasks_characterizeImage_Debug Debug variables 258 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a flag 259 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug for more about `debug.py`. 261 CharacterizeImageTask has a debug dictionary with the following keys: 264 <dd>int: if specified, the frame of first debug image displayed (defaults to 1) 266 <dd>bool; if True display image after each repair in the measure PSF loop 268 <dd>bool; if True display image after each background subtraction in the measure PSF loop 270 <dd>bool; if True display image and sources at the end of each iteration of the measure PSF loop 271 See @ref lsst.meas.astrom.displayAstrometry for the meaning of the various symbols. 273 <dd>bool; if True display image and sources after PSF is measured; 274 this will be identical to the final image displayed by measure_iter if measure_iter is true 276 <dd>bool; if True display image and sources after final repair 278 <dd>bool; if True display image and sources after final measurement 281 For example, put something like: 285 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 286 if name == "lsst.pipe.tasks.characterizeImage": 293 lsstDebug.Info = DebugInfo 295 into your `debug.py` file and run `calibrateTask.py` with the `--debug` flag. 297 Some subtasks may have their own debug variables; see individual Task documentation. 302 ConfigClass = CharacterizeImageConfig
303 _DefaultName =
"characterizeImage" 304 RunnerClass = pipeBase.ButlerInitializedTaskRunner
307 inputs = butlerQC.get(inputRefs)
308 if 'exposureIdInfo' not in inputs.keys():
310 exposureIdInfo.expId, exposureIdInfo.expBits = butlerQC.quantum.dataId.pack(
"visit_detector",
312 inputs[
'exposureIdInfo'] = exposureIdInfo
313 outputs = self.
run(**inputs)
314 butlerQC.put(outputs, outputRefs)
316 def __init__(self, butler=None, refObjLoader=None, schema=None, **kwargs):
317 """!Construct a CharacterizeImageTask 319 @param[in] butler A butler object is passed to the refObjLoader constructor in case 320 it is needed to load catalogs. May be None if a catalog-based star selector is 321 not used, if the reference object loader constructor does not require a butler, 322 or if a reference object loader is passed directly via the refObjLoader argument. 323 @param[in] refObjLoader An instance of LoadReferenceObjectsTasks that supplies an 324 external reference catalog to a catalog-based star selector. May be None if a 325 catalog star selector is not used or the loader can be constructed from the 327 @param[in,out] schema initial schema (an lsst.afw.table.SourceTable), or None 328 @param[in,out] kwargs other keyword arguments for lsst.pipe.base.CmdLineTask 333 schema = SourceTable.makeMinimalSchema()
335 self.makeSubtask(
"background")
336 self.makeSubtask(
"installSimplePsf")
337 self.makeSubtask(
"repair")
338 self.makeSubtask(
"measurePsf", schema=self.
schema)
339 if self.config.doMeasurePsf
and self.measurePsf.usesMatches:
341 self.makeSubtask(
'refObjLoader', butler=butler)
342 refObjLoader = self.refObjLoader
343 self.makeSubtask(
"ref_match", refObjLoader=refObjLoader)
345 self.makeSubtask(
'detection', schema=self.
schema)
346 if self.config.doDeblend:
347 self.makeSubtask(
"deblend", schema=self.
schema)
348 self.makeSubtask(
'measurement', schema=self.
schema, algMetadata=self.
algMetadata)
349 if self.config.doApCorr:
350 self.makeSubtask(
'measureApCorr', schema=self.
schema)
351 self.makeSubtask(
'applyApCorr', schema=self.
schema)
352 self.makeSubtask(
'catalogCalculation', schema=self.
schema)
355 self.
schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
360 outputCatSchema.getTable().setMetadata(self.
algMetadata)
361 return {
'outputSchema': outputCatSchema}
364 def runDataRef(self, dataRef, exposure=None, background=None, doUnpersist=True):
365 """!Characterize a science image and, if wanted, persist the results 367 This simply unpacks the exposure and passes it to the characterize method to do the work. 369 @param[in] dataRef: butler data reference for science exposure 370 @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar). 371 If None then unpersist from "postISRCCD". 372 The following changes are made, depending on the config: 373 - set psf to the measured PSF 374 - set apCorrMap to the measured aperture correction 375 - subtract background 376 - interpolate over cosmic rays 377 - update detection and cosmic ray mask planes 378 @param[in,out] background initial model of background already subtracted from exposure 379 (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted, 380 which is typical for image characterization. 381 A refined background model is output. 382 @param[in] doUnpersist if True the exposure is read from the repository 383 and the exposure and background arguments must be None; 384 if False the exposure must be provided. 385 True is intended for running as a command-line task, False for running as a subtask 387 @return same data as the characterize method 390 self.log.
info(
"Processing %s" % (dataRef.dataId))
393 if exposure
is not None or background
is not None:
394 raise RuntimeError(
"doUnpersist true; exposure and background must be None")
395 exposure = dataRef.get(
"postISRCCD", immediate=
True)
396 elif exposure
is None:
397 raise RuntimeError(
"doUnpersist false; exposure must be provided")
399 exposureIdInfo = dataRef.get(
"expIdInfo")
403 exposureIdInfo=exposureIdInfo,
404 background=background,
407 if self.config.doWrite:
408 dataRef.put(charRes.sourceCat,
"icSrc")
409 if self.config.doWriteExposure:
410 dataRef.put(charRes.exposure,
"icExp")
411 dataRef.put(charRes.background,
"icExpBackground")
416 def run(self, exposure, exposureIdInfo=None, background=None):
417 """!Characterize a science image 419 Peforms the following operations: 420 - Iterate the following config.psfIterations times, or once if config.doMeasurePsf false: 421 - detect and measure sources and estimate PSF (see detectMeasureAndEstimatePsf for details) 422 - interpolate over cosmic rays 423 - perform final measurement 425 @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar). 426 The following changes are made: 429 - update detection and cosmic ray mask planes 430 - subtract background and interpolate over cosmic rays 431 @param[in] exposureIdInfo ID info for exposure (an lsst.obs.base.ExposureIdInfo). 432 If not provided, returned SourceCatalog IDs will not be globally unique. 433 @param[in,out] background initial model of background already subtracted from exposure 434 (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted, 435 which is typical for image characterization. 437 @return pipe_base Struct containing these fields, all from the final iteration 438 of detectMeasureAndEstimatePsf: 439 - exposure: characterized exposure; image is repaired by interpolating over cosmic rays, 440 mask is updated accordingly, and the PSF model is set 441 - sourceCat: detected sources (an lsst.afw.table.SourceCatalog) 442 - background: model of background subtracted from exposure (an lsst.afw.math.BackgroundList) 443 - psfCellSet: spatial cells of PSF candidates (an lsst.afw.math.SpatialCellSet) 447 if not self.config.doMeasurePsf
and not exposure.hasPsf():
448 self.log.
warn(
"Source catalog detected and measured with placeholder or default PSF")
449 self.installSimplePsf.
run(exposure=exposure)
451 if exposureIdInfo
is None:
455 background = self.background.
run(exposure).background
457 psfIterations = self.config.psfIterations
if self.config.doMeasurePsf
else 1
458 for i
in range(psfIterations):
461 exposureIdInfo=exposureIdInfo,
462 background=background,
465 psf = dmeRes.exposure.getPsf()
466 psfSigma = psf.computeShape().getDeterminantRadius()
467 psfDimensions = psf.computeImage().getDimensions()
468 medBackground = np.median(dmeRes.background.getImage().getArray())
469 self.log.
info(
"iter %s; PSF sigma=%0.2f, dimensions=%s; median background=%0.2f" %
470 (i + 1, psfSigma, psfDimensions, medBackground))
472 self.
display(
"psf", exposure=dmeRes.exposure, sourceCat=dmeRes.sourceCat)
475 self.repair.
run(exposure=dmeRes.exposure)
476 self.
display(
"repair", exposure=dmeRes.exposure, sourceCat=dmeRes.sourceCat)
480 self.measurement.
run(measCat=dmeRes.sourceCat, exposure=dmeRes.exposure,
481 exposureId=exposureIdInfo.expId)
482 if self.config.doApCorr:
483 apCorrMap = self.measureApCorr.
run(exposure=dmeRes.exposure, catalog=dmeRes.sourceCat).apCorrMap
484 dmeRes.exposure.getInfo().setApCorrMap(apCorrMap)
485 self.applyApCorr.
run(catalog=dmeRes.sourceCat, apCorrMap=exposure.getInfo().getApCorrMap())
486 self.catalogCalculation.
run(dmeRes.sourceCat)
488 self.
display(
"measure", exposure=dmeRes.exposure, sourceCat=dmeRes.sourceCat)
490 return pipeBase.Struct(
491 exposure=dmeRes.exposure,
492 sourceCat=dmeRes.sourceCat,
493 background=dmeRes.background,
494 psfCellSet=dmeRes.psfCellSet,
496 characterized=dmeRes.exposure,
497 backgroundModel=dmeRes.background
502 """!Perform one iteration of detect, measure and estimate PSF 504 Performs the following operations: 505 - if config.doMeasurePsf or not exposure.hasPsf(): 506 - install a simple PSF model (replacing the existing one, if need be) 507 - interpolate over cosmic rays with keepCRs=True 508 - estimate background and subtract it from the exposure 509 - detect, deblend and measure sources, and subtract a refined background model; 510 - if config.doMeasurePsf: 513 @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar) 514 The following changes are made: 516 - update detection and cosmic ray mask planes 517 - subtract background 518 @param[in] exposureIdInfo ID info for exposure (an lsst.obs_base.ExposureIdInfo) 519 @param[in,out] background initial model of background already subtracted from exposure 520 (an lsst.afw.math.BackgroundList). 522 @return pipe_base Struct containing these fields, all from the final iteration 523 of detect sources, measure sources and estimate PSF: 524 - exposure characterized exposure; image is repaired by interpolating over cosmic rays, 525 mask is updated accordingly, and the PSF model is set 526 - sourceCat detected sources (an lsst.afw.table.SourceCatalog) 527 - background model of background subtracted from exposure (an lsst.afw.math.BackgroundList) 528 - psfCellSet spatial cells of PSF candidates (an lsst.afw.math.SpatialCellSet) 531 if not exposure.hasPsf()
or (self.config.doMeasurePsf
and self.config.useSimplePsf):
532 self.log.
warn(
"Source catalog detected and measured with placeholder or default PSF")
533 self.installSimplePsf.
run(exposure=exposure)
536 self.repair.
run(exposure=exposure, keepCRs=
True)
537 self.
display(
"repair_iter", exposure=exposure)
539 if background
is None:
542 sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits)
543 table = SourceTable.make(self.
schema, sourceIdFactory)
546 detRes = self.detection.
run(table=table, exposure=exposure, doSmooth=
True)
547 sourceCat = detRes.sources
548 if detRes.fpSets.background:
549 for bg
in detRes.fpSets.background:
550 background.append(bg)
552 if self.config.doDeblend:
553 self.deblend.
run(exposure=exposure, sources=sourceCat)
555 self.measurement.
run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId)
557 measPsfRes = pipeBase.Struct(cellSet=
None)
558 if self.config.doMeasurePsf:
559 if self.measurePsf.usesMatches:
560 matches = self.ref_match.loadAndMatch(exposure=exposure, sourceCat=sourceCat).matches
563 measPsfRes = self.measurePsf.
run(exposure=exposure, sources=sourceCat, matches=matches,
564 expId=exposureIdInfo.expId)
565 self.
display(
"measure_iter", exposure=exposure, sourceCat=sourceCat)
567 return pipeBase.Struct(
570 background=background,
571 psfCellSet=measPsfRes.cellSet,
575 """Return a dict of empty catalogs for each catalog dataset produced by this task. 579 return {
"icSrc": sourceCat}
581 def display(self, itemName, exposure, sourceCat=None):
582 """Display exposure and sources on next frame, if display of itemName has been requested 584 @param[in] itemName name of item in debugInfo 585 @param[in] exposure exposure to display 586 @param[in] sourceCat source catalog to display
def getInitOutputDatasets(self)
def display(self, itemName, exposure, sourceCat=None)
Class for storing ordered metadata with comments.
def run(self, exposure, exposureIdInfo=None, background=None)
Characterize a science image.
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
Measure bright sources and use this to estimate background and PSF of an exposure.
def getSchemaCatalogs(self)
def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, matches=None, frame=1, title="", pause=True)
def runQuantum(self, butlerQC, inputRefs, outputRefs)
def getDebugFrame(debugDisplay, name)
Config for CharacterizeImageTask.
def runDataRef(self, dataRef, exposure=None, background=None, doUnpersist=True)
Characterize a science image and, if wanted, persist the results.
def __init__(self, butler=None, refObjLoader=None, schema=None, kwargs)
Construct a CharacterizeImageTask.
def detectMeasureAndEstimatePsf(self, exposure, exposureIdInfo, background)
Perform one iteration of detect, measure and estimate PSF.