lsst.meas.algorithms  14.0-15-gaf93ae82
matcherSourceSelector.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 #
4 # Copyright 2008-2017 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <https://www.lsstcorp.org/LegalNotices/>.
22 #
23 from __future__ import absolute_import, division, print_function
24 
25 import numpy as np
26 
27 from lsst.afw import table
28 import lsst.pex.config as pexConfig
29 from .sourceSelector import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry
30 from lsst.pipe.base import Struct
31 
32 
34  sourceFluxType = pexConfig.Field(
35  doc="Type of source flux; typically one of Ap or Psf",
36  dtype=str,
37  default="Ap",
38  )
39  minSnr = pexConfig.Field(
40  dtype=float,
41  doc="Minimum allowed signal-to-noise ratio for sources used for matching "
42  "(in the flux specified by sourceFluxType); <= 0 for no limit",
43  default=40,
44  )
45 
46 
48  """
49  !Select sources that are useful for matching.
50 
51  Good matching sources have high signal/noise, are non-blended. They need not
52  be PSF sources, just have reliable centroids.
53  """
54  ConfigClass = MatcherSourceSelectorConfig
55 
56  def __init__(self, *args, **kwargs):
57  BaseSourceSelectorTask.__init__(self, *args, **kwargs)
58 
59  def selectSources(self, sourceCat, matches=None):
60  """
61  !Return a catalog of sources: a subset of sourceCat.
62 
63  If sourceCat is cotiguous in memory, will use vectorized tests for ~100x
64  execution speed advantage over non-contiguous catalogs. This would be
65  even faster if we didn't have to check footprints for multiple peaks.
66 
67  @param[in] sourceCat catalog of sources that may be sources
68  (an lsst.afw.table.SourceCatalog)
69 
70  @return a pipeBase.Struct containing:
71  - sourceCat a catalog of sources
72  """
73  self._getSchemaKeys(sourceCat.schema)
74 
75  if sourceCat.isContiguous():
76  good = self._isUsable_vector(sourceCat)
77  result = sourceCat[good]
78  else:
79  result = table.SourceCatalog(sourceCat.table)
80  for i, source in enumerate(sourceCat):
81  if self._isUsable(source):
82  result.append(source)
83  return Struct(sourceCat=result)
84 
85  def _getSchemaKeys(self, schema):
86  """Extract and save the necessary keys from schema with asKey."""
87  self.parentKey = schema["parent"].asKey()
88  self.centroidXKey = schema["slot_Centroid_x"].asKey()
89  self.centroidYKey = schema["slot_Centroid_y"].asKey()
90  self.centroidFlagKey = schema["slot_Centroid_flag"].asKey()
91 
92  fluxPrefix = "slot_%sFlux_" % (self.config.sourceFluxType,)
93  self.fluxField = fluxPrefix + "flux"
94  self.fluxKey = schema[fluxPrefix + "flux"].asKey()
95  self.fluxFlagKey = schema[fluxPrefix + "flag"].asKey()
96  self.fluxSigmaKey = schema[fluxPrefix + "fluxSigma"].asKey()
97 
98  def _isParent_vector(self, sourceCat):
99  """Return True for each source that is the parent source."""
100  test = (sourceCat.get(self.parentKey) == 0)
101  return test
102 
103  def _isParent(self, source):
104  """Return True if source is the parent source."""
105  if (source.get(self.parentKey) == 0):
106  return True
107  return False
108 
109  def _hasCentroid_vector(self, sourceCat):
110  """Return True for each source that has a valid centroid"""
111  return np.isfinite(sourceCat.get(self.centroidXKey)) \
112  & np.isfinite(sourceCat.get(self.centroidYKey)) \
113  & ~sourceCat.get(self.centroidFlagKey)
114 
115  def _hasCentroid(self, source):
116  """Return True if the source has a valid centroid"""
117  centroid = source.getCentroid()
118  return np.all(np.isfinite(centroid)) and not source.getCentroidFlag()
119 
120  def _goodSN_vector(self, sourceCat):
121  """Return True for each source that has Signal/Noise > config.minSnr."""
122  if self.config.minSnr <= 0:
123  return True
124  else:
125  with np.errstate(invalid="ignore"): # suppress NAN warnings
126  return sourceCat.get(self.fluxKey)/sourceCat.get(self.fluxSigmaKey) > self.config.minSnr
127 
128  def _goodSN(self, source):
129  """Return True if source has Signal/Noise > config.minSnr."""
130  return (self.config.minSnr <= 0 or
131  (source.get(self.fluxKey)/source.get(self.fluxSigmaKey) > self.config.minSnr))
132 
133  def _isUsable_vector(self, sourceCat):
134  """
135  Return True for each source that is usable for matching, even if it may
136  have a poor centroid.
137 
138  For a source to be usable it must:
139  - have a valid centroid
140  - not be deblended
141  - have a valid flux (of the type specified in this object's constructor)
142  - have adequate signal-to-noise
143  """
144  return self._hasCentroid_vector(sourceCat) \
145  & self._isParent_vector(sourceCat) \
146  & self._goodSN_vector(sourceCat) \
147  & ~sourceCat.get(self.fluxFlagKey)
148 
149  def _isUsable(self, source):
150  """
151  Return True if the source is usable for matching, even if it may have a
152  poor centroid.
153 
154  For a source to be usable it must:
155  - have a valid centroid
156  - not be deblended
157  - have a valid flux (of the type specified in this object's constructor)
158  - have adequate signal-to-noise
159  """
160  return self._hasCentroid(source) \
161  and self._isParent(source) \
162  and not source.get(self.fluxFlagKey) \
163  and self._goodSN(source)
164 
165 
167  """
168  !Select sources that are useful for matching.
169 
170  Good matching sources have high signal/noise, are non-blended. They need not
171  be PSF sources, just have reliable centroids. This inherited class adds
172  the removal of saturated, interpolated, and edge_key objects to the set of
173  bad flags. It is a temporary addition designed preserve the source selction
174  used in matchOptimisticB. Once matchPessimisticB is adopted as the default
175  source selector the class will be removed and the saturated, interpoalted, and
176  edge_key flags will be added to the matcherSourceSelector class.
177 
178  TODO: Once DM-10399 is complete an RFC will be filed to make matchPessimisticB
179  the default matcher this class will replace matcherSourceSelector with this source
180  selector resulting in only one matcherSourceSeletor. The ticket describing
181  this work is DM-10800.
182  """
183  def _getSchemaKeys(self, schema):
184  """Extract and save the necessary keys from schema with asKey."""
185  MatcherSourceSelectorTask._getSchemaKeys(self, schema)
186 
187  self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey()
188  self.interpolatedCenterKey = schema["base_PixelFlags_flag_interpolatedCenter"].asKey()
189  self.saturatedKey = schema["base_PixelFlags_flag_saturated"].asKey()
190 
191  def _isUsable_vector(self, sourceCat):
192  """
193  Return True for each source that is usable for matching, even if it may
194  have a poor centroid.
195 
196  For a source to be usable it must:
197  - have a valid centroid
198  - not be deblended
199  - have a valid flux (of the type specified in this object's constructor)
200  - have adequate signal-to-noise
201  """
202  result = MatcherSourceSelectorTask._isUsable_vector(self, sourceCat)
203 
204  return result \
205  & ~sourceCat.get(self.edgeKey) \
206  & ~sourceCat.get(self.interpolatedCenterKey) \
207  & ~sourceCat.get(self.saturatedKey)
208 
209  def _isUsable(self, source):
210  """
211  Return True if the source is usable for matching, even if it may have a
212  poor centroid.
213 
214  For a source to be usable it must:
215  - have a valid centroid
216  - not be deblended
217  - have a valid flux (of the type specified in this object's constructor)
218  - have adequate signal-to-noise
219  """
220  result = MatcherSourceSelectorTask._isUsable(self, source)
221 
222  return result \
223  and not source.get(self.edgeKey) \
224  and not source.get(self.interpolatedCenterKey) \
225  and not source.get(self.saturatedKey)
226 
227 
228 sourceSelectorRegistry.register("matcher", MatcherSourceSelectorTask)
229 sourceSelectorRegistry.register("matcherPessimistic",
230  MatcherPessimisticSourceSelectorTask)