LSST Applications 26.0.0,g0265f82a02+6660c170cc,g07994bdeae+30b05a742e,g0a0026dc87+17526d298f,g0a60f58ba1+17526d298f,g0e4bf8285c+96dd2c2ea9,g0ecae5effc+c266a536c8,g1e7d6db67d+6f7cb1f4bb,g26482f50c6+6346c0633c,g2bbee38e9b+6660c170cc,g2cc88a2952+0a4e78cd49,g3273194fdb+f6908454ef,g337abbeb29+6660c170cc,g337c41fc51+9a8f8f0815,g37c6e7c3d5+7bbafe9d37,g44018dc512+6660c170cc,g4a941329ef+4f7594a38e,g4c90b7bd52+5145c320d2,g58be5f913a+bea990ba40,g635b316a6c+8d6b3a3e56,g67924a670a+bfead8c487,g6ae5381d9b+81bc2a20b4,g93c4d6e787+26b17396bd,g98cecbdb62+ed2cb6d659,g98ffbb4407+81bc2a20b4,g9ddcbc5298+7f7571301f,ga1e77700b3+99e9273977,gae46bcf261+6660c170cc,gb2715bf1a1+17526d298f,gc86a011abf+17526d298f,gcf0d15dbbd+96dd2c2ea9,gdaeeff99f8+0d8dbea60f,gdb4ec4c597+6660c170cc,ge23793e450+96dd2c2ea9,gf041782ebf+171108ac67
LSST Data Management Base Package
Loading...
Searching...
No Matches
VeresPlugin.py
Go to the documentation of this file.
2# This file is part of meas_extensions_trailedSources.
3#
4# Developed for the LSST Data Management System.
5# This product includes software developed by the LSST Project
6# (http://www.lsst.org).
7# See the COPYRIGHT file at the top-level directory of this distribution
8# for details of code ownership.
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23
24import numpy as np
25import scipy.optimize as sciOpt
26
27from lsst.pex.config import Field
28
29from lsst.meas.base.pluginRegistry import register
30from lsst.meas.base import SingleFramePlugin, SingleFramePluginConfig
31from lsst.meas.base import FlagHandler, FlagDefinitionList, SafeCentroidExtractor
32
33from ._trailedSources import VeresModel
34from .NaivePlugin import SingleFrameNaiveTrailPlugin
35from .utils import getMeasurementCutout
36
37__all__ = ("SingleFrameVeresTrailConfig", "SingleFrameVeresTrailPlugin")
38
39
41 """Config class for SingleFrameVeresTrailPlugin
42 """
43
44 optimizerMethod = Field(
45 doc="Optimizer method for scipy.optimize.minimize",
46 dtype=str,
47 default="L-BFGS-B"
48 )
49
50
51@register("ext_trailedSources_Veres")
53 """Veres trailed source characterization plugin.
54
55 Measures the length, angle, flux, centroid, and end points of a trailed
56 source using the Veres et al. 2012 model [1]_.
57
58 Parameters
59 ----------
60 config: `SingleFrameNaiveTrailConfig`
61 Plugin configuration.
62 name: `str`
63 Plugin name.
64 schema: `lsst.afw.table.Schema`
65 Schema for the output catalog.
67 Metadata to be attached to output catalog.
68
69 Notes
70 -----
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.
78
79 References
80 ----------
81 .. [1] Vereš, P., et al. "Improved Asteroid Astrometry and Photometry with
82 Trail Fitting" PASP, vol. 124, 2012.
83
84 See also
85 --------
87 """
88
89 ConfigClass = SingleFrameVeresTrailConfig
90
91 @classmethod
93 # Needs centroids, shape, flux, and NaivePlugin measurements.
94 # Make sure this always runs after NaivePlugin.
95 return SingleFrameNaiveTrailPlugin.getExecutionOrder() + 0.1
96
97 def __init__(self, config, name, schema, metadata, logName=None):
98 super().__init__(config, name, schema, metadata, logName=logName)
99
100 self.keyXC = schema.addField(
101 name + "_centroid_x", type="D", doc="Trail centroid X coordinate.", units="pixel")
102 self.keyYC = schema.addField(
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.keyLength = 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")
112
113 flagDefs = FlagDefinitionList()
114 self.FAILURE = 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")
117 self.flagHandler = FlagHandler.addFields(schema, name, flagDefs)
118
120
121 def measure(self, measRecord, exposure):
122 """Run the Veres trailed source measurement plugin.
123
124 Parameters
125 ----------
126 measRecord : `lsst.afw.table.SourceRecord`
127 Record describing the object being measured.
128 exposure : `lsst.afw.image.Exposure`
129 Pixel data to be measured.
130
131 See also
132 --------
133 lsst.meas.base.SingleFramePlugin.measure
134 """
135 xc, yc = self.centroidExtractor(measRecord, self.flagHandler)
136
137 # Look at measRecord for Naive measurements
138 # ASSUMES NAIVE ALREADY RAN
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):
143 self.flagHandler.setValue(measRecord, self.NO_NAIVE.number)
144 self.flagHandler.setValue(measRecord, self.FAILURE.number)
145 return
146
147 # Get exposure cutout
148 # sigma = exposure.getPsf().getSigma()
149 # cutout = getMeasurementCutout(exposure, xc, yc, length, sigma)
150 cutout = getMeasurementCutout(measRecord, exposure)
151
152 # Make VeresModel
153 model = VeresModel(cutout)
154
155 # Do optimization with scipy
156 params = np.array([xc, yc, flux, length, theta])
157 results = sciOpt.minimize(
158 model, params, method=self.config.optimizerMethod, jac=model.gradient)
159
160 # Check if optimizer converged
161 if not results.success:
162 self.flagHandler.setValue(measRecord, self.NON_CONVERGE.number)
163 self.flagHandler.setValue(measRecord, self.FAILURE.number)
164 return
165
166 # Calculate end points and reduced chi-squared
167 xc_fit, yc_fit, flux_fit, length_fit, theta_fit = results.x
168 a = length_fit/2
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)
174
175 # Set keys
176 measRecord.set(self.keyXC, xc_fit)
177 measRecord.set(self.keyYC, yc_fit)
178 measRecord.set(self.keyX0, x0_fit)
179 measRecord.set(self.keyY0, y0_fit)
180 measRecord.set(self.keyX1, x1_fit)
181 measRecord.set(self.keyY1, y1_fit)
182 measRecord.set(self.keyFlux, flux_fit)
183 measRecord.set(self.keyLength, length_fit)
184 measRecord.set(self.keyTheta, theta_fit)
185 measRecord.set(self.keyRChiSq, rChiSq)
186
187 def fail(self, measRecord, error=None):
188 """Record failure
189
190 See also
191 --------
192 lsst.meas.base.SingleFramePlugin.fail
193 """
194 if error is None:
195 self.flagHandler.handleFailure(measRecord)
196 else:
197 self.flagHandler.handleFailure(measRecord, error.cpp)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition Exposure.h:72
Defines the fields and offsets for a table.
Definition Schema.h:51
Record class that contains measurements made on a single exposure.
Definition Source.h:78
Class for storing generic metadata.
Definition PropertySet.h:66
vector-type utility class to build a collection of FlagDefinitions
Definition FlagHandler.h:60
Utility class for measurement algorithms that extracts a position from the Centroid slot and handles ...
__init__(self, config, name, schema, metadata, logName=None)