Coverage for tests/test_maskedImage.py: 11%

467 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-05-30 01:45 -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 MaskedImages 

24 

25Run with: 

26 python test_maskedImage.py 

27or 

28 pytest test_maskedImage.py 

29""" 

30 

31import itertools 

32import os 

33import unittest 

34 

35import numpy as np 

36 

37import lsst.utils 

38import lsst.utils.tests 

39import lsst.pex.exceptions 

40import lsst.daf.base 

41import lsst.geom 

42import lsst.afw.image as afwImage 

43import lsst.afw.math as afwMath 

44import lsst.afw.display as afwDisplay 

45 

46try: 

47 type(display) 

48except NameError: 

49 display = False 

50afwDisplay.setDefaultMaskTransparency(75) 

51 

52 

53def makeRampImage(width, height, imgClass=afwImage.MaskedImageF): 

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

55 

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

57 Variance values equal image values + 100 

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

59 """ 

60 mi = imgClass(width, height) 

61 image = mi.image 

62 mask = mi.mask 

63 variance = mi.variance 

64 val = 0 

65 for yInd in range(height): 

66 for xInd in range(width): 

67 image[xInd, yInd] = val 

68 variance[xInd, yInd] = val + 100 

69 mask[xInd, yInd] = val % 0x100 

70 val += 1 

71 return mi 

72 

73 

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

75 """A test case for MaskedImage""" 

76 

77 def setUp(self): 

78 self.imgVal1, self.varVal1 = 100.0, 10.0 

79 self.imgVal2, self.varVal2 = 200.0, 15.0 

80 self.mimage = afwImage.MaskedImageF(100, 200) 

81 

82 self.mimage.image.set(self.imgVal1) 

83 # 

84 # Set center of mask to 0, with 2 pixel border set to EDGE 

85 # 

86 self.BAD = afwImage.Mask.getPlaneBitMask("BAD") 

87 self.EDGE = afwImage.Mask.getPlaneBitMask("EDGE") 

88 

89 self.mimage.mask.set(self.EDGE) 

90 centre = afwImage.Mask( 

91 self.mimage.mask, 

92 lsst.geom.Box2I(lsst.geom.Point2I(2, 2), 

93 self.mimage.getDimensions() - lsst.geom.Extent2I(4)), 

94 afwImage.LOCAL) 

95 centre.set(0x0) 

96 # 

97 self.mimage.variance.set(self.varVal1) 

98 # 

99 # Second MaskedImage 

100 # 

101 self.mimage2 = afwImage.MaskedImageF(self.mimage.getDimensions()) 

102 self.mimage2.image.set(self.imgVal2) 

103 self.mimage2.variance.set(self.varVal2) 

104 # 

105 # a Function2 

106 # 

107 self.function = afwMath.PolynomialFunction2D(2) 

108 self.function.setParameters( 

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

110 

111 def tearDown(self): 

112 del self.mimage 

113 del self.mimage2 

114 del self.function 

115 

116 def testProperties(self): 

117 self.assertImagesEqual(self.mimage.image, self.mimage.image) 

118 self.assertMasksEqual(self.mimage.mask, self.mimage.mask) 

119 self.assertImagesEqual(self.mimage.variance, self.mimage.variance) 

120 image2 = self.mimage.image.Factory(self.mimage.getDimensions()) 

121 image2.array[:] = 5.0 

122 self.mimage.image = image2 

123 self.assertImagesEqual(self.mimage.image, image2) 

124 mask2 = self.mimage.mask.Factory(self.mimage.getDimensions()) 

125 mask2.array[:] = 0x4 

126 self.mimage.mask = mask2 

127 self.assertMasksEqual(self.mimage.mask, mask2) 

128 var2 = self.mimage.image.Factory(self.mimage.getDimensions()) 

129 var2.array[:] = 3.0 

130 self.mimage.variance = var2 

131 self.assertImagesEqual(self.mimage.variance, var2) 

132 with self.assertRaises(TypeError): 

133 self.mimage.image.array = None 

134 

135 def testSetGetValues(self): 

136 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], 

