24 Definitions and registration of pure-Python plugins with trivial implementations, 25 and automatic plugin-from-algorithm calls for those implemented in C++. 27 from __future__
import absolute_import, division, print_function
35 from .pluginRegistry
import register
36 from .pluginsBase
import BasePlugin
37 from .baseMeasurement
import BaseMeasurementPluginConfig
38 from .sfm
import SingleFramePluginConfig, SingleFramePlugin
39 from .forcedMeasurement
import ForcedPluginConfig, ForcedPlugin
40 from .wrappers
import wrapSimpleAlgorithm, wrapTransform, GenericPlugin
41 from .transforms
import SimpleCentroidTransform
43 from .apertureFlux
import ApertureFluxControl, ApertureFluxTransform
44 from .transform
import BaseTransform
45 from .blendedness
import BlendednessAlgorithm, BlendednessControl
46 from .circularApertureFlux
import CircularApertureFluxAlgorithm
47 from .gaussianFlux
import GaussianFluxAlgorithm, GaussianFluxControl, GaussianFluxTransform
48 from .exceptions
import MeasurementError
49 from .naiveCentroid
import NaiveCentroidAlgorithm, NaiveCentroidControl, NaiveCentroidTransform
50 from .peakLikelihoodFlux
import PeakLikelihoodFluxAlgorithm, PeakLikelihoodFluxControl, \
51 PeakLikelihoodFluxTransform
52 from .pixelFlags
import PixelFlagsAlgorithm, PixelFlagsControl
53 from .psfFlux
import PsfFluxAlgorithm, PsfFluxControl, PsfFluxTransform
54 from .scaledApertureFlux
import ScaledApertureFluxAlgorithm, ScaledApertureFluxControl, \
55 ScaledApertureFluxTransform
56 from .sdssCentroid
import SdssCentroidAlgorithm, SdssCentroidControl, SdssCentroidTransform
57 from .sdssShape
import SdssShapeAlgorithm, SdssShapeControl, SdssShapeTransform
60 "SingleFrameFPPositionConfig",
"SingleFrameFPPositionPlugin",
61 "SingleFrameJacobianConfig",
"SingleFrameJacobianPlugin",
62 "VarianceConfig",
"SingleFrameVariancePlugin",
"ForcedVariancePlugin",
63 "InputCountConfig",
"SingleFrameInputCountPlugin",
"ForcedInputCountPlugin",
64 "SingleFramePeakCentroidConfig",
"SingleFramePeakCentroidPlugin",
65 "SingleFrameSkyCoordConfig",
"SingleFrameSkyCoordPlugin",
66 "ForcedPeakCentroidConfig",
"ForcedPeakCentroidPlugin",
67 "ForcedTransformedCentroidConfig",
"ForcedTransformedCentroidPlugin",
68 "ForcedTransformedShapeConfig",
"ForcedTransformedShapePlugin",
74 TransformClass=PsfFluxTransform, executionOrder=BasePlugin.FLUX_ORDER,
75 shouldApCorr=
True, hasLogName=
True)
77 TransformClass=PeakLikelihoodFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
79 TransformClass=GaussianFluxTransform, executionOrder=BasePlugin.FLUX_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)
116 Algorithm to calculate the position of a centroid on the focal plane 119 ConfigClass = SingleFrameFPPositionConfig
125 def __init__(self, config, name, schema, metadata):
126 SingleFramePlugin.__init__(self, config, name, schema, metadata)
127 self.
focalValue = lsst.afw.table.Point2DKey.addFields(schema, name,
"Position on the focal plane",
129 self.
focalFlag = schema.addField(name +
"_flag", type=
"Flag", doc=
"Set to True for any fatal failure")
130 self.
detectorFlag = schema.addField(name +
"_missingDetector_flag", type=
"Flag",
131 doc=
"Set to True if detector object is missing")
134 det = exposure.getDetector()
139 center = measRecord.getCentroid()
140 posInPix = det.makeCameraPoint(center, lsst.afw.cameraGeom.PIXELS)
141 fp = det.transform(posInPix, lsst.afw.cameraGeom.FOCAL_PLANE).getPoint()
144 def fail(self, measRecord, error=None):
149 pixelScale = lsst.pex.config.Field(dtype=float, default=0.5, doc=
"Nominal pixel size (arcsec)")
155 Algorithm which computes the Jacobian about a source and computes its ratio with a nominal pixel area. 156 This allows one to compare relative instead of absolute areas of pixels. 159 ConfigClass = SingleFrameJacobianConfig
165 def __init__(self, config, name, schema, metadata):
166 SingleFramePlugin.__init__(self, config, name, schema, metadata)
167 self.
jacValue = schema.addField(name +
'_value', type=
"D", doc=
"Jacobian correction")
168 self.
jacFlag = schema.addField(name +
'_flag', type=
"Flag", doc=
"Set to 1 for any fatal failure")
173 center = measRecord.getCentroid()
176 result = numpy.abs(self.
scale*exposure.getWcs().linearizePixelToSky(
178 lsst.afw.geom.arcseconds).getLinear().computeDeterminant())
179 measRecord.set(self.
jacValue, result)
181 def fail(self, measRecord, error=None):
182 measRecord.set(self.
jacFlag,
True)
186 scale = lsst.pex.config.Field(dtype=float, default=5.0, optional=
True,
187 doc=
"Scale factor to apply to shape for aperture")
188 mask = lsst.pex.config.ListField(doc=
"Mask planes to ignore", dtype=str,
189 default=[
"DETECTED",
"DETECTED_NEGATIVE",
"BAD",
"SAT"])
194 Calculate the median variance within a Footprint scaled from the object shape so 195 the value is not terribly influenced by the object and instead represents the 196 variance in the background near the object. 198 ConfigClass = VarianceConfig
199 FAILURE_BAD_CENTROID = 1
200 FAILURE_EMPTY_FOOTPRINT = 2
204 return BasePlugin.FLUX_ORDER
206 def __init__(self, config, name, schema, metadata):
207 GenericPlugin.__init__(self, config, name, schema, metadata)
208 self.
varValue = schema.addField(name +
'_value', type=
"D", doc=
"Variance at object position")
210 doc=
"Set to True when the footprint has no usable pixels")
214 schema.getAliasMap().set(name +
'_flag_badCentroid', schema.getAliasMap().apply(
"slot_Centroid_flag"))
216 def measure(self, measRecord, exposure, center):
219 if not numpy.all(numpy.isfinite(measRecord.getCentroid())):
222 aperture.scale(self.
config.scale)
225 foot.clipTo(exposure.getBBox(lsst.afw.image.PARENT))
228 maskedImage = exposure.getMaskedImage()
230 maskBits = maskedImage.getMask().getPlaneBitMask(self.
config.mask)
231 logicalMask = numpy.logical_not(pixels.getMaskArray() & maskBits)
235 if numpy.any(logicalMask):
236 medVar = numpy.median(pixels.getVarianceArray()[logicalMask])
237 measRecord.set(self.
varValue, medVar)
239 raise MeasurementError(
"Footprint empty, or all pixels are masked, can't compute median",
242 def fail(self, measRecord, error=None):
244 if isinstance(error, MeasurementError):
249 measRecord.set(self.
varValue, numpy.nan)
250 GenericPlugin.fail(self, measRecord, error)
253 SingleFrameVariancePlugin = VariancePlugin.makeSingleFramePlugin(
"base_Variance")
254 ForcedVariancePlugin = VariancePlugin.makeForcedPlugin(
"base_Variance")
261 class InputCountPlugin(GenericPlugin):
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 = InputCountConfig
271 FAILURE_BAD_CENTROID = 1
272 FAILURE_NO_INPUTS = 2
276 return BasePlugin.SHAPE_ORDER
278 def __init__(self, config, name, schema, metadata):
279 GenericPlugin.__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.
noInputsFlag = schema.addField(name +
'_flag_noInputs', type=
"Flag",
284 doc=
"No coadd inputs available")
287 schema.getAliasMap().set(name +
'_flag_badCentroid', schema.getAliasMap().apply(
"slot_Centroid_flag"))
289 def measure(self, measRecord, exposure, center):
290 if not exposure.getInfo().getCoaddInputs():
292 if not numpy.all(numpy.isfinite(center)):
295 ccds = exposure.getInfo().getCoaddInputs().ccds
296 measRecord.set(self.
numberKey, len(ccds.subsetContaining(center, exposure.getWcs())))
298 def fail(self, measRecord, error=None):
299 if error
is not None:
304 GenericPlugin.fail(self, measRecord, error)
307 SingleFrameInputCountPlugin = InputCountPlugin.makeSingleFramePlugin(
"base_InputCount")
308 ForcedInputCountPlugin = InputCountPlugin.makeForcedPlugin(
"base_InputCount")
318 A centroid algorithm that simply uses the first (i.e. highest) Peak in the Source's 319 Footprint as the centroid. This is of course a relatively poor measure of the true 320 centroid of the object; this algorithm is provided mostly for testing and debugging. 323 ConfigClass = SingleFramePeakCentroidConfig
329 def __init__(self, config, name, schema, metadata):
330 SingleFramePlugin.__init__(self, config, name, schema, metadata)
331 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
332 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
333 self.
flag = schema.addField(name +
"_flag", type=
"Flag", doc=
"Centroiding failed")
336 peak = measRecord.getFootprint().getPeaks()[0]
337 measRecord.set(self.
keyX, peak.getFx())
338 measRecord.set(self.
keyY, peak.getFy())
340 def fail(self, measRecord, error=None):
341 measRecord.set(self.
flag,
True)
345 return SimpleCentroidTransform
355 A measurement plugin that sets the "coord" field (part of the Source minimal schema) 356 using the slot centroid and the Wcs attached to the Exposure. 359 ConfigClass = SingleFrameSkyCoordConfig
368 if not exposure.hasWcs():
369 raise Exception(
"Wcs not attached to exposure. Required for " + self.
name +
" algorithm")
370 measRecord.updateCoord(exposure.getWcs())
372 def fail(self, measRecord, error=None):
381 class ForcedPeakCentroidConfig(ForcedPluginConfig):
388 The forced peak centroid is like the SFM peak centroid plugin, except that it must transform 389 the peak coordinate from the original (reference) coordinate system to the coordinate system 390 of the exposure being measured. 393 ConfigClass = ForcedPeakCentroidConfig
399 def __init__(self, config, name, schemaMapper, metadata):
400 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
401 schema = schemaMapper.editOutputSchema()
402 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
403 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
405 def measure(self, measRecord, exposure, refRecord, refWcs):
406 targetWcs = exposure.getWcs()
407 peak = refRecord.getFootprint().getPeaks()[0]
409 if not refWcs == targetWcs:
410 result = targetWcs.skyToPixel(refWcs.pixelToSky(result))
411 measRecord.set(self.
keyX, result.getX())
412 measRecord.set(self.
keyY, result.getY())
416 return SimpleCentroidTransform
423 @
register(
"base_TransformedCentroid")
425 """A centroid pseudo-algorithm for forced measurement that simply transforms the centroid 426 from the reference catalog to the measurement coordinate system. This is used as 427 the slot centroid by default in forced measurement, allowing subsequent measurements 428 to simply refer to the slot value just as they would in single-frame measurement. 431 ConfigClass = ForcedTransformedCentroidConfig
437 def __init__(self, config, name, schemaMapper, metadata):
438 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
439 schema = schemaMapper.editOutputSchema()
441 xKey = schema.addField(name +
"_x", type=
"D", doc=
"transformed reference centroid column",
443 yKey = schema.addField(name +
"_y", type=
"D", doc=
"transformed reference centroid row",
449 if "slot_Centroid_flag" in schemaMapper.getInputSchema():
450 self.
flagKey = schema.addField(name +
"_flag", type=
"Flag",
451 doc=
"whether the reference centroid is marked as bad")
455 def measure(self, measRecord, exposure, refRecord, refWcs):
456 targetWcs = exposure.getWcs()
457 if not refWcs == targetWcs:
458 targetPos = targetWcs.skyToPixel(refWcs.pixelToSky(refRecord.getCentroid()))
461 measRecord.set(self.
centroidKey, refRecord.getCentroid())
463 measRecord.set(self.
flagKey, refRecord.getCentroidFlag())
472 """A shape pseudo-algorithm for forced measurement that simply transforms the shape 473 from the reference catalog to the measurement coordinate system. This is used as 474 the slot shape by default in forced measurement, allowing subsequent measurements 475 to simply refer to the slot value just as they would in single-frame measurement. 478 ConfigClass = ForcedTransformedShapeConfig
484 def __init__(self, config, name, schemaMapper, metadata):
485 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
486 schema = schemaMapper.editOutputSchema()
488 xxKey = schema.addField(name +
"_xx", type=
"D", doc=
"transformed reference shape x^2 moment",
490 yyKey = schema.addField(name +
"_yy", type=
"D", doc=
"transformed reference shape y^2 moment",
492 xyKey = schema.addField(name +
"_xy", type=
"D", doc=
"transformed reference shape xy moment",
498 if "slot_Shape_flag" in schemaMapper.getInputSchema():
499 self.
flagKey = schema.addField(name +
"_flag", type=
"Flag",
500 doc=
"whether the reference shape is marked as bad")
504 def measure(self, measRecord, exposure, refRecord, refWcs):
505 targetWcs = exposure.getWcs()
506 if not refWcs == targetWcs:
508 localTransform = fullTransform.linearizeForwardTransform(refRecord.getCentroid())
509 measRecord.set(self.
shapeKey, refRecord.getShape().
transform(localTransform.getLinear()))
511 measRecord.set(self.
shapeKey, refRecord.getShape())
513 measRecord.set(self.
flagKey, refRecord.getShapeFlag())
Base config class for all measurement plugins.
def measure(self, measRecord, exposure)
def getExecutionOrder(cls)
def getExecutionOrder(cls)
static std::shared_ptr< geom::SpanSet > fromShape(int r, Stencil s=Stencil::CIRCLE, Point2I offset=Point2I())
def fail(self, measRecord, error=None)
def __init__(self, config, name, schema, metadata)
def fail(self, measRecord, error=None)
def fail(self, measRecord, error=None)
def __init__(self, config, name, schema, metadata)
def measure(self, measRecord, exposure)
Base class for configs of single-frame plugin algorithms.
def getExecutionOrder(cls)
int FAILURE_EMPTY_FOOTPRINT
def fail(self, measRecord, error=None)
def __init__(self, config, name, schemaMapper, metadata)
Base class for single-frame plugin algorithms.
def getExecutionOrder(cls)
def fail(self, measRecord, error=None)
def getExecutionOrder(cls)
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 wrapTransform(transformClass, hasLogName=False)
def measure(self, measRecord, exposure, refRecord, refWcs)
def __init__(self, config, name, schema, metadata)
def measure(self, measRecord, exposure)
def measure(self, measRecord, exposure, center)
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...
def __init__(self, config, name, schema, metadata)
HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > makeHeavyFootprint(Footprint const &foot, lsst::afw::image::MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > const &img, HeavyFootprintCtrl const *ctrl=NULL)