22 r"""Base classes for single-frame measurement plugins and the associated task.
24 In single-frame measurement, we assume that detection and probably deblending
25 have already been run on the same frame, so a `~lsst.afw.table.SourceCatalog`
26 has already been created with `lsst.afw.detection.Footprint`\ s (which may be
27 "heavy" — that is, include pixel data). Measurements are generally recorded in
28 the coordinate system of the image being measured (and all slot-eligible
29 fields must be), but non-slot fields may be recorded in other coordinate
30 systems if necessary to avoid information loss (this should, of course, be
31 indicated in the field documentation).
36 from .pluginRegistry
import PluginRegistry
37 from .baseMeasurement
import (BaseMeasurementPluginConfig, BaseMeasurementPlugin,
38 BaseMeasurementConfig, BaseMeasurementTask)
39 from .noiseReplacer
import NoiseReplacer, DummyNoiseReplacer
41 __all__ = (
"SingleFramePluginConfig",
"SingleFramePlugin",
42 "SingleFrameMeasurementConfig",
"SingleFrameMeasurementTask")
46 """Base class for single-frame plugin configuration classes.
52 """Base class for single-frame measurement plugin.
56 config : `SingleFramePlugin.ConfigClass`
57 Configuration for this plugin.
59 The string with which the plugin was registered.
60 schema : `lsst.afw.table.Schema`
61 The schema for the source table . New fields are added here to
62 hold measurements produced by this plugin.
63 metadata : `lsst.daf.base.PropertySet`
64 Plugin metadata that will be attached to the output catalog
65 logName : `str`, optional
66 Name to use when logging errors.
70 New plugins can be created in Python by inheriting directly from this
71 class and implementing the `measure`, `fail` (from `BasePlugin`), and
72 optionally `__init__` and `measureN` methods. Plugins can also be defined
73 in C++ via the `WrappedSingleFramePlugin` class.
77 """Registry of subclasses of `SingleFramePlugin` (`PluginRegistry`).
80 ConfigClass = SingleFramePluginConfig
82 def __init__(self, config, name, schema, metadata, logName=None, **kwds):
83 BaseMeasurementPlugin.__init__(self, config, name, logName=logName)
86 """Measure the properties of a source on a single image.
88 The image may be from a single epoch, or it may be a coadd.
92 measRecord : `lsst.afw.table.SourceRecord`
93 Record describing the object being measured. Previously-measured
94 quantities may be retrieved from here, and it will be updated
95 in-place tih the outputs of this plugin.
96 exposure : `lsst.afw.image.ExposureF`
97 The pixel data to be measured, together with the associated PSF,
98 WCS, etc. All other sources in the image should have been replaced
99 by noise according to deblender outputs.
101 raise NotImplementedError()
104 """Measure the properties of blended sources on a single image.
106 This operates on all members of a blend family at once. The image may
107 be from a single epoch, or it may be a coadd.
111 measCat : `lsst.afw.table.SourceCatalog`
112 Catalog describing the objects (and only those objects) being
113 measured. Previously-measured quantities will be retrieved from
114 here, and it will be updated in-place with the outputs of this
116 exposure : `lsst.afw.image.ExposureF`
117 The pixel data to be measured, together with the associated PSF,
118 WCS, etc. All other sources in the image should have been replaced
119 by noise according to deblender outputs.
123 Derived classes that do not implement ``measureN`` should just inherit
124 this disabled version. Derived classes that do implement ``measureN``
125 should additionally add a bool doMeasureN config field to their config
126 class to signal that measureN-mode is available.
128 raise NotImplementedError()
132 """Config class for single frame measurement driver task.
135 plugins = SingleFramePlugin.registry.makeField(
137 default=[
"base_PixelFlags",
139 "base_NaiveCentroid",
143 "base_CircularApertureFlux",
147 "base_LocalBackground",
149 doc=
"Plugins to be run and their configuration"
151 algorithms = property(
lambda self: self.
pluginsplugins, doc=
"backwards-compatibility alias for plugins")
152 undeblended = SingleFramePlugin.registry.makeField(
155 doc=
"Plugins to run on undeblended image"
160 """A subtask for measuring the properties of sources on a single exposure.
164 schema : `lsst.afw.table.Schema`
165 Schema of the output resultant catalog. Will be updated to provide
166 fields to accept the outputs of plugins which will be executed by this
168 algMetadata : `lsst.daf.base.PropertyList`, optional
169 Used to record metadaa about algorithm execution. An empty
170 `lsst.daf.base.PropertyList` will be created if `None`.
172 Keyword arguments forwarded to `BaseMeasurementTask`.
175 ConfigClass = SingleFrameMeasurementConfig
177 NOISE_SEED_MULTIPLIER =
"NOISE_SEED_MULTIPLIER"
178 """Name by which the noise seed multiplier is recorded in metadata ('str').
181 NOISE_SOURCE =
"NOISE_SOURCE"
182 """Name by which the noise source is recorded in metadata ('str').
185 NOISE_OFFSET =
"NOISE_OFFSET"
186 """Name by which the noise offset is recorded in metadata ('str').
189 NOISE_EXPOSURE_ID =
"NOISE_EXPOSURE_ID"
190 """Name by which the noise exposire ID is recorded in metadata ('str').
193 def __init__(self, schema, algMetadata=None, **kwds):
194 super(SingleFrameMeasurementTask, self).
__init__(algMetadata=algMetadata, **kwds)
200 if 'base_Blendedness' in self.
pluginsplugins:
207 def run(self, measCat, exposure, noiseImage=None, exposureId=None, beginOrder=None, endOrder=None):
208 r"""Run single frame measurement over an exposure and source catalog.
212 measCat : `lsst.afw.table.SourceCatalog`
213 Catalog to be filled with the results of measurement. Must contain
214 all the `lsst.afw.table.SourceRecord`\ s to be measured (with
215 `lsst.afw.detection.Footprint`\ s attached), and have a schema
216 that is a superset of ``self.schema``.
217 exposure : `lsst.afw.image.ExposureF`
218 Image containing the pixel data to be measured together with
219 associated PSF, WCS, etc.
220 noiseImage : `lsst.afw.image.ImageF`, optional
221 Can be used to specify the a predictable noise replacement field
222 for testing purposes.
223 exposureId : `int`, optional
224 Unique exposure identifier used to calculate the random number
225 generator seed during noise replacement.
226 beginOrder : `float`, optional
227 Start execution order (inclusive): measurements with
228 ``executionOrder < beginOrder`` are not executed. `None` for no
230 endOrder : `float`, optional
231 Final execution order (exclusive): measurements with
232 ``executionOrder >= endOrder`` are not executed. `None` for no
236 footprints = {measRecord.getId(): (measRecord.getParent(), measRecord.getFootprint())
237 for measRecord
in measCat}
245 if self.
configconfig.doReplaceWithNoise:
247 noiseImage=noiseImage, log=self.
loglog, exposureId=exposureId)
248 algMetadata = measCat.getMetadata()
249 if algMetadata
is not None:
251 algMetadata.addString(self.
NOISE_SOURCENOISE_SOURCE, self.
configconfig.noiseReplacer.noiseSource)
252 algMetadata.addDouble(self.
NOISE_OFFSETNOISE_OFFSET, self.
configconfig.noiseReplacer.noiseOffset)
253 if exposureId
is not None:
258 self.
runPluginsrunPlugins(noiseReplacer, measCat, exposure, beginOrder, endOrder)
260 def runPlugins(self, noiseReplacer, measCat, exposure, beginOrder=None, endOrder=None):
261 r"""Call the configured measument plugins on an image.
265 noiseReplacer : `NoiseReplacer`
266 Used to fill sources not being measured with noise.
267 measCat : `lsst.afw.table.SourceCatalog`
268 Catalog to be filled with the results of measurement. Must contain
269 all the `lsst.afw.table.SourceRecord`\ s to be measured (with
270 `lsst.afw.detection.Footprint`\ s attached), and have a schema
271 that is a superset of ``self.schema``.
272 exposure : `lsst.afw.image.ExposureF`
273 Image containing the pixel data to be measured together with
274 associated PSF, WCS, etc.
275 beginOrder : `float`, optional
276 Start execution order (inclusive): measurements with
277 ``executionOrder < beginOrder`` are not executed. `None` for no
279 endOrder : `float`, optional
280 Final execution order (exclusive): measurements with
281 ``executionOrder >= endOrder`` are not executed. `None` for no
286 measParentCat = measCat.getChildren(0)
288 nMeasCat = len(measCat)
289 nMeasParentCat = len(measParentCat)
290 self.
loglog.
info(
"Measuring %d source%s (%d parent%s, %d child%s) ",
291 nMeasCat, (
"" if nMeasCat == 1
else "s"),
292 nMeasParentCat, (
"" if nMeasParentCat == 1
else "s"),
293 nMeasCat - nMeasParentCat, (
"" if nMeasCat - nMeasParentCat == 1
else "ren"))
295 for parentIdx, measParentRecord
in enumerate(measParentCat):
298 measChildCat = measCat.getChildren(measParentRecord.getId())
301 for measChildRecord
in measChildCat:
302 noiseReplacer.insertSource(measChildRecord.getId())
303 self.
callMeasurecallMeasure(measChildRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
306 self.
blendPluginblendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measChildRecord)
308 noiseReplacer.removeSource(measChildRecord.getId())
311 noiseReplacer.insertSource(measParentRecord.getId())
312 self.
callMeasurecallMeasure(measParentRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
315 self.
blendPluginblendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measParentRecord)
318 self.
callMeasureNcallMeasureN(measParentCat[parentIdx:parentIdx+1], exposure,
319 beginOrder=beginOrder, endOrder=endOrder)
320 self.
callMeasureNcallMeasureN(measChildCat, exposure, beginOrder=beginOrder, endOrder=endOrder)
321 noiseReplacer.removeSource(measParentRecord.getId())
328 for source
in measCat:
334 for source
in measCat:
335 self.
blendPluginblendPlugin.cpp.measureParentPixels(exposure.getMaskedImage(), source)
338 """Backwards-compatibility alias for `run`.
340 self.
runrun(measCat, exposure)
def doMeasurement(self, plugin, measRecord, *args, **kwds)
def initializePlugins(self, **kwds)
def callMeasure(self, measRecord, *args, **kwds)
def callMeasureN(self, measCat, *args, **kwds)
def measure(self, measCat, exposure)
def __init__(self, schema, algMetadata=None, **kwds)
string NOISE_SEED_MULTIPLIER
def runPlugins(self, noiseReplacer, measCat, exposure, beginOrder=None, endOrder=None)
def run(self, measCat, exposure, noiseImage=None, exposureId=None, beginOrder=None, endOrder=None)
def measureN(self, measCat, exposure)
def __init__(self, config, name, schema, metadata, logName=None, **kwds)
def measure(self, measRecord, exposure)