137 (self.imgVal1, self.EDGE, self.varVal1)) 

138 

139 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE) 

140 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0) 

141 

142 def testImagesOverlap(self): 

143 # make pairs of image, variance and mask planes 

144 # using the same dimensions for each so we can mix and match 

145 # while making masked images 

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

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

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

149 bboxes = ( 

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

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

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

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

154 ) 

155 masks = [afwImage.Mask(dim), afwImage.Mask(dim)] 

156 variances = [afwImage.ImageF(dim), afwImage.ImageF(dim)] 

157 imageClasses = (afwImage.ImageF, afwImage.ImageD, afwImage.ImageI, afwImage.ImageU) 

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

159 images = [ImageClass1(dim), ImageClass2(dim)] 

160 for image1, mask1, variance1, image2, mask2, variance2 in itertools.product( 

161 images, masks, variances, images, masks, variances): 

162 with self.subTest(ImageClass1=str(ImageClass1), ImageClass2=str(ImageClass2), 

163 image1=repr(image1), mask1=repr(mask1), variance1=repr(variance1), 

164 image2=repr(image2), mask2=repr(mask2), variance2=repr(variance2)): 

165 shouldOverlap = (image1 is image2) or (mask1 is mask2) or (variance1 is variance2) 

166 

167 mi1 = afwImage.makeMaskedImage(image=image1, mask=mask1, variance=variance1) 

168 mi2 = afwImage.makeMaskedImage(image=image2, mask=mask2, variance=variance2) 

169 self.assertEqual(afwImage.imagesOverlap(mi1, mi2), shouldOverlap) 

170 self.assertEqual(afwImage.imagesOverlap(mi2, mi1), shouldOverlap) 

171 

172 for bbox1, bbox2 in itertools.product(bboxes, bboxes): 

173 with self.subTest(bbox1=repr(bbox1), bbox2=repr(bbox2)): 

174 subMi1 = afwImage.makeMaskedImage(image=type(image1)(image1, bbox1), 

175 mask=afwImage.Mask(mask1, bbox1), 

176 variance=afwImage.ImageF(variance1, bbox1)) 

177 subMi2 = afwImage.makeMaskedImage(image=type(image2)(image2, bbox2), 

178 mask=afwImage.Mask(mask2, bbox2), 

179 variance=afwImage.ImageF(variance2, bbox2)) 

180 subregionsShouldOverlap = shouldOverlap and bbox1.overlaps(bbox2) 

181 self.assertEqual(afwImage.imagesOverlap(subMi1, subMi2), subregionsShouldOverlap) 

182 self.assertEqual(afwImage.imagesOverlap(subMi2, subMi1), subregionsShouldOverlap) 

183 

184 def testMaskedImageFromImage(self): 

185 w, h = 10, 20 

186 dims = lsst.geom.Extent2I(w, h) 

187 im, mask, var = afwImage.ImageF(dims), \ 

188 afwImage.Mask(dims), \ 

189 afwImage.ImageF(dims) 

190 im.set(666) 

191 

192 maskedImage = afwImage.MaskedImageF(im, mask, var) 

193 

194 maskedImage = afwImage.makeMaskedImage(im, mask, var) 

195 

196 maskedImage = afwImage.MaskedImageF(im) 

197 self.assertEqual(im.getDimensions(), 

198 maskedImage.image.getDimensions()) 

199 self.assertEqual(im.getDimensions(), 

200 maskedImage.mask.getDimensions()) 

201 self.assertEqual(im.getDimensions(), 

202 maskedImage.variance.getDimensions()) 

203 

204 self.assertEqual(maskedImage[0, 0, afwImage.LOCAL], (im[0, 0, afwImage.LOCAL], 0x0, 0.0)) 

205 

206 def testMakeMaskedImageXY0(self): 

