23 """Select sources that are useful for astrometry.
25 Such sources have good signal-to-noise, are well centroided, not blended,
26 and not flagged with a handful of "bad" flags.
29 __all__ = [
"AstrometrySourceSelectorConfig",
"AstrometrySourceSelectorTask"]
34 from .sourceSelector
import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry
35 from lsst.pipe.base
import Struct
36 from functools
import reduce
40 badFlags = pexConfig.ListField(
41 doc=
"List of flags which cause a source to be rejected as bad",
44 "base_PixelFlags_flag_edge",
45 "base_PixelFlags_flag_interpolatedCenter",
46 "base_PixelFlags_flag_saturatedCenter",
47 "base_PixelFlags_flag_crCenter",
48 "base_PixelFlags_flag_bad",
51 sourceFluxType = pexConfig.Field(
52 doc=
"Type of source flux; typically one of Ap or Psf",
56 minSnr = pexConfig.Field(
58 doc=
"Minimum allowed signal-to-noise ratio for sources used for matching "
59 "(in the flux specified by sourceFluxType); <= 0 for no limit",
64 @pexConfig.registerConfigurable("astrometry", sourceSelectorRegistry)
66 """Select sources that are useful for astrometry.
68 Good astrometry sources have high signal/noise, are non-blended, and
69 did not have certain "bad" flags set during source extraction. They need not
70 be PSF sources, just have reliable centroids.
72 ConfigClass = AstrometrySourceSelectorConfig
75 BaseSourceSelectorTask.__init__(self, *args, **kwargs)
78 """Return a selection of sources that are useful for astrometry.
82 sourceCat : `lsst.afw.table.SourceCatalog`
83 Catalog of sources to select from.
84 This catalog must be contiguous in memory.
85 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
86 Ignored in this SourceSelector.
87 exposure : `lsst.afw.image.Exposure` or None
88 The exposure the catalog was built from; used for debug display.
92 struct : `lsst.pipe.base.Struct`
93 The struct contains the following data:
95 - selected : `array` of `bool``
96 Boolean array of sources that were selected, same length as
101 bad = reduce(
lambda x, y: np.logical_or(x, sourceCat.get(y)), self.config.badFlags,
False)
102 good = self.
_isGood_isGood(sourceCat)
103 return Struct(selected=good & ~bad)
105 def _getSchemaKeys(self, schema):
106 """Extract and save the necessary keys from schema with asKey.
109 self.
nChildKeynChildKey = schema[
"deblend_nChild"].asKey()
116 self.
edgeKeyedgeKey = schema[
"base_PixelFlags_flag_edge"].asKey()
118 self.
saturatedKeysaturatedKey = schema[
"base_PixelFlags_flag_saturated"].asKey()
120 fluxPrefix =
"slot_%sFlux_" % (self.config.sourceFluxType,)
121 self.
instFluxKeyinstFluxKey = schema[fluxPrefix +
"instFlux"].asKey()
125 def _isMultiple(self, sourceCat):
126 """Return True for each source that is likely multiple sources.
128 test = (sourceCat.get(self.
parentKeyparentKey) != 0) | (sourceCat.get(self.
nChildKeynChildKey) != 0)
130 for i, cat
in enumerate(sourceCat):
131 footprint = cat.getFootprint()
132 test[i] |= (footprint
is not None)
and (len(footprint.getPeaks()) > 1)
135 def _hasCentroid(self, sourceCat):
136 """Return True for each source that has a valid centroid
138 def checkNonfiniteCentroid():
139 """Return True for sources with non-finite centroids.
141 return ~np.isfinite(sourceCat.get(self.
centroidXKeycentroidXKey)) | \
142 ~np.isfinite(sourceCat.get(self.
centroidYKeycentroidYKey))
143 assert ~checkNonfiniteCentroid().any(), \
144 "Centroids not finite for %d unflagged sources." % (checkNonfiniteCentroid().sum())
145 return np.isfinite(sourceCat.get(self.
centroidXErrKeycentroidXErrKey)) \
149 def _goodSN(self, sourceCat):
150 """Return True for each source that has Signal/Noise > config.minSnr.
152 if self.config.minSnr <= 0:
155 with np.errstate(invalid=
"ignore"):
158 def _isUsable(self, sourceCat):
159 """Return True for each source that is usable for matching, even if it may
160 have a poor centroid.
162 For a source to be usable it must:
163 - have a valid centroid
165 - have a valid flux (of the type specified in this object's constructor)
166 - have adequate signal-to-noise
171 & self.
_goodSN_goodSN(sourceCat) \
174 def _isGood(self, sourceCat):
175 """Return True for each source that is usable for matching and likely has a
178 The additional tests for a good centroid, beyond isUsable, are:
179 - not interpolated in the center
184 return self.
_isUsable_isUsable(sourceCat) \
187 & ~sourceCat.get(self.
edgeKeyedgeKey)
189 def _isBadFlagged(self, source):
190 """Return True if any of config.badFlags are set for this source.
192 return any(source.get(flag)
for flag
in self.config.badFlags)
def _hasCentroid(self, sourceCat)
def _isUsable(self, sourceCat)
def __init__(self, *args, **kwargs)
def selectSources(self, sourceCat, matches=None, exposure=None)
def _isMultiple(self, sourceCat)
def _isGood(self, sourceCat)
def _getSchemaKeys(self, schema)
def _goodSN(self, sourceCat)