LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
matchOptimisticBContinued.py
Go to the documentation of this file.
1 
2 __all__ = ["matchOptimisticB", "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.meas.algorithms.sourceSelector import sourceSelectorRegistry
10 
11 from ..setMatchDistance import setMatchDistance
12 from . 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 
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=300,
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  sourceSelector = sourceSelectorRegistry.makeField(
94  doc="How to select sources for cross-matching",
95  default="matcher"
96  )
97 
98  def setDefaults(self):
99  sourceSelector = self.sourceSelector["matcher"]
100  sourceSelector.setDefaults()
101 
102 
103 # The following block adds links to this task from the Task Documentation page.
104 # \addtogroup LSST_task_documentation
105 # \{
106 # \page measAstrom_matchOptimisticBTask
107 # \ref MatchOptimisticBTask "MatchOptimisticBTask"
108 # Match sources to reference objects
109 # \}
110 
111 
112 class MatchOptimisticBTask(pipeBase.Task):
113  """Match sources to reference objects using the Optimistic Pattern Matcher
114  B algorithm of Tabur 2007.
115  """
116  ConfigClass = MatchOptimisticBConfig
117  _DefaultName = "matchObjectsToSources"
118 
119  def __init__(self, **kwargs):
120  pipeBase.Task.__init__(self, **kwargs)
121  self.makeSubtask("sourceSelector")
122 
123  def filterStars(self, refCat):
124  """Extra filtering pass; subclass if desired.
125 
126  Parameters
127  ----------
128  refCat : `lsst.afw.table.SimpleCatalog`
129  Catalog of reference objects.
130 
131  Returns
132  -------
133  trimmedRefCat : `lsst.afw.table.SimpleCatalog`
134  Reference catalog with some filtering applied. Currently no
135  filtering is applied.
136  """
137  return refCat
138 
139  @pipeBase.timeMethod
140  def matchObjectsToSources(self, refCat, sourceCat, wcs, refFluxField,
141  match_tolerance=None):
142  """Match sources to position reference stars.
143 
144  Parameters
145  ----------
146  refCat : `lsst.afw.table.SimpleCatalog`
147  Reference catalog to match.
148  sourceCat : `lsst.afw.table.SourceCatalog`
149  Source catalog to match.
150  wcs : `lsst.afw.geom.SkyWcs`
151  Current WCS of the exposure containing the sources.
152  refFluxField : `str`
153  Name of the reference catalog filter to use.
154  match_tolerance : `lsst.meas.astrom.MatchTolerance`
155  Object containing information from previous
156  `lsst.meas.astrom.AstrometryTask` match/fit cycles for use in
157  matching. If `None` is config defaults.
158 
159  Returns
160  -------
161  matchResult : `lsst.pipe.base.Struct`
162  Result struct with components
163 
164  - ``matches`` : List of matches with distance below the maximum match
165  distance (`list` of `lsst.afw.table.ReferenceMatch`).
166  - ``useableSourceCat`` : Catalog of sources matched and suited for
167  WCS fitting (`lsst.afw.table.SourceCatalog`).
168  - ``match_tolerance`` : MatchTolerance object updated from this
169  match iteration (`lsst.meas.astrom.MatchTolerance`).
170  """
171  import lsstDebug
172  debug = lsstDebug.Info(__name__)
173 
174  preNumObj = len(refCat)
175  refCat = self.filterStars(refCat)
176  numRefObj = len(refCat)
177 
178  if self.log:
179  self.log.info("filterStars purged %d reference stars, leaving %d stars" %
180  (preNumObj - numRefObj, numRefObj))
181 
182  if match_tolerance is None:
183  match_tolerance = MatchTolerance()
184 
185  # usableSourceCat: sources that are good but may be saturated
186  numSources = len(sourceCat)
187  selectedSources = self.sourceSelector.run(sourceCat)
188  usableSourceCat = selectedSources.sourceCat
189  numUsableSources = len(usableSourceCat)
190  self.log.info("Purged %d unusable sources, leaving %d usable sources" %
191  (numSources - numUsableSources, numUsableSources))
192 
193  if len(usableSourceCat) == 0:
194  raise pipeBase.TaskError("No sources are usable")
195 
196  del sourceCat # avoid accidentally using sourceCat; use usableSourceCat or goodSourceCat from now on
197 
198  minMatchedPairs = min(self.config.minMatchedPairs,
199  int(self.config.minFracMatchedPairs * min([len(refCat), len(usableSourceCat)])))
200 
201  # match usable (possibly saturated) sources and then purge saturated sources from the match list
202  usableMatches = self._doMatch(
203  refCat=refCat,
204  sourceCat=usableSourceCat,
205  wcs=wcs,
206  refFluxField=refFluxField,
207  numUsableSources=numUsableSources,
208  minMatchedPairs=minMatchedPairs,
209  maxMatchDist=match_tolerance.maxMatchDist,
210  sourceFluxField=self.sourceSelector.fluxField,
211  verbose=debug.verbose,
212  )
213 
214  # cull non-good sources
215  matches = []
216  self._getIsGoodKeys(usableSourceCat.schema)
217  for match in usableMatches:
218  if self._isGoodTest(match.second):
219  # Append the isGood match.
220  matches.append(match)
221 
222  self.log.debug("Found %d usable matches, of which %d had good sources",
223  len(usableMatches), len(matches))
224 
225  if len(matches) == 0:
226  raise RuntimeError("Unable to match sources")
227 
228  self.log.info("Matched %d sources" % len(matches))
229  if len(matches) < minMatchedPairs:
230  self.log.warn("Number of matches is smaller than request")
231 
232  return pipeBase.Struct(
233  matches=matches,
234  usableSourceCat=usableSourceCat,
235  match_tolerance=match_tolerance,
236  )
237 
238  def _getIsGoodKeys(self, schema):
239  """Retrieve the keys needed for the isGoodTest from the source catalog
240  schema.
241 
242  Parameters
243  ----------
244  schema : `lsst.afw.table.Schema`
245  Source schema to retrieve `lsst.afw.table.Key` s from.
246  """
247  self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey()
248  self.interpolatedCenterKey = schema["base_PixelFlags_flag_interpolatedCenter"].asKey()
249  self.saturatedKey = schema["base_PixelFlags_flag_saturated"].asKey()
250 
251  def _isGoodTest(self, source):
252  """Test that an object is good for use in the WCS fitter.
253 
254  This is a hard coded version of the isGood flag from the old SourceInfo
255  class that used to be part of this class.
256 
257  Parameters
258  ----------
259  source : `lsst.afw.table.SourceRecord`
260  Source to test.
261 
262  Returns
263  -------
264  isGood : `bool`
265  Source passes CCD edge and saturated tests.
266  """
267  return (not source.get(self.edgeKey) and
268  not source.get(self.interpolatedCenterKey) and
269  not source.get(self.saturatedKey))
270 
271  @pipeBase.timeMethod
272  def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs,
273  maxMatchDist, sourceFluxField, verbose):
274  """Implementation of matching sources to position reference stars.
275 
276  Unlike matchObjectsToSources, this method does not check if the sources
277  are suitable.
278 
279  Parameters
280  ----------
281  refCat : `lsst.afw.table.SimpleCatalog`
282  Catalog of reference objects.
283  sourceCat : `lsst.afw.table.SourceCatalog`
284  Catalog of detected sources.
285  wcs : `lsst.afw.geom.SkyWcs`
286  Current best WCS of the image.
287  refFluxFioeld : `str`
288  Name of flux field in refCat to use.
289  numUsableSources : `int`
290  Total number of source usable for matching.
291  mintMatchPairs : `int`
292  Minimum number of objects to match between the refCat and sourceCat
293  to consider a valid match.
294  maxMatchDist : `lsst.geom.Angle`
295  Maximum separation to considering a reference and a source a match.
296  sourceFluxField : `str`
297  Name of source catalog flux field.
298  verbose : `bool`
299  Print diagnostic information std::cout
300 
301  Returns
302  -------
303  matches : `list` of `lsst.afw.table.ReferenceMatch`
304  """
305  numSources = len(sourceCat)
306  posRefBegInd = numUsableSources - numSources
307  if maxMatchDist is None:
308  maxMatchDistArcSec = self.config.maxMatchDistArcSec
309  else:
310  maxMatchDistArcSec = min(maxMatchDist.asArcseconds(), self.config.maxMatchDistArcSec)
311  configMatchDistPix = maxMatchDistArcSec/wcs.getPixelScale().asArcseconds()
312 
313  matchControl = MatchOptimisticBControl()
314  matchControl.refFluxField = refFluxField
315  matchControl.sourceFluxField = sourceFluxField
316  matchControl.numBrightStars = self.config.numBrightStars
317  matchControl.minMatchedPairs = self.config.minMatchedPairs
318  matchControl.maxOffsetPix = self.config.maxOffsetPix
319  matchControl.numPointsForShape = self.config.numPointsForShape
320  matchControl.maxDeterminant = self.config.maxDeterminant
321 
322  for maxRotInd in range(4):
323  matchControl.maxRotationDeg = self.config.maxRotationDeg * math.pow(2.0, 0.5*maxRotInd)
324  for matchRadInd in range(3):
325  matchControl.matchingAllowancePix = configMatchDistPix * math.pow(1.25, matchRadInd)
326 
327  for angleDiffInd in range(3):
328  matchControl.allowedNonperpDeg = self.config.allowedNonperpDeg*(angleDiffInd+1)
329  matches = matchOptimisticB(
330  refCat,
331  sourceCat,
332  matchControl,
333  wcs,
334  posRefBegInd,
335  verbose,
336  )
337  if matches is not None and len(matches) > 0:
338  setMatchDistance(matches)
339  return matches
340  return matches
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...
int min
def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs, maxMatchDist, sourceFluxField, verbose)
def matchObjectsToSources(self, refCat, sourceCat, wcs, refFluxField, match_tolerance=None)