Coverage for tests/test_image.py: 13%

498 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-31 02:57 -0700

1# This file is part of afw. 

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 

22""" 

23Tests for Images 

24 

25Run with: 

26 python test_image.py 

27or 

28 pytest test_image.py 

29""" 

30 

31import itertools 

32import os.path 

33import shutil 

34import tempfile 

35import unittest 

36 

37import numpy as np 

38 

39import lsst.utils 

40import lsst.utils.tests 

41import lsst.pex.exceptions 

42import lsst.daf.base 

43import lsst.geom 

44import lsst.afw.image as afwImage 

45import lsst.afw.math as afwMath 

46from lsst.afw.fits import readMetadata 

47import lsst.afw.display as afwDisplay 

48 

49try: 

50 afwdataDir = lsst.utils.getPackageDir("afwdata") 

51except LookupError: 

52 afwdataDir = None 

53 

54try: 

55 type(display) 

56except NameError: 

57 display = False 

58 

59 

60def makeRampImage(width, height, imgClass=afwImage.ImageF): 

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

62 

63 Values start from 0 at the lower left corner and increase by 1 along rows 

64 """ 

65 im = imgClass(width, height) 

66 val = 0 

67 for yInd in range(height): 

68 for xInd in range(width): 

69 im[xInd, yInd] = val 

70 val += 1 

71 return im 

72 

73 

74class ImageTestCase(lsst.utils.tests.TestCase): 

75 """A test case for Image""" 

76 

77 def setUp(self): 

78 np.random.seed(1) 

79 self.val1, self.val2 = 10, 100 

80 self.image1 = afwImage.ImageF(lsst.geom.ExtentI(100, 200)) 

81 self.image1.set(self.val1) 

82 self.image2 = afwImage.ImageF(self.image1.getDimensions()) 

83 self.image2.set(self.val2) 

84 self.function = afwMath.PolynomialFunction2D(2) 

85 self.function.setParameters( 

86 list(range(self.function.getNParameters()))) 

87 

88 def tearDown(self): 

89 del self.image1 

90 del self.image2 

91 del self.function 

92 

93 def testArrays(self): 

94 for cls in (afwImage.ImageU, afwImage.ImageI, afwImage.ImageF, afwImage.ImageD): 

95 image1 = cls(lsst.geom.Extent2I(5, 6)) 

96 array1 = image1.getArray() 

97 self.assertEqual(array1.shape[0], image1.getHeight()) 

98 self.assertEqual(array1.shape[1], image1.getWidth()) 

99 image2 = cls(array1, False) 

100 self.assertEqual(array1.shape[0], image2.getHeight()) 

101 self.assertEqual(array1.shape[1], image2.getWidth()) 

102 image3 = afwImage.makeImageFromArray(array1) 

103 self.assertEqual(array1.shape[0], image2.getHeight()) 

104 self.assertEqual(array1.shape[1], image2.getWidth()) 

105 self.assertEqual(type(image3), cls) 

106 array2 = image1.array 

107 np.testing.assert_array_equal(array1, array2) 

108 array1[:, :] = np.random.uniform(low=0, high=10, size=array1.shape) 

109 for j in range(image1.getHeight()): 

110 for i in range(image1.getWidth()): 

111 self.assertEqual(image1[i, j, afwImage.LOCAL], array1[j, i]) 

112 self.assertEqual(image2[i, j, afwImage.LOCAL], array1[j, i]) 

113 array3 = np.random.uniform(low=0, high=10, 

114 size=array1.shape).astype(array1.dtype) 

115 image1.array[:] = array3 

116 np.testing.assert_array_equal(array1, array3) 

117 image1.array[2:4, 3:] = 10 

118 np.testing.assert_array_equal(array1[2:4, 3:], 10) 

119 array4 = image1.array.copy() 

120 array4 += 5 

121 image1.array += 5 

122 np.testing.assert_array_equal(image1.array, array4) 

123 

124 def testImagesOverlap(self): 

125 dim = lsst.geom.Extent2I(10, 8) 

126 # a set of bounding boxes, some of which overlap each other 

127 # and some of which do not, and include the full image bounding box 

128 bboxes = ( 

129 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dim), 

130 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(3, 3)), 

131 lsst.geom.Box2I(lsst.geom.Point2I(2, 2), lsst.geom.Extent2I(6, 4)), 

132 lsst.geom.Box2I(lsst.geom.Point2I(4, 4), lsst.geom.Extent2I(6, 4)), 

133 ) 

134 

135 imageClasses = (afwImage.ImageF, afwImage.ImageD, afwImage.ImageI, afwImage.Mask) 

136 

137 for ImageClass1, ImageClass2 in itertools.product(imageClasses, imageClasses): 

138 with self.subTest(ImageClass1=str(ImageClass1), ImageClass2=str(ImageClass2)): 

139 image1 = ImageClass1(dim) 

140 self.assertTrue(afwImage.imagesOverlap(image1, image1)) 

141 

142 image2 = ImageClass2(dim) 

143 self.assertFalse(afwImage.imagesOverlap(image1, image2)) 

144 self.assertFalse(afwImage.imagesOverlap(image2, image1)) 

145 

146 for bboxa, bboxb in itertools.product(bboxes, bboxes): 

147 shouldOverlap = bboxa.overlaps(bboxb) 

148 with self.subTest(bboxa=bboxa, bboxb=bboxb): 

149 subim1a = ImageClass1(image1, bboxa) 

150 subim1b = ImageClass1(image1, bboxb) 

151 self.assertEqual(afwImage.imagesOverlap(subim1a, subim1b), shouldOverlap) 

152 self.assertEqual(afwImage.imagesOverlap(subim1b, subim1a), shouldOverlap) 

153 

154 subim2b = ImageClass2(image2, bboxb) 

155 self.assertFalse(afwImage.imagesOverlap(subim1a, subim2b)) 

156 self.assertFalse(afwImage.imagesOverlap(subim2b, subim1a)) 

157 

158 def testInitializeImages(self): 

159 val = 666 

160 for ctor in (afwImage.ImageU, afwImage.ImageI, afwImage.ImageF, afwImage.ImageD): 

161 im = ctor(10, 10, val) 

162 self.assertEqual(im[0, 0], val) 

163 

164 im2 = ctor(lsst.geom.Box2I(lsst.geom.Point2I(0, 0), 

165 lsst.geom.Extent2I(10, 10)), val) 

166 self.assertEqual(im2[0, 0], val) 

167 

168 def testSetGetImages(self): 

169 self.image1.setXY0(3, 4) 

170 self.assertEqual(self.image1[0, 0, afwImage.LOCAL], self.val1) 

171 self.assertEqual(self.image1[3, 4], self.val1) 

172 self.image1[0, 0, afwImage.LOCAL] = 42. 

173 self.assertEqual(self.image1[0, 0, afwImage.LOCAL], 42.) 

174 self.assertEqual(self.image1[3, 4], 42.) 

175 self.image1[3, 4] = self.val1 

176 self.assertEqual(self.image1[0, 0, afwImage.LOCAL], self.val1) 

177 self.assertEqual(self.image1[3, 4], self.val1) 

178 

179 def testAllocateLargeImages(self): 

180 """Try to allocate a Very large image""" 

181 bbox = lsst.geom.BoxI(lsst.geom.PointI(-1 << 30, -1 << 30), 

182 lsst.geom.PointI(1 << 30, 1 << 30)) 

183 

184 def tst(): 

185 afwImage.ImageF(bbox) 

186 

187 self.assertRaises(lsst.pex.exceptions.LengthError, tst) 

188 

189 def testAddImages(self): 

190 self.image2 += self.image1 

191 self.image1 += self.val1 

192 

193 self.assertEqual(self.image1[0, 0], 2*self.val1) 

194 self.assertEqual(self.image2[0, 0], self.val1 + self.val2) 

195 

196 self.image1.set(self.val1) 

197 self.image1 += self.function 

198 

199 for j in range(self.image1.getHeight()): 

200 for i in range(self.image1.getWidth()): 

201 self.assertEqual(self.image1[i, j], 

202 self.val1 + self.function(i, j)) 

203 

204 def testAssignWithBBox(self): 

205 """Test assign(rhs, bbox) with non-empty bbox 

