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` or `int`; 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 to use the 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)
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 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 wrapAlgorithmControl(Base, Control, module=2, hasMeasureN=False)
def wrapSimpleAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, hasLogName=False, kwds)
def fail(self, measRecord, error=None)