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