LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
matchOptimisticBTask.py
Go to the documentation of this file.
1 
2 __all__ = ["MatchOptimisticBTask", "MatchOptimisticBConfig",
3  "MatchTolerance"]
4 
5 import math
6 
7 import lsst.pex.config as pexConfig
8 import lsst.pipe.base as pipeBase
9 from lsst.utils.timer import timeMethod
10 
11 from .setMatchDistance import setMatchDistance
12 from .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.maxMatchDistmaxMatchDist = maxMatchDist
31 
32 
33 class 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 
104 class 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  ----------
119  refCat : `lsst.afw.table.SimpleCatalog`
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  ----------
137  refCat : `lsst.afw.table.SimpleCatalog`
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.
142  wcs : `lsst.afw.geom.SkyWcs`
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.filterStarsfilterStars(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_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_getIsGoodKeys(usableSourceCat.schema)
207  for match in usableMatches:
208  if self._isGoodTest_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.edgeKeyedgeKey = schema["base_PixelFlags_flag_edge"].asKey()
238  self.interpolatedCenterKeyinterpolatedCenterKey = schema["base_PixelFlags_flag_interpolatedCenter"].asKey()
239  self.saturatedKeysaturatedKey = 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  ----------
249  source : `lsst.afw.table.SourceRecord`
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.edgeKeyedgeKey)
258  and not source.get(self.interpolatedCenterKeyinterpolatedCenterKey)
259  and not source.get(self.saturatedKeysaturatedKey))
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  ----------
271  refCat : `lsst.afw.table.SimpleCatalog`
272  Catalog of reference objects.
273  sourceCat : `lsst.afw.table.SourceCatalog`
274  Catalog of detected sources.
275  wcs : `lsst.afw.geom.SkyWcs`
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
def matchObjectsToSources(self, refCat, sourceCat, wcs, sourceFluxField, refFluxField, match_tolerance=None)
def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs, maxMatchDist, sourceFluxField, verbose)
afw::table::ReferenceMatchVector matchOptimisticB(afw::table::SimpleCatalog const &posRefCat, afw::table::SourceCatalog const &sourceCat, MatchOptimisticBControl const &control, afw::geom::SkyWcs const &wcs, int posRefBegInd=0, bool verbose=false)
Match sources to stars in a position reference catalog using optimistic pattern matching B.