206 """ 

207 for xy0 in (lsst.geom.Point2I(*val) for val in ( 

208 (0, 0), 

209 (-100, 120), # an arbitrary value that is off the image 

210 )): 

211 destImDim = lsst.geom.Extent2I(5, 4) 

212 srcImDim = lsst.geom.Extent2I(3, 2) 

213 destIm = afwImage.ImageF(destImDim) 

214 destIm.setXY0(xy0) 

215 srcIm = makeRampImage(*srcImDim) 

216 srcIm.setXY0(55, -33) # an arbitrary value that should be ignored 

217 self.assertRaises(Exception, destIm.set, srcIm) # size mismatch 

218 

219 for validMin in (lsst.geom.Point2I(*val) for val in ( 

220 (0, 0), 

221 (2, 0), 

222 (0, 1), 

223 (1, 2), 

224 )): 

225 # None to omit the argument 

226 for origin in (None, afwImage.PARENT, afwImage.LOCAL): 

227 destIm[:] = -1.0 

228 bbox = lsst.geom.Box2I(validMin, srcIm.getDimensions()) 

229 if origin != afwImage.LOCAL: 

230 bbox.shift(lsst.geom.Extent2I(xy0)) 

231 if origin is None: 

232 destIm.assign(srcIm, bbox) 

233 destImView = afwImage.ImageF(destIm, bbox) 

234 else: 

235 destIm.assign(srcIm, bbox, origin) 

236 destImView = afwImage.ImageF(destIm, bbox, origin) 

237 self.assertFloatsEqual( 

238 destImView.getArray(), srcIm.getArray()) 

239 numPixNotAssigned = (destImDim[0] * destImDim[1]) - \ 

240 (srcImDim[0] * srcImDim[1]) 

241 self.assertEqual( 

242 np.sum(destIm.getArray() < -0.5), numPixNotAssigned) 

243 

244 for badMin in (lsst.geom.Point2I(*val) + lsst.geom.Extent2I(xy0) for val in ( 

245 (-1, 0), 

246 (3, 0), 

247 (0, -1), 

248 (1, 3), 

249 )): 

250 # None to omit the argument 

251 for origin in (None, afwImage.PARENT, afwImage.LOCAL): 

252 bbox = lsst.geom.Box2I(badMin, srcIm.getDimensions()) 

253 if origin != afwImage.LOCAL: 

254 bbox.shift(lsst.geom.Extent2I(xy0)) 

255 if origin is None: 

256 self.assertRaises(Exception, destIm.set, srcIm, bbox) 

257 else: 

258 self.assertRaises( 

259 Exception, destIm.set, srcIm, bbox, origin) 

260 

261 def testAssignWithoutBBox(self): 

262 """Test assign(rhs, [bbox]) with an empty bbox and with no bbox specified; both set all pixels 

