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

44 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-08-01 01:39 -0700

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 .singleGaussianPsf import SingleGaussianPsf 

30from .doubleGaussianPsf import DoubleGaussianPsf 

31 

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

33 

34 

35def isPositive(x): 

36 return x > 0 

37 

38 

39class GaussianPsfFactory(Config): 

40 """Factory for simple Gaussian PSF models 

41 

42 Provides a high-level interface to DoubleGaussianPsf and SingleGaussianPsf 

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

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

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

46 """ 

47 size = Field( 

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

49 dtype=int, 

50 optional=True, 

51 default=None, 

52 check=isPositive, 

53 ) 

54 sizeFactor = Field( 

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

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

57 dtype=float, 

58 optional=False, 

59 default=3.0, 

60 check=isPositive, 

61 ) 

62 minSize = Field( 

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

64 dtype=int, 

65 optional=True, 

66 default=5, 

67 check=isPositive, 

68 ) 

69 maxSize = Field( 

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

71 dtype=int, 

72 optional=True, 

73 default=None, 

74 check=isPositive, 

75 ) 

76 defaultFwhm = Field( 

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

78 dtype=float, 

79 default=3.0, 

80 check=isPositive, 

81 ) 

82 addWing = Field( 

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

84 dtype=bool, 

85 optional=False, 

86 default=True, 

87 ) 

88 wingFwhmFactor = Field( 

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

90 dtype=float, 

91 optional=False, 

92 default=2.5, 

93 check=isPositive, 

94 ) 

95 wingAmplitude = Field( 

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

97 dtype=float, 

98 optional=False, 

99 default=0.1, 

100 check=isPositive, 

101 ) 

102 

103 def computeSizeAndSigma(self, fwhm=None): 

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

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

106 a valid config. 

107 

108 Parameters 

109 ---------- 

110 fwhm : `float` 

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

112 

113 Returns 

114 ------- 

115 size : `int` 

116 Kernel size (width == height) in pixels 

117 sigma : `float` 

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

119 """ 

120 if fwhm is None: 

121 fwhm = self.defaultFwhm 

122 

123 if self.size is not None: 

124 size = self.size 

125 else: 

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

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

128 size = self.minSize 

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

130 size = self.maxSize 

131 else: 

132 size = desSize 

133 

134 return size, fwhm * SigmaPerFwhm 

135 

136 def validate(self): 

137 Config.validate(self) 

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

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

140 

141 def apply(self, fwhm=None): 

142 """Construct a GaussianPsf 

143 

144 Parameters 

145 ---------- 

146 fwhm : `float` 

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

148 

149 Returns 

150 ------- 

151 DoubleGaussianPsf : ``lsst.meas.algorithms.DoubleGaussianPsf`` 

152 Returns if self.addWing is True 

153 SingleGaussianPsf : ``lsst.meas.algorithms.SingleGaussianPsf`` 

154 Returns if self.addWing is False 

155 """ 

156 kernelSize, sigma = self.computeSizeAndSigma(fwhm) 

157 if self.addWing: 

158 wingsSigma = sigma * self.wingFwhmFactor 

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

160 else: 

161 return SingleGaussianPsf(kernelSize, kernelSize, sigma) 

162 

163 @classmethod 

164 def makeField(cls, doc): 

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

166 """ 

167 def applyWrapper(config, **kwargs): 

168 """Construct a Gaussian PSF 

169 

170 Parameters 

171 ---------- 

172 config : instance of ``GaussianPsfFactory`` 

173 """ 

174 return config.apply(**kwargs) 

175 return ConfigurableField( 

176 doc=doc, 

177 target=applyWrapper, 

178 ConfigClass=cls 

179 )