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
36 from .pluginRegistry
import register
37 from .pluginsBase
import BasePlugin
38 from .baseMeasurement
import BaseMeasurementPluginConfig
39 from .sfm
import SingleFramePluginConfig, SingleFramePlugin
40 from .forcedMeasurement
import ForcedPluginConfig, ForcedPlugin
41 from .wrappers
import wrapSimpleAlgorithm, wrapTransform, GenericPlugin
42 from .transforms
import SimpleCentroidTransform
44 from .apertureFlux
import ApertureFluxControl, ApertureFluxTransform
45 from .transform
import BaseTransform
46 from .blendedness
import BlendednessAlgorithm, BlendednessControl
47 from .circularApertureFlux
import CircularApertureFluxAlgorithm
48 from .gaussianFlux
import GaussianFluxAlgorithm, GaussianFluxControl, GaussianFluxTransform
49 from .exceptions
import MeasurementError
50 from .naiveCentroid
import NaiveCentroidAlgorithm, NaiveCentroidControl, NaiveCentroidTransform
51 from .peakLikelihoodFlux
import PeakLikelihoodFluxAlgorithm, PeakLikelihoodFluxControl, \
52 PeakLikelihoodFluxTransform
53 from .pixelFlags
import PixelFlagsAlgorithm, PixelFlagsControl
54 from .psfFlux
import PsfFluxAlgorithm, PsfFluxControl, PsfFluxTransform
55 from .scaledApertureFlux
import ScaledApertureFluxAlgorithm, ScaledApertureFluxControl, \
56 ScaledApertureFluxTransform
57 from .sdssCentroid
import SdssCentroidAlgorithm, SdssCentroidControl, SdssCentroidTransform
58 from .sdssShape
import SdssShapeAlgorithm, SdssShapeControl, SdssShapeTransform
61 "SingleFrameFPPositionConfig",
"SingleFrameFPPositionPlugin",
62 "SingleFrameJacobianConfig",
"SingleFrameJacobianPlugin",
63 "VarianceConfig",
"SingleFrameVariancePlugin",
"ForcedVariancePlugin",
64 "InputCountConfig",
"SingleFrameInputCountPlugin",
"ForcedInputCountPlugin",
65 "SingleFramePeakCentroidConfig",
"SingleFramePeakCentroidPlugin",
66 "SingleFrameSkyCoordConfig",
"SingleFrameSkyCoordPlugin",
67 "ForcedPeakCentroidConfig",
"ForcedPeakCentroidPlugin",
68 "ForcedTransformedCentroidConfig",
"ForcedTransformedCentroidPlugin",
69 "ForcedTransformedShapeConfig",
"ForcedTransformedShapePlugin",
75 TransformClass=PsfFluxTransform, executionOrder=BasePlugin.FLUX_ORDER,
76 shouldApCorr=
True, hasLogName=
True)
78 TransformClass=PeakLikelihoodFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
80 TransformClass=GaussianFluxTransform, executionOrder=BasePlugin.FLUX_ORDER,
83 TransformClass=NaiveCentroidTransform, executionOrder=BasePlugin.CENTROID_ORDER)
85 TransformClass=SdssCentroidTransform, executionOrder=BasePlugin.CENTROID_ORDER)
87 executionOrder=BasePlugin.FLUX_ORDER)
89 TransformClass=SdssShapeTransform, executionOrder=BasePlugin.SHAPE_ORDER)
91 TransformClass=ScaledApertureFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
94 TransformClass=ApertureFluxTransform, executionOrder=BasePlugin.FLUX_ORDER)
96 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()
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"])
195 Calculate the median variance within a Footprint scaled from the object shape so 196 the value is not terribly influenced by the object and instead represents the 197 variance in the background near the object. 199 ConfigClass = VarianceConfig
200 FAILURE_BAD_CENTROID = 1
201 FAILURE_EMPTY_FOOTPRINT = 2
205 return BasePlugin.FLUX_ORDER
207 def __init__(self, config, name, schema, metadata):
208 GenericPlugin.__init__(self, config, name, schema, metadata)
209 self.
varValue = schema.addField(name +
'_value', type=
"D", doc=
"Variance at object position")
211 doc=
"Set to True when the footprint has no usable pixels")
215 schema.getAliasMap().set(name +
'_flag_badCentroid', schema.getAliasMap().apply(
"slot_Centroid_flag"))
217 def measure(self, measRecord, exposure, center):
220 if not numpy.all(numpy.isfinite(measRecord.getCentroid())):
222 aperture = lsst.afw.geom.Ellipse(measRecord.getShape(), measRecord.getCentroid())
223 aperture.scale(self.
config.scale)
226 foot.clipTo(exposure.getBBox(lsst.afw.image.PARENT))
229 maskedImage = exposure.getMaskedImage()
231 maskBits = maskedImage.getMask().getPlaneBitMask(self.
config.mask)
232 logicalMask = numpy.logical_not(pixels.getMaskArray() & maskBits)
236 if numpy.any(logicalMask):
237 medVar = numpy.median(pixels.getVarianceArray()[logicalMask])
238 measRecord.set(self.
varValue, medVar)
240 raise MeasurementError(
"Footprint empty, or all pixels are masked, can't compute median",
243 def fail(self, measRecord, error=None):
245 if isinstance(error, MeasurementError):
250 measRecord.set(self.
varValue, numpy.nan)
251 GenericPlugin.fail(self, measRecord, error)
254 SingleFrameVariancePlugin = VariancePlugin.makeSingleFramePlugin(
"base_Variance")
255 ForcedVariancePlugin = VariancePlugin.makeForcedPlugin(
"base_Variance")
262 class InputCountPlugin(GenericPlugin):
264 Plugin to count how many input images contributed to each source. This information 265 is in the exposure's coaddInputs. Some limitations: 266 * This is only for the pixel containing the center, not for all the pixels in the 268 * This does not account for any clipping in the coadd 271 ConfigClass = InputCountConfig
272 FAILURE_BAD_CENTROID = 1
273 FAILURE_NO_INPUTS = 2
277 return BasePlugin.SHAPE_ORDER
279 def __init__(self, config, name, schema, metadata):
280 GenericPlugin.__init__(self, config, name, schema, metadata)
281 self.
numberKey = schema.addField(name +
'_value', type=
"I",
282 doc=
"Number of images contributing at center, not including any" 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"))
290 def measure(self, measRecord, exposure, center):
291 if not exposure.getInfo().getCoaddInputs():
293 if not numpy.all(numpy.isfinite(center)):
296 ccds = exposure.getInfo().getCoaddInputs().ccds
297 measRecord.set(self.
numberKey, len(ccds.subsetContaining(center, exposure.getWcs())))
299 def fail(self, measRecord, error=None):
300 if error
is not None:
305 GenericPlugin.fail(self, measRecord, error)
308 SingleFrameInputCountPlugin = InputCountPlugin.makeSingleFramePlugin(
"base_InputCount")
309 ForcedInputCountPlugin = InputCountPlugin.makeForcedPlugin(
"base_InputCount")
319 A centroid algorithm that simply uses the first (i.e. highest) Peak in the Source's 320 Footprint as the centroid. This is of course a relatively poor measure of the true 321 centroid of the object; this algorithm is provided mostly for testing and debugging. 324 ConfigClass = SingleFramePeakCentroidConfig
330 def __init__(self, config, name, schema, metadata):
331 SingleFramePlugin.__init__(self, config, name, schema, metadata)
332 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
333 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
334 self.
flag = schema.addField(name +
"_flag", type=
"Flag", doc=
"Centroiding failed")
337 peak = measRecord.getFootprint().getPeaks()[0]
338 measRecord.set(self.
keyX, peak.getFx())
339 measRecord.set(self.
keyY, peak.getFy())
341 def fail(self, measRecord, error=None):
342 measRecord.set(self.
flag,
True)
346 return SimpleCentroidTransform
356 A measurement plugin that sets the "coord" field (part of the Source minimal schema) 357 using the slot centroid and the Wcs attached to the Exposure. 360 ConfigClass = SingleFrameSkyCoordConfig
369 if not exposure.hasWcs():
370 raise Exception(
"Wcs not attached to exposure. Required for " + self.
name +
" algorithm")
371 measRecord.updateCoord(exposure.getWcs())
373 def fail(self, measRecord, error=None):
382 class ForcedPeakCentroidConfig(ForcedPluginConfig):
389 The forced peak centroid is like the SFM peak centroid plugin, except that it must transform 390 the peak coordinate from the original (reference) coordinate system to the coordinate system 391 of the exposure being measured. 394 ConfigClass = ForcedPeakCentroidConfig
400 def __init__(self, config, name, schemaMapper, metadata):
401 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
402 schema = schemaMapper.editOutputSchema()
403 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
404 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
406 def measure(self, measRecord, exposure, refRecord, refWcs):
407 targetWcs = exposure.getWcs()
408 peak = refRecord.getFootprint().getPeaks()[0]
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:
507 fullTransform = makeWcsPairTransform(refWcs, targetWcs)
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.
AffineTransform linearizeTransform(TransformPoint2ToPoint2 const &original, Point2D const &inPoint)
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)