25import scipy.optimize
as sciOpt
30from lsst.meas.base import SingleFramePlugin, SingleFramePluginConfig
31from lsst.meas.base import FlagHandler, FlagDefinitionList, SafeCentroidExtractor
33from ._trailedSources
import VeresModel
34from .NaivePlugin
import SingleFrameNaiveTrailPlugin
35from .utils
import getMeasurementCutout
37__all__ = (
"SingleFrameVeresTrailConfig",
"SingleFrameVeresTrailPlugin")
41 """Config class for SingleFrameVeresTrailPlugin
45 doc="Optimizer method for scipy.optimize.minimize",
51@register("ext_trailedSources_Veres")
53 """Veres trailed source characterization plugin.
55 Measures the length, angle, flux, centroid, and end points of a trailed
56 source using the Veres et al. 2012 model [1]_.
60 config: `SingleFrameNaiveTrailConfig`
65 Schema
for the output catalog.
67 Metadata to be attached to output catalog.
71 This plugin
is designed to refine the measurements of trail length,
72 angle,
and end points
from `NaivePlugin`,
and of flux
and centroid
from
73 previous measurement algorithms. Vereš et al. 2012 [1]_ derive a model
for
74 the flux
in a given image pixel by convolving an axisymmetric Gaussian
with
75 a line. The model
is parameterized by the total flux, trail length, angle
76 from the x-axis,
and the centroid. The best estimates are computed using a
77 chi-squared minimization.
81 .. [1] Vereš, P., et al.
"Improved Asteroid Astrometry and Photometry with
82 Trail Fitting" PASP, vol. 124, 2012.
89 ConfigClass = SingleFrameVeresTrailConfig
95 return SingleFrameNaiveTrailPlugin.getExecutionOrder() + 0.1
97 def __init__(self, config, name, schema, metadata, logName=None):
98 super().
__init__(config, name, schema, metadata, logName=logName)
101 name +
"_centroid_x", type=
"D", doc=
"Trail centroid X coordinate.", units=
"pixel")
103 name +
"_centroid_y", type=
"D", doc=
"Trail centroid Y coordinate.", units=
"pixel")
104 self.
keyX0keyX0 = schema.addField(name +
"_x0", type=
"D", doc=
"Trail head X coordinate.", units=
"pixel")
105 self.
keyY0keyY0 = schema.addField(name +
"_y0", type=
"D", doc=
"Trail head Y coordinate.", units=
"pixel")
106 self.
keyX1keyX1 = schema.addField(name +
"_x1", type=
"D", doc=
"Trail tail X coordinate.", units=
"pixel")
107 self.
keyY1keyY1 = schema.addField(name +
"_y1", type=
"D", doc=
"Trail tail Y coordinate.", units=
"pixel")
108 self.
keyLengthkeyLength = schema.addField(name +
"_length", type=
"D", doc=
"Length of trail.", units=
"pixel")
109 self.
keyThetakeyTheta = schema.addField(name +
"_angle", type=
"D", doc=
"Angle of trail from +x-axis.")
110 self.
keyFluxkeyFlux = schema.addField(name +
"_flux", type=
"D", doc=
"Trailed source flux.", units=
"count")
111 self.
keyRChiSqkeyRChiSq = schema.addField(name +
"_rChiSq", type=
"D", doc=
"Reduced chi-squared of fit")
114 self.
FAILUREFAILURE = flagDefs.addFailureFlag(
"No trailed-sources measured")
115 self.
NON_CONVERGENON_CONVERGE = flagDefs.add(
"flag_nonConvergence",
"Optimizer did not converge")
116 self.
NO_NAIVENO_NAIVE = flagDefs.add(
"flag_noNaive",
"Naive measurement contains NaNs")
117 self.
flagHandlerflagHandler = FlagHandler.addFields(schema, name, flagDefs)
122 """Run the Veres trailed source measurement plugin.
127 Record describing the object being measured.
129 Pixel data to be measured.
133 lsst.meas.base.SingleFramePlugin.measure
139 flux = measRecord.get(
"ext_trailedSources_Naive_flux")
140 length = measRecord.get(
"ext_trailedSources_Naive_length")
141 theta = measRecord.get(
"ext_trailedSources_Naive_angle")
142 if not np.isfinite(flux)
or not np.isfinite(length)
or not np.isfinite(theta):
153 model = VeresModel(cutout)
156 params = np.array([xc, yc, flux, length, theta])
157 results = sciOpt.minimize(
158 model, params, method=self.
configconfig.optimizerMethod, jac=model.gradient)
161 if not results.success:
167 xc_fit, yc_fit, flux_fit, length_fit, theta_fit = results.x
169 x0_fit = xc_fit - a * np.cos(theta_fit)
170 y0_fit = yc_fit - a * np.sin(theta_fit)
171 x1_fit = xc_fit + a * np.cos(theta_fit)
172 y1_fit = yc_fit + a * np.sin(theta_fit)
173 rChiSq = results.fun / (cutout.image.array.size - 6)
176 measRecord.set(self.
keyXCkeyXC, xc_fit)
177 measRecord.set(self.
keyYCkeyYC, yc_fit)
178 measRecord.set(self.
keyX0keyX0, x0_fit)
179 measRecord.set(self.
keyY0keyY0, y0_fit)
180 measRecord.set(self.
keyX1keyX1, x1_fit)
181 measRecord.set(self.
keyY1keyY1, y1_fit)
182 measRecord.set(self.
keyFluxkeyFlux, flux_fit)
183 measRecord.set(self.
keyLengthkeyLength, length_fit)
184 measRecord.set(self.
keyThetakeyTheta, theta_fit)
185 measRecord.set(self.
keyRChiSqkeyRChiSq, rChiSq)
187 def fail(self, measRecord, error=None):
192 lsst.meas.base.SingleFramePlugin.fail
195 self.
flagHandlerflagHandler.handleFailure(measRecord)
197 self.
flagHandlerflagHandler.handleFailure(measRecord, error.cpp)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Defines the fields and offsets for a table.
Record class that contains measurements made on a single exposure.
Class for storing generic metadata.
vector-type utility class to build a collection of FlagDefinitions
def fail(self, measRecord, error=None)
def getExecutionOrder(cls)
def measure(self, measRecord, exposure)
def __init__(self, config, name, schema, metadata, logName=None)
def getMeasurementCutout(measRecord, exposure)