Coverage for tests/test_maskedImage.py: 12%
472 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-14 03:43 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-14 03:43 -0800
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/>.
22"""
23Tests for MaskedImages
25Run with:
26 python test_maskedImage.py
27or
28 pytest test_maskedImage.py
29"""
31import itertools
32import os
33import unittest
35import numpy as np
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
46try:
47 type(display)
48except NameError:
49 display = False
50afwDisplay.setDefaultMaskTransparency(75)
53def makeRampImage(width, height, imgClass=afwImage.MaskedImageF):
54 """Make a ramp image of the specified size and image class
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
74class MaskedImageTestCase(lsst.utils.tests.TestCase):
75 """A test case for MaskedImage"""
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)
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")
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())))
111 def tearDown(self):
112 del self.mimage
113 del self.mimage2
114 del self.function
116 def testArrays(self):
117 """
118 This method is testing that ``lsst.afw.image.MaskedImageF.getArrays()``
119 returns the proper image, mask, and variance.
120 """
121 image, mask, variance = self.mimage.getArrays()
122 self.assertFloatsEqual(self.mimage.image.getArray(), image)
123 self.assertFloatsEqual(self.mimage.mask.getArray(), mask)
124 self.assertFloatsEqual(self.mimage.variance.getArray(), variance)
125 mimage2 = afwImage.makeMaskedImageFromArrays(image, mask, variance)
126 self.assertEqual(type(mimage2), type(self.mimage))
128 def testProperties(self):
129 self.assertImagesEqual(self.mimage.image, self.mimage.image)
130 self.assertMasksEqual(self.mimage.mask, self.mimage.mask)
131 self.assertImagesEqual(self.mimage.variance, self.mimage.variance)
132 image2 = self.mimage.image.Factory(self.mimage.getDimensions())
133 image2.array[:] = 5.0
134 self.mimage.image = image2
135 self.assertImagesEqual(self.mimage.image, image2)
136 mask2 = self.mimage.mask.Factory(self.mimage.getDimensions())
137 mask2.array[:] = 0x4
138 self.mimage.mask = mask2
139 self.assertMasksEqual(self.mimage.mask, mask2)
140 var2 = self.mimage.image.Factory(self.mimage.getDimensions())
141 var2.array[:] = 3.0
142 self.mimage.variance = var2
143 self.assertImagesEqual(self.mimage.variance, var2)
145 def testSetGetValues(self):
146 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
147 (self.imgVal1, self.EDGE, self.varVal1))
149 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
150 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
152 def testImagesOverlap(self):
153 # make pairs of image, variance and mask planes
154 # using the same dimensions for each so we can mix and match
155 # while making masked images
156 dim = lsst.geom.Extent2I(10, 8)
157 # a set of bounding boxes, some of which overlap each other
158 # and some of which do not, and include the full image bounding box
159 bboxes = (
160 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dim),
161 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(3, 3)),
162 lsst.geom.Box2I(lsst.geom.Point2I(2, 2), lsst.geom.Extent2I(6, 4)),
163 lsst.geom.Box2I(lsst.geom.Point2I(4, 4), lsst.geom.Extent2I(6, 4)),
164 )
165 masks = [afwImage.Mask(dim), afwImage.Mask(dim)]
166 variances = [afwImage.ImageF(dim), afwImage.ImageF(dim)]
167 imageClasses = (afwImage.ImageF, afwImage.ImageD, afwImage.ImageI, afwImage.ImageU)
168 for ImageClass1, ImageClass2 in itertools.product(imageClasses, imageClasses):
169 images = [ImageClass1(dim), ImageClass2(dim)]
170 for image1, mask1, variance1, image2, mask2, variance2 in itertools.product(
171 images, masks, variances, images, masks, variances):
172 with self.subTest(ImageClass1=str(ImageClass1), ImageClass2=str(ImageClass2),
173 image1=image1, mask1=mask1, variance1=variance1,
174 image2=image2, mask2=mask2, variance2=variance2):
175 shouldOverlap = (image1 is image2) or (mask1 is mask2) or (variance1 is variance2)
177 mi1 = afwImage.makeMaskedImage(image=image1, mask=mask1, variance=variance1)
178 mi2 = afwImage.makeMaskedImage(image=image2, mask=mask2, variance=variance2)
179 self.assertEqual(afwImage.imagesOverlap(mi1, mi2), shouldOverlap)
180 self.assertEqual(afwImage.imagesOverlap(mi2, mi1), shouldOverlap)
182 for bbox1, bbox2 in itertools.product(bboxes, bboxes):
183 with self.subTest(bbox1=bbox1, bbox2=bbox2):
184 subMi1 = afwImage.makeMaskedImage(image=type(image1)(image1, bbox1),
185 mask=afwImage.Mask(mask1, bbox1),
186 variance=afwImage.ImageF(variance1, bbox1))
187 subMi2 = afwImage.makeMaskedImage(image=type(image2)(image2, bbox2),
188 mask=afwImage.Mask(mask2, bbox2),
189 variance=afwImage.ImageF(variance2, bbox2))
190 subregionsShouldOverlap = shouldOverlap and bbox1.overlaps(bbox2)
191 self.assertEqual(afwImage.imagesOverlap(subMi1, subMi2), subregionsShouldOverlap)
192 self.assertEqual(afwImage.imagesOverlap(subMi2, subMi1), subregionsShouldOverlap)
194 def testMaskedImageFromImage(self):
195 w, h = 10, 20
196 dims = lsst.geom.Extent2I(w, h)
197 im, mask, var = afwImage.ImageF(dims), \
198 afwImage.Mask(dims), \
199 afwImage.ImageF(dims)
200 im.set(666)
202 maskedImage = afwImage.MaskedImageF(im, mask, var)
204 maskedImage = afwImage.makeMaskedImage(im, mask, var)
206 maskedImage = afwImage.MaskedImageF(im)
207 self.assertEqual(im.getDimensions(),
208 maskedImage.image.getDimensions())
209 self.assertEqual(im.getDimensions(),
210 maskedImage.mask.getDimensions())
211 self.assertEqual(im.getDimensions(),
212 maskedImage.variance.getDimensions())
214 self.assertEqual(maskedImage[0, 0, afwImage.LOCAL], (im[0, 0, afwImage.LOCAL], 0x0, 0.0))
216 def testMakeMaskedImageXY0(self):
217 """Test that makeMaskedImage sets XY0 correctly"""
218 im = afwImage.ImageF(200, 300)
219 xy0 = lsst.geom.PointI(10, 20)
220 im.setXY0(*xy0)
221 mi = afwImage.makeMaskedImage(im)
223 self.assertEqual(mi.image.getXY0(), xy0)
224 self.assertEqual(mi.mask.getXY0(), xy0)
225 self.assertEqual(mi.variance.getXY0(), xy0)
227 def testCopyMaskedImage(self):
228 """Test copy constructor"""
229 #
230 # shallow copy
231 #
232 mi = self.mimage.Factory(self.mimage, False)
234 val00 = self.mimage[0, 0, afwImage.LOCAL]
235 nval00 = (100, 0xff, -1) # the new value we'll set
236 self.assertNotEqual(val00, nval00)
238 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
239 mi[0, 0, afwImage.LOCAL] = nval00
241 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], nval00)
242 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00)
243 mi[0, 0, afwImage.LOCAL] = val00 # reinstate initial value
244 #
245 # deep copy
246 #
247 mi = self.mimage.Factory(self.mimage, True)
249 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
250 mi[0, 0, afwImage.LOCAL] = nval00
252 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], val00)
253 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00)
254 #
255 # Copy with change of Image type
256 #
257 mi = self.mimage.convertD()
259 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
260 mi[0, 0, afwImage.LOCAL] = nval00
262 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], val00)
263 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00)
264 #
265 # Convert from U to F
266 #
267 mi = afwImage.MaskedImageU(lsst.geom.Extent2I(10, 20))
268 val00 = (10, 0x10, 1)
269 mi.set(val00)
270 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
272 fmi = mi.convertF()
273 self.assertEqual(fmi[0, 0, afwImage.LOCAL], val00)
275 def testAddImages(self):
276 "Test addition"
277 # add an image
278 self.mimage2 += self.mimage
280 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], (self.imgVal1 + self.imgVal2, self.EDGE,
281 self.varVal1 + self.varVal2))
283 # Add an Image<int> to a MaskedImage<int>
284 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
285 mimage_i.set(900, 0x0, 1000.0)
286 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
288 mimage_i += image_i
290 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (902, 0x0, 1000.0))
292 # add a scalar
293 self.mimage += self.imgVal1
295 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
296 (2*self.imgVal1, self.EDGE, self.varVal1))
298 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
299 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
301 # add a function
302 self.mimage.set(self.imgVal1, 0x0, 0.0)
303 self.mimage += self.function
305 for i, j in [(2, 3)]:
306 self.assertEqual(self.mimage.image[i, j, afwImage.LOCAL],
307 self.imgVal1 + self.function(i, j))
309 def testAddScaledImages(self):
310 "Test addition by a scaled MaskedImage"
311 # add an image
312 c = 10.0
313 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
314 self.mimage2.scaledPlus(c, self.mimage)
315 #
316 # Now repeat calculation using a temporary
317 #
318 tmp = self.mimage.Factory(self.mimage, True)
319 tmp *= c
320 mimage2_copy += tmp
322 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
324 def testAssignWithBBox(self):
325 """Test assign(rhs, bbox) with non-empty bbox
326 """
327 for xy0 in (lsst.geom.Point2I(*val) for val in (
328 (0, 0),
329 (-100, 120), # an arbitrary value that is off the image
330 )):
331 destMIDim = lsst.geom.Extent2I(5, 4)
332 srcMIDim = lsst.geom.Extent2I(3, 2)
333 destMI = afwImage.MaskedImageF(destMIDim)
334 destImage = destMI.image
335 destVariance = destMI.variance
336 destMask = destMI.mask
337 destMI.setXY0(xy0)
338 srcMI = makeRampImage(*srcMIDim)
339 srcMI.setXY0(55, -33) # an arbitrary value that should be ignored
340 self.assertRaises(Exception, destMI.set, srcMI) # size mismatch
342 for validMin in (lsst.geom.Point2I(*val) for val in (
343 (0, 0),
344 (2, 0),
345 (0, 1),
346 (1, 2),
347 )):
348 # None to omit the argument
349 for origin in (None, afwImage.PARENT, afwImage.LOCAL):
350 destImage[:] = -1.0
351 destVariance[:] = -1.0
352 destMask[:] = 0xFFFF
353 bbox = lsst.geom.Box2I(validMin, srcMI.getDimensions())
354 if origin != afwImage.LOCAL:
355 bbox.shift(lsst.geom.Extent2I(xy0))
356 if origin is None:
357 destMI.assign(srcMI, bbox)
358 destMIView = afwImage.MaskedImageF(destMI, bbox)
359 else:
360 destMI.assign(srcMI, bbox, origin)
361 destMIView = afwImage.MaskedImageF(
362 destMI, bbox, origin)
363 for i in range(3):
364 self.assertListEqual(destMIView.getArrays()[i].flatten().tolist(),
365 srcMI.getArrays()[i].flatten().tolist())
366 numPixNotAssigned = (
367 destMIDim[0] * destMIDim[1]) - (srcMIDim[0] * srcMIDim[1])
368 self.assertEqual(
369 np.sum(destImage.getArray() < -0.5), numPixNotAssigned)
370 self.assertEqual(
371 np.sum(destVariance.getArray() < -0.5), numPixNotAssigned)
372 self.assertEqual(
373 np.sum(destMask.getArray() == 0xFFFF), numPixNotAssigned)
375 for badMin in (lsst.geom.Point2I(*val) + lsst.geom.Extent2I(xy0) for val in (
376 (-1, 0),
377 (3, 0),
378 (0, -1),
379 (1, 3),
380 )):
381 # None to omit the argument
382 for origin in (None, afwImage.PARENT, afwImage.LOCAL):
383 bbox = lsst.geom.Box2I(validMin, srcMI.getDimensions())
384 if origin != afwImage.LOCAL:
385 bbox.shift(lsst.geom.Extent2I(xy0))
386 if origin is None:
387 self.assertRaises(Exception, destMI.set, srcMI, bbox)
388 else:
389 self.assertRaises(
390 Exception, destMI.set, srcMI, bbox, origin)
392 def testAssignWithoutBBox(self):
393 """Test assign(rhs, [bbox]) with an empty bbox and with no bbox specified; both set all pixels
394 """
395 for xy0 in (lsst.geom.Point2I(*val) for val in (
396 (0, 0),
397 (-100, 120), # an arbitrary value that is off the image
398 )):
399 destMIDim = lsst.geom.Extent2I(5, 4)
400 destMI = afwImage.MaskedImageF(destMIDim)
401 destMI.setXY0(xy0)
402 destImage = destMI.image
403 destVariance = destMI.variance
404 destMask = destMI.mask
405 srcMI = makeRampImage(*destMIDim)
406 srcMI.setXY0(55, -33) # an arbitrary value that should be ignored
408 destImage[:] = -1.0
409 destVariance[:] = -1.0
410 destMask[:] = 0xFFFF
411 destMI.assign(srcMI)
412 for i in range(3):
413 self.assertListEqual(destMI.getArrays()[i].flatten().tolist(),
414 srcMI.getArrays()[i].flatten().tolist())
416 destImage[:] = -1.0
417 destVariance[:] = -1.0
418 destMask[:] = 0xFFFF
419 destMI.assign(srcMI, lsst.geom.Box2I())
420 for i in range(3):
421 self.assertListEqual(destMI.getArrays()[i].flatten().tolist(),
422 srcMI.getArrays()[i].flatten().tolist())
424 def testSubtractImages(self):
425 "Test subtraction"
426 # subtract an image
427 self.mimage2 -= self.mimage
428 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL],
429 (self.imgVal2 - self.imgVal1, self.EDGE, self.varVal2 + self.varVal1))
431 # Subtract an Image<int> from a MaskedImage<int>
432 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
433 mimage_i.set(900, 0x0, 1000.0)
434 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
436 mimage_i -= image_i
438 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (898, 0x0, 1000.0))
440 # subtract a scalar
441 self.mimage -= self.imgVal1
442 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], (0.0, self.EDGE, self.varVal1))
444 def testSubtractScaledImages(self):
445 "Test subtraction by a scaled MaskedImage"
446 # subtract a scaled image
447 c = 10.0
448 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
449 self.mimage2.scaledMinus(c, self.mimage)
450 #
451 # Now repeat calculation using a temporary
452 #
453 tmp = self.mimage.Factory(self.mimage, True)
454 tmp *= c
455 mimage2_copy -= tmp
457 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
459 def testArithmeticImagesMismatch(self):
460 "Test arithmetic operations on MaskedImages of different sizes"
461 i1 = afwImage.MaskedImageF(lsst.geom.Extent2I(100, 100))
462 i1.set(100)
463 i2 = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10))
464 i2.set(10)
466 def tst1(i1, i2):
467 i1 -= i2
469 def tst2(i1, i2):
470 i1.scaledMinus(1.0, i2)
472 def tst3(i1, i2):
473 i1 += i2
475 def tst4(i1, i2):
476 i1.scaledPlus(1.0, i2)
478 def tst5(i1, i2):
479 i1 *= i2
481 def tst6(i1, i2):
482 i1.scaledMultiplies(1.0, i2)
484 def tst7(i1, i2):
485 i1 /= i2
487 def tst8(i1, i2):
488 i1.scaledDivides(1.0, i2)
490 tsts12 = [tst1, tst3, tst5, tst7]
491 for tst in tsts12:
492 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i1, i2)
494 tsts21 = [tst2, tst4, tst6, tst8]
495 for tst in tsts21:
496 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i2, i1)
498 def testMultiplyImages(self):
499 """Test multiplication"""
500 # Multiply by a MaskedImage
501 self.mimage2 *= self.mimage
503 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL],
504 (self.imgVal2*self.imgVal1, self.EDGE,
505 self.varVal2*pow(self.imgVal1, 2) + self.varVal1*pow(self.imgVal2, 2)))
507 # Divide a MaskedImage<int> by an Image<int>; this divides the variance Image<float>
508 # by an Image<int> in C++
509 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
510 mimage_i.set(900, 0x0, 1000.0)
511 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
513 mimage_i *= image_i
515 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (1800, 0x0, 4000.0))
517 # multiply by a scalar
518 self.mimage *= self.imgVal1
520 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
521 (self.imgVal1*self.imgVal1, self.EDGE, self.varVal1*pow(self.imgVal1, 2)))
523 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
524 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
526 def testScaledMultiplyImages(self):
527 """Test multiplication by a scaled image"""
528 # Multiply by an image
529 c = 10.0
530 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
531 self.mimage2.scaledMultiplies(c, self.mimage)
532 #
533 # Now repeat calculation using a temporary
534 #
535 tmp = self.mimage.Factory(self.mimage, True)
536 tmp *= c
537 mimage2_copy *= tmp
539 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
541 def testDivideImages(self):
542 """Test division"""
543 # Divide by a MaskedImage
544 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
545 mimage2_copy /= self.mimage
547 self.assertEqual(mimage2_copy.image[0, 0, afwImage.LOCAL],
548 self.imgVal2/self.imgVal1)
549 self.assertEqual(mimage2_copy.mask[0, 0, afwImage.LOCAL], self.EDGE)
550 self.assertAlmostEqual(mimage2_copy.variance[0, 0, afwImage.LOCAL],
551 (self.varVal2*pow(self.imgVal1, 2)
552 + self.varVal1*pow(self.imgVal2, 2))/pow(self.imgVal1, 4), 10)
553 # Divide by an Image (of the same type as MaskedImage.image)
554 mimage = self.mimage2.Factory(self.mimage2, True)
555 mimage /= mimage.image
557 self.assertEqual(mimage[0, 0, afwImage.LOCAL], (self.imgVal2 / self.imgVal2, 0x0, self.varVal2))
559 # Divide by an Image (of a different type from MaskedImage.image)
560 # this isn't supported from python (it's OK in C++)
561 if False:
562 mimage = self.mimage2.Factory(self.mimage2, True)
563 image = afwImage.ImageI(mimage.getDimensions(), 1)
564 mimage /= image
566 self.assertEqual(mimage[0, 0, afwImage.LOCAL],
567 (self.imgVal2, 0x0, self.varVal2))
569 # Divide a MaskedImage<int> by an Image<int>; this divides the variance Image<float>
570 # by an Image<int> in C++
571 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
572 mimage_i.set(900, 0x0, 1000.0)
573 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
575 mimage_i /= image_i
577 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (450, 0x0, 250.0))
579 # divide by a scalar
580 self.mimage /= self.imgVal1
582 self.assertEqual(self.mimage.image[0, 0, afwImage.LOCAL],
583 self.imgVal1/self.imgVal1)
584 self.assertEqual(self.mimage.mask[0, 0, afwImage.LOCAL], self.EDGE)
585 self.assertAlmostEqual(self.mimage.variance[0, 0, afwImage.LOCAL],
586 self.varVal1/pow(self.imgVal1, 2), 9)
588 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
589 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
591 def testScaledDivideImages(self):
592 """Test division by a scaled image"""
593 # Divide by an image
594 c = 10.0
595 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
596 self.mimage2.scaledDivides(c, self.mimage)
597 #
598 # Now repeat calculation using a temporary
599 #
600 tmp = self.mimage.Factory(self.mimage, True)
601 tmp *= c
602 mimage2_copy /= tmp
604 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
606 def testCopyConstructors(self):
607 dimage = afwImage.MaskedImageF(self.mimage, True) # deep copy
608 simage = afwImage.MaskedImageF(self.mimage) # shallow copy
610 self.mimage += 2 # should only change dimage
611 self.assertEqual(dimage.image[0, 0, afwImage.LOCAL], self.imgVal1)
612 self.assertEqual(simage.image[0, 0, afwImage.LOCAL], self.imgVal1 + 2)
614 def checkImgPatch12(self, img, x0, y0):
615 """Check that a patch of an image is correct; origin of patch is at (x0, y0) in full image
616 N.b. This isn't a general routine! Works only for testSubimages[12]"""
618 self.assertEqual(img[x0 - 1, y0 - 1, afwImage.LOCAL],
619 (self.imgVal1, self.EDGE, self.varVal1))
620 self.assertEqual(img[x0, y0, afwImage.LOCAL], (666, self.BAD, 0))
621 self.assertEqual(img[x0 + 3, y0, afwImage.LOCAL],
622 (self.imgVal1, 0x0, self.varVal1))
623 self.assertEqual(img[x0, y0 + 1, afwImage.LOCAL], (666, self.BAD, 0))
624 self.assertEqual(img[x0 + 3, y0 + 1, afwImage.LOCAL],
625 (self.imgVal1, 0x0, self.varVal1))
626 self.assertEqual(img[x0, y0 + 2, afwImage.LOCAL],
627 (self.imgVal1, 0x0, self.varVal1))
629 def testOrigin(self):
630 """Check that we can set and read the origin"""
632 im = afwImage.MaskedImageF(lsst.geom.ExtentI(10, 20))
633 x0 = y0 = 0
635 self.assertEqual(im.getX0(), x0)
636 self.assertEqual(im.getY0(), y0)
637 self.assertEqual(im.getXY0(), lsst.geom.PointI(x0, y0))
639 x0, y0 = 3, 5
640 im.setXY0(x0, y0)
641 self.assertEqual(im.getX0(), x0)
642 self.assertEqual(im.getY0(), y0)
643 self.assertEqual(im.getXY0(), lsst.geom.PointI(x0, y0))
645 x0, y0 = 30, 50
646 im.setXY0(lsst.geom.Point2I(x0, y0))
647 self.assertEqual(im.getX0(), x0)
648 self.assertEqual(im.getY0(), y0)
649 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0))
651 def testSubimages1(self):
652 smimage = afwImage.MaskedImageF(
653 self.mimage,
654 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)),
655 afwImage.LOCAL
656 )
658 simage = afwImage.MaskedImageF(
659 smimage,
660 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(3, 2)),
661 afwImage.LOCAL
662 )
663 self.assertEqual(simage.getX0(), 2)
664 self.assertEqual(simage.getY0(), 2) # i.e. wrt self.mimage
666 mimage2 = afwImage.MaskedImageF(simage.getDimensions())
667 mimage2.image.set(666)
668 mimage2.mask.set(self.BAD)
669 simage[:] = mimage2
671 del simage
672 del mimage2
674 self.checkImgPatch12(self.mimage, 2, 2)
675 self.checkImgPatch12(smimage, 1, 1)
677 def testSubimages2(self):
678 """Test subimages when we've played with the (x0, y0) value"""
680 self.mimage[9, 4, afwImage.LOCAL] = (888, 0x0, 0)
682 smimage = afwImage.MaskedImageF(
683 self.mimage,
684 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)),
685 afwImage.LOCAL
686 )
687 # reset origin; doesn't affect pixel coordinate systems
688 smimage.setXY0(lsst.geom.Point2I(0, 0))
690 simage = afwImage.MaskedImageF(
691 smimage, lsst.geom.Box2I(lsst.geom.Point2I(1, 1),
692 lsst.geom.Extent2I(3, 2)),
693 afwImage.LOCAL
694 )
695 self.assertEqual(simage.getX0(), 1)
696 self.assertEqual(simage.getY0(), 1)
698 mimage2 = afwImage.MaskedImageF(simage.getDimensions())
699 mimage2.set(666, self.BAD, 0.0)
700 simage[:] = mimage2
701 del simage
702 del mimage2
704 self.checkImgPatch12(self.mimage, 2, 2)
705 self.checkImgPatch12(smimage, 1, 1)
707 def checkImgPatch3(self, img, deep):
708 """Check that a patch of an image is correct; origin of patch is at (x0, y0) in full image
709 N.b. This isn't a general routine! Works only for testSubimages3"""
711 # Include deep in comparison so we can see which test fails
712 self.assertEqual(img[0, 0, afwImage.LOCAL] + (deep, ),
713 (100, 0x0, self.varVal1, deep))
714 self.assertEqual(img[10, 10, afwImage.LOCAL] + (deep, ),
715 (200, 0xf, self.varVal1, deep))
717 def testSubimages3(self):
718 """Test subimages when we've played with the (x0, y0) value"""
720 self.mimage.image[20, 20, afwImage.LOCAL] = 200
721 self.mimage.mask[20, 20, afwImage.LOCAL] = 0xf
723 for deep in (True, False):
724 mimage = self.mimage.Factory(
725 self.mimage,
726 lsst.geom.Box2I(lsst.geom.Point2I(10, 10),
727 lsst.geom.Extent2I(64, 64)),
728 afwImage.LOCAL,
729 deep)
730 mimage.setXY0(lsst.geom.Point2I(0, 0))
731 mimage2 = mimage.Factory(mimage)
733 if display:
734 afwDisplay.Display(frame=0).mtv(mimage2, title="testSubimages3")
736 self.checkImgPatch3(mimage2, deep)
738 def testSetCopiedMask(self):
739 """Check that we can set the Mask with a copied Mask"""
741 crMask = self.mimage.mask.Factory(self.mimage.mask, True)
742 msk = self.mimage.mask
743 msk |= crMask
744 del msk
746 def testVariance(self):
747 """Check that we can set the variance from the gain"""
748 gain = 2
750 var = self.mimage.variance
751 var[:] = self.mimage.image
752 var /= gain
754 def testTicket653(self):
755 """How-to-repeat for #653"""
756 # The original ticket read this file, but it doesn't reproduce for me,
757 # As I don't see how reading an exposure from disk could make a difference
758 # it's easier to just build an Image
759 if False:
760 im = afwImage.ImageF(os.path.join(
761 lsst.utils.getPackageDir("afwdata"), "med_img.fits"))
762 else:
763 im = afwImage.ImageF(lsst.geom.Extent2I(10, 10))
764 mi = afwImage.MaskedImageF(im)
765 afwImage.ExposureF(mi)
767 def testMaskedImageInitialisation(self):
768 dims = self.mimage.getDimensions()
769 factory = self.mimage.Factory
771 self.mimage.set(666)
773 del self.mimage # tempt C++ to reuse the memory
774 self.mimage = factory(dims)
775 self.assertEqual(self.mimage[10, 10, afwImage.LOCAL], (0, 0x0, 0))
777 del self.mimage
778 self.mimage = factory(lsst.geom.Extent2I(20, 20))
779 self.assertEqual(self.mimage[10, 10, afwImage.LOCAL], (0, 0x0, 0))
781 def testImageSlices(self):
782 """Test image slicing, which generate sub-images using Box2I under the covers"""
783 im = afwImage.MaskedImageF(10, 20)
784 im[4, 10] = (10, 0x2, 100)
785 im[-3:, -2:, afwImage.LOCAL] = 100
786 sim = im[1:4, 6:10]
787 nan = -666 # a real NaN != NaN so tests fail
788 sim[:] = (-1, 0x8, nan)
789 im[0:4, 0:4] = im[2:6, 8:12]
791 if display:
792 afwDisplay.Display(frame=1).mtv(im, title="testImageSlices")
794 self.assertEqual(im[0, 6, afwImage.LOCAL], (0, 0x0, 0))
795 self.assertEqual(im[6, 17, afwImage.LOCAL], (0, 0x0, 0))
796 self.assertEqual(im[7, 18, afwImage.LOCAL], (100, 0x0, 0))
797 self.assertEqual(im[9, 19, afwImage.LOCAL], (100, 0x0, 0))
798 self.assertEqual(im[1, 6, afwImage.LOCAL], (-1, 0x8, nan))
799 self.assertEqual(im[3, 9, afwImage.LOCAL], (-1, 0x8, nan))
800 self.assertEqual(im[4, 10, afwImage.LOCAL], (10, 0x2, 100))
801 self.assertEqual(im[4, 9, afwImage.LOCAL], (0, 0x0, 0))
802 self.assertEqual(im[2, 2, afwImage.LOCAL], (10, 0x2, 100))
803 self.assertEqual(im[0, 0, afwImage.LOCAL], (-1, 0x8, nan))
805 def testConversionToScalar(self):
806 """Test that even 1-pixel MaskedImages can't be converted to scalars"""
807 im = afwImage.MaskedImageF(10, 20)
809 # only single pixel images may be converted
810 self.assertRaises(TypeError, float, im)
811 # actually, can't convert (img, msk, var) to scalar
812 self.assertRaises(TypeError, float, im[0, 0])
814 def testString(self):
815 image = afwImage.MaskedImageF(100, 100)
816 self.assertIn("image=", str(image))
817 self.assertIn("mask=", str(image))
818 self.assertIn("variance=", str(image))
819 self.assertIn(str(np.zeros((100, 100), dtype=image.image.dtype)), str(image))
820 self.assertIn(str(np.zeros((100, 100), dtype=image.mask.dtype)), str(image))
821 self.assertIn(str(np.zeros((100, 100), dtype=image.variance.dtype)), str(image))
822 self.assertIn("bbox=%s"%str(image.getBBox()), str(image))
823 self.assertIn("maskPlaneDict=%s"%str(image.mask.getMaskPlaneDict()), str(image))
825 self.assertIn("MaskedImageF=(", repr(image))
828def printImg(img):
829 print("%4s " % "", end=' ')
830 for c in range(img.getWidth()):
831 print("%7d" % c, end=' ')
832 print()
834 for r in range(img.getHeight() - 1, -1, -1):
835 print("%4d " % r, end=' ')
836 for c in range(img.getWidth()):
837 print("%7.1f" % float(img[c, r, afwImage.LOCAL]), end=' ')
838 print()
841class TestMemory(lsst.utils.tests.MemoryTestCase):
842 pass
845def setup_module(module):
846 lsst.utils.tests.init()
849if __name__ == "__main__": 849 ↛ 850line 849 didn't jump to line 850, because the condition on line 849 was never true
850 lsst.utils.tests.init()
851 unittest.main()