22r"""Base classes for single-frame measurement plugins and the associated task.
24In single-frame measurement, we assume that detection and probably deblending
25have already been run on the same frame, so a `~lsst.afw.table.SourceCatalog`
26has already been created with `lsst.afw.detection.Footprint`\ s (which may be
27"heavy" — that is, include pixel data). Measurements are generally recorded in
28the coordinate system of the image being measured (and all slot-eligible
29fields must be), but non-slot fields may be recorded in other coordinate
30systems if necessary to avoid information loss (this should, of course, be
31indicated in the field documentation).
35from lsst.utils.logging
import PeriodicLogger
36from lsst.utils.timer
import timeMethod
38from .pluginRegistry
import PluginRegistry
39from .baseMeasurement
import (BaseMeasurementPluginConfig, BaseMeasurementPlugin,
40 BaseMeasurementConfig, BaseMeasurementTask)
41from .noiseReplacer
import NoiseReplacer, DummyNoiseReplacer
43__all__ = (
"SingleFramePluginConfig",
"SingleFramePlugin",
44 "SingleFrameMeasurementConfig",
"SingleFrameMeasurementTask")
48 """Base class for single-frame plugin configuration classes.
54 """Base class for single-frame measurement plugin.
58 config : `SingleFramePlugin.ConfigClass`
59 Configuration for this plugin.
61 The string
with which the plugin was registered.
63 The schema
for the source table . New fields are added here to
64 hold measurements produced by this plugin.
66 Plugin metadata that will be attached to the output catalog
67 logName : `str`, optional
68 Name to use when logging errors.
72 New plugins can be created
in Python by inheriting directly
from this
73 class and implementing the `measure`, `fail` (
from `BasePlugin`),
and
74 optionally `__init__`
and `measureN` methods. Plugins can also be defined
75 in C++ via the `WrappedSingleFramePlugin`
class.
79 """Registry of subclasses of `SingleFramePlugin` (`PluginRegistry`).
82 ConfigClass = SingleFramePluginConfig
84 def __init__(self, config, name, schema, metadata, logName=None, **kwds):
85 BaseMeasurementPlugin.__init__(self, config, name, logName=logName)
88 """Measure the properties of a source on a single image.
90 The image may be from a single epoch,
or it may be a coadd.
95 Record describing the object being measured. Previously-measured
96 quantities may be retrieved
from here,
and it will be updated
97 in-place tih the outputs of this plugin.
98 exposure : `lsst.afw.image.ExposureF`
99 The pixel data to be measured, together
with the associated PSF,
100 WCS, etc. All other sources
in the image should have been replaced
101 by noise according to deblender outputs.
103 raise NotImplementedError()
106 """Measure the properties of blended sources on a single image.
108 This operates on all members of a blend family at once. The image may
109 be from a single epoch,
or it may be a coadd.
114 Catalog describing the objects (
and only those objects) being
115 measured. Previously-measured quantities will be retrieved
from
116 here,
and it will be updated
in-place
with the outputs of this
118 exposure : `lsst.afw.image.ExposureF`
119 The pixel data to be measured, together
with the associated PSF,
120 WCS, etc. All other sources
in the image should have been replaced
121 by noise according to deblender outputs.
125 Derived classes that do
not implement ``measureN`` should just inherit
126 this disabled version. Derived classes that do implement ``measureN``
127 should additionally add a bool doMeasureN config field to their config
128 class to signal that
measureN-mode
is available.
130 raise NotImplementedError()
134 """Config class for single frame measurement driver task.
137 plugins = SingleFramePlugin.registry.makeField(
139 default=[
"base_PixelFlags",
141 "base_NaiveCentroid",
145 "base_CircularApertureFlux",
149 "base_LocalBackground",
151 doc=
"Plugins to be run and their configuration"
153 algorithms = property(
lambda self: self.
pluginsplugins, doc=
"backwards-compatibility alias for plugins")
154 undeblended = SingleFramePlugin.registry.makeField(
157 doc=
"Plugins to run on undeblended image"
162 doc=
"Interval (in seconds) to log messages (at VERBOSE level) while running measurement plugins.",
163 deprecated=
"This field is no longer used and will be removed in v25.",
168 """A subtask for measuring the properties of sources on a single exposure.
173 Schema of the output resultant catalog. Will be updated to provide
174 fields to accept the outputs of plugins which will be executed by this
177 Used to record metadaa about algorithm execution. An empty
180 Keyword arguments forwarded to `BaseMeasurementTask`.
183 ConfigClass = SingleFrameMeasurementConfig
185 NOISE_SEED_MULTIPLIER = "NOISE_SEED_MULTIPLIER"
186 """Name by which the noise seed multiplier is recorded in metadata ('str').
189 NOISE_SOURCE = "NOISE_SOURCE"
190 """Name by which the noise source is recorded in metadata ('str').
193 NOISE_OFFSET = "NOISE_OFFSET"
194 """Name by which the noise offset is recorded in metadata ('str').
197 NOISE_EXPOSURE_ID = "NOISE_EXPOSURE_ID"
198 """Name by which the noise exposire ID is recorded in metadata ('str').
201 def __init__(self, schema, algMetadata=None, **kwds):
202 super(SingleFrameMeasurementTask, self).
__init__(algMetadata=algMetadata, **kwds)
204 self.config.slots.setupSchema(self.
schemaschema)
208 if 'base_Blendedness' in self.
pluginsplugins:
215 def run(self, measCat, exposure, noiseImage=None, exposureId=None, beginOrder=None, endOrder=None):
216 r"""Run single frame measurement over an exposure and source catalog.
221 Catalog to be filled with the results of measurement. Must contain
224 that
is a superset of ``self.
schemaschema``.
225 exposure : `lsst.afw.image.ExposureF`
226 Image containing the pixel data to be measured together
with
227 associated PSF, WCS, etc.
228 noiseImage : `lsst.afw.image.ImageF`, optional
229 Can be used to specify the a predictable noise replacement field
230 for testing purposes.
231 exposureId : `int`, optional
232 Unique exposure identifier used to calculate the random number
233 generator seed during noise replacement.
234 beginOrder : `float`, optional
235 Start execution order (inclusive): measurements
with
236 ``executionOrder < beginOrder`` are
not executed. `
None`
for no
238 endOrder : `float`, optional
239 Final execution order (exclusive): measurements
with
240 ``executionOrder >= endOrder`` are
not executed. `
None`
for no
244 footprints = {measRecord.getId(): (measRecord.getParent(), measRecord.getFootprint())
245 for measRecord
in measCat}
253 if self.config.doReplaceWithNoise:
254 noiseReplacer =
NoiseReplacer(self.config.noiseReplacer, exposure, footprints,
255 noiseImage=noiseImage, log=self.log, exposureId=exposureId)
256 algMetadata = measCat.getMetadata()
257 if algMetadata
is not None:
258 algMetadata.addInt(self.
NOISE_SEED_MULTIPLIERNOISE_SEED_MULTIPLIER, self.config.noiseReplacer.noiseSeedMultiplier)
259 algMetadata.addString(self.
NOISE_SOURCENOISE_SOURCE, self.config.noiseReplacer.noiseSource)
260 algMetadata.addDouble(self.
NOISE_OFFSETNOISE_OFFSET, self.config.noiseReplacer.noiseOffset)
261 if exposureId
is not None:
266 self.
runPluginsrunPlugins(noiseReplacer, measCat, exposure, beginOrder, endOrder)
268 def runPlugins(self, noiseReplacer, measCat, exposure, beginOrder=None, endOrder=None):
269 r"""Call the configured measument plugins on an image.
273 noiseReplacer : `NoiseReplacer`
274 Used to fill sources not being measured
with noise.
276 Catalog to be filled
with the results of measurement. Must contain
279 that
is a superset of ``self.
schemaschema``.
280 exposure : `lsst.afw.image.ExposureF`
281 Image containing the pixel data to be measured together
with
282 associated PSF, WCS, etc.
283 beginOrder : `float`, optional
284 Start execution order (inclusive): measurements
with
285 ``executionOrder < beginOrder`` are
not executed. `
None`
for no
287 endOrder : `float`, optional
288 Final execution order (exclusive): measurements
with
289 ``executionOrder >= endOrder`` are
not executed. `
None`
for no
294 measParentCat = measCat.getChildren(0)
296 nMeasCat = len(measCat)
297 nMeasParentCat = len(measParentCat)
298 self.log.
info(
"Measuring %d source%s (%d parent%s, %d child%s) ",
299 nMeasCat, (
"" if nMeasCat == 1
else "s"),
300 nMeasParentCat, (
"" if nMeasParentCat == 1
else "s"),
301 nMeasCat - nMeasParentCat, (
"" if nMeasCat - nMeasParentCat == 1
else "ren"))
304 periodicLog = PeriodicLogger(self.log)
306 childrenIter = measCat.getChildren([measParentRecord.getId()
for measParentRecord
in measParentCat])
307 for parentIdx, (measParentRecord, measChildCat)
in enumerate(zip(measParentCat, childrenIter)):
312 for measChildRecord
in measChildCat:
313 noiseReplacer.insertSource(measChildRecord.getId())
314 self.
callMeasurecallMeasure(measChildRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
317 self.
blendPluginblendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measChildRecord)
319 noiseReplacer.removeSource(measChildRecord.getId())
322 noiseReplacer.insertSource(measParentRecord.getId())
323 self.
callMeasurecallMeasure(measParentRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
326 self.
blendPluginblendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measParentRecord)
329 self.
callMeasureNcallMeasureN(measParentCat[parentIdx:parentIdx+1], exposure,
330 beginOrder=beginOrder, endOrder=endOrder)
331 self.
callMeasureNcallMeasureN(measChildCat, exposure, beginOrder=beginOrder, endOrder=endOrder)
332 noiseReplacer.removeSource(measParentRecord.getId())
334 periodicLog.log(
"Measurement complete for %d parents (and their children) out of %d",
335 parentIdx + 1, nMeasParentCat)
342 for sourceIndex, source
in enumerate(measCat):
346 periodicLog.log(
"Undeblended measurement complete for %d sources out of %d",
347 sourceIndex + 1, nMeasCat)
352 for source
in measCat:
353 self.
blendPluginblendPlugin.cpp.measureParentPixels(exposure.getMaskedImage(), source)
356 """Backwards-compatibility alias for `run`.
358 self.runrun(measCat, exposure)
Defines the fields and offsets for a table.
Record class that contains measurements made on a single exposure.
Class for storing ordered metadata with comments.
Class for storing generic metadata.
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)