207 """Test that makeMaskedImage sets XY0 correctly""" 

208 im = afwImage.ImageF(200, 300) 

209 xy0 = lsst.geom.PointI(10, 20) 

210 im.setXY0(*xy0) 

211 mi = afwImage.makeMaskedImage(im) 

212 

213 self.assertEqual(mi.image.getXY0(), xy0) 

214 self.assertEqual(mi.mask.getXY0(), xy0) 

215 self.assertEqual(mi.variance.getXY0(), xy0) 

216 

217 def testCopyMaskedImage(self): 

218 """Test copy constructor""" 

219 # 

220 # shallow copy 

221 # 

222 mi = self.mimage.Factory(self.mimage, False) 

223 

224 val00 = self.mimage[0, 0, afwImage.LOCAL] 

225 nval00 = (100, 0xff, -1) # the new value we'll set 

226 self.assertNotEqual(val00, nval00) 

227 

228 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00) 

229 mi[0, 0, afwImage.LOCAL] = nval00 

230 

231 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], nval00) 

232 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00) 

233 mi[0, 0, afwImage.LOCAL] = val00 # reinstate initial value 

234 # 

235 # deep copy 

236 # 

237 mi = self.mimage.Factory(self.mimage, True) 

238 

239 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00) 

240 mi[0, 0, afwImage.LOCAL] = nval00 

241 

242 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], val00) 

243 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00) 

244 # 

245 # Copy with change of Image type 

246 # 

247 mi = self.mimage.convertD() 

248 

249 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00) 

250 mi[0, 0, afwImage.LOCAL] = nval00 

251 

252 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], val00) 

253 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00) 

254 # 

255 # Convert from U to F 

256 # 

257 mi = afwImage.MaskedImageU(lsst.geom.Extent2I(10, 20)) 

258 val00 = (10, 0x10, 1) 

259 mi.set(val00) 

260 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00) 

261 

262 fmi = mi.convertF() 

263 self.assertEqual(fmi[0, 0, afwImage.LOCAL], val00) 

264 

265 def testAddImages(self): 

266 "Test addition" 

267 # add an image 

268 self.mimage2 += self.mimage 

269 

270 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], (self.imgVal1 + self.imgVal2, self.EDGE, 

271 self.varVal1 + self.varVal2)) 

272 

273 # Add an Image<int> to a MaskedImage<int> 

274 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions()) 

275 mimage_i.set(900, 0x0, 1000.0) 

276 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2) 

277 

278 mimage_i += image_i 

279 

280 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (902, 0x0, 1000.0)) 

281 

282 # add a scalar 

283 self.mimage += self.imgVal1 

284 

285 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], 

286 (2*self.imgVal1, self.EDGE, self.varVal1)) 

287 

288 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE) 

289 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0) 

290 

291 # add a function 

292 self.mimage.set(self.imgVal1, 0x0, 0.0) 

293 self.mimage += self.function 

294 

295 for i, j in [(2, 3)]: 

296 self.assertEqual(self.mimage.image[i, j, afwImage.LOCAL], 

297 self.imgVal1 + self.function(i, j)) 

298 

299 def testAddScaledImages(self): 

300 "Test addition by a scaled MaskedImage" 

301 # add an image 

302 c = 10.0 

303 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy 

304 self.mimage2.scaledPlus(c, self.mimage) 

305 # 

306 # Now repeat calculation using a temporary 

307 # 

308 tmp = self.mimage.Factory(self.mimage, True) 

309 tmp *= c 

310 mimage2_copy += tmp 

311 

312 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL]) 

313 

314 def testAssignWithBBox(self): 

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