263 """ 

264 for xy0 in (lsst.geom.Point2I(*val) for val in ( 

265 (0, 0), 

266 (-100, 120), # an arbitrary value that is off the image 

267 )): 

268 destImDim = lsst.geom.Extent2I(5, 4) 

269 destIm = afwImage.ImageF(destImDim) 

270 destIm.setXY0(xy0) 

271 srcIm = makeRampImage(*destImDim) 

272 srcIm.setXY0(55, -33) # an arbitrary value that should be ignored 

273 

274 destIm[:] = -1.0 

275 destIm.assign(srcIm) 

276 self.assertFloatsEqual(destIm.getArray(), srcIm.getArray()) 

277 

278 destIm[:] = -1.0 

279 destIm.assign(srcIm, lsst.geom.Box2I()) 

280 self.assertFloatsEqual(destIm.getArray(), srcIm.getArray()) 

281 

282 def testAddScaledImages(self): 

283 c = 10.0 

284 self.image1.scaledPlus(c, self.image2) 

285 

286 self.assertEqual(self.image1[0, 0], self.val1 + c*self.val2) 

287 

288 def testSubtractImages(self): 

289 self.image2 -= self.image1 

290 self.image1 -= self.val1 

291 

292 self.assertEqual(self.image1[0, 0], 0) 

293 self.assertEqual(self.image2[0, 0], self.val2 - self.val1) 

294 

295 self.image1.set(self.val1) 

296 self.image1 -= self.function 

297 

298 for j in range(self.image1.getHeight()): 

299 for i in range(self.image1.getWidth()): 

300 self.assertEqual(self.image1[i, j], 

301 self.val1 - self.function(i, j)) 

302 

303 def testArithmeticImagesMismatch(self): 

304 "Test arithmetic operations on Images of different sizes" 

305 i1 = afwImage.ImageF(100, 100) 

306 i1.set(100) 

307 i2 = afwImage.ImageF(10, 10) 

308 i2.set(10) 

309 

310 def tst1(i1, i2): 

311 i1 -= i2 

312 

313 def tst2(i1, i2): 

314 i1.scaledMinus(1.0, i2) 

315 

316 def tst3(i1, i2): 

317 i1 += i2 

318 

319 def tst4(i1, i2): 

320 i1.scaledPlus(1.0, i2) 

321 

322 def tst5(i1, i2): 

323 i1 *= i2 

324 

325 def tst6(i1, i2): 

326 i1.scaledMultiplies(1.0, i2) 

327 

328 def tst7(i1, i2): 

329 i1 /= i2 

330 

331 def tst8(i1, i2): 

332 i1.scaledDivides(1.0, i2) 

333 

334 tsts12 = [tst1, tst3, tst5, tst7] 

335 for tst in tsts12: 

336 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i1, i2) 

337 

338 tsts21 = [tst2, tst4, tst6, tst8] 

339 for tst in tsts21: 

340 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i2, i1) 

341 

342 def testSubtractScaledImages(self): 

343 c = 10.0 

344 self.image1.scaledMinus(c, self.image2) 

345 

346 self.assertEqual(self.image1[0, 0], self.val1 - c*self.val2) 

347 

348 def testMultiplyImages(self): 

349 self.image2 *= self.image1 

350 self.image1 *= self.val1 

351 

352 self.assertEqual(self.image1[0, 0], self.val1*self.val1) 

353 self.assertEqual(self.image2[0, 0], self.val2*self.val1) 

354 

355 def testMultiplesScaledImages(self): 

356 c = 10.0 

357 self.image1.scaledMultiplies(c, self.image2) 

358 

359 self.assertEqual(self.image1[0, 0], self.val1 * c*self.val2) 

360 

361 def testDivideImages(self): 

362 self.image2 /= self.image1 

363 self.image1 /= self.val1 

364 

365 self.assertEqual(self.image1[0, 0], 1) 

366 self.assertEqual(self.image2[0, 0], self.val2/self.val1) 

367 

368 def testDividesScaledImages(self): 

369 c = 10.0 

370 self.image1.scaledDivides(c, self.image2) 

371 

372 self.assertAlmostEqual(self.image1[0, 0], self.val1/(c*self.val2)) 

373 

374 def testCopyConstructors(self): 

375 dimage = afwImage.ImageF(self.image1, True) # deep copy 

376 simage = afwImage.ImageF(self.image1) # shallow copy 

377 

378 self.image1 += 2 # should only change dimage 

379 self.assertEqual(dimage[0, 0], self.val1) 

380 self.assertEqual(simage[0, 0], self.val1 + 2) 

381 

382 def testGeneralisedCopyConstructors(self): 

383 # these are generalised (templated) copy constructors in C++ 

384 imageU = self.image1.convertU() 

385 imageF = imageU.convertF() 

386 imageD = imageF.convertD() 

387 

388 self.assertEqual(imageU[0, 0], self.val1) 

389 self.assertEqual(imageF[0, 0], self.val1) 

390 self.assertEqual(imageD[0, 0], self.val1) 

391 

392 def checkImgPatch(self, img, x0=0, y0=0): 

393 """Check that a patch of an image is correct; origin of patch is at (x0, y0)""" 

394 

395 self.assertEqual(img[x0 - 1, y0 - 1, afwImage.LOCAL], self.val1) 

396 self.assertEqual(img[x0, y0, afwImage.LOCAL], 666) 

397 self.assertEqual(img[x0 + 3, y0, afwImage.LOCAL], self.val1) 

398 self.assertEqual(img[x0, y0 + 1, afwImage.LOCAL], 666) 

399 self.assertEqual(img[x0 + 3, y0 + 1, afwImage.LOCAL], self.val1) 

400 self.assertEqual(img[x0, y0 + 2, afwImage.LOCAL], self.val1) 

401 

402 def testOrigin(self): 

403 """Check that we can set and read the origin""" 

404 

405 im = afwImage.ImageF(10, 20) 

406 x0 = y0 = 0 

407 

408 self.assertEqual(im.getX0(), x0) 

409 self.assertEqual(im.getY0(), y0) 

410 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0)) 

411 

412 x0, y0 = 3, 5 

413 im.setXY0(x0, y0) 

414 self.assertEqual(im.getX0(), x0) 

415 self.assertEqual(im.getY0(), y0) 

416 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0)) 

417 

418 x0, y0 = 30, 50 

419 im.setXY0(lsst.geom.Point2I(x0, y0)) 

420 self.assertEqual(im.getX0(), x0) 

421 self.assertEqual(im.getY0(), y0) 

422 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0)) 

423 

424 def testSubimages(self): 

425 simage1 = afwImage.ImageF( 

426 self.image1, 

427 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)), 

428 afwImage.LOCAL) 

429 

430 simage = afwImage.ImageF( 

431 simage1, 

432 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(3, 2)), 

433 afwImage.LOCAL 

434 ) 

435 self.assertEqual(simage.getX0(), 2) 

436 self.assertEqual(simage.getY0(), 2) # i.e. wrt self.image1 

437 

438 image2 = afwImage.ImageF(simage.getDimensions()) 

439 image2.set(666) 

440 simage[:] = image2 

441 del simage 

442 del image2 

443 

444 self.checkImgPatch(self.image1, 2, 2) 

445 self.checkImgPatch(simage1, 1, 1) 

446 

447 def testSubimages2(self): 

448 """Test subimages when we've played with the (x0, y0) value""" 

