22 """Base measurement task, which subclassed by the single frame and forced
29 from .pluginRegistry
import PluginMap
30 from .exceptions
import FatalAlgorithmError, MeasurementError
31 from .pluginsBase
import BasePluginConfig, BasePlugin
32 from .noiseReplacer
import NoiseReplacerConfig
34 __all__ = (
"BaseMeasurementPluginConfig",
"BaseMeasurementPlugin",
35 "BaseMeasurementConfig",
"BaseMeasurementTask")
38 FATAL_EXCEPTIONS = (MemoryError, FatalAlgorithmError)
42 """Base config class for all measurement plugins.
46 Most derived classes will want to override `setDefaults` in order to
47 customize the default `executionOrder`.
49 A derived class whose corresponding Plugin class implements a do `measureN`
50 method should additionally add a bool `doMeasureN` field to replace the
51 bool class attribute defined here.
54 doMeasure = lsst.pex.config.Field(dtype=bool, default=
True,
55 doc=
"whether to run this plugin in single-object mode")
61 """Base class for all measurement plugins.
65 This is class is a placeholder for future behavior which will be shared
66 only between measurement plugins and is implemented for symmetry with the
67 measurement base plugin configuration class
74 """Assign named plugins to measurement slots.
76 Slot configuration which assigns a particular named plugin to each of a set
77 of slots. Each slot allows a type of measurement to be fetched from the
78 `lsst.afw.table.SourceTable` without knowing which algorithm was used to
83 The default algorithm for each slot must be registered, even if the default
87 Field = lsst.pex.config.Field
88 centroid =
Field(dtype=str, default=
"base_SdssCentroid", optional=
True,
89 doc=
"the name of the centroiding algorithm used to set source x,y")
90 shape =
Field(dtype=str, default=
"base_SdssShape", optional=
True,
91 doc=
"the name of the algorithm used to set source moments parameters")
92 psfShape =
Field(dtype=str, default=
"base_SdssShape_psf", optional=
True,
93 doc=
"the name of the algorithm used to set PSF moments parameters")
94 apFlux =
Field(dtype=str, default=
"base_CircularApertureFlux_12_0", optional=
True,
95 doc=
"the name of the algorithm used to set the source aperture instFlux slot")
96 modelFlux =
Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
97 doc=
"the name of the algorithm used to set the source model instFlux slot")
98 psfFlux =
Field(dtype=str, default=
"base_PsfFlux", optional=
True,
99 doc=
"the name of the algorithm used to set the source psf instFlux slot")
100 gaussianFlux =
Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
101 doc=
"the name of the algorithm used to set the source Gaussian instFlux slot")
102 calibFlux =
Field(dtype=str, default=
"base_CircularApertureFlux_12_0", optional=
True,
103 doc=
"the name of the instFlux measurement algorithm used for calibration")
106 """Set up a slots in a schema following configuration directives.
110 schema : `lsst.afw.table.Schema`
111 The schema in which slots will be set up.
115 This is defined in this configuration class to support use in unit
116 tests without needing to construct an `lsst.pipe.base.Task` object.
118 aliases = schema.getAliasMap()
119 if self.
centroidcentroid
is not None:
120 aliases.set(
"slot_Centroid", self.
centroidcentroid)
121 if self.
shapeshape
is not None:
122 aliases.set(
"slot_Shape", self.
shapeshape)
123 if self.
psfShapepsfShape
is not None:
124 aliases.set(
"slot_PsfShape", self.
psfShapepsfShape)
125 if self.
apFluxapFlux
is not None:
126 aliases.set(
"slot_ApFlux", self.
apFluxapFlux)
128 aliases.set(
"slot_ModelFlux", self.
modelFluxmodelFlux)
129 if self.
psfFluxpsfFlux
is not None:
130 aliases.set(
"slot_PsfFlux", self.
psfFluxpsfFlux)
132 aliases.set(
"slot_GaussianFlux", self.
gaussianFluxgaussianFlux)
134 aliases.set(
"slot_CalibFlux", self.
calibFluxcalibFlux)
138 """Base configuration for all measurement driver tasks.
142 Subclasses should define the 'plugins' and 'undeblended' registries, e.g.
146 plugins = PluginBaseClass.registry.makeField(
149 doc="Plugins to be run and their configuration"
151 undeblended = PluginBaseClass.registry.makeField(
154 doc="Plugins to run on undeblended image"
157 where ``PluginBaseClass`` is the appropriate base class of the plugin
158 (e.g., `SingleFramePlugin` or `ForcedPlugin`).
161 slots = lsst.pex.config.ConfigField(
162 dtype=SourceSlotConfig,
163 doc=
"Mapping from algorithms to special aliases in Source."
166 doReplaceWithNoise = lsst.pex.config.Field(
167 dtype=bool, default=
True, optional=
False,
168 doc=
'When measuring, replace other detected footprints with noise?')
170 noiseReplacer = lsst.pex.config.ConfigField(
171 dtype=NoiseReplacerConfig,
172 doc=
"configuration that sets how to replace neighboring sources with noise"
174 undeblendedPrefix = lsst.pex.config.Field(
175 dtype=str, default=
"undeblended_",
176 doc=
"Prefix to give undeblended plugins"
180 lsst.pex.config.Config.validate(self)
181 if self.
slotsslots.centroid
is not None and self.
slotsslots.centroid
not in self.plugins.names:
182 raise ValueError(
"source centroid slot algorithm is not being run.")
183 if self.
slotsslots.shape
is not None and self.
slotsslots.shape
not in self.plugins.names:
184 raise ValueError(
"source shape slot algorithm '%s' is not being run." % self.
slotsslots.shape)
185 for slot
in (self.
slotsslots.psfFlux, self.
slotsslots.apFlux, self.
slotsslots.modelFlux,
186 self.
slotsslots.gaussianFlux, self.
slotsslots.calibFlux):
188 for name
in self.plugins.names:
189 if len(name) <= len(slot)
and name == slot[:len(name)]:
192 raise ValueError(
"source instFlux slot algorithm '%s' is not being run." % slot)
196 """Ultimate base class for all measurement tasks.
200 algMetadata : `lsst.daf.base.PropertyList` or `None`
201 Will be modified in-place to contain metadata about the plugins being
202 run. If `None`, an empty `~lsst.daf.base.PropertyList` will be
205 Additional arguments passed to `lsst.pipe.base.Task.__init__`.
209 This base class for `SingleFrameMeasurementTask` and
210 `ForcedMeasurementTask` mostly exists to share code between the two, and
211 generally should not be used directly.
214 ConfigClass = BaseMeasurementConfig
215 _DefaultName =
"measurement"
218 """Plugins to be invoked (`PluginMap`).
220 Initially empty, this will be populated as plugins are initialized. It
221 should be considered read-only.
225 """Metadata about active plugins (`lsst.daf.base.PropertyList`).
227 Contains additional information about active plugins to be saved with
228 the output catalog. Will be filled by subclasses.
232 super(BaseMeasurementTask, self).
__init__(**kwds)
235 if algMetadata
is None:
240 """Initialize plugins (and slots) according to configuration.
245 Keyword arguments forwarded directly to plugin constructors.
249 Derived class constructors should call this method to fill the
250 `plugins` attribute and add corresponding output fields and slot
251 aliases to the output schema.
253 In addition to the attributes added by `BaseMeasurementTask.__init__`,
254 a ``schema``` attribute holding the output schema must be present
255 before this method is called.
257 Keyword arguments are forwarded directly to plugin constructors,
258 allowing derived classes to use plugins with different signatures.
263 if self.config.slots.centroid
is not None:
264 self.
pluginsplugins[self.config.slots.centroid] =
None
266 for executionOrder, name, config, PluginClass
in sorted(self.config.plugins.apply()):
269 if hasattr(PluginClass,
"hasLogName")
and PluginClass.hasLogName:
270 self.
pluginsplugins[name] = PluginClass(config, name, metadata=self.
algMetadataalgMetadata,
271 logName=self.log.getChild(name).name, **kwds)
273 self.
pluginsplugins[name] = PluginClass(config, name, metadata=self.
algMetadataalgMetadata, **kwds)
278 if self.config.slots.centroid
is not None and self.
pluginsplugins[self.config.slots.centroid]
is None:
279 del self.
pluginsplugins[self.config.slots.centroid]
281 for executionOrder, name, config, PluginClass
in sorted(self.config.undeblended.apply()):
282 undeblendedName = self.config.undeblendedPrefix + name
287 """Call ``measure`` on all plugins and consistently handle exceptions.
291 measRecord : `lsst.afw.table.SourceRecord`
292 The record corresponding to the object being measured. Will be
293 updated in-place with the results of measurement.
295 Positional arguments forwarded to ``plugin.measure``
297 Keyword arguments. Two are handled locally:
300 Beginning execution order (inclusive). Measurements with
301 ``executionOrder`` < ``beginOrder`` are not executed. `None`
305 Ending execution order (exclusive). Measurements with
306 ``executionOrder`` >= ``endOrder`` are not executed. `None`
309 Others are forwarded to ``plugin.measure()``.
313 This method can be used with plugins that have different signatures;
314 the only requirement is that ``measRecord`` be the first argument.
315 Subsequent positional arguments and keyword arguments are forwarded
316 directly to the plugin.
318 This method should be considered "protected": it is intended for use by
319 derived classes, not users.
321 beginOrder = kwds.pop(
"beginOrder",
None)
322 endOrder = kwds.pop(
"endOrder",
None)
323 for plugin
in self.
pluginsplugins.iter():
324 if beginOrder
is not None and plugin.getExecutionOrder() < beginOrder:
326 if endOrder
is not None and plugin.getExecutionOrder() >= endOrder:
328 self.
doMeasurementdoMeasurement(plugin, measRecord, *args, **kwds)
331 """Call ``measure`` on the specified plugin.
333 Exceptions are handled in a consistent way.
337 plugin : subclass of `BasePlugin`
338 Plugin that will be executed.
339 measRecord : `lsst.afw.table.SourceRecord`
340 The record corresponding to the object being measured. Will be
341 updated in-place with the results of measurement.
343 Positional arguments forwarded to ``plugin.measure()``.
345 Keyword arguments forwarded to ``plugin.measure()``.
349 This method can be used with plugins that have different signatures;
350 the only requirement is that ``plugin`` and ``measRecord`` be the first
351 two arguments. Subsequent positional arguments and keyword arguments
352 are forwarded directly to the plugin.
354 This method should be considered "protected": it is intended for use by
355 derived classes, not users.
358 plugin.measure(measRecord, *args, **kwds)
359 except FATAL_EXCEPTIONS:
361 except MeasurementError
as error:
362 self.log.getChild(plugin.name).debug(
363 "MeasurementError in %s.measure on record %s: %s",
364 plugin.name, measRecord.getId(), error)
365 plugin.fail(measRecord, error)
366 except Exception
as error:
367 self.log.getChild(plugin.name).debug(
368 "Exception in %s.measure on record %s: %s",
369 plugin.name, measRecord.getId(), error)
370 plugin.fail(measRecord)
373 """Call ``measureN`` on all plugins and consistently handle exceptions.
377 measCat : `lsst.afw.table.SourceCatalog`
378 Catalog containing only the records for the source family to be
379 measured, and where outputs should be written.
381 Positional arguments forwarded to ``plugin.measure()``
383 Keyword arguments. Two are handled locally:
386 Beginning execution order (inclusive): Measurements with
387 ``executionOrder`` < ``beginOrder`` are not executed. `None`
390 Ending execution order (exclusive): measurements with
391 ``executionOrder`` >= ``endOrder`` are not executed. `None` for
394 Others are are forwarded to ``plugin.measure()``.
398 This method can be used with plugins that have different signatures;
399 the only requirement is that ``measRecord`` be the first argument.
400 Subsequent positional arguments and keyword arguments are forwarded
401 directly to the plugin.
403 This method should be considered "protected": it is intended for use by
404 derived classes, not users.
406 beginOrder = kwds.pop(
"beginOrder",
None)
407 endOrder = kwds.pop(
"endOrder",
None)
408 for plugin
in self.
pluginsplugins.iterN():
409 if beginOrder
is not None and plugin.getExecutionOrder() < beginOrder:
411 if endOrder
is not None and plugin.getExecutionOrder() >= endOrder:
416 """Call ``measureN`` on the specified plugin.
418 Exceptions are handled in a consistent way.
422 plugin : subclass of `BasePlugin`
423 Plugin that will be executed.
424 measCat : `lsst.afw.table.SourceCatalog`
425 Catalog containing only the records for the source family to be
426 measured, and where outputs should be written.
428 Positional arguments forwarded to ``plugin.measureN()``.
430 Keyword arguments forwarded to ``plugin.measureN()``.
434 This method can be used with plugins that have different signatures;
435 the only requirement is that the ``plugin`` and ``measCat`` be the
436 first two arguments. Subsequent positional arguments and keyword
437 arguments are forwarded directly to the plugin.
439 This method should be considered "protected": it is intended for use by
440 derived classes, not users.
443 plugin.measureN(measCat, *args, **kwds)
444 except FATAL_EXCEPTIONS:
447 except MeasurementError
as error:
448 for measRecord
in measCat:
449 self.log.getChild(plugin.name).debug(
450 "MeasurementError in %s.measureN on records %s-%s: %s",
451 plugin.name, measCat[0].getId(), measCat[-1].getId(), error)
452 plugin.fail(measRecord, error)
453 except Exception
as error:
454 for measRecord
in measCat:
455 plugin.fail(measRecord)
456 self.log.getChild(plugin.name).debug(
457 "Exception in %s.measureN on records %s-%s: %s",
458 plugin.name, measCat[0].getId(), measCat[-1].getId(), error)
def doMeasurement(self, plugin, measRecord, *args, **kwds)
def initializePlugins(self, **kwds)
def doMeasurementN(self, plugin, measCat, *args, **kwds)
def callMeasure(self, measRecord, *args, **kwds)
def __init__(self, algMetadata=None, **kwds)
def callMeasureN(self, measCat, *args, **kwds)
def setupSchema(self, schema)