316 """ 

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

318 (0, 0), 

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

320 )): 

321 destMIDim = lsst.geom.Extent2I(5, 4) 

322 srcMIDim = lsst.geom.Extent2I(3, 2) 

323 destMI = afwImage.MaskedImageF(destMIDim) 

324 destImage = destMI.image 

325 destVariance = destMI.variance 

326 destMask = destMI.mask 

327 destMI.setXY0(xy0) 

328 srcMI = makeRampImage(*srcMIDim) 

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

330 self.assertRaises(Exception, destMI.set, srcMI) # size mismatch 

331 

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

333 (0, 0), 

334 (2, 0), 

335 (0, 1), 

336 (1, 2), 

337 )): 

338 # None to omit the argument 

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

340 destImage[:] = -1.0 

341 destVariance[:] = -1.0 

342 destMask[:] = 0xFFFF 

343 bbox = lsst.geom.Box2I(validMin, srcMI.getDimensions()) 

344 if origin != afwImage.LOCAL: 

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

346 if origin is None: 

347 destMI.assign(srcMI, bbox) 

348 destMIView = afwImage.MaskedImageF(destMI, bbox) 

349 else: 

350 destMI.assign(srcMI, bbox, origin) 

351 destMIView = afwImage.MaskedImageF(destMI, bbox, origin) 

352 self.assertMaskedImagesEqual(destMIView, srcMI) 

353 numPixNotAssigned = ( 

354 destMIDim[0] * destMIDim[1]) - (srcMIDim[0] * srcMIDim[1]) 

355 self.assertEqual( 

356 np.sum(destImage.getArray() < -0.5), numPixNotAssigned) 

357 self.assertEqual( 

358 np.sum(destVariance.getArray() < -0.5), numPixNotAssigned) 

359 self.assertEqual( 

360 np.sum(destMask.getArray() == 0xFFFF), numPixNotAssigned) 

361 

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

363 (-1, 0), 

364 (3, 0), 

365 (0, -1), 

366 (1, 3), 

367 )): 

368 # None to omit the argument 

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

370 bbox = lsst.geom.Box2I(validMin, srcMI.getDimensions()) 

371 if origin != afwImage.LOCAL: 

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

373 if origin is None: 

374 self.assertRaises(Exception, destMI.set, srcMI, bbox) 

375 else: 

376 self.assertRaises( 

377 Exception, destMI.set, srcMI, bbox, origin) 

378 

379 def testAssignWithoutBBox(self): 

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

381 """ 

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

383 (0, 0), 

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

385 )): 

386 destMIDim = lsst.geom.Extent2I(5, 4) 

387 destMI = afwImage.MaskedImageF(destMIDim) 

388 destMI.setXY0(xy0) 

389 destImage = destMI.image 

390 destVariance = destMI.variance 

391 destMask = destMI.mask 

392 srcMI = makeRampImage(*destMIDim) 

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

394 

395 destImage[:] = -1.0 

396 destVariance[:] = -1.0 

397 destMask[:] = 0xFFFF 

398 destMI.assign(srcMI) 

399 self.assertMaskedImagesEqual(destMI, srcMI) 

400 

401 destImage[:] = -1.0 

402 destVariance[:] = -1.0 

403 destMask[:] = 0xFFFF 

404 destMI.assign(srcMI, lsst.geom.Box2I()) 

405 self.assertMaskedImagesEqual(destMI, srcMI) 

406 

407 def testSubtractImages(self): 

408 "Test subtraction" 

409 # subtract an image 

410 self.mimage2 -= self.mimage 

411 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], 

412 (self.imgVal2 - self.imgVal1, self.EDGE, self.varVal2 + self.varVal1)) 

413 

414 # Subtract an Image<int> from a MaskedImage<int> 

415 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions()) 

416 mimage_i.set(900, 0x0, 1000.0) 

417 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2) 

418 

419 mimage_i -= image_i 

420 

421 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (898, 0x0, 1000.0)) 

422 

423 # subtract a scalar 

424 self.mimage -= self.imgVal1 

425 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], (0.0, self.EDGE, self.varVal1)) 

426 

427 def testSubtractScaledImages(self): 

428 "Test subtraction by a scaled MaskedImage" 

429 # subtract a scaled image 

430 c = 10.0 

