Coverage for tests/test_methods.py: 7%
229 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-07 02:37 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-07 02:37 -0800
1#
2# LSST Data Management System
3# Copyright 2008, 2009, 2010 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
22import unittest
23import re
25import numpy as np
27import lsst.utils.tests
28import lsst.daf.base as dafBase
29import lsst.geom
30import lsst.afw.geom as afwGeom
31import lsst.afw.image as afwImage
32from lsst.afw.image.testUtils import imagesDiffer
33from lsst.afw.geom.utils import _compareWcsOverBBox
36class TestTestUtils(lsst.utils.tests.TestCase):
37 """Test test methods added to lsst.utils.tests.TestCase
38 """
39 def testAssertWcsAlmostEqualOverBBox(self):
40 """Test assertWcsAlmostEqualOverBBox and wcsAlmostEqualOverBBox"""
41 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
42 lsst.geom.Extent2I(3001, 3001))
43 ctrPix = lsst.geom.Point2I(1500, 1500)
44 metadata = dafBase.PropertySet()
45 metadata.set("RADESYS", "FK5")
46 metadata.set("EQUINOX", 2000.0)
47 metadata.set("CTYPE1", "RA---TAN")
48 metadata.set("CTYPE2", "DEC--TAN")
49 metadata.set("CUNIT1", "deg")
50 metadata.set("CUNIT2", "deg")
51 metadata.set("CRVAL1", 215.5)
52 metadata.set("CRVAL2", 53.0)
53 metadata.set("CRPIX1", ctrPix[0] + 1)
54 metadata.set("CRPIX2", ctrPix[1] + 1)
55 metadata.set("CD1_1", 5.1e-05)
56 metadata.set("CD1_2", 0.0)
57 metadata.set("CD2_2", -5.1e-05)
58 metadata.set("CD2_1", 0.0)
59 wcs0 = lsst.afw.geom.makeSkyWcs(metadata, strip=False)
60 metadata.set("CRVAL2", 53.000001) # tweak CRVAL2 for wcs1
61 wcs1 = lsst.afw.geom.makeSkyWcs(metadata)
63 self.assertWcsAlmostEqualOverBBox(wcs0, wcs0, bbox,
64 maxDiffSky=0*lsst.geom.arcseconds, maxDiffPix=0)
65 self.assertTrue(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs0, bbox,
66 maxDiffSky=0*lsst.geom.arcseconds, maxDiffPix=0))
68 self.assertWcsAlmostEqualOverBBox(wcs0, wcs1, bbox,
69 maxDiffSky=0.04*lsst.geom.arcseconds, maxDiffPix=0.02)
70 self.assertTrue(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs1, bbox,
71 maxDiffSky=0.04*lsst.geom.arcseconds, maxDiffPix=0.02))
73 with self.assertRaises(AssertionError):
74 self.assertWcsAlmostEqualOverBBox(wcs0, wcs1, bbox,
75 maxDiffSky=0.001*lsst.geom.arcseconds, maxDiffPix=0.02)
76 self.assertFalse(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs1, bbox,
77 maxDiffSky=0.001*lsst.geom.arcseconds,
78 maxDiffPix=0.02))
80 with self.assertRaises(AssertionError):
81 self.assertWcsAlmostEqualOverBBox(wcs0, wcs1, bbox,
82 maxDiffSky=0.04*lsst.geom.arcseconds, maxDiffPix=0.001)
83 self.assertFalse(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs1, bbox,
84 maxDiffSky=0.04*lsst.geom.arcseconds,
85 maxDiffPix=0.001))
87 # check that doShortCircuit works in the private implementation
88 errStr1 = _compareWcsOverBBox(wcs0, wcs1, bbox,
89 maxDiffSky=0.001*lsst.geom.arcseconds, maxDiffPix=0.001,
90 doShortCircuit=False)
91 errStr2 = _compareWcsOverBBox(wcs0, wcs1, bbox,
92 maxDiffSky=0.001*lsst.geom.arcseconds, maxDiffPix=0.001,
93 doShortCircuit=True)
94 self.assertNotEqual(errStr1, errStr2)
96 def checkMaskedImage(self, mi):
97 """Run assertImage-like function tests on a masked image
99 Compare the masked image to itself, then alter copies and check that the altered copy
100 is or is not nearly equal the original, depending on the amount of change, rtol and atol
101 """
102 epsilon = 1e-5 # margin to avoid roundoff error
104 mi0 = mi.Factory(mi, True) # deep copy
105 mi1 = mi.Factory(mi, True)
107 # a masked image should be exactly equal to itself
108 self.assertMaskedImagesEqual(mi0, mi1)
109 self.assertMaskedImagesEqual(mi1, mi0)
110 self.assertMaskedImagesAlmostEqual(mi0, mi1, atol=0, rtol=0)
111 self.assertMaskedImagesAlmostEqual(mi0, mi1, atol=0, rtol=0)
112 self.assertMaskedImagesAlmostEqual(
113 mi0.getArrays(), mi1, atol=0, rtol=0)
114 self.assertMaskedImagesAlmostEqual(
115 mi0, mi1.getArrays(), atol=0, rtol=0)
116 self.assertMaskedImagesAlmostEqual(
117 mi0.getArrays(), mi1.getArrays(), atol=0, rtol=0)
118 for getName in ("getImage", "getVariance"):
119 plane0 = getattr(mi0, getName)()
120 plane1 = getattr(mi1, getName)()
121 self.assertImagesEqual(plane0, plane1)
122 self.assertImagesEqual(plane1, plane0)
123 self.assertImagesAlmostEqual(plane0, plane1, atol=0, rtol=0)
124 self.assertImagesAlmostEqual(plane1, plane0, atol=0, rtol=0)
125 self.assertImagesAlmostEqual(
126 plane0.getArray(), plane1, atol=0, rtol=0)
127 self.assertImagesAlmostEqual(
128 plane0, plane1.getArray(), atol=0, rtol=0)
129 self.assertImagesAlmostEqual(
130 plane0.getArray(), plane1.getArray(), atol=0, rtol=0)
131 self.assertMasksEqual(plane0, plane1)
132 self.assertMasksEqual(plane1, plane0)
133 self.assertMasksEqual(plane0.getArray(), plane1)
134 self.assertMasksEqual(plane0, plane1.getArray())
135 self.assertMasksEqual(plane0.getArray(), plane1.getArray())
136 self.assertMasksEqual(mi0.getMask(), mi1.getMask())
137 self.assertMasksEqual(mi1.getMask(), mi0.getMask())
139 # alter image and variance planes and check the results
140 for getName in ("getImage", "getVariance"):
141 isFloat = getattr(mi, getName)().getArray().dtype.kind == "f"
142 if isFloat:
143 for errVal in (np.nan, np.inf, -np.inf):
144 mi0 = mi.Factory(mi, True)
145 mi1 = mi.Factory(mi, True)
146 plane0 = getattr(mi0, getName)()
147 plane1 = getattr(mi1, getName)()
148 plane1[2, 2] = errVal
149 with self.assertRaises(Exception):
150 self.assertImagesAlmostEqual(plane0, plane1)
151 with self.assertRaises(Exception):
152 self.assertImagesAlmostEqual(plane0.getArray(), plane1)
153 with self.assertRaises(Exception):
154 self.assertImagesAlmostEqual(plane1, plane0)
155 with self.assertRaises(Exception):
156 self.assertMaskedImagesAlmostEqual(mi0, mi1)
157 with self.assertRaises(Exception):
158 self.assertMaskedImagesAlmostEqual(
159 mi0, mi1.getArrays())
160 with self.assertRaises(Exception):
161 self.assertMaskedImagesAlmostEqual(mi1, mi0)
163 skipMask = mi.getMask().Factory(mi.getMask(), True)
164 skipMaskArr = skipMask.getArray()
165 skipMaskArr[:] = 0
166 skipMaskArr[2, 2] = 1
167 self.assertImagesAlmostEqual(
168 plane0, plane1, skipMask=skipMaskArr, atol=0, rtol=0)
169 self.assertImagesAlmostEqual(
170 plane0, plane1, skipMask=skipMask, atol=0, rtol=0)
171 self.assertMaskedImagesAlmostEqual(
172 mi0, mi1, skipMask=skipMaskArr, atol=0, rtol=0)
173 self.assertMaskedImagesAlmostEqual(
174 mi0, mi1, skipMask=skipMask, atol=0, rtol=0)
176 for dval in (0.001, 0.03):
177 mi0 = mi.Factory(mi, True)
178 mi1 = mi.Factory(mi, True)
179 plane0 = getattr(mi0, getName)()
180 plane1 = getattr(mi1, getName)()
181 plane1[2, 2] += dval
182 val1 = plane1[2, 2, afwImage.LOCAL]
183 self.assertImagesAlmostEqual(
184 plane0, plane1, rtol=0, atol=dval + epsilon)
185 self.assertImagesAlmostEqual(
186 plane0, plane1, rtol=dval/val1 + epsilon, atol=0)
187 self.assertMaskedImagesAlmostEqual(
188 mi0, mi1, rtol=0, atol=dval + epsilon)
189 self.assertMaskedImagesAlmostEqual(
190 mi1, mi0, rtol=0, atol=dval + epsilon)
191 with self.assertRaises(Exception):
192 self.assertImagesAlmostEqual(
193 plane0, plane1, rtol=0, atol=dval - epsilon)
194 with self.assertRaises(Exception):
195 self.assertImagesAlmostEqual(
196 plane0, plane1, rtol=dval/val1 - epsilon, atol=0)
197 with self.assertRaises(Exception):
198 self.assertMaskedImagesAlmostEqual(
199 mi0, mi1, rtol=0, atol=dval - epsilon)
200 with self.assertRaises(Exception):
201 self.assertMaskedImagesAlmostEqual(
202 mi0, mi1, rtol=dval/val1 - epsilon, atol=0)
203 else:
204 # plane is an integer of some type
205 for dval in (1, 3):
206 mi0 = mi.Factory(mi, True)
207 mi1 = mi.Factory(mi, True)
208 plane0 = getattr(mi0, getName)()
209 plane1 = getattr(mi1, getName)()
210 plane1[2, 2] += dval
211 val1 = plane1[2, 2, afwImage.LOCAL]
212 # int value and test is <= so epsilon not required for atol
213 # but rtol is a fraction, so epsilon is still safest for
214 # the rtol test
215 self.assertImagesAlmostEqual(
216 plane0, plane1, rtol=0, atol=dval)
217 self.assertImagesAlmostEqual(
218 plane0, plane1, rtol=dval/val1 + epsilon, atol=0)
219 with self.assertRaises(Exception):
220 self.assertImagesAlmostEqual(
221 plane0, plane1, rtol=0, atol=dval - epsilon)
222 with self.assertRaises(Exception):
223 self.assertImagesAlmostEqual(
224 plane0, plane1, rtol=dval/val1 - epsilon, atol=0)
226 # alter mask and check the results
227 mi0 = mi.Factory(mi, True)
228 mi1 = mi.Factory(mi, True)
229 mask0 = mi0.getMask()
230 mask1 = mi1.getMask()
231 for dval in (1, 3):
232 # getArray avoids "unsupported operand type" failure
233 mask1.getArray()[2, 2] += 1
234 with self.assertRaises(Exception):
235 self.assertMasksEqual(mask0, mask1)
236 with self.assertRaises(Exception):
237 self.assertMasksEqual(mask1, mask0)
238 with self.assertRaises(Exception):
239 self.assertMaskedImagesEqual(mi0, mi1)
240 with self.assertRaises(Exception):
241 self.assertMaskedImagesEqual(mi1, mi0)
243 skipMask = mi.getMask().Factory(mi.getMask(), True)
244 skipMaskArr = skipMask.getArray()
245 skipMaskArr[:] = 0
246 skipMaskArr[2, 2] = 1
247 self.assertMasksEqual(mask0, mask1, skipMask=skipMaskArr)
248 self.assertMasksEqual(mask0, mask1, skipMask=skipMask)
249 self.assertMaskedImagesAlmostEqual(
250 mi0, mi1, skipMask=skipMaskArr, atol=0, rtol=0)
251 self.assertMaskedImagesAlmostEqual(
252 mi0, mi1, skipMask=skipMask, atol=0, rtol=0)
254 def testAssertImagesAlmostEqual(self):
255 """Test assertImagesAlmostEqual, assertMasksEqual and assertMaskedImagesAlmostEqual
256 """
257 width = 10
258 height = 9
260 for miType in (afwImage.MaskedImageF, afwImage.MaskedImageD, afwImage.MaskedImageI,
261 afwImage.MaskedImageU):
262 mi = makeRampMaskedImageWithNans(width, height, miType)
263 self.checkMaskedImage(mi)
265 for invalidType in (np.zeros([width+1, height]), str, self.assertRaises):
266 mi = makeRampMaskedImageWithNans(width, height, miType)
267 with self.assertRaises(TypeError):
268 self.assertMasksEqual(mi.getMask(), invalidType)
269 with self.assertRaises(TypeError):
270 self.assertMasksEqual(invalidType, mi.getMask())
271 with self.assertRaises(TypeError):
272 self.assertMasksEqual(
273 mi.getMask(), mi.getMask(), skipMask=invalidType)
275 with self.assertRaises(TypeError):
276 self.assertImagesAlmostEqual(mi.getImage(), invalidType)
277 with self.assertRaises(TypeError):
278 self.assertImagesAlmostEqual(invalidType, mi.getImage())
279 with self.assertRaises(TypeError):
280 self.assertImagesAlmostEqual(
281 mi.getImage(), mi.getImage(), skipMask=invalidType)
283 with self.assertRaises(TypeError):
284 self.assertMaskedImagesAlmostEqual(mi, invalidType)
285 with self.assertRaises(TypeError):
286 self.assertMaskedImagesAlmostEqual(invalidType, mi)
287 with self.assertRaises(TypeError):
288 self.assertMaskedImagesAlmostEqual(
289 mi, mi, skipMask=invalidType)
291 with self.assertRaises(TypeError):
292 self.assertMaskedImagesAlmostEqual(
293 mi.getImage(), mi.getImage())
295 def testUnsignedImages(self):
296 """Unsigned images can give incorrect differences unless the test code is careful
297 """
298 image0 = np.zeros([5, 5], dtype=np.uint8)
299 image1 = np.zeros([5, 5], dtype=np.uint8)
300 image0[0, 0] = 1
301 image1[0, 1] = 2
303 # arrays differ by a maximum of 2
304 errMsg1 = imagesDiffer(image0, image1)
305 match = re.match(r"maxDiff *= *(\d+)", errMsg1, re.IGNORECASE)
306 self.assertIsNotNone(match)
307 self.assertEqual(match.group(1), "2")
309 # arrays are equal to within 5
310 self.assertImagesAlmostEqual(image0, image1, atol=5)
313def makeRampMaskedImageWithNans(width, height, imgClass=afwImage.MaskedImageF):
314 """Make a masked image that is a ramp with additional non-finite values
316 Make a masked image with the following additional non-finite values
317 in the variance plane and (if image is of some floating type) image plane:
318 - nan at [0, 0]
319 - inf at [1, 0]
320 - -inf at [0, 1]
321 """
322 mi = makeRampMaskedImage(width, height, imgClass)
324 var = mi.getVariance()
325 var[0, 0] = np.nan
326 var[1, 0] = np.inf
327 var[0, 1] = -np.inf
329 im = mi.getImage()
330 try:
331 np.array([np.nan], dtype=im.getArray().dtype)
332 except Exception:
333 # image plane does not support nan, etc. (presumably an int of some
334 # variety)
335 pass
336 else:
337 # image plane does support nan, etc.
338 im[0, 0] = np.nan
339 im[1, 0] = np.inf
340 im[0, 1] = -np.inf
341 return mi
344def makeRampMaskedImage(width, height, imgClass=afwImage.MaskedImageF):
345 """Make a ramp image of the specified size and image class
347 Image values start from 0 at the lower left corner and increase by 1 along rows
348 Variance values equal image values + 100
349 Mask values equal image values modulo 8 bits (leaving plenty of unused values)
350 """
351 mi = imgClass(width, height)
352 image = mi.getImage()
353 mask = mi.getMask()
354 variance = mi.getVariance()
355 val = 0
356 for yInd in range(height):
357 for xInd in range(width):
358 image[xInd, yInd, afwImage.LOCAL] = val
359 variance[xInd, yInd, afwImage.LOCAL] = val + 100
360 mask[xInd, yInd, afwImage.LOCAL] = val % 0x100
361 val += 1
362 return mi
365class MemoryTester(lsst.utils.tests.MemoryTestCase):
366 pass
369def setup_module(module):
370 lsst.utils.tests.init()
373if __name__ == "__main__": 373 ↛ 374line 373 didn't jump to line 374, because the condition on line 373 was never true
374 lsst.utils.tests.init()
375 unittest.main()