Coverage for tests/test_gaussianPsf.py: 17%

149 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-23 11:30 +0000

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( 

195 resizedKPsf.computeBBox(resizedKPsf.getAveragePosition()).getDimensions(), 

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

197 ) 

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

199 kPsf.getKernel().getKernelParameters()) 

200 self._compareKernelImages(kPsf, resizedKPsf) 

201 if display: 

202 mos = afwDisplay.utils.Mosaic() 

203 mos.setBackground(-0.1) 

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

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

206 

207 def testResize(self): 

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

209 same model parameters, but new kernel dimensions. 

210 """ 

211 

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

213 # Test Double Gaussian 

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

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

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

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

218 self._compareKernelImages(psfResized, self.psfDg) 

219 

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

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

222 

223 # Test Single Gaussian Parameters 

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

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

226 self._compareKernelImages(psfResized, self.psfSg) 

227 

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

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

230 

231 def _compareKernelImages(self, psf1, psf2): 

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

233 """ 

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

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

236 bboxIntersection = im1.getBBox() 

237 bboxIntersection.clip(im2.getBBox()) 

238 im1Intersection = afwImage.ImageD(im1, bboxIntersection) 

239 im2Intersection = afwImage.ImageD(im2, bboxIntersection) 

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

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

242 im1Arr = scale1*im1Intersection.getArray() 

243 im2Arr = scale2*im2Intersection.getArray() 

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

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

246 

247 def testComputeBBox(self): 

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

249 """ 

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

251 self.assertEqual( 

252 psf.computeBBox(psf.getAveragePosition()), 

253 psf.getKernel().getBBox() 

254 ) 

255 

256 

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

258 pass 

259 

260 

261def setup_module(module): 

262 lsst.utils.tests.init() 

263 

264 

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

266 lsst.utils.tests.init() 

267 unittest.main()