Coverage for tests/test_maskedImage.py: 12%

469 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-23 03:25 -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 # TODO DM-39935: remove this test when you remove getArrays() 

117 def testArrays(self): 

118 """ 

119 This method is testing that ``lsst.afw.image.MaskedImageF.getArrays()`` 

120 returns the proper image, mask, and variance. 

121 """ 

122 image, mask, variance = self.mimage.getArrays() 

123 self.assertFloatsEqual(self.mimage.image.getArray(), image) 

124 self.assertFloatsEqual(self.mimage.mask.getArray(), mask) 

125 self.assertFloatsEqual(self.mimage.variance.getArray(), variance) 

126 mimage2 = afwImage.makeMaskedImageFromArrays(image, mask, variance) 

127 self.assertEqual(type(mimage2), type(self.mimage)) 

128 

129 def testProperties(self): 

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

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

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

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

134 image2.array[:] = 5.0 

135 self.mimage.image = image2 

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

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

138 mask2.array[:] = 0x4 

139 self.mimage.mask = mask2 

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

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

142 var2.array[:] = 3.0 

143 self.mimage.variance = var2 

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

145 

146 def testSetGetValues(self): 

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

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

149 

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

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

152 

153 def testImagesOverlap(self): 

154 # make pairs of image, variance and mask planes 

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

156 # while making masked images 

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

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

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

160 bboxes = ( 

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

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

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

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

165 ) 

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

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

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

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

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

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

172 images, masks, variances, images, masks, variances): 

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

174 image1=image1, mask1=mask1, variance1=variance1, 

175 image2=image2, mask2=mask2, variance2=variance2): 

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

177 

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

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

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

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

182 

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

184 with self.subTest(bbox1=bbox1, bbox2=bbox2): 

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

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

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

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

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

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

191 subregionsShouldOverlap = shouldOverlap and bbox1.overlaps(bbox2) 

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

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

194 

195 def testMaskedImageFromImage(self): 

196 w, h = 10, 20 

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

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

199 afwImage.Mask(dims), \ 

200 afwImage.ImageF(dims) 

201 im.set(666) 

202 

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

204 

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

206 

207 maskedImage = afwImage.MaskedImageF(im) 

208 self.assertEqual(im.getDimensions(), 

209 maskedImage.image.getDimensions()) 

210 self.assertEqual(im.getDimensions(), 

211 maskedImage.mask.getDimensions()) 

212 self.assertEqual(im.getDimensions(), 

213 maskedImage.variance.getDimensions()) 

214 

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

216 

217 def testMakeMaskedImageXY0(self): 

218 """Test that makeMaskedImage sets XY0 correctly""" 

219 im = afwImage.ImageF(200, 300) 

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

221 im.setXY0(*xy0) 

222 mi = afwImage.makeMaskedImage(im) 

223 

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

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

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

227 

228 def testCopyMaskedImage(self): 

229 """Test copy constructor""" 

230 # 

231 # shallow copy 

232 # 

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

234 

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

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

237 self.assertNotEqual(val00, nval00) 

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], nval00) 

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

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

245 # 

246 # deep copy 

247 # 

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

249 

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

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

252 

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

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

255 # 

256 # Copy with change of Image type 

257 # 

258 mi = self.mimage.convertD() 

259 

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

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

262 

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

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

265 # 

266 # Convert from U to F 

267 # 

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

269 val00 = (10, 0x10, 1) 

270 mi.set(val00) 

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

272 

273 fmi = mi.convertF() 

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

275 

276 def testAddImages(self): 

277 "Test addition" 

278 # add an image 

279 self.mimage2 += self.mimage 

280 

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

282 self.varVal1 + self.varVal2)) 

283 

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

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

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

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

288 

289 mimage_i += image_i 

290 

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

292 

293 # add a scalar 

294 self.mimage += self.imgVal1 

295 

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

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

298 

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

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

301 

302 # add a function 

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

304 self.mimage += self.function 

305 

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

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

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

309 

310 def testAddScaledImages(self): 

311 "Test addition by a scaled MaskedImage" 

312 # add an image 

313 c = 10.0 

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

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

316 # 

317 # Now repeat calculation using a temporary 

318 # 

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

320 tmp *= c 

321 mimage2_copy += tmp 

322 

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

324 

325 def testAssignWithBBox(self): 

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

