lsst.meas.astrom  13.0-16-g30f6da5+8
 All Classes Namespaces Files Functions Variables Typedefs Friends Macros Groups Pages
ref_match.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2016 AURA/LSST.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 from __future__ import absolute_import, division, print_function
23 
24 __all__ = ['RefMatchConfig', 'RefMatchTask']
25 
26 from lsst.afw.image.utils import getDistortedWcs
27 import lsst.afw.geom as afwGeom
28 import lsst.afw.math as afwMath
29 import lsst.pex.config as pexConfig
30 import lsst.pipe.base as pipeBase
31 from .matchOptimisticB import MatchOptimisticBTask
32 from .display import displayAstrometry
33 from . import makeMatchStatistics
34 
35 
36 class RefMatchConfig(pexConfig.Config):
37  matcher = pexConfig.ConfigurableField(
38  target=MatchOptimisticBTask,
39  doc="reference object/source matcher",
40  )
41  matchDistanceSigma = pexConfig.RangeField(
42  doc="the maximum match distance is set to "
43  " mean_match_distance + matchDistanceSigma*std_dev_match_distance; " +
44  "ignored if not fitting a WCS",
45  dtype=float,
46  default=2,
47  min=0,
48  )
49 
50 # The following block adds links to this task from the Task Documentation page.
51 ## \addtogroup LSST_task_documentation
52 ## \{
53 ## \page measAlgorithms_RefMatchTask
54 ## \ref RefMatchTask_ "RefMatchTask"
55 ## Basic functionality for all calibration tasks: i.e. a matcher
56 ## \}
57 
58 
59 class RefMatchTask(pipeBase.Task):
60  """!Match an input source catalog with objects from a reference catalog
61 
62  @anchor RefMatchTask_
63  """
64  ConfigClass = RefMatchConfig
65  _DefaultName = "calibrationBaseClass"
66 
67  def __init__(self, refObjLoader, schema=None, **kwargs):
68  """!Construct a RefMatchTask
69 
70  @param[in] refObjLoader A reference object loader object
71  @param[in] schema ignored; available for compatibility with an older astrometry task
72  @param[in] kwargs additional keyword arguments for pipe_base Task.\_\_init\_\_
73  """
74  pipeBase.Task.__init__(self, **kwargs)
75  self.refObjLoader = refObjLoader
76  self.makeSubtask("matcher")
77 
78  @pipeBase.timeMethod
79  def loadAndMatch(self, exposure, sourceCat):
80  """!Load reference objects overlapping an exposure and match to sources detected on that exposure
81 
82  @param[in] exposure exposure that the sources overlap
83  @param[in] sourceCat catalog of sources detected on the exposure (an lsst.afw.table.SourceCatalog)
84 
85  @return an lsst.pipe.base.Struct with these fields:
86  - refCat reference object catalog of objects that overlap the exposure (with some margin)
87  (an lsst::afw::table::SimpleCatalog)
88  - matches a list of lsst.afw.table.ReferenceMatch
89  - matchMeta metadata needed to unpersist matches (an lsst.daf.base.PropertyList)
90 
91  @note ignores config.matchDistanceSigma
92  """
93  import lsstDebug
94  debug = lsstDebug.Info(__name__)
95 
96  expMd = self._getExposureMetadata(exposure)
97 
98  loadRes = self.refObjLoader.loadPixelBox(
99  bbox=expMd.bbox,
100  wcs=expMd.wcs,
101  filterName=expMd.filterName,
102  calib=expMd.calib,
103  )
104  matchMeta = self.refObjLoader.getMetadataBox(
105  bbox=expMd.bbox,
106  wcs=expMd.wcs,
107  filterName=expMd.filterName,
108  calib=expMd.calib,
109  )
110 
111  matchRes = self.matcher.matchObjectsToSources(
112  refCat=loadRes.refCat,
113  sourceCat=sourceCat,
114  wcs=expMd.wcs,
115  refFluxField=loadRes.fluxField,
116  match_tolerance=None,
117  )
118 
119  distStats = self._computeMatchStatsOnSky(matchRes.matches)
120  self.log.info(
121  "Found %d matches with scatter = %0.3f +- %0.3f arcsec; " %
122  (len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds())
123  )
124 
125  if debug.display:
126  frame = int(debug.frame)
128  refCat=loadRes.refCat,
129  sourceCat=sourceCat,
130  matches=matchRes.matches,
131  exposure=exposure,
132  bbox=expMd.bbox,
133  frame=frame,
134  title="Matches",
135  )
136 
137  return pipeBase.Struct(
138  refCat=loadRes.refCat,
139  matches=matchRes.matches,
140  matchMeta=matchMeta,
141  )
142 
143  def _computeMatchStatsOnSky(self, matchList):
144  """Compute on-sky radial distance statistics for a match list
145 
146  @param[in] matchList list of matches between reference object and sources;
147  the distance field is the only field read and it must be set to distance in radians
148 
149  @return a pipe_base Struct containing these fields:
150  - distMean clipped mean of on-sky radial separation
151  - distStdDev clipped standard deviation of on-sky radial separation
152  - maxMatchDist distMean + self.config.matchDistanceSigma*distStdDev
153  """
154  distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
155  distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*afwGeom.radians
156  distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*afwGeom.radians
157  return pipeBase.Struct(
158  distMean=distMean,
159  distStdDev=distStdDev,
160  maxMatchDist=distMean + self.config.matchDistanceSigma*distStdDev,
161  )
162 
163  def _getExposureMetadata(self, exposure):
164  """!Extract metadata from an exposure
165 
166  @return an lsst.pipe.base.Struct containing the following exposure metadata:
167  - bbox: parent bounding box
168  - wcs: WCS (an lsst.afw.image.Wcs)
169  - calib calibration (an lsst.afw.image.Calib), or None if unknown
170  - filterName: name of filter, or None if unknown
171  """
172  exposureInfo = exposure.getInfo()
173  filterName = exposureInfo.getFilter().getName() or None
174  if filterName == "_unknown_":
175  filterName = None
176  return pipeBase.Struct(
177  bbox=exposure.getBBox(),
178  wcs=getDistortedWcs(exposureInfo, log=self.log),
179  calib=exposureInfo.getCalib() if exposureInfo.hasCalib() else None,
180  filterName=filterName,
181  )
afw::math::Statistics makeMatchStatistics(std::vector< MatchT > const &matchList, int const flags, afw::math::StatisticsControl const &sctrl=afw::math::StatisticsControl())
Compute statistics of the distance field of a match list.
def __init__
Construct a RefMatchTask.
Definition: ref_match.py:67
def loadAndMatch
Load reference objects overlapping an exposure and match to sources detected on that exposure...
Definition: ref_match.py:79
Match an input source catalog with objects from a reference catalog.
Definition: ref_match.py:59
def _getExposureMetadata
Extract metadata from an exposure.
Definition: ref_match.py:163