Coverage for python/lsst/meas/algorithms/matcherSourceSelector.py: 45%

49 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-16 11:09 +0000

1# This file is part of meas_algorithms. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

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 GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22__all__ = ["MatcherSourceSelectorConfig", "MatcherSourceSelectorTask"] 

23 

24from deprecated.sphinx import deprecated 

25 

26import numpy as np 

27 

28import lsst.pex.config as pexConfig 

29from .sourceSelector import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry 

30from lsst.pipe.base import Struct 

31 

32 

33class MatcherSourceSelectorConfig(BaseSourceSelectorConfig): 

34 sourceFluxType = pexConfig.Field( 

35 doc="Type of source flux; typically one of Ap or Psf", 

36 dtype=str, 

37 default="Ap", 

38 ) 

39 minSnr = pexConfig.Field( 

40 dtype=float, 

41 doc="Minimum allowed signal-to-noise ratio for sources used for matching " 

42 "(in the flux specified by sourceFluxType); <= 0 for no limit", 

43 default=40, 

44 ) 

45 excludePixelFlags = pexConfig.Field( 

46 dtype=bool, 

47 doc="Exclude objects that have saturated, interpolated, or edge " 

48 "pixels using PixelFlags. For matchOptimisticB set this to False " 

49 "to recover previous matcher selector behavior.", 

50 default=True, 

51 ) 

52 

53 

54# remove this file on DM-41146 

55@deprecated(reason=("This Task has been replaced by an appropriately configured ScienceSourceSelector." 

56 " Will be removed after v27."), 

57 version="v27.0", category=FutureWarning) 

58@pexConfig.registerConfigurable("matcher", sourceSelectorRegistry) 

59class MatcherSourceSelectorTask(BaseSourceSelectorTask): 

60 """Select sources that are useful for matching. 

61 

62 Good matching sources have high signal/noise, are non-blended. They need not 

63 be PSF sources, just have reliable centroids. 

64 

65 Distinguished from astrometrySourceSelector because it is more lenient 

66 (i.e. not checking footprints or bad flags). 

67 """ 

68 ConfigClass = MatcherSourceSelectorConfig 

69 

70 def __init__(self, *args, **kwargs): 

71 BaseSourceSelectorTask.__init__(self, *args, **kwargs) 

72 

73 def selectSources(self, sourceCat, matches=None, exposure=None): 

74 """Return a selection of sources that are useful for matching. 

75 

76 Parameters 

77 ---------- 

78 sourceCat : `lsst.afw.table.SourceCatalog` 

79 Catalog of sources to select from. 

80 This catalog must be contiguous in memory. 

81 matches : `list` of `lsst.afw.table.ReferenceMatch` or None 

82 Ignored in this SourceSelector. 

83 exposure : `lsst.afw.image.Exposure` or None 

84 The exposure the catalog was built from; used for debug display. 

85 

86 Returns 

87 ------- 

88 struct : `lsst.pipe.base.Struct` 

89 The struct contains the following data: 

90 

91 ``selected`` 

92 Boolean array of sources that were selected, same length as 

93 sourceCat. (`numpy.ndarray` of `bool`) 

94 """ 

95 self._getSchemaKeys(sourceCat.schema) 

96 

97 good = self._isUsable(sourceCat) 

98 if self.config.excludePixelFlags: 

99 good = good & self._isGood(sourceCat) 

100 return Struct(selected=good) 

101 

102 def _getSchemaKeys(self, schema): 

103 """Extract and save the necessary keys from schema with asKey. 

104 """ 

105 self.parentKey = schema["parent"].asKey() 

106 self.centroidXKey = schema["slot_Centroid_x"].asKey() 

107 self.centroidYKey = schema["slot_Centroid_y"].asKey() 

108 self.centroidFlagKey = schema["slot_Centroid_flag"].asKey() 

109 

110 fluxPrefix = "slot_%sFlux_" % (self.config.sourceFluxType,) 

111 self.fluxField = fluxPrefix + "instFlux" 

112 self.fluxKey = schema[fluxPrefix + "instFlux"].asKey() 

113 self.fluxFlagKey = schema[fluxPrefix + "flag"].asKey() 

114 self.fluxErrKey = schema[fluxPrefix + "instFluxErr"].asKey() 

115 

116 self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey() 

117 self.interpolatedCenterKey = schema["base_PixelFlags_flag_interpolatedCenter"].asKey() 

118 self.saturatedKey = schema["base_PixelFlags_flag_saturated"].asKey() 

119 

120 def _isParent(self, sourceCat): 

121 """Return True for each source that is the parent source. 

122 """ 

123 test = (sourceCat[self.parentKey] == 0) 

124 return test 

125 

126 def _hasCentroid(self, sourceCat): 

127 """Return True for each source that has a valid centroid 

128 """ 

129 return np.isfinite(sourceCat[self.centroidXKey]) \ 

130 & np.isfinite(sourceCat[self.centroidYKey]) \ 

131 & ~sourceCat[self.centroidFlagKey] 

132 

133 def _goodSN(self, sourceCat): 

134 """Return True for each source that has Signal/Noise > config.minSnr. 

135 """ 

136 if self.config.minSnr <= 0: 

137 return True 

138 else: 

139 with np.errstate(invalid="ignore"): # suppress NAN warnings 

140 return sourceCat[self.fluxKey]/sourceCat[self.fluxErrKey] > self.config.minSnr 

141 

142 def _isUsable(self, sourceCat): 

143 """ 

144 Return True for each source that is usable for matching, even if it may 

145 have a poor centroid. 

146 

147 For a source to be usable it must: 

148 

149 - have a valid centroid 

150 - not be deblended 

151 - have a valid instFlux (of the type specified in this object's constructor) 

152 - have adequate signal-to-noise 

153 """ 

154 return self._hasCentroid(sourceCat) \ 

155 & self._isParent(sourceCat) \ 

156 & self._goodSN(sourceCat) \ 

157 & ~sourceCat[self.fluxFlagKey] 

158 

159 def _isGood(self, sourceCat): 

160 """ 

161 Return True for each source that is usable for matching, even if it may 

162 have a poor centroid. 

163 

164 For a source to be usable it must: 

165 

166 - Not be on a CCD edge. 

167 - Not have an interpolated pixel within 3x3 around their centroid. 

168 - Not have a saturated pixel in their footprint. 

169 """ 

170 return ~sourceCat[self.edgeKey] & \ 

171 ~sourceCat[self.interpolatedCenterKey] & \ 

172 ~sourceCat[self.saturatedKey]