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
matchOptimisticBTask.py
Go to the documentation of this file.
2__all__ = ["MatchOptimisticBTask", "MatchOptimisticBConfig",
3 "MatchTolerance"]
4
5import math
6
7import lsst.pex.config as pexConfig
8import lsst.pipe.base as pipeBase
9from lsst.utils.timer import timeMethod
10
11from .setMatchDistance import setMatchDistance
12from .matchOptimisticB import matchOptimisticB, MatchOptimisticBControl
13
14
16 """Stores match tolerances for use in `lsst.meas.astrom.AstrometryTask` and
17 later iterations of the matcher.
18
19 MatchOptimsiticBTask relies on a maximum distance for matching
20 set by either the default in MatchOptimisticBConfig or the 2 sigma
21 scatter found after AstrometryTask has fit for a wcs.
22
23 Parameters
24 ----------
25 maxMatchDist : `lsst.geom.Angle`
26 Current maximum distance to consider a match.
27 """
28
29 def __init__(self, maxMatchDist=None):
30 self.maxMatchDist = maxMatchDist
31
32
33class MatchOptimisticBConfig(pexConfig.Config):
34 """Configuration for MatchOptimisticBTask
35 """
36 maxMatchDistArcSec = pexConfig.RangeField(
37 doc="Maximum separation between reference objects and sources "
38 "beyond which they will not be considered a match (arcsec)",
39 dtype=float,
40 default=3,
41 min=0,
42 )
43 numBrightStars = pexConfig.RangeField(
44 doc="Number of bright stars to use",
45 dtype=int,
46 default=50,
47 min=2,
48 )
49 minMatchedPairs = pexConfig.RangeField(
50 doc="Minimum number of matched pairs; see also minFracMatchedPairs",
51 dtype=int,
52 default=30,
53 min=2,
54 )
55 minFracMatchedPairs = pexConfig.RangeField(
56 doc="Minimum number of matched pairs as a fraction of the smaller of "
57 "the number of reference stars or the number of good sources; "
58 "the actual minimum is the smaller of this value or minMatchedPairs",
59 dtype=float,
60 default=0.3,
61 min=0,
62 max=1,
63 )
64 maxOffsetPix = pexConfig.RangeField(
65 doc="Maximum allowed shift of WCS, due to matching (pixel). "
66 "When changing this value, the LoadReferenceObjectsConfig.pixelMargin should also be updated.",
67 dtype=int,
68 default=250,
69 max=4000,
70 )
71 maxRotationDeg = pexConfig.RangeField(
72 doc="Rotation angle allowed between sources and position reference objects (degrees)",
73 dtype=float,
74 default=1.0,
75 max=6.0,
76 )
77 allowedNonperpDeg = pexConfig.RangeField(
78 doc="Allowed non-perpendicularity of x and y (degree)",
79 dtype=float,
80 default=3.0,
81 max=45.0,
82 )
83 numPointsForShape = pexConfig.Field(
84 doc="number of points to define a shape for matching",
85 dtype=int,
86 default=6,
87 )
88 maxDeterminant = pexConfig.Field(
89 doc="maximum determinant of linear transformation matrix for a usable solution",
90 dtype=float,
91 default=0.02,
92 )
93
94
95# The following block adds links to this task from the Task Documentation page.
96# \addtogroup LSST_task_documentation
97# \{
98# \page measAstrom_matchOptimisticBTask
99# \ref MatchOptimisticBTask "MatchOptimisticBTask"
100# Match sources to reference objects
101# \}
102
103
104class MatchOptimisticBTask(pipeBase.Task):
105 """Match sources to reference objects using the Optimistic Pattern Matcher
106 B algorithm of Tabur 2007.
107 """
108 ConfigClass = MatchOptimisticBConfig
109 _DefaultName = "matchObjectsToSources"
110
111 def __init__(self, **kwargs):
112 pipeBase.Task.__init__(self, **kwargs)
113
114 def filterStars(self, refCat):
115 """Extra filtering pass; subclass if desired.
116
117 Parameters
118 ----------
120 Catalog of reference objects.
121
122 Returns
123 -------
124 trimmedRefCat : `lsst.afw.table.SimpleCatalog`
125 Reference catalog with some filtering applied. Currently no
126 filtering is applied.
127 """
128 return refCat
129
130 @timeMethod
131 def matchObjectsToSources(self, refCat, sourceCat, wcs, sourceFluxField, refFluxField,
132 match_tolerance=None):
133 """Match sources to position reference stars.
134
135 Parameters
136 ----------
138 Reference catalog to match.
139 sourceCat : `lsst.afw.table.SourceCatalog`
140 Catalog of sources found on an exposure. This should already be
141 down-selected to "good"/"usable" sources in the calling Task.
143 Current WCS of the exposure containing the sources.
144 sourceFluxField : `str`
145 Field of the sourceCat to use for flux
146 refFluxField : `str`
147 Field of the refCat to use for flux
148 match_tolerance : `lsst.meas.astrom.MatchTolerance`
149 Object containing information from previous
150 `lsst.meas.astrom.AstrometryTask` match/fit cycles for use in
151 matching. If `None` is config defaults.
152
153 Returns
154 -------
155 matchResult : `lsst.pipe.base.Struct`
156 Result struct with components
157
158 - ``matches`` : List of matches with distance below the maximum match
159 distance (`list` of `lsst.afw.table.ReferenceMatch`).
160 - ``useableSourceCat`` : Catalog of sources matched and suited for
161 WCS fitting (`lsst.afw.table.SourceCatalog`).
162 - ``match_tolerance`` : MatchTolerance object updated from this
163 match iteration (`lsst.meas.astrom.MatchTolerance`).
164 """
165 import lsstDebug
166 debug = lsstDebug.Info(__name__)
167
168 preNumObj = len(refCat)
169 refCat = self.filterStars(refCat)
170 numRefObj = len(refCat)
171
172 if self.log:
173 self.log.info("filterStars purged %d reference stars, leaving %d stars",
174 preNumObj - numRefObj, numRefObj)
175
176 if match_tolerance is None:
177 match_tolerance = MatchTolerance()
178
179 # Make a name alias here for consistency with older code, and to make
180 # it clear that this is a good/usable (cleaned) source catalog.
181 usableSourceCat = sourceCat
182
183 numUsableSources = len(usableSourceCat)
184
185 if len(usableSourceCat) == 0:
186 raise pipeBase.TaskError("No sources are usable")
187
188 minMatchedPairs = min(self.config.minMatchedPairs,
189 int(self.config.minFracMatchedPairs * min([len(refCat), len(usableSourceCat)])))
190
191 # match usable (possibly saturated) sources and then purge saturated sources from the match list
192 usableMatches = self._doMatch(
193 refCat=refCat,
194 sourceCat=usableSourceCat,
195 wcs=wcs,
196 refFluxField=refFluxField,
197 numUsableSources=numUsableSources,
198 minMatchedPairs=minMatchedPairs,
199 maxMatchDist=match_tolerance.maxMatchDist,
200 sourceFluxField=sourceFluxField,
201 verbose=debug.verbose,
202 )
203
204 # cull non-good sources
205 matches = []
206 self._getIsGoodKeys(usableSourceCat.schema)
207 for match in usableMatches:
208 if self._isGoodTest(match.second):
209 # Append the isGood match.
210 matches.append(match)
211
212 self.log.debug("Found %d usable matches, of which %d had good sources",
213 len(usableMatches), len(matches))
214
215 if len(matches) == 0:
216 raise RuntimeError("Unable to match sources")
217
218 self.log.info("Matched %d sources", len(matches))
219 if len(matches) < minMatchedPairs:
220 self.log.warning("Number of matches is smaller than request")
221
222 return pipeBase.Struct(
223 matches=matches,
224 usableSourceCat=usableSourceCat,
225 match_tolerance=match_tolerance,
226 )
227
228 def _getIsGoodKeys(self, schema):
229 """Retrieve the keys needed for the isGoodTest from the source catalog
230 schema.
231
232 Parameters
233 ----------
234 schema : `lsst.afw.table.Schema`
235 Source schema to retrieve `lsst.afw.table.Key` s from.
236 """
237 self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey()
238 self.interpolatedCenterKey = schema["base_PixelFlags_flag_interpolatedCenter"].asKey()
239 self.saturatedKey = schema["base_PixelFlags_flag_saturated"].asKey()
240
241 def _isGoodTest(self, source):
242 """Test that an object is good for use in the WCS fitter.
243
244 This is a hard coded version of the isGood flag from the old SourceInfo
245 class that used to be part of this class.
246
247 Parameters
248 ----------
250 Source to test.
251
252 Returns
253 -------
254 isGood : `bool`
255 Source passes CCD edge and saturated tests.
256 """
257 return (not source.get(self.edgeKey)
258 and not source.get(self.interpolatedCenterKey)
259 and not source.get(self.saturatedKey))
260
261 @timeMethod
262 def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs,
263 maxMatchDist, sourceFluxField, verbose):
264 """Implementation of matching sources to position reference stars.
265
266 Unlike matchObjectsToSources, this method does not check if the sources
267 are suitable.
268
269 Parameters
270 ----------
272 Catalog of reference objects.
273 sourceCat : `lsst.afw.table.SourceCatalog`
274 Catalog of detected sources.
276 Current best WCS of the image.
277 refFluxFioeld : `str`
278 Name of flux field in refCat to use.
279 numUsableSources : `int`
280 Total number of source usable for matching.
281 mintMatchPairs : `int`
282 Minimum number of objects to match between the refCat and sourceCat
283 to consider a valid match.
284 maxMatchDist : `lsst.geom.Angle`
285 Maximum separation to considering a reference and a source a match.
286 sourceFluxField : `str`
287 Name of source catalog flux field.
288 verbose : `bool`
289 Print diagnostic information std::cout
290
291 Returns
292 -------
293 matches : `list` of `lsst.afw.table.ReferenceMatch`
294 """
295 numSources = len(sourceCat)
296 posRefBegInd = numUsableSources - numSources
297 if maxMatchDist is None:
298 maxMatchDistArcSec = self.config.maxMatchDistArcSec
299 else:
300 maxMatchDistArcSec = min(maxMatchDist.asArcseconds(), self.config.maxMatchDistArcSec)
301 configMatchDistPix = maxMatchDistArcSec/wcs.getPixelScale().asArcseconds()
302
303 matchControl = MatchOptimisticBControl()
304 matchControl.refFluxField = refFluxField
305 matchControl.sourceFluxField = sourceFluxField
306 matchControl.numBrightStars = self.config.numBrightStars
307 matchControl.minMatchedPairs = self.config.minMatchedPairs
308 matchControl.maxOffsetPix = self.config.maxOffsetPix
309 matchControl.numPointsForShape = self.config.numPointsForShape
310 matchControl.maxDeterminant = self.config.maxDeterminant
311
312 for maxRotInd in range(4):
313 matchControl.maxRotationDeg = self.config.maxRotationDeg * math.pow(2.0, 0.5*maxRotInd)
314 for matchRadInd in range(3):
315 matchControl.matchingAllowancePix = configMatchDistPix * math.pow(1.25, matchRadInd)
316
317 for angleDiffInd in range(3):
318 matchControl.allowedNonperpDeg = self.config.allowedNonperpDeg*(angleDiffInd+1)
319 matches = matchOptimisticB(
320 refCat,
321 sourceCat,
322 matchControl,
323 wcs,
324 posRefBegInd,
325 verbose,
326 )
327 if matches is not None and len(matches) > 0:
328 setMatchDistance(matches)
329 return matches
330 return matches
int min
table::Key< int > to
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
Definition SkyWcs.h:117
A class used as a handle to a particular field in a table.
Definition Key.h:53
Defines the fields and offsets for a table.
Definition Schema.h:51
Custom catalog class for record/table subclasses that are guaranteed to have an ID,...
Record class that contains measurements made on a single exposure.
Definition Source.h:78
A class representing an angle.
Definition Angle.h:128
matchObjectsToSources(self, refCat, sourceCat, wcs, sourceFluxField, refFluxField, match_tolerance=None)
_doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs, maxMatchDist, sourceFluxField, verbose)
Lightweight representation of a geometric match between two records.
Definition Match.h:67