431 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy 

432 self.mimage2.scaledMinus(c, self.mimage) 

433 # 

434 # Now repeat calculation using a temporary 

435 # 

436 tmp = self.mimage.Factory(self.mimage, True) 

437 tmp *= c 

438 mimage2_copy -= tmp 

439 

440 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL]) 

441 

442 def testArithmeticImagesMismatch(self): 

443 "Test arithmetic operations on MaskedImages of different sizes" 

444 i1 = afwImage.MaskedImageF(lsst.geom.Extent2I(100, 100)) 

445 i1.set(100) 

446 i2 = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10)) 

447 i2.set(10) 

448 

449 def tst1(i1, i2): 

450 i1 -= i2 

451 

452 def tst2(i1, i2): 

453 i1.scaledMinus(1.0, i2) 

454 

455 def tst3(i1, i2): 

456 i1 += i2 

457 

458 def tst4(i1, i2): 

459 i1.scaledPlus(1.0, i2) 

460 

461 def tst5(i1, i2): 

462 i1 *= i2 

463 

464 def tst6(i1, i2): 

465 i1.scaledMultiplies(1.0, i2) 

466 

467 def tst7(i1, i2): 

468 i1 /= i2 

469 

470 def tst8(i1, i2): 

471 i1.scaledDivides(1.0, i2) 

472 

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

474 for tst in tsts12: 

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

476 

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

478 for tst in tsts21: 

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

480 

481 def testMultiplyImages(self): 

482 """Test multiplication""" 

483 # Multiply by a MaskedImage 

484 self.mimage2 *= self.mimage 

485 

486 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], 

487 (self.imgVal2*self.imgVal1, self.EDGE, 

488 self.varVal2*pow(self.imgVal1, 2) + self.varVal1*pow(self.imgVal2, 2))) 

489 

490 # Divide a MaskedImage<int> by an Image<int>; this divides the variance Image<float> 

491 # by an Image<int> in C++ 

492 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions()) 

493 mimage_i.set(900, 0x0, 1000.0) 

494 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2) 

495 

496 mimage_i *= image_i 

497 

498 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (1800, 0x0, 4000.0)) 

499 

500 # multiply by a scalar 

501 self.mimage *= self.imgVal1 

502 

503 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], 

504 (self.imgVal1*self.imgVal1, self.EDGE, self.varVal1*pow(self.imgVal1, 2))) 

505 

506 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE) 

507 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0) 

508 

509 def testScaledMultiplyImages(self): 

510 """Test multiplication by a scaled image""" 

511 # Multiply by an image 

512 c = 10.0 

513 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy 

514 self.mimage2.scaledMultiplies(c, self.mimage) 

515 # 

516 # Now repeat calculation using a temporary 

517 # 

518 tmp = self.mimage.Factory(self.mimage, True) 

519 tmp *= c 

520 mimage2_copy *= tmp 

521 

522 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL]) 

523 

524 def testDivideImages(self): 

525 """Test division""" 

526 # Divide by a MaskedImage 

527 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy 

528 mimage2_copy /= self.mimage 

529 

530 self.assertEqual(mimage2_copy.image[0, 0, afwImage.LOCAL], 

531 self.imgVal2/self.imgVal1) 

532 self.assertEqual(mimage2_copy.mask[0, 0, afwImage.LOCAL], self.EDGE) 

533 self.assertAlmostEqual(mimage2_copy.variance[0, 0, afwImage.LOCAL], 

534 (self.varVal2*pow(self.imgVal1, 2) 

535 + self.varVal1*pow(self.imgVal2, 2))/pow(self.imgVal1, 4), 10) 

536 # Divide by an Image (of the same type as MaskedImage.image) 

537 mimage = self.mimage2.Factory(self.mimage2, True) 

538 mimage /= mimage.image 

539 

540 self.assertEqual(mimage[0, 0, afwImage.LOCAL], (self.imgVal2 / self.imgVal2, 0x0, self.varVal2)) 

