23 """Base classes for single-frame measurement plugins and the driver task for these.
25 In single-frame measurement, we assume that detection and probably deblending have already been run on
26 the same frame, so a SourceCatalog has already been created with Footprints (which may be HeavyFootprints).
27 Measurements are generally recorded in the coordinate system of the image being measured (and all
28 slot-eligible fields must be), but non-slot fields may be recorded in other coordinate systems if necessary
29 to avoid information loss (this should, of course, be indicated in the field documentation).
32 import lsst.pipe.base
as pipeBase
34 from .pluginRegistry
import PluginRegistry
35 from .baseMeasurement
import (BaseMeasurementPluginConfig, BaseMeasurementPlugin,
36 BaseMeasurementConfig, BaseMeasurementTask)
37 from .noiseReplacer
import NoiseReplacer, DummyNoiseReplacer
39 __all__ = (
"SingleFramePluginConfig",
"SingleFramePlugin",
40 "SingleFrameMeasurementConfig",
"SingleFrameMeasurementTask")
45 Base class for configs of single-frame plugin algorithms.
52 Base class for single-frame plugin algorithms.
54 New Plugins can be created in Python by inheriting directly from this class
55 and implementing measure(), fail() (from BasePlugin), and optionally __init__
56 and measureN(). Plugins can also be defined in C++ via the WrappedSingleFramePlugin
61 registry = PluginRegistry(SingleFramePluginConfig)
62 ConfigClass = SingleFramePluginConfig
64 def __init__(self, config, name, schema, metadata, logName=None, **kwds):
66 Initialize the measurement object.
68 @param[in] config An instance of this class's ConfigClass.
69 @param[in] name The string the plugin was registered with.
70 @param[in,out] schema The Source schema. New fields should be added here to
71 hold measurements produced by this plugin.
72 @param[in] metadata Plugin metadata that will be attached to the output catalog
73 @param[in] logName May include logName to use for this plugin
75 BaseMeasurementPlugin.__init__(self, config, name, logName=logName)
79 Measure the properties of a source on a single image (single-epoch image or coadd).
81 @param[in,out] measRecord lsst.afw.table.SourceRecord to be filled with outputs,
82 and from which previously-measured quantities can be
85 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
86 be measured and the associated Psf, Wcs, etc. All
87 other sources in the image will have been replaced by
88 noise according to deblender outputs.
91 raise NotImplementedError()
95 Measure the properties of a group of blended sources on a single image
96 (single-epoch image or coadd).
98 @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs,
99 and from which previously-measured quantities can be
100 retrieved, containing only the sources that should be
101 measured together in this call.
103 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
104 be measured and the associated Psf, Wcs, etc. Sources
105 not in the blended hierarchy to be measured will have
106 been replaced with noise using deblender outputs.
108 Derived classes that do not implement measureN() should just inherit this
109 disabled version. Derived classes that do implement measureN() should additionally
110 add a bool doMeasureN config field to their config class to signal that measureN-mode
113 raise NotImplementedError()
118 Config class for single frame measurement driver task.
121 plugins = SingleFramePlugin.registry.makeField(
123 default=[
"base_PixelFlags",
125 "base_GaussianCentroid",
126 "base_NaiveCentroid",
130 "base_CircularApertureFlux",
135 doc=
"Plugins to be run and their configuration"
137 algorithms = property(
lambda self: self.
plugins, doc=
"backwards-compatibility alias for plugins")
138 undeblended = SingleFramePlugin.registry.makeField(
141 doc=
"Plugins to run on undeblended image"
154 \anchor SingleFrameMeasurementTask_
156 \brief A subtask for measuring the properties of sources on a single exposure.
158 The task is configured with a list of "plugins": each plugin defines the values it
159 measures (i.e. the columns in a table it will fill) and conducts that measurement
160 on each detected source (see SingleFramePlugin). The job of the
161 measurement task is to initialize the set of plugins (which includes setting up the
162 catalog schema) from their configuration, and then invoke each plugin on each
165 When run after the deblender (see lsst.meas.deblender.SourceDeblendTask),
166 SingleFrameMeasurementTask also replaces each source's neighbors with noise before
167 measuring each source, utilizing the HeavyFootprints created by the deblender (see
170 SingleFrameMeasurementTask has only two methods: __init__() and run(). For configuration
171 options, see SingleFrameMeasurementConfig.
173 @section meas_base_sfm_Example A complete example of using SingleFrameMeasurementTask
175 The code below is in examples/runSingleFrameTask.py
177 @dontinclude runSingleFrameTask.py
179 See meas_algorithms_detection_Example for more information on SourceDetectionTask.
181 First, import the required tasks (there are some other standard imports;
182 read the file if you're confused):
184 @skip SourceDetectionTask
185 @until SingleFrameMeasurementTask
187 We need to create our tasks before processing any data as the task constructors
188 can add extra columns to the schema. The most important argument we pass these to these
189 is an lsst.afw.table.Schema object, which contains information about the fields (i.e. columns) of the
190 measurement catalog we'll create, including names, types, and additional documentation.
191 Tasks that operate on a catalog are typically passed a Schema upon construction, to which
192 they add the fields they'll fill later when run. We construct a mostly empty Schema that
193 contains just the fields required for a SourceCatalog like this:
197 Now we can configure and create the SourceDetectionTask:
201 We then move on to configuring the measurement task:
205 While a reasonable set of plugins is configured by default, we'll customize the list.
206 We also need to unset one of the slots at the same time, because we're
207 not running the algorithm that it's set to by default, and that would cause problems later:
211 Now, finally, we can construct the measurement task:
213 @skipline measureTask
215 After constructing all the tasks, we can inspect the Schema we've created:
217 @skipline print schema
219 All of the fields in the
220 schema can be accessed via the get() method on a record object. See afwTable for more
223 We're now ready to process the data (we could loop over multiple exposures/catalogs using the same
224 task objects). First create the output table and process the image to find sources:
234 We then might plot the results (@em e.g. if you set `--ds9` on the command line)
239 and end up with something like
241 @image html runSingleFrameTask-ds9.png
244 ConfigClass = SingleFrameMeasurementConfig
246 NOISE_SEED_MULTIPLIER =
"NOISE_SEED_MULTIPLIER"
247 NOISE_SOURCE =
"NOISE_SOURCE"
248 NOISE_OFFSET =
"NOISE_OFFSET"
249 NOISE_EXPOSURE_ID =
"NOISE_EXPOSURE_ID"
251 def __init__(self, schema, algMetadata=None, **kwds):
253 Initialize the task. Set up the execution order of the plugins and initialize
254 the plugins, giving each plugin an opportunity to add its measurement fields to
255 the output schema and to record information in the task metadata.
257 @param[in,out] schema lsst.afw.table.Schema, to be initialized to include the
258 measurement fields from the plugins already
259 @param[in,out] algMetadata lsst.daf.base.PropertyList used to record information about
260 each algorithm. An empty PropertyList will be created if None.
261 @param[in] **kwds Keyword arguments forwarded to lsst.pipe.base.Task.__init__
264 super(SingleFrameMeasurementTask, self).
__init__(algMetadata=algMetadata, **kwds)
266 self.config.slots.setupSchema(self.
schema)
267 self.initializePlugins(schema=self.
schema)
270 if 'base_Blendedness' in self.plugins:
277 def run(self, measCat, exposure, noiseImage=None, exposureId=None, beginOrder=None, endOrder=None):
279 Run single frame measurement over an exposure and source catalog
281 @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs. Must
282 contain all the SourceRecords to be measured (with Footprints
283 attached), and have a schema that is a superset of self.schema.
285 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
286 be measured and the associated Psf, Wcs, etc.
287 @param[in] noiseImage optional lsst.afw.image.ImageF for test which need to control
289 @param[in] exposureId optional unique exposureId used to calculate random number
290 generator seed in the NoiseReplacer.
291 @param[in] beginOrder beginning execution order (inclusive): measurements with
292 executionOrder < beginOrder are not executed. None for no limit.
293 @param[in] endOrder ending execution order (exclusive): measurements with
294 executionOrder >= endOrder are not executed. None for no limit.
296 assert measCat.getSchema().contains(self.
schema)
297 footprints = {measRecord.getId(): (measRecord.getParent(), measRecord.getFootprint())
298 for measRecord
in measCat}
304 if self.config.doReplaceWithNoise:
305 noiseReplacer = NoiseReplacer(self.config.noiseReplacer, exposure, footprints,
306 noiseImage=noiseImage, log=self.log, exposureId=exposureId)
307 algMetadata = measCat.getMetadata()
308 if algMetadata
is not None:
310 algMetadata.addString(self.
NOISE_SOURCE, self.config.noiseReplacer.noiseSource)
311 algMetadata.addDouble(self.
NOISE_OFFSET, self.config.noiseReplacer.noiseOffset)
312 if exposureId
is not None:
315 noiseReplacer = DummyNoiseReplacer()
317 self.
runPlugins(noiseReplacer, measCat, exposure, beginOrder, endOrder)
319 def runPlugins(self, noiseReplacer, measCat, exposure, beginOrder=None, endOrder=None):
320 """Function which calls the defined measument plugins on an exposure
324 noiseReplacer : lsst.meas.base.NoiseReplacer
325 noiseReplacer to fill sources not being measured with noise.
327 measCat : lsst.afw.table.SourceCatalog
328 SourceCatalog to be filled with outputs. Must contain all the SourceRecords to be measured (with
329 Footprints attached), and have a schema that is a superset of self.schema.
331 exposure : lsst.afw.image.ExposureF
332 Exposure contaning the pixel data to be measured and the associated PSF, WCS, etc.
335 beginning execution order (inclusive): measurements with executionOrder < beginOrder are not
336 executed. None for no limit.
339 ending execution order (exclusive): measurements with executionOrder >= endOrder are not
340 executed. None for no limit.
344 measParentCat = measCat.getChildren(0)
346 nMeasCat = len(measCat)
347 nMeasParentCat = len(measParentCat)
348 self.log.info(
"Measuring %d source%s (%d parent%s, %d child%s) ",
349 nMeasCat, (
"" if nMeasCat == 1
else "s"),
350 nMeasParentCat, (
"" if nMeasParentCat == 1
else "s"),
351 nMeasCat - nMeasParentCat, (
"" if nMeasCat - nMeasParentCat == 1
else "ren"))
353 for parentIdx, measParentRecord
in enumerate(measParentCat):
355 measChildCat = measCat.getChildren(measParentRecord.getId())
357 for measChildRecord
in measChildCat:
358 noiseReplacer.insertSource(measChildRecord.getId())
359 self.callMeasure(measChildRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
362 self.blendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measChildRecord)
364 noiseReplacer.removeSource(measChildRecord.getId())
367 noiseReplacer.insertSource(measParentRecord.getId())
368 self.callMeasure(measParentRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
371 self.blendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measParentRecord)
374 self.callMeasureN(measParentCat[parentIdx:parentIdx+1], exposure,
375 beginOrder=beginOrder, endOrder=endOrder)
376 self.callMeasureN(measChildCat, exposure, beginOrder=beginOrder, endOrder=endOrder)
377 noiseReplacer.removeSource(measParentRecord.getId())
383 for source
in measCat:
384 for plugin
in self.undeblendedPlugins.iter():
385 self.doMeasurement(plugin, source, exposure)
389 for source
in measCat:
390 self.blendPlugin.cpp.measureParentPixels(exposure.getMaskedImage(), source)
394 Backwards-compatibility alias for run()
396 self.
run(measCat, exposure)
def run
Run single frame measurement over an exposure and source catalog.
def __init__
Initialize the task.
A subtask for measuring the properties of sources on a single exposure.
string NOISE_SEED_MULTIPLIER
Base class for configs of single-frame plugin algorithms.
Base class for single-frame plugin algorithms.
Config class for single frame measurement driver task.
def __init__
Initialize the measurement object.
def measureN
Measure the properties of a group of blended sources on a single image (single-epoch image or coadd)...
def measure
Backwards-compatibility alias for run()
def measure
Measure the properties of a source on a single image (single-epoch image or coadd).