23 from .pluginsBase
import BasePlugin
24 from .pluginRegistry
import generateAlgorithmName, register
25 from .apCorrRegistry
import addApCorrName
26 from .sfm
import SingleFramePlugin, SingleFramePluginConfig
27 from .forcedMeasurement
import ForcedPlugin, ForcedPluginConfig
29 __all__ = (
"wrapSingleFrameAlgorithm",
"wrapForcedAlgorithm",
"wrapSimpleAlgorithm",
30 "wrapAlgorithm",
"wrapAlgorithmControl",
"wrapTransform",
"GenericPlugin")
35 def __init__(self, config, name, schema, metadata, logName=None):
36 SingleFramePlugin.__init__(self, config, name, schema, metadata, logName=logName)
37 if hasattr(self,
"hasLogName")
and self.hasLogName
and logName
is not None:
38 self.
cppcpp = self.factory(config, name, schema, metadata, logName=logName)
40 self.
cppcpp = self.factory(config, name, schema, metadata)
48 def fail(self, measRecord, error=None):
49 self.
cppcpp.
fail(measRecord, error.cpp
if error
is not None else None)
54 def __init__(self, config, name, schemaMapper, metadata, logName=None):
55 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata, logName=logName)
56 if hasattr(self,
"hasLogName")
and self.hasLogName
and logName
is not None:
57 self.
cppcpp = self.factory(config, name, schemaMapper, metadata, logName=logName)
59 self.
cppcpp = self.factory(config, name, schemaMapper, metadata)
61 def measure(self, measRecord, exposure, refRecord, refWcs):
62 self.
cppcpp.measureForced(measRecord, exposure, refRecord, refWcs)
64 def measureN(self, measCat, exposure, refCat, refWcs):
65 self.
cppcpp.measureNForced(measCat, exposure, refCat, refWcs)
67 def fail(self, measRecord, error=None):
68 self.
cppcpp.
fail(measRecord, error.cpp
if error
is not None else None)
72 """Wrap a C++ algorithm's control class into a Python config class.
76 Base : `SingleFramePluginConfig` or `ForcedPluginConfig`
77 Base class for the returned config.
78 Control : pybind11-wrapped version of a C++ class.
79 Control class to be wrapped.
80 module : module, `str`, `int`, or `None`; optional
81 Either a module object, a string specifying the name of the module, or
82 an integer specifying how far back in the stack to look for the module
83 to use: ``0`` is `lsst.pex.config.wrap`, ``1`` is
84 `lsst.meas.base.wrappers`, ``2`` is the immediate caller, etc. This
85 will be used to set ``__module__`` for the new config class, and the
86 class will also be added to the module. The default is none in which
87 case module will be looked up from Control.
88 hasMeasureN : `bool`, optional
89 Whether the plugin supports fitting multiple objects at once (if so, a
90 config option to enable/disable this will be added).
94 ConfigClass : `lsst.pex.config.Config`
95 A new subclass of lsst.pex.config.Config.
99 This function is generally only called by `wrapAlgorithm`; it is unlikely
100 users will have to call it directly.
109 Control.__name__.replace(
"Control",
"Config"),
112 doc=
"whether to run this plugin in multi-object mode")}
114 ConfigClass = lsst.pex.config.makeConfigClass(Control, module=module, cls=cls)
118 ConfigClass = lsst.pex.config.makeConfigClass(Control, module=module, base=Base)
122 def wrapAlgorithm(Base, AlgClass, factory, executionOrder, name=None, Control=None,
123 ConfigClass=None, TransformClass=None, doRegister=True, shouldApCorr=False,
124 apCorrList=(), hasLogName=
False, **kwds):
125 """Wrap a C++ algorithm class to create a measurement plugin.
129 Base : `SingleFramePlugin` or `ForcedPlugin`
130 Base class for the returned Plugin.
131 AlgClass : API compatible with `SingleFrameAlgorithm` or `ForcedAlgorithm`
132 C++ algorithm class to convert. May either derive directly from
133 `SingleFrameAlgorithm` or `ForcedAlgorithm`, or be an unrelated class
134 which has the same ``measure`` and ``measureN`` signatures.
136 A callable that is used to construct an instance of ``AlgClass``. It
137 must take four arguments, either ``(config, name, schema, metadata)``
138 or ``(config, name, schemaMapper, metadata)``, depending on whether
139 the algorithm is single-frame or forced.
140 executionOrder : `float`
141 The order this plugin should be run, relative to others
142 (see `BasePlugin.getExecutionOrder`).
143 name : `str`, optional
144 String to use when registering the algorithm. Ignored if
145 ``doRegistry=False``, set to ``generateAlgorithmName(AlgClass)`` if
147 Control : Pybind11-wrapped version of a C++ class, optional
148 Pybind11-wrapped C++ Control class for the algorithm;
149 ``AlgClass.Control`` is used if `None`. Ignored if ``ConfigClass``
151 ConfigClass : subclass of `BaseMeasurementPluginConfig`
152 Python config class that wraps the C++ algorithm's pybind11-wrapped
153 Control class. If `None`, `wrapAlgorithmControl` is called to
154 generate a Config class using the ``Control`` argument.
155 TransformClass : subclass of `MeasurementTransform`, optional
156 Transformation which may be used to post-process the results of
157 measurement. If `None`, the default defined by `BasePlugin` is
159 doRegister : `bool`, optional
160 If `True` (the default), register the plugin with ``Base``'s
161 registry, allowing it to be used by measurement tasks.
162 shouldApCorr : `bool`, optional
163 Does this algorithm measure an instFlux that can be aperture
164 corrected? This is shorthand for ``apCorrList=[name]`` and is ignored
165 if ``apCorrList`` is specified.
166 apCorrList : iterable of `str`, optional
167 Field name prefixes for instFlux fields to be aperture corrected. If
168 an algorithm measures a single instFlux that should be aperture
169 corrected, then it is simpler to set ``shouldApCorr=True``. However,
170 if an algorithm produces multiple such fields, then specify
171 ``apCorrList`` instead. For example, ``modelfit_CModel`` produces
172 three such fields: ``apCorrList= ("modelfit_CModel_exp",
173 "modelfit_CModel_exp", "modelfit_CModel_def")`` If ``apCorrList`` is
174 not empty then ``shouldApCorr`` is ignored. If non-empty and
175 ``doRegister`` is `True` then the names are added to the set
176 retrieved by ``getApCorrNameSet``.
177 hasLogName : `bool`, optional
178 `True` if the C++ algorithm supports ``logName`` as a constructor
181 Additional keyword arguments passed to generateAlgorithmControl, which
184 - ``hasMeasureN``: Whether the plugin supports fitting multiple
185 objects at once ;if so, a config option to enable/disable this will
187 - ``executionOrder``: If not `None`, an override for the default
188 execution order for this plugin (the default is ``2.0``, which is
189 usually appropriate for fluxes; `bool`).
193 PluginClass : subclass of ``Base``
194 The new plugin class.
196 if ConfigClass
is None:
198 Control = AlgClass.Control
201 def getExecutionOrder():
202 return executionOrder
203 typeDict = dict(AlgClass=AlgClass, ConfigClass=ConfigClass, factory=staticmethod(factory),
204 getExecutionOrder=staticmethod(getExecutionOrder))
206 typeDict[
'getTransformClass'] = staticmethod(
lambda: TransformClass)
207 PluginClass =
type(AlgClass.__name__ + Base.__name__, (Base,), typeDict)
211 Base.registry.register(name, PluginClass)
214 PluginClass.hasLogName = hasLogName
219 hasLogName=False, **kwds):
220 """Expose a C++ ``SingleFrameAlgorithm`` class as a measurement plugin.
224 AlgClass : API compatible with `SingleFrameAlgorithm`
225 C++ algorithm class to convert. May either derive directly from
226 `SingleFrameAlgorithm` or be an unrelated class which has the same
227 ``measure``, ``measureN`` and ``fail`` signatures.
228 executionOrder : `float`
229 The order this plugin should be run, relative to others
230 (see `BasePlugin.getExecutionOrder`).
231 name : `str`, optional
232 Name to use when registering the algorithm. Ignored if
233 ``doRegistry=False``; set to ``generateAlgorithmName(AlgClass)`` if
235 needsMetadata : `bool`, optional
236 Sets whether the ``AlgClass``'s constructor should be passed a
237 `~lsst.daf.base.PropertySet` metadata argument.
238 hasMeasureN : `bool`, optional
239 Does the algorithm support simultaneous measurement of multiple
240 sources? If `True`, a `bool` ``doMeasureN`` field will be added to
241 the generated config class, and its value will be passed as the last
242 argument when calling the ``AlgClass`` constructor.
243 hasLogName : `bool`, optional
244 `True` if the C++ algorithm supports ``logName`` as a constructor
247 Additional keyword arguments are passed to the lower-level
248 `wrapAlgorithm` and `wrapAlgorithmControl` classes.
252 singleFramePlugin : subclass of `SingleFramePlugin`
253 The new measurement plugin class.
257 The first three arguments to the C++ constructor are expected to be
258 ``Control const & ctrl, std::string const & name, Schema & schema``.
260 If ``needsMetadata`` is `True`, we also append ``PropertySet & metadata``.
262 If ``hasMeasureN`` is `True`, we also append ``bool doMeasureN``.
264 If ``hasLogName`` is `True`, we also append ``std::string logName``.
266 If more than one of the above is `True`, the metadata ``PropertySet``
267 precedes the ``doMeasureN`` ``bool`` and the ``logName`` comes last of the
272 def factory(config, name, schema, metadata, **kwargs):
273 return AlgClass(config.makeControl(), name, schema, metadata, config.doMeasureN, **kwargs)
275 def factory(config, name, schema, metadata, **kwargs):
276 return AlgClass(config.makeControl(), name, schema, config.doMeasureN, **kwargs)
279 def factory(config, name, schema, metadata, **kwargs):
280 return AlgClass(config.makeControl(), name, schema, metadata, **kwargs)
282 def factory(config, name, schema, metadata, **kwargs):
283 return AlgClass(config.makeControl(), name, schema, **kwargs)
285 return wrapAlgorithm(WrappedSingleFramePlugin, AlgClass, executionOrder=executionOrder, name=name,
286 factory=factory, hasMeasureN=hasMeasureN, hasLogName=hasLogName, **kwds)
290 hasMeasureN=False, needsSchemaOnly=False, hasLogName=False, **kwds):
291 """Expose a C++ ``ForcedAlgorithm`` class as a measurement plugin.
295 AlgClass : API compatible with `ForcedAlgorithm`
296 C++ algorithm class to convert. May either derive directly from
297 `ForcedAlgorithm` or be an unrelated class which has the same
298 ``measure``, ``measureN`` and ``fail`` signatures.
299 executionOrder : `float`
300 The order this plugin should be run, relative to others
301 (see `BasePlugin.getExecutionOrder`).
302 name : `str`, optional
303 Name to use when registering the algorithm. Ignored if
304 ``doRegistry=False``; set to ``generateAlgorithmName(AlgClass)`` if
306 needsMetadata : `bool`, optional
307 Sets whether the ``AlgClass``'s constructor should be passed a
308 `~lsst.daf.base.PropertySet` metadata argument.
309 hasMeasureN : `bool`, optional
310 Does the algorithm support simultaneous measurement of multiple
311 sources? If `True`, a `bool` ``doMeasureN`` field will be added to
312 the generated config class, and its value will be passed as the last
313 argument when calling the ``AlgClass`` constructor.
314 hasLogName : `bool`, optional
315 `True` if the C++ algorithm supports ``logName`` as a constructor
317 needsSchemaOnly : `bool`, optional
318 Whether the algorithm constructor expects a Schema argument
319 (representing the output `~lsst.afw.table.Schema`) rather than the
320 full `~lsst.afw.table.SchemaMapper` (which provides access to both the
321 reference schema and the output schema).
323 Additional keyword arguments are passed to the lower-level
324 `wrapAlgorithm` and `wrapAlgorithmControl` classes.
328 forcedPlugin : subclass of `ForcedPlugin`
329 The new measurement plugin class.
333 The first two arguments to the C++ constructor are expected to be
334 ``Control const & ctrl, std::string const & name``
336 If ``needsSchemaOnly`` is `True`, then the third argument will be
337 ``Schema & schema``; otherwise, it will be ``SchemaMapper &
340 If ``needsMetadata`` is `True`, we also append ``PropertySet &
343 If ``hasMeasureN`` is `True`, we also append ``bool doMeasureN``.
345 If ``hasLogName`` is `True`, we also append ``std::string logName``.
347 If more than one of the above is `True`, the metadata ``PropertySet``
348 precedes the ``doMeasureN`` ``bool`` and the ``logName`` comes last of the
352 def extractSchemaArg(m):
353 return m.editOutputSchema()
355 def extractSchemaArg(m):
359 def factory(config, name, schemaMapper, metadata, **kwargs):
360 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper),
361 metadata, config.doMeasureN, **kwargs)
363 def factory(config, name, schemaMapper, metadata, **kwargs):
364 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper),
365 config.doMeasureN, **kwargs)
368 def factory(config, name, schemaMapper, metadata, **kwargs):
369 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper),
372 def factory(config, name, schemaMapper, metadata, **kwargs):
373 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper), **kwargs)
375 return wrapAlgorithm(WrappedForcedPlugin, AlgClass, executionOrder=executionOrder, name=name,
376 factory=factory, hasLogName=hasLogName, **kwds)
380 hasLogName=False, **kwds):
381 r"""Expose a C++ ``SimpleAlgorithm`` class as a measurement plugin.
383 ``SimpleAlgorithm``\ s are made available as both `SingleFramePlugin`\ s
384 and `ForcedPlugin`\ s.
388 AlgClass : Subclass of C++ ``SimpleAlgorithm``, or API compatible
389 Algorithm class to convert. The C++ class should be wrapped with
390 Pybind11, and must provide ``measure()``, ``measureN()`` and ``fail()`
391 signatures equivalent to ``SimpleAlgorithm``.
392 executionOrder : `float`
393 The order this plugin should be run, relative to others
394 (see `~BasePlugin.getExecutionOrder`).
395 name : `str`, optional
396 Name to use when registering the algorithm. Ignored if
397 ``doRegistry=False``; set to ``generateAlgorithmName(AlgClass)`` if
399 needsMetadata : `bool`, optional
400 Sets whether the ``AlgClass``'s constructor should be passed a
401 `~lsst.daf.base.PropertySet` metadata argument.
402 hasMeasureN : `bool`, optional
403 Does the algorithm support simultaneous measurement of multiple
404 sources? If `True`, a `bool` ``doMeasureN`` field will be added to
405 the generated config class, and its value will be passed as the last
406 argument when calling the ``AlgClass`` constructor.
407 hasLogName : `bool`, optional
408 `True` if the C++ algorithm supports ``logName`` as a constructor
411 Additional keyword arguments are passed to the lower-level
412 `wrapAlgorithm` and `wrapAlgorithmControl` classes.
416 singleFramePlugin : subclass of `SingleFramePlugin`
417 The new single frame measurement plugin class.
418 forcedPlugin : subclass of `ForcedPlugin`
419 The new forced measurement plugin class.
423 The first three arguments to the C++ constructor are expected to be
424 ``Control const & ctrl, std::string const & name, Schema & schema``.
426 If ``needsMetadata`` is `True`, we also append ``PropertySet &
429 If ``hasMeasureN`` is `True`, we also append ``bool doMeasureN``.
431 If ``hasLogName`` is `True`, we also append ``std::string logName``.
433 If more than one of the above is `True`, the metadata ``PropertySet``
434 precedes the ``doMeasureN`` ``bool`` and the ``logName`` comes last of the
438 needsMetadata=needsMetadata, hasLogName=hasLogName, **kwds),
440 needsMetadata=needsMetadata, hasLogName=hasLogName,
441 needsSchemaOnly=
True, **kwds))
445 """Modify a C++ transform to accept either a ``Config`` or a ``Control``.
447 That is, the configuration may either be provided as a (C++) ``Control``
448 object or an instance of a Python class derived from
449 `~lsst.meas.base.BasePluginConfig`.
453 transformClass : Subclass of C++ ``BaseTransform``
454 A C++ transform class, wrapped with pybind11. Its constructor must
455 take a ``Control`` object, a ``std::string``, and a
456 `~lsst.afw.table.SchemaMapper`, in that order.
457 hasLogName : `bool`, optional
460 oldInit = transformClass.__init__
462 def _init(self, ctrl, name, mapper, logName=None):
463 if hasattr(ctrl,
"makeControl"):
464 ctrl = ctrl.makeControl()
467 oldInit(self, ctrl, name, mapper)
469 transformClass.__init__ = _init
473 """Abstract base class for a generic plugin.
477 config : `lsst.pex.config.Config`
478 An instance of this class' ``ConfigClass``.
480 Name of this measurement plguin, for registering.
481 schema : `lsst.afw.table.Schema`
482 The catalog schema. New fields should be added here to
483 hold measurements produced by this plugin.
484 metadata : `lsst.daf.base.PropertySet`
485 Metadata that will be attached to the output catalog.
486 logName : `str`, optional
487 Name of log component.
491 A generic plugin can be used with the `singleFramePluginFromGeneric`
492 and/or `forcedPluginFromGeneric` wrappers to create classes that can be
493 used for single frame measurement and/or forced measurement (as
494 appropriate). The only real difference between `SingleFramePlugin` and
495 `ForcedPlugin` is the ``measure`` method; this class introduces a shared
496 signature for `measure` that, in combination with the aforementioned
497 wrappers, allows both plugin styles to share a single implementation.
499 This doesn't use `abc.ABCMeta` because I couldn't get it to work
502 Sub-classes should set `ConfigClass` and implement the `measure` and
503 `measureN` methods. They may optionally provide alternative
504 implementations for the `__init__`, `fail` and `getExecutionOrder`
507 This default implementation simply adds a field for recording
508 a fatal failure of the measurement plugin.
516 def __init__(self, config, name, schema, metadata, logName=None):
517 BasePlugin.__init__(self, config, name, logName=logName)
518 self.
_failKey_failKey = schema.addField(name +
'_flag', type=
"Flag", doc=
"Set for any fatal failure")
520 def measure(self, measRecord, exposure, center):
521 """Measure a single source.
523 It is the responsibility of this method to perform the desired
524 measurement and record the result in the `measRecord`.
528 measRecord : `lsst.afw.table.SourceRecord`
529 Catalog record for the source being measured.
530 exposure : `lsst.afw.image.Exposure`
531 Exposure on which the source is being measured.
532 center : `lsst.geom.Point2D`
533 Pixel coordinates of the object.
538 Raised if the measurement fails for a known/justifiable reason.
540 raise NotImplementedError()
542 def measureN(self, measCat, exposure, refCat, refWcs):
543 """Measure multiple sources.
545 It is the responsibility of this method to perform the desired
546 measurement and record the result in the `measCat`.
550 measCat : `lsst.afw.table.SourceCatalog`
551 Catalog for the sources being measured.
552 exposure : `lsst.afw.image.Exposure`
553 Exposure on which the source is being measured.
554 refCat : `lsst.afw.table.SourceCatalog`
556 refWcs : `lsst.afw.image.Wcs`
557 Astrometric solution for the reference image.
562 Raised if the measurement fails for a known/justifiable reason.
564 raise NotImplementedError()
566 def fail(self, measRecord, error=None):
567 """Record a measurement failure.
569 This default implementation simply records the failure in the source
574 measRecord : `lsst.afw.table.SourceRecord`
575 Catalog record for the source being measured.
577 Error causing failure, or `None`.
579 measRecord.set(self.
_failKey_failKey,
True)
583 """Produce a SingleFramePlugin subclass from this GenericPlugin class.
585 The class is also registered.
590 Name of plugin to register.
597 ConfigClass = SingleFrameFromGenericConfig
599 def __init__(self, config, name, schema, metadata, logName=None):
600 SingleFramePlugin.__init__(self, config, name, schema, metadata, logName=logName)
601 self.
_generic_generic = cls(config, name, schema, metadata)
603 def measure(self, measRecord, exposure):
604 center = measRecord.getCentroid()
607 def measureN(self, measCat, exposure, refCat, refWcs):
610 def fail(self, measRecord, error=None):
611 self.
_generic_generic.
fail(measRecord, error
if error
is not None else None)
620 return SingleFrameFromGenericPlugin
624 """Produce a ForcedPlugin subclass from this GenericPlugin class.
626 The class is also registered.
631 Name of plugin to register.
638 ConfigClass = ForcedFromGenericConfig
640 def __init__(self, config, name, schemaMapper, metadata, logName=None):
641 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata, logName=logName)
642 schema = schemaMapper.editOutputSchema()
643 self.
_generic_generic = cls(config, name, schema, metadata)
645 def measure(self, measRecord, exposure, refRecord, refWcs):
651 center = measRecord.getCentroid()
654 def measureN(self, measCat, exposure, refCat, refWcs):
657 def fail(self, measRecord, error=None):
658 self.
_generic_generic.
fail(measRecord, error
if error
is not None else None)
667 return ForcedFromGenericPlugin
def getExecutionOrder(cls)
def measure(self, measRecord, exposure, center)
def makeSingleFramePlugin(cls, name)
def makeForcedPlugin(cls, name)
def __init__(self, config, name, schema, metadata, logName=None)
def getExecutionOrder(cls)
def measureN(self, measCat, exposure, refCat, refWcs)
def fail(self, measRecord, error=None)
def measureN(self, measCat, exposure, refCat, refWcs)
def fail(self, measRecord, error=None)
def measure(self, measRecord, exposure, refRecord, refWcs)
def __init__(self, config, name, schemaMapper, metadata, logName=None)
def measure(self, measRecord, exposure)
def __init__(self, config, name, schema, metadata, logName=None)
def fail(self, measRecord, error=None)
def measureN(self, measCat, exposure)
def generateAlgorithmName(AlgClass)
def wrapForcedAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, needsSchemaOnly=False, hasLogName=False, **kwds)
def wrapAlgorithm(Base, AlgClass, factory, executionOrder, name=None, Control=None, ConfigClass=None, TransformClass=None, doRegister=True, shouldApCorr=False, apCorrList=(), hasLogName=False, **kwds)
def wrapAlgorithmControl(Base, Control, module=None, hasMeasureN=False)
def wrapTransform(transformClass, hasLogName=False)
def wrapSingleFrameAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, hasLogName=False, **kwds)
def wrapSimpleAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, hasLogName=False, **kwds)