449 

450 self.image1[9, 4] = 888 

451 

452 simage1 = afwImage.ImageF( 

453 self.image1, 

454 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)), 

455 afwImage.LOCAL 

456 ) 

457 # reset origin; doesn't affect pixel coordinate systems 

458 simage1.setXY0(lsst.geom.Point2I(0, 0)) 

459 

460 simage = afwImage.ImageF( 

461 simage1, 

462 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(3, 2)), 

463 afwImage.LOCAL 

464 ) 

465 self.assertEqual(simage.getX0(), 1) 

466 self.assertEqual(simage.getY0(), 1) 

467 

468 image2 = afwImage.ImageF(simage.getDimensions()) 

469 image2.set(666) 

470 simage[:] = image2 

471 del simage 

472 del image2 

473 

474 self.checkImgPatch(self.image1, 2, 2) 

475 self.checkImgPatch(simage1, 1, 1) 

476 

477 def testBadSubimages(self): 

478 def tst(): 

479 afwImage.ImageF( 

480 self.image1, 

481 lsst.geom.Box2I(lsst.geom.Point2I(1, -1), lsst.geom.Extent2I(10, 5)), 

482 afwImage.LOCAL 

483 ) 

484 

485 self.assertRaises(lsst.pex.exceptions.LengthError, tst) 

