Coverage for tests / test_methods.py: 8%

226 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-26 02:01 -0700

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 

23 

24import numpy as np 

25 

26import lsst.utils.tests 

27import lsst.daf.base as dafBase 

28import lsst.geom 

29import lsst.afw.geom as afwGeom 

30import lsst.afw.image as afwImage 

31from lsst.afw.image.testUtils import imagesDiffer 

32from lsst.afw.geom.utils import _compareWcsOverBBox 

33 

34 

35class TestTestUtils(lsst.utils.tests.TestCase): 

36 """Test test methods added to lsst.utils.tests.TestCase 

37 """ 

38 def testAssertWcsAlmostEqualOverBBox(self): 

39 """Test assertWcsAlmostEqualOverBBox and wcsAlmostEqualOverBBox""" 

40 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), 

41 lsst.geom.Extent2I(3001, 3001)) 

42 ctrPix = lsst.geom.Point2I(1500, 1500) 

43 metadata = dafBase.PropertySet() 

44 metadata.set("RADESYS", "FK5") 

45 metadata.set("EQUINOX", 2000.0) 

46 metadata.set("CTYPE1", "RA---TAN") 

47 metadata.set("CTYPE2", "DEC--TAN") 

48 metadata.set("CUNIT1", "deg") 

49 metadata.set("CUNIT2", "deg") 

50 metadata.set("CRVAL1", 215.5) 

51 metadata.set("CRVAL2", 53.0) 

52 metadata.set("CRPIX1", ctrPix[0] + 1) 

53 metadata.set("CRPIX2", ctrPix[1] + 1) 

54 metadata.set("CD1_1", 5.1e-05) 

55 metadata.set("CD1_2", 0.0) 

56 metadata.set("CD2_2", -5.1e-05) 

57 metadata.set("CD2_1", 0.0) 

58 wcs0 = lsst.afw.geom.makeSkyWcs(metadata, strip=False) 

59 metadata.set("CRVAL2", 53.000001) # tweak CRVAL2 for wcs1 

60 wcs1 = lsst.afw.geom.makeSkyWcs(metadata) 

61 

62 self.assertWcsAlmostEqualOverBBox(wcs0, wcs0, bbox, 

63 maxDiffSky=0*lsst.geom.arcseconds, maxDiffPix=0) 

64 self.assertTrue(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs0, bbox, 

65 maxDiffSky=0*lsst.geom.arcseconds, maxDiffPix=0)) 

66 

67 self.assertWcsAlmostEqualOverBBox(wcs0, wcs1, bbox, 

68 maxDiffSky=0.04*lsst.geom.arcseconds, maxDiffPix=0.02) 

69 self.assertTrue(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs1, bbox, 

70 maxDiffSky=0.04*lsst.geom.arcseconds, maxDiffPix=0.02)) 

71 

72 with self.assertRaises(AssertionError): 

73 self.assertWcsAlmostEqualOverBBox(wcs0, wcs1, bbox, 

74 maxDiffSky=0.001*lsst.geom.arcseconds, maxDiffPix=0.02) 

75 self.assertFalse(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs1, bbox, 

76 maxDiffSky=0.001*lsst.geom.arcseconds, 

77 maxDiffPix=0.02)) 

78 

79 with self.assertRaises(AssertionError): 

80 self.assertWcsAlmostEqualOverBBox(wcs0, wcs1, bbox, 

81 maxDiffSky=0.04*lsst.geom.arcseconds, maxDiffPix=0.001) 

82 self.assertFalse(afwGeom.wcsAlmostEqualOverBBox(wcs0, wcs1, bbox, 

83 maxDiffSky=0.04*lsst.geom.arcseconds, 

84 maxDiffPix=0.001)) 

85 

86 # check that doShortCircuit works in the private implementation 

87 errStr1 = _compareWcsOverBBox(wcs0, wcs1, bbox, 

88 maxDiffSky=0.001*lsst.geom.arcseconds, maxDiffPix=0.001, 

89 doShortCircuit=False) 

90 errStr2 = _compareWcsOverBBox(wcs0, wcs1, bbox, 

91 maxDiffSky=0.001*lsst.geom.arcseconds, maxDiffPix=0.001, 

92 doShortCircuit=True) 

93 self.assertNotEqual(errStr1, errStr2) 

94 

95 def checkMaskedImage(self, mi): 

96 """Run assertImage-like function tests on a masked image 

97 

98 Compare the masked image to itself, then alter copies and check that the altered copy 

99 is or is not nearly equal the original, depending on the amount of change, rtol and atol 

100 """ 

101 epsilon = 1e-5 # margin to avoid roundoff error 

102 

