Coverage for python/lsst/meas/algorithms/gaussianPsfFactory.py: 42%

43 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-17 10:00 +0000

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__ = ["GaussianPsfFactory", "SigmaPerFwhm"] 

25 

26import math 

27 

28from lsst.pex.config import Config, Field, ConfigurableField 

29from ._algorithmsLib import DoubleGaussianPsf, SingleGaussianPsf 

30 

31SigmaPerFwhm = 1.0 / (2.0 * math.sqrt(2.0 * math.log(2.0))) 

32 

33 

34def isPositive(x): 

35 return x > 0 

36 

37 

38class GaussianPsfFactory(Config): 

39 """Factory for simple Gaussian PSF models 

40 

41 Provides a high-level interface to DoubleGaussianPsf and SingleGaussianPsf 

42 by specifying Gaussian PSF model width in FWHM instead of sigma, 

43 and supporting computing kernel size as a multiple of PSF width. 

44 This makes it suitable for tasks where PSF width is not known in advance. 

45 """ 

46 size = Field( 

47 doc="Kernel size (width and height) (pixels); if None then sizeFactor is used", 

48 dtype=int, 

49 optional=True, 

50 default=None, 

51 check=isPositive, 

52 ) 

53 sizeFactor = Field( 

54 doc="Kernel size as a factor of fwhm (dimensionless); " 

55 "size = sizeFactor * fwhm; ignored if size is not None", 

56 dtype=float, 

57 optional=False, 

58 default=3.0, 

59 check=isPositive, 

60 ) 

61 minSize = Field( 

62 doc="Minimum kernel size if using sizeFactor (pixels); ignored if size is not None", 

63 dtype=int, 

64 optional=True, 

65 default=5, 

66 check=isPositive, 

67 ) 

68 maxSize = Field( 

69 doc="Maximum kernel size if using sizeFactor (pixels); ignored if size is not None", 

70 dtype=int, 

71 optional=True, 

72 default=None, 

73 check=isPositive, 

74 ) 

75 defaultFwhm = Field( 

76 doc="Default FWHM of Gaussian model of core of star (pixels)", 

77 dtype=float, 

78 default=3.0, 

79 check=isPositive, 

80 ) 

81 addWing = Field( 

82 doc="Add a Gaussian to represent wings?", 

83 dtype=bool, 

84 optional=False, 

85 default=True, 

86 ) 

87 wingFwhmFactor = Field( 

88 doc="wing width, as a multiple of core width (dimensionless); ignored if addWing false", 

89 dtype=float, 

90 optional=False, 

91 default=2.5, 

92 check=isPositive, 

93 ) 

94 wingAmplitude = Field( 

95 doc="wing amplitude, as a multiple of core amplitude (dimensionless); ignored if addWing false", 

96 dtype=float, 

97 optional=False, 

98 default=0.1, 

99 check=isPositive, 

100 ) 

101 

102 def computeSizeAndSigma(self, fwhm=None): 

103 """Compute kernel size and star width as sigma. The kernel size will be 

104 odd unless minSize or maxSize is used and that value is even. Assumes 

105 a valid config. 

106 

107 Parameters 

108 ---------- 

109 fwhm : `float` 

110 FWHM of core star (pixels); if None then defaultFwhm is used 

111 

112 Returns 

113 ------- 

114 size : `int` 

115 Kernel size (width == height) in pixels 

116 sigma : `float` 

117 Sigma equivalent to supplied FWHM, assuming a Gaussian (pixels) 

118 """ 

119 if fwhm is None: 

120 fwhm = self.defaultFwhm 

121 

122 if self.size is not None: 

123 size = self.size 

124 else: 

125 desSize = (int(self.sizeFactor * fwhm) // 2) * 2 + 1 # make result odd 

126 if self.minSize and self.minSize > desSize: 

127 size = self.minSize 

128 elif self.maxSize and self.maxSize < desSize: 

129 size = self.maxSize 

130 else: 

131 size = desSize 

132 

133 return size, fwhm * SigmaPerFwhm 

134 

135 def validate(self): 

136 Config.validate(self) 

137 if self.minSize and self.maxSize and self.minSize > self.maxSize: 

138 raise RuntimeError("minSize=%s > maxSize=%s" % (self.minSize, self.maxSize)) 

139 

140 def apply(self, fwhm=None): 

141 """Construct a GaussianPsf 

142 

143 Parameters 

144 ---------- 

145 fwhm : `float` 

146 FWHM of core of star (pixels); if None then self.defaultFwhm is used 

147 

148 Returns 

149 ------- 

150 DoubleGaussianPsf : ``lsst.meas.algorithms.DoubleGaussianPsf`` 

151 Returns if self.addWing is True 

152 SingleGaussianPsf : ``lsst.meas.algorithms.SingleGaussianPsf`` 

153 Returns if self.addWing is False 

154 """ 

155 kernelSize, sigma = self.computeSizeAndSigma(fwhm) 

156 if self.addWing: 

157 wingsSigma = sigma * self.wingFwhmFactor 

158 return DoubleGaussianPsf(kernelSize, kernelSize, sigma, wingsSigma, self.wingAmplitude) 

159 else: 

160 return SingleGaussianPsf(kernelSize, kernelSize, sigma) 

161 

162 @classmethod 

163 def makeField(cls, doc): 

164 """Make an lsst.pex.config.ConfigurableField 

165 """ 

166 def applyWrapper(config, **kwargs): 

167 """Construct a Gaussian PSF 

168 

169 Parameters 

170 ---------- 

171 config : instance of ``GaussianPsfFactory`` 

172 """ 

173 return config.apply(**kwargs) 

174 return ConfigurableField( 

175 doc=doc, 

176 target=applyWrapper, 

177 ConfigClass=cls 

178 )