25from lsst.pipe.base import (Struct, PipelineTask, PipelineTaskConfig, PipelineTaskConnections)
26import lsst.pipe.base.connectionTypes
as cT
27from lsst.pex.config import Config, Field, ConfigurableField, ChoiceField
29from lsst.meas.base import SingleFrameMeasurementTask, ApplyApCorrTask, CatalogCalculationTask
31from lsst.meas.extensions.scarlet
import ScarletDeblendTask
40from lsst.obs.base
import ExposureIdInfo
43from .mergeDetections
import MergeDetectionsConfig, MergeDetectionsTask
44from .mergeMeasurements
import MergeMeasurementsConfig, MergeMeasurementsTask
45from .multiBandUtils
import CullPeaksConfig, _makeGetSchemaCatalogs
46from .deblendCoaddSourcesPipeline
import DeblendCoaddSourcesSingleConfig
47from .deblendCoaddSourcesPipeline
import DeblendCoaddSourcesSingleTask
48from .deblendCoaddSourcesPipeline
import DeblendCoaddSourcesMultiConfig
49from .deblendCoaddSourcesPipeline
import DeblendCoaddSourcesMultiTask
54* deepCoadd_det: detections from what used to be processCoadd (tract, patch, filter)
55* deepCoadd_mergeDet: merged detections (tract, patch)
56* deepCoadd_meas: measurements of merged detections (tract, patch, filter)
57* deepCoadd_ref: reference sources (tract, patch)
58All of these have associated *_schema catalogs that require no data ID and hold no records.
60In addition, we have a schema-only dataset, which saves the schema for the PeakRecords in
61the mergeDet, meas, and ref dataset Footprints:
62* deepCoadd_peak_schema
68 dimensions=(
"tract",
"patch",
"band",
"skymap"),
69 defaultTemplates={
"inputCoaddName":
"deep",
"outputCoaddName":
"deep"}):
70 detectionSchema = cT.InitOutput(
71 doc=
"Schema of the detection catalog",
72 name=
"{outputCoaddName}Coadd_det_schema",
73 storageClass=
"SourceCatalog",
76 doc=
"Exposure on which detections are to be performed",
77 name=
"{inputCoaddName}Coadd",
78 storageClass=
"ExposureF",
79 dimensions=(
"tract",
"patch",
"band",
"skymap")
81 outputBackgrounds = cT.Output(
82 doc=
"Output Backgrounds used in detection",
83 name=
"{outputCoaddName}Coadd_calexp_background",
84 storageClass=
"Background",
85 dimensions=(
"tract",
"patch",
"band",
"skymap")
87 outputSources = cT.Output(
88 doc=
"Detected sources catalog",
89 name=
"{outputCoaddName}Coadd_det",
90 storageClass=
"SourceCatalog",
91 dimensions=(
"tract",
"patch",
"band",
"skymap")
93 outputExposure = cT.Output(
94 doc=
"Exposure post detection",
95 name=
"{outputCoaddName}Coadd_calexp",
96 storageClass=
"ExposureF",
97 dimensions=(
"tract",
"patch",
"band",
"skymap")
101class DetectCoaddSourcesConfig(PipelineTaskConfig, pipelineConnections=DetectCoaddSourcesConnections):
103 @anchor DetectCoaddSourcesConfig_
105 @brief Configuration parameters
for the DetectCoaddSourcesTask
107 doScaleVariance = Field(dtype=bool, default=True, doc=
"Scale variance plane using empirical noise?")
108 scaleVariance = ConfigurableField(target=ScaleVarianceTask, doc=
"Variance rescaling")
109 detection = ConfigurableField(target=DynamicDetectionTask, doc=
"Source detection")
110 coaddName = Field(dtype=str, default=
"deep", doc=
"Name of coadd")
111 doInsertFakes = Field(dtype=bool, default=
False,
112 doc=
"Run fake sources injection task",
113 deprecated=(
"doInsertFakes is no longer supported. This config will be removed "
115 insertFakes = ConfigurableField(target=BaseFakeSourcesTask,
116 doc=
"Injection of fake sources for testing "
117 "purposes (must be retargeted)",
118 deprecated=(
"insertFakes is no longer supported. This config will "
119 "be removed after v24."))
123 doc=
"Should be set to True if fake sources have been inserted into the input data.",
126 def setDefaults(self):
127 super().setDefaults()
128 self.detection.thresholdType =
"pixel_stdev"
129 self.detection.isotropicGrow =
True
131 self.detection.reEstimateBackground =
False
132 self.detection.background.useApprox =
False
133 self.detection.background.binSize = 4096
134 self.detection.background.undersampleStyle =
'REDUCE_INTERP_ORDER'
135 self.detection.doTempWideBackground =
True
145class DetectCoaddSourcesTask(PipelineTask):
146 """Detect sources on a coadd."""
147 _DefaultName =
"detectCoaddSources"
148 ConfigClass = DetectCoaddSourcesConfig
149 getSchemaCatalogs = _makeGetSchemaCatalogs(
"det")
151 def __init__(self, schema=None, **kwargs):
153 @brief Initialize the task. Create the
@ref SourceDetectionTask_
"detection" subtask.
155 Keyword arguments (
in addition to those forwarded to PipelineTask.__init__):
157 @param[
in] schema: initial schema
for the output catalog, modified-
in place to include all
158 fields set by this task. If
None, the source minimal schema will be used.
159 @param[
in] **kwargs: keyword arguments to be passed to lsst.pipe.base.task.Task.__init__
163 super().__init__(**kwargs)
165 schema = afwTable.SourceTable.makeMinimalSchema()
167 self.makeSubtask(
"detection", schema=self.schema)
168 if self.config.doScaleVariance:
169 self.makeSubtask(
"scaleVariance")
171 self.detectionSchema = afwTable.SourceCatalog(self.schema)
173 def runQuantum(self, butlerQC, inputRefs, outputRefs):
174 inputs = butlerQC.get(inputRefs)
175 exposureIdInfo = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId,
"tract_patch_band")
176 inputs[
"idFactory"] = exposureIdInfo.makeSourceIdFactory()
177 inputs[
"expId"] = exposureIdInfo.expId
178 outputs = self.run(**inputs)
179 butlerQC.put(outputs, outputRefs)
181 def run(self, exposure, idFactory, expId):
183 @brief Run detection on an exposure.
185 First scale the variance plane to match the observed variance
186 using
@ref ScaleVarianceTask. Then invoke the
@ref SourceDetectionTask_
"detection" subtask to
189 @param[
in,out] exposure: Exposure on which to detect (may be backround-subtracted
and scaled,
190 depending on configuration).
191 @param[
in] idFactory: IdFactory to set source identifiers
192 @param[
in] expId: Exposure identifier (integer)
for RNG seed
194 @return a pipe.base.Struct
with fields
195 - sources: catalog of detections
196 - backgrounds: list of backgrounds
198 if self.config.doScaleVariance:
199 varScale = self.scaleVariance.run(exposure.maskedImage)
200 exposure.getMetadata().add(
"VARIANCE_SCALE", varScale)
201 backgrounds = afwMath.BackgroundList()
202 table = afwTable.SourceTable.make(self.schema, idFactory)
203 detections = self.detection.run(table, exposure, expId=expId)
204 sources = detections.sources
205 fpSets = detections.fpSets
206 if hasattr(fpSets,
"background")
and fpSets.background:
207 for bg
in fpSets.background:
208 backgrounds.append(bg)
209 return Struct(outputSources=sources, outputBackgrounds=backgrounds, outputExposure=exposure)
215class DeblendCoaddSourcesConfig(Config):
216 """DeblendCoaddSourcesConfig
218 Configuration parameters for the `DeblendCoaddSourcesTask`.
220 singleBandDeblend = ConfigurableField(target=SourceDeblendTask,
221 doc="Deblend sources separately in each band")
222 multiBandDeblend = ConfigurableField(target=ScarletDeblendTask,
223 doc=
"Deblend sources simultaneously across bands")
224 simultaneous = Field(dtype=bool,
226 doc=
"Simultaneously deblend all bands? "
227 "True uses `multibandDeblend` while False uses `singleBandDeblend`")
228 coaddName = Field(dtype=str, default=
"deep", doc=
"Name of coadd")
229 hasFakes = Field(dtype=bool,
231 doc=
"Should be set to True if fake sources have been inserted into the input data.")
233 def setDefaults(self):
234 Config.setDefaults(self)
235 self.singleBandDeblend.propagateAllPeaks =
True
238class MeasureMergedCoaddSourcesConnections(PipelineTaskConnections, dimensions=(
"tract",
"patch",
"band",
"skymap"),
239 defaultTemplates={
"inputCoaddName":
"deep",
240 "outputCoaddName":
"deep",
241 "deblendedCatalog":
"deblendedFlux"}):
242 warnings.warn(
"MeasureMergedCoaddSourcesConnections.defaultTemplates is deprecated and no longer used. "
243 "Use MeasureMergedCoaddSourcesConfig.inputCatalog.")
244 inputSchema = cT.InitInput(
245 doc=
"Input schema for measure merged task produced by a deblender or detection task",
246 name=
"{inputCoaddName}Coadd_deblendedFlux_schema",
247 storageClass=
"SourceCatalog"
249 outputSchema = cT.InitOutput(
250 doc=
"Output schema after all new fields are added by task",
251 name=
"{inputCoaddName}Coadd_meas_schema",
252 storageClass=
"SourceCatalog"
254 refCat = cT.PrerequisiteInput(
255 doc=
"Reference catalog used to match measured sources against known sources",
257 storageClass=
"SimpleCatalog",
258 dimensions=(
"skypix",),
263 doc=
"Input coadd image",
264 name=
"{inputCoaddName}Coadd_calexp",
265 storageClass=
"ExposureF",
266 dimensions=(
"tract",
"patch",
"band",
"skymap")
269 doc=
"SkyMap to use in processing",
270 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
271 storageClass=
"SkyMap",
272 dimensions=(
"skymap",),
274 visitCatalogs = cT.Input(
275 doc=
"Source catalogs for visits which overlap input tract, patch, band. Will be "
276 "further filtered in the task for the purpose of propagating flags from image calibration "
277 "and characterization to coadd objects. Only used in legacy PropagateVisitFlagsTask.",
279 dimensions=(
"instrument",
"visit",
"detector"),
280 storageClass=
"SourceCatalog",
283 sourceTableHandles = cT.Input(
284 doc=(
"Source tables that are derived from the ``CalibrateTask`` sources. "
285 "These tables contain astrometry and photometry flags, and optionally "
287 name=
"sourceTable_visit",
288 storageClass=
"DataFrame",
289 dimensions=(
"instrument",
"visit"),
293 finalizedSourceTableHandles = cT.Input(
294 doc=(
"Finalized source tables from ``FinalizeCalibrationTask``. These "
295 "tables contain PSF flags from the finalized PSF estimation."),
296 name=
"finalized_src_table",
297 storageClass=
"DataFrame",
298 dimensions=(
"instrument",
"visit"),
302 inputCatalog = cT.Input(
303 doc=(
"Name of the input catalog to use."
304 "If the single band deblender was used this should be 'deblendedFlux."
305 "If the multi-band deblender was used this should be 'deblendedModel, "
306 "or deblendedFlux if the multiband deblender was configured to output "
307 "deblended flux catalogs. If no deblending was performed this should "
309 name=
"{inputCoaddName}Coadd_{deblendedCatalog}",
310 storageClass=
"SourceCatalog",
311 dimensions=(
"tract",
"patch",
"band",
"skymap"),
313 scarletCatalog = cT.Input(
314 doc=
"Catalogs produced by multiband deblending",
315 name=
"{inputCoaddName}Coadd_deblendedCatalog",
316 storageClass=
"SourceCatalog",
317 dimensions=(
"tract",
"patch",
"skymap"),
319 scarletModels = cT.Input(
320 doc=
"Multiband scarlet models produced by the deblender",
321 name=
"{inputCoaddName}Coadd_scarletModelData",
322 storageClass=
"ScarletModelData",
323 dimensions=(
"tract",
"patch",
"skymap"),
325 outputSources = cT.Output(
326 doc=
"Source catalog containing all the measurement information generated in this task",
327 name=
"{outputCoaddName}Coadd_meas",
328 dimensions=(
"tract",
"patch",
"band",
"skymap"),
329 storageClass=
"SourceCatalog",
331 matchResult = cT.Output(
332 doc=
"Match catalog produced by configured matcher, optional on doMatchSources",
333 name=
"{outputCoaddName}Coadd_measMatch",
334 dimensions=(
"tract",
"patch",
"band",
"skymap"),
335 storageClass=
"Catalog",
337 denormMatches = cT.Output(
338 doc=
"Denormalized Match catalog produced by configured matcher, optional on "
339 "doWriteMatchesDenormalized",
340 name=
"{outputCoaddName}Coadd_measMatchFull",
341 dimensions=(
"tract",
"patch",
"band",
"skymap"),
342 storageClass=
"Catalog",
345 def __init__(self, *, config=None):
346 super().__init__(config=config)
347 if config.doPropagateFlags
is False:
348 self.inputs -= set((
"visitCatalogs",))
349 self.inputs -= set((
"sourceTableHandles",))
350 self.inputs -= set((
"finalizedSourceTableHandles",))
351 elif config.propagateFlags.target == PropagateSourceFlagsTask:
353 self.inputs -= set((
"visitCatalogs",))
355 if not config.propagateFlags.source_flags:
356 self.inputs -= set((
"sourceTableHandles",))
357 if not config.propagateFlags.finalized_source_flags:
358 self.inputs -= set((
"finalizedSourceTableHandles",))
361 self.inputs -= set((
"sourceTableHandles",))
362 self.inputs -= set((
"finalizedSourceTableHandles",))
364 if config.inputCatalog ==
"deblendedCatalog":
365 self.inputs -= set((
"inputCatalog",))
367 if not config.doAddFootprints:
368 self.inputs -= set((
"scarletModels",))
370 self.inputs -= set((
"deblendedCatalog"))
371 self.inputs -= set((
"scarletModels",))
373 if config.doMatchSources
is False:
374 self.outputs -= set((
"matchResult",))
376 if config.doWriteMatchesDenormalized
is False:
377 self.outputs -= set((
"denormMatches",))
380class MeasureMergedCoaddSourcesConfig(PipelineTaskConfig,
381 pipelineConnections=MeasureMergedCoaddSourcesConnections):
383 @anchor MeasureMergedCoaddSourcesConfig_
385 @brief Configuration parameters
for the MeasureMergedCoaddSourcesTask
387 inputCatalog = ChoiceField(
389 default="deblendedCatalog",
391 "deblendedCatalog":
"Output catalog from ScarletDeblendTask",
392 "deblendedFlux":
"Output catalog from SourceDeblendTask",
393 "mergeDet":
"The merged detections before deblending."
395 doc=
"The name of the input catalog.",
397 doAddFootprints = Field(dtype=bool,
399 doc=
"Whether or not to add footprints to the input catalog from scarlet models. "
400 "This should be true whenever using the multi-band deblender, "
401 "otherwise this should be False.")
402 doConserveFlux = Field(dtype=bool, default=
True,
403 doc=
"Whether to use the deblender models as templates to re-distribute the flux "
404 "from the 'exposure' (True), or to perform measurements on the deblender "
406 doStripFootprints = Field(dtype=bool, default=
True,
407 doc=
"Whether to strip footprints from the output catalog before "
409 "This is usually done when using scarlet models to save disk space.")
410 measurement = ConfigurableField(target=SingleFrameMeasurementTask, doc=
"Source measurement")
411 setPrimaryFlags = ConfigurableField(target=SetPrimaryFlagsTask, doc=
"Set flags for primary tract/patch")
412 doPropagateFlags = Field(
413 dtype=bool, default=
True,
414 doc=
"Whether to match sources to CCD catalogs to propagate flags (to e.g. identify PSF stars)"
416 propagateFlags = ConfigurableField(target=PropagateSourceFlagsTask, doc=
"Propagate source flags to coadd")
417 doMatchSources = Field(dtype=bool, default=
True, doc=
"Match sources to reference catalog?")
418 match = ConfigurableField(target=DirectMatchTask, doc=
"Matching to reference catalog")
419 doWriteMatchesDenormalized = Field(
422 doc=(
"Write reference matches in denormalized format? "
423 "This format uses more disk space, but is more convenient to read."),
425 coaddName = Field(dtype=str, default=
"deep", doc=
"Name of coadd")
426 psfCache = Field(dtype=int, default=100, doc=
"Size of psfCache")
427 checkUnitsParseStrict = Field(
428 doc=
"Strictness of Astropy unit compatibility check, can be 'raise', 'warn' or 'silent'",
435 doc=
"Apply aperture corrections"
437 applyApCorr = ConfigurableField(
438 target=ApplyApCorrTask,
439 doc=
"Subtask to apply aperture corrections"
441 doRunCatalogCalculation = Field(
444 doc=
'Run catalogCalculation task'
446 catalogCalculation = ConfigurableField(
447 target=CatalogCalculationTask,
448 doc=
"Subtask to run catalogCalculation plugins on catalog"
454 doc=
"Should be set to True if fake sources have been inserted into the input data."
458 def refObjLoader(self):
459 return self.match.refObjLoader
461 def setDefaults(self):
462 super().setDefaults()
463 self.measurement.plugins.names |= [
'base_InputCount',
465 'base_LocalPhotoCalib',
467 self.measurement.plugins[
'base_PixelFlags'].masksFpAnywhere = [
'CLIPPED',
'SENSOR_EDGE',
469 self.measurement.plugins[
'base_PixelFlags'].masksFpCenter = [
'CLIPPED',
'SENSOR_EDGE',
480class MeasureMergedCoaddSourcesTask(PipelineTask):
481 """Deblend sources from main catalog in each coadd seperately and measure."""
482 _DefaultName =
"measureCoaddSources"
483 ConfigClass = MeasureMergedCoaddSourcesConfig
484 getSchemaCatalogs = _makeGetSchemaCatalogs(
"meas")
486 def __init__(self, butler=None, schema=None, peakSchema=None, refObjLoader=None, initInputs=None,
489 @brief Initialize the task.
491 Keyword arguments (
in addition to those forwarded to PipelineTask.__init__):
492 @param[
in] schema: the schema of the merged detection catalog used
as input to this one
493 @param[
in] peakSchema: the schema of the PeakRecords
in the Footprints
in the merged detection catalog
494 @param[
in] refObjLoader: an instance of LoadReferenceObjectsTasks that supplies an external reference
495 catalog. May be
None if the loader can be constructed
from the butler argument
or all steps
496 requiring a reference catalog are disabled.
497 @param[
in] butler: a butler used to read the input schemas
from disk
or construct the reference
498 catalog loader,
if schema
or peakSchema
or refObjLoader
is None
500 The task will set its own self.schema attribute to the schema of the output measurement catalog.
501 This will include all fields
from the input schema,
as well
as additional fields
for all the
504 super().__init__(**kwargs)
505 self.deblended = self.config.inputCatalog.startswith("deblended")
506 self.inputCatalog =
"Coadd_" + self.config.inputCatalog
507 if initInputs
is not None:
508 schema = initInputs[
'inputSchema'].schema
510 assert butler
is not None,
"Neither butler nor schema is defined"
511 schema = butler.get(self.config.coaddName + self.inputCatalog +
"_schema", immediate=
True).schema
512 self.schemaMapper = afwTable.SchemaMapper(schema)
513 self.schemaMapper.addMinimalSchema(schema)
514 self.schema = self.schemaMapper.getOutputSchema()
516 self.makeSubtask(
"measurement", schema=self.schema, algMetadata=self.algMetadata)
517 self.makeSubtask(
"setPrimaryFlags", schema=self.schema)
518 if self.config.doMatchSources:
519 self.makeSubtask(
"match", butler=butler, refObjLoader=refObjLoader)
520 if self.config.doPropagateFlags:
521 self.makeSubtask(
"propagateFlags", schema=self.schema)
522 self.schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
523 if self.config.doApCorr:
524 self.makeSubtask(
"applyApCorr", schema=self.schema)
525 if self.config.doRunCatalogCalculation:
526 self.makeSubtask(
"catalogCalculation", schema=self.schema)
528 self.outputSchema = afwTable.SourceCatalog(self.schema)
530 def runQuantum(self, butlerQC, inputRefs, outputRefs):
531 inputs = butlerQC.get(inputRefs)
533 refObjLoader = ReferenceObjectLoader([ref.datasetRef.dataId
for ref
in inputRefs.refCat],
534 inputs.pop(
'refCat'), config=self.config.refObjLoader,
536 self.match.setRefObjLoader(refObjLoader)
540 inputs[
'exposure'].getPsf().setCacheCapacity(self.config.psfCache)
543 exposureIdInfo = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId,
"tract_patch")
544 inputs[
'exposureId'] = exposureIdInfo.expId
545 idFactory = exposureIdInfo.makeSourceIdFactory()
547 table = afwTable.SourceTable.make(self.schema, idFactory)
548 sources = afwTable.SourceCatalog(table)
550 if "scarletCatalog" in inputs:
551 inputCatalog = inputs.pop(
"scarletCatalog")
552 catalogRef = inputRefs.scarletCatalog
554 inputCatalog = inputs.pop(
"inputCatalog")
555 catalogRef = inputRefs.inputCatalog
556 sources.extend(inputCatalog, self.schemaMapper)
559 if self.config.doAddFootprints:
560 modelData = inputs.pop(
'scarletModels')
561 if self.config.doConserveFlux:
562 redistributeImage = inputs[
'exposure'].image
564 redistributeImage =
None
565 modelData.updateCatalogFootprints(
567 band=inputRefs.exposure.dataId[
"band"],
568 psfModel=inputs[
'exposure'].getPsf(),
569 redistributeImage=redistributeImage,
570 removeScarletData=
True,
572 table = sources.getTable()
573 table.setMetadata(self.algMetadata)
574 inputs[
'sources'] = sources
576 skyMap = inputs.pop(
'skyMap')
577 tractNumber = catalogRef.dataId[
'tract']
578 tractInfo = skyMap[tractNumber]
579 patchInfo = tractInfo.getPatchInfo(catalogRef.dataId[
'patch'])
584 wcs=tractInfo.getWcs(),
585 bbox=patchInfo.getOuterBBox()
587 inputs[
'skyInfo'] = skyInfo
589 if self.config.doPropagateFlags:
590 if self.config.propagateFlags.target == PropagateSourceFlagsTask:
592 ccdInputs = inputs[
"exposure"].getInfo().getCoaddInputs().ccds
593 inputs[
"ccdInputs"] = ccdInputs
595 if "sourceTableHandles" in inputs:
596 sourceTableHandles = inputs.pop(
"sourceTableHandles")
597 sourceTableHandleDict = {handle.dataId[
"visit"]: handle
598 for handle
in sourceTableHandles}
599 inputs[
"sourceTableHandleDict"] = sourceTableHandleDict
600 if "finalizedSourceTableHandles" in inputs:
601 finalizedSourceTableHandles = inputs.pop(
"finalizedSourceTableHandles")
602 finalizedSourceTableHandleDict = {handle.dataId[
"visit"]: handle
603 for handle
in finalizedSourceTableHandles}
604 inputs[
"finalizedSourceTableHandleDict"] = finalizedSourceTableHandleDict
608 ccdInputs = inputs[
'exposure'].getInfo().getCoaddInputs().ccds
609 visitKey = ccdInputs.schema.find(
"visit").key
610 ccdKey = ccdInputs.schema.find(
"ccd").key
611 inputVisitIds = set()
613 for ccdRecord
in ccdInputs:
614 visit = ccdRecord.get(visitKey)
615 ccd = ccdRecord.get(ccdKey)
616 inputVisitIds.add((visit, ccd))
617 ccdRecordsWcs[(visit, ccd)] = ccdRecord.getWcs()
619 inputCatalogsToKeep = []
620 inputCatalogWcsUpdate = []
621 for i, dataRef
in enumerate(inputRefs.visitCatalogs):
622 key = (dataRef.dataId[
'visit'], dataRef.dataId[
'detector'])
623 if key
in inputVisitIds:
624 inputCatalogsToKeep.append(inputs[
'visitCatalogs'][i])
625 inputCatalogWcsUpdate.append(ccdRecordsWcs[key])
626 inputs[
'visitCatalogs'] = inputCatalogsToKeep
627 inputs[
'wcsUpdates'] = inputCatalogWcsUpdate
628 inputs[
'ccdInputs'] = ccdInputs
630 outputs = self.run(**inputs)
632 sources = outputs.outputSources
633 butlerQC.put(outputs, outputRefs)
635 def run(self, exposure, sources, skyInfo, exposureId, ccdInputs=None, visitCatalogs=None, wcsUpdates=None,
636 butler=None, sourceTableHandleDict=None, finalizedSourceTableHandleDict=None):
637 """Run measurement algorithms on the input exposure, and optionally populate the
638 resulting catalog with extra information.
642 exposure : `lsst.afw.exposure.Exposure`
643 The input exposure on which measurements are to be performed
645 A catalog built
from the results of merged detections,
or
647 skyInfo : `lsst.pipe.base.Struct`
648 A struct containing information about the position of the input exposure within
649 a `SkyMap`, the `SkyMap`, its `Wcs`,
and its bounding box
650 exposureId : `int`
or `bytes`
651 packed unique number
or bytes unique to the input exposure
653 Catalog containing information on the individual visits which went into making
655 sourceTableHandleDict : `dict` [`int`: `lsst.daf.butler.DeferredDatasetHandle`]
656 Dict
for sourceTable_visit handles (key
is visit)
for propagating flags.
657 These tables are derived
from the ``CalibrateTask`` sources,
and contain
658 astrometry
and photometry flags,
and optionally PSF flags.
659 finalizedSourceTableHandleDict : `dict` [`int`: `lsst.daf.butler.DeferredDatasetHandle`], optional
660 Dict
for finalized_src_table handles (key
is visit)
for propagating flags.
661 These tables are derived
from ``FinalizeCalibrationTask``
and contain
662 PSF flags
from the finalized PSF estimation.
663 visitCatalogs : list of `lsst.afw.table.SourceCatalogs`
664 A list of source catalogs corresponding to measurements made on the individual
665 visits which went into the input exposure. If
None and butler
is `
None` then
666 the task cannot propagate visit flags to the output catalog.
667 Deprecated, to be removed
with PropagateVisitFlagsTask.
669 If visitCatalogs
is not `
None` this should be a list of wcs objects which correspond
670 to the input visits. Used to put all coordinates to common system. If `
None`
and
671 butler
is `
None` then the task cannot propagate visit flags to the output catalog.
672 Deprecated, to be removed
with PropagateVisitFlagsTask.
674 This was a Gen2 butler used to load visit catalogs.
675 No longer used
and should
not be set. Will be removed
in the
680 results : `lsst.pipe.base.Struct`
681 Results of running measurement task. Will contain the catalog
in the
682 sources attribute. Optionally will have results of matching to a
683 reference catalog
in the matchResults attribute,
and denormalized
684 matches
in the denormMatches attribute.
686 if butler
is not None:
687 warnings.warn(
"The 'butler' parameter is no longer used and can be safely removed.",
688 category=FutureWarning, stacklevel=2)
691 self.measurement.run(sources, exposure, exposureId=exposureId)
693 if self.config.doApCorr:
694 self.applyApCorr.run(
696 apCorrMap=exposure.getInfo().getApCorrMap()
703 if not sources.isContiguous():
704 sources = sources.copy(deep=
True)
706 if self.config.doRunCatalogCalculation:
707 self.catalogCalculation.run(sources)
709 self.setPrimaryFlags.run(sources, skyMap=skyInfo.skyMap, tractInfo=skyInfo.tractInfo,
710 patchInfo=skyInfo.patchInfo)
711 if self.config.doPropagateFlags:
712 if self.config.propagateFlags.target == PropagateSourceFlagsTask:
714 self.propagateFlags.run(
717 sourceTableHandleDict,
718 finalizedSourceTableHandleDict
722 self.propagateFlags.run(
733 if self.config.doMatchSources:
734 matchResult = self.match.run(sources, exposure.getInfo().getFilter().bandLabel)
735 matches = afwTable.packMatches(matchResult.matches)
736 matches.table.setMetadata(matchResult.matchMeta)
737 results.matchResult = matches
738 if self.config.doWriteMatchesDenormalized:
739 if matchResult.matches:
740 denormMatches = denormalizeMatches(matchResult.matches, matchResult.matchMeta)
742 self.log.warning(
"No matches, so generating dummy denormalized matches file")
743 denormMatches = afwTable.BaseCatalog(afwTable.Schema())
745 denormMatches.getMetadata().add(
"COMMENT",
746 "This catalog is empty because no matches were found.")
747 results.denormMatches = denormMatches
748 results.denormMatches = denormMatches
750 results.outputSources = sources