Coverage for tests/test_gaussianPsf.py: 17%
149 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-14 09:15 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-14 09:15 +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/>.
22import math
23import unittest
24import numpy as np
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
33try:
34 display
35except NameError:
36 display = False
37else:
38 import lsst.afw.display as afwDisplay
39 afwDisplay.setDefaultMaskTransparency(75)
42class GaussianPsfTestCase(lsst.utils.tests.TestCase):
43 """Test SingleGaussianPsf and DoubleGaussianPsf.
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)
55 def tearDown(self):
56 del self.psfDg
57 del self.psfSg
59 def testComputeImage(self):
60 """Test the computation of the PSF's image at a point."""
62 for psf in [self.psfDg, self.psfSg]:
63 ccdXY = lsst.geom.Point2D(0, 0)
64 kIm = psf.computeImage(ccdXY)
66 if False:
67 afwDisplay.Display(frame=1).mtv(kIm, title=self._testMethodName + ": kIm")
69 self.assertEqual(kIm.getWidth(), self.ksize)
70 kIm = psf.computeImage(ccdXY)
71 self.assertAlmostEqual(afwMath.makeStatistics(kIm, afwMath.SUM).getValue(), 1.0)
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)
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)
89 self.assertEqual(kIm.getWidth(), self.ksize)
90 self.assertAlmostEqual(afwMath.makeStatistics(kIm, afwMath.SUM).getValue(), 1.0)
92 if False:
93 afwDisplay.Display(frame=2).mtv(kIm, title=self._testMethodName + ": kIm")
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)
101 def badSigma1():
102 sigma1 = 0
103 measAlg.DoubleGaussianPsf(self.ksize, self.ksize, sigma1, sigma2, b)
105 with self.assertRaises(pexExceptions.DomainError):
106 badSigma1()
108 def badSigma2():
109 sigma2, b = 0, 1
110 measAlg.DoubleGaussianPsf(self.ksize, self.ksize, sigma1, sigma2, b)
112 with self.assertRaises(pexExceptions.DomainError):
113 badSigma2()
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)
121 def badSigma1():
122 sigma = 0
123 measAlg.SingleGaussianPsf(self.ksize, self.ksize, sigma)
125 with self.assertRaises(pexExceptions.DomainError):
126 badSigma1()
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
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
144 im = psf.computeImage(lsst.geom.Point2D(x, y)).convertF()
146 stamps.append(im.Factory(im, True))
147 trueCenters.append([xcen + fx, ycen + fy])
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")
154 for i in range(len(trueCenters)):
155 bbox = mos.getBBox(i)
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])
162 disp.dot("%.2f, %.2f" % (trueCenters[i][0], trueCenters[i][1]),
163 bbox.getMinX() + xcen, bbox.getMinY() + 2)
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)))
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)
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")
205 def testResize(self):
206 """Test that resized Single and Double Gaussian PSFs have
207 same model parameters, but new kernel dimensions.
208 """
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)
218 self.assertEqual(psfResized.getKernel().getWidth(), lengthNew)
219 self.assertEqual(psfResized.getKernel().getHeight(), lengthNew)
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)
226 self.assertEqual(psfResized.getKernel().getWidth(), lengthNew)
227 self.assertEqual(psfResized.getKernel().getHeight(), lengthNew)
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))
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())
252class TestMemory(lsst.utils.tests.MemoryTestCase):
253 pass
256def setup_module(module):
257 lsst.utils.tests.init()
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()