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 from .pluginRegistry
import PluginRegistry
33 from .baseMeasurement
import BasePluginConfig, BasePlugin, BaseMeasurementConfig, BaseMeasurementTask
34 from .noiseReplacer
import NoiseReplacer, DummyNoiseReplacer
36 __all__ = (
"SingleFramePluginConfig",
"SingleFramePlugin",
37 "SingleFrameMeasurementConfig",
"SingleFrameMeasurementTask")
41 Base class for configs of single-frame plugin algorithms.
47 Base class for single-frame plugin algorithms.
49 New Plugins can be created in Python by inheriting directly from this class
50 and implementing measure(), fail() (from BasePlugin), and optionally __init__
51 and measureN(). Plugins can also be defined in C++ via the WrappedSingleFramePlugin
56 registry = PluginRegistry(SingleFramePluginConfig)
57 ConfigClass = SingleFramePluginConfig
59 def __init__(self, config, name, schema, metadata):
61 Initialize the measurement object.
63 @param[in] config An instance of this class's ConfigClass.
64 @param[in] name The string the plugin was registered with.
65 @param[in,out] schema The Source schema. New fields should be added here to
66 hold measurements produced by this plugin.
67 @param[in] metadata Plugin metadata that will be attached to the output catalog
69 BasePlugin.__init__(self, config, name)
73 Measure the properties of a source on a single image (single-epoch image or coadd).
75 @param[in,out] measRecord lsst.afw.table.SourceRecord to be filled with outputs,
76 and from which previously-measured quantities can be
79 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
80 be measured and the associated Psf, Wcs, etc. All
81 other sources in the image will have been replaced by
82 noise according to deblender outputs.
85 raise NotImplementedError()
89 Measure the properties of a group of blended sources on a single image
90 (single-epoch image or coadd).
92 @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs,
93 and from which previously-measured quantities can be
94 retrieved, containing only the sources that should be
95 measured together in this call.
97 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
98 be measured and the associated Psf, Wcs, etc. Sources
99 not in the blended hierarchy to be measured will have
100 been replaced with noise using deblender outputs.
102 Derived classes that do not implement measureN() should just inherit this
103 disabled version. Derived classes that do implement measureN() should additionally
104 add a bool doMeasureN config field to their config class to signal that measureN-mode
107 raise NotImplementedError()
111 Config class for single frame measurement driver task.
114 plugins = SingleFramePlugin.registry.makeField(
116 default=[
"base_PixelFlags",
118 "base_GaussianCentroid",
119 "base_NaiveCentroid",
123 "base_CircularApertureFlux",
124 "base_ClassificationExtendedness",
127 doc=
"Plugins to be run and their configuration"
129 algorithms = property(
lambda self: self.
plugins, doc=
"backwards-compatibility alias for plugins")
140 \anchor SingleFrameMeasurementTask_
142 \brief A subtask for measuring the properties of sources on a single exposure.
144 The task is configured with a list of "plugins": each plugin defines the values it
145 measures (i.e. the columns in a table it will fill) and conducts that measurement
146 on each detected source (see SingleFramePlugin). The job of the
147 measurement task is to initialize the set of plugins (which includes setting up the
148 catalog schema) from their configuration, and then invoke each plugin on each
151 When run after the deblender (see lsst.meas.deblender.SourceDeblendTask),
152 SingleFrameMeasurementTask also replaces each source's neighbors with noise before
153 measuring each source, utilizing the HeavyFootprints created by the deblender (see
156 SingleFrameMeasurementTask has only two methods: __init__() and run(). For configuration
157 options, see SingleFrameMeasurementConfig.
159 @section meas_base_sfm_Example A complete example of using SingleFrameMeasurementTask
161 The code below is in examples/runSingleFrameTask.py
163 @dontinclude runSingleFrameTask.py
165 See meas_algorithms_detection_Example for more information on SourceDetectionTask.
167 First, import the required tasks (there are some other standard imports;
168 read the file if you're confused):
170 @skip SourceDetectionTask
171 @until SingleFrameMeasurementTask
173 We need to create our tasks before processing any data as the task constructors
174 can add extra columns to the schema. The most important argument we pass these to these
175 is an lsst.afw.table.Schema object, which contains information about the fields (i.e. columns) of the
176 measurement catalog we'll create, including names, types, and additional documentation.
177 Tasks that operate on a catalog are typically passed a Schema upon construction, to which
178 they add the fields they'll fill later when run. We construct a mostly empty Schema that
179 contains just the fields required for a SourceCatalog like this:
183 Now we can configure and create the SourceDetectionTask:
187 We then move on to configuring the measurement task:
191 While a reasonable set of plugins is configured by default, we'll customize the list.
192 We also need to unset one of the slots at the same time, because we're
193 not running the algorithm that it's set to by default, and that would cause problems later:
197 Now, finally, we can construct the measurement task:
199 @skipline measureTask
201 After constructing all the tasks, we can inspect the Schema we've created:
203 @skipline print schema
205 All of the fields in the
206 schema can be accessed via the get() method on a record object. See afwTable for more
209 We're now ready to process the data (we could loop over multiple exposures/catalogs using the same
210 task objects). First create the output table and process the image to find sources:
220 We then might plot the results (@em e.g. if you set `--ds9` on the command line)
225 and end up with something like
227 @image html runSingleFrameTask-ds9.png
230 ConfigClass = SingleFrameMeasurementConfig
232 def __init__(self, schema, algMetadata=None, **kwds):
234 Initialize the task. Set up the execution order of the plugins and initialize
235 the plugins, giving each plugin an opportunity to add its measurement fields to
236 the output schema and to record information in the task metadata.
238 @param[in,out] schema lsst.afw.table.Schema, to be initialized to include the
239 measurement fields from the plugins already
240 @param[in,out] algMetadata lsst.daf.base.PropertyList used to record information about
241 each algorithm. An empty PropertyList will be created if None.
242 @param[in] **kwds Keyword arguments forwarded to lsst.pipe.base.Task.__init__
244 super(SingleFrameMeasurementTask, self).
__init__(algMetadata=algMetadata, **kwds)
246 self.config.slots.setupSchema(self.
schema)
247 self.initializePlugins(schema=self.
schema)
248 self.makeSubtask(
"applyApCorr", schema=self.
schema)
250 def run(self, measCat, exposure, noiseImage=None, exposureId=None, beginOrder=None, endOrder=None,
253 Run single frame measurement over an exposure and source catalog
255 @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs. Must
256 contain all the SourceRecords to be measured (with Footprints
257 attached), and have a schema that is a superset of self.schema.
259 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
260 be measured and the associated Psf, Wcs, etc.
261 @param[in] noiseImage optional lsst.afw.image.ImageF for test which need to control
263 @param[in] exposureId optional unique exposureId used to calculate random number
264 generator seed in the NoiseReplacer.
265 @param[in] beginOrder beginning execution order (inclusive): measurements with
266 executionOrder < beginOrder are not executed. None for no limit.
267 @param[in] endOrder ending execution order (exclusive): measurements with
268 executionOrder >= endOrder are not executed. None for no limit.
269 @param[in] allowApCorr allow application of aperture correction?
273 if exposure.__class__.__name__ ==
"SourceCatalog":
277 assert measCat.getSchema().contains(self.
schema)
278 footprints = {measRecord.getId(): (measRecord.getParent(), measRecord.getFootprint())
279 for measRecord
in measCat}
285 if self.config.doReplaceWithNoise:
286 noiseReplacer = NoiseReplacer(self.config.noiseReplacer, exposure, footprints,
287 noiseImage=noiseImage, log=self.log, exposureId=exposureId)
288 algMetadata = measCat.getMetadata()
289 if not algMetadata
is None:
290 algMetadata.addInt(
"NOISE_SEED_MULTIPLIER", self.config.noiseReplacer.noiseSeedMultiplier)
291 algMetadata.addString(
"NOISE_SOURCE", self.config.noiseReplacer.noiseSource)
292 algMetadata.addDouble(
"NOISE_OFFSET", self.config.noiseReplacer.noiseOffset)
293 if not exposureId
is None:
294 algMetadata.addLong(
"NOISE_EXPOSURE_ID", exposureId)
296 noiseReplacer = DummyNoiseReplacer()
300 measParentCat = measCat.getChildren(0)
302 self.log.info(
"Measuring %d sources (%d parents, %d children) "
303 % (len(measCat), len(measParentCat), len(measCat) - len(measParentCat)))
305 for parentIdx, measParentRecord
in enumerate(measParentCat):
307 measChildCat = measCat.getChildren(measParentRecord.getId())
309 for measChildRecord
in measChildCat:
310 noiseReplacer.insertSource(measChildRecord.getId())
311 self.callMeasure(measChildRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
312 noiseReplacer.removeSource(measChildRecord.getId())
314 noiseReplacer.insertSource(measParentRecord.getId())
315 self.callMeasure(measParentRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
317 self.callMeasureN(measParentCat[parentIdx:parentIdx+1], exposure,
318 beginOrder=beginOrder, endOrder=endOrder)
319 self.callMeasureN(measChildCat, exposure, beginOrder=beginOrder, endOrder=endOrder)
320 noiseReplacer.removeSource(measParentRecord.getId())
325 self._applyApCorrIfWanted(
327 apCorrMap = exposure.getInfo().getApCorrMap(),
333 Backwards-compatibility alias for run()
335 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.
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).