LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
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 from lsst.afw.image.utils import getDistortedWcs
25 import lsst.afw.geom as afwGeom
26 import lsst.afw.math as afwMath
27 import lsst.pex.config as pexConfig
28 import lsst.pipe.base as pipeBase
29 from .matchOptimisticB import MatchOptimisticBTask
30 from .display import displayAstrometry
31 from .astromLib import makeMatchStatistics
32 from .createMatchMetadata import createMatchMetadata
33 
34 __all__ = ['RefMatchConfig', 'RefMatchTask']
35 
36 
37 class RefMatchConfig(pexConfig.Config):
38  matcher = pexConfig.ConfigurableField(
39  target=MatchOptimisticBTask,
40  doc="reference object/source matcher",
41  )
42  matchDistanceSigma = pexConfig.RangeField(
43  doc="the maximum match distance is set to "
44  " mean_match_distance + matchDistanceSigma*std_dev_match_distance; " +
45  "ignored if not fitting a WCS",
46  dtype=float,
47  default=2,
48  min=0,
49  )
50 
51 # The following block adds links to this task from the Task Documentation page.
52 ## \addtogroup LSST_task_documentation
53 ## \{
54 ## \page measAlgorithms_RefMatchTask
55 ## \ref RefMatchTask_ "RefMatchTask"
56 ## Basic functionality for all calibration tasks: i.e. a matcher
57 ## \}
58 
59 
60 class RefMatchTask(pipeBase.Task):
61  """!Match an input source catalog with objects from a reference catalog
62  """
63  ConfigClass = RefMatchConfig
64  _DefaultName = "calibrationBaseClass"
65 
66  def __init__(self, refObjLoader, schema=None, **kwargs):
67  """!Construct an AstrometryTask
68 
69  @param[in] refObjLoader A reference object loader object
70  @param[in] schema ignored; available for compatibility with an older astrometry task
71  @param[in] kwargs additional keyword arguments for pipe_base Task.\_\_init\_\_
72  """
73  pipeBase.Task.__init__(self, **kwargs)
74  self.refObjLoader = refObjLoader
75  self.makeSubtask("matcher")
76 
77  @pipeBase.timeMethod
78  def loadAndMatch(self, exposure, sourceCat):
79  """!Load reference objects overlapping an exposure and match to sources detected on that exposure
80 
81  @param[in] exposure exposure that the sources overlap
82  @param[in] sourceCat catalog of sources detected on the exposure (an lsst.afw.table.SourceCatalog)
83 
84  @return an lsst.pipe.base.Struct with these fields:
85  - refCat reference object catalog of objects that overlap the exposure (with some margin)
86  (an lsst::afw::table::SimpleCatalog)
87  - matches list of reference object/source matches (an lsst.afw.table.ReferenceMatchVector)
88  - matchMeta metadata needed to unpersist matches (an lsst.daf.base.PropertyList)
89 
90  @note ignores config.matchDistanceSigma
91  """
92  import lsstDebug
93  debug = lsstDebug.Info(__name__)
94 
95  matchMeta = createMatchMetadata(exposure, border=self.refObjLoader.config.pixelMargin)
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 
105  matchRes = self.matcher.matchObjectsToSources(
106  refCat=loadRes.refCat,
107  sourceCat=sourceCat,
108  wcs=expMd.wcs,
109  refFluxField=loadRes.fluxField,
110  maxMatchDist=None,
111  )
112 
113  distStats = self._computeMatchStatsOnSky(matchRes.matches)
114  self.log.info(
115  "Found %d matches with scatter = %0.3f +- %0.3f arcsec; " %
116  (len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds())
117  )
118 
119  if debug.display:
120  frame = int(debug.frame)
122  refCat=loadRes.refCat,
123  sourceCat=sourceCat,
124  matches=matchRes.matches,
125  exposure=exposure,
126  bbox=expMd.bbox,
127  frame=frame,
128  title="Matches",
129  )
130 
131  return pipeBase.Struct(
132  refCat=loadRes.refCat,
133  matches=matchRes.matches,
134  matchMeta=matchMeta,
135  )
136 
137  def _computeMatchStatsOnSky(self, matchList):
138  """Compute on-sky radial distance statistics for a match list
139 
140  @param[in] matchList list of matches between reference object and sources;
141  the distance field is the only field read and it must be set to distance in radians
142 
143  @return a pipe_base Struct containing these fields:
144  - distMean clipped mean of on-sky radial separation
145  - distStdDev clipped standard deviation of on-sky radial separation
146  - maxMatchDist distMean + self.config.matchDistanceSigma*distStdDev
147  """
148  distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
149  distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*afwGeom.radians
150  distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*afwGeom.radians
151  return pipeBase.Struct(
152  distMean=distMean,
153  distStdDev=distStdDev,
154  maxMatchDist=distMean + self.config.matchDistanceSigma*distStdDev,
155  )
156 
157  def _getExposureMetadata(self, exposure):
158  """!Extract metadata from an exposure
159 
160  @return an lsst.pipe.base.Struct containing the following exposure metadata:
161  - bbox: parent bounding box
162  - wcs: WCS (an lsst.afw.image.Wcs)
163  - calib calibration (an lsst.afw.image.Calib), or None if unknown
164  - filterName: name of filter, or None if unknown
165  """
166  exposureInfo = exposure.getInfo()
167  filterName = exposureInfo.getFilter().getName() or None
168  if filterName == "_unknown_":
169  filterName = None
170  return pipeBase.Struct(
171  bbox=exposure.getBBox(),
172  wcs=getDistortedWcs(exposureInfo, log=self.log),
173  calib=exposureInfo.getCalib() if exposureInfo.hasCalib() else None,
174  filterName=filterName,
175  )
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 an AstrometryTask.
Definition: ref_match.py:66
def loadAndMatch
Load reference objects overlapping an exposure and match to sources detected on that exposure...
Definition: ref_match.py:78
Match an input source catalog with objects from a reference catalog.
Definition: ref_match.py:60
def getDistortedWcs
Get a WCS from an exposureInfo, with distortion terms if possible.
Definition: utils.py:48
def _getExposureMetadata
Extract metadata from an exposure.
Definition: ref_match.py:157