23 """Base classes for forced measurement plugins and the driver task for these.
25 In forced measurement, a reference catalog is used to define restricted measurements (usually just fluxes)
26 on an image. As the reference catalog may be deeper than the detection limit of the measurement image, we
27 do not assume that we can use detection and deblend information from the measurement image. Instead, we
28 assume this information is present in the reference catalog and can be "transformed" in some sense to
29 the measurement frame. At the very least, this means that Footprints from the reference catalog should
30 be transformed and installed as Footprints in the output measurement catalog. If we have a procedure that
31 can transform HeavyFootprints, we can then proceed with measurement as usual, but using the reference
32 catalog's id and parent fields to define deblend families. If this transformation does not preserve
33 HeavyFootprints (this is currently the case), then we will only be able to replace objects with noise
34 one deblend family at a time, and hence measurements run in single-object mode may be contaminated by
35 neighbors when run on objects with parent != 0.
37 Measurements are generally recorded in the coordinate system of the image being measured (and all
38 slot-eligible fields must be), but non-slot fields may be recorded in other coordinate systems if necessary
39 to avoid information loss (this should, of course, be indicated in the field documentation). Note that
40 the reference catalog may be in a different coordinate system; it is the responsibility of plugins
41 to transform the data they need themselves, using the reference WCS provided. However, for plugins
42 that only require a position or shape, they may simply use output SourceCatalog's centroid or shape slots,
43 which will generally be set to the transformed position of the reference object before any other plugins are
44 run, and hence avoid using the reference catalog at all.
46 Command-line driver tasks for forced measurement can be found in forcedPhotImage.py, including
47 ForcedPhotImageTask, ForcedPhotCcdTask, and ForcedPhotCoaddTask.
55 __all__ = (
"ForcedPluginConfig",
"ForcedPlugin",
56 "ForcedMeasurementConfig",
"ForcedMeasurementTask")
59 """Base class for configs of forced measurement plugins."""
65 registry = PluginRegistry(ForcedPluginConfig)
67 ConfigClass = ForcedPluginConfig
69 def __init__(self, config, name, schemaMapper, metadata):
70 """Initialize the measurement object.
72 @param[in] config An instance of this class's ConfigClass.
73 @param[in] name The string the plugin was registered with.
74 @param[in,out] schemaMapper A SchemaMapper that maps reference catalog fields to output
75 catalog fields. Output fields should be added to the
76 output schema. While most plugins will not need to map
77 fields from the reference schema, if they do so, those fields
78 will be transferred before any plugins are run.
79 @param[in] metadata Plugin metadata that will be attached to the output catalog
81 BasePlugin.__init__(self)
85 def measure(self, measRecord, exposure, refRecord, refWcs):
86 """Measure the properties of a source on a single image, given data from a
89 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
90 be measured and the associated Psf, Wcs, etc. All
91 other sources in the image will have been replaced by
92 noise according to deblender outputs.
93 @param[in,out] measRecord lsst.afw.table.SourceRecord to be filled with outputs,
94 and from which previously-measured quantities can be
96 @param[in] refRecord lsst.afw.table.SimpleRecord that contains additional
97 parameters to define the fit, as measured elsewhere.
98 @param[in] refWcs The coordinate system for the reference catalog values.
99 An afw.geom.Angle may be passed, indicating that a
100 local tangent Wcs should be created for each object
101 using afw.image.makeLocalWcs and the given angle as
104 In the normal mode of operation, the source centroid will be set to the
105 WCS-transformed position of the reference object, so plugins that only
106 require a reference position should not have to access the reference object
109 raise NotImplementedError()
111 def measureN(self, measCat, exposure, refCat, refWcs):
112 """Measure the properties of a group of blended sources on a single image,
113 given data from a reference record.
115 @param[in] exposure lsst.afw.image.ExposureF, containing the pixel data to
116 be measured and the associated Psf, Wcs, etc. Sources
117 not in the blended hierarchy to be measured will have
118 been replaced with noise using deblender outputs.
119 @param[in,out] measCat lsst.afw.table.SourceCatalog to be filled with outputs,
120 and from which previously-measured quantities can be
121 retrieved, containing only the sources that should be
122 measured together in this call.
123 @param[in] refCat lsst.afw.table.SimpleCatalog that contains additional
124 parameters to define the fit, as measured elsewhere.
125 Ordered such that zip(sources, references) may be used.
126 @param[in] refWcs The coordinate system for the reference catalog values.
127 An afw.geom.Angle may be passed, indicating that a
128 local tangent Wcs should be created for each object
129 using afw.image.makeLocalWcs and the given Angle as
132 In the normal mode of operation, the source centroids will be set to the
133 WCS-transformed position of the reference object, so plugins that only
134 require a reference position should not have to access the reference object
137 raise NotImplementedError()
140 """Config class for forced measurement driver task."""
142 plugins = ForcedPlugin.registry.makeField(
144 default=[
"base_TransformedCentroid",
145 "base_TransformedShape",
151 doc=
"Plugins to be run and their configuration"
153 algorithms = property(
lambda self: self.
plugins, doc=
"backwards-compatibility alias for plugins")
155 copyColumns = lsst.pex.config.DictField(
156 keytype=str, itemtype=str, doc=
"Mapping of reference columns to source columns",
157 default={
"id":
"objectId",
"parent":
"parentObjectId"}
161 self.slots.centroid =
"base_TransformedCentroid"
162 self.slots.shape =
"base_TransformedShape"
163 self.slots.apFlux =
None
164 self.slots.modelFlux =
None
165 self.slots.psfFlux =
None
166 self.slots.instFlux =
None
177 A subtask for measuring the properties of sources on a single exposure, using an existing
178 "reference" catalog to constrain some aspects of the measurement.
180 The task is configured with a list of "plugins": each plugin defines the values it
181 measures (i.e. the columns in a table it will fill) and conducts that measurement
182 on each detected source (see ForcedPlugin). The job of the
183 measurement task is to initialize the set of plugins (which includes setting up the
184 catalog schema) from their configuration, and then invoke each plugin on each
187 Most of the time, ForcedMeasurementTask will be used via one of the subclasses of
188 ForcedPhotImageTask, ForcedPhotCcdTask and ForcedPhotCoaddTask. These combine
189 this measurement subtask with a "references" subtask (see BaseReferencesTask and
190 CoaddSrcReferencesTask) to perform forced measurement using measurements performed on
191 another image as the references. There is generally little reason to use
192 ForcedMeasurementTask outside of one of these drivers, unless it is necessary to avoid
193 using the Butler for I/O.
195 ForcedMeasurementTask has only three methods: __init__(), run(), and generateSources().
196 For configuration options, see SingleFrameMeasurementConfig.
199 ConfigClass = ForcedMeasurementConfig
201 def __init__(self, refSchema, algMetadata=None, **kwds):
203 Initialize the task. Set up the execution order of the plugins and initialize
204 the plugins, giving each plugin an opportunity to add its measurement fields to
205 the output schema and to record information in the task metadata.
207 Note that while SingleFrameMeasurementTask is passed an initial Schema that is
208 appended to in order to create the output Schema, ForcedMeasurementTask is
209 initialized with the Schema of the reference catalog, from which a new Schema
210 for the output catalog is created. Fields to be copied directly from the
211 reference Schema are added before Plugin fields are added.
213 @param[in] refSchema Schema of the reference catalog. Must match the catalog
214 later passed to generateSources() and/or run().
215 @param[in,out] algMetadata lsst.daf.base.PropertyList used to record information about
216 each algorithm. An empty PropertyList will be created if None.
217 @param[in] **kwds Keyword arguments passed from lsst.pipe.base.Task.__init__
219 super(ForcedMeasurementTask, self).
__init__(algMetadata=algMetadata, **kwds)
222 self.config.slots.setupSchema(self.mapper.editOutputSchema())
223 for refName, targetName
in self.config.copyColumns.items():
224 refItem = refSchema.find(refName)
225 self.mapper.addMapping(refItem.key, targetName)
229 if self.config.slots.centroid !=
None:
230 self.plugins[self.config.slots.centroid] =
None
232 for executionOrder, name, config, PluginClass
in sorted(self.config.plugins.apply()):
233 self.plugins[name] = PluginClass(config, name, self.
mapper, metadata=self.algMetadata)
235 def run(self, exposure, refCat, refWcs, idFactory=None):
237 Perform forced measurement.
239 @param[in] exposure lsst.afw.image.ExposureF to be measured; must have at least a Wcs attached.
240 @param[in] refCat A sequence of SourceRecord objects that provide reference information
241 for the measurement. These will be passed to each Plugin in addition
242 to the output SourceRecord.
243 @param[in] refWcs Wcs that defines the X,Y coordinate system of refCat
244 @param[in] idFactory factory for creating IDs for sources
246 @return SourceCatalog containing measurement results.
248 Delegates creating the initial empty SourceCatalog to generateSources(), then fills it.
258 footprints = {ref.getId(): ref.getParent()
for ref
in refCat}
264 if not topId
in footprints.keys():
265 footprints.pop(refId)
267 topId = footprints[topId]
275 for (reference, source)
in zip(refList, sources):
276 footprints[reference.getId()] = (reference.getParent(), source.getFootprint())
278 self.log.info(
"Performing forced measurement on %d sources" % len(refList))
282 referenceCat.extend(refList)
285 if self.config.doReplaceWithNoise:
286 noiseReplacer = NoiseReplacer(self.config.noiseReplacer, exposure, footprints, log=self.log)
288 noiseReplacer = DummyNoiseReplacer()
292 refParentCat, measParentCat = referenceCat.getChildren(0, sources)
293 for parentIdx, (refParentRecord, measParentRecord)
in enumerate(zip(refParentCat,measParentCat)):
296 refChildCat, measChildCat = referenceCat.getChildren(refParentRecord.getId(), sources)
298 for refChildRecord, measChildRecord
in zip(refChildCat, measChildCat):
299 noiseReplacer.insertSource(refChildRecord.getId())
300 self.callMeasure(measChildRecord, exposure, refChildRecord, refWcs)
301 noiseReplacer.removeSource(refChildRecord.getId())
304 noiseReplacer.insertSource(refParentRecord.getId())
305 self.callMeasure(measParentRecord, exposure, refParentRecord, refWcs)
306 self.callMeasureN(measParentCat[parentIdx:parentIdx+1], exposure,
307 refParentCat[parentIdx:parentIdx+1])
309 self.callMeasureN(measChildCat, exposure, refChildCat)
310 noiseReplacer.removeSource(refParentRecord.getId())
312 return lsst.pipe.base.Struct(sources=sources)
316 Initialize an output SourceCatalog using information from the reference catalog.
318 This generate a new blank SourceRecord for each record in refCat, copying any
319 fields in ForcedMeasurementConfig.copyColumns. This also transforms the Footprints
320 in refCat to the measurement coordinate system if it differs from refWcs, and attaches
321 these to the new SourceRecords. Note that we do not currently have the ability to
322 transform heavy footprints, so when the reference and measure WCSs are different,
323 HeavyFootprints will be converted to regular Footprints, which makes it impossible
324 to properly measure blended objects.
326 @param[in] exposure Exposure to be measured
327 @param[in] refCat Sequence (not necessarily a SourceCatalog) of reference SourceRecords.
328 @param[in] refWcs Wcs that defines the X,Y coordinate system of refCat
329 @param[in] idFactory factory for creating IDs for sources
331 @return Source catalog ready for measurement
333 if idFactory ==
None:
335 schema = self.mapper.getOutputSchema()
338 table = sources.table
339 table.setMetadata(self.algMetadata)
340 table.preallocate(len(refCat))
341 expRegion = exposure.getBBox()
342 targetWcs = exposure.getWcs()
344 newSource = sources.addNew()
345 newSource.assign(ref, self.
mapper)
346 footprint = newSource.getFootprint()
347 if footprint
is not None:
349 if footprint.isHeavy():
351 if not refWcs == targetWcs:
352 footprint = footprint.transform(refWcs, targetWcs, expRegion,
True)
353 newSource.setFootprint(footprint)
Defines the fields and offsets for a table.
def generateSources
Initialize an output SourceCatalog using information from the reference catalog.
A mapping between the keys of two Schemas, used to copy data between them.
A subtask for measuring the properties of sources on a single exposure, using an existing "reference"...
static boost::shared_ptr< IdFactory > makeSimple()
Return a simple IdFactory that simply counts from 1.
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
static Schema makeMinimalSchema()
Return a minimal schema for Source tables and records.
def __init__
Initialize the task.
static boost::shared_ptr< SourceTable > make(Schema const &schema, boost::shared_ptr< IdFactory > const &idFactory)
Construct a new table.
def run
Perform forced measurement.