486 

487 def testImageInitialisation(self): 

488 dims = self.image1.getDimensions() 

489 factory = self.image1.Factory 

490 

491 self.image1.set(666) 

492 

493 del self.image1 # tempt C++ to reuse the memory 

494 self.image1 = factory(dims) 

495 self.assertEqual(self.image1[10, 10], 0) 

496 

497 def testImageSlicesOrigin(self): 

498 """Test image slicing, which generate sub-images using Box2I under the covers""" 

499 im = afwImage.ImageF(10, 20) 

500 im.setXY0(50, 100) 

501 im[54, 110] = 10 

502 im[-3:, -2:, afwImage.LOCAL] = 100 

503 im[-2, -2, afwImage.LOCAL] = -10 

504 sim = im[51:54, 106:110] 

505 sim[:] = -1 

506 im[50:54, 100:104] = im[2:6, 8:12, afwImage.LOCAL] 

507 

508 if display: 

509 afwDisplay.Display(frame=1).mtv(im, title="testImageSlicesOrigin") 

510 

511 self.assertEqual(im[0, 6, afwImage.LOCAL], 0) 

512 self.assertEqual(im[6, 17, afwImage.LOCAL], 0) 

513 self.assertEqual(im[7, 18, afwImage.LOCAL], 100) 

