25import scipy.optimize
as sciOpt
30from lsst.meas.base import SingleFramePlugin, SingleFramePluginConfig
31from lsst.meas.base import FlagHandler, FlagDefinitionList, SafeCentroidExtractor
34from ._trailedSources
import VeresModel
35from .NaivePlugin
import SingleFrameNaiveTrailPlugin
37__all__ = (
"SingleFrameVeresTrailConfig",
"SingleFrameVeresTrailPlugin")
41 """Config class for SingleFrameVeresTrailPlugin
44 optimizerMethod = Field(
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.
86 lsst.meas.base.SingleFramePlugin
89 ConfigClass = SingleFrameVeresTrailConfig
95 return SingleFrameNaiveTrailPlugin.getExecutionOrder() + 0.1
97 def __init__(self, config, name, schema, metadata):
98 super().
__init__(config, name, schema, metadata)
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.
keyX0 = schema.addField(name +
"_x0", type=
"D", doc=
"Trail head X coordinate.", units=
"pixel")
105 self.
keyY0 = schema.addField(name +
"_y0", type=
"D", doc=
"Trail head Y coordinate.", units=
"pixel")
106 self.
keyX1 = schema.addField(name +
"_x1", type=
"D", doc=
"Trail tail X coordinate.", units=
"pixel")
107 self.
keyY1 = schema.addField(name +
"_y1", type=
"D", doc=
"Trail tail Y coordinate.", units=
"pixel")
108 self.
keyL = schema.addField(name +
"_length", type=
"D", doc=
"Length of trail.", units=
"pixel")
109 self.
keyTheta = schema.addField(name +
"_angle", type=
"D", doc=
"Angle of trail from +x-axis.")
110 self.
keyFlux = schema.addField(name +
"_flux", type=
"D", doc=
"Trailed source flux.", units=
"count")
111 self.
keyRChiSq = schema.addField(name +
"_rChiSq", type=
"D", doc=
"Reduced chi-squared of fit")
114 flagDefs.addFailureFlag(
"No trailed-sources measured")
115 self.
NON_CONVERGE = flagDefs.add(
"flag_nonConvergence",
"Optimizer did not converge")
116 self.
NO_NAIVE = flagDefs.add(
"flag_noNaive",
"Naive measurement contains NaNs")
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 F = measRecord.get(
"ext_trailedSources_Naive_flux")
140 L = measRecord.get(
"ext_trailedSources_Naive_length")
141 theta = measRecord.get(
"ext_trailedSources_Naive_angle")
142 if not np.isfinite(F)
or not np.isfinite(L)
or not np.isfinite(theta):
146 model = VeresModel(exposure)
149 params = np.array([xc, yc, F, L, theta])
150 results = sciOpt.minimize(
151 model, params, method=self.config.optimizerMethod, jac=model.gradient)
154 if not results.success:
158 xc_fit, yc_fit, F_fit, L_fit, theta_fit = results.x
160 x0_fit = xc_fit - a * np.cos(theta_fit)
161 y0_fit = yc_fit - a * np.sin(theta_fit)
162 x1_fit = xc_fit + a * np.cos(theta_fit)
163 y1_fit = yc_fit + a * np.sin(theta_fit)
164 rChiSq = results.fun / (exposure.image.array.size - 6)
167 measRecord.set(self.
keyXC, xc_fit)
168 measRecord.set(self.
keyYC, yc_fit)
169 measRecord.set(self.
keyX0, x0_fit)
170 measRecord.set(self.
keyY0, y0_fit)
171 measRecord.set(self.
keyX1, x1_fit)
172 measRecord.set(self.
keyY1, y1_fit)
173 measRecord.set(self.
keyFlux, F_fit)
174 measRecord.set(self.
keyL, L_fit)
175 measRecord.set(self.
keyTheta, theta_fit)
178 def fail(self, measRecord, error=None):
183 lsst.meas.base.SingleFramePlugin.fail
188 self.
flagHandler.handleFailure(measRecord, error.cpp)
def fail(self, measRecord, error=None)
def getExecutionOrder(cls)
def __init__(self, config, name, schema, metadata)
def measure(self, measRecord, exposure)