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).
38 __all__ = (
"SingleFramePluginConfig",
"SingleFramePlugin",
39 "SingleFrameMeasurementConfig",
"SingleFrameMeasurementTask")
43 Base class for configs of single-frame plugin algorithms.
49 Base class for single-frame plugin algorithms.
51 New Plugins can be created in Python by inheriting directly from this class
52 and implementing measure(), fail() (from BasePlugin), and optionally __init__
53 and measureN(). Plugins can also be defined in C++ via the WrappedSingleFramePlugin
58 registry = PluginRegistry(SingleFramePluginConfig)
59 ConfigClass = SingleFramePluginConfig
61 def __init__(self, config, name, schema, metadata):
63 Initialize the measurement object.
65 @param[in] config An instance of this class's ConfigClass.
66 @param[in] name The string the plugin was registered with.
67 @param[in,out] schema The Source schema. New fields should be added here to
68 hold measurements produced by this plugin.
69 @param[in] metadata Plugin metadata that will be attached to the output catalog
71 BasePlugin.__init__(self)
77 Measure the properties of a source on a single image (single-epoch image or coadd).
79 @param[in,out] measRecord lsst.afw.table.SourceRecord to be filled with outputs,
80 and from which previously-measured quantities can be
83 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
84 be measured and the associated Psf, Wcs, etc. All
85 other sources in the image will have been replaced by
86 noise according to deblender outputs.
89 raise NotImplementedError()
93 Measure the properties of a group of blended sources on a single image
94 (single-epoch image or coadd).
96 @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs,
97 and from which previously-measured quantities can be
98 retrieved, containing only the sources that should be
99 measured together in this call.
101 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
102 be measured and the associated Psf, Wcs, etc. Sources
103 not in the blended hierarchy to be measured will have
104 been replaced with noise using deblender outputs.
106 Derived classes that do not implement measureN() should just inherit this
107 disabled version. Derived classes that do implement measureN() should additionally
108 add a bool doMeasureN config field to their config class to signal that measureN-mode
111 raise NotImplementedError()
115 Config class for single frame measurement driver task.
118 plugins = SingleFramePlugin.registry.makeField(
120 default=[
"base_PixelFlags",
122 "base_GaussianCentroid",
123 "base_NaiveCentroid",
129 "base_ClassificationExtendedness",
132 doc=
"Plugins to be run and their configuration"
134 algorithms = property(
lambda self: self.
plugins, doc=
"backwards-compatibility alias for plugins")
145 A subtask for measuring the properties of sources on a single exposure.
147 The task is configured with a list of "plugins": each plugin defines the values it
148 measures (i.e. the columns in a table it will fill) and conducts that measurement
149 on each detected source (see SingleFramePlugin). The job of the
150 measurement task is to initialize the set of plugins (which includes setting up the
151 catalog schema) from their configuration, and then invoke each plugin on each
154 When run after the deblender (see lsst.meas.deblender.SourceDeblendTask),
155 SingleFrameMeasurementTask also replaces each source's neighbors with noise before
156 measuring each source, utilizing the HeavyFootprints created by the deblender (see
159 SingleFrameMeasurementTask has only two methods: __init__() and run(). For configuration
160 options, see SingleFrameMeasurementConfig.
162 @section meas_base_sfm_Example A complete example of using SingleFrameMeasurementTask
164 The code below is in examples/runSingleFrameTask.py
166 @dontinclude runSingleFrameTask.py
168 See meas_algorithms_detection_Example for more information on SourceDetectionTask.
170 Import the tasks (there are some other standard imports; read the file if you're confused)
171 @skip SourceDetectionTask
172 @until SingleFrameMeasurementTask
174 We need to create our tasks before processing any data as the task constructors
175 can add extra columns to the schema. The most important argument we pass these to these
176 is a lsst.afw.table.Schema object, which contains information about the fields (i.e. columns) of the
177 measurement catalog we'll create, including names, types, and additional documentation.
178 Tasks that operate on a catalog are typically passed a Schema upon construction, to which
179 they'll add the fields they'll fill later when run. We construct a mostly empty Schema that
180 contains just the fields required for a SourceCatalog like this:
182 Now we can configure and create the SourceDetectionTask:
184 (tableVersion is a temporary parameter that will be removed after the transition from the meas_algorithms
185 Tasks to the meas_base Tasks is complete; for now, setting tableVersion=1 is necessary when using
186 meas_base Tasks via pipe_tasks drivers).
188 We then move on to configuring the measurement task:
190 While a reasonable set of plugins is configured by default, we'll customize the list.
191 We also need to unset one of the slots at the same time, because we're
192 not running the algorithm that it' set to by default, and that would cause problems later:
195 Now, finally, we can construct the measurement task:
198 After constructing all the tasks, we can inspect the Schema we've created:
199 @skipline print schema
200 All of the fields in the
201 schema can be accessed via the get() method on a record object. See afwTable for more
204 We're now ready to process the data (we could loop over multiple exposures/catalogs using the same
205 task objects). First create the output table and process the image to find sources:
213 We then might plot the results (@em e.g. if you set @c --ds9 on the command line)
216 and end up with something like
217 @image html runSingleFrameTask-ds9.png
220 ConfigClass = SingleFrameMeasurementConfig
222 def __init__(self, schema, algMetadata=None, **kwds):
224 Initialize the task. Set up the execution order of the plugins and initialize
225 the plugins, giving each plugin an opportunity to add its measurement fields to
226 the output schema and to record information in the task metadata.
228 @param[in,out] schema lsst.afw.table.Schema, to be initialized to include the
229 measurement fields from the plugins already
230 @param[in,out] algMetadata lsst.daf.base.PropertyList used to record information about
231 each algorithm. An empty PropertyList will be created if None.
232 @param[in] **kwds Keyword arguments forwarded to lsst.pipe.base.Task.__init__
234 super(SingleFrameMeasurementTask, self).
__init__(algMetadata=algMetadata, **kwds)
235 if schema.getVersion() == 0:
236 raise lsst.pex.exceptions.LogicError(
"schema must have version=1")
238 self.config.slots.setupSchema(schema)
242 if self.config.slots.centroid !=
None:
243 self.plugins[self.config.slots.centroid] =
None
245 for executionOrder, name, config, PluginClass
in sorted(self.config.plugins.apply()):
246 self.plugins[name] = PluginClass(config, name, schema=schema, metadata=self.algMetadata)
250 def run(self, measCat, exposure):
252 Run single frame measurement over an exposure and source catalog
254 @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs. Must
255 contain all the SourceRecords to be measured (with Footprints
256 attached), and have a schema that is a superset of self.schema.
258 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
259 be measured and the associated Psf, Wcs, etc.
263 if exposure.__class__.__name__ ==
"SourceCatalog":
267 assert measCat.getSchema().contains(self.
schema)
268 footprints = {measRecord.getId(): (measRecord.getParent(), measRecord.getFootprint())
269 for measRecord
in measCat}
275 if self.config.doReplaceWithNoise:
276 noiseReplacer = NoiseReplacer(self.config.noiseReplacer, exposure, footprints, log=self.log)
278 noiseReplacer = DummyNoiseReplacer()
282 measParentCat = measCat.getChildren(0)
284 self.log.info(
"Measuring %d sources (%d parents, %d children) "
285 % (len(measCat), len(measParentCat), len(measCat) - len(measParentCat)))
287 for parentIdx, measParentRecord
in enumerate(measParentCat):
289 measChildCat = measCat.getChildren(measParentRecord.getId())
291 for measChildRecord
in measChildCat:
292 noiseReplacer.insertSource(measChildRecord.getId())
293 self.callMeasure(measChildRecord, exposure)
294 noiseReplacer.removeSource(measChildRecord.getId())
296 noiseReplacer.insertSource(measParentRecord.getId())
297 self.callMeasure(measParentRecord, exposure)
299 self.callMeasureN(measParentCat[parentIdx:parentIdx+1], exposure)
300 self.callMeasureN(measChildCat, exposure)
301 noiseReplacer.removeSource(measParentRecord.getId())
307 Backwards-compatibility alias for run()
309 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).