Coverage for tests/test_maskedImage.py: 11%
467 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-29 01:21 -0700
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-29 01:21 -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/>.
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 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
135 def testSetGetValues(self):
136 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
137 (self.imgVal1, self.EDGE, self.varVal1))
139 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
140 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
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)
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)
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)
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)
192 maskedImage = afwImage.MaskedImageF(im, mask, var)
194 maskedImage = afwImage.makeMaskedImage(im, mask, var)
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())
204 self.assertEqual(maskedImage[0, 0, afwImage.LOCAL], (im[0, 0, afwImage.LOCAL], 0x0, 0.0))
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)
213 self.assertEqual(mi.image.getXY0(), xy0)
214 self.assertEqual(mi.mask.getXY0(), xy0)
215 self.assertEqual(mi.variance.getXY0(), xy0)
217 def testCopyMaskedImage(self):
218 """Test copy constructor"""
219 #
220 # shallow copy
221 #
222 mi = self.mimage.Factory(self.mimage, False)
224 val00 = self.mimage[0, 0, afwImage.LOCAL]
225 nval00 = (100, 0xff, -1) # the new value we'll set
226 self.assertNotEqual(val00, nval00)
228 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
229 mi[0, 0, afwImage.LOCAL] = nval00
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)
239 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
240 mi[0, 0, afwImage.LOCAL] = nval00
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()
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 # 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)
262 fmi = mi.convertF()
263 self.assertEqual(fmi[0, 0, afwImage.LOCAL], val00)
265 def testAddImages(self):
266 "Test addition"
267 # add an image
268 self.mimage2 += self.mimage
270 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], (self.imgVal1 + self.imgVal2, self.EDGE,
271 self.varVal1 + self.varVal2))
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)
278 mimage_i += image_i
280 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (902, 0x0, 1000.0))
282 # add a scalar
283 self.mimage += self.imgVal1
285 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
286 (2*self.imgVal1, self.EDGE, self.varVal1))
288 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
289 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
291 # add a function
292 self.mimage.set(self.imgVal1, 0x0, 0.0)
293 self.mimage += self.function
295 for i, j in [(2, 3)]:
296 self.assertEqual(self.mimage.image[i, j, afwImage.LOCAL],
297 self.imgVal1 + self.function(i, j))
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
312 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
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
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)
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)
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
395 destImage[:] = -1.0
396 destVariance[:] = -1.0
397 destMask[:] = 0xFFFF
398 destMI.assign(srcMI)
399 self.assertMaskedImagesEqual(destMI, srcMI)
401 destImage[:] = -1.0
402 destVariance[:] = -1.0
403 destMask[:] = 0xFFFF
404 destMI.assign(srcMI, lsst.geom.Box2I())
405 self.assertMaskedImagesEqual(destMI, srcMI)
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))
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)
419 mimage_i -= image_i
421 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (898, 0x0, 1000.0))
423 # subtract a scalar
424 self.mimage -= self.imgVal1
425 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], (0.0, self.EDGE, self.varVal1))
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
440 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
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)
449 def tst1(i1, i2):
450 i1 -= i2
452 def tst2(i1, i2):
453 i1.scaledMinus(1.0, i2)
455 def tst3(i1, i2):
456 i1 += i2
458 def tst4(i1, i2):
459 i1.scaledPlus(1.0, i2)
461 def tst5(i1, i2):
462 i1 *= i2
464 def tst6(i1, i2):
465 i1.scaledMultiplies(1.0, i2)
467 def tst7(i1, i2):
468 i1 /= i2
470 def tst8(i1, i2):
471 i1.scaledDivides(1.0, i2)
473 tsts12 = [tst1, tst3, tst5, tst7]
474 for tst in tsts12:
475 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i1, i2)
477 tsts21 = [tst2, tst4, tst6, tst8]
478 for tst in tsts21:
479 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i2, i1)
481 def testMultiplyImages(self):
482 """Test multiplication"""
483 # Multiply by a MaskedImage
484 self.mimage2 *= self.mimage
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)))
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)
496 mimage_i *= image_i
498 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (1800, 0x0, 4000.0))
500 # multiply by a scalar
501 self.mimage *= self.imgVal1
503 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
504 (self.imgVal1*self.imgVal1, self.EDGE, self.varVal1*pow(self.imgVal1, 2)))
506 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
507 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
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
522 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
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
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
540 self.assertEqual(mimage[0, 0, afwImage.LOCAL], (self.imgVal2 / self.imgVal2, 0x0, self.varVal2))
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
549 self.assertEqual(mimage[0, 0, afwImage.LOCAL],
550 (self.imgVal2, 0x0, self.varVal2))
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)
558 mimage_i /= image_i
560 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (450, 0x0, 250.0))
562 # divide by a scalar
563 self.mimage /= self.imgVal1
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)
571 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
572 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
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
587 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
589 def testCopyConstructors(self):
590 dimage = afwImage.MaskedImageF(self.mimage, True) # deep copy
591 simage = afwImage.MaskedImageF(self.mimage) # shallow copy
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)
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]"""
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))
612 def testOrigin(self):
613 """Check that we can set and read the origin"""
615 im = afwImage.MaskedImageF(lsst.geom.ExtentI(10, 20))
616 x0 = y0 = 0
618 self.assertEqual(im.getX0(), x0)
619 self.assertEqual(im.getY0(), y0)
620 self.assertEqual(im.getXY0(), lsst.geom.PointI(x0, y0))
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))
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))
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 )
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
649 mimage2 = afwImage.MaskedImageF(simage.getDimensions())
650 mimage2.image.set(666)
651 mimage2.mask.set(self.BAD)
652 simage[:] = mimage2
654 del simage
655 del mimage2
657 self.checkImgPatch12(self.mimage, 2, 2)
658 self.checkImgPatch12(smimage, 1, 1)
660 def testSubimages2(self):
661 """Test subimages when we've played with the (x0, y0) value"""
663 self.mimage[9, 4, afwImage.LOCAL] = (888, 0x0, 0)
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))
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)
681 mimage2 = afwImage.MaskedImageF(simage.getDimensions())
682 mimage2.set(666, self.BAD, 0.0)
683 simage[:] = mimage2
684 del simage
685 del mimage2
687 self.checkImgPatch12(self.mimage, 2, 2)
688 self.checkImgPatch12(smimage, 1, 1)
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"""
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))
700 def testSubimages3(self):
701 """Test subimages when we've played with the (x0, y0) value"""
703 self.mimage.image[20, 20, afwImage.LOCAL] = 200
704 self.mimage.mask[20, 20, afwImage.LOCAL] = 0xf
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)
716 if display:
717 afwDisplay.Display(frame=0).mtv(mimage2, title="testSubimages3")
719 self.checkImgPatch3(mimage2, deep)
721 def testSetCopiedMask(self):
722 """Check that we can set the Mask with a copied Mask"""
724 crMask = self.mimage.mask.Factory(self.mimage.mask, True)
725 msk = self.mimage.mask
726 msk |= crMask
727 del msk
729 def testVariance(self):
730 """Check that we can set the variance from the gain"""
731 gain = 2
733 var = self.mimage.variance
734 var[:] = self.mimage.image
735 var /= gain
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)
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())
756 def testMaskedImageInitialisation(self):
757 dims = self.mimage.getDimensions()
758 factory = self.mimage.Factory
760 self.mimage.set(666)
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))
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))
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]
780 if display:
781 afwDisplay.Display(frame=1).mtv(im, title="testImageSlices")
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))
794 def testConversionToScalar(self):
795 """Test that even 1-pixel MaskedImages can't be converted to scalars"""
796 im = afwImage.MaskedImageF(10, 20)
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])
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))
814 self.assertIn("MaskedImageF=(", repr(image))
817def printImg(img):
818 print("%4s " % "", end=' ')
819 for c in range(img.getWidth()):
820 print("%7d" % c, end=' ')
821 print()
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()
830class TestMemory(lsst.utils.tests.MemoryTestCase):
831 pass
834def setup_module(module):
835 lsst.utils.tests.init()
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()