24 Definitions and registration of pure-Python plugins with trivial implementations, 25 and automatic plugin-from-algorithm calls for those implemented in C++. 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 .localBackground
import LocalBackgroundControl, LocalBackgroundAlgorithm, LocalBackgroundTransform
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)
99 TransformClass=LocalBackgroundTransform, executionOrder=BasePlugin.FLUX_ORDER)
118 @register(
"base_FPPosition")
121 Algorithm to calculate the position of a centroid on the focal plane 124 ConfigClass = SingleFrameFPPositionConfig
130 def __init__(self, config, name, schema, metadata):
131 SingleFramePlugin.__init__(self, config, name, schema, metadata)
132 self.
focalValue = lsst.afw.table.Point2DKey.addFields(schema, name,
"Position on the focal plane",
134 self.
focalFlag = schema.addField(name +
"_flag", type=
"Flag", doc=
"Set to True for any fatal failure")
135 self.
detectorFlag = schema.addField(name +
"_missingDetector_flag", type=
"Flag",
136 doc=
"Set to True if detector object is missing")
139 det = exposure.getDetector()
144 center = measRecord.getCentroid()
145 fp = det.transform(center, lsst.afw.cameraGeom.PIXELS, lsst.afw.cameraGeom.FOCAL_PLANE)
148 def fail(self, measRecord, error=None):
153 pixelScale = lsst.pex.config.Field(dtype=float, default=0.5, doc=
"Nominal pixel size (arcsec)")
156 @register(
"base_Jacobian")
159 Algorithm which computes the Jacobian about a source and computes its ratio with a nominal pixel area. 160 This allows one to compare relative instead of absolute areas of pixels. 163 ConfigClass = SingleFrameJacobianConfig
169 def __init__(self, config, name, schema, metadata):
170 SingleFramePlugin.__init__(self, config, name, schema, metadata)
171 self.
jacValue = schema.addField(name +
'_value', type=
"D", doc=
"Jacobian correction")
172 self.
jacFlag = schema.addField(name +
'_flag', type=
"Flag", doc=
"Set to 1 for any fatal failure")
177 center = measRecord.getCentroid()
180 result = np.abs(self.
scale*exposure.getWcs().linearizePixelToSky(
182 lsst.geom.arcseconds).getLinear().computeDeterminant())
183 measRecord.set(self.
jacValue, result)
185 def fail(self, measRecord, error=None):
186 measRecord.set(self.
jacFlag,
True)
190 scale = lsst.pex.config.Field(dtype=float, default=5.0, optional=
True,
191 doc=
"Scale factor to apply to shape for aperture")
192 mask = lsst.pex.config.ListField(doc=
"Mask planes to ignore", dtype=str,
193 default=[
"DETECTED",
"DETECTED_NEGATIVE",
"BAD",
"SAT"])
198 Calculate the median variance within a Footprint scaled from the object shape so 199 the value is not terribly influenced by the object and instead represents the 200 variance in the background near the object. 202 ConfigClass = VarianceConfig
203 FAILURE_BAD_CENTROID = 1
204 FAILURE_EMPTY_FOOTPRINT = 2
208 return BasePlugin.FLUX_ORDER
210 def __init__(self, config, name, schema, metadata):
211 GenericPlugin.__init__(self, config, name, schema, metadata)
212 self.
varValue = schema.addField(name +
'_value', type=
"D", doc=
"Variance at object position")
214 doc=
"Set to True when the footprint has no usable pixels")
218 schema.getAliasMap().set(name +
'_flag_badCentroid', schema.getAliasMap().apply(
"slot_Centroid_flag"))
220 def measure(self, measRecord, exposure, center):
223 if not np.all(np.isfinite(measRecord.getCentroid())):
225 aperture = lsst.afw.geom.Ellipse(measRecord.getShape(), measRecord.getCentroid())
226 aperture.scale(self.
config.scale)
229 foot.clipTo(exposure.getBBox(lsst.afw.image.PARENT))
232 maskedImage = exposure.getMaskedImage()
234 maskBits = maskedImage.getMask().getPlaneBitMask(self.
config.mask)
235 logicalMask = np.logical_not(pixels.getMaskArray() & maskBits)
239 if np.any(logicalMask):
240 medVar = np.median(pixels.getVarianceArray()[logicalMask])
241 measRecord.set(self.
varValue, medVar)
243 raise MeasurementError(
"Footprint empty, or all pixels are masked, can't compute median",
246 def fail(self, measRecord, error=None):
248 if isinstance(error, MeasurementError):
253 measRecord.set(self.
varValue, np.nan)
254 GenericPlugin.fail(self, measRecord, error)
257 SingleFrameVariancePlugin = VariancePlugin.makeSingleFramePlugin(
"base_Variance")
258 ForcedVariancePlugin = VariancePlugin.makeForcedPlugin(
"base_Variance")
265 class InputCountPlugin(GenericPlugin):
267 Plugin to count how many input images contributed to each source. This information 268 is in the exposure's coaddInputs. Some limitations: 269 * This is only for the pixel containing the center, not for all the pixels in the 271 * This does not account for any clipping in the coadd 274 ConfigClass = InputCountConfig
275 FAILURE_BAD_CENTROID = 1
276 FAILURE_NO_INPUTS = 2
280 return BasePlugin.SHAPE_ORDER
282 def __init__(self, config, name, schema, metadata):
283 GenericPlugin.__init__(self, config, name, schema, metadata)
284 self.
numberKey = schema.addField(name +
'_value', type=
"I",
285 doc=
"Number of images contributing at center, not including any" 287 self.
noInputsFlag = schema.addField(name +
'_flag_noInputs', type=
"Flag",
288 doc=
"No coadd inputs available")
291 schema.getAliasMap().set(name +
'_flag_badCentroid', schema.getAliasMap().apply(
"slot_Centroid_flag"))
293 def measure(self, measRecord, exposure, center):
294 if not exposure.getInfo().getCoaddInputs():
296 if not np.all(np.isfinite(center)):
299 ccds = exposure.getInfo().getCoaddInputs().ccds
300 measRecord.set(self.
numberKey, len(ccds.subsetContaining(center, exposure.getWcs())))
302 def fail(self, measRecord, error=None):
303 if error
is not None:
308 GenericPlugin.fail(self, measRecord, error)
311 SingleFrameInputCountPlugin = InputCountPlugin.makeSingleFramePlugin(
"base_InputCount")
312 ForcedInputCountPlugin = InputCountPlugin.makeForcedPlugin(
"base_InputCount")
319 @register(
"base_PeakCentroid")
322 A centroid algorithm that simply uses the first (i.e. highest) Peak in the Source's 323 Footprint as the centroid. This is of course a relatively poor measure of the true 324 centroid of the object; this algorithm is provided mostly for testing and debugging. 327 ConfigClass = SingleFramePeakCentroidConfig
333 def __init__(self, config, name, schema, metadata):
334 SingleFramePlugin.__init__(self, config, name, schema, metadata)
335 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
336 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
337 self.
flag = schema.addField(name +
"_flag", type=
"Flag", doc=
"Centroiding failed")
340 peak = measRecord.getFootprint().getPeaks()[0]
341 measRecord.set(self.
keyX, peak.getFx())
342 measRecord.set(self.
keyY, peak.getFy())
344 def fail(self, measRecord, error=None):
345 measRecord.set(self.
flag,
True)
349 return SimpleCentroidTransform
356 @register(
"base_SkyCoord")
359 A measurement plugin that sets the "coord" field (part of the Source minimal schema) 360 using the slot centroid and the Wcs attached to the Exposure. 363 ConfigClass = SingleFrameSkyCoordConfig
372 if not exposure.hasWcs():
373 raise Exception(
"Wcs not attached to exposure. Required for " + self.
name +
" algorithm")
374 measRecord.updateCoord(exposure.getWcs())
376 def fail(self, measRecord, error=None):
385 class ForcedPeakCentroidConfig(ForcedPluginConfig):
389 @register(
"base_PeakCentroid")
392 The forced peak centroid is like the SFM peak centroid plugin, except that it must transform 393 the peak coordinate from the original (reference) coordinate system to the coordinate system 394 of the exposure being measured. 397 ConfigClass = ForcedPeakCentroidConfig
403 def __init__(self, config, name, schemaMapper, metadata):
404 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
405 schema = schemaMapper.editOutputSchema()
406 self.
keyX = schema.addField(name +
"_x", type=
"D", doc=
"peak centroid", units=
"pixel")
407 self.
keyY = schema.addField(name +
"_y", type=
"D", doc=
"peak centroid", units=
"pixel")
409 def measure(self, measRecord, exposure, refRecord, refWcs):
410 targetWcs = exposure.getWcs()
411 peak = refRecord.getFootprint().getPeaks()[0]
413 result = targetWcs.skyToPixel(refWcs.pixelToSky(result))
414 measRecord.set(self.
keyX, result.getX())
415 measRecord.set(self.
keyY, result.getY())
419 return SimpleCentroidTransform
426 @register(
"base_TransformedCentroid")
428 """A centroid pseudo-algorithm for forced measurement that simply transforms the centroid 429 from the reference catalog to the measurement coordinate system. This is used as 430 the slot centroid by default in forced measurement, allowing subsequent measurements 431 to simply refer to the slot value just as they would in single-frame measurement. 434 ConfigClass = ForcedTransformedCentroidConfig
440 def __init__(self, config, name, schemaMapper, metadata):
441 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
442 schema = schemaMapper.editOutputSchema()
444 xKey = schema.addField(name +
"_x", type=
"D", doc=
"transformed reference centroid column",
446 yKey = schema.addField(name +
"_y", type=
"D", doc=
"transformed reference centroid row",
452 if "slot_Centroid_flag" in schemaMapper.getInputSchema():
453 self.
flagKey = schema.addField(name +
"_flag", type=
"Flag",
454 doc=
"whether the reference centroid is marked as bad")
458 def measure(self, measRecord, exposure, refRecord, refWcs):
459 targetWcs = exposure.getWcs()
460 if not refWcs == targetWcs:
461 targetPos = targetWcs.skyToPixel(refWcs.pixelToSky(refRecord.getCentroid()))
464 measRecord.set(self.
centroidKey, refRecord.getCentroid())
466 measRecord.set(self.
flagKey, refRecord.getCentroidFlag())
473 @register(
"base_TransformedShape")
475 """A shape pseudo-algorithm for forced measurement that simply transforms the shape 476 from the reference catalog to the measurement coordinate system. This is used as 477 the slot shape by default in forced measurement, allowing subsequent measurements 478 to simply refer to the slot value just as they would in single-frame measurement. 481 ConfigClass = ForcedTransformedShapeConfig
487 def __init__(self, config, name, schemaMapper, metadata):
488 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
489 schema = schemaMapper.editOutputSchema()
491 xxKey = schema.addField(name +
"_xx", type=
"D", doc=
"transformed reference shape x^2 moment",
493 yyKey = schema.addField(name +
"_yy", type=
"D", doc=
"transformed reference shape y^2 moment",
495 xyKey = schema.addField(name +
"_xy", type=
"D", doc=
"transformed reference shape xy moment",
501 if "slot_Shape_flag" in schemaMapper.getInputSchema():
502 self.
flagKey = schema.addField(name +
"_flag", type=
"Flag",
503 doc=
"whether the reference shape is marked as bad")
507 def measure(self, measRecord, exposure, refRecord, refWcs):
508 targetWcs = exposure.getWcs()
509 if not refWcs == targetWcs:
512 measRecord.set(self.
shapeKey, refRecord.getShape().
transform(localTransform.getLinear()))
514 measRecord.set(self.
shapeKey, refRecord.getShape())
516 measRecord.set(self.
flagKey, refRecord.getShapeFlag())
Base config class for all measurement plugins.
def measure(self, measRecord, exposure)
def getExecutionOrder(cls)
def getExecutionOrder(cls)
def fail(self, measRecord, error=None)
lsst::geom::AffineTransform linearizeTransform(TransformPoint2ToPoint2 const &original, lsst::geom::Point2D const &inPoint)
def __init__(self, config, name, schema, metadata)
def fail(self, measRecord, error=None)
static std::shared_ptr< geom::SpanSet > fromShape(int r, Stencil s=Stencil::CIRCLE, lsst::geom::Point2I offset=lsst::geom::Point2I())
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 getExecutionOrder(cls)
def measure(self, measRecord, exposure)
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
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)