541 

542 # Divide by an Image (of a different type from MaskedImage.image) 

543 # this isn't supported from python (it's OK in C++) 

544 if False: 

545 mimage = self.mimage2.Factory(self.mimage2, True) 

546 image = afwImage.ImageI(mimage.getDimensions(), 1) 

547 mimage /= image 

548 

549 self.assertEqual(mimage[0, 0, afwImage.LOCAL], 

550 (self.imgVal2, 0x0, self.varVal2)) 

551 

552 # Divide a MaskedImage<int> by an Image<int>; this divides the variance Image<float> 

553 # by an Image<int> in C++ 

554 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions()) 

555 mimage_i.set(900, 0x0, 1000.0) 

556 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2) 

557 

558 mimage_i /= image_i 

559 

560 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (450, 0x0, 250.0)) 

561 

562 # divide by a scalar 

563 self.mimage /= self.imgVal1 

564 

565 self.assertEqual(self.mimage.image[0, 0, afwImage.LOCAL], 

566 self.imgVal1/self.imgVal1) 

567 self.assertEqual(self.mimage.mask[0, 0, afwImage.LOCAL], self.EDGE) 

568 self.assertAlmostEqual(self.mimage.variance[0, 0, afwImage.LOCAL], 

569 self.varVal1/pow(self.imgVal1, 2), 9) 

570 

571 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE) 

572 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0) 

573 

574 def testScaledDivideImages(self): 

575 """Test division by a scaled image""" 

576 # Divide by an image 

577 c = 10.0 

578 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy 

579 self.mimage2.scaledDivides(c, self.mimage) 

580 # 

581 # Now repeat calculation using a temporary 

582 # 

583 tmp = self.mimage.Factory(self.mimage, True) 

584 tmp *= c 

585 mimage2_copy /= tmp 

586 

587 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL]) 

588 

589 def testCopyConstructors(self): 

590 dimage = afwImage.MaskedImageF(self.mimage, True) # deep copy 

591 simage = afwImage.MaskedImageF(self.mimage) # shallow copy 

592 

593 self.mimage += 2 # should only change dimage 

594 self.assertEqual(dimage.image[0, 0, afwImage.LOCAL], self.imgVal1) 

595 self.assertEqual(simage.image[0, 0, afwImage.LOCAL], self.imgVal1 + 2) 

596 

597 def checkImgPatch12(self, img, x0, y0): 

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

599 N.b. This isn't a general routine! Works only for testSubimages[12]""" 

600 

601 self.assertEqual(img[x0 - 1, y0 - 1, afwImage.LOCAL], 

602 (self.imgVal1, self.EDGE, self.varVal1)) 

603 self.assertEqual(img[x0, y0, afwImage.LOCAL], (666, self.BAD, 0)) 

604 self.assertEqual(img[x0 + 3, y0, afwImage.LOCAL], 

605 (self.imgVal1, 0x0, self.varVal1)) 

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

607 self.assertEqual(img[x0 + 3, y0 + 1, afwImage.LOCAL], 

608 (self.imgVal1, 0x0, self.varVal1)) 

609 self.assertEqual(img[x0, y0 + 2, afwImage.LOCAL], 

610 (self.imgVal1, 0x0, self.varVal1)) 

611 

612 def testOrigin(self): 

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

614 

615 im = afwImage.MaskedImageF(lsst.geom.ExtentI(10, 20)) 

616 x0 = y0 = 0 

617 

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

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

620 self.assertEqual(im.getXY0(), lsst.geom.PointI(x0, y0)) 

621 

622 x0, y0 = 3, 5 

623 im.setXY0(x0, y0) 

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

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

626 self.assertEqual(im.getXY0(), lsst.geom.PointI(x0, y0)) 

627 

628 x0, y0 = 30, 50 

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

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

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

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

633 

634 def testSubimages1(self): 