103 mi0 = mi.Factory(mi, True) # deep copy 

104 mi1 = mi.Factory(mi, True) 

105 

106 # a masked image should be exactly equal to itself 

107 self.assertMaskedImagesEqual(mi0, mi1) 

108 self.assertMaskedImagesEqual(mi1, mi0) 

109 self.assertMaskedImagesAlmostEqual(mi0, mi1, atol=0, rtol=0) 

110 self.assertMaskedImagesAlmostEqual(mi0, mi1, atol=0, rtol=0) 

111 self.assertMaskedImagesAlmostEqual( 

112 (mi0.image.array, mi0.mask.array, mi0.variance.array), mi1, atol=0, rtol=0) 

113 self.assertMaskedImagesAlmostEqual( 

114 mi0, (mi1.image.array, mi1.mask.array, mi1.variance.array), atol=0, rtol=0) 

115 self.assertMaskedImagesAlmostEqual( 

116 (mi0.image.array, mi0.mask.array, mi0.variance.array), 

117 (mi1.image.array, mi1.mask.array, mi1.variance.array), 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()) 

138 

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.image.array, mi1.mask.array, mi1.variance.array)) 

160 with self.assertRaises(Exception): 

161 self.assertMaskedImagesAlmostEqual(mi1, mi0) 

162 

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) 

175 

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) 

225 

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) 

242 

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) 

253 

254 def testAssertImagesAlmostEqual(self): 

255 """Test assertImagesAlmostEqual, assertMasksEqual and assertMaskedImagesAlmostEqual 

256 """ 

257 width = 10 

258 height = 9 

259 

260 for miType in (afwImage.MaskedImageF, afwImage.MaskedImageD, afwImage.MaskedImageI, 

261 afwImage.MaskedImageU): 

262 mi = makeRampMaskedImageWithNans(width, height, miType) 

263 self.checkMaskedImage(mi) 

264 

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) 

274 

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) 

282 

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) 

290 

291 with self.assertRaises(TypeError): 

292 self.assertMaskedImagesAlmostEqual( 

293 mi.getImage(), mi.getImage()) 

294 

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 

302 

303 # arrays differ by a maximum of 2 

304 errMsg1 = imagesDiffer(image0, image1) 

305 self.assertIn("maximum absolute error: |0 - 2| = 2 at position (0, 1).", errMsg1) 

306 

307 # arrays are equal to within 5 

308 self.assertImagesAlmostEqual(image0, image1, atol=5) 

309 

310 

311def makeRampMaskedImageWithNans(width, height, imgClass=afwImage.MaskedImageF): 

312 """Make a masked image that is a ramp with additional non-finite values 

313 

314 Make a masked image with the following additional non-finite values 

315 in the variance plane and (if image is of some floating type) image plane: 

316 - nan at [0, 0] 

317 - inf at [1, 0] 

318 - -inf at [0, 1] 

319 """ 

320 mi = makeRampMaskedImage(width, height, imgClass) 

321 

322 var = mi.getVariance() 

323 var[0, 0] = np.nan 

324 var[1, 0] = np.inf 

325 var[0, 1] = -np.inf 

326 

327 im = mi.getImage() 

328 try: 

329 np.array([np.nan], dtype=im.getArray().dtype) 

330 except Exception: 

331 # image plane does not support nan, etc. (presumably an int of some 

332 # variety) 

333 pass 

334 else: 

335 # image plane does support nan, etc. 

336 im[0, 0] = np.nan 

337 im[1, 0] = np.inf 

338 im[0, 1] = -np.inf 

339 return mi 

340 

341 

342def makeRampMaskedImage(width, height, imgClass=afwImage.MaskedImageF): 

343 """Make a ramp image of the specified size and image class 

344 

345 Image values start from 0 at the lower left corner and increase by 1 along rows 

346 Variance values equal image values + 100 

347 Mask values equal image values modulo 8 bits (leaving plenty of unused values) 

348 """ 

349 mi = imgClass(width, height) 

350 image = mi.getImage() 

351 mask = mi.getMask() 

352 variance = mi.getVariance() 

353 val = 0 

354 for yInd in range(height): 

355 for xInd in range(width): 

356 image[xInd, yInd, afwImage.LOCAL] = val 

357 variance[xInd, yInd, afwImage.LOCAL] = val + 100 

358 mask[xInd, yInd, afwImage.LOCAL] = val % 0x100 

359 val += 1 

360 return mi 

361 

362 

363class MemoryTester(lsst.utils.tests.MemoryTestCase): 

364 pass 

365 

366 

367def setup_module(module): 

368 lsst.utils.tests.init() 

369 

370 

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

372 lsst.utils.tests.init() 

373 unittest.main()