514 self.assertEqual(im[9, 19, afwImage.LOCAL], 100) 

515 self.assertEqual(im[8, 18, afwImage.LOCAL], -10) 

516 self.assertEqual(im[1, 6, afwImage.LOCAL], -1) 

517 self.assertEqual(im[3, 9, afwImage.LOCAL], -1) 

518 self.assertEqual(im[4, 10, afwImage.LOCAL], 10) 

519 self.assertEqual(im[4, 9, afwImage.LOCAL], 0) 

520 self.assertEqual(im[2, 2, afwImage.LOCAL], 10) 

521 self.assertEqual(im[0, 0, afwImage.LOCAL], -1) 

522 

523 def testImageSliceFromBox(self): 

524 """Test using a Box2I to index an Image""" 

525 im = afwImage.ImageF(10, 20) 

526 bbox = lsst.geom.BoxI(lsst.geom.PointI(1, 3), lsst.geom.PointI(6, 9)) 

527 im[bbox] = -1 

528 

529 if display: 

530 afwDisplay.Display(frame=0).mtv(im, title="testImageSliceFromBox") 

531 

532 self.assertEqual(im[0, 6], 0) 

533 self.assertEqual(im[1, 6], -1) 

534 self.assertEqual(im[3, 9], -1) 

535 

536 def testImageSliceFromBoxOrigin(self): 

537 """Test using a Box2I to index an Image""" 

538 im = afwImage.ImageF(10, 20) 

539 im.setXY0(50, 100) 

540 bbox = lsst.geom.BoxI(lsst.geom.PointI(51, 103), lsst.geom.ExtentI(6, 7)) 

541 im[bbox] = -1 

542 

543 if display: 

544 afwDisplay.Display(frame=2).mtv(im, title="testImageSliceFromBoxOrigin") 

545 

546 self.assertEqual(im[0, 6, afwImage.LOCAL], 0) 

547 self.assertEqual(im[1, 6, afwImage.LOCAL], -1) 

548 self.assertEqual(im[3, 9, afwImage.LOCAL], -1) 

549 

550 def testClone(self): 

551 """Test that clone works properly""" 

552 im = afwImage.ImageF(10, 20) 

553 im[0, 0] = 100 

554 

555 im2 = im.clone() # check that clone with no arguments makes a deep copy 

556 self.assertEqual(im.getDimensions(), im2.getDimensions()) 

557 self.assertEqual(im[0, 0], im2[0, 0]) 

558 im2[0, 0] += 100 

559 self.assertNotEqual(im[0, 0], im2[0, 0]) # so it's a deep copy 

560 

561 im2 = im[0:3, 0:5].clone() # check that we can slice-then-clone 

562 self.assertEqual(im2.getDimensions(), lsst.geom.ExtentI(3, 5)) 

563 self.assertEqual(im[0, 0], im2[0, 0]) 

564 im2[0, 0] += 10 

565 self.assertNotEqual(float(im[0, 0]), float(im2[0, 0])) 

566 

567 def testString(self): 

568 imageF = afwImage.ImageF(100, 100) 

569 imageDSmall = afwImage.ImageD(2, 2) 

570 imageISmall = afwImage.ImageI(2, 2) 

571 imageU = afwImage.ImageU(100, 100) 

572 

573 # NumPy's string representation varies depending on the size of the 

574 # array; we test both large and small. 

575 self.assertIn(str(np.zeros((100, 100), dtype=imageF.dtype)), str(imageF)) 

576 self.assertIn(f"bbox={imageF.getBBox()}", str(imageF)) 

577 

578 self.assertIn(str(np.zeros((2, 2), dtype=imageDSmall.dtype)), str(imageDSmall)) 

579 self.assertIn(str(np.zeros((2, 2), dtype=imageISmall.dtype)), str(imageISmall)) 

580 

581 self.assertIn("ImageF=", repr(imageF)) 

582 self.assertIn("ImageU=", repr(imageU)) 

583 

584 

585class DecoratedImageTestCase(lsst.utils.tests.TestCase): 

586 """A test case for DecoratedImage""" 

587 

588 def setUp(self): 

589 np.random.seed(1) 

