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