Coverage for tests/test_heavyFootprint.py: 16%
191 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-04 02:32 -0700
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-04 02:32 -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 HeavyFootprints
25Run with:
26 python test_heavyFootprint.py
27or
28 pytest test_heavyFootprint.py
29"""
31import os
32import unittest
34import numpy as np
36import lsst.utils.tests
37import lsst.geom
38import lsst.afw.image as afwImage
39import lsst.afw.detection as afwDetect
40import lsst.afw.geom as afwGeom
41import lsst.afw.display as afwDisplay
42from lsst.log import Log
44Log.getLogger("lsst.afw.image.Mask").setLevel(Log.INFO)
45afwDisplay.setDefaultMaskTransparency(75)
47try:
48 type(display)
49except NameError:
50 display = False
53class HeavyFootprintTestCase(lsst.utils.tests.TestCase):
54 """A test case for HeavyFootprint"""
56 def setUp(self):
57 self.mi = afwImage.MaskedImageF(20, 10)
58 self.objectPixelVal = (10, 0x1, 100)
60 spanList = []
61 for y, x0, x1 in [(2, 10, 13),
62 (3, 11, 14)]:
63 spanList.append(afwGeom.Span(y, x0, x1))
65 for x in range(x0, x1 + 1):
66 self.mi[x, y, afwImage.LOCAL] = self.objectPixelVal
67 self.foot = afwDetect.Footprint(afwGeom.SpanSet(spanList))
69 def tearDown(self):
70 del self.foot
71 del self.mi
73 def testCreate(self):
74 """Check that we can create a HeavyFootprint"""
76 imi = self.mi.Factory(self.mi, True) # copy of input image
78 hfoot = afwDetect.makeHeavyFootprint(self.foot, self.mi)
79 # check we can call a base-class method
80 self.assertTrue(hfoot.isHeavy())
81 #
82 # Check we didn't modify the input image
83 #
84 self.assertFloatsEqual(
85 self.mi.getImage().getArray(), imi.getImage().getArray())
87 omi = self.mi.Factory(self.mi.getDimensions())
88 omi.set((1, 0x4, 0.1))
89 hfoot.insert(omi)
91 if display:
92 afwDisplay.Display(frame=0).mtv(imi, title="testCreate input")
93 afwDisplay.Display(frame=1).mtv(omi, title="testCreate output")
95 for s in self.foot.getSpans():
96 y = s.getY()
97 for x in range(s.getX0(), s.getX1() + 1):
98 self.assertEqual(imi[x, y, afwImage.LOCAL], omi[x, y, afwImage.LOCAL])
100 # Check that we can call getImageArray(), etc
101 arr = hfoot.getImageArray()
102 # Check that it's iterable
103 for x in arr:
104 pass
105 arr = hfoot.getMaskArray()
106 for x in arr:
107 pass
108 arr = hfoot.getVarianceArray()
109 # Check that it's iterable
110 for x in arr:
111 pass
113 def testSetFootprint(self):
114 """Check that we can create a HeavyFootprint and set the pixels under it"""
116 ctrl = afwDetect.HeavyFootprintCtrl()
117 # clear the pixels in the Footprint
118 ctrl.setModifySource(afwDetect.HeavyFootprintCtrl.SET)
119 ctrl.setMaskVal(self.objectPixelVal[1])
121 afwDetect.makeHeavyFootprint(self.foot, self.mi, ctrl)
122 #
123 # Check that we cleared all the pixels
124 #
125 self.assertEqual(np.min(self.mi.getImage().getArray()), 0.0)
126 self.assertEqual(np.max(self.mi.getImage().getArray()), 0.0)
127 self.assertEqual(np.min(self.mi.getMask().getArray()), 0.0)
128 self.assertEqual(np.max(self.mi.getMask().getArray()), 0.0)
129 self.assertEqual(np.min(self.mi.getVariance().getArray()), 0.0)
130 self.assertEqual(np.max(self.mi.getVariance().getArray()), 0.0)
132 def testMakeHeavy(self):
133 """Test that we can make a FootprintSet heavy"""
134 fs = afwDetect.FootprintSet(self.mi, afwDetect.Threshold(1))
136 ctrl = afwDetect.HeavyFootprintCtrl(afwDetect.HeavyFootprintCtrl.NONE)
137 fs.makeHeavy(self.mi, ctrl)
139 omi = self.mi.Factory(self.mi.getDimensions())
141 for foot in fs.getFootprints():
142 foot.insert(omi)
144 for foot in fs.getFootprints():
145 foot.insert(omi)
147 if display:
148 afwDisplay.Display(frame=0).mtv(self.mi, title="testMakeHeavy input")
149 afwDisplay.Display(frame=1).mtv(omi, title="testMakeHeavy output")
151 self.assertFloatsEqual(
152 self.mi.getImage().getArray(), omi.getImage().getArray())
154 def testXY0(self):
155 """Test that inserting a HeavyFootprint obeys XY0"""
156 fs = afwDetect.FootprintSet(self.mi, afwDetect.Threshold(1))
158 fs.makeHeavy(self.mi)
160 bbox = lsst.geom.BoxI(lsst.geom.PointI(9, 1), lsst.geom.ExtentI(7, 4))
161 omi = self.mi.Factory(self.mi, bbox, afwImage.LOCAL, True)
162 omi.set((0, 0x0, 0))
164 for foot in fs.getFootprints():
165 foot.insert(omi)
167 if display:
168 afwDisplay.Display(frame=0).mtv(self.mi, title="testXY0 input")
169 afwDisplay.Display(frame=1).mtv(omi, title="testXY0 sub")
171 submi = self.mi.Factory(self.mi, bbox, afwImage.LOCAL)
172 self.assertFloatsEqual(submi.getImage().getArray(),
173 omi.getImage().getArray())
175 def testMergeHeavyFootprints(self):
176 mi = afwImage.MaskedImageF(20, 10)
177 objectPixelVal = (42, 0x9, 400)
179 spanList = []
180 for y, x0, x1 in [(1, 9, 12),
181 (2, 12, 13),
182 (3, 11, 15)]:
183 spanList.append(afwGeom.Span(y, x0, x1))
184 for x in range(x0, x1 + 1):
185 mi[x, y, afwImage.LOCAL] = objectPixelVal
187 foot = afwDetect.Footprint(afwGeom.SpanSet(spanList))
189 hfoot1 = afwDetect.makeHeavyFootprint(self.foot, self.mi)
190 hfoot2 = afwDetect.makeHeavyFootprint(foot, mi)
192 hsum = afwDetect.mergeHeavyFootprints(hfoot1, hfoot2)
194 bb = hsum.getBBox()
195 self.assertEqual(bb.getMinX(), 9)
196 self.assertEqual(bb.getMaxX(), 15)
197 self.assertEqual(bb.getMinY(), 1)
198 self.assertEqual(bb.getMaxY(), 3)
200 msum = afwImage.MaskedImageF(20, 10)
201 hsum.insert(msum)
203 sa = msum.getImage().getArray()
205 self.assertFloatsEqual(sa[1, 9:13], objectPixelVal[0])
206 self.assertFloatsEqual(
207 sa[2, 12:14], objectPixelVal[0] + self.objectPixelVal[0])
208 self.assertFloatsEqual(sa[2, 10:12], self.objectPixelVal[0])
210 sv = msum.getVariance().getArray()
212 self.assertFloatsEqual(sv[1, 9:13], objectPixelVal[2])
213 self.assertFloatsEqual(
214 sv[2, 12:14], objectPixelVal[2] + self.objectPixelVal[2])
215 self.assertFloatsEqual(sv[2, 10:12], self.objectPixelVal[2])
217 sm = msum.getMask().getArray()
219 self.assertFloatsEqual(sm[1, 9:13], objectPixelVal[1])
220 self.assertFloatsEqual(
221 sm[2, 12:14], objectPixelVal[1] | self.objectPixelVal[1])
222 self.assertFloatsEqual(sm[2, 10:12], self.objectPixelVal[1])
224 if False:
225 import matplotlib
226 matplotlib.use('Agg')
227 import pylab as plt
228 im1 = afwImage.ImageF(bb)
229 hfoot1.insert(im1)
230 im2 = afwImage.ImageF(bb)
231 hfoot2.insert(im2)
232 im3 = afwImage.ImageF(bb)
233 hsum.insert(im3)
234 plt.clf()
235 plt.subplot(1, 3, 1)
236 plt.imshow(im1.getArray(), interpolation='nearest', origin='lower')
237 plt.subplot(1, 3, 2)
238 plt.imshow(im2.getArray(), interpolation='nearest', origin='lower')
239 plt.subplot(1, 3, 3)
240 plt.imshow(im3.getArray(), interpolation='nearest', origin='lower')
241 plt.savefig('merge.png')
243 def testFitsPersistence(self):
244 heavy1 = afwDetect.HeavyFootprintF(self.foot)
245 heavy1.getImageArray()[:] = \
246 np.random.randn(self.foot.getArea()).astype(np.float32)
247 heavy1.getMaskArray()[:] = \
248 np.random.randint(low=0, high=2, size=self.foot.getArea()).astype(np.uint16)
249 heavy1.getVarianceArray()[:] = \
250 np.random.randn(self.foot.getArea()).astype(np.float32)
251 with lsst.utils.tests.getTempFilePath(".fits") as filename:
252 heavy1.writeFits(filename)
253 heavy2 = afwDetect.HeavyFootprintF.readFits(filename)
254 self.assertEqual(heavy1.getArea(), heavy2.getArea())
255 self.assertEqual(list(heavy1.getSpans()), list(heavy2.getSpans()))
256 self.assertEqual(list(heavy1.getPeaks()), list(heavy2.getPeaks()))
257 self.assertFloatsAlmostEqual(heavy1.getImageArray(),
258 heavy2.getImageArray(), rtol=0.0, atol=0.0)
259 self.assertFloatsAlmostEqual(heavy1.getMaskArray(),
260 heavy2.getMaskArray(), rtol=0.0, atol=0.0)
261 self.assertFloatsAlmostEqual(heavy1.getVarianceArray(),
262 heavy2.getVarianceArray(), rtol=0.0, atol=0.0)
264 def testLegacyHeavyFootprintMaskLoading(self):
265 filename = os.path.join(os.path.split(__file__)[0],
266 "data", "legacyHeavyFootprint.fits")
267 heavyFp = afwDetect.HeavyFootprintF.readFits(filename)
268 self.assertTrue(all(heavyFp.getMaskArray() == 32))
269 self.assertTrue(heavyFp.getMaskArray().dtype == afwImage.MaskPixel)
271 def testDot(self):
272 """Test HeavyFootprint::dot"""
273 size = 20, 20
274 for xOffset, yOffset in [(0, 0), (0, 3), (3, 0), (2, 2)]:
275 mi1 = afwImage.MaskedImageF(*size)
276 mi2 = afwImage.MaskedImageF(*size)
277 mi1.set(0)
278 mi2.set(0)
280 spanList1 = []
281 spanList2 = []
282 for y, x0, x1 in [(5, 3, 7),
283 (6, 3, 4),
284 (6, 6, 7),
285 (7, 3, 7), ]:
286 spanList1.append(afwGeom.Span(y, x0, x1))
287 spanList2.append(afwGeom.Span(y + yOffset, x0 + xOffset,
288 x1 + xOffset))
289 for x in range(x0, x1 + 1):
290 value = (x + y, 0, 1.0)
291 mi1[x, y, afwImage.LOCAL] = value
292 mi2[x + xOffset, y + yOffset, afwImage.LOCAL] = value
294 fp1 = afwDetect.Footprint(afwGeom.SpanSet(spanList1))
295 fp2 = afwDetect.Footprint(afwGeom.SpanSet(spanList2))
297 hfp1 = afwDetect.makeHeavyFootprint(fp1, mi1)
298 hfp2 = afwDetect.makeHeavyFootprint(fp2, mi2)
300 dot = np.vdot(mi1.getImage().getArray(), mi2.getImage().getArray())
301 self.assertEqual(hfp1.dot(hfp2), dot)
302 self.assertEqual(hfp2.dot(hfp1), dot)
304 def testAddSubtract(self):
305 """Test that we can add and subtract a HeavyFootprint."""
306 fs = afwDetect.FootprintSet(self.mi, afwDetect.Threshold(1))
308 fs.makeHeavy(self.mi)
310 bbox = lsst.geom.BoxI(lsst.geom.PointI(9, 1), lsst.geom.ExtentI(7, 4))
311 compImage = self.mi.Factory(self.mi, bbox, afwImage.LOCAL, True)
312 compImage.set((10, 0x0, 0))
314 blankImage = compImage.clone()
315 blankImage.set((0, 0x0, 0))
317 testImage = compImage.clone()
319 for foot in fs.getFootprints():
320 # Add via addTo
321 foot.addTo(testImage.image)
323 # Add by using a temporary image and insert
324 foot.insert(blankImage.image)
325 inserted = blankImage.image.array > 0
326 compImage.image.array[inserted] += blankImage.image.array[inserted]
328 np.testing.assert_array_almost_equal(testImage.image.array,
329 compImage.image.array)
331 # Subtract via subtractFrom
332 foot.subtractFrom(testImage.image)
334 compImage.image.array[inserted] -= blankImage.image.array[inserted]
336 np.testing.assert_array_almost_equal(testImage.image.array,
337 compImage.image.array)
339 blankImage.set((0, 0x0, 0))
342class TestMemory(lsst.utils.tests.MemoryTestCase):
343 pass
346def setup_module(module):
347 lsst.utils.tests.init()
350if __name__ == "__main__": 350 ↛ 351line 350 didn't jump to line 351, because the condition on line 350 was never true
351 lsst.utils.tests.init()
352 unittest.main()