LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
sfm.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # LSST Data Management System
4 # Copyright 2008-2015 LSST Corporation.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <http://www.lsstcorp.org/LegalNotices/>.
22 #
23 """Base classes for single-frame measurement plugins and the driver task for these.
24 
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).
30 """
31 
32 from .pluginRegistry import PluginRegistry
33 from .baseMeasurement import (BaseMeasurementPluginConfig, BaseMeasurementPlugin,
34  BaseMeasurementConfig, BaseMeasurementTask)
35 from .noiseReplacer import NoiseReplacer, DummyNoiseReplacer
36 
37 __all__ = ("SingleFramePluginConfig", "SingleFramePlugin",
38  "SingleFrameMeasurementConfig", "SingleFrameMeasurementTask")
39 
40 
41 class SingleFramePluginConfig(BaseMeasurementPluginConfig):
42  """!
43  Base class for configs of single-frame plugin algorithms.
44  """
45  pass
46 
47 
48 class SingleFramePlugin(BaseMeasurementPlugin):
49  """!
50  Base class for single-frame plugin algorithms.
51 
52  New Plugins can be created in Python by inheriting directly from this class
53  and implementing measure(), fail() (from BasePlugin), and optionally __init__
54  and measureN(). Plugins can also be defined in C++ via the WrappedSingleFramePlugin
55  class.
56  """
57 
58  # All subclasses of SingleFramePlugin should be registered here
59  registry = PluginRegistry(SingleFramePluginConfig)
60  ConfigClass = SingleFramePluginConfig
61 
62  def __init__(self, config, name, schema, metadata):
63  """!
64  Initialize the measurement object.
65 
66  @param[in] config An instance of this class's ConfigClass.
67  @param[in] name The string the plugin was registered with.
68  @param[in,out] schema The Source schema. New fields should be added here to
69  hold measurements produced by this plugin.
70  @param[in] metadata Plugin metadata that will be attached to the output catalog
71  """
72  BaseMeasurementPlugin.__init__(self, config, name)
73 
74  def measure(self, measRecord, exposure):
75  """!
76  Measure the properties of a source on a single image (single-epoch image or coadd).
77 
78  @param[in,out] measRecord lsst.afw.table.SourceRecord to be filled with outputs,
79  and from which previously-measured quantities can be
80  retreived.
81 
82  @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
83  be measured and the associated Psf, Wcs, etc. All
84  other sources in the image will have been replaced by
85  noise according to deblender outputs.
86 
87  """
88  raise NotImplementedError()
89 
90  def measureN(self, measCat, exposure):
91  """!
92  Measure the properties of a group of blended sources on a single image
93  (single-epoch image or coadd).
94 
95  @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs,
96  and from which previously-measured quantities can be
97  retrieved, containing only the sources that should be
98  measured together in this call.
99 
100  @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
101  be measured and the associated Psf, Wcs, etc. Sources
102  not in the blended hierarchy to be measured will have
103  been replaced with noise using deblender outputs.
104 
105  Derived classes that do not implement measureN() should just inherit this
106  disabled version. Derived classes that do implement measureN() should additionally
107  add a bool doMeasureN config field to their config class to signal that measureN-mode
108  is available.
109  """
110  raise NotImplementedError()
111 
112 
113 class SingleFrameMeasurementConfig(BaseMeasurementConfig):
114  """!
115  Config class for single frame measurement driver task.
116  """
117 
118  plugins = SingleFramePlugin.registry.makeField(
119  multi=True,
120  default=["base_PixelFlags",
121  "base_SdssCentroid",
122  "base_GaussianCentroid",
123  "base_NaiveCentroid",
124  "base_SdssShape",
125  "base_GaussianFlux",
126  "base_PsfFlux",
127  "base_CircularApertureFlux",
128  "base_SkyCoord",
129  "base_Variance",
130  ],
131  doc="Plugins to be run and their configuration"
132  )
133  algorithms = property(lambda self: self.plugins, doc="backwards-compatibility alias for plugins")
134 
135 ## \addtogroup LSST_task_documentation
136 ## \{
137 ## \page SingleFrameMeasurementTask
138 ## \ref SingleFrameMeasurementTask_ "SingleFrameMeasurementTask"
139 ## \copybrief SingleFrameMeasurementTask
140 ## \}
141 
142 
143 class SingleFrameMeasurementTask(BaseMeasurementTask):
144  """!
145  \anchor SingleFrameMeasurementTask_
146 
147  \brief A subtask for measuring the properties of sources on a single exposure.
148 
149  The task is configured with a list of "plugins": each plugin defines the values it
150  measures (i.e. the columns in a table it will fill) and conducts that measurement
151  on each detected source (see SingleFramePlugin). The job of the
152  measurement task is to initialize the set of plugins (which includes setting up the
153  catalog schema) from their configuration, and then invoke each plugin on each
154  source.
155 
156  When run after the deblender (see lsst.meas.deblender.SourceDeblendTask),
157  SingleFrameMeasurementTask also replaces each source's neighbors with noise before
158  measuring each source, utilizing the HeavyFootprints created by the deblender (see
159  NoiseReplacer).
160 
161  SingleFrameMeasurementTask has only two methods: __init__() and run(). For configuration
162  options, see SingleFrameMeasurementConfig.
163 
164  @section meas_base_sfm_Example A complete example of using SingleFrameMeasurementTask
165 
166  The code below is in examples/runSingleFrameTask.py
167 
168  @dontinclude runSingleFrameTask.py
169 
170  See meas_algorithms_detection_Example for more information on SourceDetectionTask.
171 
172  First, import the required tasks (there are some other standard imports;
173  read the file if you're confused):
174 
175  @skip SourceDetectionTask
176  @until SingleFrameMeasurementTask
177 
178  We need to create our tasks before processing any data as the task constructors
179  can add extra columns to the schema. The most important argument we pass these to these
180  is an lsst.afw.table.Schema object, which contains information about the fields (i.e. columns) of the
181  measurement catalog we'll create, including names, types, and additional documentation.
182  Tasks that operate on a catalog are typically passed a Schema upon construction, to which
183  they add the fields they'll fill later when run. We construct a mostly empty Schema that
184  contains just the fields required for a SourceCatalog like this:
185 
186  @skipline schema
187 
188  Now we can configure and create the SourceDetectionTask:
189 
190  @until detectionTask
191 
192  We then move on to configuring the measurement task:
193 
194  @until config
195 
196  While a reasonable set of plugins is configured by default, we'll customize the list.
197  We also need to unset one of the slots at the same time, because we're
198  not running the algorithm that it's set to by default, and that would cause problems later:
199 
200  @until psfFlux
201 
202  Now, finally, we can construct the measurement task:
203 
204  @skipline measureTask
205 
206  After constructing all the tasks, we can inspect the Schema we've created:
207 
208  @skipline print schema
209 
210  All of the fields in the
211  schema can be accessed via the get() method on a record object. See afwTable for more
212  information.
213 
214  We're now ready to process the data (we could loop over multiple exposures/catalogs using the same
215  task objects). First create the output table and process the image to find sources:
216 
217  @skipline afwTable
218  @skip result
219  @until sources
220 
221  Then measure them:
222 
223  @skipline measure
224 
225  We then might plot the results (@em e.g. if you set `--ds9` on the command line)
226 
227  @skip display
228  @until RED
229 
230  and end up with something like
231 
232  @image html runSingleFrameTask-ds9.png
233  """
234 
235  ConfigClass = SingleFrameMeasurementConfig
236 
237  def __init__(self, schema, algMetadata=None, **kwds):
238  """!
239  Initialize the task. Set up the execution order of the plugins and initialize
240  the plugins, giving each plugin an opportunity to add its measurement fields to
241  the output schema and to record information in the task metadata.
242 
243  @param[in,out] schema lsst.afw.table.Schema, to be initialized to include the
244  measurement fields from the plugins already
245  @param[in,out] algMetadata lsst.daf.base.PropertyList used to record information about
246  each algorithm. An empty PropertyList will be created if None.
247  @param[in] **kwds Keyword arguments forwarded to lsst.pipe.base.Task.__init__
248  """
249  super(SingleFrameMeasurementTask, self).__init__(algMetadata=algMetadata, **kwds)
250  self.schema = schema
251  self.config.slots.setupSchema(self.schema)
252  self.initializePlugins(schema=self.schema)
253 
254  # Check to see if blendedness is one of the plugins
255  if 'base_Blendedness' in self.plugins:
256  self.doBlendedness = True
257  self.blendPlugin = self.plugins['base_Blendedness']
258  else:
259  self.doBlendedness = False
260 
261  def run(self, measCat, exposure, noiseImage=None, exposureId=None, beginOrder=None, endOrder=None):
262  """!
263  Run single frame measurement over an exposure and source catalog
264 
265  @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs. Must
266  contain all the SourceRecords to be measured (with Footprints
267  attached), and have a schema that is a superset of self.schema.
268 
269  @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
270  be measured and the associated Psf, Wcs, etc.
271  @param[in] noiseImage optional lsst.afw.image.ImageF for test which need to control
272  noiseReplacement
273  @param[in] exposureId optional unique exposureId used to calculate random number
274  generator seed in the NoiseReplacer.
275  @param[in] beginOrder beginning execution order (inclusive): measurements with
276  executionOrder < beginOrder are not executed. None for no limit.
277  @param[in] endOrder ending execution order (exclusive): measurements with
278  executionOrder >= endOrder are not executed. None for no limit.
279  """
280  # Temporary workaround for change in order of arguments; will be removed when transition
281  # from meas_algorithms to meas_base is complete.
282  if exposure.__class__.__name__ == "SourceCatalog":
283  temp = exposure
284  exposure = measCat
285  measCat = temp
286  assert measCat.getSchema().contains(self.schema)
287  footprints = {measRecord.getId(): (measRecord.getParent(), measRecord.getFootprint())
288  for measRecord in measCat}
289 
290  # noiseReplacer is used to fill the footprints with noise and save heavy footprints
291  # of the source pixels so that they can be restored one at a time for measurement.
292  # After the NoiseReplacer is constructed, all pixels in the exposure.getMaskedImage()
293  # which belong to objects in measCat will be replaced with noise
294  if self.config.doReplaceWithNoise:
295  noiseReplacer = NoiseReplacer(self.config.noiseReplacer, exposure, footprints,
296  noiseImage=noiseImage, log=self.log, exposureId=exposureId)
297  algMetadata = measCat.getMetadata()
298  if algMetadata is not None:
299  algMetadata.addInt("NOISE_SEED_MULTIPLIER", self.config.noiseReplacer.noiseSeedMultiplier)
300  algMetadata.addString("NOISE_SOURCE", self.config.noiseReplacer.noiseSource)
301  algMetadata.addDouble("NOISE_OFFSET", self.config.noiseReplacer.noiseOffset)
302  if exposureId is not None:
303  algMetadata.addLong("NOISE_EXPOSURE_ID", exposureId)
304  else:
305  noiseReplacer = DummyNoiseReplacer()
306 
307  # First, create a catalog of all parentless sources
308  # Loop through all the parent sources, first processing the children, then the parent
309  measParentCat = measCat.getChildren(0)
310 
311  self.log.info("Measuring %d sources (%d parents, %d children) "
312  % (len(measCat), len(measParentCat), len(measCat) - len(measParentCat)))
313 
314  for parentIdx, measParentRecord in enumerate(measParentCat):
315  # first get all the children of this parent, insert footprint in turn, and measure
316  measChildCat = measCat.getChildren(measParentRecord.getId())
317  # TODO: skip this loop if there are no plugins configured for single-object mode
318  for measChildRecord in measChildCat:
319  noiseReplacer.insertSource(measChildRecord.getId())
320  self.callMeasure(measChildRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
321 
322  if self.doBlendedness:
323  self.blendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measChildRecord)
324 
325  noiseReplacer.removeSource(measChildRecord.getId())
326 
327  # Then insert the parent footprint, and measure that
328  noiseReplacer.insertSource(measParentRecord.getId())
329  self.callMeasure(measParentRecord, exposure, beginOrder=beginOrder, endOrder=endOrder)
330 
331  if self.doBlendedness:
332  self.blendPlugin.cpp.measureChildPixels(exposure.getMaskedImage(), measParentRecord)
333 
334  # Finally, process both the parent and the child set through measureN
335  self.callMeasureN(measParentCat[parentIdx:parentIdx+1], exposure,
336  beginOrder=beginOrder, endOrder=endOrder)
337  self.callMeasureN(measChildCat, exposure, beginOrder=beginOrder, endOrder=endOrder)
338  noiseReplacer.removeSource(measParentRecord.getId())
339  # when done, restore the exposure to its original state
340  noiseReplacer.end()
341 
342  # Now we loop over all of the sources one more time to compute the blendedness metrics
343  # on the original image (i.e. with no noise replacement).
344  for source in measCat:
345  if self.doBlendedness:
346  self.blendPlugin.cpp.measureParentPixels(exposure.getMaskedImage(), source)
347 
348  def measure(self, measCat, exposure):
349  """!
350  Backwards-compatibility alias for run()
351  """
352  self.run(measCat, exposure)
def run
Run single frame measurement over an exposure and source catalog.
Definition: sfm.py:261
def __init__
Initialize the task.
Definition: sfm.py:237
A subtask for measuring the properties of sources on a single exposure.
Definition: sfm.py:143
Base class for configs of single-frame plugin algorithms.
Definition: sfm.py:41
Base class for single-frame plugin algorithms.
Definition: sfm.py:48
Config class for single frame measurement driver task.
Definition: sfm.py:113
def __init__
Initialize the measurement object.
Definition: sfm.py:62
def measureN
Measure the properties of a group of blended sources on a single image (single-epoch image or coadd)...
Definition: sfm.py:90
def measure
Backwards-compatibility alias for run()
Definition: sfm.py:348
def measure
Measure the properties of a source on a single image (single-epoch image or coadd).
Definition: sfm.py:74