Coverage for tests/test_gaussianPsf.py: 19%

149 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-20 02:34 -0700

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 

22import math 

23import unittest 

24import numpy as np 

25 

26import lsst.geom 

27import lsst.afw.image as afwImage 

28import lsst.afw.math as afwMath 

29import lsst.meas.algorithms as measAlg 

30import lsst.pex.exceptions as pexExceptions 

31import lsst.utils.tests 

32 

33try: 

34 display 

35except NameError: 

36 display = False 

37else: 

38 import lsst.afw.display as afwDisplay 

39 afwDisplay.setDefaultMaskTransparency(75) 

40 

41 

42class GaussianPsfTestCase(lsst.utils.tests.TestCase): 

43 """Test SingleGaussianPsf and DoubleGaussianPsf. 

44 

45 This test case may be extended to cover any new classes derived from KernelPsf. 

46 """ 

47 def setUp(self): 

48 FWHM = 5 

49 self.ksize = 25 # size of desired kernel 

50 sigma = FWHM/(2*math.sqrt(2*math.log(2))) 

51 self.psfDg = measAlg.DoubleGaussianPsf(self.ksize, self.ksize, 

52 sigma, 1, 0.1) 

53 self.psfSg = measAlg.SingleGaussianPsf(self.ksize, self.ksize, sigma) 

54 

55 def tearDown(self): 

56 del self.psfDg 

57 del self.psfSg 

58 

59 def testComputeImage(self): 

60 """Test the computation of the PSF's image at a point.""" 

61 

62 for psf in [self.psfDg, self.psfSg]: 

63 ccdXY = lsst.geom.Point2D(0, 0) 

64 kIm = psf.computeImage(ccdXY) 

65 

66 if False: 

67 afwDisplay.Display(frame=1).mtv(kIm, title=self._testMethodName + ": kIm") 

68 

69 self.assertEqual(kIm.getWidth(), self.ksize) 

70 kIm = psf.computeImage(ccdXY) 

71 self.assertAlmostEqual(afwMath.makeStatistics(kIm, afwMath.SUM).getValue(), 1.0) 

72 

73 def testComputeImage2(self): 

74 """Test the computation of the PSF's image at a point. 

75 """ 

76 ccdXY = lsst.geom.Point2D(0, 0) 

77 for psf in [self.psfDg, self.psfSg]: 

78 kIm = psf.computeImage(ccdXY) 

79 self.assertEqual(kIm.getWidth(), self.ksize) 

80 self.assertAlmostEqual(afwMath.makeStatistics(kIm, afwMath.SUM).getValue(), 1.0) 

81 

82 def testKernel(self): 

83 """Test the creation of the dgPsf's kernel. 

84 """ 

85 for psf in [self.psfDg, self.psfSg]: 

86 kIm = afwImage.ImageD(psf.getKernel().getDimensions()) 

87 psf.getKernel().computeImage(kIm, False) 

88 

89 self.assertEqual(kIm.getWidth(), self.ksize) 

90 self.assertAlmostEqual(afwMath.makeStatistics(kIm, afwMath.SUM).getValue(), 1.0) 

91 

92 if False: 

93 afwDisplay.Display(frame=2).mtv(kIm, title=self._testMethodName + ": kIm") 

94 

95 def testInvalidDgPsf(self): 

96 """Test parameters of dgPsfs, both valid and not. 

97 """ 

98 sigma1, sigma2, b = 1, 0, 0 # sigma2 may be 0 iff b == 0 

99 measAlg.DoubleGaussianPsf(self.ksize, self.ksize, sigma1, sigma2, b) 

100 

101 def badSigma1(): 

102 sigma1 = 0 

103 measAlg.DoubleGaussianPsf(self.ksize, self.ksize, sigma1, sigma2, b) 

104 

105 with self.assertRaises(pexExceptions.DomainError): 

106 badSigma1() 

107 

108 def badSigma2(): 

109 sigma2, b = 0, 1 

110 measAlg.DoubleGaussianPsf(self.ksize, self.ksize, sigma1, sigma2, b) 

111 

112 with self.assertRaises(pexExceptions.DomainError): 

113 badSigma2() 

114 

115 def testInvalidSgPsf(self): 

116 """Test parameters of sgPsfs, both valid and not. 

117 """ 

118 sigma = 1. 

119 measAlg.SingleGaussianPsf(self.ksize, self.ksize, sigma) 

120 

121 def badSigma1(): 

122 sigma = 0 

123 measAlg.SingleGaussianPsf(self.ksize, self.ksize, sigma) 

124 

125 with self.assertRaises(pexExceptions.DomainError): 

126 badSigma1() 

127 

128 def testGetImage(self): 

129 """Test returning a realisation of the dgPsf. 

130 """ 

131 for psf in [self.psfSg, self.psfDg]: 

132 xcen = psf.getKernel().getWidth()//2 

133 ycen = psf.getKernel().getHeight()//2 

134 

135 stamps = [] 

136 trueCenters = [] 

137 for x, y in ([10, 10], [9.4999, 10.4999], [10.5001, 10.5001]): 

138 fx, fy = x - int(x), y - int(y) 

139 if fx >= 0.5: 

140 fx -= 1.0 

141 if fy >= 0.5: 

142 fy -= 1.0 

143 

144 im = psf.computeImage(lsst.geom.Point2D(x, y)).convertF() 

145 

146 stamps.append(im.Factory(im, True)) 

147 trueCenters.append([xcen + fx, ycen + fy]) 

148 

149 if display: 

150 mos = afwDisplay.utils.Mosaic() # control mosaics 

151 disp = afwDisplay.Display(frame=0) 

152 disp.mtv(mos.makeMosaic(stamps), title=self._testMethodName + ": mosaic") 

