lsst.meas.algorithms  15.0-9-g4b67208d
starSelector.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 
24 __all__ = ["BaseStarSelectorConfig", "BaseStarSelectorTask", "starSelectorRegistry"]
25 
26 import abc
27 
28 import numpy as np
29 
30 from lsst.afw.table import SourceCatalog, Schema
31 import lsst.afw.math as afwMath
32 import lsst.pex.config as pexConfig
33 import lsst.pipe.base as pipeBase
34 from . import makePsfCandidate
35 
36 
37 class BaseStarSelectorConfig(pexConfig.Config):
38  kernelSize = pexConfig.Field(
39  doc="size of the kernel to create",
40  dtype=int,
41  default=21,
42  )
43  borderWidth = pexConfig.Field(
44  doc="number of pixels to ignore around the edge of PSF candidate postage stamps",
45  dtype=int,
46  default=0,
47  )
48  badFlags = pexConfig.ListField(
49  doc="List of flags which cause a source to be rejected as bad",
50  dtype=str,
51  default=[
52  "base_PixelFlags_flag_edge",
53  "base_PixelFlags_flag_interpolatedCenter",
54  "base_PixelFlags_flag_saturatedCenter",
55  "base_PixelFlags_flag_crCenter",
56  "base_PixelFlags_flag_bad",
57  "base_PixelFlags_flag_interpolated",
58  ],
59  )
60 
61 
62 class BaseStarSelectorTask(pipeBase.Task, metaclass=abc.ABCMeta):
63  """!Base class for star selectors
64 
65  Register all star selectors with the starSelectorRegistry using:
66  starSelectorRegistry.register(name, class)
67  """
68 
69  usesMatches = False # Does the star selector use the "matches" argument in the "run method? Few do.
70  ConfigClass = BaseStarSelectorConfig
71  _DefaultName = "starSelector"
72 
73  def __init__(self, schema, **kwds):
74  # catch code that passed config positionally before schema argument was added
75  assert isinstance(schema, Schema)
76  pipeBase.Task.__init__(self, **kwds)
77 
78  def run(self, exposure, sourceCat, matches=None, isStarField=None):
79  """!Select stars, make PSF candidates, and set a flag field True for stars in the input catalog
80 
81  @param[in] exposure the exposure containing the sources
82  @param[in] sourceCat catalog of sources that may be stars (an lsst.afw.table.SourceCatalog)
83  @param[in] matches astrometric matches; ignored by this star selector
84  (an lsst.afw.table.ReferenceMatchVector), or None. Some star selectors
85  will ignore this argument, others may require it. See the usesMatches class variable.
86  @param[in] isStarField name of flag field to set True for stars, or None to not set a field;
87  the field is left unchanged for non-stars
88 
89  @return an lsst.pipe.base.Struct containing:
90  - starCat catalog of stars that were selected as stars and successfuly made into PSF candidates
91  (a subset of sourceCat whose records are shallow copies)
92  - psfCandidates list of PSF candidates (lsst.meas.algorithms.PsfCandidate)
93  """
94  selRes = self.selectStars(exposure=exposure, sourceCat=sourceCat, matches=matches)
95  psfRes = self.makePsfCandidates(exposure=exposure, starCat=selRes.starCat)
96 
97  if isStarField is not None:
98  isStarKey = sourceCat.schema[isStarField].asKey()
99  for star in psfRes.goodStarCat:
100  star.set(isStarKey, True)
101 
102  return pipeBase.Struct(
103  starCat=psfRes.goodStarCat,
104  psfCandidates=psfRes.psfCandidates,
105  )
106 
107  @abc.abstractmethod
108  def selectStars(self, exposure, sourceCat, matches=None):
109  """!Return a catalog of stars: a subset of sourceCat whose records are shallow copies
110 
111  @param[in] exposure the exposure containing the sources
112  @param[in] sourceCat catalog of sources that may be stars (an lsst.afw.table.SourceCatalog)
113  @param[in] matches astrometric matches; ignored by this star selector
114  (an lsst.afw.table.ReferenceMatchVector), or None. Some star selectors
115  will ignore this argument, others may require it. See the usesMatches class variable.
116 
117  @warning The returned catalog must have records that are shallow copies
118  (fortunately this is the default behavior when you add a record from one catalog to another);
119  otherwise the run method cannot set the isStarField flag in the original source catalog.
120 
121  @return a pipeBase.Struct containing:
122  - starCat a catalog of stars (a subset of sourceCat whose records are shallow copies)
123  """
124  raise NotImplementedError("BaseStarSelectorTask is abstract, subclasses must override this method")
125 
126  def makePsfCandidates(self, exposure, starCat):
127  """!Make a list of PSF candidates from a star catalog
128 
129  @param[in] exposure the exposure containing the sources
130  @param[in] starCat catalog of stars (an lsst.afw.table.SourceCatalog),
131  e.g. as returned by the run or selectStars method
132 
133  @return an lsst.pipe.base.Struct with fields:
134  - psfCandidates list of PSF candidates (lsst.meas.algorithms.PsfCandidate)
135  - goodStarCat catalog of stars that were successfully made into PSF candidates (a subset of starCat)
136  """
137  goodStarCat = SourceCatalog(starCat.schema)
138 
139  psfCandidateList = []
140  didSetSize = False
141  for star in starCat:
142  try:
143  psfCandidate = makePsfCandidate(star, exposure)
144 
145  # The setXXX methods are class static, but it's convenient to call them on
146  # an instance as we don't know Exposure's pixel type
147  # (and hence psfCandidate's exact type)
148  if not didSetSize:
149  psfCandidate.setBorderWidth(self.config.borderWidth)
150  psfCandidate.setWidth(self.config.kernelSize + 2*self.config.borderWidth)
151  psfCandidate.setHeight(self.config.kernelSize + 2*self.config.borderWidth)
152  didSetSize = True
153 
154  im = psfCandidate.getMaskedImage().getImage()
155  except Exception as err:
156  self.log.debug("Failed to make a psfCandidate from star %d: %s", star.getId(), err)
157  continue
158 
159  vmax = afwMath.makeStatistics(im, afwMath.MAX).getValue()
160  if not np.isfinite(vmax):
161  continue
162  psfCandidateList.append(psfCandidate)
163  goodStarCat.append(star)
164 
165  return pipeBase.Struct(
166  psfCandidates=psfCandidateList,
167  goodStarCat=goodStarCat,
168  )
169 
170 
171 starSelectorRegistry = pexConfig.makeRegistry(
172  doc="A registry of star selectors (subclasses of BaseStarSelectorTask)",
173 )
def selectStars(self, exposure, sourceCat, matches=None)
Return a catalog of stars: a subset of sourceCat whose records are shallow copies.
def makePsfCandidates(self, exposure, starCat)
Make a list of PSF candidates from a star catalog.
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
std::shared_ptr< PsfCandidate< PixelT > > makePsfCandidate(boost::shared_ptr< afw::table::SourceRecord > const &source, boost::shared_ptr< afw::image::Exposure< PixelT > > image)
Return a PsfCandidate of the right sort.
Definition: PsfCandidate.h:182
def run(self, exposure, sourceCat, matches=None, isStarField=None)
Select stars, make PSF candidates, and set a flag field True for stars in the input catalog...
Definition: starSelector.py:78