23 """Base and utility classes for measurement frameworks
25 This includes base classes for plugins and tasks and utility classes such as PluginMap
26 that are shared by the single-frame measurement framework and the forced measurement framework.
35 from .baseLib
import *
36 from .noiseReplacer
import *
38 FATAL_EXCEPTIONS = (MemoryError, FatalAlgorithmError)
41 """Generate a string name for an algorithm class that strips away terms that are generally redundant
42 while (hopefully) remaining easy to trace to the code.
44 The returned name will cobmine the package name, with any "lsst" and/or "meas" prefix removed,
45 with the class name, with any "Algorithm" suffix removed. For instance,
46 lsst.meas.base.SdssShapeAlgorithm becomes "base_SdssShape".
48 name = AlgClass.__name__
49 pkg = AlgClass.__module__
50 name = name.replace(
"Algorithm",
"")
51 terms = pkg.split(
".")
52 if terms[-1].endswith(
"Lib"):
54 if terms[0] ==
"lsst":
56 if terms[0] ==
"meas":
58 if name.lower().startswith(terms[-1].lower()):
60 return "%s_%s" % (
"_".join(terms), name)
65 "base_PixelFlags_flag_bad":
"flags.pixel.bad",
66 "base_PixelFlags_flag_edge":
"flags.pixel.edge",
67 "base_PixelFlags_flag_interpolated":
"flags.pixel.interpolated.any",
68 "base_PixelFlags_flag_saturated":
"flags.pixel.saturated.any",
69 "base_PixelFlags_flag_cr":
"flags.pixel.cr.any",
70 "base_PixelFlags_flag_interpolatedCenter":
"flags.pixel.interpolated.center",
71 "base_PixelFlags_flag_saturatedCenter":
"flags.pixel.saturated.center",
72 "base_PixelFlags_flag_crCenter":
"flags.pixel.cr.center",
78 if name
in _flagMap.keys():
79 _flags.append(_flagMap[name])
86 Add aliases to flag fields that an algorithm depends on.
88 When an algorithm relies on the slot centroid or shape as an input, it can fail (or be unreliable)
89 simply because the algorithm it depends on failed. In that case, we already have a flag field
90 that indicates why the algorithm failed (the slot failure flag), but it's not obviously connected
91 to the failure of the dependent algorithm. This function adds aliases to the appropriate slot flag
92 for C++ algorithms that depend on that slot, in the namespace of the dependent algorithm.
94 @param[in] AlgClass Swig-wrappped C++ algorithm class; must have a class attribute Input that
95 constains the type of Input object it requires.
96 @param[in] name Name of the dependent algorithm
97 @param[out] schema Schema to add the aliases to
100 if issubclass(AlgClass.Input, FootprintCentroidInput):
101 schema.getAliasMap().set(
"%s_flag_badCentroid" % name,
"slot_Centroid_flag")
102 if issubclass(AlgClass.Input, FootprintCentroidShapeInput):
103 schema.getAliasMap().set(
"%s_flag_badShape" % name,
"slot_Shape_flag")
108 Base class for plugin registries
110 The Plugin class allowed in the registry is defined on the ctor of the registry
111 Single-frame and forced plugins have different registries.
116 Class used as the actual element in the registry
118 Rather than constructing a Plugin instance, it returns a tuple
119 of (runlevel, name, config, PluginClass), which can then
120 be sorted before the plugins are instantiated.
123 __slots__ =
"PluginClass",
"name"
127 Initialize registry with Plugin Class
140 Register a Plugin class with the given name.
142 The same Plugin may be registered multiple times with different names; this can
143 be useful if we often want to run it multiple times with different configuration.
145 The name will be used as a prefix for all fields produced by the Plugin, and it
146 should generally contain the name of the Plugin or Algorithm class itself
147 as well as enough of the namespace to make it clear where to find the code.
149 lsst.pex.config.Registry.register(self, name, self.
Configurable(name, PluginClass))
151 def makeField(self, doc, default=None, optional=False, multi=False):
152 return lsst.pex.config.RegistryField(doc, self, default, optional, multi)
157 A Python decorator that registers a class, using the given name, in its base class's PluginRegistry.
160 @register("base_TransformedCentroid")
161 class ForcedTransformedCentroidPlugin(ForcedPlugin):
166 class ForcedTransformedCentroidPlugin(ForcedPlugin):
168 @ForcedPlugin.registry.register("base_TransformedCentroid", ForcedTransformedCentroidPlugin)
171 def decorate(PluginClass):
172 PluginClass.registry.register(name, PluginClass)
179 Map of plugins to be run for a task
181 We assume Plugins are added to the PluginMap according to their executionOrder, so this
182 class doesn't actually do any of the sorting (though it does have to maintain that order).
186 """Call each plugin in the map which has a measure() method
188 for plugin
in self.itervalues():
189 if plugin.config.doMeasure:
193 """Call each plugin in the map which has a measureN() method
195 for plugin
in self.itervalues():
196 if plugin.config.doMeasureN:
201 Base class measurement Plugin config classes.
203 Most derived classes will want to override setDefaults() in order to customize
204 the default exceutionOrder.
206 A derived class whose corresponding Plugin class implements measureN() should
207 additionally add a bool doMeasureN field to replace the bool class attribute
211 executionOrder = lsst.pex.config.Field(
212 dtype=float, default=2.0,
213 doc=
"""Sets the relative order of plugins (smaller numbers run first).
215 In general, the following values should be used (intermediate values
216 are also allowed, but should be avoided unless they are needed):
217 0.0 ------ centroids and other algorithms that require only a Footprint and
219 1.0 ------ shape measurements and other algorithms that require
220 getCentroid() to return a good centroid in addition to a
221 Footprint and its Peaks.
222 2.0 ------ flux algorithms that require both getShape() and getCentroid()
223 in addition to the Footprint and its Peaks
224 3.0 ------ Corrections applied to fluxes (i.e. aperture corrections, tying
225 model to PSF fluxes). All flux measurements should have an
226 executionOrder < 3.0, while all algorithms that rely on corrected
227 fluxes (i.e. classification) should have executionOrder > 3.0.
232 doMeasure = lsst.pex.config.Field(dtype=bool, default=
True,
233 doc=
"whether to run this plugin in single-object mode")
239 Base class for measurement plugins.
241 This is the base class for SingleFramePlugin and ForcedPlugin; derived classes should inherit
245 def fail(self, measRecord, error=None):
247 Record a failure of the measure or measureN() method.
249 When measure() raises an exception, the measurement framework
250 will call fail() to allow the plugin to set its failure flag
251 field(s). When measureN() raises an exception, fail() will be
252 called repeatedly with all the records that were being
255 If the exception is a MeasurementError, it will be passed as
256 the error argument; in all other cases the error argument will
257 be None, and the failure will be logged by the measurement
258 framework as a warning.
260 traceback.print_exc()
261 message = (
"The algorithm '%s' thinks it cannot fail, but it did; "
262 "please report this as a bug (the full traceback is above)."
263 % self.__class__.__name__)
264 raise NotImplementedError(message)
268 Slot configuration which assigns a particular named plugin to each of a set of
269 slots. Each slot allows a type of measurement to be fetched from the SourceTable
270 without knowing which algorithm was used to produced the data.
272 NOTE: the default algorithm for each slot must be registered, even if the default is not used.
275 centroid = lsst.pex.config.Field(dtype=str, default=
"base_SdssCentroid", optional=
True,
276 doc=
"the name of the centroiding algorithm used to set source x,y")
277 shape = lsst.pex.config.Field(dtype=str, default=
"base_SdssShape", optional=
True,
278 doc=
"the name of the algorithm used to set source moments parameters")
279 apFlux = lsst.pex.config.Field(dtype=str, default=
"base_SincFlux", optional=
True,
280 doc=
"the name of the algorithm used to set the source aperture flux slot")
281 modelFlux = lsst.pex.config.Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
282 doc=
"the name of the algorithm used to set the source model flux slot")
283 psfFlux = lsst.pex.config.Field(dtype=str, default=
"base_PsfFlux", optional=
True,
284 doc=
"the name of the algorithm used to set the source psf flux slot")
285 instFlux = lsst.pex.config.Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
286 doc=
"the name of the algorithm used to set the source inst flux slot")
289 """Convenience method to setup a Schema's slots according to the config definition.
291 This is defined in the Config class to support use in unit tests without needing
292 to construct a Task object.
294 aliases = schema.getAliasMap()
296 if self.
shape is not None: aliases.set(
"slot_Shape", self.
shape)
297 if self.
apFlux is not None: aliases.set(
"slot_ApFlux", self.
apFlux)
299 if self.
psfFlux is not None: aliases.set(
"slot_PsfFlux", self.
psfFlux)
305 Base config class for all measurement driver tasks.
308 slots = lsst.pex.config.ConfigField(
309 dtype = SourceSlotConfig,
310 doc=
"Mapping from algorithms to special aliases in Source."
313 doReplaceWithNoise = lsst.pex.config.Field(dtype=bool, default=
True, optional=
False,
314 doc=
'When measuring, replace other detected footprints with noise?')
316 noiseReplacer = lsst.pex.config.ConfigField(
317 dtype=NoiseReplacerConfig,
318 doc=
"configuration that sets how to replace neighboring sources with noise"
322 lsst.pex.config.Config.validate(self)
323 if self.slots.centroid
is not None and self.slots.centroid
not in self.plugins.names:
324 raise ValueError(
"source centroid slot algorithm is not being run.")
325 if self.slots.shape
is not None and self.slots.shape
not in self.plugins.names:
326 raise ValueError(
"source shape slot algorithm '%s' is not being run." % self.slots.shape)
327 for slot
in (self.slots.psfFlux, self.slots.apFlux, self.slots.modelFlux, self.slots.instFlux):
329 for name
in self.plugins.names:
330 if len(name) <= len(slot)
and name == slot[:len(name)]:
333 raise ValueError(
"source flux slot algorithm '%s' is not being run." % slot)
344 Ultimate base class for all measurement tasks.
346 This base class for SingleFrameMeasurementTask and ForcedMeasurementTask mostly exists to share
347 code between the two, and generally should not be used directly.
350 ConfigClass = BaseMeasurementConfig
351 _DefaultName =
"measurement"
356 Constructor; only called by derived classes.
358 @param[in] algMetadata An lsst.daf.base.PropertyList that will be filled with metadata
359 about the plugins being run. If None, an empty PropertyList will
361 @param[in] **kwds Additional arguments passed to lsst.pipe.base.Task.__init__.
363 This attaches two public attributes to the class for use by derived classes and parent tasks:
364 - plugins: an empty PluginMap, which will eventually contain all active plugins that will by
365 invoked by the run() method (to be filled by subclasses). This should be considered read-only.
366 - algMetadata: a lsst.daf.base.PropertyList that will contain additional information about the
367 active plugins to be saved with the output catalog (to be filled by subclasses).
369 super(BaseMeasurementTask, self).
__init__(**kwds)
371 if algMetadata
is None:
377 Call the measure() method on all plugins, handling exceptions in a consistent way.
379 @param[in,out] measRecord lsst.afw.table.SourceRecord that corresponds to the object being
380 measured, and where outputs should be written.
381 @param[in] *args Positional arguments forwarded to Plugin.measure()
382 @param[in] **kwds Keyword arguments forwarded to Plugin.measure()
384 This method can be used with plugins that have different signatures; the only requirement is that
385 'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
386 forwarded directly to the plugin.
388 This method should be considered "protected"; it is intended for use by derived classes, not users.
390 for plugin
in self.plugins.iter():
392 plugin.measure(measRecord, *args, **kwds)
393 except FATAL_EXCEPTIONS:
395 except MeasurementError
as error:
396 plugin.fail(measRecord, error)
397 except Exception
as error:
398 self.log.warn(
"Error in %s.measure on record %s: %s"
399 % (plugin.name, measRecord.getId(), error))
400 plugin.fail(measRecord)
404 Call the measureN() method on all plugins, handling exceptions in a consistent way.
406 @param[in,out] measCat lsst.afw.table.SourceCatalog containing records for just
407 the source family to be measured, and where outputs should
409 @param[in] *args Positional arguments forwarded to Plugin.measure()
410 @param[in] **kwds Keyword arguments forwarded to Plugin.measure()
412 This method can be used with plugins that have different signatures; the only requirement is that
413 'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
414 forwarded directly to the plugin.
416 This method should be considered "protected"; it is intended for use by derived classes, not users.
418 for plugin
in self.plugins.iterN():
420 plugin.measureN(measCat, *args, **kwds)
421 except FATAL_EXCEPTIONS:
423 except MeasurementError
as error:
424 for measRecord
in measCat:
425 plugin.fail(measRecord, error)
426 except Exception
as error:
427 for measRecord
in measCat:
428 plugin.fail(measRecord)
429 self.log.warn(
"Error in %s.measureN on records %s-%s: %s"
430 % (plugin.name, measCat[0].getId(), measCat[-1].getId(), error))
def __init__
Constructor; only called by derived classes.
Slot configuration which assigns a particular named plugin to each of a set of slots.
Base class measurement Plugin config classes.
def generateAlgorithmName
Class for storing ordered metadata with comments.
Class used as the actual element in the registry.
Ultimate base class for all measurement tasks.
Base class for plugin registries.
def __init__
Initialize registry with Plugin Class.
def addDependencyFlagAliases
Add aliases to flag fields that an algorithm depends on.
def callMeasure
Call the measure() method on all plugins, handling exceptions in a consistent way.
def register
Register a Plugin class with the given name.
Base class for measurement plugins.
Map of plugins to be run for a task.
Base config class for all measurement driver tasks.
def register
A Python decorator that registers a class, using the given name, in its base class's PluginRegistry...
def callMeasureN
Call the measureN() method on all plugins, handling exceptions in a consistent way.
def fail
Record a failure of the measure or measureN() method.