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 

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 

105 

106 kernel size will be odd unless minSize or maxSize is used and that value is even. 

107 

108 @param[in] fwhm: FWHM of core star (pixels); if None then defaultFwhm is used 

109 @return two values: 

110 - kernel size (width == height) in pixels 

111 - sigma equivalent to supplied fwhm, assuming a Gaussian (pixels) 

112 

113 @warning assumes a valid config 

114 """ 

115 if fwhm is None: 

116 fwhm = self.defaultFwhm 

117 

118 if self.size is not None: 

119 size = self.size 

120 else: 

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

122 if self.minSize and self.minSize > desSize: 122 ↛ 123line 122 didn't jump to line 123, because the condition on line 122 was never true

123 size = self.minSize 

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

125 size = self.maxSize 

126 else: 

127 size = desSize 

128 

129 return size, fwhm * SigmaPerFwhm 

130 

131 def validate(self): 

132 Config.validate(self) 

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

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

135 

136 def apply(self, fwhm=None): 

137 """Construct a GaussianPsf 

138 

139 @param[in] self: an instance of ConfigClass 

140 @param[in] fwhm: FWHM of core of star (pixels); if None then self.defaultFwhm is used 

141 @return a DoubleGaussianPsf if self.addWing is True, else a SingleGaussianPsf 

142 """ 

143 kernelSize, sigma = self.computeSizeAndSigma(fwhm) 

144 if self.addWing: 

145 wingsSigma = sigma * self.wingFwhmFactor 

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

147 else: 

148 return SingleGaussianPsf(kernelSize, kernelSize, sigma) 

149 

150 @classmethod 

151 def makeField(cls, doc): 

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

153 """ 

154 def applyWrapper(config, **kwargs): 

155 """Construct a Gaussian PSF 

156 

157 @param[in] config: an instance of GaussianPsfFactory 

158 """ 

159 return config.apply(**kwargs) 

160 return ConfigurableField( 

161 doc=doc, 

162 target=applyWrapper, 

163 ConfigClass=cls 

164 )