Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

48import lsst.pex.exceptions as pexExcept 

49 

50try: 

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

52except pexExcept.NotFoundError: 

53 afwdataDir = None 

54 

55try: 

56 type(display) 

57except NameError: 

58 display = False 

59 

60 

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

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

63 

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

65 """ 

66 im = imgClass(width, height) 

67 val = 0 

68 for yInd in range(height): 

69 for xInd in range(width): 

70 im[xInd, yInd] = val 

71 val += 1 

72 return im 

73 

74 

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

76 """A test case for Image""" 

77 

78 def setUp(self): 

79 np.random.seed(1) 

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

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

82 self.image1.set(self.val1) 

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

84 self.image2.set(self.val2) 

85 self.function = afwMath.PolynomialFunction2D(2) 

86 self.function.setParameters( 

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

88 

89 def tearDown(self): 

90 del self.image1 

91 del self.image2 

92 del self.function 

93 

94 def testArrays(self): 

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

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

97 array1 = image1.getArray() 

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

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

100 image2 = cls(array1, False) 

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

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

103 image3 = afwImage.makeImageFromArray(array1) 

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

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

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

107 array2 = image1.array 

108 np.testing.assert_array_equal(array1, array2) 

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

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

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

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

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

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

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

116 image1.array[:] = array3 

117 np.testing.assert_array_equal(array1, array3) 

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

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

120 array4 = image1.array.copy() 

121 array4 += 5 

122 image1.array += 5 

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

124 

125 def testImagesOverlap(self): 

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

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

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

129 bboxes = ( 

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

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

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

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

134 ) 

135 

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

137 

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

139 with self.subTest(ImageClass1=ImageClass1, ImageClass2=ImageClass2): 

140 image1 = ImageClass1(dim) 

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

142 

143 image2 = ImageClass2(dim) 

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

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

146 

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

148 shouldOverlap = bboxa.overlaps(bboxb) 

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

150 subim1a = ImageClass1(image1, bboxa) 

151 subim1b = ImageClass1(image1, bboxb) 

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

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

154 

155 subim2b = ImageClass2(image2, bboxb) 

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

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

158 

159 def testInitializeImages(self): 

160 val = 666 

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

162 im = ctor(10, 10, val) 

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

164 

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

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

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

168 

169 def testSetGetImages(self): 

170 self.image1.setXY0(3, 4) 

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

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

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

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

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

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

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

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

179 

180 def testAllocateLargeImages(self): 

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

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

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

184 

185 def tst(): 

186 afwImage.ImageF(bbox) 

187 

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

189 

190 def testAddImages(self): 

191 self.image2 += self.image1 

192 self.image1 += self.val1 

193 

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

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

196 

197 self.image1.set(self.val1) 

198 self.image1 += self.function 

199 

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

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

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

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

204 

205 def testAssignWithBBox(self): 

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

207 """ 

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

209 (0, 0), 

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

211 )): 

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

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

214 destIm = afwImage.ImageF(destImDim) 

215 destIm.setXY0(xy0) 

216 srcIm = makeRampImage(*srcImDim) 

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

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

219 

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

221 (0, 0), 

222 (2, 0), 

223 (0, 1), 

224 (1, 2), 

225 )): 

226 # None to omit the argument 

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

228 destIm[:] = -1.0 

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

230 if origin != afwImage.LOCAL: 

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

232 if origin is None: 

233 destIm.assign(srcIm, bbox) 

234 destImView = afwImage.ImageF(destIm, bbox) 

235 else: 

236 destIm.assign(srcIm, bbox, origin) 

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

238 self.assertFloatsEqual( 

239 destImView.getArray(), srcIm.getArray()) 

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

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

242 self.assertEqual( 

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

244 

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

246 (-1, 0), 

247 (3, 0), 

248 (0, -1), 

249 (1, 3), 

250 )): 

251 # None to omit the argument 

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

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

254 if origin != afwImage.LOCAL: 

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

256 if origin is None: 

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

258 else: 