590 self.val1, self.val2 = 10, 100 

591 self.width, self.height = 200, 100 

592 self.dimage1 = afwImage.DecoratedImageF( 

593 lsst.geom.Extent2I(self.width, self.height) 

594 ) 

595 self.dimage1.image.set(self.val1) 

596 

597 if afwdataDir is not None: 

598 self.fileForMetadata = os.path.join( 

599 afwdataDir, "data", "small_MI.fits") 

600 self.trueMetadata = {"RELHUMID": 10.69} 

601 

602 def tearDown(self): 

603 del self.dimage1 

604 

605 def testCreateDecoratedImage(self): 

606 self.assertEqual(self.dimage1.getWidth(), self.width) 

607 self.assertEqual(self.dimage1.getHeight(), self.height) 

608 self.assertEqual(self.dimage1.image[0, 0], self.val1) 

609 

610 def testCreateDecoratedImageFromImage(self): 

611 image = afwImage.ImageF(lsst.geom.Extent2I(self.width, self.height)) 

612 image[:] = self.dimage1.image 

613 

614 dimage = afwImage.DecoratedImageF(image) 

615 self.assertEqual(dimage.getWidth(), self.width) 

616 self.assertEqual(dimage.getHeight(), self.height) 

617 self.assertEqual(dimage.image[0, 0], self.val1) 

618 

619 def testCopyConstructors(self): 

620 dimage = afwImage.DecoratedImageF(self.dimage1, True) # deep copy 

621 self.dimage1.image[0, 0] = 1 + 2*self.val1 

622 self.assertEqual(dimage.image[0, 0], self.val1) 

623 

624 dimage = afwImage.DecoratedImageF(self.dimage1) # shallow copy 

625 self.dimage1.image[0, 0] = 1 + 2*self.val1 

626 self.assertNotEqual(dimage.image[0, 0], self.val1) 

627 

628 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

629 def testReadFits(self): 

630 """Test reading FITS files""" 

631 

632 hdus = {} 

633 hdus["img"] = 1 # an S16 fits HDU 

634 hdus["msk"] = 2 # an U8 fits HDU 

635 hdus["var"] = 3 # an F32 fits HDU 

636 

637 # read as unsigned short 

638 imgU = afwImage.DecoratedImageU(self.fileForMetadata, hdus["img"], allowUnsafe=True) 

639 # read as float 

640 imgF = afwImage.DecoratedImageF(self.fileForMetadata, hdus["img"]) 

641 

642 self.assertEqual(imgU.getHeight(), 256) 

643 self.assertEqual(imgF.image.getWidth(), 256) 

644 self.assertEqual(imgU.image[0, 0, afwImage.LOCAL], imgF.image[0, 0, afwImage.LOCAL]) 

645 # 

646 # Check the metadata 

647 # 

648 meta = self.trueMetadata 

649 for k in meta.keys(): 

650 self.assertEqual(imgU.getMetadata().getAsDouble(k), meta[k]) 

651 self.assertEqual(imgF.getMetadata().getAsDouble(k), meta[k]) 

652 # 

653 # Read an F32 image 

654 # 

655 # read as unsigned short 

656 varU = afwImage.DecoratedImageF(self.fileForMetadata, hdus["var"]) 

657 # read as float 

658 varF = afwImage.DecoratedImageF(self.fileForMetadata, hdus["var"]) 

659 

660 self.assertEqual(varU.getHeight(), 256) 

661 self.assertEqual(varF.image.getWidth(), 256) 

662 self.assertEqual(varU.image[0, 0, afwImage.LOCAL], varF.image[0, 0, afwImage.LOCAL]) 

663 # 

664 # Read a char image 

665 # 

666 maskImg = afwImage.DecoratedImageU( 

667 self.fileForMetadata, hdus["msk"]).image # read a char file 

668 

669 self.assertEqual(maskImg.getHeight(), 256) 

670 self.assertEqual(maskImg.getWidth(), 256) 

671 self.assertEqual(maskImg[0, 0, afwImage.LOCAL], 1) 

672 # 

673 # Read a U16 image 

674 # 

675 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

676 imgU.writeFits(tmpFile) 

677 

678 afwImage.DecoratedImageF(tmpFile) # read as unsigned short 