153 

154 for i in range(len(trueCenters)): 

155 bbox = mos.getBBox(i) 

156 

157 disp.dot("+", 

158 bbox.getMinX() + xcen, bbox.getMinY() + ycen, ctype=afwDisplay.RED, size=1) 

159 disp.dot("+", 

160 bbox.getMinX() + trueCenters[i][0], bbox.getMinY() + trueCenters[i][1]) 

161 

162 disp.dot("%.2f, %.2f" % (trueCenters[i][0], trueCenters[i][1]), 

163 bbox.getMinX() + xcen, bbox.getMinY() + 2) 

164 

165 def testKernelPsf(self): 

166 """Test creating a Psf from a Kernel. 

167 """ 

168 x, y = 10.4999, 10.4999 

169 ksize = 15 

170 sigma1 = 1 

171 # 

172 # Make a PSF from that kernel 

173 # 

174 kPsf = measAlg.KernelPsf(afwMath.AnalyticKernel(ksize, ksize, 

175 afwMath.GaussianFunction2D(sigma1, sigma1))) 

176 

177 kIm = kPsf.computeImage(lsst.geom.Point2D(x, y)) 

178 # 

179 # And now via the dgPsf model 

180 # 

181 dgPsf = measAlg.DoubleGaussianPsf(ksize, ksize, sigma1) 

182 dgIm = dgPsf.computeImage(lsst.geom.Point2D(x, y)) 

183 # 

184 # Check that they're the same 

185 # 

186 diff = type(kIm)(kIm, True) 

187 diff -= dgIm 

188 stats = afwMath.makeStatistics(diff, afwMath.MAX | afwMath.MIN) 

189 self.assertAlmostEqual(stats.getValue(afwMath.MAX), 0.0, places=16) 

190 self.assertAlmostEqual(stats.getValue(afwMath.MIN), 0.0, places=16) 

191 

192 for pad in [-2, 4, 0]: 

193 resizedKPsf = kPsf.resized(ksize + pad, ksize + pad) 

194 self.assertEqual(resizedKPsf.computeBBox().getDimensions(), 

195 lsst.geom.Extent2I(ksize + pad, ksize + pad)) 

196 self.assertEqual(resizedKPsf.getKernel().getKernelParameters(), 

197 kPsf.getKernel().getKernelParameters()) 

198 self._compareKernelImages(kPsf, resizedKPsf) 

199 if display: 

200 mos = afwDisplay.utils.Mosaic() 

201 mos.setBackground(-0.1) 

202 afwDisplay.Display(frame=1).mtv(mos.makeMosaic([kIm, dgIm, diff], mode="x"), 

203 title=self._testMethodName + ": mosaic") 

204 

205 def testResize(self): 

206 """Test that resized Single and Double Gaussian PSFs have 

207 same model parameters, but new kernel dimensions. 

208 """ 

209 

210 for lengthNew in [1, 11, 99]: 

211 # Test Double Gaussian 

212 psfResized = self.psfDg.resized(lengthNew, lengthNew) 

213 self.assertEqual(psfResized.getSigma1(), self.psfDg.getSigma1()) 

214 self.assertEqual(psfResized.getSigma2(), self.psfDg.getSigma2()) 

215 self.assertEqual(psfResized.getB(), self.psfDg.getB()) 

216 self._compareKernelImages(psfResized, self.psfDg) 

217 

218 self.assertEqual(psfResized.getKernel().getWidth(), lengthNew) 

219 self.assertEqual(psfResized.getKernel().getHeight(), lengthNew) 

220 

221 # Test Single Gaussian Parameters 

222 psfResized = self.psfSg.resized(lengthNew, lengthNew) 

223 self.assertEqual(psfResized.getSigma(), self.psfSg.getSigma()) 

224 self._compareKernelImages(psfResized, self.psfSg) 

225 

226 self.assertEqual(psfResized.getKernel().getWidth(), lengthNew) 

227 self.assertEqual(psfResized.getKernel().getHeight(), lengthNew) 

228 

229 def _compareKernelImages(self, psf1, psf2): 

230 """Test that overlapping portions of kernel images are identical. 

231 """ 

232 im1 = psf1.computeKernelImage(psf1.getAveragePosition()) 

233 im2 = psf2.computeKernelImage(psf2.getAveragePosition()) 

234 bboxIntersection = im1.getBBox() 

235 bboxIntersection.clip(im2.getBBox()) 

236 im1Intersection = afwImage.ImageD(im1, bboxIntersection) 

237 im2Intersection = afwImage.ImageD(im2, bboxIntersection) 

238 scale1 = im1.getArray().sum()/im1Intersection.getArray().sum() 

239 scale2 = im2.getArray().sum()/im2Intersection.getArray().sum() 

240 im1Arr = scale1*im1Intersection.getArray() 

241 im2Arr = scale2*im2Intersection.getArray() 

242 self.assertTrue(np.allclose(im1Arr, im2Arr), 

243 "kernel images %s, %s do not match" % (im1Arr, im2Arr)) 

244 

245 def testComputeBBox(self): 

246 """Test that computeBBox returns same bbox as kernel. 

247 """ 

248 for psf in [self.psfDg, self.psfSg]: 

249 self.assertEqual(psf.computeBBox(), psf.getKernel().getBBox()) 

250 

251 

252class TestMemory(lsst.utils.tests.MemoryTestCase): 

253 pass 

254 

255 

256def setup_module(module): 

257 lsst.utils.tests.init() 

258 

259 

260if __name__ == "__main__": 260 ↛ 261line 260 didn't jump to line 261, because the condition on line 260 was never true

261 lsst.utils.tests.init() 

262 unittest.main()