Coverage for python/lsst/meas/base/plugins.py : 49%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# This file is part of meas_base. # # Developed for the LSST Data Management System. # This product includes software developed by the LSST Project # (https://www.lsst.org). # See the COPYRIGHT file at the top-level directory of this distribution # for details of code ownership. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>.
This module defines and registers a series of pure-Python measurement plugins which have trivial implementations. It also wraps measurement algorithms defined in C++ to expose them to the measurement framework. """
PeakLikelihoodFluxTransform ScaledApertureFluxTransform
"SingleFrameFPPositionConfig", "SingleFrameFPPositionPlugin", "SingleFrameJacobianConfig", "SingleFrameJacobianPlugin", "VarianceConfig", "SingleFrameVariancePlugin", "ForcedVariancePlugin", "InputCountConfig", "SingleFrameInputCountPlugin", "ForcedInputCountPlugin", "SingleFramePeakCentroidConfig", "SingleFramePeakCentroidPlugin", "SingleFrameSkyCoordConfig", "SingleFrameSkyCoordPlugin", "ForcedPeakCentroidConfig", "ForcedPeakCentroidPlugin", "ForcedTransformedCentroidConfig", "ForcedTransformedCentroidPlugin", "ForcedTransformedShapeConfig", "ForcedTransformedShapePlugin", )
TransformClass=PsfFluxTransform, executionOrder=BasePlugin.FLUX_ORDER, shouldApCorr=True, hasLogName=True) TransformClass=PeakLikelihoodFluxTransform, executionOrder=BasePlugin.FLUX_ORDER) TransformClass=GaussianFluxTransform, executionOrder=BasePlugin.FLUX_ORDER, shouldApCorr=True) TransformClass=NaiveCentroidTransform, executionOrder=BasePlugin.CENTROID_ORDER) TransformClass=SdssCentroidTransform, executionOrder=BasePlugin.CENTROID_ORDER) executionOrder=BasePlugin.FLUX_ORDER) TransformClass=SdssShapeTransform, executionOrder=BasePlugin.SHAPE_ORDER) TransformClass=ScaledApertureFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
TransformClass=ApertureFluxTransform, executionOrder=BasePlugin.FLUX_ORDER) TransformClass=BaseTransform, executionOrder=BasePlugin.SHAPE_ORDER)
TransformClass=LocalBackgroundTransform, executionOrder=BasePlugin.FLUX_ORDER)
"""Configuration for the focal plane position measurment algorithm. """
"""Algorithm to calculate the position of a centroid on the focal plane.
Parameters ---------- config : `SingleFrameFPPositionConfig` Plugin configuraion. name : `str` Plugin name. schema : `lsst.afw.table.Schema` The schema for the measurement output catalog. New fields will be added to hold measurements produced by this plugin. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog """
def getExecutionOrder(cls): return cls.SHAPE_ORDER
SingleFramePlugin.__init__(self, config, name, schema, metadata) self.focalValue = lsst.afw.table.Point2DKey.addFields(schema, name, "Position on the focal plane", "mm") self.focalFlag = schema.addField(name + "_flag", type="Flag", doc="Set to True for any fatal failure") self.detectorFlag = schema.addField(name + "_missingDetector_flag", type="Flag", doc="Set to True if detector object is missing")
det = exposure.getDetector() if not det: measRecord.set(self.detectorFlag, True) fp = lsst.geom.Point2D(np.nan, np.nan) else: center = measRecord.getCentroid() fp = det.transform(center, lsst.afw.cameraGeom.PIXELS, lsst.afw.cameraGeom.FOCAL_PLANE) measRecord.set(self.focalValue, fp)
measRecord.set(self.focalFlag, True)
"""Configuration for the Jacobian calculation plugin. """
"""Compute the Jacobian and its ratio with a nominal pixel area.
This enables one to compare relative, rather than absolute, pixel areas.
Parameters ---------- config : `SingleFrameJacobianConfig` Plugin configuraion. name : `str` Plugin name. schema : `lsst.afw.table.Schema` The schema for the measurement output catalog. New fields will be added to hold measurements produced by this plugin. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog """
def getExecutionOrder(cls): return cls.SHAPE_ORDER
SingleFramePlugin.__init__(self, config, name, schema, metadata) self.jacValue = schema.addField(name + '_value', type="D", doc="Jacobian correction") self.jacFlag = schema.addField(name + '_flag', type="Flag", doc="Set to 1 for any fatal failure") # Calculate one over the area of a nominal reference pixel, where area is in arcsec^2 self.scale = pow(self.config.pixelScale, -2)
center = measRecord.getCentroid() # Compute the area of a pixel at a source record's centroid, and take # the ratio of that with the defined reference pixel area. result = np.abs(self.scale*exposure.getWcs().linearizePixelToSky( center, lsst.geom.arcseconds).getLinear().computeDeterminant()) measRecord.set(self.jacValue, result)
measRecord.set(self.jacFlag, True)
"""Configuration for the variance calculation plugin. """ doc="Scale factor to apply to shape for aperture") default=["DETECTED", "DETECTED_NEGATIVE", "BAD", "SAT"])
"""Compute the median variance corresponding to a footprint.
The aim here is to measure the background variance, rather than that of the object itself. In order to achieve this, the variance is calculated over an area scaled up from the shape of the input footprint.
Parameters ---------- config : `VarianceConfig` Plugin configuraion. name : `str` Plugin name. schema : `lsst.afw.table.Schema` The schema for the measurement output catalog. New fields will be added to hold measurements produced by this plugin. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog """
"""Denotes failures due to bad centroiding (`int`). """
"""Denotes failures due to a lack of usable pixels (`int`). """
def getExecutionOrder(cls): return BasePlugin.FLUX_ORDER
GenericPlugin.__init__(self, config, name, schema, metadata) self.varValue = schema.addField(name + '_value', type="D", doc="Variance at object position") self.emptyFootprintFlag = schema.addField(name + '_flag_emptyFootprint', type="Flag", doc="Set to True when the footprint has no usable pixels")
# Alias the badCentroid flag to that which is defined for the target # of the centroid slot. We do not simply rely on the alias because # that could be changed post-measurement. schema.getAliasMap().set(name + '_flag_badCentroid', schema.getAliasMap().apply("slot_Centroid_flag"))
# Create an aperture and grow it by scale value defined in config to # ensure there are enough pixels around the object to get decent # statistics if not np.all(np.isfinite(measRecord.getCentroid())): raise MeasurementError("Bad centroid and/or shape", self.FAILURE_BAD_CENTROID) aperture = lsst.afw.geom.Ellipse(measRecord.getShape(), measRecord.getCentroid()) aperture.scale(self.config.scale) ellipse = lsst.afw.geom.SpanSet.fromShape(aperture) foot = lsst.afw.detection.Footprint(ellipse) foot.clipTo(exposure.getBBox(lsst.afw.image.PARENT)) # Filter out any pixels which have mask bits set corresponding to the # planes to be excluded (defined in config.mask) maskedImage = exposure.getMaskedImage() pixels = lsst.afw.detection.makeHeavyFootprint(foot, maskedImage) maskBits = maskedImage.getMask().getPlaneBitMask(self.config.mask) logicalMask = np.logical_not(pixels.getMaskArray() & maskBits) # Compute the median variance value for each pixel not excluded by the # mask and write the record. Numpy median is used here instead of # afw.math makeStatistics because of an issue with data types being # passed into the C++ layer (DM-2379). if np.any(logicalMask): medVar = np.median(pixels.getVarianceArray()[logicalMask]) measRecord.set(self.varValue, medVar) else: raise MeasurementError("Footprint empty, or all pixels are masked, can't compute median", self.FAILURE_EMPTY_FOOTPRINT)
# Check that we have an error object and that it is of type # MeasurementError if isinstance(error, MeasurementError): assert error.getFlagBit() in (self.FAILURE_BAD_CENTROID, self.FAILURE_EMPTY_FOOTPRINT) # FAILURE_BAD_CENTROID handled by alias to centroid record. if error.getFlagBit() == self.FAILURE_EMPTY_FOOTPRINT: measRecord.set(self.emptyFootprintFlag, True) measRecord.set(self.varValue, np.nan) GenericPlugin.fail(self, measRecord, error)
"""Single-frame version of `VariancePlugin`. """
"""Forced version of `VariancePlugin`. """
"""Configuration for the input image counting plugin. """
"""Count the number of input images which contributed to a a source.
Parameters ---------- config : `InputCountConfig` Plugin configuraion. name : `str` Plugin name. schema : `lsst.afw.table.Schema` The schema for the measurement output catalog. New fields will be added to hold measurements produced by this plugin. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog
Notes ----- Information is derived from the image's `~lsst.afw.image.CoaddInputs`. Note these limitation:
- This records the number of images which contributed to the pixel in the center of the source footprint, rather than to any or all pixels in the source. - Clipping in the coadd is not taken into account. """
"""Denotes failures due to bad centroiding (`int`). """
"""Denotes failures due to the image not having coadd inputs. (`int`) """
def getExecutionOrder(cls): return BasePlugin.SHAPE_ORDER
GenericPlugin.__init__(self, config, name, schema, metadata) self.numberKey = schema.addField(name + '_value', type="I", doc="Number of images contributing at center, not including any" "clipping") self.noInputsFlag = schema.addField(name + '_flag_noInputs', type="Flag", doc="No coadd inputs available") # Alias the badCentroid flag to that which is defined for the target of the centroid slot. # We do not simply rely on the alias because that could be changed post-measurement. schema.getAliasMap().set(name + '_flag_badCentroid', schema.getAliasMap().apply("slot_Centroid_flag"))
if not exposure.getInfo().getCoaddInputs(): raise MeasurementError("No coadd inputs defined.", self.FAILURE_NO_INPUTS) if not np.all(np.isfinite(center)): raise MeasurementError("Source has a bad centroid.", self.FAILURE_BAD_CENTROID)
ccds = exposure.getInfo().getCoaddInputs().ccds measRecord.set(self.numberKey, len(ccds.subsetContaining(center, exposure.getWcs())))
if error is not None: assert error.getFlagBit() in (self.FAILURE_BAD_CENTROID, self.FAILURE_NO_INPUTS) # FAILURE_BAD_CENTROID handled by alias to centroid record. if error.getFlagBit() == self.FAILURE_NO_INPUTS: measRecord.set(self.noInputsFlag, True) GenericPlugin.fail(self, measRecord, error)
"""Single-frame version of `InputCoutPlugin`. """
"""Forced version of `InputCoutPlugin`. """
"""Configuration for the single frame peak centroiding algorithm. """
"""Record the highest peak in a source footprint as its centroid.
This is of course a relatively poor measure of the true centroid of the object; this algorithm is provided mostly for testing and debugging.
Parameters ---------- config : `SingleFramePeakCentroidConfig` Plugin configuraion. name : `str` Plugin name. schema : `lsst.afw.table.Schema` The schema for the measurement output catalog. New fields will be added to hold measurements produced by this plugin. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog """
def getExecutionOrder(cls): return cls.CENTROID_ORDER
SingleFramePlugin.__init__(self, config, name, schema, metadata) self.keyX = schema.addField(name + "_x", type="D", doc="peak centroid", units="pixel") self.keyY = schema.addField(name + "_y", type="D", doc="peak centroid", units="pixel") self.flag = schema.addField(name + "_flag", type="Flag", doc="Centroiding failed")
peak = measRecord.getFootprint().getPeaks()[0] measRecord.set(self.keyX, peak.getFx()) measRecord.set(self.keyY, peak.getFy())
measRecord.set(self.flag, True)
def getTransformClass(): return SimpleCentroidTransform
"""Configuration for the sky coordinates algorithm. """
"""Record the sky position of an object based on its centroid slot and WCS.
The position is record in the ``coord`` field, which is part of the `~lsst.afw.table.SourceCatalog` minimal schema.
Parameters ---------- config : `SingleFrameSkyCoordConfig` Plugin configuraion. name : `str` Plugin name. schema : `lsst.afw.table.Schema` The schema for the measurement output catalog. New fields will be added to hold measurements produced by this plugin. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog """
def getExecutionOrder(cls): return cls.SHAPE_ORDER
# There should be a base class method for handling this exception. Put # this on a later ticket. Also, there should be a python Exception of # the appropriate type for this error if not exposure.hasWcs(): raise Exception("Wcs not attached to exposure. Required for " + self.name + " algorithm") measRecord.updateCoord(exposure.getWcs())
# Override fail() to do nothing in the case of an exception: this is # not ideal, but we don't have a place to put failures because we # don't allocate any fields. Should consider fixing as part of # DM-1011 pass
"""Configuration for the forced peak centroid algorithm. """
"""Record the highest peak in a source footprint as its centroid.
This is of course a relatively poor measure of the true centroid of the object; this algorithm is provided mostly for testing and debugging.
This is similar to `SingleFramePeakCentroidPlugin`, except that transforms the peak coordinate from the original (reference) coordinate system to the coordinate system of the exposure being measured.
Parameters ---------- config : `ForcedPeakCentroidConfig` Plugin configuraion. name : `str` Plugin name. schemaMapper : `lsst.afw.table.SchemaMapper` A mapping from reference catalog fields to output catalog fields. Output fields are added to the output schema. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog. """
def getExecutionOrder(cls): return cls.CENTROID_ORDER
ForcedPlugin.__init__(self, config, name, schemaMapper, metadata) schema = schemaMapper.editOutputSchema() self.keyX = schema.addField(name + "_x", type="D", doc="peak centroid", units="pixel") self.keyY = schema.addField(name + "_y", type="D", doc="peak centroid", units="pixel")
targetWcs = exposure.getWcs() peak = refRecord.getFootprint().getPeaks()[0] result = lsst.geom.Point2D(peak.getFx(), peak.getFy()) result = targetWcs.skyToPixel(refWcs.pixelToSky(result)) measRecord.set(self.keyX, result.getX()) measRecord.set(self.keyY, result.getY())
def getTransformClass(): return SimpleCentroidTransform
"""Configuration for the forced transformed centroid algorithm. """
"""Record the transformation of the reference catalog centroid.
The centroid recorded in the reference catalog is tranformed to the measurement coordinate system and stored.
Parameters ---------- config : `ForcedTransformedCentroidConfig` Plugin configuration name : `str` Plugin name schemaMapper : `lsst.afw.table.SchemaMapper` A mapping from reference catalog fields to output catalog fields. Output fields are added to the output schema. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog.
Notes ----- This is used as the slot centroid by default in forced measurement, allowing subsequent measurements to simply refer to the slot value just as they would in single-frame measurement. """
def getExecutionOrder(cls): return cls.CENTROID_ORDER
ForcedPlugin.__init__(self, config, name, schemaMapper, metadata) schema = schemaMapper.editOutputSchema() # Allocate x and y fields, join these into a single FunctorKey for ease-of-use. xKey = schema.addField(name + "_x", type="D", doc="transformed reference centroid column", units="pixel") yKey = schema.addField(name + "_y", type="D", doc="transformed reference centroid row", units="pixel") self.centroidKey = lsst.afw.table.Point2DKey(xKey, yKey) # Because we're taking the reference position as given, we don't bother transforming its # uncertainty and reporting that here, so there are no sigma or cov fields. We do propagate # the flag field, if it exists. if "slot_Centroid_flag" in schemaMapper.getInputSchema(): self.flagKey = schema.addField(name + "_flag", type="Flag", doc="whether the reference centroid is marked as bad") else: self.flagKey = None
targetWcs = exposure.getWcs() if not refWcs == targetWcs: targetPos = targetWcs.skyToPixel(refWcs.pixelToSky(refRecord.getCentroid())) measRecord.set(self.centroidKey, targetPos) else: measRecord.set(self.centroidKey, refRecord.getCentroid()) if self.flagKey is not None: measRecord.set(self.flagKey, refRecord.getCentroidFlag())
"""Configuration for the forced transformed shape algorithm. """
"""Record the transformation of the reference catalog shape.
The shape recorded in the reference catalog is tranformed to the measurement coordinate system and stored.
Parameters ---------- config : `ForcedTransformedShapeConfig` Plugin configuration name : `str` Plugin name schemaMapper : `lsst.afw.table.SchemaMapper` A mapping from reference catalog fields to output catalog fields. Output fields are added to the output schema. metadata : `lsst.daf.base.PropertySet` Plugin metadata that will be attached to the output catalog.
Notes ----- This is used as the slot shape by default in forced measurement, allowing subsequent measurements to simply refer to the slot value just as they would in single-frame measurement. """
def getExecutionOrder(cls): return cls.SHAPE_ORDER
ForcedPlugin.__init__(self, config, name, schemaMapper, metadata) schema = schemaMapper.editOutputSchema() # Allocate xx, yy, xy fields, join these into a single FunctorKey for ease-of-use. xxKey = schema.addField(name + "_xx", type="D", doc="transformed reference shape x^2 moment", units="pixel^2") yyKey = schema.addField(name + "_yy", type="D", doc="transformed reference shape y^2 moment", units="pixel^2") xyKey = schema.addField(name + "_xy", type="D", doc="transformed reference shape xy moment", units="pixel^2") self.shapeKey = lsst.afw.table.QuadrupoleKey(xxKey, yyKey, xyKey) # Because we're taking the reference position as given, we don't bother transforming its # uncertainty and reporting that here, so there are no sigma or cov fields. We do propagate # the flag field, if it exists. if "slot_Shape_flag" in schemaMapper.getInputSchema(): self.flagKey = schema.addField(name + "_flag", type="Flag", doc="whether the reference shape is marked as bad") else: self.flagKey = None
targetWcs = exposure.getWcs() if not refWcs == targetWcs: fullTransform = lsst.afw.geom.makeWcsPairTransform(refWcs, targetWcs) localTransform = lsst.afw.geom.linearizeTransform(fullTransform, refRecord.getCentroid()) measRecord.set(self.shapeKey, refRecord.getShape().transform(localTransform.getLinear())) else: measRecord.set(self.shapeKey, refRecord.getShape()) if self.flagKey is not None: measRecord.set(self.flagKey, refRecord.getShapeFlag()) |