679 

680 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

681 def testWriteFits(self): 

682 """Test writing FITS files""" 

683 

684 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

685 if self.fileForMetadata: 

686 imgU = afwImage.DecoratedImageF(self.fileForMetadata) 

687 else: 

688 imgU = afwImage.DecoratedImageF() 

689 

690 self.dimage1.writeFits(tmpFile, imgU.getMetadata()) 

691 # 

692 # Read it back 

693 # 

694 rimage = afwImage.DecoratedImageF(tmpFile) 

695 

696 self.assertEqual(self.dimage1.image[0, 0, afwImage.LOCAL], 

697 rimage.image[0, 0, afwImage.LOCAL]) 

698 # 

699 # Check that we wrote (and read) the metadata successfully 

700 if self.fileForMetadata: 

701 meta = self.trueMetadata 

702 for k in meta.keys(): 

703 self.assertEqual( 

704 rimage.getMetadata().getAsDouble(k), meta[k]) 

705 

706 def testReadWriteXY0(self): 

707 """Test that we read and write (X0, Y0) correctly""" 

708 im = afwImage.ImageF(lsst.geom.Extent2I(10, 20)) 

709 

710 x0, y0 = 1, 2 

711 im.setXY0(x0, y0) 

712 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

713 im.writeFits(tmpFile) 

714 

715 im2 = im.Factory(tmpFile) 

716 

717 self.assertEqual(im2.getX0(), x0) 

718 self.assertEqual(im2.getY0(), y0) 

719 

720 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

721 def testReadMetadata(self): 

722 im = afwImage.DecoratedImageF(self.fileForMetadata) 

723 

724 meta = readMetadata(self.fileForMetadata) 

725 self.assertIn("NAXIS1", meta.names()) 

726 self.assertEqual(im.getWidth(), meta.getScalar("NAXIS1")) 

727 self.assertEqual(im.getHeight(), meta.getScalar("NAXIS2")) 

728 

729 def testTicket1040(self): 

730 """ How to repeat from #1040""" 

731 image = afwImage.ImageD(lsst.geom.Extent2I(6, 6)) 

732 image[2, 2] = 100 

733 

734 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(5, 5)) 

735 subImage = image.Factory(image, bbox) 

736 subImageF = subImage.convertFloat() 

737 

738 if display: 

739 afwDisplay.Display(frame=0).mtv(subImage, title="subImage") 

740 afwDisplay.Display(frame=1).mtv(subImageF, title="converted subImage") 

741 

742 self.assertEqual(subImage[1, 1, afwImage.LOCAL], subImageF[1, 1, afwImage.LOCAL]) 

743 

744 def testDM882(self): 

745 """Test that we can write a dotted header unit to a FITS file. See DM-882.""" 

746 self.dimage1.getMetadata().add("A.B.C.D", 12345) 

747 tempdir = tempfile.mkdtemp() 

748 testfile = os.path.join(tempdir, "test.fits") 

749 try: 

750 self.dimage1.writeFits(testfile) 

751 meta = readMetadata(testfile) 

752 self.assertEqual(meta.getScalar("A.B.C.D"), 12345) 

753 finally: 

754 shutil.rmtree(tempdir) 

755 

756 def testLargeImage(self): 

757 """Test that creating an extremely large image raises, rather than segfaulting. DM-89, -527.""" 

758 for imtype in (afwImage.ImageD, afwImage.ImageF, afwImage.ImageI, afwImage.ImageU): 

759 self.assertRaises(lsst.pex.exceptions.LengthError, 

760 imtype, 60000, 60000) 

761 

762 

763def printImg(img): 

764 print("%4s " % "", end=' ') 

765 for c in range(img.getWidth()): 

766 print("%7d" % c, end=' ') 

767 print() 

768 

769 for r in range(img.getHeight() - 1, -1, -1): 

770 print("%4d " % r, end=' ') 

771 for c in range(img.getWidth()): 

772 print("%7.1f" % float(img[c, r, afwImage.LOCAL]), end=' ') 

773 print() 

774 

775 

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

777 pass 

778 

779 

780def setup_module(module): 

781 lsst.utils.tests.init() 

782 

783 

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

785 lsst.utils.tests.init() 

786 unittest.main()