Loading [MathJax]/extensions/tex2jax.js
LSST Applications g0fba68d861+05816baf74,g1ec0fe41b4+f536777771,g1fd858c14a+a9301854fb,g35bb328faa+fcb1d3bbc8,g4af146b050+a5c07d5b1d,g4d2262a081+6e5fcc2a4e,g53246c7159+fcb1d3bbc8,g56a49b3a55+9c12191793,g5a012ec0e7+3632fc3ff3,g60b5630c4e+ded28b650d,g67b6fd64d1+ed4b5058f4,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g8352419a5c+fcb1d3bbc8,g87b7deb4dc+7b42cf88bf,g8852436030+e5453db6e6,g89139ef638+ed4b5058f4,g8e3bb8577d+d38d73bdbd,g9125e01d80+fcb1d3bbc8,g94187f82dc+ded28b650d,g989de1cb63+ed4b5058f4,g9d31334357+ded28b650d,g9f33ca652e+50a8019d8c,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+d9fb1f8026,gb58c049af0+f03b321e39,gb665e3612d+2a0c9e9e84,gb89ab40317+ed4b5058f4,gcf25f946ba+e5453db6e6,gd6cbbdb0b4+bb83cc51f8,gdd1046aedd+ded28b650d,gde0f65d7ad+941d412827,ge278dab8ac+d65b3c2b70,ge410e46f29+ed4b5058f4,gf23fb2af72+b7cae620c0,gf5e32f922b+fcb1d3bbc8,gf67bdafdda+ed4b5058f4,w.2025.16
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
measurementDriver.py
Go to the documentation of this file.
1# This file is part of pipe_tasks.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22__all__ = [
23 "SingleBandMeasurementDriverConfig",
24 "SingleBandMeasurementDriverTask",
25 "MultiBandMeasurementDriverConfig",
26 "MultiBandMeasurementDriverTask",
27]
28
29import copy
30import logging
31from abc import ABCMeta, abstractmethod
32
33import lsst.afw.detection as afwDetection
34import lsst.afw.geom as afwGeom
35import lsst.afw.image as afwImage
36import lsst.afw.math as afwMath
37import lsst.afw.table as afwTable
38import lsst.meas.algorithms as measAlgorithms
39import lsst.meas.base as measBase
40import lsst.meas.deblender as measDeblender
41import lsst.meas.extensions.scarlet as scarlet
42import lsst.pipe.base as pipeBase
43import lsst.scarlet.lite as scl
44import numpy as np
45from lsst.pex.config import Config, ConfigurableField, Field
46
47logging.basicConfig(level=logging.INFO)
48
49
51 """Base configuration for measurement driver tasks.
52
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.
57 """
58
59 doScaleVariance = Field[bool](doc="Scale variance plane using empirical noise?", default=False)
60
61 scaleVariance = ConfigurableField(
62 doc="Subtask to rescale variance plane", target=measAlgorithms.ScaleVarianceTask
63 )
64
65 doDetect = Field[bool](doc="Run the source detection algorithm?", default=True)
66
67 detection = ConfigurableField(
68 doc="Subtask to detect sources in the image", target=measAlgorithms.SourceDetectionTask
69 )
70
71 doDeblend = Field[bool](doc="Run the source deblending algorithm?", default=True)
72 # N.B. The 'deblend' configurable field should be defined in subclasses.
73
74 doMeasure = Field[bool](doc="Run the source measurement algorithm?", default=True)
75
76 measurement = ConfigurableField(
77 doc="Subtask to measure sources and populate the output catalog",
78 target=measBase.SingleFrameMeasurementTask,
79 )
80
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.",
85 default=-1,
86 )
87
88 checkUnitsParseStrict = Field[str](
89 doc="Strictness of Astropy unit compatibility check, can be 'raise', 'warn' or 'silent'",
90 default="raise",
91 )
92
93 doApCorr = Field[bool](
94 doc="Apply aperture corrections? If yes, your image must have an aperture correction map",
95 default=False,
96 )
97
98 applyApCorr = ConfigurableField(
99 doc="Subtask to apply aperture corrections",
100 target=measBase.ApplyApCorrTask,
101 )
102
103 doRunCatalogCalculation = Field[bool](doc="Run catalogCalculation task?", default=False)
104
105 catalogCalculation = ConfigurableField(
106 doc="Subtask to run catalogCalculation plugins on catalog", target=measBase.CatalogCalculationTask
107 )
108
109 doOptions = [
110 "doScaleVariance",
111 "doDetect",
112 "doDeblend",
113 "doMeasure",
114 "doApCorr",
115 "doRunCatalogCalculation",
116 ]
117
118 def validate(self):
119 """Ensure that at least one processing step is enabled."""
120 super().validate()
121
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}")
124
125
126class MeasurementDriverBaseTask(pipeBase.Task, metaclass=ABCMeta):
127 """Base class for the mid-level driver running variance scaling, detection,
128 deblending, measurement, apperture correction, and catalog calculation in
129 one go.
130
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.
139
140 Parameters
141 ----------
142 schema :
143 Schema used to create the output `~lsst.afw.table.SourceCatalog`,
144 modified in place with fields that will be written by this task.
145 peakSchema :
146 Schema of Footprint Peaks that will be passed to the deblender.
147 **kwargs :
148 Additional kwargs to pass to lsst.pipe.base.Task.__init__()
149
150 Notes
151 -----
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.
155 """
156
157 ConfigClass = MeasurementDriverBaseConfig
158 _DefaultName = "measurementDriverBase"
159 _Deblender = ""
160
161 def __init__(self, schema: afwTable.Schema = None, peakSchema: afwTable.Schema = None, **kwargs: dict):
162 super().__init__(**kwargs)
163
164 # Schema for the output catalog.
165 self.schema = schema
166
167 # Schema for deblender peaks.
168 self.peakSchema = peakSchema
169
170 # Placeholders for subclasses to populate.
172 self.scaleVariance: measAlgorithms.ScaleVarianceTask
173 self.detection: measAlgorithms.SourceDetectionTask
174 self.deblend: measDeblender.SourceDeblendTask | scarlet.ScarletDeblendTask
175 self.measurement: measBase.SingleFrameMeasurementTask
176 self.applyApCorr: measBase.ApplyApCorrTask
177 self.catalogCalculation: measBase.CatalogCalculationTask
178
179 # Store the initial Schema to use for reinitialization if necessary.
181 # To safeguard against user tampering and ensure predictable behavior,
182 # the following attribute can only be modified within the class using
183 # a controlled setter.
184 super().__setattr__("initSchema", copy.deepcopy(schema))
185
186 def __setattr__(self, name, value):
187 """Prevent external modifications of the initial Schema."""
188 if name == "initSchema":
189 raise AttributeError(f"Cannot modify {name} directly")
190 super().__setattr__(name, value)
191
192 @abstractmethod
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.
196 """
197 raise NotImplementedError("This is not implemented on the base class")
198
200 self,
201 catalog: afwTable.SourceCatalog | None,
202 ):
203 """Perform validation and adjustments of inputs without heavy
204 computation.
205
206 Parameters
207 ----------
208 catalog :
209 Catalog to be extended by the driver task.
210 """
211 # Validate the configuration before proceeding.
212 self.config.validate()
213
214 if self.config.doDetect:
215 if catalog is not None:
216 raise RuntimeError(
217 "An input catalog was given to bypass detection, but 'doDetect' is still on"
218 )
219 else:
220 if catalog is None:
221 raise RuntimeError("Cannot run without detection if no 'catalog' is provided")
222
223 def _initializeSchema(self, catalog: afwTable.SourceCatalog = None):
224 """Initialize the Schema to be used for constructing the subtasks.
225
226 Though it may seem clunky, this workaround is necessary to ensure
227 Schema consistency across all subtasks.
228
229 Parameters
230 ----------
231 catalog :
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.
235 """
236 # If the Schema has been modified (either by subtasks or externally by
237 # the user), reset it to the initial state before creating subtasks.
238 # This would be neccessary when running the same driver task multiple
239 # times with different configs/inputs.
240 if self.schema != self.initSchema:
241 self.schema = copy.deepcopy(self.initSchema)
242
243 if catalog is None:
244 if self.schema is None:
245 # Create a minimal Schema that will be extended by tasks.
246 self.schema = afwTable.SourceTable.makeMinimalSchema()
247
248 # Add coordinate error fields to avoid missing field issues.
250 else:
251 if self.schema is not None:
252 self.log.warning(
253 "Both a catalog and a Schema were provided; using the Schema from the catalog only"
254 )
255
256 # Since a catalog is provided, use its Schema as the base.
257 catalogSchema = catalog.schema
258
259 # Ensure that the Schema has coordinate error fields.
260 self._addCoordErrorFieldsIfMissing(catalogSchema)
261
262 # Create a SchemaMapper that maps from catalogSchema to a new one
263 # it will create.
264 self.mapper = afwTable.SchemaMapper(catalogSchema)
265
266 # Add everything from catalogSchema to output Schema.
267 self.mapper.addMinimalSchema(catalogSchema, True)
268
269 # Get the output Schema from the SchemaMapper and assign it as the
270 # Schema to be used for constructing the subtasks.
271 self.schema = self.mapper.getOutputSchema()
272
273 def _addCoordErrorFieldsIfMissing(self, schema: afwTable.Schema):
274 """Add coordinate error fields to the schema in-place if they are not
275 already present.
276
277 Parameters
278 ----------
279 schema :
280 Schema to be checked for coordinate error fields.
281 """
282 if not any(
283 errorField in schema.getNames()
284 for errorField in ("coord_raErr", "coord_decErr", "coord_ra_dec_Cov")
285 ):
286 afwTable.CoordKey.addErrorFields(schema)
287
288 def _makeSubtasks(self):
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"
292 ):
293 raise RuntimeError(
294 "Cannot create requested subtasks without a Schema; "
295 "ensure one is provided explicitly or via a catalog"
296 )
297
298 if self.config.doScaleVariance:
299 self.makeSubtask("scaleVariance")
300
301 if self.config.doDetect:
302 self.makeSubtask("detection", schema=self.schema)
303
304 if self.config.doDeblend:
305 self.makeSubtask("deblend", schema=self.schema, peakSchema=self.peakSchema)
306
307 if self.config.doMeasure:
308 self.makeSubtask("measurement", schema=self.schema)
309
310 if self.config.doApCorr:
311 self.makeSubtask("applyApCorr", schema=self.schema)
312
313 if self.config.doRunCatalogCalculation:
314 self.makeSubtask("catalogCalculation", schema=self.schema)
315
317 self, catalog: afwTable.SourceCatalog | None
318 ) -> afwTable.SourceCatalog | None:
319 """Ensure subtasks are properly initialized according to the
320 configuration and the provided catalog.
321
322 Parameters
323 ----------
324 catalog :
325 Optional catalog to be used for initializing the Schema and the
326 subtasks.
327
328 Returns
329 -------
330 catalog :
331 Updated catalog to be passed to the subtasks, if it was provided.
332 """
333 # Set up the Schema before creating subtasks.
334 self._initializeSchema(catalog)
335
336 # Create subtasks, passing the same Schema to each subtask's
337 # constructor that requires it.
338 self._makeSubtasks()
339
340 # Check that all units in the Schema are valid Astropy unit strings.
341 self.schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
342
343 # Adjust the catalog Schema to align with changes made by the subtasks.
344 if catalog:
345 catalog = self._updateCatalogSchema(catalog)
346
347 return catalog
348
349 def _scaleVariance(self, exposure: afwImage.Exposure, band: str = "a single"):
350 """Scale the variance plane of an exposure to match the observed
351 variance.
352
353 Parameters
354 ----------
355 exposure :
356 Exposure on which to run the variance scaling algorithm.
357 band :
358 Band associated with the exposure. Used for logging.
359 """
360 self.log.info(f"Scaling variance plane for {band} band")
361 varScale = self.scaleVariance.run(exposure.maskedImage)
362 exposure.getMetadata().add("VARIANCE_SCALE", varScale)
363
365 self, catalog: afwTable.SourceCatalog | dict[str, afwTable.SourceCatalog]
367 """Make a catalog or catalogs contiguous if they are not already.
368
369 Parameters
370 ----------
371 catalog :
372 Catalog or dictionary of catalogs with bands as keys to be made
373 contiguous.
374
375 Returns
376 -------
377 catalog :
378 Contiguous catalog or dictionary of contiguous catalogs.
379 """
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)
385 else:
386 if not catalog.isContiguous():
387 self.log.info("Catalog is not contiguous; making it contiguous")
388 catalog = catalog.copy(deep=True)
389 return catalog
390
391 def _updateCatalogSchema(self, catalog: afwTable.SourceCatalog) -> afwTable.SourceCatalog:
392 """Update the Schema of the provided catalog to incorporate changes
393 made by the configured subtasks.
394
395 Parameters
396 ----------
397 catalog :
398 Catalog to be updated with the Schema changes.
399
400 Returns
401 -------
402 updatedCatalog :
403 Catalog with the updated Schema.
404 """
405 # Create an empty catalog with the Schema required by the subtasks that
406 # are configured to run.
407 updatedCatalog = afwTable.SourceCatalog(self.schema)
408
409 # Transfer all records from the original catalog to the new catalog,
410 # using the SchemaMapper to copy values.
411 updatedCatalog.extend(catalog, mapper=self.mapper)
412
413 # Return the updated catalog, preserving the records while applying the
414 # updated Schema.
415 return updatedCatalog
416
418 self, exposure: afwImage.Exposure | afwImage.MultibandExposure, idGenerator: measBase.IdGenerator
420 """Run the detection subtask to identify sources in the image.
421
422 Parameters
423 ----------
424 exposure :
425 Exposure on which to run the detection algorithm.
426 idGenerator :
427 Generator for unique source IDs.
428
429 Returns
430 -------
431 catalog :
432 A catalog containing detected sources.
433 backgroundList :
434 A list of background models obtained from the detection process,
435 if available.
436 """
437 self.log.info(f"Running detection on a {exposure.width}x{exposure.height} pixel exposure")
438
439 # Create an empty source table with the known Schema into which
440 # detected sources will be placed next.
441 table = afwTable.SourceTable.make(self.schema, idGenerator.make_table_id_factory())
442
443 # Run the detection task on the exposure and make a source catalog.
444 detections = self.detection.run(table, exposure)
445 catalog = detections.sources
446 backgroundList = afwMath.BackgroundList()
447
448 # Get the background model from the detection task, if available.
449 if hasattr(detections, "background") and detections.background:
450 for bg in detections.background:
451 backgroundList.append(bg)
452
453 return catalog, backgroundList
454
455 @abstractmethod
456 def _deblendSources(self, *args, **kwargs):
457 """Run the deblending subtask to separate blended sources. Subclasses
458 must implement this method to handle task-specific deblending logic.
459 """
460 raise NotImplementedError("This is not implemented on the base class")
461
463 self,
464 exposure: afwImage.Exposure,
465 catalog: afwTable.SourceCatalog,
466 idGenerator: measBase.IdGenerator,
467 ):
468 """Run the measurement subtask to compute properties of sources.
469
470 Parameters
471 ----------
472 exposure :
473 Exposure on which to run the measurement algorithm.
474 catalog :
475 Catalog containing sources on which to run the measurement subtask.
476 idGenerator :
477 Generator for unique source IDs.
478 """
479 self.measurement.run(measCat=catalog, exposure=exposure, exposureId=idGenerator.catalog_id)
480
482 self, exposure: afwImage.Exposure, catalog: afwTable.SourceCatalog, idGenerator: measBase.IdGenerator
483 ):
484 """Apply aperture corrections to the catalog.
485
486 Parameters
487 ----------
488 exposure :
489 Exposure on which to apply aperture corrections.
490 catalog :
491 Catalog to be corrected using the aperture correction map from
492 the exposure.
493 idGenerator :
494 Generator for unique source IDs.
495 """
496 apCorrMap = exposure.getInfo().getApCorrMap()
497 if apCorrMap is None:
498 self.log.warning(
499 "Image does not have valid aperture correction map for catalog id "
500 f"{idGenerator.catalog_id}; skipping aperture correction"
501 )
502 else:
503 self.applyApCorr.run(catalog=catalog, apCorrMap=apCorrMap)
504
505 def _runCatalogCalculation(self, catalog: afwTable.SourceCatalog):
506 """Run the catalog calculation plugins on the catalog.
507
508 Parameters
509 ----------
510 catalog :
511 Catalog to be processed by the catalog calculation subtask.
512 """
513 self.catalogCalculation.run(catalog)
514
516 self,
517 exposure: afwImage.Exposure,
518 catalog: afwTable.SourceCatalog,
519 idGenerator: measBase.IdGenerator,
520 band: str = "a single",
522 """Process a catalog through measurement, aperture correction, and
523 catalog calculation subtasks.
524
525 Parameters
526 ----------
527 exposure :
528 Exposure associated with the catalog.
529 catalog :
530 Catalog to be processed by the subtasks.
531 idGenerator :
532 Generator for unique source IDs.
533 band :
534 Band associated with the exposure and catalog. Used for logging.
535 """
536 # Set the PSF cache capacity to cache repeated PSF evaluations at the
537 # same point coming from different measurement plugins.
538 if self.config.psfCache > 0:
539 # Set a hard limit on the number of PSFs to cache.
540 exposure.psf.setCacheCapacity(self.config.psfCache)
541 else:
542 # Auto-size the cache based on the number of measurement
543 # plugins. We assume each algorithm tries to evaluate the PSF
544 # twice, which is more than enough since many don't evaluate it
545 # at all, and there's no *good* reason for any algorithm to
546 # evaluate it more than once.
547 # (Adopted from drp_tasks/ForcedPhotCoaddTask)
548 exposure.psf.setCacheCapacity(2 * len(self.config.measurement.plugins.names))
549
550 # Measure properties of sources in the catalog.
551 if self.config.doMeasure:
552 self.log.info(f"Measuring {len(catalog)} sources in {band} band")
553 self._measureSources(exposure, catalog, idGenerator)
554
555 # Ensure contiguity again.
556 catalog = self._toContiguous(catalog)
557
558 # Apply aperture corrections to the catalog.
559 if self.config.doApCorr:
560 self.log.info(f"Applying aperture corrections to {band} band")
561 self._applyApCorr(exposure, catalog, idGenerator)
562
563 # Run catalogCalculation on the catalog.
564 if self.config.doRunCatalogCalculation:
565 self.log.info(f"Running catalog calculation on {band} band")
566 self._runCatalogCalculation(catalog)
567
568 self.log.info(
569 f"Finished processing for {band} band; output catalog has {catalog.schema.getFieldCount()} "
570 f"fields and {len(catalog)} records"
571 )
572
573 return catalog
574
575
577 """Configuration for the single-band measurement driver task."""
578
579 deblend = ConfigurableField(target=measDeblender.SourceDeblendTask, doc="Deblender for single-band data.")
580
581
583 """Mid-level driver for processing single-band data.
584
585 Offers a helper method for direct handling of raw image data in addition to
586 the standard single-band exposure.
587
588 Examples
589 --------
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:
592
593 >>> from lsst.pipe.tasks.measurementDriver import (
594 ... SingleBandMeasurementDriverConfig,
595 ... SingleBandMeasurementDriverTask,
596 ... )
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",
610 ... ]
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")
617
618 Alternatively, if an exposure is not available, the driver can also process
619 raw image data:
620
621 >>> image = ...
622 >>> mask = ...
623 >>> variance = ...
624 >>> wcs = ...
625 >>> psf = ...
626 >>> photoCalib = ...
627 >>> results = driver.runFromImage(
628 ... image, mask, variance, wcs, psf, photoCalib
629 ... )
630 >>> results.catalog.writeFits("meas_catalog.fits")
631 """
632
633 ConfigClass = SingleBandMeasurementDriverConfig
634 _DefaultName = "singleBandMeasurementDriver"
635 _Deblender = "meas_deblender"
636
637 def __init__(self, *args, **kwargs):
638 super().__init__(*args, **kwargs)
639
640 self.deblend: measDeblender.SourceDeblendTask
641
642 def run(
643 self,
644 exposure: afwImage.Exposure,
645 catalog: afwTable.SourceCatalog | None = None,
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.
650
651 Parameters
652 ----------
653 exposure :
654 The exposure on which to run the driver task.
655 catalog :
656 Catalog to be extended by the driver task. If not provided, an
657 empty catalog will be created and populated.
658 idGenerator :
659 Object that generates source IDs and provides random seeds.
660
661 Returns
662 -------
663 result : `lsst.pipe.base.Struct`
664 Results as a struct with attributes:
665
666 ``catalog``
667 Catalog containing the measured sources
668 (`~lsst.afw.table.SourceCatalog`).
669 ``backgroundList``
670 List of backgrounds (`list[~lsst.afw.math.Background]`). Only
671 populated if detection is enabled.
672 """
673
674 # Validate inputs before proceeding.
675 self._ensureValidInputs(catalog)
676
677 # Prepare the Schema and subtasks for processing.
678 catalog = self._prepareSchemaAndSubtasks(catalog)
679
680 # Generate catalog IDs consistently across subtasks.
681 if idGenerator is None:
682 idGenerator = measBase.IdGenerator()
683
684 # Scale the variance plane. If enabled, this should be done before
685 # detection.
686 if self.config.doScaleVariance:
687 self._scaleVariance(exposure)
688
689 # Detect sources in the image and populate the catalog.
690 if self.config.doDetect:
691 catalog, backgroundList = self._detectSources(exposure, idGenerator)
692 else:
693 self.log.info("Skipping detection; using detections from the provided catalog")
694 backgroundList = None
695
696 # Deblend detected sources and update the catalog.
697 if self.config.doDeblend:
698 catalog = self._deblendSources(exposure, catalog)
699 else:
700 self.log.info("Skipping deblending")
701
702 # Process catalog through measurement, aperture correction, and catalog
703 # calculation subtasks.
704 catalog = self._processCatalog(exposure, catalog, idGenerator)
705
706 return pipeBase.Struct(catalog=catalog, backgroundList=backgroundList)
707
709 self,
710 image: afwImage.MaskedImage | afwImage.Image | np.ndarray,
711 mask: afwImage.Mask | np.ndarray = None,
712 variance: afwImage.Image | np.ndarray = None,
713 wcs: afwGeom.SkyWcs = None,
714 psf: afwDetection.Psf | np.ndarray = None,
715 photoCalib: afwImage.PhotoCalib = None,
716 catalog: afwTable.SourceCatalog = None,
717 idGenerator: measBase.IdGenerator = None,
718 ) -> pipeBase.Struct:
719 """Convert image data to an `Exposure`, then run it through the
720 configured subtasks.
721
722 Parameters
723 ----------
724 image :
725 Input image data. Will be converted into an `Exposure` before
726 processing.
727 mask :
728 Mask data for the image. Used if ``image`` is a bare `array` or
729 `Image`.
730 variance :
731 Variance plane data for the image.
732 wcs :
733 World Coordinate System to associate with the exposure that will
734 be created from ``image``.
735 psf :
736 PSF model for the exposure.
737 photoCalib :
738 Photometric calibration model for the exposure.
739 catalog :
740 Catalog to be extended by the driver task. If not provided, a new
741 catalog will be created during detection and populated.
742 idGenerator :
743 Generator for unique source IDs.
744
745 Returns
746 -------
747 result : `lsst.pipe.base.Struct`
748 Results as a struct with attributes:
749
750 ``catalog``
751 Catalog containing the measured sources
752 (`~lsst.afw.table.SourceCatalog`).
753 ``backgroundList``
754 List of backgrounds (`list[~lsst.afw.math.Background]`).
755 """
756 # Convert raw image data into an Exposure.
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)
763 if isinstance(image, afwImage.Image):
764 image = afwImage.makeMaskedImage(image, mask, variance)
765
766 # By now, the input should already be - or have been converted to - a
767 # MaskedImage.
768 if isinstance(image, afwImage.MaskedImage):
769 exposure = afwImage.makeExposure(image, wcs)
770 else:
771 raise TypeError(f"Unsupported 'image' type: {type(image)}")
772
773 if psf is not None:
774 if isinstance(psf, np.ndarray):
775 # Create a FixedKernel using the array.
776 psf /= psf.sum()
777 kernel = afwMath.FixedKernel(afwImage.makeImageFromArray(psf))
778 # Create a KernelPsf using the kernel.
779 psf = afwDetection.KernelPsf(kernel)
780 elif not isinstance(psf, afwDetection.Psf):
781 raise TypeError(f"Unsupported 'psf' type: {type(psf)}")
782 exposure.setPsf(psf)
783
784 if photoCalib is not None:
785 exposure.setPhotoCalib(photoCalib)
786
787 return self.run(exposure, catalog=catalog, idGenerator=idGenerator)
788
790 self, exposure: afwImage.Exposure, catalog: afwTable.SourceCatalog
792 """Run single-band deblending given an exposure and a catalog.
793
794 Parameters
795 ----------
796 exposure :
797 Exposure on which to run the deblending algorithm.
798 catalog :
799 Catalog containing sources to be deblended.
800 """
801 self.log.info(f"Deblending using '{self._Deblender}' on {len(catalog)} detection footprints")
802 self.deblend.run(exposure=exposure, sources=catalog)
803 # The deblender may not produce contiguous catalogs; ensure
804 # contiguity for the subsequent subtasks.
805 return self._toContiguous(catalog)
806
807
809 """Configuration for the multi-band measurement driver task."""
810
812 target=scarlet.ScarletDeblendTask, doc="Scarlet deblender for multi-band data"
813 )
814
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.",
818 default=False,
819 )
820
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.",
825 default=False,
826 )
827
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.",
835 default=False,
836 )
837
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.",
841 default=True,
842 )
843
844
846 """Mid-level driver for processing multi-band data.
847
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
853 multi-band exposure.
854
855 Examples
856 --------
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
859 exposure:
860
861 >>> from lsst.afw.image import MultibandExposure
862 >>> from lsst.pipe.tasks.measurementDriver import (
863 ... MultiBandMeasurementDriverConfig,
864 ... MultiBandMeasurementDriverTask,
865 ... )
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",
881 ... ]
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", ...
887 ... )
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")
892 """
893
894 ConfigClass = MultiBandMeasurementDriverConfig
895 _DefaultName = "multiBandMeasurementDriver"
896 _Deblender = "scarlet"
897
898 def __init__(self, *args, **kwargs):
899 super().__init__(*args, **kwargs)
900
901 self.deblend: scarlet.ScarletDeblendTask
902
903 # Placeholder for the model data produced by the deblender. Caching
904 # this data has proven be useful for debugging.
905 self.modelData: scl.io.ScarletModelData
906
907 def run(
908 self,
909 mExposure: afwImage.MultibandExposure | list[afwImage.Exposure] | afwImage.Exposure,
910 refBand: str | None = None,
911 bands: list[str] | None = None,
912 catalog: afwTable.SourceCatalog = 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.
917
918 Parameters
919 ----------
920 mExposure :
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.
926 refBand :
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.
934 bands :
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.
939 catalog :
940 Catalog to be extended by the driver task. If not provided, a new
941 catalog will be created and populated.
942 idGenerator :
943 Generator for unique source IDs.
944
945 Returns
946 -------
947 result : `lsst.pipe.base.Struct`
948 Results as a struct with attributes:
949
950 ``catalogs``
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.
956 ``backgroundList``
957 List of backgrounds (`list[~lsst.afw.math.Background]`). Will
958 be None if detection is disabled.
959 ``modelData``
960 Multiband scarlet models produced during deblending
961 (`~lsst.scarlet.lite.io.ScarletModelData`). Will be None if
962 deblending is disabled.
963 """
964
965 # Validate inputs and adjust them as necessary.
966 mExposure, refBand, bands = self._ensureValidInputs(mExposure, refBand, bands, catalog)
967
968 # Prepare the Schema and subtasks for processing.
969 catalog = self._prepareSchemaAndSubtasks(catalog)
970
971 # Generate catalog IDs consistently across subtasks.
972 if idGenerator is None:
973 idGenerator = measBase.IdGenerator()
974
975 # Scale the variance plane. If enabled, this should be done before
976 # detection.
977 if self.config.doScaleVariance:
978 # Here, we iterate over references to the exposures, not copies.
979 for band in mExposure.bands:
980 self._scaleVariance(mExposure[band], band=f"'{band}'")
981
982 # Detect sources in the reference band and populate the catalog.
983 if self.config.doDetect:
984 catalog, backgroundList = self._detectSources(mExposure[refBand], idGenerator)
985 else:
986 self.log.info("Skipping detection; using detections from provided catalog")
987 backgroundList = None
988
989 # Deblend detected sources and update the catalog(s).
990 if self.config.doDeblend:
991 catalogs, self.modelData = self._deblendSources(mExposure, catalog, refBand=refBand)
992 else:
993 self.log.warning(
994 "Skipping deblending; proceeding with the provided catalog in the reference band"
995 )
996 catalogs = {refBand: catalog}
997 self.modelData = None
998
999 # Process catalog(s) through measurement, aperture correction, and
1000 # catalog calculation subtasks.
1001 for band, catalog in catalogs.items():
1002 exposure = mExposure[band]
1003 self._processCatalog(exposure, catalog, idGenerator, band=f"'{band}'")
1004
1005 return pipeBase.Struct(catalogs=catalogs, backgroundList=backgroundList, modelData=self.modelData)
1006
1008 self,
1009 mExposure: afwImage.MultibandExposure | list[afwImage.Exposure] | afwImage.Exposure,
1010 refBand: str | None,
1011 bands: list[str] | None,
1012 catalog: afwTable.SourceCatalog | None = None,
1013 ) -> tuple[afwImage.MultibandExposure, str, list[str] | None]:
1014 """Perform validation and adjustments of inputs without heavy
1015 computation.
1016
1017 Parameters
1018 ----------
1019 mExposure :
1020 Multi-band data to be processed by the driver task.
1021 refBand :
1022 Reference band to use for detection or measurements.
1023 bands :
1024 List of bands associated with the exposures in ``mExposure``.
1025 catalog :
1026 Catalog to be extended by the driver task.
1027
1028 Returns
1029 -------
1030 mExposure :
1031 Multi-band exposure to be processed by the driver task.
1032 refBand :
1033 Reference band to use for detection or measurements.
1034 bands :
1035 List of bands associated with the exposures in ``mExposure``.
1036 """
1037
1038 # Perform basic checks that are shared with all driver tasks.
1039 super()._ensureValidInputs(catalog)
1040
1041 # Multi-band-specific validation and adjustments.
1042 if isinstance(mExposure, afwImage.MultibandExposure):
1043 if bands is not None:
1044 if any(b not in mExposure.bands for b in bands):
1045 raise ValueError(
1046 "Some bands in the 'bands' list are not present in the input multi-band exposure"
1047 )
1048 self.log.info(
1049 f"Using bands {bands} out of the available {mExposure.bands} in the multi-band exposure"
1050 )
1051 elif isinstance(mExposure, list):
1052 if bands is None:
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")
1056 elif isinstance(mExposure, afwImage.Exposure):
1057 if bands is not None and len(bands) != 1:
1058 raise ValueError(
1059 "The 'bands' list, if provided, must only contain a single band "
1060 "if a single-band exposure is given"
1061 )
1062 if bands is None and refBand is None:
1063 refBand = "unknown" # Placeholder for single-band deblending
1064 bands = [refBand]
1065 elif bands is None and refBand is not None:
1066 bands = [refBand]
1067 elif bands is not None and refBand is None:
1068 refBand = bands[0]
1069 else:
1070 raise TypeError(f"Unsupported 'mExposure' type: {type(mExposure)}")
1071
1072 # Convert mExposure to a MultibandExposure object with the bands
1073 # provided.
1074 mExposure = self._buildMultibandExposure(mExposure, bands)
1075
1076 if len(mExposure.bands) == 1:
1077 # N.B. Scarlet is designed to leverage multi-band information to
1078 # differentiate overlapping sources based on their spectral and
1079 # spatial profiles. However, it can also run on a single band and
1080 # often give better results than 'meas_deblender'.
1081 self.log.info(f"Running '{self._Deblender}' in single-band mode; make sure it was intended!")
1082 if refBand is None:
1083 refBand = mExposure.bands[0]
1084 self.log.info(
1085 "No reference band provided for single-band data; "
1086 f"using the only available band ('{refBand}') as the reference band"
1087 )
1088 else:
1089 if catalog is None:
1090 if self.config.measureOnlyInRefBand:
1091 measInfo = "and everything downstream of deblending"
1092 else:
1093 measInfo = (
1094 "while subtasks downstream of deblending will be run in each of "
1095 f"the {mExposure.bands} bands"
1096 )
1097 self.log.info(f"Using '{refBand}' as the reference band for detection {measInfo}")
1098
1099 # Final sanity checks after all the adjustments above.
1100 if refBand is None:
1101 raise ValueError("Reference band must be provided for multi-band data")
1102
1103 if refBand not in mExposure.bands:
1104 raise ValueError(f"Requested band '{refBand}' is not present in the multi-band exposure")
1105
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}")
1108
1109 return mExposure, refBand, bands
1110
1112 self, mExposure: afwImage.MultibandExposure, catalog: afwTable.SourceCatalog, refBand: str
1113 ) -> tuple[dict[str, afwTable.SourceCatalog], scl.io.ScarletModelData]:
1114 """Run multi-band deblending given a multi-band exposure and a catalog.
1115
1116 Parameters
1117 ----------
1118 mExposure :
1119 Multi-band exposure on which to run the deblending algorithm.
1120 catalog :
1121 Catalog containing sources to be deblended.
1122 refBand :
1123 Reference band used for detection or the band to use for
1124 measurements if `measureOnlyInRefBand` is enabled.
1125
1126 Returns
1127 -------
1128 catalogs :
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
1132 each band.
1133 modelData :
1134 Multiband scarlet models produced during deblending.
1135 """
1136 self.log.info(f"Deblending using '{self._Deblender}' on {len(catalog)} detection footprints")
1137
1138 # Run the deblender on the multi-band exposure.
1139 catalog, modelData = self.deblend.run(mExposure, catalog)
1140
1141 # Determine which bands to process post-deblending.
1142 bands = [refBand] if self.config.measureOnlyInRefBand else mExposure.bands
1143
1144 catalogs = {band: catalog.copy(deep=True) for band in bands}
1145 for band in bands:
1146 # The footprints need to be updated for the subsequent measurement.
1147 imageForRedistribution = mExposure[band] if self.config.doConserveFlux else None
1148 scarlet.io.updateCatalogFootprints(
1149 modelData=modelData,
1150 catalog=catalogs[band], # In-place modification
1151 band=band,
1152 imageForRedistribution=imageForRedistribution,
1153 removeScarletData=self.config.removeScarletData,
1154 updateFluxColumns=self.config.updateFluxColumns,
1155 )
1156
1157 return self._toContiguous(catalogs), modelData
1158
1160 self,
1161 mExposureData: afwImage.MultibandExposure | list[afwImage.Exposure] | afwImage.Exposure,
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.
1166
1167 No conversion will be done if ``mExposureData`` is already a
1168 `MultibandExposure` except it will be subsetted to the bands provided.
1169
1170 Parameters
1171 ----------
1172 mExposureData :
1173 Input multi-band data.
1174 bands :
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
1179 available ones.
1180
1181 Returns
1182 -------
1183 mExposure :
1184 Converted multi-band exposure.
1185 """
1186 if isinstance(mExposureData, afwImage.MultibandExposure):
1187 if bands and not set(bands).issubset(mExposureData.bands):
1188 raise ValueError(
1189 f"Requested bands {bands} are not a subset of available bands: {mExposureData.bands}"
1190 )
1191 return mExposureData[bands,] if bands and len(bands) > 1 else mExposureData
1192 elif isinstance(mExposureData, list):
1193 mExposure = afwImage.MultibandExposure.fromExposures(bands, mExposureData)
1194 elif isinstance(mExposureData, afwImage.Exposure):
1195 # We still need to build a multi-band exposure to satisfy scarlet
1196 # function's signature, even when using a single band.
1197 mExposure = afwImage.MultibandExposure.fromExposures(bands, [mExposureData])
1198
1199 # Attach the WCS from each input exposure to the corresponding band of
1200 # the multi-band exposure; otherwise, their WCS will be None,
1201 # potentially causing issues downstream. Note that afwImage does not do
1202 # this when constructing a MultibandExposure from exposures.
1203 for band, exposure in zip(bands, mExposureData):
1204 mExposure[band].setWcs(exposure.getWcs())
1205
1206 return mExposure
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
Definition SkyWcs.h:117
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition Exposure.h:72
A class to represent a 2-dimensional array of pixels.
Definition Image.h:51
Represent a 2-dimensional array of bitmask pixels.
Definition Mask.h:82
A class to manipulate images, masks, and variance as a single object.
Definition MaskedImage.h:74
The photometric calibration of an exposure.
Definition PhotoCalib.h:114
A kernel created from an Image.
Definition Kernel.h:471
Defines the fields and offsets for a table.
Definition Schema.h:51
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)
_applyApCorr(self, afwImage.Exposure exposure, afwTable.SourceCatalog catalog, measBase.IdGenerator idGenerator)
_scaleVariance(self, afwImage.Exposure exposure, str band="a single")
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
afwTable.SourceCatalog|None _prepareSchemaAndSubtasks(self, afwTable.SourceCatalog|None catalog)
_initializeSchema(self, afwTable.SourceCatalog catalog=None)
_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)
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)
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.
Definition Exposure.h:484