Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

24import numpy as np 

25 

26import lsst.pex.config as pexConfig 

27from .sourceSelector import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry 

28from lsst.pipe.base import Struct 

29 

30 

31class MatcherSourceSelectorConfig(BaseSourceSelectorConfig): 

32 sourceFluxType = pexConfig.Field( 

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

34 dtype=str, 

35 default="Ap", 

36 ) 

37 minSnr = pexConfig.Field( 

38 dtype=float, 

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

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

41 default=40, 

42 ) 

43 excludePixelFlags = pexConfig.Field( 

44 dtype=bool, 

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

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

47 "to recover previous matcher selector behavior.", 

48 default=True, 

49 ) 

50 

51 

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

53class MatcherSourceSelectorTask(BaseSourceSelectorTask): 

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

55 

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

57 be PSF sources, just have reliable centroids. 

58 

59 Distinguished from astrometrySourceSelector because it is more lenient 

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

61 """ 

62 ConfigClass = MatcherSourceSelectorConfig 

63 

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

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

66 

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

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

69 

70 Parameters: 

71 ----------- 

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

73 Catalog of sources to select from. 

74 This catalog must be contiguous in memory. 

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

76 Ignored in this SourceSelector. 

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

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

79 

80 Return 

81 ------ 

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

83 The struct contains the following data: 

84 

85 - selected : `array` of `bool`` 

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

87 sourceCat. 

88 """ 

89 self._getSchemaKeys(sourceCat.schema) 

90 

91 good = self._isUsable(sourceCat) 

92 if self.config.excludePixelFlags: 

93 good = good & self._isGood(sourceCat) 

94 return Struct(selected=good) 

95 

96 def _getSchemaKeys(self, schema): 

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

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

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

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

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

102 

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

104 self.fluxField = fluxPrefix + "instFlux" 

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

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

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

108 

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

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

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

112 

113 def _isParent(self, sourceCat): 

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

115 test = (sourceCat.get(self.parentKey) == 0) 

116 return test 

117 

118 def _hasCentroid(self, sourceCat): 

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

120 return np.isfinite(sourceCat.get(self.centroidXKey)) \ 

121 & np.isfinite(sourceCat.get(self.centroidYKey)) \ 

122 & ~sourceCat.get(self.centroidFlagKey) 

123 

124 def _goodSN(self, sourceCat): 

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

126 if self.config.minSnr <= 0: 

127 return True 

128 else: 

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

130 return sourceCat.get(self.fluxKey)/sourceCat.get(self.fluxErrKey) > self.config.minSnr 

131 

132 def _isUsable(self, sourceCat): 

133 """ 

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

135 have a poor centroid. 

136 

137 For a source to be usable it must: 

138 - have a valid centroid 

139 - not be deblended 

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

141 - have adequate signal-to-noise 

142 """ 

143 return self._hasCentroid(sourceCat) \ 

144 & self._isParent(sourceCat) \ 

145 & self._goodSN(sourceCat) \ 

146 & ~sourceCat.get(self.fluxFlagKey) 

147 

148 def _isGood(self, sourceCat): 

149 """ 

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

151 have a poor centroid. 

152 

153 For a source to be usable it must: 

154 - Not be on a CCD edge. 

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

156 - Not have a saturated pixel in their footprint. 

157 """ 

158 return ~sourceCat.get(self.edgeKey) & \ 

159 ~sourceCat.get(self.interpolatedCenterKey) & \ 

160 ~sourceCat.get(self.saturatedKey)