24 Definitions and registration of pure-Python plugins with trivial implementations, 25 and automatic plugin-from-algorithm calls for those implemented in C++. 29 import lsst.pex.exceptions
30 import lsst.afw.detection
33 from .pluginRegistry
import register
34 from .pluginsBase
import BasePlugin
35 from .sfm
import SingleFramePluginConfig, SingleFramePlugin
36 from .forcedMeasurement
import ForcedPluginConfig, ForcedPlugin
37 from .wrappers
import wrapSimpleAlgorithm, wrapTransform
38 from .transforms
import SimpleCentroidTransform
40 from .apertureFlux
import ApertureFluxControl, ApertureFluxTransform
41 from .transform
import BaseTransform
42 from .blendedness
import BlendednessAlgorithm, BlendednessControl
43 from .circularApertureFlux
import CircularApertureFluxAlgorithm
44 from .gaussianCentroid
import GaussianCentroidAlgorithm, GaussianCentroidControl, GaussianCentroidTransform
45 from .gaussianFlux
import GaussianFluxAlgorithm, GaussianFluxControl, GaussianFluxTransform
46 from .exceptions
import MeasurementError
47 from .naiveCentroid
import NaiveCentroidAlgorithm, NaiveCentroidControl, NaiveCentroidTransform
48 from .peakLikelihoodFlux
import PeakLikelihoodFluxAlgorithm, PeakLikelihoodFluxControl, \
49 PeakLikelihoodFluxTransform
50 from .pixelFlags
import PixelFlagsAlgorithm, PixelFlagsControl
51 from .psfFlux
import PsfFluxAlgorithm, PsfFluxControl, PsfFluxTransform
52 from .scaledApertureFlux
import ScaledApertureFluxAlgorithm, ScaledApertureFluxControl, \
53 ScaledApertureFluxTransform
54 from .sdssCentroid
import SdssCentroidAlgorithm, SdssCentroidControl, SdssCentroidTransform
55 from .sdssShape
import SdssShapeAlgorithm, SdssShapeControl, SdssShapeTransform
58 "SingleFrameFPPositionConfig",
"SingleFrameFPPositionPlugin",
59 "SingleFrameJacobianConfig",
"SingleFrameJacobianPlugin",
60 "SingleFrameVarianceConfig",
"SingleFrameVariancePlugin",
61 "SingleFrameInputCountConfig",
"SingleFrameInputCountPlugin",
62 "SingleFramePeakCentroidConfig",
"SingleFramePeakCentroidPlugin",
63 "SingleFrameSkyCoordConfig",
"SingleFrameSkyCoordPlugin",
64 "ForcedPeakCentroidConfig",
"ForcedPeakCentroidPlugin",
65 "ForcedTransformedCentroidConfig",
"ForcedTransformedCentroidPlugin",
66 "ForcedTransformedShapeConfig",
"ForcedTransformedShapePlugin",
72 TransformClass=PsfFluxTransform, executionOrder=BasePlugin.FLUX_ORDER,
73 shouldApCorr=
True, hasLogName=
True)
75 TransformClass=PeakLikelihoodFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
77 TransformClass=GaussianFluxTransform, executionOrder=BasePlugin.FLUX_ORDER,
80 TransformClass=GaussianCentroidTransform, executionOrder=BasePlugin.CENTROID_ORDER)
82 TransformClass=NaiveCentroidTransform, executionOrder=BasePlugin.CENTROID_ORDER)
84 TransformClass=SdssCentroidTransform, executionOrder=BasePlugin.CENTROID_ORDER)
86 executionOrder=BasePlugin.FLUX_ORDER)
88 TransformClass=SdssShapeTransform, executionOrder=BasePlugin.SHAPE_ORDER)
90 TransformClass=ScaledApertureFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
93 TransformClass=ApertureFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
95 TransformClass=BaseTransform, executionOrder=BasePlugin.SHAPE_ORDER)
117 Algorithm to calculate the position of a centroid on the focal plane 120 ConfigClass = SingleFrameFPPositionConfig
126 def __init__(self, config, name, schema, metadata):
127 SingleFramePlugin.__init__(self, config, name, schema, metadata)
128 self.
focalValue = lsst.afw.table.Point2DKey.addFields(schema, name,
"Position on the focal plane",
130 self.
focalFlag = schema.addField(name +
"_flag", type=
"Flag", doc=
"Set to True for any fatal failure")
131 self.
detectorFlag = schema.addField(name +
"_missingDetector_flag", type=
"Flag",
132 doc=
"Set to True if detector object is missing")
135 det = exposure.getDetector()
138 fp = lsst.afw.geom.Point2D(numpy.nan, numpy.nan)
140 center = measRecord.getCentroid()
141 posInPix = det.makeCameraPoint(center, lsst.afw.cameraGeom.PIXELS)
142 fp = det.transform(posInPix, lsst.afw.cameraGeom.FOCAL_PLANE).getPoint()
145 def fail(self, measRecord, error=None):
150 pixelScale = lsst.pex.config.Field(dtype=float, default=0.5, doc=
"Nominal pixel size (arcsec)")
156 Algorithm which computes the Jacobian about a source and computes its ratio with a nominal pixel area. 157 This allows one to compare relative instead of absolute areas of pixels. 160 ConfigClass = SingleFrameJacobianConfig
166 def __init__(self, config, name, schema, metadata):
167 SingleFramePlugin.__init__(self, config, name, schema, metadata)
168 self.
jacValue = schema.addField(name +
'_value', type=
"D", doc=
"Jacobian correction")
169 self.
jacFlag = schema.addField(name +
'_flag', type=
"Flag", doc=
"Set to 1 for any fatal failure")
174 center = measRecord.getCentroid()
177 result = numpy.abs(self.
scale*exposure.getWcs().linearizePixelToSky(
179 lsst.afw.geom.arcseconds).getLinear().computeDeterminant())
180 measRecord.set(self.
jacValue, result)
182 def fail(self, measRecord, error=None):
183 measRecord.set(self.
jacFlag,
True)
187 scale = lsst.pex.config.Field(dtype=float, default=5.0, optional=
True,
188 doc=
"Scale factor to apply to shape for aperture")
189 mask = lsst.pex.config.ListField(doc=
"Mask planes to ignore", dtype=str,
190 default=[
"DETECTED",
"DETECTED_NEGATIVE",
"BAD",
"SAT"])
196 Calculate the median variance within a Footprint scaled from the object shape so 197 the value is not terribly influenced by the object and instead represents the 198 variance in the background near the object. 200 ConfigClass = SingleFrameVarianceConfig
201 FAILURE_BAD_CENTROID = 1
202 FAILURE_EMPTY_FOOTPRINT = 2
208 def __init__(self, config, name, schema, metadata):
209 SingleFramePlugin.__init__(self, config, name, schema, metadata)
210 self.
varValue = schema.addField(name +
'_value', type=
"D", doc=
"Variance at object position")
211 self.
varFlag = schema.addField(name +
'_flag', type=
"Flag", doc=
"Set to True for any fatal failure")
213 doc=
"Set to True when the footprint has no usable pixels")
217 schema.getAliasMap().set(name +
'_flag_badCentroid', schema.getAliasMap().apply(
"slot_Centroid_flag"))
220 if measRecord.getCentroidFlag():
224 aperture = lsst.afw.geom.ellipses.Ellipse(measRecord.getShape(), measRecord.getCentroid())
225 aperture.scale(self.
config.scale)
226 ellipse = lsst.afw.geom.SpanSet.fromShape(aperture)
227 foot = lsst.afw.detection.Footprint(ellipse)
228 foot.clipTo(exposure.getBBox(lsst.afw.image.PARENT))
231 maskedImage = exposure.getMaskedImage()
232 pixels = lsst.afw.detection.makeHeavyFootprint(foot, maskedImage)
233 maskBits = maskedImage.getMask().getPlaneBitMask(self.
config.mask)
234 logicalMask = numpy.logical_not(pixels.getMaskArray() & maskBits)
238 if numpy.any(logicalMask):
239 medVar = numpy.median(pixels.getVarianceArray()[logicalMask])
240 measRecord.set(self.
varValue, medVar)
242 raise MeasurementError(
"Footprint empty, or all pixels are masked, can't compute median",
245 def fail(self, measRecord, error=None):
247 if isinstance(error, MeasurementError):
252 measRecord.set(self.
varValue, numpy.nan)
253 measRecord.set(self.
varFlag,
True)
263 Plugin to count how many input images contributed to each source. This information 264 is in the exposure's coaddInputs. Some limitations: 265 * This is only for the pixel containing the center, not for all the pixels in the 267 * This does not account for any clipping in the coadd 270 ConfigClass = SingleFrameInputCountConfig
271 FAILURE_BAD_CENTROID = 1
272 FAILURE_NO_INPUTS = 2
278 def __init__(self, config, name, schema, metadata):
279 SingleFramePlugin.__init__(self, config, name, schema, metadata)
280 self.
numberKey = schema.addField(name +
'_value', type=
"I",
281 doc=
"Number of images contributing at center, not including any" 283 self.
numberFlag = schema.addField(name +
'_flag', type=
"Flag", doc=
"Set to True for fatal failure")
284 self.
noInputsFlag = schema.addField(name +
'_flag_noInputs', type=
"Flag",
285 doc=
"No coadd inputs available")
288 schema.getAliasMap().set(name +
'_flag_badCentroid', schema.getAliasMap().apply(
"slot_Centroid_flag"))
291 if not exposure.getInfo().getCoaddInputs():
293 if measRecord.getCentroidFlag():
296 center = measRecord.getCentroid()
297 ccds = exposure.getInfo().getCoaddInputs().ccds
298 measRecord.set(self.
numberKey, len(ccds.subsetContaining(center, exposure.getWcs())))
300 def fail(self, measRecord, error=None):
302 if error
is not None:
316 A centroid algorithm that simply uses the first (i.e. highest) Peak in the Source's 317 Footprint as the centroid. This is of course a relatively poor measure of the true 318 centroid of the object; this algorithm is provided mostly for testing and debugging. 321 ConfigClass = SingleFramePeakCentroidConfig
327 def __init__(self, config, name, schema, metadata):
328 SingleFramePlugin.__init__(self, config, name, schema, metadata)
329 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
330 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
331 self.
flag = schema.addField(name +
"_flag", type=
"Flag", doc=
"Centroiding failed")
334 peak = measRecord.getFootprint().getPeaks()[0]
335 measRecord.set(self.
keyX, peak.getFx())
336 measRecord.set(self.
keyY, peak.getFy())
338 def fail(self, measRecord, error=None):
339 measRecord.set(self.
flag,
True)
343 return SimpleCentroidTransform
353 A measurement plugin that sets the "coord" field (part of the Source minimal schema) 354 using the slot centroid and the Wcs attached to the Exposure. 357 ConfigClass = SingleFrameSkyCoordConfig
366 if not exposure.hasWcs():
367 raise Exception(
"Wcs not attached to exposure. Required for " + self.
name +
" algorithm")
368 measRecord.updateCoord(exposure.getWcs())
370 def fail(self, measRecord, error=None):
379 class ForcedPeakCentroidConfig(ForcedPluginConfig):
386 The forced peak centroid is like the SFM peak centroid plugin, except that it must transform 387 the peak coordinate from the original (reference) coordinate system to the coordinate system 388 of the exposure being measured. 391 ConfigClass = ForcedPeakCentroidConfig
397 def __init__(self, config, name, schemaMapper, metadata):
398 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
399 schema = schemaMapper.editOutputSchema()
400 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
401 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
403 def measure(self, measRecord, exposure, refRecord, refWcs):
404 targetWcs = exposure.getWcs()
405 peak = refRecord.getFootprint().getPeaks()[0]
406 result = lsst.afw.geom.Point2D(peak.getFx(), peak.getFy())
407 if not refWcs == targetWcs:
408 result = targetWcs.skyToPixel(refWcs.pixelToSky(result))
409 measRecord.set(self.
keyX, result.getX())
410 measRecord.set(self.
keyY, result.getY())
414 return SimpleCentroidTransform
421 @
register(
"base_TransformedCentroid")
423 """A centroid pseudo-algorithm for forced measurement that simply transforms the centroid 424 from the reference catalog to the measurement coordinate system. This is used as 425 the slot centroid by default in forced measurement, allowing subsequent measurements 426 to simply refer to the slot value just as they would in single-frame measurement. 429 ConfigClass = ForcedTransformedCentroidConfig
435 def __init__(self, config, name, schemaMapper, metadata):
436 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
437 schema = schemaMapper.editOutputSchema()
439 xKey = schema.addField(name +
"_x", type=
"D", doc=
"transformed reference centroid column",
441 yKey = schema.addField(name +
"_y", type=
"D", doc=
"transformed reference centroid row",
447 if "slot_Centroid_flag" in schemaMapper.getInputSchema():
448 self.
flagKey = schema.addField(name +
"_flag", type=
"Flag",
449 doc=
"whether the reference centroid is marked as bad")
453 def measure(self, measRecord, exposure, refRecord, refWcs):
454 targetWcs = exposure.getWcs()
455 if not refWcs == targetWcs:
456 targetPos = targetWcs.skyToPixel(refWcs.pixelToSky(refRecord.getCentroid()))
459 measRecord.set(self.
centroidKey, refRecord.getCentroid())
461 measRecord.set(self.
flagKey, refRecord.getCentroidFlag())
470 """A shape pseudo-algorithm for forced measurement that simply transforms the shape 471 from the reference catalog to the measurement coordinate system. This is used as 472 the slot shape by default in forced measurement, allowing subsequent measurements 473 to simply refer to the slot value just as they would in single-frame measurement. 476 ConfigClass = ForcedTransformedShapeConfig
482 def __init__(self, config, name, schemaMapper, metadata):
483 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
484 schema = schemaMapper.editOutputSchema()
486 xxKey = schema.addField(name +
"_xx", type=
"D", doc=
"transformed reference shape x^2 moment",
488 yyKey = schema.addField(name +
"_yy", type=
"D", doc=
"transformed reference shape y^2 moment",
490 xyKey = schema.addField(name +
"_xy", type=
"D", doc=
"transformed reference shape xy moment",
492 self.
shapeKey = lsst.afw.table.QuadrupoleKey(xxKey, yyKey, xyKey)
496 if "slot_Shape_flag" in schemaMapper.getInputSchema():
497 self.
flagKey = schema.addField(name +
"_flag", type=
"Flag",
498 doc=
"whether the reference shape is marked as bad")
502 def measure(self, measRecord, exposure, refRecord, refWcs):
503 targetWcs = exposure.getWcs()
504 if not refWcs == targetWcs:
505 fullTransform = lsst.afw.image.XYTransformFromWcsPair(targetWcs, refWcs)
506 localTransform = fullTransform.linearizeForwardTransform(refRecord.getCentroid())
507 measRecord.set(self.
shapeKey, refRecord.getShape().
transform(localTransform.getLinear()))
509 measRecord.set(self.
shapeKey, refRecord.getShape())
511 measRecord.set(self.
flagKey, refRecord.getShapeFlag())
def measure(self, measRecord, exposure)
def getExecutionOrder(cls)
def getExecutionOrder(cls)
def fail(self, measRecord, error=None)
def __init__(self, config, name, schema, metadata)
int FAILURE_EMPTY_FOOTPRINT
def fail(self, measRecord, error=None)
def fail(self, measRecord, error=None)
def getExecutionOrder(cls)
def __init__(self, config, name, schema, metadata)
def measure(self, measRecord, exposure)
Base class for configs of single-frame plugin algorithms.
def getExecutionOrder(cls)
def __init__(self, config, name, schemaMapper, metadata)
def fail(self, measRecord, error=None)
Base class for single-frame plugin algorithms.
def getExecutionOrder(cls)
def fail(self, measRecord, error=None)
def register(name, shouldApCorr=False, apCorrList=())
A Python decorator that registers a class, using the given name, in its base class's PluginRegistry...
def getExecutionOrder(cls)
def measure(self, measRecord, exposure)
def measure(self, measRecord, exposure)
def wrapTransform(transformClass, hasLogName=False)
def measure(self, measRecord, exposure, refRecord, refWcs)
def __init__(self, config, name, schema, metadata)
def __init__(self, config, name, schema, metadata)
def measure(self, measRecord, exposure)
def wrapSimpleAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, hasLogName=False, kwds)
Wrap a C++ SimpleAlgorithm class into both a Python SingleFramePlugin and ForcedPlugin classes...