327 """ 

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

329 (0, 0), 

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

331 )): 

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

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

334 destMI = afwImage.MaskedImageF(destMIDim) 

335 destImage = destMI.image 

336 destVariance = destMI.variance 

337 destMask = destMI.mask 

338 destMI.setXY0(xy0) 

339 srcMI = makeRampImage(*srcMIDim) 

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

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

342 

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

344 (0, 0), 

345 (2, 0), 

346 (0, 1), 

347 (1, 2), 

348 )): 

349 # None to omit the argument 

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

351 destImage[:] = -1.0 

352 destVariance[:] = -1.0 

353 destMask[:] = 0xFFFF 

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

355 if origin != afwImage.LOCAL: 

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

357 if origin is None: 

358 destMI.assign(srcMI, bbox) 

359 destMIView = afwImage.MaskedImageF(destMI, bbox) 

360 else: 

361 destMI.assign(srcMI, bbox, origin) 

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

363 self.assertMaskedImagesEqual(destMIView, srcMI) 

364 numPixNotAssigned = ( 

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

366 self.assertEqual( 

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

368 self.assertEqual( 

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

370 self.assertEqual( 

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

372 

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

374 (-1, 0), 

375 (3, 0), 

376 (0, -1), 

377 (1, 3), 

378 )): 

379 # None to omit the argument 

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

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

382 if origin != afwImage.LOCAL: 

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

384 if origin is None: 

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

386 else: 

387 self.assertRaises( 

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

389 

390 def testAssignWithoutBBox(self): 

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

392 """ 

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

394 (0, 0), 

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

396 )): 

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

398 destMI = afwImage.MaskedImageF(destMIDim) 

399 destMI.setXY0(xy0) 

400 destImage = destMI.image 

401 destVariance = destMI.variance 

402 destMask = destMI.mask 

403 srcMI = makeRampImage(*destMIDim) 

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

405 

406 destImage[:] = -1.0 

407 destVariance[:] = -1.0 

408 destMask[:] = 0xFFFF 

409 destMI.assign(srcMI) 

410 self.assertMaskedImagesEqual(destMI, srcMI) 

411 

412 destImage[:] = -1.0 

413 destVariance[:] = -1.0 

414 destMask[:] = 0xFFFF 

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

416 self.assertMaskedImagesEqual(destMI, srcMI) 

417 

418 def testSubtractImages(self): 

419 "Test subtraction" 

420 # subtract an image 

421 self.mimage2 -= self.mimage 

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

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

424 

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

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

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

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

429 

430 mimage_i -= image_i 

431 

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

433 

434 # subtract a scalar 

435 self.mimage -= self.imgVal1 

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

437 

438 def testSubtractScaledImages(self): 

439 "Test subtraction by a scaled MaskedImage" 

440 # subtract a scaled image 

441 c = 10.0 

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

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

444 # 

445 # Now repeat calculation using a temporary 

446 # 

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

448 tmp *= c 

449 mimage2_copy -= tmp 

450 

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

452 

453 def testArithmeticImagesMismatch(self): 

454 "Test arithmetic operations on MaskedImages of different sizes" 

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

456 i1.set(100) 

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

458 i2.set(10) 

459 

460 def tst1(i1, i2): 

461 i1 -= i2 

462 

463 def tst2(i1, i2): 

464 i1.scaledMinus(1.0, i2) 

465 

466 def tst3(i1, i2): 

467 i1 += i2 

468 

469 def tst4(i1, i2): 

470 i1.scaledPlus(1.0, i2) 

471 

472 def tst5(i1, i2): 

473 i1 *= i2 

474 

475 def tst6(i1, i2): 

476 i1.scaledMultiplies(1.0, i2) 

477 

478 def tst7(i1, i2): 

479 i1 /= i2 

480 

481 def tst8(i1, i2): 

482 i1.scaledDivides(1.0, i2) 

483 

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

485 for tst in tsts12: 

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

487 

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

489 for tst in tsts21: 

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

491 

492 def testMultiplyImages(self): 

493 """Test multiplication""" 

494 # Multiply by a MaskedImage 

495 self.mimage2 *= self.mimage 

496 

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

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

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

500 

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

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

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

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

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

506 

507 mimage_i *= image_i 

508 

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

510 

511 # multiply by a scalar 

512 self.mimage *= self.imgVal1 

513 

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

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

516 

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

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

519 

520 def testScaledMultiplyImages(self): 

521 """Test multiplication by a scaled image""" 

522 # Multiply by an image 

523 c = 10.0 

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

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

526 # 

527 # Now repeat calculation using a temporary 

528 # 

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

530 tmp *= c 

531 mimage2_copy *= tmp 

532 

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

534 

535 def testDivideImages(self): 

536 """Test division""" 

537 # Divide by a MaskedImage 

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

539 mimage2_copy /= self.mimage 

540 

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

