LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
catalogStarSelector.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
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 <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 import numpy
23 
24 import lsst.pex.config as pexConfig
25 import lsst.afw.display.ds9 as ds9
26 import lsst.afw.math as afwMath
27 import lsst.meas.algorithms as measAlg
28 
29 class CatalogStarSelectorConfig(pexConfig.Config):
30  fluxLim = pexConfig.Field(
31  doc = "specify the minimum psfFlux for good Psf Candidates",
32  dtype = float,
33  default = 0.0,
34  check = lambda x: x >= 0.0,
35  )
36  fluxMax = pexConfig.Field(
37  doc = "specify the maximum psfFlux for good Psf Candidates (ignored if == 0)",
38  dtype = float,
39  default = 0.0,
40 # minValue = 0.0,
41  check = lambda x: x >= 0.0,
42  )
43  badStarPixelFlags = pexConfig.ListField(
44  doc = "PSF candidate objects may not have any of these bits set",
45  dtype = str,
46  default = ["base_PixelFlags_flag_edge", "base_PixelFlags_flag_interpolatedCenter", "base_PixelFlags_flag_saturatedCenter"],
47  )
48  kernelSize = pexConfig.Field(
49  doc = "size of the kernel to create",
50  dtype = int,
51  default = 21,
52  )
53  borderWidth = pexConfig.Field(
54  doc = "number of pixels to ignore around the edge of PSF candidate postage stamps",
55  dtype = int,
56  default = 0,
57  )
58 
59 class CheckSource(object):
60  """A functor to check whether a source has any flags set that should cause it to be labeled bad."""
61 
62  def __init__(self, table, fluxLim, fluxMax, badStarPixelFlags):
63  self.keys = [table.getSchema().find(name).key for name in badStarPixelFlags]
64  self.keys.append(table.getCentroidFlagKey())
65  self.fluxLim = fluxLim
66  self.fluxMax = fluxMax
67 
68  def __call__(self, source):
69  for k in self.keys:
70  if source.get(k):
71  return False
72  if self.fluxLim != None and source.getPsfFlux() < self.fluxLim: # ignore faint objects
73  return False
74  if self.fluxMax != 0.0 and source.getPsfFlux() > self.fluxMax: # ignore bright objects
75  return False
76  return True
77 
78 class CatalogStarSelector(object):
79  ConfigClass = CatalogStarSelectorConfig
80 
81  def __init__(self, config=None):
82  """Construct a star selector that uses second moments
83 
84  This is a naive algorithm and should be used with caution.
85 
86  @param[in] config: An instance of CatalogStarSelectorConfig
87  """
88  if not config:
89  config = CatalogStarSelector.ConfigClass()
90 
91  self._kernelSize = config.kernelSize
92  self._borderWidth = config.borderWidth
93  self._fluxLim = config.fluxLim
94  self._fluxMax = config.fluxMax
95  self._badStarPixelFlags = config.badStarPixelFlags
96 
97  def selectStars(self, exposure, sources, matches=None):
98  """Return a list of PSF candidates that represent likely stars
99 
100  A list of PSF candidates may be used by a PSF fitter to construct a PSF.
101 
102  @param[in] exposure: the exposure containing the sources
103  @param[in] sources: a source list containing sources that may be stars
104  @param[in] matches: a match vector as produced by meas_astrom; not actually optional
105  (passing None just allows us to handle the exception better here
106  than in calling code)
107 
108  @return psfCandidateList: a list of PSF candidates.
109  """
110  import lsstDebug
111  display = lsstDebug.Info(__name__).display
112  displayExposure = lsstDebug.Info(__name__).displayExposure # display the Exposure + spatialCells
113  pauseAtEnd = lsstDebug.Info(__name__).pauseAtEnd # pause when done
114 
115  if matches is None:
116  raise RuntimeError(
117  "Cannot use catalog star selector without running astrometry."
118  )
119 
120  mi = exposure.getMaskedImage()
121 
122  if display:
123  frames = {}
124  if displayExposure:
125  frames["displayExposure"] = 1
126  ds9.mtv(mi, frame=frames["displayExposure"], title="PSF candidates")
127  #
128  # Read the reference catalogue
129  #
130  isGoodSource = CheckSource(sources, self._fluxLim, self._fluxMax, self._badStarPixelFlags)
131 
132  #
133  # Go through and find all the PSFs in the catalogue
134  #
135  # We'll split the image into a number of cells, each of which contributes only
136  # one PSF candidate star
137  #
138  psfCandidateList = []
139 
140  with ds9.Buffering():
141  for ref, source, d in matches:
142  if not ref.get("resolved"):
143  if not isGoodSource(source):
144  symb, ctype = "+", ds9.RED
145  else:
146  try:
147  psfCandidate = measAlg.makePsfCandidate(source, exposure)
148 
149  # The setXXX methods are class static, but it's convenient to call them on
150  # an instance as we don't know Exposure's pixel type
151  # (and hence psfCandidate's exact type)
152  if psfCandidate.getWidth() == 0:
153  psfCandidate.setBorderWidth(self._borderWidth)
154  psfCandidate.setWidth(self._kernelSize + 2*self._borderWidth)
155  psfCandidate.setHeight(self._kernelSize + 2*self._borderWidth)
156 
157  im = psfCandidate.getMaskedImage().getImage()
158  max = afwMath.makeStatistics(im, afwMath.MAX).getValue()
159  if not numpy.isfinite(max):
160  continue
161  psfCandidateList.append(psfCandidate)
162 
163  symb, ctype = "+", ds9.GREEN
164  except Exception as err:
165  symb, ctype = "o", ds9.RED
166  print "RHL", err
167  pass # FIXME: should log this!
168  else:
169  symb, ctype = "o", ds9.BLUE
170 
171  if display and displayExposure:
172  ds9.dot(symb, source.getX() - mi.getX0(), source.getY() - mi.getY0(),
173  size=4, frame=frames["displayExposure"], ctype=ctype)
174 
175  if display and pauseAtEnd:
176  raw_input("Continue? y[es] p[db] ")
177 
178  return psfCandidateList
179 
180 measAlg.starSelectorRegistry.register("catalog", CatalogStarSelector)
181 
boost::shared_ptr< PsfCandidate< PixelT > > makePsfCandidate(boost::shared_ptr< afw::table::SourceRecord > const &source, boost::shared_ptr< afw::image::Exposure< PixelT > > image)
Definition: PsfCandidate.h:188
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Definition: Statistics.cc:1082