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.
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 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)
379 def wrapSimpleAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False,
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 = 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`. 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):
646 center = exposure.getWcs().skyToPixel(refWcs.pixelToSky(refRecord.getCentroid()))
649 def measureN(self, measCat, exposure, refCat, refWcs):
652 def fail(self, measRecord, error=None):
653 self.
_generic.
fail(measRecord, error
if error
is not None else None)
662 return ForcedFromGenericPlugin
def measureN(self, measCat, exposure, refCat, refWcs)
def makeSingleFramePlugin(cls, name)
def fail(self, measRecord, error=None)
def generateAlgorithmName(AlgClass)
def wrapForcedAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, needsSchemaOnly=False, hasLogName=False, kwds)
def wrapAlgorithmControl(Base, Control, module=None, hasMeasureN=False)
def getExecutionOrder(cls)
def measure(self, measRecord, exposure)
def __init__(self, config, name, schema, metadata, logName=None)
def fail(self, measRecord, error=None)
def getExecutionOrder(cls)
def measureN(self, measCat, exposure)
def __init__(self, config, name, schemaMapper, metadata, logName=None)
def measure(self, measRecord, exposure, refRecord, refWcs)
def wrapAlgorithm(Base, AlgClass, factory, executionOrder, name=None, Control=None, ConfigClass=None, TransformClass=None, doRegister=True, shouldApCorr=False, apCorrList=(), hasLogName=False, kwds)
def wrapTransform(transformClass, hasLogName=False)
def __init__(self, config, name, schema, metadata, logName=None)
def measure(self, measRecord, exposure, center)
def measureN(self, measCat, exposure, refCat, refWcs)
def makeForcedPlugin(cls, name)
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)
def fail(self, measRecord, error=None)