LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
LSSTDataManagementBasePackage
sfm.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # LSST Data Management System
4 # Copyright 2008, 2009, 2010, 2014 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 import lsst.pex.config
33 import lsst.pipe.base
34 import lsst.daf.base
35 
36 from .base import *
37 
38 __all__ = ("SingleFramePluginConfig", "SingleFramePlugin",
39  "SingleFrameMeasurementConfig", "SingleFrameMeasurementTask")
40 
41 class SingleFramePluginConfig(BasePluginConfig):
42  """!
43  Base class for configs of single-frame plugin algorithms.
44  """
45  pass
46 
47 class SingleFramePlugin(BasePlugin):
48  """!
49  Base class for single-frame plugin algorithms.
50 
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
54  class.
55  """
56 
57  # All subclasses of SingleFramePlugin should be registered here
58  registry = PluginRegistry(SingleFramePluginConfig)
59  ConfigClass = SingleFramePluginConfig
60 
61  def __init__(self, config, name, schema, metadata):
62  """!
63  Initialize the measurement object.
64 
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
70  """
71  BasePlugin.__init__(self)
72  self.config = config
73  self.name = name
74 
75  def measure(self, measRecord, exposure):
76  """!
77  Measure the properties of a source on a single image (single-epoch image or coadd).
78 
79  @param[in,out] measRecord lsst.afw.table.SourceRecord to be filled with outputs,
80  and from which previously-measured quantities can be
81  retreived.
82 
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.
87 
88  """
89  raise NotImplementedError()
90 
91  def measureN(self, measCat, exposure):
92  """!
93  Measure the properties of a group of blended sources on a single image
94  (single-epoch image or coadd).
95 
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.
100 
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.
105 
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
109  is available.
110  """
111  raise NotImplementedError()
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_NaiveFlux",
127  "base_PsfFlux",
128  "base_SincFlux",
129  "base_ClassificationExtendedness",
130  "base_SkyCoord",
131  ],
132  doc="Plugins to be run and their configuration"
133  )
134  algorithms = property(lambda self: self.plugins, doc="backwards-compatibility alias for plugins")
135 
136 ## @addtogroup LSST_task_documentation
137 ## @{
138 ## @page pageSingleFrameMeasurementTask SingleFrameMeasurementTask
139 ## SingleFrameMeasurementTask
140 ## @copybrief SingleFrameMeasurementTask
141 ## @}
142 
143 class SingleFrameMeasurementTask(BaseMeasurementTask):
144  """!
145  A subtask for measuring the properties of sources on a single exposure.
146 
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
152  source.
153 
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
157  NoiseReplacer).
158 
159  SingleFrameMeasurementTask has only two methods: __init__() and run(). For configuration
160  options, see SingleFrameMeasurementConfig.
161 
162  @section meas_base_sfm_Example A complete example of using SingleFrameMeasurementTask
163 
164  The code below is in examples/runSingleFrameTask.py
165 
166  @dontinclude runSingleFrameTask.py
167 
168  See meas_algorithms_detection_Example for more information on SourceDetectionTask.
169 
170  Import the tasks (there are some other standard imports; read the file if you're confused)
171  @skip SourceDetectionTask
172  @until SingleFrameMeasurementTask
173 
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:
181  @skipline schema
182  Now we can configure and create the SourceDetectionTask:
183  @until detectionTask
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).
187 
188  We then move on to configuring the measurement task:
189  @until config
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:
193  @until flags
194 
195  Now, finally, we can construct the measurement task:
196  @until measureTask
197 
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
202  information.
203 
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:
206  @skipline afwTable
207  @skip result
208  @until sources
209 
210  Then measure them:
211  @skipline measure
212 
213  We then might plot the results (@em e.g. if you set @c --ds9 on the command line)
214  @skip display
215  @until RED
216  and end up with something like
217  @image html runSingleFrameTask-ds9.png
218  """
219 
220  ConfigClass = SingleFrameMeasurementConfig
221 
222  def __init__(self, schema, algMetadata=None, **kwds):
223  """!
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.
227 
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__
233  """
234  super(SingleFrameMeasurementTask, self).__init__(algMetadata=algMetadata, **kwds)
235  if schema.getVersion() == 0:
236  raise lsst.pex.exceptions.LogicError("schema must have version=1")
237  self.schema = schema
238  self.config.slots.setupSchema(schema)
239  # Make a place at the beginning for the centroid plugin to run first (because it's an OrderedDict,
240  # adding an empty element in advance means it will get run first when it's reassigned to the
241  # actual Plugin).
242  if self.config.slots.centroid != None:
243  self.plugins[self.config.slots.centroid] = None
244  # Init the plugins, sorted by execution order. At the same time add to the schema
245  for executionOrder, name, config, PluginClass in sorted(self.config.plugins.apply()):
246  self.plugins[name] = PluginClass(config, name, schema=schema, metadata=self.algMetadata)
247 
248 
249 
250  def run(self, measCat, exposure):
251  """!
252  Run single frame measurement over an exposure and source catalog
253 
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.
257 
258  @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
259  be measured and the associated Psf, Wcs, etc.
260  """
261  # Temporary workaround for change in order of arguments; will be removed when transition
262  # from meas_algorithms to meas_base is complete.
263  if exposure.__class__.__name__ == "SourceCatalog":
264  temp = exposure
265  exposure = measCat
266  measCat = temp
267  assert measCat.getSchema().contains(self.schema)
268  footprints = {measRecord.getId(): (measRecord.getParent(), measRecord.getFootprint())
269  for measRecord in measCat}
270 
271  # noiseReplacer is used to fill the footprints with noise and save heavy footprints
272  # of the source pixels so that they can be restored one at a time for measurement.
273  # After the NoiseReplacer is constructed, all pixels in the exposure.getMaskedImage()
274  # which belong to objects in measCat will be replaced with noise
275  if self.config.doReplaceWithNoise:
276  noiseReplacer = NoiseReplacer(self.config.noiseReplacer, exposure, footprints, log=self.log)
277  else:
278  noiseReplacer = DummyNoiseReplacer()
279 
280  # First, create a catalog of all parentless sources
281  # Loop through all the parent sources, first processing the children, then the parent
282  measParentCat = measCat.getChildren(0)
283 
284  self.log.info("Measuring %d sources (%d parents, %d children) "
285  % (len(measCat), len(measParentCat), len(measCat) - len(measParentCat)))
286 
287  for parentIdx, measParentRecord in enumerate(measParentCat):
288  # first get all the children of this parent, insert footprint in turn, and measure
289  measChildCat = measCat.getChildren(measParentRecord.getId())
290  # TODO: skip this loop if there are no plugins configured for single-object mode
291  for measChildRecord in measChildCat:
292  noiseReplacer.insertSource(measChildRecord.getId())
293  self.callMeasure(measChildRecord, exposure)
294  noiseReplacer.removeSource(measChildRecord.getId())
295  # Then insert the parent footprint, and measure that
296  noiseReplacer.insertSource(measParentRecord.getId())
297  self.callMeasure(measParentRecord, exposure)
298  # Finally, process both the parent and the child set through measureN
299  self.callMeasureN(measParentCat[parentIdx:parentIdx+1], exposure)
300  self.callMeasureN(measChildCat, exposure)
301  noiseReplacer.removeSource(measParentRecord.getId())
302  # when done, restore the exposure to its original state
303  noiseReplacer.end()
304 
305  def measure(self, measCat, exposure):
306  """!
307  Backwards-compatibility alias for run()
308  """
309  self.run(measCat, exposure)
def run
Run single frame measurement over an exposure and source catalog.
Definition: sfm.py:250
def __init__
Initialize the task.
Definition: sfm.py:222
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:47
Config class for single frame measurement driver task.
Definition: sfm.py:113
def __init__
Initialize the measurement object.
Definition: sfm.py:61
def measureN
Measure the properties of a group of blended sources on a single image (single-epoch image or coadd)...
Definition: sfm.py:91
def measure
Backwards-compatibility alias for run()
Definition: sfm.py:305
def measure
Measure the properties of a source on a single image (single-epoch image or coadd).
Definition: sfm.py:75