259 self.assertRaises( 

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

261 

262 def testAssignWithoutBBox(self): 

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

264 """ 

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

266 (0, 0), 

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

268 )): 

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

270 destIm = afwImage.ImageF(destImDim) 

271 destIm.setXY0(xy0) 

272 srcIm = makeRampImage(*destImDim) 

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

274 

275 destIm[:] = -1.0 

276 destIm.assign(srcIm) 

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

278 

279 destIm[:] = -1.0 

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

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

282 

283 def testAddScaledImages(self): 

284 c = 10.0 

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

286 

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

288 

289 def testSubtractImages(self): 

290 self.image2 -= self.image1 

291 self.image1 -= self.val1 

292 

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

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

295 

296 self.image1.set(self.val1) 

297 self.image1 -= self.function 

298 

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

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

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

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

303 

304 def testArithmeticImagesMismatch(self): 

305 "Test arithmetic operations on Images of different sizes" 

306 i1 = afwImage.ImageF(100, 100) 

307 i1.set(100) 

308 i2 = afwImage.ImageF(10, 10) 

309 i2.set(10) 

310 

311 def tst1(i1, i2): 

312 i1 -= i2 

313 

314 def tst2(i1, i2): 

315 i1.scaledMinus(1.0, i2) 

316 

317 def tst3(i1, i2): 

318 i1 += i2 

319 

320 def tst4(i1, i2): 

321 i1.scaledPlus(1.0, i2) 

322 

323 def tst5(i1, i2): 

324 i1 *= i2 

325 

326 def tst6(i1, i2): 

327 i1.scaledMultiplies(1.0, i2) 

328 

329 def tst7(i1, i2): 

330 i1 /= i2 

331 

332 def tst8(i1, i2): 

333 i1.scaledDivides(1.0, i2) 

334 

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

336 for tst in tsts12: 

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

338 

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

340 for tst in tsts21: 

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

342 

343 def testSubtractScaledImages(self): 

344 c = 10.0 

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

346 

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

348 

349 def testMultiplyImages(self): 

350 self.image2 *= self.image1 

351 self.image1 *= self.val1 

352 

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

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

355 

356 def testMultiplesScaledImages(self): 

357 c = 10.0 

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

359 

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

361 

362 def testDivideImages(self): 

363 self.image2 /= self.image1 

364 self.image1 /= self.val1 

365 

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

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

368 

369 def testDividesScaledImages(self): 

370 c = 10.0 

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

372 

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

374 

375 def testCopyConstructors(self): 

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

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

378 

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

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

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

382 

383 def testGeneralisedCopyConstructors(self): 

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

385 imageU = self.image1.convertU() 

386 imageF = imageU.convertF() 

387 imageD = imageF.convertD() 

388 

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

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

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

392 

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

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

395 

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

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

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

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

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

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

402 

403 def testOrigin(self): 

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

405 

406 im = afwImage.ImageF(10, 20) 

407 x0 = y0 = 0 

408 

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

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

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

412 

413 x0, y0 = 3, 5 

414 im.setXY0(x0, y0) 

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

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

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

418 

419 x0, y0 = 30, 50 

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

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

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

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

424 

425 def testSubimages(self): 

426 simage1 = afwImage.ImageF( 

427 self.image1, 

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

429 afwImage.LOCAL) 

430 

431 simage = afwImage.ImageF( 

432 simage1, 

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

434 afwImage.LOCAL 

435 ) 

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

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

438 

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

440 image2.set(666) 

441 simage[:] = image2 

442 del simage 

443 del image2 

444 

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

446 self.checkImgPatch(simage1, 1, 1) 

447 

448 def testSubimages2(self): 

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

450 

451 self.image1[9, 4] = 888 

452 

453 simage1 = afwImage.ImageF( 

454 self.image1, 

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

456 afwImage.LOCAL 

457 ) 

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

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

460 

461 simage = afwImage.ImageF( 

462 simage1, 

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

464 afwImage.LOCAL 

465 ) 

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

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

468 

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

470 image2.set(666) 

471 simage[:] = image2 

472 del simage 

473 del image2 

474 

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

476 self.checkImgPatch(simage1, 1, 1) 

477 

478 def testBadSubimages(self): 

479 def tst(): 

480 afwImage.ImageF( 

481 self.image1, 

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

483 afwImage.LOCAL 

484 ) 

485 

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

487 

488 def testImageInitialisation(self): 

489 dims = self.image1.getDimensions() 

490 factory = self.image1.Factory 

491 

492 self.image1.set(666) 

493 

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

495 self.image1 = factory(dims) 

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

497 

498 def testImageSlicesOrigin(self): 

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

500 im = afwImage.ImageF(10, 20) 

501 im.setXY0(50, 100) 

502 im[54, 110] = 10 

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

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

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

506 sim[:] = -1 

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

508 

509 if display: 

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

511 

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

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

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

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

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

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

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

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

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

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

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

523 

524 def testImageSliceFromBox(self): 

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

526 im = afwImage.ImageF(10, 20) 

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

528 im[bbox] = -1 

529 

530 if display: 

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

532 

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

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

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

536 

537 def testImageSliceFromBoxOrigin(self): 

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

539 im = afwImage.ImageF(10, 20) 

540 im.setXY0(50, 100) 

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

542 im[bbox] = -1 

543 

544 if display: 

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

546 

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

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

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

550 

551 def testClone(self): 

552 """Test that clone works properly""" 

553 im = afwImage.ImageF(10, 20) 

554 im[0, 0] = 100 

555 

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

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

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

559 im2[0, 0] += 100 

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

561 

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

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

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

565 im2[0, 0] += 10 

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

567 

568 def testString(self): 

569 imageF = afwImage.ImageF(100, 100) 

570 imageDSmall = afwImage.ImageD(2, 2) 

571 imageISmall = afwImage.ImageI(2, 2) 

572 imageU = afwImage.ImageU(100, 100) 

573 

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

575 # array; we test both large and small. 

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

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

578 

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

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

581 

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

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

584 

585 

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

587 """A test case for DecoratedImage""" 

588 

589 def setUp(self): 

590 np.random.seed(1) 

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

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

593 self.dimage1 = afwImage.DecoratedImageF( 

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

595 ) 

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

597 

598 if afwdataDir is not None: 

599 self.fileForMetadata = os.path.join( 

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

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

602 

603 def tearDown(self): 

604 del self.dimage1 

605 

606 def testCreateDecoratedImage(self): 

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

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

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

610 

611 def testCreateDecoratedImageFromImage(self): 

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

613 image[:] = self.dimage1.image 

614 

615 dimage = afwImage.DecoratedImageF(image) 

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

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

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

619 

620 def testCopyConstructors(self): 

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

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

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

624 

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

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

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

628 

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

630 def testReadFits(self): 

631 """Test reading FITS files""" 

632 

633 hdus = {} 

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

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

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

637 

638 # read as unsigned short 

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

640 # read as float 

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

642 

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

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

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

646 # 

647 # Check the metadata 

648 # 

649 meta = self.trueMetadata 

650 for k in meta.keys(): 

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

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

653 # 

654 # Read an F32 image 

655 # 

656 # read as unsigned short 

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

658 # read as float 

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

660 

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

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

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

664 # 

665 # Read a char image 

666 # 

667 maskImg = afwImage.DecoratedImageU( 

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

669 

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

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

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

673 # 

674 # Read a U16 image 

675 # 

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

677 imgU.writeFits(tmpFile) 

678 

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

680 

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

682 def testWriteFits(self): 

683 """Test writing FITS files""" 

684 

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

686 if self.fileForMetadata: 

687 imgU = afwImage.DecoratedImageF(self.fileForMetadata) 

688 else: 

689 imgU = afwImage.DecoratedImageF() 

690 

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

692 # 

693 # Read it back 

694 # 

695 rimage = afwImage.DecoratedImageF(tmpFile) 

696 

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

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

699 # 

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

701 if self.fileForMetadata: 

702 meta = self.trueMetadata 

703 for k in meta.keys(): 

704 self.assertEqual( 

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

706 

707 def testReadWriteXY0(self): 

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

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

710 

711 x0, y0 = 1, 2 

712 im.setXY0(x0, y0) 

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

714 im.writeFits(tmpFile) 

715 

716 im2 = im.Factory(tmpFile) 

717 

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

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

720 

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

722 def testReadMetadata(self): 

723 im = afwImage.DecoratedImageF(self.fileForMetadata) 

724 

725 meta = readMetadata(self.fileForMetadata) 

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

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

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

729 

730 def testTicket1040(self): 

731 """ How to repeat from #1040""" 

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

733 image[2, 2] = 100 

734 

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

736 subImage = image.Factory(image, bbox) 

737 subImageF = subImage.convertFloat() 

738 

739 if display: 

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

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

742 

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

744 

745 def testDM882(self): 

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

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

748 tempdir = tempfile.mkdtemp() 

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

750 try: 

751 self.dimage1.writeFits(testfile) 

752 meta = readMetadata(testfile) 

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

754 finally: 

755 shutil.rmtree(tempdir) 

756 

757 def testLargeImage(self): 

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

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

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

761 imtype, 60000, 60000) 

762 

763 

764def printImg(img): 

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

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

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

768 print() 

769 

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

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

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

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

774 print() 

775 

776 

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

778 pass 

779 

780 

781def setup_module(module): 

782 lsst.utils.tests.init() 

783 

784 

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

786 lsst.utils.tests.init() 

787 unittest.main()