635 smimage = afwImage.MaskedImageF( 

636 self.mimage, 

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

638 afwImage.LOCAL 

639 ) 

640 

641 simage = afwImage.MaskedImageF( 

642 smimage, 

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

644 afwImage.LOCAL 

645 ) 

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

647 self.assertEqual(simage.getY0(), 2) # i.e. wrt self.mimage 

648 

649 mimage2 = afwImage.MaskedImageF(simage.getDimensions()) 

650 mimage2.image.set(666) 

651 mimage2.mask.set(self.BAD) 

652 simage[:] = mimage2 

653 

654 del simage 

655 del mimage2 

656 

657 self.checkImgPatch12(self.mimage, 2, 2) 

658 self.checkImgPatch12(smimage, 1, 1) 

659 

660 def testSubimages2(self): 

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

662 

663 self.mimage[9, 4, afwImage.LOCAL] = (888, 0x0, 0) 

664 

665 smimage = afwImage.MaskedImageF( 

666 self.mimage, 

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

668 afwImage.LOCAL 

669 ) 

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

671 smimage.setXY0(lsst.geom.Point2I(0, 0)) 

672 

673 simage = afwImage.MaskedImageF( 

674 smimage, lsst.geom.Box2I(lsst.geom.Point2I(1, 1), 

675 lsst.geom.Extent2I(3, 2)), 

676 afwImage.LOCAL 

677 ) 

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

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

680 

681 mimage2 = afwImage.MaskedImageF(simage.getDimensions()) 

682 mimage2.set(666, self.BAD, 0.0) 

683 simage[:] = mimage2 

684 del simage 

685 del mimage2 

686 

687 self.checkImgPatch12(self.mimage, 2, 2) 

688 self.checkImgPatch12(smimage, 1, 1) 

689 

690 def checkImgPatch3(self, img, deep): 

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

692 N.b. This isn't a general routine! Works only for testSubimages3""" 

693 

694 # Include deep in comparison so we can see which test fails 

695 self.assertEqual(img[0, 0, afwImage.LOCAL] + (deep, ), 

696 (100, 0x0, self.varVal1, deep)) 

697 self.assertEqual(img[10, 10, afwImage.LOCAL] + (deep, ), 

698 (200, 0xf, self.varVal1, deep)) 

699 

700 def testSubimages3(self): 

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

702 

703 self.mimage.image[20, 20, afwImage.LOCAL] = 200 

704 self.mimage.mask[20, 20, afwImage.LOCAL] = 0xf 

705 

706 for deep in (True, False): 

707 mimage = self.mimage.Factory( 

708 self.mimage, 

709 lsst.geom.Box2I(lsst.geom.Point2I(10, 10), 

710 lsst.geom.Extent2I(64, 64)), 

711 afwImage.LOCAL, 

712 deep) 

713 mimage.setXY0(lsst.geom.Point2I(0, 0)) 

714 mimage2 = mimage.Factory(mimage) 

715 

716 if display: 

717 afwDisplay.Display(frame=0).mtv(mimage2, title="testSubimages3") 

718 

719 self.checkImgPatch3(mimage2, deep) 

720 

721 def testSetCopiedMask(self): 

722 """Check that we can set the Mask with a copied Mask""" 

723 

724 crMask = self.mimage.mask.Factory(self.mimage.mask, True) 

725 msk = self.mimage.mask 

726 msk |= crMask 

727 del msk 

728 

729 def testVariance(self): 

730 """Check that we can set the variance from the gain""" 

731 gain = 2 

732 

733 var = self.mimage.variance 

734 var[:] = self.mimage.image 

735 var /= gain 

736 

737 def testTicket653(self): 

738 """How-to-repeat for #653""" 

739 # The original ticket read this file, but it doesn't reproduce for me, 

740 # As I don't see how reading an exposure from disk could make a difference 

741 # it's easier to just build an Image 

742 if False: 

743 im = afwImage.ImageF(os.path.join( 

744 lsst.utils.getPackageDir("afwdata"), "med_img.fits")) 

