23 "SingleBandMeasurementDriverConfig",
24 "SingleBandMeasurementDriverTask",
25 "MultiBandMeasurementDriverConfig",
26 "MultiBandMeasurementDriverTask",
31from abc
import ABCMeta, abstractmethod
35import lsst.afw.image
as afwImage
41import lsst.meas.extensions.scarlet
as scarlet
47logging.basicConfig(level=logging.INFO)
51 """Base configuration for measurement driver tasks.
53 This class provides foundational configuration for its subclasses to handle
54 single-band and multi-band data. It defines variance scaling, detection,
55 deblending, measurement, aperture correction, and catalog calculation
56 subtasks, which are intended to be executed in sequence by the driver task.
59 doScaleVariance = Field[bool](doc=
"Scale variance plane using empirical noise?", default=
False)
62 doc=
"Subtask to rescale variance plane", target=measAlgorithms.ScaleVarianceTask
65 doDetect = Field[bool](doc=
"Run the source detection algorithm?", default=
True)
68 doc=
"Subtask to detect sources in the image", target=measAlgorithms.SourceDetectionTask
71 doDeblend = Field[bool](doc=
"Run the source deblending algorithm?", default=
True)
74 doMeasure = Field[bool](doc=
"Run the source measurement algorithm?", default=
True)
77 doc=
"Subtask to measure sources and populate the output catalog",
78 target=measBase.SingleFrameMeasurementTask,
81 psfCache = Field[int](
82 doc=
"Maximum number of PSFs to cache, preventing repeated PSF evaluations at the same "
83 "point across different measurement plugins. Defaults to -1, which auto-sizes the cache "
84 "based on the plugin count.",
88 checkUnitsParseStrict = Field[str](
89 doc=
"Strictness of Astropy unit compatibility check, can be 'raise', 'warn' or 'silent'",
93 doApCorr = Field[bool](
94 doc=
"Apply aperture corrections? If yes, your image must have an aperture correction map",
99 doc=
"Subtask to apply aperture corrections",
100 target=measBase.ApplyApCorrTask,
103 doRunCatalogCalculation = Field[bool](doc=
"Run catalogCalculation task?", default=
False)
106 doc=
"Subtask to run catalogCalculation plugins on catalog", target=measBase.CatalogCalculationTask
115 "doRunCatalogCalculation",
119 """Ensure that at least one processing step is enabled."""
122 if not any(getattr(self, opt)
for opt
in self.
doOptions):
123 raise ValueError(f
"At least one of these options must be enabled: {self.doOptions}")
127 """Base class for the mid-level driver running variance scaling, detection,
128 deblending, measurement, apperture correction, and catalog calculation in
131 Users don't need to Butlerize their input data, which is a significant
132 advantage for quick data exploration and testing. This driver simplifies
133 the process of applying measurement algorithms to images by abstracting
134 away low-level implementation details such as Schema and table boilerplate.
135 It's a convenient way to process images into catalogs with a user-friendly
136 interface for non-developers while allowing extensive configuration and
137 integration into unit tests for developers. It also considerably improves
138 how demos and workflows are showcased in Jupyter notebooks.
143 Schema used to create the output `~lsst.afw.table.SourceCatalog`,
144 modified in place with fields that will be written by this task.
146 Schema of Footprint Peaks that will be passed to the deblender.
148 Additional kwargs to pass to lsst.pipe.base.Task.__init__()
152 Subclasses (e.g., single-band vs. multi-band) share most methods and config
153 options but differ in handling and validating inputs by overriding the base
154 config class and any methods that require their own logic.
157 ConfigClass = MeasurementDriverBaseConfig
158 _DefaultName =
"measurementDriverBase"
174 self.
deblend: measDeblender.SourceDeblendTask | scarlet.ScarletDeblendTask
184 super().
__setattr__(
"initSchema", copy.deepcopy(schema))
187 """Prevent external modifications of the initial Schema."""
188 if name ==
"initSchema":
189 raise AttributeError(f
"Cannot modify {name} directly")
193 def run(self, *args, **kwargs) -> pipeBase.Struct:
194 """Run the measurement driver task. Subclasses must implement this
195 method using their own logic to handle single-band or multi-band data.
197 raise NotImplementedError(
"This is not implemented on the base class")
193 def run(self, *args, **kwargs) -> pipeBase.Struct:
…
201 catalog: afwTable.SourceCatalog |
None,
203 """Perform validation and adjustments of inputs without heavy
209 Catalog to be extended by the driver task.
215 if catalog
is not None:
217 "An input catalog was given to bypass detection, but 'doDetect' is still on"
221 raise RuntimeError(
"Cannot run without detection if no 'catalog' is provided")
224 """Initialize the Schema to be used for constructing the subtasks.
226 Though it may seem clunky, this workaround is necessary to ensure
227 Schema consistency across all subtasks.
232 Catalog from which to extract the Schema. If not provided, the
233 user-provided Schema and if that is also not provided during
234 initialization, a minimal Schema will be used.
246 self.
schema = afwTable.SourceTable.makeMinimalSchema()
251 if self.
schema is not None:
253 "Both a catalog and a Schema were provided; using the Schema from the catalog only"
257 catalogSchema = catalog.schema
267 self.
mapper.addMinimalSchema(catalogSchema,
True)
274 """Add coordinate error fields to the schema in-place if they are not
280 Schema to be checked for coordinate error fields.
283 errorField
in schema.getNames()
284 for errorField
in (
"coord_raErr",
"coord_decErr",
"coord_ra_dec_Cov")
286 afwTable.CoordKey.addErrorFields(schema)
289 """Construct subtasks based on the configuration and the Schema."""
290 if self.
schema is None and any(
291 getattr(self.
config, attr)
for attr
in self.
config.doOptions
if attr !=
"doScaleVariance"
294 "Cannot create requested subtasks without a Schema; "
295 "ensure one is provided explicitly or via a catalog"
298 if self.
config.doScaleVariance:
299 self.makeSubtask(
"scaleVariance")
302 self.makeSubtask(
"detection", schema=self.
schema)
308 self.makeSubtask(
"measurement", schema=self.
schema)
311 self.makeSubtask(
"applyApCorr", schema=self.
schema)
313 if self.
config.doRunCatalogCalculation:
314 self.makeSubtask(
"catalogCalculation", schema=self.
schema)
317 self, catalog: afwTable.SourceCatalog |
None
319 """Ensure subtasks are properly initialized according to the
320 configuration and the provided catalog.
325 Optional catalog to be used for initializing the Schema and the
331 Updated catalog to be passed to the subtasks, if it was provided.
341 self.
schema.checkUnits(parse_strict=self.
config.checkUnitsParseStrict)
350 """Scale the variance plane of an exposure to match the observed
356 Exposure on which to run the variance scaling algorithm.
358 Band associated with the exposure. Used for logging.
360 self.log.info(f
"Scaling variance plane for {band} band")
362 exposure.getMetadata().add(
"VARIANCE_SCALE", varScale)
367 """Make a catalog or catalogs contiguous if they are not already.
372 Catalog or dictionary of catalogs with bands as keys to be made
378 Contiguous catalog or dictionary of contiguous catalogs.
380 if isinstance(catalog, dict):
381 for band, cat
in catalog.items():
382 if not cat.isContiguous():
383 self.log.info(f
"{band}-band catalog is not contiguous; making it contiguous")
384 catalog[band] = cat.copy(deep=
True)
386 if not catalog.isContiguous():
387 self.log.info(
"Catalog is not contiguous; making it contiguous")
388 catalog = catalog.copy(deep=
True)
392 """Update the Schema of the provided catalog to incorporate changes
393 made by the configured subtasks.
398 Catalog to be updated with the Schema changes.
403 Catalog with the updated Schema.
411 updatedCatalog.extend(catalog, mapper=self.
mapper)
415 return updatedCatalog
420 """Run the detection subtask to identify sources in the image.
425 Exposure on which to run the detection algorithm.
427 Generator for unique source IDs.
432 A catalog containing detected sources.
434 A list of background models obtained from the detection process,
437 self.log.info(f
"Running detection on a {exposure.width}x{exposure.height} pixel exposure")
441 table = afwTable.SourceTable.make(self.
schema, idGenerator.make_table_id_factory())
444 detections = self.
detection.run(table, exposure)
445 catalog = detections.sources
449 if hasattr(detections,
"background")
and detections.background:
450 for bg
in detections.background:
451 backgroundList.append(bg)
453 return catalog, backgroundList
457 """Run the deblending subtask to separate blended sources. Subclasses
458 must implement this method to handle task-specific deblending logic.
460 raise NotImplementedError(
"This is not implemented on the base class")
464 exposure: afwImage.Exposure,
466 idGenerator: measBase.IdGenerator,
468 """Run the measurement subtask to compute properties of sources.
473 Exposure on which to run the measurement algorithm.
475 Catalog containing sources on which to run the measurement subtask.
477 Generator for unique source IDs.
479 self.
measurement.run(measCat=catalog, exposure=exposure, exposureId=idGenerator.catalog_id)
484 """Apply aperture corrections to the catalog.
489 Exposure on which to apply aperture corrections.
491 Catalog to be corrected using the aperture correction map from
494 Generator for unique source IDs.
496 apCorrMap = exposure.getInfo().getApCorrMap()
497 if apCorrMap
is None:
499 "Image does not have valid aperture correction map for catalog id "
500 f
"{idGenerator.catalog_id}; skipping aperture correction"
503 self.
applyApCorr.run(catalog=catalog, apCorrMap=apCorrMap)
506 """Run the catalog calculation plugins on the catalog.
511 Catalog to be processed by the catalog calculation subtask.
517 exposure: afwImage.Exposure,
519 idGenerator: measBase.IdGenerator,
520 band: str =
"a single",
522 """Process a catalog through measurement, aperture correction, and
523 catalog calculation subtasks.
528 Exposure associated with the catalog.
530 Catalog to be processed by the subtasks.
532 Generator for unique source IDs.
534 Band associated with the exposure and catalog. Used for logging.
538 if self.
config.psfCache > 0:
540 exposure.psf.setCacheCapacity(self.
config.psfCache)
548 exposure.psf.setCacheCapacity(2 * len(self.
config.measurement.plugins.names))
552 self.log.info(f
"Measuring {len(catalog)} sources in {band} band")
560 self.log.info(f
"Applying aperture corrections to {band} band")
564 if self.
config.doRunCatalogCalculation:
565 self.log.info(f
"Running catalog calculation on {band} band")
569 f
"Finished processing for {band} band; output catalog has {catalog.schema.getFieldCount()} "
570 f
"fields and {len(catalog)} records"
577 """Configuration for the single-band measurement driver task."""
579 deblend =
ConfigurableField(target=measDeblender.SourceDeblendTask, doc=
"Deblender for single-band data.")
583 """Mid-level driver for processing single-band data.
585 Offers a helper method for direct handling of raw image data in addition to
586 the standard single-band exposure.
590 Here is an example of how to use this class to run variance scaling,
591 detection, deblending, and measurement on a single-band exposure:
593 >>> from lsst.pipe.tasks.measurementDriver import (
594 ... SingleBandMeasurementDriverConfig,
595 ... SingleBandMeasurementDriverTask,
597 >>> import lsst.meas.extensions.shapeHSM # To register its plugins
598 >>> config = SingleBandMeasurementDriverConfig()
599 >>> config.doScaleVariance = True
600 >>> config.doDetect = True
601 >>> config.doDeblend = True
602 >>> config.doMeasure = True
603 >>> config.scaleVariance.background.binSize = 64
604 >>> config.detection.thresholdValue = 5.5
605 >>> config.deblend.tinyFootprintSize = 3
606 >>> config.measurement.plugins.names |= [
607 ... "base_SdssCentroid",
608 ... "base_SdssShape",
609 ... "ext_shapeHSM_HsmSourceMoments",
611 >>> config.measurement.slots.psfFlux = None
612 >>> config.measurement.doReplaceWithNoise = False
613 >>> exposure = butler.get("deepCoadd", dataId=...)
614 >>> driver = SingleBandMeasurementDriverTask(config=config)
615 >>> results = driver.run(exposure)
616 >>> results.catalog.writeFits("meas_catalog.fits")
618 Alternatively, if an exposure is not available, the driver can also process
627 >>> results = driver.runFromImage(
628 ... image, mask, variance, wcs, psf, photoCalib
630 >>> results.catalog.writeFits("meas_catalog.fits")
633 ConfigClass = SingleBandMeasurementDriverConfig
634 _DefaultName =
"singleBandMeasurementDriver"
635 _Deblender =
"meas_deblender"
644 exposure: afwImage.Exposure,
646 idGenerator: measBase.IdGenerator |
None =
None,
647 ) -> pipeBase.Struct:
648 """Process a single-band exposure through the configured subtasks and
649 return the results as a struct.
654 The exposure on which to run the driver task.
656 Catalog to be extended by the driver task. If not provided, an
657 empty catalog will be created and populated.
659 Object that generates source IDs and provides random seeds.
663 result : `lsst.pipe.base.Struct`
664 Results as a struct with attributes:
667 Catalog containing the measured sources
668 (`~lsst.afw.table.SourceCatalog`).
670 List of backgrounds (`list[~lsst.afw.math.Background]`). Only
671 populated if detection is enabled.
681 if idGenerator
is None:
682 idGenerator = measBase.IdGenerator()
686 if self.
config.doScaleVariance:
691 catalog, backgroundList = self.
_detectSources(exposure, idGenerator)
693 self.log.info(
"Skipping detection; using detections from the provided catalog")
694 backgroundList =
None
700 self.log.info(
"Skipping deblending")
706 return pipeBase.Struct(catalog=catalog, backgroundList=backgroundList)
714 psf: afwDetection.Psf | np.ndarray =
None,
717 idGenerator: measBase.IdGenerator =
None,
718 ) -> pipeBase.Struct:
719 """Convert image data to an `Exposure`, then run it through the
725 Input image data. Will be converted into an `Exposure` before
728 Mask data for the image. Used if ``image`` is a bare `array` or
731 Variance plane data for the image.
733 World Coordinate System to associate with the exposure that will
734 be created from ``image``.
736 PSF model for the exposure.
738 Photometric calibration model for the exposure.
740 Catalog to be extended by the driver task. If not provided, a new
741 catalog will be created during detection and populated.
743 Generator for unique source IDs.
747 result : `lsst.pipe.base.Struct`
748 Results as a struct with attributes:
751 Catalog containing the measured sources
752 (`~lsst.afw.table.SourceCatalog`).
754 List of backgrounds (`list[~lsst.afw.math.Background]`).
757 if isinstance(image, np.ndarray):
758 image = afwImage.makeImageFromArray(image)
759 if isinstance(mask, np.ndarray):
760 mask = afwImage.makeMaskFromArray(mask)
761 if isinstance(variance, np.ndarray):
762 variance = afwImage.makeImageFromArray(variance)
771 raise TypeError(f
"Unsupported 'image' type: {type(image)}")
774 if isinstance(psf, np.ndarray):
779 psf = afwDetection.KernelPsf(kernel)
780 elif not isinstance(psf, afwDetection.Psf):
781 raise TypeError(f
"Unsupported 'psf' type: {type(psf)}")
784 if photoCalib
is not None:
785 exposure.setPhotoCalib(photoCalib)
787 return self.
run(exposure, catalog=catalog, idGenerator=idGenerator)
792 """Run single-band deblending given an exposure and a catalog.
797 Exposure on which to run the deblending algorithm.
799 Catalog containing sources to be deblended.
801 self.log.info(f
"Deblending using '{self._Deblender}' on {len(catalog)} detection footprints")
802 self.
deblend.run(exposure=exposure, sources=catalog)
809 """Configuration for the multi-band measurement driver task."""
812 target=scarlet.ScarletDeblendTask, doc=
"Scarlet deblender for multi-band data"
815 doConserveFlux = Field[bool](
816 doc=
"Whether to use the deblender models as templates to re-distribute the flux from "
817 "the 'exposure' (True), or to perform measurements on the deblender model footprints.",
821 measureOnlyInRefBand = Field[bool](
822 doc=
"If True, all measurements downstream of deblending run only in the reference band that "
823 "was used for detection; otherwise, they are performed in all available bands, generating a "
824 "catalog for each. Regardless of this setting, deblending still uses all available bands.",
828 removeScarletData = Field[bool](
829 doc=
"Whether or not to remove `ScarletBlendData` for each blend in order to save memory. "
830 "If set to True, some sources may end up with missing footprints in catalogs other than the "
831 "reference-band catalog, leading to failures in subsequent measurements that require footprints. "
832 "For example, keep this False if `measureOnlyInRefBand` is set to False and "
833 "`measurement.doReplaceWithNoise` to True, in order to make the footprints available in "
834 "non-reference bands in addition to the reference band.",
838 updateFluxColumns = Field[bool](
839 doc=
"Whether or not to update the `deblend_*` columns in the catalog. This should only be "
840 "True when the input catalog schema already contains those columns.",
846 """Mid-level driver for processing multi-band data.
848 The default behavior is to run detection on the reference band, use all
849 available bands for deblending, and then process everything downstream
850 separately for each band making per-band catalogs unless configured
851 otherwise. This subclass provides functionality for handling a singe-band
852 exposure and a list of single-band exposures in addition to a standard
857 Here is an example of how to use this class to run variance scaling,
858 detection, deblending, measurement, and aperture correction on a multi-band
861 >>> from lsst.afw.image import MultibandExposure
862 >>> from lsst.pipe.tasks.measurementDriver import (
863 ... MultiBandMeasurementDriverConfig,
864 ... MultiBandMeasurementDriverTask,
866 >>> import lsst.meas.extensions.shapeHSM # To register its plugins
867 >>> config = MultiBandMeasurementDriverConfig()
868 >>> config.doScaleVariance = True
869 >>> config.doDetect = True
870 >>> config.doDeblend = True
871 >>> config.doMeasure = True
872 >>> config.doApCorr = True
873 >>> config.scaleVariance.background.binSize = 64
874 >>> config.detection.thresholdValue = 5.5
875 >>> config.deblend.minSNR = 42.0
876 >>> config.deblend.maxIter = 20
877 >>> config.measurement.plugins.names |= [
878 ... "base_SdssCentroid",
879 ... "base_SdssShape",
880 ... "ext_shapeHSM_HsmSourceMoments",
882 >>> config.measurement.slots.psfFlux = None
883 >>> config.measurement.doReplaceWithNoise = False
884 >>> config.applyApCorr.doFlagApCorrFailures = False
885 >>> mExposure = MultibandExposure.fromButler(
886 ... butler, ["g", "r", "i"], "deepCoadd_calexp", ...
888 >>> driver = MultiBandMeasurementDriverTask(config=config)
889 >>> results = driver.run(mExposure, "r")
890 >>> for band, catalog in results.catalogs.items():
891 ... catalog.writeFits(f"meas_catalog_{band}.fits")
894 ConfigClass = MultiBandMeasurementDriverConfig
895 _DefaultName =
"multiBandMeasurementDriver"
896 _Deblender =
"scarlet"
910 refBand: str |
None =
None,
911 bands: list[str] |
None =
None,
913 idGenerator: measBase.IdGenerator =
None,
914 ) -> pipeBase.Struct:
915 """Process an exposure through the configured subtasks while using
916 multi-band information for deblending.
921 Multi-band data. May be a `MultibandExposure`, a single-band
922 exposure (i.e., `Exposure`), or a list of single-band exposures
923 associated with different bands in which case ``bands`` must be
924 provided. If a single-band exposure is given, it will be treated as
925 a `MultibandExposure` that contains only that one band.
927 Reference band to use for detection. Not required for single-band
928 exposures. If `measureOnlyInRefBand` is enabled while detection is
929 disabled and a catalog of detected sources is provided, this
930 should specify the band the sources were detected on (or the band
931 you want to use to perform measurements on exclusively). If
932 `measureOnlyInRefBand` is disabled instead in the latter scenario,
933 ``refBand`` does not need to be provided.
935 List of bands associated with the exposures in ``mExposure``. Only
936 required if ``mExposure`` is a list of single-band exposures. If
937 provided for a multi-band exposure, it will be used to only process
938 that subset of bands from the available ones in the exposure.
940 Catalog to be extended by the driver task. If not provided, a new
941 catalog will be created and populated.
943 Generator for unique source IDs.
947 result : `lsst.pipe.base.Struct`
948 Results as a struct with attributes:
951 Dictionary of catalogs containing the measured sources with
952 bands as keys (`dict[str, ~lsst.afw.table.SourceCatalog]`). If
953 `measureOnlyInRefBand` is enabled or deblending is disabled,
954 this will only contain the reference-band catalog; otherwise,
955 it will contain a catalog for each band.
957 List of backgrounds (`list[~lsst.afw.math.Background]`). Will
958 be None if detection is disabled.
960 Multiband scarlet models produced during deblending
961 (`~lsst.scarlet.lite.io.ScarletModelData`). Will be None if
962 deblending is disabled.
966 mExposure, refBand, bands = self.
_ensureValidInputs(mExposure, refBand, bands, catalog)
972 if idGenerator
is None:
973 idGenerator = measBase.IdGenerator()
977 if self.
config.doScaleVariance:
979 for band
in mExposure.bands:
984 catalog, backgroundList = self.
_detectSources(mExposure[refBand], idGenerator)
986 self.log.info(
"Skipping detection; using detections from provided catalog")
987 backgroundList =
None
994 "Skipping deblending; proceeding with the provided catalog in the reference band"
996 catalogs = {refBand: catalog}
1001 for band, catalog
in catalogs.items():
1002 exposure = mExposure[band]
1003 self.
_processCatalog(exposure, catalog, idGenerator, band=f
"'{band}'")
1005 return pipeBase.Struct(catalogs=catalogs, backgroundList=backgroundList, modelData=self.
modelData)
1010 refBand: str |
None,
1011 bands: list[str] |
None,
1014 """Perform validation and adjustments of inputs without heavy
1020 Multi-band data to be processed by the driver task.
1022 Reference band to use for detection or measurements.
1024 List of bands associated with the exposures in ``mExposure``.
1026 Catalog to be extended by the driver task.
1031 Multi-band exposure to be processed by the driver task.
1033 Reference band to use for detection or measurements.
1035 List of bands associated with the exposures in ``mExposure``.
1043 if bands
is not None:
1044 if any(b
not in mExposure.bands
for b
in bands):
1046 "Some bands in the 'bands' list are not present in the input multi-band exposure"
1049 f
"Using bands {bands} out of the available {mExposure.bands} in the multi-band exposure"
1051 elif isinstance(mExposure, list):
1053 raise ValueError(
"The 'bands' list must be provided if 'mExposure' is a list")
1054 if len(bands) != len(mExposure):
1055 raise ValueError(
"Number of bands and exposures must match")
1057 if bands
is not None and len(bands) != 1:
1059 "The 'bands' list, if provided, must only contain a single band "
1060 "if a single-band exposure is given"
1062 if bands
is None and refBand
is None:
1065 elif bands
is None and refBand
is not None:
1067 elif bands
is not None and refBand
is None:
1070 raise TypeError(f
"Unsupported 'mExposure' type: {type(mExposure)}")
1076 if len(mExposure.bands) == 1:
1081 self.log.info(f
"Running '{self._Deblender}' in single-band mode; make sure it was intended!")
1083 refBand = mExposure.bands[0]
1085 "No reference band provided for single-band data; "
1086 f
"using the only available band ('{refBand}') as the reference band"
1090 if self.
config.measureOnlyInRefBand:
1091 measInfo =
"and everything downstream of deblending"
1094 "while subtasks downstream of deblending will be run in each of "
1095 f
"the {mExposure.bands} bands"
1097 self.log.info(f
"Using '{refBand}' as the reference band for detection {measInfo}")
1101 raise ValueError(
"Reference band must be provided for multi-band data")
1103 if refBand
not in mExposure.bands:
1104 raise ValueError(f
"Requested band '{refBand}' is not present in the multi-band exposure")
1106 if bands
is not None and refBand
not in bands:
1107 raise ValueError(f
"Reference band '{refBand}' is not in the list of 'bands' provided: {bands}")
1109 return mExposure, refBand, bands
1114 """Run multi-band deblending given a multi-band exposure and a catalog.
1119 Multi-band exposure on which to run the deblending algorithm.
1121 Catalog containing sources to be deblended.
1123 Reference band used for detection or the band to use for
1124 measurements if `measureOnlyInRefBand` is enabled.
1129 Dictionary of catalogs containing the deblended sources. If
1130 `measureOnlyInRefBand` is enabled, this will only contain the
1131 reference-band catalog; otherwise, it will contain a catalog for
1134 Multiband scarlet models produced during deblending.
1136 self.log.info(f
"Deblending using '{self._Deblender}' on {len(catalog)} detection footprints")
1139 catalog, modelData = self.
deblend.run(mExposure, catalog)
1142 bands = [refBand]
if self.
config.measureOnlyInRefBand
else mExposure.bands
1144 catalogs = {band: catalog.copy(deep=
True)
for band
in bands}
1147 imageForRedistribution = mExposure[band]
if self.
config.doConserveFlux
else None
1148 scarlet.io.updateCatalogFootprints(
1149 modelData=modelData,
1150 catalog=catalogs[band],
1152 imageForRedistribution=imageForRedistribution,
1153 removeScarletData=self.
config.removeScarletData,
1154 updateFluxColumns=self.
config.updateFluxColumns,
1162 bands: list[str] |
None,
1164 """Convert a single-band exposure or a list of single-band exposures to
1165 a `MultibandExposure` if not already of that type.
1167 No conversion will be done if ``mExposureData`` is already a
1168 `MultibandExposure` except it will be subsetted to the bands provided.
1173 Input multi-band data.
1175 List of bands associated with the exposures in ``mExposure``. Only
1176 required if ``mExposure`` is a list of single-band exposures. If
1177 provided while ``mExposureData`` is a ``MultibandExposure``, it
1178 will be used to select a specific subset of bands from the
1184 Converted multi-band exposure.
1187 if bands
and not set(bands).issubset(mExposureData.bands):
1189 f
"Requested bands {bands} are not a subset of available bands: {mExposureData.bands}"
1191 return mExposureData[bands,]
if bands
and len(bands) > 1
else mExposureData
1192 elif isinstance(mExposureData, list):
1193 mExposure = afwImage.MultibandExposure.fromExposures(bands, mExposureData)
1197 mExposure = afwImage.MultibandExposure.fromExposures(bands, [mExposureData])
1203 for band, exposure
in zip(bands, mExposureData):
1204 mExposure[band].setWcs(exposure.getWcs())
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
A class to contain the data, WCS, and other information needed to describe an image of the sky.
A class to represent a 2-dimensional array of pixels.
Represent a 2-dimensional array of bitmask pixels.
A class to manipulate images, masks, and variance as a single object.
The photometric calibration of an exposure.
A kernel created from an Image.
Defines the fields and offsets for a table.
A mapping between the keys of two Schemas, used to copy data between them.
afwTable.SourceCatalog _processCatalog(self, afwImage.Exposure exposure, afwTable.SourceCatalog catalog, measBase.IdGenerator idGenerator, str band="a single")
_ensureValidInputs(self, afwTable.SourceCatalog|None catalog)
pipeBase.Struct run(self, *args, **kwargs)
_applyApCorr(self, afwImage.Exposure exposure, afwTable.SourceCatalog catalog, measBase.IdGenerator idGenerator)
afwTable.Schema initSchema
afwTable.SchemaMapper mapper
_scaleVariance(self, afwImage.Exposure exposure, str band="a single")
_deblendSources(self, *args, **kwargs)
afwTable.SchemaMapper schema
__setattr__(self, name, value)
_addCoordErrorFieldsIfMissing(self, afwTable.Schema schema)
measAlgorithms.ScaleVarianceTask scaleVariance
measAlgorithms.SourceDetectionTask detection
afwTable.SourceCatalog|dict[str, afwTable.SourceCatalog] _toContiguous(self, afwTable.SourceCatalog|dict[str, afwTable.SourceCatalog] catalog)
tuple[afwTable.SourceCatalog, afwMath.BackgroundList] _detectSources(self, afwImage.Exposure|afwImage.MultibandExposure exposure, measBase.IdGenerator idGenerator)
__init__(self, afwTable.Schema schema=None, afwTable.Schema peakSchema=None, **dict kwargs)
measDeblender.SourceDeblendTask|scarlet.ScarletDeblendTask deblend
measBase.CatalogCalculationTask catalogCalculation
measBase.SingleFrameMeasurementTask measurement
afwTable.SourceCatalog|None _prepareSchemaAndSubtasks(self, afwTable.SourceCatalog|None catalog)
_initializeSchema(self, afwTable.SourceCatalog catalog=None)
measBase.ApplyApCorrTask applyApCorr
_runCatalogCalculation(self, afwTable.SourceCatalog catalog)
afwTable.SourceCatalog _updateCatalogSchema(self, afwTable.SourceCatalog catalog)
_measureSources(self, afwImage.Exposure exposure, afwTable.SourceCatalog catalog, measBase.IdGenerator idGenerator)
pipeBase.Struct run(self, afwImage.MultibandExposure|list[afwImage.Exposure]|afwImage.Exposure mExposure, str|None refBand=None, list[str]|None bands=None, afwTable.SourceCatalog catalog=None, measBase.IdGenerator idGenerator=None)
tuple[afwImage.MultibandExposure, str, list[str]|None] _ensureValidInputs(self, afwImage.MultibandExposure|list[afwImage.Exposure]|afwImage.Exposure mExposure, str|None refBand, list[str]|None bands, afwTable.SourceCatalog|None catalog=None)
tuple[dict[str, afwTable.SourceCatalog], scl.io.ScarletModelData] _deblendSources(self, afwImage.MultibandExposure mExposure, afwTable.SourceCatalog catalog, str refBand)
afwImage.MultibandExposure _buildMultibandExposure(self, afwImage.MultibandExposure|list[afwImage.Exposure]|afwImage.Exposure mExposureData, list[str]|None bands)
__init__(self, *args, **kwargs)
scl.io.ScarletModelData modelData
afwTable.SourceCatalog _deblendSources(self, afwImage.Exposure exposure, afwTable.SourceCatalog catalog)
pipeBase.Struct runFromImage(self, afwImage.MaskedImage|afwImage.Image|np.ndarray image, afwImage.Mask|np.ndarray mask=None, afwImage.Image|np.ndarray variance=None, afwGeom.SkyWcs wcs=None, afwDetection.Psf|np.ndarray psf=None, afwImage.PhotoCalib photoCalib=None, afwTable.SourceCatalog catalog=None, measBase.IdGenerator idGenerator=None)
__init__(self, *args, **kwargs)
pipeBase.Struct run(self, afwImage.Exposure exposure, afwTable.SourceCatalog|None catalog=None, measBase.IdGenerator|None idGenerator=None)
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT > > image, typename std::shared_ptr< Mask< MaskPixelT > > mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT > > variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.