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