745 else: 

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

747 mi = afwImage.MaskedImageF(im) 

748 afwImage.ExposureF(mi) 

749 

750 def testTicket41478(self): 

751 """Test for DM-41478 fix""" 

752 masked_image = afwImage.MaskedImageF(None) 

753 image = afwImage.ImageF(None) 

754 self.assertEqual(masked_image.getBBox(), image.getBBox()) 

755 

756 def testMaskedImageInitialisation(self): 

757 dims = self.mimage.getDimensions() 

758 factory = self.mimage.Factory 

759 

760 self.mimage.set(666) 

761 

762 del self.mimage # tempt C++ to reuse the memory 

763 self.mimage = factory(dims) 

764 self.assertEqual(self.mimage[10, 10, afwImage.LOCAL], (0, 0x0, 0)) 

765 

766 del self.mimage 

767 self.mimage = factory(lsst.geom.Extent2I(20, 20)) 

768 self.assertEqual(self.mimage[10, 10, afwImage.LOCAL], (0, 0x0, 0)) 

769 

770 def testImageSlices(self): 

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

772 im = afwImage.MaskedImageF(10, 20) 

773 im[4, 10] = (10, 0x2, 100) 

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

775 sim = im[1:4, 6:10] 

776 nan = -666 # a real NaN != NaN so tests fail 

777 sim[:] = (-1, 0x8, nan) 

778 im[0:4, 0:4] = im[2:6, 8:12] 

779 

780 if display: 

781 afwDisplay.Display(frame=1).mtv(im, title="testImageSlices") 

782 

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

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

785 self.assertEqual(im[7, 18, afwImage.LOCAL], (100, 0x0, 0)) 

786 self.assertEqual(im[9, 19, afwImage.LOCAL], (100, 0x0, 0)) 

787 self.assertEqual(im[1, 6, afwImage.LOCAL], (-1, 0x8, nan)) 

788 self.assertEqual(im[3, 9, afwImage.LOCAL], (-1, 0x8, nan)) 

789 self.assertEqual(im[4, 10, afwImage.LOCAL], (10, 0x2, 100)) 

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

791 self.assertEqual(im[2, 2, afwImage.LOCAL], (10, 0x2, 100)) 

792 self.assertEqual(im[0, 0, afwImage.LOCAL], (-1, 0x8, nan)) 

793 

794 def testConversionToScalar(self): 

795 """Test that even 1-pixel MaskedImages can't be converted to scalars""" 

796 im = afwImage.MaskedImageF(10, 20) 

797 

798 # only single pixel images may be converted 

799 self.assertRaises(TypeError, float, im) 

800 # actually, can't convert (img, msk, var) to scalar 

801 self.assertRaises(TypeError, float, im[0, 0]) 

802 

803 def testString(self): 

804 image = afwImage.MaskedImageF(100, 100) 

805 self.assertIn("image=", str(image)) 

806 self.assertIn("mask=", str(image)) 

807 self.assertIn("variance=", str(image)) 

808 self.assertIn(str(np.zeros((100, 100), dtype=image.image.dtype)), str(image)) 

809 self.assertIn(str(np.zeros((100, 100), dtype=image.mask.dtype)), str(image)) 

810 self.assertIn(str(np.zeros((100, 100), dtype=image.variance.dtype)), str(image)) 

811 self.assertIn("bbox=%s"%str(image.getBBox()), str(image)) 

812 self.assertIn("maskPlaneDict=%s"%str(image.mask.getMaskPlaneDict()), str(image)) 

813 

814 self.assertIn("MaskedImageF=(", repr(image)) 

815 

816 

817def printImg(img): 

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

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

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

821 print() 

822 

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

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

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

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

827 print() 

828 

829 

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

831 pass 

832 

833 

834def setup_module(module): 

835 lsst.utils.tests.init() 

836 

837 

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

839 lsst.utils.tests.init() 

840 unittest.main()