542 self.imgVal2/self.imgVal1) 

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

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

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

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

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

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

549 mimage /= mimage.image 

550 

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

552 

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

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

555 if False: 

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

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

558 mimage /= image 

559 

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

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

562 

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

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

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

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

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

568 

569 mimage_i /= image_i 

570 

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

572 

573 # divide by a scalar 

574 self.mimage /= self.imgVal1 

575 

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

577 self.imgVal1/self.imgVal1) 

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

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

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

581 

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

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

584 

585 def testScaledDivideImages(self): 

586 """Test division by a scaled image""" 

587 # Divide by an image 

588 c = 10.0 

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

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

591 # 

592 # Now repeat calculation using a temporary 

593 # 

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

595 tmp *= c 

596 mimage2_copy /= tmp 

597 

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

599 

600 def testCopyConstructors(self): 

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

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

603 

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

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

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

607 

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

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

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

611 

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

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

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

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

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

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

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

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

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

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

622 

623 def testOrigin(self): 

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

625 

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

627 x0 = y0 = 0 

628 

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

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

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

632 

633 x0, y0 = 3, 5 

634 im.setXY0(x0, y0) 

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

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

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

638 

639 x0, y0 = 30, 50 

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

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

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

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

644 

645 def testSubimages1(self): 

646 smimage = afwImage.MaskedImageF( 

647 self.mimage, 

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

649 afwImage.LOCAL 

650 ) 

651 

652 simage = afwImage.MaskedImageF( 

653 smimage, 

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

655 afwImage.LOCAL 

656 ) 

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

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

659 

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

661 mimage2.image.set(666) 

662 mimage2.mask.set(self.BAD) 

663 simage[:] = mimage2 

664 

665 del simage 

666 del mimage2 

667 

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

669 self.checkImgPatch12(smimage, 1, 1) 

670 

671 def testSubimages2(self): 

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

673 

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

675 

676 smimage = afwImage.MaskedImageF( 

677 self.mimage, 

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

679 afwImage.LOCAL 

680 ) 

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

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

683 

684 simage = afwImage.MaskedImageF( 

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

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

687 afwImage.LOCAL 

688 ) 

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

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

691 

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

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

694 simage[:] = mimage2 

695 del simage 

696 del mimage2 

697 

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

699 self.checkImgPatch12(smimage, 1, 1) 

700 

701 def checkImgPatch3(self, img, deep): 

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

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

704 

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

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

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

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

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

710 

711 def testSubimages3(self): 

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

713 

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

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

716 

717 for deep in (True, False): 

718 mimage = self.mimage.Factory( 

719 self.mimage, 

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

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

722 afwImage.LOCAL, 

723 deep) 

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

725 mimage2 = mimage.Factory(mimage) 

726 

727 if display: 

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

729 

730 self.checkImgPatch3(mimage2, deep) 

731 

732 def testSetCopiedMask(self): 

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

734 

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

736 msk = self.mimage.mask 

737 msk |= crMask 

738 del msk 

739 

740 def testVariance(self): 

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

742 gain = 2 

743 

744 var = self.mimage.variance 

745 var[:] = self.mimage.image 

746 var /= gain 

747 

748 def testTicket653(self): 

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

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

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

752 # it's easier to just build an Image 

753 if False: 

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

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

756 else: 

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

758 mi = afwImage.MaskedImageF(im) 

759 afwImage.ExposureF(mi) 

760 

761 def testMaskedImageInitialisation(self): 

762 dims = self.mimage.getDimensions() 

763 factory = self.mimage.Factory 

764 

765 self.mimage.set(666) 

766 

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

768 self.mimage = factory(dims) 

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

770 

771 del self.mimage 

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

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

774 

775 def testImageSlices(self): 

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

777 im = afwImage.MaskedImageF(10, 20) 

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

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

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

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

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

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

784 

785 if display: 

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

787 

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

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

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

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

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

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

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

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

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

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

798 

799 def testConversionToScalar(self): 

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

801 im = afwImage.MaskedImageF(10, 20) 

802 

803 # only single pixel images may be converted 

804 self.assertRaises(TypeError, float, im) 

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

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

807 

808 def testString(self): 

809 image = afwImage.MaskedImageF(100, 100) 

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

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

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

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

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

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

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

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

818 

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

820 

821 

822def printImg(img): 

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

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

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

826 print() 

827 

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

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

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

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

832 print() 

833 

834 

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

836 pass 

837 

838 

839def setup_module(module): 

840 lsst.utils.tests.init() 

841 

842 

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

844 lsst.utils.tests.init() 

845 unittest.main()