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
#!/usr/bin/env python # # LSST Data Management System # Copyright 2008-2016 AURA/LSST. # # This product includes software developed by the # LSST Project (http://www.lsst.org/). # # 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 LSST License Statement and # the GNU General Public License along with this program. If not, # see <http://www.lsstcorp.org/LegalNotices/>. # Definitions and registration of pure-Python plugins with trivial implementations, and automatic plugin-from-algorithm calls for those implemented in C++. """
PeakLikelihoodFluxTransform ScaledApertureFluxTransform
"SingleFrameFPPositionConfig", "SingleFrameFPPositionPlugin", "SingleFrameJacobianConfig", "SingleFrameJacobianPlugin", "VarianceConfig", "SingleFrameVariancePlugin", "ForcedVariancePlugin", "InputCountConfig", "SingleFrameInputCountPlugin", "ForcedInputCountPlugin", "SingleFramePeakCentroidConfig", "SingleFramePeakCentroidPlugin", "SingleFrameSkyCoordConfig", "SingleFrameSkyCoordPlugin", "ForcedPeakCentroidConfig", "ForcedPeakCentroidPlugin", "ForcedTransformedCentroidConfig", "ForcedTransformedCentroidPlugin", "ForcedTransformedShapeConfig", "ForcedTransformedShapePlugin", )
# --- Wrapped C++ Plugins ---
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)
# --- Single-Frame Measurement Plugins ---
''' Algorithm to calculate the position of a centroid on the focal plane '''
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)
''' Algorithm which computes the Jacobian about a source and computes its ratio with a nominal pixel area. This allows one to compare relative instead of absolute areas of pixels. '''
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)
doc="Scale factor to apply to shape for aperture") default=["DETECTED", "DETECTED_NEGATIVE", "BAD", "SAT"])
''' Calculate the median variance within a Footprint scaled from the object shape so the value is not terribly influenced by the object and instead represents the variance in the background near the object. '''
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 a 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)
""" Plugin to count how many input images contributed to each source. This information is in the exposure's coaddInputs. Some limitations: * This is only for the pixel containing the center, not for all the pixels in the Footprint * This does not account for any clipping in the coadd """
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)
""" A centroid algorithm that simply uses the first (i.e. highest) Peak in the Source's Footprint as the 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. """
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
""" A measurement plugin that sets the "coord" field (part of the Source minimal schema) using the slot centroid and the Wcs attached to the Exposure. """
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
# --- Forced Plugins ---
""" The forced peak centroid is like the SFM peak centroid plugin, except that it must transform the peak coordinate from the original (reference) coordinate system to the coordinate system of the exposure being measured. """
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
"""A centroid pseudo-algorithm for forced measurement that simply transforms the centroid from the reference catalog to the measurement coordinate system. 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())
"""A shape pseudo-algorithm for forced measurement that simply transforms the shape from the reference catalog to the measurement coordinate system. 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()) |