23from .pluginsBase
import BasePlugin
24from .pluginRegistry
import generateAlgorithmName, register
25from .apCorrRegistry
import addApCorrName
26from .sfm
import SingleFramePlugin, SingleFramePluginConfig
27from .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.
cpp = self.factory(config, name, schema, metadata, logName=logName)
40 self.
cpp = self.factory(config, name, schema, metadata)
48 def fail(self, measRecord, error=None):
49 self.
cpp.
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.
cpp = self.factory(config, name, schemaMapper, metadata, logName=logName)
59 self.
cpp = self.factory(config, name, schemaMapper, metadata)
61 def measure(self, measRecord, exposure, refRecord, refWcs):
62 self.
cpp.measureForced(measRecord, exposure, refRecord, refWcs)
64 def measureN(self, measCat, exposure, refCat, refWcs):
65 self.
cpp.measureNForced(measCat, exposure, refCat, refWcs)
67 def fail(self, measRecord, error=None):
68 self.
cpp.
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
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"),
111 {
"doMeasureN": lsst.pex.config.Field(dtype=bool, default=
True,
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)
122def 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
147 Control : Pybind11-wrapped version of a C++
class, optional
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
235 needsMetadata : `bool`, optional
236 Sets whether the ``AlgClass``
's constructor should be passed a
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
306 needsMetadata : `bool`, optional
307 Sets whether the ``AlgClass``
's constructor should be passed a
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
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
399 needsMetadata : `bool`, optional
400 Sets whether the ``AlgClass``
's constructor should be passed a
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
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
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.
482 The catalog schema. New fields should be added here to
483 hold measurements produced by this plugin.
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 = 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`.
529 Catalog record
for the source being measured.
531 Exposure on which the source
is being measured.
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`.
551 Catalog
for the sources being measured.
553 Exposure on which the source
is being measured.
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
575 Catalog record
for the source being measured.
577 Error causing failure,
or `
None`.
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 = 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.
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 = 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.
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)