Coverage for tests/test_maskedImage.py: 12%
462 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 03:31 -0700
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 03:31 -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)
133 def testSetGetValues(self):
134 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
135 (self.imgVal1, self.EDGE, self.varVal1))
137 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
138 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
140 def testImagesOverlap(self):
141 # make pairs of image, variance and mask planes
142 # using the same dimensions for each so we can mix and match
143 # while making masked images
144 dim = lsst.geom.Extent2I(10, 8)
145 # a set of bounding boxes, some of which overlap each other
146 # and some of which do not, and include the full image bounding box
147 bboxes = (
148 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dim),
149 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(3, 3)),
150 lsst.geom.Box2I(lsst.geom.Point2I(2, 2), lsst.geom.Extent2I(6, 4)),
151 lsst.geom.Box2I(lsst.geom.Point2I(4, 4), lsst.geom.Extent2I(6, 4)),
152 )
153 masks = [afwImage.Mask(dim), afwImage.Mask(dim)]
154 variances = [afwImage.ImageF(dim), afwImage.ImageF(dim)]
155 imageClasses = (afwImage.ImageF, afwImage.ImageD, afwImage.ImageI, afwImage.ImageU)
156 for ImageClass1, ImageClass2 in itertools.product(imageClasses, imageClasses):
157 images = [ImageClass1(dim), ImageClass2(dim)]
158 for image1, mask1, variance1, image2, mask2, variance2 in itertools.product(
159 images, masks, variances, images, masks, variances):
160 with self.subTest(ImageClass1=str(ImageClass1), ImageClass2=str(ImageClass2),
161 image1=image1, mask1=mask1, variance1=variance1,
162 image2=image2, mask2=mask2, variance2=variance2):
163 shouldOverlap = (image1 is image2) or (mask1 is mask2) or (variance1 is variance2)
165 mi1 = afwImage.makeMaskedImage(image=image1, mask=mask1, variance=variance1)
166 mi2 = afwImage.makeMaskedImage(image=image2, mask=mask2, variance=variance2)
167 self.assertEqual(afwImage.imagesOverlap(mi1, mi2), shouldOverlap)
168 self.assertEqual(afwImage.imagesOverlap(mi2, mi1), shouldOverlap)
170 for bbox1, bbox2 in itertools.product(bboxes, bboxes):
171 with self.subTest(bbox1=bbox1, bbox2=bbox2):
172 subMi1 = afwImage.makeMaskedImage(image=type(image1)(image1, bbox1),
173 mask=afwImage.Mask(mask1, bbox1),
174 variance=afwImage.ImageF(variance1, bbox1))
175 subMi2 = afwImage.makeMaskedImage(image=type(image2)(image2, bbox2),
176 mask=afwImage.Mask(mask2, bbox2),
177 variance=afwImage.ImageF(variance2, bbox2))
178 subregionsShouldOverlap = shouldOverlap and bbox1.overlaps(bbox2)
179 self.assertEqual(afwImage.imagesOverlap(subMi1, subMi2), subregionsShouldOverlap)
180 self.assertEqual(afwImage.imagesOverlap(subMi2, subMi1), subregionsShouldOverlap)
182 def testMaskedImageFromImage(self):
183 w, h = 10, 20
184 dims = lsst.geom.Extent2I(w, h)
185 im, mask, var = afwImage.ImageF(dims), \
186 afwImage.Mask(dims), \
187 afwImage.ImageF(dims)
188 im.set(666)
190 maskedImage = afwImage.MaskedImageF(im, mask, var)
192 maskedImage = afwImage.makeMaskedImage(im, mask, var)
194 maskedImage = afwImage.MaskedImageF(im)
195 self.assertEqual(im.getDimensions(),
196 maskedImage.image.getDimensions())
197 self.assertEqual(im.getDimensions(),
198 maskedImage.mask.getDimensions())
199 self.assertEqual(im.getDimensions(),
200 maskedImage.variance.getDimensions())
202 self.assertEqual(maskedImage[0, 0, afwImage.LOCAL], (im[0, 0, afwImage.LOCAL], 0x0, 0.0))
204 def testMakeMaskedImageXY0(self):
205 """Test that makeMaskedImage sets XY0 correctly"""
206 im = afwImage.ImageF(200, 300)
207 xy0 = lsst.geom.PointI(10, 20)
208 im.setXY0(*xy0)
209 mi = afwImage.makeMaskedImage(im)
211 self.assertEqual(mi.image.getXY0(), xy0)
212 self.assertEqual(mi.mask.getXY0(), xy0)
213 self.assertEqual(mi.variance.getXY0(), xy0)
215 def testCopyMaskedImage(self):
216 """Test copy constructor"""
217 #
218 # shallow copy
219 #
220 mi = self.mimage.Factory(self.mimage, False)
222 val00 = self.mimage[0, 0, afwImage.LOCAL]
223 nval00 = (100, 0xff, -1) # the new value we'll set
224 self.assertNotEqual(val00, nval00)
226 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
227 mi[0, 0, afwImage.LOCAL] = nval00
229 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], nval00)
230 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00)
231 mi[0, 0, afwImage.LOCAL] = val00 # reinstate initial value
232 #
233 # deep copy
234 #
235 mi = self.mimage.Factory(self.mimage, True)
237 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
238 mi[0, 0, afwImage.LOCAL] = nval00
240 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], val00)
241 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00)
242 #
243 # Copy with change of Image type
244 #
245 mi = self.mimage.convertD()
247 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
248 mi[0, 0, afwImage.LOCAL] = nval00
250 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], val00)
251 self.assertEqual(mi[0, 0, afwImage.LOCAL], nval00)
252 #
253 # Convert from U to F
254 #
255 mi = afwImage.MaskedImageU(lsst.geom.Extent2I(10, 20))
256 val00 = (10, 0x10, 1)
257 mi.set(val00)
258 self.assertEqual(mi[0, 0, afwImage.LOCAL], val00)
260 fmi = mi.convertF()
261 self.assertEqual(fmi[0, 0, afwImage.LOCAL], val00)
263 def testAddImages(self):
264 "Test addition"
265 # add an image
266 self.mimage2 += self.mimage
268 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], (self.imgVal1 + self.imgVal2, self.EDGE,
269 self.varVal1 + self.varVal2))
271 # Add an Image<int> to a MaskedImage<int>
272 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
273 mimage_i.set(900, 0x0, 1000.0)
274 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
276 mimage_i += image_i
278 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (902, 0x0, 1000.0))
280 # add a scalar
281 self.mimage += self.imgVal1
283 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
284 (2*self.imgVal1, self.EDGE, self.varVal1))
286 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
287 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
289 # add a function
290 self.mimage.set(self.imgVal1, 0x0, 0.0)
291 self.mimage += self.function
293 for i, j in [(2, 3)]:
294 self.assertEqual(self.mimage.image[i, j, afwImage.LOCAL],
295 self.imgVal1 + self.function(i, j))
297 def testAddScaledImages(self):
298 "Test addition by a scaled MaskedImage"
299 # add an image
300 c = 10.0
301 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
302 self.mimage2.scaledPlus(c, self.mimage)
303 #
304 # Now repeat calculation using a temporary
305 #
306 tmp = self.mimage.Factory(self.mimage, True)
307 tmp *= c
308 mimage2_copy += tmp
310 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
312 def testAssignWithBBox(self):
313 """Test assign(rhs, bbox) with non-empty bbox
314 """
315 for xy0 in (lsst.geom.Point2I(*val) for val in (
316 (0, 0),
317 (-100, 120), # an arbitrary value that is off the image
318 )):
319 destMIDim = lsst.geom.Extent2I(5, 4)
320 srcMIDim = lsst.geom.Extent2I(3, 2)
321 destMI = afwImage.MaskedImageF(destMIDim)
322 destImage = destMI.image
323 destVariance = destMI.variance
324 destMask = destMI.mask
325 destMI.setXY0(xy0)
326 srcMI = makeRampImage(*srcMIDim)
327 srcMI.setXY0(55, -33) # an arbitrary value that should be ignored
328 self.assertRaises(Exception, destMI.set, srcMI) # size mismatch
330 for validMin in (lsst.geom.Point2I(*val) for val in (
331 (0, 0),
332 (2, 0),
333 (0, 1),
334 (1, 2),
335 )):
336 # None to omit the argument
337 for origin in (None, afwImage.PARENT, afwImage.LOCAL):
338 destImage[:] = -1.0
339 destVariance[:] = -1.0
340 destMask[:] = 0xFFFF
341 bbox = lsst.geom.Box2I(validMin, srcMI.getDimensions())
342 if origin != afwImage.LOCAL:
343 bbox.shift(lsst.geom.Extent2I(xy0))
344 if origin is None:
345 destMI.assign(srcMI, bbox)
346 destMIView = afwImage.MaskedImageF(destMI, bbox)
347 else:
348 destMI.assign(srcMI, bbox, origin)
349 destMIView = afwImage.MaskedImageF(destMI, bbox, origin)
350 self.assertMaskedImagesEqual(destMIView, srcMI)
351 numPixNotAssigned = (
352 destMIDim[0] * destMIDim[1]) - (srcMIDim[0] * srcMIDim[1])
353 self.assertEqual(
354 np.sum(destImage.getArray() < -0.5), numPixNotAssigned)
355 self.assertEqual(
356 np.sum(destVariance.getArray() < -0.5), numPixNotAssigned)
357 self.assertEqual(
358 np.sum(destMask.getArray() == 0xFFFF), numPixNotAssigned)
360 for badMin in (lsst.geom.Point2I(*val) + lsst.geom.Extent2I(xy0) for val in (
361 (-1, 0),
362 (3, 0),
363 (0, -1),
364 (1, 3),
365 )):
366 # None to omit the argument
367 for origin in (None, afwImage.PARENT, afwImage.LOCAL):
368 bbox = lsst.geom.Box2I(validMin, srcMI.getDimensions())
369 if origin != afwImage.LOCAL:
370 bbox.shift(lsst.geom.Extent2I(xy0))
371 if origin is None:
372 self.assertRaises(Exception, destMI.set, srcMI, bbox)
373 else:
374 self.assertRaises(
375 Exception, destMI.set, srcMI, bbox, origin)
377 def testAssignWithoutBBox(self):
378 """Test assign(rhs, [bbox]) with an empty bbox and with no bbox specified; both set all pixels
379 """
380 for xy0 in (lsst.geom.Point2I(*val) for val in (
381 (0, 0),
382 (-100, 120), # an arbitrary value that is off the image
383 )):
384 destMIDim = lsst.geom.Extent2I(5, 4)
385 destMI = afwImage.MaskedImageF(destMIDim)
386 destMI.setXY0(xy0)
387 destImage = destMI.image
388 destVariance = destMI.variance
389 destMask = destMI.mask
390 srcMI = makeRampImage(*destMIDim)
391 srcMI.setXY0(55, -33) # an arbitrary value that should be ignored
393 destImage[:] = -1.0
394 destVariance[:] = -1.0
395 destMask[:] = 0xFFFF
396 destMI.assign(srcMI)
397 self.assertMaskedImagesEqual(destMI, srcMI)
399 destImage[:] = -1.0
400 destVariance[:] = -1.0
401 destMask[:] = 0xFFFF
402 destMI.assign(srcMI, lsst.geom.Box2I())
403 self.assertMaskedImagesEqual(destMI, srcMI)
405 def testSubtractImages(self):
406 "Test subtraction"
407 # subtract an image
408 self.mimage2 -= self.mimage
409 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL],
410 (self.imgVal2 - self.imgVal1, self.EDGE, self.varVal2 + self.varVal1))
412 # Subtract an Image<int> from a MaskedImage<int>
413 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
414 mimage_i.set(900, 0x0, 1000.0)
415 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
417 mimage_i -= image_i
419 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (898, 0x0, 1000.0))
421 # subtract a scalar
422 self.mimage -= self.imgVal1
423 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL], (0.0, self.EDGE, self.varVal1))
425 def testSubtractScaledImages(self):
426 "Test subtraction by a scaled MaskedImage"
427 # subtract a scaled image
428 c = 10.0
429 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
430 self.mimage2.scaledMinus(c, self.mimage)
431 #
432 # Now repeat calculation using a temporary
433 #
434 tmp = self.mimage.Factory(self.mimage, True)
435 tmp *= c
436 mimage2_copy -= tmp
438 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
440 def testArithmeticImagesMismatch(self):
441 "Test arithmetic operations on MaskedImages of different sizes"
442 i1 = afwImage.MaskedImageF(lsst.geom.Extent2I(100, 100))
443 i1.set(100)
444 i2 = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10))
445 i2.set(10)
447 def tst1(i1, i2):
448 i1 -= i2
450 def tst2(i1, i2):
451 i1.scaledMinus(1.0, i2)
453 def tst3(i1, i2):
454 i1 += i2
456 def tst4(i1, i2):
457 i1.scaledPlus(1.0, i2)
459 def tst5(i1, i2):
460 i1 *= i2
462 def tst6(i1, i2):
463 i1.scaledMultiplies(1.0, i2)
465 def tst7(i1, i2):
466 i1 /= i2
468 def tst8(i1, i2):
469 i1.scaledDivides(1.0, i2)
471 tsts12 = [tst1, tst3, tst5, tst7]
472 for tst in tsts12:
473 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i1, i2)
475 tsts21 = [tst2, tst4, tst6, tst8]
476 for tst in tsts21:
477 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i2, i1)
479 def testMultiplyImages(self):
480 """Test multiplication"""
481 # Multiply by a MaskedImage
482 self.mimage2 *= self.mimage
484 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL],
485 (self.imgVal2*self.imgVal1, self.EDGE,
486 self.varVal2*pow(self.imgVal1, 2) + self.varVal1*pow(self.imgVal2, 2)))
488 # Divide a MaskedImage<int> by an Image<int>; this divides the variance Image<float>
489 # by an Image<int> in C++
490 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
491 mimage_i.set(900, 0x0, 1000.0)
492 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
494 mimage_i *= image_i
496 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (1800, 0x0, 4000.0))
498 # multiply by a scalar
499 self.mimage *= self.imgVal1
501 self.assertEqual(self.mimage[0, 0, afwImage.LOCAL],
502 (self.imgVal1*self.imgVal1, self.EDGE, self.varVal1*pow(self.imgVal1, 2)))
504 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
505 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
507 def testScaledMultiplyImages(self):
508 """Test multiplication by a scaled image"""
509 # Multiply by an image
510 c = 10.0
511 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
512 self.mimage2.scaledMultiplies(c, self.mimage)
513 #
514 # Now repeat calculation using a temporary
515 #
516 tmp = self.mimage.Factory(self.mimage, True)
517 tmp *= c
518 mimage2_copy *= tmp
520 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
522 def testDivideImages(self):
523 """Test division"""
524 # Divide by a MaskedImage
525 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
526 mimage2_copy /= self.mimage
528 self.assertEqual(mimage2_copy.image[0, 0, afwImage.LOCAL],
529 self.imgVal2/self.imgVal1)
530 self.assertEqual(mimage2_copy.mask[0, 0, afwImage.LOCAL], self.EDGE)
531 self.assertAlmostEqual(mimage2_copy.variance[0, 0, afwImage.LOCAL],
532 (self.varVal2*pow(self.imgVal1, 2)
533 + self.varVal1*pow(self.imgVal2, 2))/pow(self.imgVal1, 4), 10)
534 # Divide by an Image (of the same type as MaskedImage.image)
535 mimage = self.mimage2.Factory(self.mimage2, True)
536 mimage /= mimage.image
538 self.assertEqual(mimage[0, 0, afwImage.LOCAL], (self.imgVal2 / self.imgVal2, 0x0, self.varVal2))
540 # Divide by an Image (of a different type from MaskedImage.image)
541 # this isn't supported from python (it's OK in C++)
542 if False:
543 mimage = self.mimage2.Factory(self.mimage2, True)
544 image = afwImage.ImageI(mimage.getDimensions(), 1)
545 mimage /= image
547 self.assertEqual(mimage[0, 0, afwImage.LOCAL],
548 (self.imgVal2, 0x0, self.varVal2))
550 # Divide a MaskedImage<int> by an Image<int>; this divides the variance Image<float>
551 # by an Image<int> in C++
552 mimage_i = afwImage.MaskedImageI(self.mimage2.getDimensions())
553 mimage_i.set(900, 0x0, 1000.0)
554 image_i = afwImage.ImageI(mimage_i.getDimensions(), 2)
556 mimage_i /= image_i
558 self.assertEqual(mimage_i[0, 0, afwImage.LOCAL], (450, 0x0, 250.0))
560 # divide by a scalar
561 self.mimage /= self.imgVal1
563 self.assertEqual(self.mimage.image[0, 0, afwImage.LOCAL],
564 self.imgVal1/self.imgVal1)
565 self.assertEqual(self.mimage.mask[0, 0, afwImage.LOCAL], self.EDGE)
566 self.assertAlmostEqual(self.mimage.variance[0, 0, afwImage.LOCAL],
567 self.varVal1/pow(self.imgVal1, 2), 9)
569 self.assertEqual(self.mimage.mask[1, 1, afwImage.LOCAL], self.EDGE)
570 self.assertEqual(self.mimage.mask[2, 2, afwImage.LOCAL], 0x0)
572 def testScaledDivideImages(self):
573 """Test division by a scaled image"""
574 # Divide by an image
575 c = 10.0
576 mimage2_copy = self.mimage2.Factory(self.mimage2, True) # make a copy
577 self.mimage2.scaledDivides(c, self.mimage)
578 #
579 # Now repeat calculation using a temporary
580 #
581 tmp = self.mimage.Factory(self.mimage, True)
582 tmp *= c
583 mimage2_copy /= tmp
585 self.assertEqual(self.mimage2[0, 0, afwImage.LOCAL], mimage2_copy[0, 0, afwImage.LOCAL])
587 def testCopyConstructors(self):
588 dimage = afwImage.MaskedImageF(self.mimage, True) # deep copy
589 simage = afwImage.MaskedImageF(self.mimage) # shallow copy
591 self.mimage += 2 # should only change dimage
592 self.assertEqual(dimage.image[0, 0, afwImage.LOCAL], self.imgVal1)
593 self.assertEqual(simage.image[0, 0, afwImage.LOCAL], self.imgVal1 + 2)
595 def checkImgPatch12(self, img, x0, y0):
596 """Check that a patch of an image is correct; origin of patch is at (x0, y0) in full image
597 N.b. This isn't a general routine! Works only for testSubimages[12]"""
599 self.assertEqual(img[x0 - 1, y0 - 1, afwImage.LOCAL],
600 (self.imgVal1, self.EDGE, self.varVal1))
601 self.assertEqual(img[x0, y0, afwImage.LOCAL], (666, self.BAD, 0))
602 self.assertEqual(img[x0 + 3, y0, afwImage.LOCAL],
603 (self.imgVal1, 0x0, self.varVal1))
604 self.assertEqual(img[x0, y0 + 1, afwImage.LOCAL], (666, self.BAD, 0))
605 self.assertEqual(img[x0 + 3, y0 + 1, afwImage.LOCAL],
606 (self.imgVal1, 0x0, self.varVal1))
607 self.assertEqual(img[x0, y0 + 2, afwImage.LOCAL],
608 (self.imgVal1, 0x0, self.varVal1))
610 def testOrigin(self):
611 """Check that we can set and read the origin"""
613 im = afwImage.MaskedImageF(lsst.geom.ExtentI(10, 20))
614 x0 = y0 = 0
616 self.assertEqual(im.getX0(), x0)
617 self.assertEqual(im.getY0(), y0)
618 self.assertEqual(im.getXY0(), lsst.geom.PointI(x0, y0))
620 x0, y0 = 3, 5
621 im.setXY0(x0, y0)
622 self.assertEqual(im.getX0(), x0)
623 self.assertEqual(im.getY0(), y0)
624 self.assertEqual(im.getXY0(), lsst.geom.PointI(x0, y0))
626 x0, y0 = 30, 50
627 im.setXY0(lsst.geom.Point2I(x0, y0))
628 self.assertEqual(im.getX0(), x0)
629 self.assertEqual(im.getY0(), y0)
630 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0))
632 def testSubimages1(self):
633 smimage = afwImage.MaskedImageF(
634 self.mimage,
635 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)),
636 afwImage.LOCAL
637 )
639 simage = afwImage.MaskedImageF(
640 smimage,
641 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(3, 2)),
642 afwImage.LOCAL
643 )
644 self.assertEqual(simage.getX0(), 2)
645 self.assertEqual(simage.getY0(), 2) # i.e. wrt self.mimage
647 mimage2 = afwImage.MaskedImageF(simage.getDimensions())
648 mimage2.image.set(666)
649 mimage2.mask.set(self.BAD)
650 simage[:] = mimage2
652 del simage
653 del mimage2
655 self.checkImgPatch12(self.mimage, 2, 2)
656 self.checkImgPatch12(smimage, 1, 1)
658 def testSubimages2(self):
659 """Test subimages when we've played with the (x0, y0) value"""
661 self.mimage[9, 4, afwImage.LOCAL] = (888, 0x0, 0)
663 smimage = afwImage.MaskedImageF(
664 self.mimage,
665 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)),
666 afwImage.LOCAL
667 )
668 # reset origin; doesn't affect pixel coordinate systems
669 smimage.setXY0(lsst.geom.Point2I(0, 0))
671 simage = afwImage.MaskedImageF(
672 smimage, lsst.geom.Box2I(lsst.geom.Point2I(1, 1),
673 lsst.geom.Extent2I(3, 2)),
674 afwImage.LOCAL
675 )
676 self.assertEqual(simage.getX0(), 1)
677 self.assertEqual(simage.getY0(), 1)
679 mimage2 = afwImage.MaskedImageF(simage.getDimensions())
680 mimage2.set(666, self.BAD, 0.0)
681 simage[:] = mimage2
682 del simage
683 del mimage2
685 self.checkImgPatch12(self.mimage, 2, 2)
686 self.checkImgPatch12(smimage, 1, 1)
688 def checkImgPatch3(self, img, deep):
689 """Check that a patch of an image is correct; origin of patch is at (x0, y0) in full image
690 N.b. This isn't a general routine! Works only for testSubimages3"""
692 # Include deep in comparison so we can see which test fails
693 self.assertEqual(img[0, 0, afwImage.LOCAL] + (deep, ),
694 (100, 0x0, self.varVal1, deep))
695 self.assertEqual(img[10, 10, afwImage.LOCAL] + (deep, ),
696 (200, 0xf, self.varVal1, deep))
698 def testSubimages3(self):
699 """Test subimages when we've played with the (x0, y0) value"""
701 self.mimage.image[20, 20, afwImage.LOCAL] = 200
702 self.mimage.mask[20, 20, afwImage.LOCAL] = 0xf
704 for deep in (True, False):
705 mimage = self.mimage.Factory(
706 self.mimage,
707 lsst.geom.Box2I(lsst.geom.Point2I(10, 10),
708 lsst.geom.Extent2I(64, 64)),
709 afwImage.LOCAL,
710 deep)
711 mimage.setXY0(lsst.geom.Point2I(0, 0))
712 mimage2 = mimage.Factory(mimage)
714 if display:
715 afwDisplay.Display(frame=0).mtv(mimage2, title="testSubimages3")
717 self.checkImgPatch3(mimage2, deep)
719 def testSetCopiedMask(self):
720 """Check that we can set the Mask with a copied Mask"""
722 crMask = self.mimage.mask.Factory(self.mimage.mask, True)
723 msk = self.mimage.mask
724 msk |= crMask
725 del msk
727 def testVariance(self):
728 """Check that we can set the variance from the gain"""
729 gain = 2
731 var = self.mimage.variance
732 var[:] = self.mimage.image
733 var /= gain
735 def testTicket653(self):
736 """How-to-repeat for #653"""
737 # The original ticket read this file, but it doesn't reproduce for me,
738 # As I don't see how reading an exposure from disk could make a difference
739 # it's easier to just build an Image
740 if False:
741 im = afwImage.ImageF(os.path.join(
742 lsst.utils.getPackageDir("afwdata"), "med_img.fits"))
743 else:
744 im = afwImage.ImageF(lsst.geom.Extent2I(10, 10))
745 mi = afwImage.MaskedImageF(im)
746 afwImage.ExposureF(mi)
748 def testMaskedImageInitialisation(self):
749 dims = self.mimage.getDimensions()
750 factory = self.mimage.Factory
752 self.mimage.set(666)
754 del self.mimage # tempt C++ to reuse the memory
755 self.mimage = factory(dims)
756 self.assertEqual(self.mimage[10, 10, afwImage.LOCAL], (0, 0x0, 0))
758 del self.mimage
759 self.mimage = factory(lsst.geom.Extent2I(20, 20))
760 self.assertEqual(self.mimage[10, 10, afwImage.LOCAL], (0, 0x0, 0))
762 def testImageSlices(self):
763 """Test image slicing, which generate sub-images using Box2I under the covers"""
764 im = afwImage.MaskedImageF(10, 20)
765 im[4, 10] = (10, 0x2, 100)
766 im[-3:, -2:, afwImage.LOCAL] = 100
767 sim = im[1:4, 6:10]
768 nan = -666 # a real NaN != NaN so tests fail
769 sim[:] = (-1, 0x8, nan)
770 im[0:4, 0:4] = im[2:6, 8:12]
772 if display:
773 afwDisplay.Display(frame=1).mtv(im, title="testImageSlices")
775 self.assertEqual(im[0, 6, afwImage.LOCAL], (0, 0x0, 0))
776 self.assertEqual(im[6, 17, afwImage.LOCAL], (0, 0x0, 0))
777 self.assertEqual(im[7, 18, afwImage.LOCAL], (100, 0x0, 0))
778 self.assertEqual(im[9, 19, afwImage.LOCAL], (100, 0x0, 0))
779 self.assertEqual(im[1, 6, afwImage.LOCAL], (-1, 0x8, nan))
780 self.assertEqual(im[3, 9, afwImage.LOCAL], (-1, 0x8, nan))
781 self.assertEqual(im[4, 10, afwImage.LOCAL], (10, 0x2, 100))
782 self.assertEqual(im[4, 9, afwImage.LOCAL], (0, 0x0, 0))
783 self.assertEqual(im[2, 2, afwImage.LOCAL], (10, 0x2, 100))
784 self.assertEqual(im[0, 0, afwImage.LOCAL], (-1, 0x8, nan))
786 def testConversionToScalar(self):
787 """Test that even 1-pixel MaskedImages can't be converted to scalars"""
788 im = afwImage.MaskedImageF(10, 20)
790 # only single pixel images may be converted
791 self.assertRaises(TypeError, float, im)
792 # actually, can't convert (img, msk, var) to scalar
793 self.assertRaises(TypeError, float, im[0, 0])
795 def testString(self):
796 image = afwImage.MaskedImageF(100, 100)
797 self.assertIn("image=", str(image))
798 self.assertIn("mask=", str(image))
799 self.assertIn("variance=", str(image))
800 self.assertIn(str(np.zeros((100, 100), dtype=image.image.dtype)), str(image))
801 self.assertIn(str(np.zeros((100, 100), dtype=image.mask.dtype)), str(image))
802 self.assertIn(str(np.zeros((100, 100), dtype=image.variance.dtype)), str(image))
803 self.assertIn("bbox=%s"%str(image.getBBox()), str(image))
804 self.assertIn("maskPlaneDict=%s"%str(image.mask.getMaskPlaneDict()), str(image))
806 self.assertIn("MaskedImageF=(", repr(image))
809def printImg(img):
810 print("%4s " % "", end=' ')
811 for c in range(img.getWidth()):
812 print("%7d" % c, end=' ')
813 print()
815 for r in range(img.getHeight() - 1, -1, -1):
816 print("%4d " % r, end=' ')
817 for c in range(img.getWidth()):
818 print("%7.1f" % float(img[c, r, afwImage.LOCAL]), end=' ')
819 print()
822class TestMemory(lsst.utils.tests.MemoryTestCase):
823 pass
826def setup_module(module):
827 lsst.utils.tests.init()
830if __name__ == "__main__": 830 ↛ 831line 830 didn't jump to line 831, because the condition on line 830 was never true
831 lsst.utils.tests.init()
832 unittest.main()