Coverage for tests/test_image.py: 14%
499 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-14 01:39 -0700
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-14 01:39 -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 Images
25Run with:
26 python test_image.py
27or
28 pytest test_image.py
29"""
31import itertools
32import os.path
33import shutil
34import tempfile
35import unittest
37import numpy as np
39import lsst.utils
40import lsst.utils.tests
41import lsst.pex.exceptions
42import lsst.daf.base
43import lsst.geom
44import lsst.afw.image as afwImage
45import lsst.afw.math as afwMath
46from lsst.afw.fits import readMetadata
47import lsst.afw.display as afwDisplay
49try:
50 afwdataDir = lsst.utils.getPackageDir("afwdata")
51except LookupError:
52 afwdataDir = None
54try:
55 type(display)
56except NameError:
57 display = False
60def makeRampImage(width, height, imgClass=afwImage.ImageF):
61 """Make a ramp image of the specified size and image class
63 Values start from 0 at the lower left corner and increase by 1 along rows
64 """
65 im = imgClass(width, height)
66 val = 0
67 for yInd in range(height):
68 for xInd in range(width):
69 im[xInd, yInd] = val
70 val += 1
71 return im
74class ImageTestCase(lsst.utils.tests.TestCase):
75 """A test case for Image"""
77 def setUp(self):
78 np.random.seed(1)
79 self.val1, self.val2 = 10, 100
80 self.image1 = afwImage.ImageF(lsst.geom.ExtentI(100, 200))
81 self.image1.set(self.val1)
82 self.image2 = afwImage.ImageF(self.image1.getDimensions())
83 self.image2.set(self.val2)
84 self.function = afwMath.PolynomialFunction2D(2)
85 self.function.setParameters(
86 list(range(self.function.getNParameters())))
88 def tearDown(self):
89 del self.image1
90 del self.image2
91 del self.function
93 def testArrays(self):
94 for cls in (afwImage.ImageU, afwImage.ImageI, afwImage.ImageF, afwImage.ImageD):
95 image1 = cls(lsst.geom.Extent2I(5, 6))
96 array1 = image1.getArray()
97 self.assertEqual(array1.shape[0], image1.getHeight())
98 self.assertEqual(array1.shape[1], image1.getWidth())
99 image2 = cls(array1, False)
100 self.assertEqual(array1.shape[0], image2.getHeight())
101 self.assertEqual(array1.shape[1], image2.getWidth())
102 image3 = afwImage.makeImageFromArray(array1)
103 self.assertEqual(array1.shape[0], image2.getHeight())
104 self.assertEqual(array1.shape[1], image2.getWidth())
105 self.assertEqual(type(image3), cls)
106 array2 = image1.array
107 np.testing.assert_array_equal(array1, array2)
108 array1[:, :] = np.random.uniform(low=0, high=10, size=array1.shape)
109 for j in range(image1.getHeight()):
110 for i in range(image1.getWidth()):
111 self.assertEqual(image1[i, j, afwImage.LOCAL], array1[j, i])
112 self.assertEqual(image2[i, j, afwImage.LOCAL], array1[j, i])
113 array3 = np.random.uniform(low=0, high=10,
114 size=array1.shape).astype(array1.dtype)
115 image1.array[:] = array3
116 np.testing.assert_array_equal(array1, array3)
117 image1.array[2:4, 3:] = 10
118 np.testing.assert_array_equal(array1[2:4, 3:], 10)
119 array4 = image1.array.copy()
120 array4 += 5
121 image1.array += 5
122 np.testing.assert_array_equal(image1.array, array4)
123 with self.assertRaises(TypeError):
124 image1.array = None
126 def testImagesOverlap(self):
127 dim = lsst.geom.Extent2I(10, 8)
128 # a set of bounding boxes, some of which overlap each other
129 # and some of which do not, and include the full image bounding box
130 bboxes = (
131 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dim),
132 lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(3, 3)),
133 lsst.geom.Box2I(lsst.geom.Point2I(2, 2), lsst.geom.Extent2I(6, 4)),
134 lsst.geom.Box2I(lsst.geom.Point2I(4, 4), lsst.geom.Extent2I(6, 4)),
135 )
137 imageClasses = (afwImage.ImageF, afwImage.ImageD, afwImage.ImageI, afwImage.Mask)
139 for ImageClass1, ImageClass2 in itertools.product(imageClasses, imageClasses):
140 with self.subTest(ImageClass1=str(ImageClass1), ImageClass2=str(ImageClass2)):
141 image1 = ImageClass1(dim)
142 self.assertTrue(afwImage.imagesOverlap(image1, image1))
144 image2 = ImageClass2(dim)
145 self.assertFalse(afwImage.imagesOverlap(image1, image2))
146 self.assertFalse(afwImage.imagesOverlap(image2, image1))
148 for bboxa, bboxb in itertools.product(bboxes, bboxes):
149 shouldOverlap = bboxa.overlaps(bboxb)
150 with self.subTest(bboxa=bboxa, bboxb=bboxb):
151 subim1a = ImageClass1(image1, bboxa)
152 subim1b = ImageClass1(image1, bboxb)
153 self.assertEqual(afwImage.imagesOverlap(subim1a, subim1b), shouldOverlap)
154 self.assertEqual(afwImage.imagesOverlap(subim1b, subim1a), shouldOverlap)
156 subim2b = ImageClass2(image2, bboxb)
157 self.assertFalse(afwImage.imagesOverlap(subim1a, subim2b))
158 self.assertFalse(afwImage.imagesOverlap(subim2b, subim1a))
160 def testInitializeImages(self):
161 val = 666
162 for ctor in (afwImage.ImageU, afwImage.ImageI, afwImage.ImageF, afwImage.ImageD):
163 im = ctor(10, 10, val)
164 self.assertEqual(im[0, 0], val)
166 im2 = ctor(lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
167 lsst.geom.Extent2I(10, 10)), val)
168 self.assertEqual(im2[0, 0], val)
170 def testSetGetImages(self):
171 self.image1.setXY0(3, 4)
172 self.assertEqual(self.image1[0, 0, afwImage.LOCAL], self.val1)
173 self.assertEqual(self.image1[3, 4], self.val1)
174 self.image1[0, 0, afwImage.LOCAL] = 42.
175 self.assertEqual(self.image1[0, 0, afwImage.LOCAL], 42.)
176 self.assertEqual(self.image1[3, 4], 42.)
177 self.image1[3, 4] = self.val1
178 self.assertEqual(self.image1[0, 0, afwImage.LOCAL], self.val1)
179 self.assertEqual(self.image1[3, 4], self.val1)
181 def testAllocateLargeImages(self):
182 """Try to allocate a Very large image"""
183 bbox = lsst.geom.BoxI(lsst.geom.PointI(-1 << 30, -1 << 30),
184 lsst.geom.PointI(1 << 30, 1 << 30))
186 def tst():
187 afwImage.ImageF(bbox)
189 self.assertRaises(lsst.pex.exceptions.LengthError, tst)
191 def testAddImages(self):
192 self.image2 += self.image1
193 self.image1 += self.val1
195 self.assertEqual(self.image1[0, 0], 2*self.val1)
196 self.assertEqual(self.image2[0, 0], self.val1 + self.val2)
198 self.image1.set(self.val1)
199 self.image1 += self.function
201 for j in range(self.image1.getHeight()):
202 for i in range(self.image1.getWidth()):
203 self.assertEqual(self.image1[i, j],
204 self.val1 + self.function(i, j))
206 def testAssignWithBBox(self):
207 """Test assign(rhs, bbox) with non-empty bbox
208 """
209 for xy0 in (lsst.geom.Point2I(*val) for val in (
210 (0, 0),
211 (-100, 120), # an arbitrary value that is off the image
212 )):
213 destImDim = lsst.geom.Extent2I(5, 4)
214 srcImDim = lsst.geom.Extent2I(3, 2)
215 destIm = afwImage.ImageF(destImDim)
216 destIm.setXY0(xy0)
217 srcIm = makeRampImage(*srcImDim)
218 srcIm.setXY0(55, -33) # an arbitrary value that should be ignored
219 self.assertRaises(Exception, destIm.set, srcIm) # size mismatch
221 for validMin in (lsst.geom.Point2I(*val) for val in (
222 (0, 0),
223 (2, 0),
224 (0, 1),
225 (1, 2),
226 )):
227 # None to omit the argument
228 for origin in (None, afwImage.PARENT, afwImage.LOCAL):
229 destIm[:] = -1.0
230 bbox = lsst.geom.Box2I(validMin, srcIm.getDimensions())
231 if origin != afwImage.LOCAL:
232 bbox.shift(lsst.geom.Extent2I(xy0))
233 if origin is None:
234 destIm.assign(srcIm, bbox)
235 destImView = afwImage.ImageF(destIm, bbox)
236 else:
237 destIm.assign(srcIm, bbox, origin)
238 destImView = afwImage.ImageF(destIm, bbox, origin)
239 self.assertFloatsEqual(
240 destImView.getArray(), srcIm.getArray())
241 numPixNotAssigned = (destImDim[0] * destImDim[1]) - \
242 (srcImDim[0] * srcImDim[1])
243 self.assertEqual(
244 np.sum(destIm.getArray() < -0.5), numPixNotAssigned)
246 for badMin in (lsst.geom.Point2I(*val) + lsst.geom.Extent2I(xy0) for val in (
247 (-1, 0),
248 (3, 0),
249 (0, -1),
250 (1, 3),
251 )):
252 # None to omit the argument
253 for origin in (None, afwImage.PARENT, afwImage.LOCAL):
254 bbox = lsst.geom.Box2I(badMin, srcIm.getDimensions())
255 if origin != afwImage.LOCAL:
256 bbox.shift(lsst.geom.Extent2I(xy0))
257 if origin is None:
258 self.assertRaises(Exception, destIm.set, srcIm, bbox)
259 else:
260 self.assertRaises(
261 Exception, destIm.set, srcIm, bbox, origin)
263 def testAssignWithoutBBox(self):
264 """Test assign(rhs, [bbox]) with an empty bbox and with no bbox specified; both set all pixels
265 """
266 for xy0 in (lsst.geom.Point2I(*val) for val in (
267 (0, 0),
268 (-100, 120), # an arbitrary value that is off the image
269 )):
270 destImDim = lsst.geom.Extent2I(5, 4)
271 destIm = afwImage.ImageF(destImDim)
272 destIm.setXY0(xy0)
273 srcIm = makeRampImage(*destImDim)
274 srcIm.setXY0(55, -33) # an arbitrary value that should be ignored
276 destIm[:] = -1.0
277 destIm.assign(srcIm)
278 self.assertFloatsEqual(destIm.getArray(), srcIm.getArray())
280 destIm[:] = -1.0
281 destIm.assign(srcIm, lsst.geom.Box2I())
282 self.assertFloatsEqual(destIm.getArray(), srcIm.getArray())
284 def testAddScaledImages(self):
285 c = 10.0
286 self.image1.scaledPlus(c, self.image2)
288 self.assertEqual(self.image1[0, 0], self.val1 + c*self.val2)
290 def testSubtractImages(self):
291 self.image2 -= self.image1
292 self.image1 -= self.val1
294 self.assertEqual(self.image1[0, 0], 0)
295 self.assertEqual(self.image2[0, 0], self.val2 - self.val1)
297 self.image1.set(self.val1)
298 self.image1 -= self.function
300 for j in range(self.image1.getHeight()):
301 for i in range(self.image1.getWidth()):
302 self.assertEqual(self.image1[i, j],
303 self.val1 - self.function(i, j))
305 def testArithmeticImagesMismatch(self):
306 "Test arithmetic operations on Images of different sizes"
307 i1 = afwImage.ImageF(100, 100)
308 i1.set(100)
309 i2 = afwImage.ImageF(10, 10)
310 i2.set(10)
312 def tst1(i1, i2):
313 i1 -= i2
315 def tst2(i1, i2):
316 i1.scaledMinus(1.0, i2)
318 def tst3(i1, i2):
319 i1 += i2
321 def tst4(i1, i2):
322 i1.scaledPlus(1.0, i2)
324 def tst5(i1, i2):
325 i1 *= i2
327 def tst6(i1, i2):
328 i1.scaledMultiplies(1.0, i2)
330 def tst7(i1, i2):
331 i1 /= i2
333 def tst8(i1, i2):
334 i1.scaledDivides(1.0, i2)
336 tsts12 = [tst1, tst3, tst5, tst7]
337 for tst in tsts12:
338 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i1, i2)
340 tsts21 = [tst2, tst4, tst6, tst8]
341 for tst in tsts21:
342 self.assertRaises(lsst.pex.exceptions.LengthError, tst, i2, i1)
344 def testSubtractScaledImages(self):
345 c = 10.0
346 self.image1.scaledMinus(c, self.image2)
348 self.assertEqual(self.image1[0, 0], self.val1 - c*self.val2)
350 def testMultiplyImages(self):
351 self.image2 *= self.image1
352 self.image1 *= self.val1
354 self.assertEqual(self.image1[0, 0], self.val1*self.val1)
355 self.assertEqual(self.image2[0, 0], self.val2*self.val1)
357 def testMultiplesScaledImages(self):
358 c = 10.0
359 self.image1.scaledMultiplies(c, self.image2)
361 self.assertEqual(self.image1[0, 0], self.val1 * c*self.val2)
363 def testDivideImages(self):
364 self.image2 /= self.image1
365 self.image1 /= self.val1
367 self.assertEqual(self.image1[0, 0], 1)
368 self.assertEqual(self.image2[0, 0], self.val2/self.val1)
370 def testDividesScaledImages(self):
371 c = 10.0
372 self.image1.scaledDivides(c, self.image2)
374 self.assertAlmostEqual(self.image1[0, 0], self.val1/(c*self.val2))
376 def testCopyConstructors(self):
377 dimage = afwImage.ImageF(self.image1, True) # deep copy
378 simage = afwImage.ImageF(self.image1) # shallow copy
380 self.image1 += 2 # should only change dimage
381 self.assertEqual(dimage[0, 0], self.val1)
382 self.assertEqual(simage[0, 0], self.val1 + 2)
384 def testGeneralisedCopyConstructors(self):
385 # these are generalised (templated) copy constructors in C++
386 imageU = self.image1.convertU()
387 imageF = imageU.convertF()
388 imageD = imageF.convertD()
390 self.assertEqual(imageU[0, 0], self.val1)
391 self.assertEqual(imageF[0, 0], self.val1)
392 self.assertEqual(imageD[0, 0], self.val1)
394 def checkImgPatch(self, img, x0=0, y0=0):
395 """Check that a patch of an image is correct; origin of patch is at (x0, y0)"""
397 self.assertEqual(img[x0 - 1, y0 - 1, afwImage.LOCAL], self.val1)
398 self.assertEqual(img[x0, y0, afwImage.LOCAL], 666)
399 self.assertEqual(img[x0 + 3, y0, afwImage.LOCAL], self.val1)
400 self.assertEqual(img[x0, y0 + 1, afwImage.LOCAL], 666)
401 self.assertEqual(img[x0 + 3, y0 + 1, afwImage.LOCAL], self.val1)
402 self.assertEqual(img[x0, y0 + 2, afwImage.LOCAL], self.val1)
404 def testOrigin(self):
405 """Check that we can set and read the origin"""
407 im = afwImage.ImageF(10, 20)
408 x0 = y0 = 0
410 self.assertEqual(im.getX0(), x0)
411 self.assertEqual(im.getY0(), y0)
412 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0))
414 x0, y0 = 3, 5
415 im.setXY0(x0, y0)
416 self.assertEqual(im.getX0(), x0)
417 self.assertEqual(im.getY0(), y0)
418 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0))
420 x0, y0 = 30, 50
421 im.setXY0(lsst.geom.Point2I(x0, y0))
422 self.assertEqual(im.getX0(), x0)
423 self.assertEqual(im.getY0(), y0)
424 self.assertEqual(im.getXY0(), lsst.geom.Point2I(x0, y0))
426 def testSubimages(self):
427 simage1 = afwImage.ImageF(
428 self.image1,
429 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)),
430 afwImage.LOCAL)
432 simage = afwImage.ImageF(
433 simage1,
434 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(3, 2)),
435 afwImage.LOCAL
436 )
437 self.assertEqual(simage.getX0(), 2)
438 self.assertEqual(simage.getY0(), 2) # i.e. wrt self.image1
440 image2 = afwImage.ImageF(simage.getDimensions())
441 image2.set(666)
442 simage[:] = image2
443 del simage
444 del image2
446 self.checkImgPatch(self.image1, 2, 2)
447 self.checkImgPatch(simage1, 1, 1)
449 def testSubimages2(self):
450 """Test subimages when we've played with the (x0, y0) value"""
452 self.image1[9, 4] = 888
454 simage1 = afwImage.ImageF(
455 self.image1,
456 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(10, 5)),
457 afwImage.LOCAL
458 )
459 # reset origin; doesn't affect pixel coordinate systems
460 simage1.setXY0(lsst.geom.Point2I(0, 0))
462 simage = afwImage.ImageF(
463 simage1,
464 lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(3, 2)),
465 afwImage.LOCAL
466 )
467 self.assertEqual(simage.getX0(), 1)
468 self.assertEqual(simage.getY0(), 1)
470 image2 = afwImage.ImageF(simage.getDimensions())
471 image2.set(666)
472 simage[:] = image2
473 del simage
474 del image2
476 self.checkImgPatch(self.image1, 2, 2)
477 self.checkImgPatch(simage1, 1, 1)
479 def testBadSubimages(self):
480 def tst():
481 afwImage.ImageF(
482 self.image1,
483 lsst.geom.Box2I(lsst.geom.Point2I(1, -1), lsst.geom.Extent2I(10, 5)),
484 afwImage.LOCAL
485 )
487 self.assertRaises(lsst.pex.exceptions.LengthError, tst)
489 def testImageInitialisation(self):
490 dims = self.image1.getDimensions()
491 factory = self.image1.Factory
493 self.image1.set(666)
495 del self.image1 # tempt C++ to reuse the memory
496 self.image1 = factory(dims)
497 self.assertEqual(self.image1[10, 10], 0)
499 def testImageSlicesOrigin(self):
500 """Test image slicing, which generate sub-images using Box2I under the covers"""
501 im = afwImage.ImageF(10, 20)
502 im.setXY0(50, 100)
503 im[54, 110] = 10
504 im[-3:, -2:, afwImage.LOCAL] = 100
505 im[-2, -2, afwImage.LOCAL] = -10
506 sim = im[51:54, 106:110]
507 sim[:] = -1
508 im[50:54, 100:104] = im[2:6, 8:12, afwImage.LOCAL]
510 if display:
511 afwDisplay.Display(frame=1).mtv(im, title="testImageSlicesOrigin")
513 self.assertEqual(im[0, 6, afwImage.LOCAL], 0)
514 self.assertEqual(im[6, 17, afwImage.LOCAL], 0)
515 self.assertEqual(im[7, 18, afwImage.LOCAL], 100)
516 self.assertEqual(im[9, 19, afwImage.LOCAL], 100)
517 self.assertEqual(im[8, 18, afwImage.LOCAL], -10)
518 self.assertEqual(im[1, 6, afwImage.LOCAL], -1)
519 self.assertEqual(im[3, 9, afwImage.LOCAL], -1)
520 self.assertEqual(im[4, 10, afwImage.LOCAL], 10)
521 self.assertEqual(im[4, 9, afwImage.LOCAL], 0)
522 self.assertEqual(im[2, 2, afwImage.LOCAL], 10)
523 self.assertEqual(im[0, 0, afwImage.LOCAL], -1)
525 def testImageSliceFromBox(self):
526 """Test using a Box2I to index an Image"""
527 im = afwImage.ImageF(10, 20)
528 bbox = lsst.geom.BoxI(lsst.geom.PointI(1, 3), lsst.geom.PointI(6, 9))
529 im[bbox] = -1
531 if display:
532 afwDisplay.Display(frame=0).mtv(im, title="testImageSliceFromBox")
534 self.assertEqual(im[0, 6], 0)
535 self.assertEqual(im[1, 6], -1)
536 self.assertEqual(im[3, 9], -1)
538 def testImageSliceFromBoxOrigin(self):
539 """Test using a Box2I to index an Image"""
540 im = afwImage.ImageF(10, 20)
541 im.setXY0(50, 100)
542 bbox = lsst.geom.BoxI(lsst.geom.PointI(51, 103), lsst.geom.ExtentI(6, 7))
543 im[bbox] = -1
545 if display:
546 afwDisplay.Display(frame=2).mtv(im, title="testImageSliceFromBoxOrigin")
548 self.assertEqual(im[0, 6, afwImage.LOCAL], 0)
549 self.assertEqual(im[1, 6, afwImage.LOCAL], -1)
550 self.assertEqual(im[3, 9, afwImage.LOCAL], -1)
552 def testClone(self):
553 """Test that clone works properly"""
554 im = afwImage.ImageF(10, 20)
555 im[0, 0] = 100
557 im2 = im.clone() # check that clone with no arguments makes a deep copy
558 self.assertEqual(im.getDimensions(), im2.getDimensions())
559 self.assertEqual(im[0, 0], im2[0, 0])
560 im2[0, 0] += 100
561 self.assertNotEqual(im[0, 0], im2[0, 0]) # so it's a deep copy
563 im2 = im[0:3, 0:5].clone() # check that we can slice-then-clone
564 self.assertEqual(im2.getDimensions(), lsst.geom.ExtentI(3, 5))
565 self.assertEqual(im[0, 0], im2[0, 0])
566 im2[0, 0] += 10
567 self.assertNotEqual(float(im[0, 0]), float(im2[0, 0]))
569 def testString(self):
570 imageF = afwImage.ImageF(100, 100)
571 imageDSmall = afwImage.ImageD(2, 2)
572 imageISmall = afwImage.ImageI(2, 2)
573 imageU = afwImage.ImageU(100, 100)
575 # NumPy's string representation varies depending on the size of the
576 # array; we test both large and small.
577 self.assertIn(str(np.zeros((100, 100), dtype=imageF.dtype)), str(imageF))
578 self.assertIn(f"bbox={imageF.getBBox()}", str(imageF))
580 self.assertIn(str(np.zeros((2, 2), dtype=imageDSmall.dtype)), str(imageDSmall))
581 self.assertIn(str(np.zeros((2, 2), dtype=imageISmall.dtype)), str(imageISmall))
583 self.assertIn("ImageF=", repr(imageF))
584 self.assertIn("ImageU=", repr(imageU))
587class DecoratedImageTestCase(lsst.utils.tests.TestCase):
588 """A test case for DecoratedImage"""
590 def setUp(self):
591 np.random.seed(1)
592 self.val1, self.val2 = 10, 100
593 self.width, self.height = 200, 100
594 self.dimage1 = afwImage.DecoratedImageF(
595 lsst.geom.Extent2I(self.width, self.height)
596 )
597 self.dimage1.image.set(self.val1)
599 if afwdataDir is not None:
600 self.fileForMetadata = os.path.join(
601 afwdataDir, "data", "small_MI.fits")
602 self.trueMetadata = {"RELHUMID": 10.69}
604 def tearDown(self):
605 del self.dimage1
607 def testCreateDecoratedImage(self):
608 self.assertEqual(self.dimage1.getWidth(), self.width)
609 self.assertEqual(self.dimage1.getHeight(), self.height)
610 self.assertEqual(self.dimage1.image[0, 0], self.val1)
612 def testCreateDecoratedImageFromImage(self):
613 image = afwImage.ImageF(lsst.geom.Extent2I(self.width, self.height))
614 image[:] = self.dimage1.image
616 dimage = afwImage.DecoratedImageF(image)
617 self.assertEqual(dimage.getWidth(), self.width)
618 self.assertEqual(dimage.getHeight(), self.height)
619 self.assertEqual(dimage.image[0, 0], self.val1)
621 def testCopyConstructors(self):
622 dimage = afwImage.DecoratedImageF(self.dimage1, True) # deep copy
623 self.dimage1.image[0, 0] = 1 + 2*self.val1
624 self.assertEqual(dimage.image[0, 0], self.val1)
626 dimage = afwImage.DecoratedImageF(self.dimage1) # shallow copy
627 self.dimage1.image[0, 0] = 1 + 2*self.val1
628 self.assertNotEqual(dimage.image[0, 0], self.val1)
630 @unittest.skipIf(afwdataDir is None, "afwdata not setup")
631 def testReadFits(self):
632 """Test reading FITS files"""
634 hdus = {}
635 hdus["img"] = 1 # an S16 fits HDU
636 hdus["msk"] = 2 # an U8 fits HDU
637 hdus["var"] = 3 # an F32 fits HDU
639 # read as unsigned short
640 imgU = afwImage.DecoratedImageU(self.fileForMetadata, hdus["img"], allowUnsafe=True)
641 # read as float
642 imgF = afwImage.DecoratedImageF(self.fileForMetadata, hdus["img"])
644 self.assertEqual(imgU.getHeight(), 256)
645 self.assertEqual(imgF.image.getWidth(), 256)
646 self.assertEqual(imgU.image[0, 0, afwImage.LOCAL], imgF.image[0, 0, afwImage.LOCAL])
647 #
648 # Check the metadata
649 #
650 meta = self.trueMetadata
651 for k in meta.keys():
652 self.assertEqual(imgU.getMetadata().getAsDouble(k), meta[k])
653 self.assertEqual(imgF.getMetadata().getAsDouble(k), meta[k])
654 #
655 # Read an F32 image
656 #
657 # read as unsigned short
658 varU = afwImage.DecoratedImageF(self.fileForMetadata, hdus["var"])
659 # read as float
660 varF = afwImage.DecoratedImageF(self.fileForMetadata, hdus["var"])
662 self.assertEqual(varU.getHeight(), 256)
663 self.assertEqual(varF.image.getWidth(), 256)
664 self.assertEqual(varU.image[0, 0, afwImage.LOCAL], varF.image[0, 0, afwImage.LOCAL])
665 #
666 # Read a char image
667 #
668 maskImg = afwImage.DecoratedImageU(
669 self.fileForMetadata, hdus["msk"]).image # read a char file
671 self.assertEqual(maskImg.getHeight(), 256)
672 self.assertEqual(maskImg.getWidth(), 256)
673 self.assertEqual(maskImg[0, 0, afwImage.LOCAL], 1)
674 #
675 # Read a U16 image
676 #
677 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
678 imgU.writeFits(tmpFile)
680 afwImage.DecoratedImageF(tmpFile) # read as unsigned short
682 @unittest.skipIf(afwdataDir is None, "afwdata not setup")
683 def testWriteFits(self):
684 """Test writing FITS files"""
686 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
687 if self.fileForMetadata:
688 imgU = afwImage.DecoratedImageF(self.fileForMetadata)
689 else:
690 imgU = afwImage.DecoratedImageF()
692 self.dimage1.writeFits(tmpFile, imgU.getMetadata())
693 #
694 # Read it back
695 #
696 rimage = afwImage.DecoratedImageF(tmpFile)
698 self.assertEqual(self.dimage1.image[0, 0, afwImage.LOCAL],
699 rimage.image[0, 0, afwImage.LOCAL])
700 #
701 # Check that we wrote (and read) the metadata successfully
702 if self.fileForMetadata:
703 meta = self.trueMetadata
704 for k in meta.keys():
705 self.assertEqual(
706 rimage.getMetadata().getAsDouble(k), meta[k])
708 def testReadWriteXY0(self):
709 """Test that we read and write (X0, Y0) correctly"""
710 im = afwImage.ImageF(lsst.geom.Extent2I(10, 20))
712 x0, y0 = 1, 2
713 im.setXY0(x0, y0)
714 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
715 im.writeFits(tmpFile)
717 im2 = im.Factory(tmpFile)
719 self.assertEqual(im2.getX0(), x0)
720 self.assertEqual(im2.getY0(), y0)
722 @unittest.skipIf(afwdataDir is None, "afwdata not setup")
723 def testReadMetadata(self):
724 im = afwImage.DecoratedImageF(self.fileForMetadata)
726 meta = readMetadata(self.fileForMetadata)
727 self.assertIn("NAXIS1", meta.names())
728 self.assertEqual(im.getWidth(), meta.getScalar("NAXIS1"))
729 self.assertEqual(im.getHeight(), meta.getScalar("NAXIS2"))
731 def testTicket1040(self):
732 """ How to repeat from #1040"""
733 image = afwImage.ImageD(lsst.geom.Extent2I(6, 6))
734 image[2, 2] = 100
736 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 1), lsst.geom.Extent2I(5, 5))
737 subImage = image.Factory(image, bbox)
738 subImageF = subImage.convertFloat()
740 if display:
741 afwDisplay.Display(frame=0).mtv(subImage, title="subImage")
742 afwDisplay.Display(frame=1).mtv(subImageF, title="converted subImage")
744 self.assertEqual(subImage[1, 1, afwImage.LOCAL], subImageF[1, 1, afwImage.LOCAL])
746 def testDM882(self):
747 """Test that we can write a dotted header unit to a FITS file. See DM-882."""
748 self.dimage1.getMetadata().add("A.B.C.D", 12345)
749 tempdir = tempfile.mkdtemp()
750 testfile = os.path.join(tempdir, "test.fits")
751 try:
752 self.dimage1.writeFits(testfile)
753 meta = readMetadata(testfile)
754 self.assertEqual(meta.getScalar("A.B.C.D"), 12345)
755 finally:
756 shutil.rmtree(tempdir, ignore_errors=True)
758 def testLargeImage(self):
759 """Test that creating an extremely large image raises, rather than segfaulting. DM-89, -527."""
760 for imtype in (afwImage.ImageD, afwImage.ImageF, afwImage.ImageI, afwImage.ImageU):
761 self.assertRaises(lsst.pex.exceptions.LengthError,
762 imtype, 60000, 60000)
765def printImg(img):
766 print("%4s " % "", end=' ')
767 for c in range(img.getWidth()):
768 print("%7d" % c, end=' ')
769 print()
771 for r in range(img.getHeight() - 1, -1, -1):
772 print("%4d " % r, end=' ')
773 for c in range(img.getWidth()):
774 print("%7.1f" % float(img[c, r, afwImage.LOCAL]), end=' ')
775 print()
778class TestMemory(lsst.utils.tests.MemoryTestCase):
779 pass
782def setup_module(module):
783 lsst.utils.tests.init()
786if __name__ == "__main__": 786 ↛ 787line 786 didn't jump to line 787 because the condition on line 786 was never true
787 lsst.utils.tests.init()
788 unittest.main()