Coverage for tests/test_footprint1.py: 9%
820 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-09 03:27 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-09 03:27 -0800
1# This file is part of afw.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""
23Tests for Footprints, and FootprintSets
24"""
26import math
27import sys
28import unittest
29import os
31import numpy as np
33import lsst.utils.tests
34import lsst.geom
35import lsst.afw.geom as afwGeom
36import lsst.afw.geom.ellipses as afwGeomEllipses
37import lsst.afw.image as afwImage
38import lsst.afw.math as afwMath
39import lsst.afw.detection as afwDetect
40import lsst.afw.detection.utils as afwDetectUtils
41import lsst.afw.display as afwDisplay
42import lsst.pex.exceptions as pexExcept
44try:
45 type(display)
46except NameError:
47 display = False
49testPath = os.path.abspath(os.path.dirname(__file__))
52def toString(*args):
53 """toString written in python"""
54 if len(args) == 1:
55 args = args[0]
57 y, x0, x1 = args
58 return f"{y}: {x0}..{x1}"
61class Object:
63 def __init__(self, val, spans):
64 self.val = val
65 self.spans = spans
67 def __str__(self):
68 return ", ".join([str(s) for s in self.spans])
70 def insert(self, im):
71 """Insert self into an image"""
72 for sp in self.spans:
73 y, x0, x1 = sp
74 for x in range(x0, x1 + 1):
75 im[x, y, afwImage.LOCAL] = self.val
77 def __eq__(self, other):
78 for osp, sp in zip(other.getSpans(), self.spans):
79 if osp.toString() != toString(sp):
80 return False
82 return True
85class SpanTestCase(unittest.TestCase):
87 def testLessThan(self):
88 span1 = afwGeom.Span(42, 0, 100)
89 span2 = afwGeom.Span(41, 0, 100)
90 span3 = afwGeom.Span(43, 0, 100)
91 span4 = afwGeom.Span(42, -100, 100)
92 span5 = afwGeom.Span(42, 100, 200)
93 span6 = afwGeom.Span(42, 0, 10)
94 span7 = afwGeom.Span(42, 0, 200)
95 span8 = afwGeom.Span(42, 0, 100)
97 # Cannot use assertLess and friends here
98 # because Span only has operator <
99 def assertOrder(x1, x2):
100 self.assertTrue(x1 < x2)
101 self.assertFalse(x2 < x1)
103 assertOrder(span2, span1)
104 assertOrder(span1, span3)
105 assertOrder(span4, span1)
106 assertOrder(span1, span5)
107 assertOrder(span6, span1)
108 assertOrder(span1, span7)
109 self.assertFalse(span1 < span8)
110 self.assertFalse(span8 < span1)
113class ThresholdTestCase(unittest.TestCase):
114 """Tests of the Threshold class."""
115 def testThresholdFactory(self):
116 """
117 Test the creation of a Threshold object with ``createThreshold``.
119 This is a white-box test.
120 -tests missing parameters
121 -tests mal-formed parameters
122 """
123 try:
124 afwDetect.createThreshold(3.4)
125 except Exception:
126 self.fail("Failed to build Threshold with proper parameters")
128 with self.assertRaises(pexExcept.InvalidParameterError):
129 afwDetect.createThreshold(3.4, "foo bar")
131 try:
132 afwDetect.createThreshold(3.4, "variance")
133 except Exception:
134 self.fail("Failed to build Threshold with proper parameters")
136 try:
137 afwDetect.createThreshold(3.4, "stdev")
138 except Exception:
139 self.fail("Failed to build Threshold with proper parameters")
141 try:
142 afwDetect.createThreshold(3.4, "value")
143 except Exception:
144 self.fail("Failed to build Threshold with proper parameters")
146 try:
147 afwDetect.createThreshold(3.4, "value", False)
148 except Exception:
149 self.fail("Failed to build Threshold with VALUE, False parameters")
151 try:
152 afwDetect.createThreshold(0x4, "bitmask")
153 except Exception:
154 self.fail("Failed to build Threshold with BITMASK parameters")
156 try:
157 afwDetect.createThreshold(5, "pixel_stdev")
158 except Exception:
159 self.fail("Failed to build Threshold with PIXEL_STDEV parameters")
161 def test_str(self):
162 """Test that str/repr provide useful information.
163 """
164 threshold = afwDetect.createThreshold(10, "pixel_stdev")
165 self.assertEqual(str(threshold), "PIXEL_STDEV value=10 (positive)")
166 self.assertEqual(repr(threshold),
167 "Threshold(value=10, type=PIXEL_STDEV, polarity=1, includeMultiplier=1)")
169 threshold = afwDetect.createThreshold(3.123456789, "value", polarity=False)
170 self.assertEqual(str(threshold), "VALUE value=3.1234568 (negative)")
171 self.assertEqual(repr(threshold),
172 "Threshold(value=3.123456789, type=VALUE, polarity=0, includeMultiplier=1)")
174 threshold = afwDetect.Threshold(2, afwDetect.Threshold.VALUE, includeMultiplier=4)
175 self.assertEqual(str(threshold), "VALUE value=2 (positive) multiplier=4")
176 self.assertEqual(repr(threshold),
177 "Threshold(value=2, type=VALUE, polarity=1, includeMultiplier=4)")
180class FootprintTestCase(lsst.utils.tests.TestCase):
181 """A test case for Footprint"""
183 def setUp(self):
184 self.foot = afwDetect.Footprint()
186 def tearDown(self):
187 del self.foot
189 def testToString(self):
190 y, x0, x1 = 10, 100, 101
191 s = afwGeom.Span(y, x0, x1)
192 self.assertEqual(s.toString(), toString(y, x0, x1))
194 def testGC(self):
195 """Check that Footprints are automatically garbage collected (when MemoryTestCase runs)"""
197 afwDetect.Footprint()
199 def testIntersectMask(self):
200 bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 0), lsst.geom.ExtentI(10))
201 fp = afwDetect.Footprint(afwGeom.SpanSet(bbox))
202 maskBBox = lsst.geom.BoxI(bbox)
203 maskBBox.grow(-2)
204 mask = afwImage.Mask(maskBBox)
205 innerBBox = lsst.geom.BoxI(maskBBox)
206 innerBBox.grow(-2)
207 subMask = mask.Factory(mask, innerBBox)
208 subMask.set(1)
210 # We only want the pixels that are unmasked, and lie in the bounding box
211 # of the mask, so not the mask (selecting only zero values) and clipped
212 fp.spans = fp.spans.intersectNot(mask).clippedTo(mask.getBBox())
213 fp.removeOrphanPeaks()
214 fpBBox = fp.getBBox()
215 self.assertEqual(fpBBox.getMinX(), maskBBox.getMinX())
216 self.assertEqual(fpBBox.getMinY(), maskBBox.getMinY())
217 self.assertEqual(fpBBox.getMaxX(), maskBBox.getMaxX())
218 self.assertEqual(fpBBox.getMaxY(), maskBBox.getMaxY())
220 self.assertEqual(fp.getArea(), maskBBox.getArea() - innerBBox.getArea())
222 def testTablePersistence(self):
223 ellipse = afwGeom.Ellipse(afwGeomEllipses.Axes(8, 6, 0.25),
224 lsst.geom.Point2D(9, 15))
225 fp1 = afwDetect.Footprint(afwGeom.SpanSet.fromShape(ellipse))
226 fp1.addPeak(6, 7, 2)
227 fp1.addPeak(8, 9, 3)
228 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
229 fp1.writeFits(tmpFile)
230 fp2 = afwDetect.Footprint.readFits(tmpFile)
231 self.assertEqual(fp1.getArea(), fp2.getArea())
232 self.assertEqual(list(fp1.getSpans()), list(fp2.getSpans()))
233 # can't use Peak operator== for comparison because it compares IDs, not positions/values
234 self.assertEqual(len(fp1.getPeaks()), len(fp2.getPeaks()))
235 for peak1, peak2 in zip(fp1.getPeaks(), fp2.getPeaks()):
236 self.assertEqual(peak1.getIx(), peak2.getIx())
237 self.assertEqual(peak1.getIy(), peak2.getIy())
238 self.assertEqual(peak1.getFx(), peak2.getFx())
239 self.assertEqual(peak1.getFy(), peak2.getFy())
240 self.assertEqual(peak1.getPeakValue(), peak2.getPeakValue())
242 def testBbox(self):
243 """Add Spans and check bounding box"""
244 foot = afwDetect.Footprint()
245 spanLists = [afwGeom.Span(10, 100, 105), afwGeom.Span(11, 99, 104)]
246 spanSet = afwGeom.SpanSet(spanLists)
247 foot.spans = spanSet
249 bbox = foot.getBBox()
250 self.assertEqual(bbox.getWidth(), 7)
251 self.assertEqual(bbox.getHeight(), 2)
252 self.assertEqual(bbox.getMinX(), 99)
253 self.assertEqual(bbox.getMinY(), 10)
254 self.assertEqual(bbox.getMaxX(), 105)
255 self.assertEqual(bbox.getMaxY(), 11)
256 # clip with a bbox that doesn't overlap at all
257 bbox2 = lsst.geom.Box2I(lsst.geom.Point2I(5, 90), lsst.geom.Extent2I(1, 2))
258 foot.clipTo(bbox2)
259 self.assertTrue(foot.getBBox().isEmpty())
260 self.assertEqual(foot.getArea(), 0)
262 def testFootprintFromBBox1(self):
263 """Create a rectangular Footprint"""
264 x0, y0, w, h = 9, 10, 7, 4
265 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
266 lsst.geom.Extent2I(w, h)))
267 foot = afwDetect.Footprint(spanSet)
269 bbox = foot.getBBox()
271 self.assertEqual(bbox.getWidth(), w)
272 self.assertEqual(bbox.getHeight(), h)
273 self.assertEqual(bbox.getMinX(), x0)
274 self.assertEqual(bbox.getMinY(), y0)
275 self.assertEqual(bbox.getMaxX(), x0 + w - 1)
276 self.assertEqual(bbox.getMaxY(), y0 + h - 1)
278 def testGetBBox(self):
279 """Check that Footprint.getBBox() returns a copy"""
280 x0, y0, w, h = 9, 10, 7, 4
281 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
282 lsst.geom.Extent2I(w, h)))
283 foot = afwDetect.Footprint(spanSet)
284 bbox = foot.getBBox()
286 dx, dy = 10, 20
287 bbox.shift(lsst.geom.Extent2I(dx, dy))
289 self.assertEqual(bbox.getMinX(), x0 + dx)
290 self.assertEqual(foot.getBBox().getMinX(), x0)
292 def testFootprintFromEllipse(self):
293 """Create an elliptical Footprint"""
294 cen = lsst.geom.Point2D(23, 25)
295 a, b, theta = 25, 15, 30
296 ellipse = afwGeom.Ellipse(
297 afwGeomEllipses.Axes(a, b, math.radians(theta)),
298 cen)
299 spanSet = afwGeom.SpanSet.fromShape(ellipse)
300 foot = afwDetect.Footprint(spanSet,
301 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
302 lsst.geom.Extent2I(50, 60)))
304 idImage = afwImage.ImageU(lsst.geom.Extent2I(
305 foot.getRegion().getWidth(), foot.getRegion().getHeight()))
306 idImage.set(0)
308 foot.spans.setImage(idImage, 42)
310 if display:
311 disp = afwDisplay.Display(frame=2)
312 disp.mtv(idImage, title=self._testMethodName + " image")
313 afwDisplay.utils.drawFootprint(foot, frame=2)
314 shape = foot.getShape()
315 shape.scale(2) # <r^2> = 1/2 for a disk
316 disp.dot(shape, *cen, ctype=afwDisplay.RED)
318 shape = foot.getShape()
319 shape.scale(2) # <r^2> = 1/2 for a disk
320 disp.dot(shape, *cen, ctype=afwDisplay.MAGENTA)
322 axes = afwGeom.ellipses.Axes(foot.getShape())
323 axes.scale(2) # <r^2> = 1/2 for a disk
325 self.assertEqual(foot.getCentroid(), cen)
326 self.assertLess(abs(a - axes.getA()), 0.15, f"a: {a:g} vs. {axes.getA():g}")
327 self.assertLess(abs(b - axes.getB()), 0.02, f"b: {b:g} va. {axes.getB():g}")
328 self.assertLess(abs(theta - math.degrees(axes.getTheta())), 0.2,
329 f"theta: {theta:g} vs. {math.degrees(axes.getTheta()):g}")
331 def testCopy(self):
332 bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 2), lsst.geom.PointI(5, 6))
334 fp = afwDetect.Footprint(afwGeom.SpanSet(bbox), bbox)
336 # test copy construct
337 fp2 = afwDetect.Footprint(fp)
339 self.assertEqual(fp2.getBBox(), bbox)
340 self.assertEqual(fp2.getRegion(), bbox)
341 self.assertEqual(fp2.getArea(), bbox.getArea())
343 y = bbox.getMinY()
344 for s in fp2.getSpans():
345 self.assertEqual(s.getY(), y)
346 self.assertEqual(s.getX0(), bbox.getMinX())
347 self.assertEqual(s.getX1(), bbox.getMaxX())
348 y += 1
350 # test assignment
351 fp3 = afwDetect.Footprint()
352 fp3.assign(fp)
353 self.assertEqual(fp3.getBBox(), bbox)
354 self.assertEqual(fp3.getRegion(), bbox)
355 self.assertEqual(fp3.getArea(), bbox.getArea())
357 y = bbox.getMinY()
358 for s in fp3.getSpans():
359 self.assertEqual(s.getY(), y)
360 self.assertEqual(s.getX0(), bbox.getMinX())
361 self.assertEqual(s.getX1(), bbox.getMaxX())
362 y += 1
364 def testShrink(self):
365 width, height = 5, 10 # Size of footprint
366 x0, y0 = 50, 50 # Position of footprint
367 imwidth, imheight = 100, 100 # Size of image
369 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
370 lsst.geom.Extent2I(width, height)))
371 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
372 lsst.geom.Extent2I(imwidth, imheight))
373 foot = afwDetect.Footprint(spanSet, region)
374 self.assertEqual(foot.getArea(), width*height)
376 # Add some peaks to the original footprint and check that those lying outside
377 # the shrunken footprint are omitted from the returned shrunken footprint.
378 foot.addPeak(50, 50, 1) # should be omitted in shrunken footprint
379 foot.addPeak(52, 52, 2) # should be kept in shrunken footprint
380 foot.addPeak(50, 59, 3) # should be omitted in shrunken footprint
381 self.assertEqual(len(foot.getPeaks()), 3) # check that all three peaks were added
383 # Shrinking by one pixel makes each dimension *two* pixels shorter.
384 shrunk = afwDetect.Footprint().assign(foot)
385 shrunk.erode(1)
386 self.assertEqual(3*8, shrunk.getArea())
388 # Shrunken footprint should now only contain one peak at (52, 52)
389 self.assertEqual(len(shrunk.getPeaks()), 1)
390 peak = shrunk.getPeaks()[0]
391 self.assertEqual((peak.getIx(), peak.getIy()), (52, 52))
393 # Without shifting the centroid
394 self.assertEqual(shrunk.getCentroid(), foot.getCentroid())
396 # Get the same result from a Manhattan shrink
397 shrunk = afwDetect.Footprint().assign(foot)
398 shrunk.erode(1, afwGeom.Stencil.MANHATTAN)
399 self.assertEqual(3*8, shrunk.getArea())
400 self.assertEqual(shrunk.getCentroid(), foot.getCentroid())
402 # Shrinking by a large amount leaves nothing.
403 shrunkToNothing = afwDetect.Footprint().assign(foot)
404 shrunkToNothing.erode(100)
405 self.assertEqual(shrunkToNothing.getArea(), 0)
407 def testShrinkIsoVsManhattan(self):
408 # Demonstrate that isotropic and Manhattan shrinks are different.
409 radius = 8
410 imwidth, imheight = 100, 100
411 x0, y0 = imwidth//2, imheight//2
412 nshrink = 4
414 ellipse = afwGeom.Ellipse(
415 afwGeomEllipses.Axes(1.5*radius, 2*radius, 0),
416 lsst.geom.Point2D(x0, y0))
417 spanSet = afwGeom.SpanSet.fromShape(ellipse)
418 foot = afwDetect.Footprint(
419 spanSet,
420 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
421 lsst.geom.Extent2I(imwidth, imheight)))
422 footIsotropic = afwDetect.Footprint()
423 footIsotropic.assign(foot)
425 foot.erode(nshrink, afwGeom.Stencil.MANHATTAN)
426 footIsotropic.erode(nshrink)
427 self.assertNotEqual(foot, footIsotropic)
429 def _fig8Test(self, x1, y1, x2, y2):
430 # Construct a "figure of 8" consisting of two circles touching at the
431 # centre of an image, then demonstrate that it shrinks correctly.
432 # (Helper method for tests below.)
433 radius = 3
434 imwidth, imheight = 100, 100
435 nshrink = 1
437 # These are the correct values for footprint sizes given the paramters
438 # above.
439 circle_npix = 29
440 initial_npix = circle_npix*2 - 1 # touch at one pixel
441 shrunk_npix = 26
443 box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
444 lsst.geom.Extent2I(imwidth, imheight))
446 e1 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0),
447 lsst.geom.Point2D(x1, y1))
448 spanSet1 = afwGeom.SpanSet.fromShape(e1)
449 f1 = afwDetect.Footprint(spanSet1, box)
450 self.assertEqual(f1.getArea(), circle_npix)
452 e2 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0),
453 lsst.geom.Point2D(x2, y2))
454 spanSet2 = afwGeom.SpanSet.fromShape(e2)
455 f2 = afwDetect.Footprint(spanSet2, box)
456 self.assertEqual(f2.getArea(), circle_npix)
458 initial = afwDetect.mergeFootprints(f1, f2)
459 initial.setRegion(f2.getRegion()) # merge does not propagate the region
460 self.assertEqual(initial_npix, initial.getArea())
462 shrunk = afwDetect.Footprint().assign(initial)
463 shrunk.erode(nshrink)
464 self.assertEqual(shrunk_npix, shrunk.getArea())
466 if display:
467 idImage = afwImage.ImageU(imwidth, imheight)
468 for i, foot in enumerate([initial, shrunk]):
469 print(foot.getArea())
470 foot.spans.setImage(idImage, i + 1)
471 afwDisplay.Display(frame=1).mtv(idImage, title=self._testMethodName + " image")
473 def testShrinkEightVertical(self):
474 # Test a "vertical" figure of 8.
475 radius = 3
476 imwidth, imheight = 100, 100
477 self._fig8Test(imwidth//2, imheight//2 - radius, imwidth//2, imheight//2 + radius)
479 def testShrinkEightHorizontal(self):
480 # Test a "horizontal" figure of 8.
481 radius = 3
482 imwidth, imheight = 100, 100
483 self._fig8Test(imwidth//2 - radius, imheight//2, imwidth//2 + radius, imheight//2)
485 def testGrow(self):
486 """Test growing a footprint"""
487 x0, y0 = 20, 20
488 width, height = 20, 30
489 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
490 lsst.geom.Extent2I(width, height)))
491 foot1 = afwDetect.Footprint(spanSet,
492 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
493 lsst.geom.Extent2I(100, 100)))
495 # Add some peaks and check that they get copied into the new grown footprint
496 foot1.addPeak(20, 20, 1)
497 foot1.addPeak(30, 35, 2)
498 foot1.addPeak(25, 45, 3)
499 self.assertEqual(len(foot1.getPeaks()), 3)
501 bbox1 = foot1.getBBox()
503 self.assertEqual(bbox1.getMinX(), x0)
504 self.assertEqual(bbox1.getMaxX(), x0 + width - 1)
505 self.assertEqual(bbox1.getWidth(), width)
507 self.assertEqual(bbox1.getMinY(), y0)
508 self.assertEqual(bbox1.getMaxY(), y0 + height - 1)
509 self.assertEqual(bbox1.getHeight(), height)
511 ngrow = 5
512 for isotropic in (True, False):
513 foot2 = afwDetect.Footprint().assign(foot1)
514 stencil = afwGeom.Stencil.CIRCLE if isotropic else \
515 afwGeom.Stencil.MANHATTAN
516 foot2.dilate(ngrow, stencil)
518 # Check that the grown footprint is bigger than the original
519 self.assertGreater(foot2.getArea(), foot1.getArea())
521 # Check that peaks got copied into grown footprint
522 self.assertEqual(len(foot2.getPeaks()), 3)
523 for peak in foot2.getPeaks():
524 self.assertIn((peak.getIx(), peak.getIy()),
525 [(20, 20), (30, 35), (25, 45)])
527 bbox2 = foot2.getBBox()
529 # check bbox2
530 self.assertEqual(bbox2.getMinX(), x0 - ngrow)
531 self.assertEqual(bbox2.getWidth(), width + 2*ngrow)
533 self.assertEqual(bbox2.getMinY(), y0 - ngrow)
534 self.assertEqual(bbox2.getHeight(), height + 2*ngrow)
535 # Check that region was preserved
536 self.assertEqual(foot1.getRegion(), foot2.getRegion())
538 def testFootprintToBBoxList(self):
539 """Test footprintToBBoxList"""
540 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(12, 10))
541 foot = afwDetect.Footprint(afwGeom.SpanSet(), region)
542 spanList = [afwGeom.Span(*span) for span in ((3, 3, 5), (3, 7, 7),
543 (4, 2, 3), (4, 5, 7),
544 (5, 2, 3), (5, 5, 8),
545 (6, 3, 5))]
546 foot.spans = afwGeom.SpanSet(spanList)
548 idImage = afwImage.ImageU(region.getDimensions())
549 idImage.set(0)
551 foot.spans.setImage(idImage, 1)
552 if display:
553 disp = afwDisplay.Display(frame=1)
554 disp.mtv(idImage, title=self._testMethodName + " image")
556 idImageFromBBox = idImage.Factory(idImage, True)
557 idImageFromBBox.set(0)
558 bboxes = afwDetect.footprintToBBoxList(foot)
559 for bbox in bboxes:
560 x0, y0, x1, y1 = bbox.getMinX(), bbox.getMinY(), \
561 bbox.getMaxX(), bbox.getMaxY()
563 for y in range(y0, y1 + 1):
564 for x in range(x0, x1 + 1):
565 idImageFromBBox[x, y, afwImage.LOCAL] = 1
567 if display:
568 x0 -= 0.5
569 y0 -= 0.5
570 x1 += 0.5
571 y1 += 0.5
573 disp.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=afwDisplay.RED)
575 idImageFromBBox -= idImage # should be blank
576 stats = afwMath.makeStatistics(idImageFromBBox, afwMath.MAX)
578 self.assertEqual(stats.getValue(), 0)
580 def testWriteDefect(self):
581 """Write a Footprint as a set of Defects"""
582 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(12, 10))
583 spanSet = afwGeom.SpanSet([afwGeom.Span(*span) for span in [(3, 3, 5),
584 (3, 7, 7),
585 (4, 2, 3),
586 (4, 5, 7),
587 (5, 2, 3),
588 (5, 5, 8),
589 (6, 3, 5)]])
590 foot = afwDetect.Footprint(spanSet, region)
592 openedFile = False
593 if True:
594 fd = open("/dev/null", "w")
595 openedFile = True
596 else:
597 fd = sys.stdout
599 afwDetectUtils.writeFootprintAsDefects(fd, foot)
600 if openedFile:
601 fd.close()
603 def testSetFromFootprint(self):
604 """Test setting mask/image pixels from a Footprint list"""
605 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
606 im = mi.getImage()
607 #
608 # Objects that we should detect
609 #
610 self.objects = []
611 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
612 self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])]
613 self.objects += [Object(20, [(6, 3, 3)])]
615 im.set(0) # clear image
616 for obj in self.objects:
617 obj.insert(im)
619 ds = afwDetect.FootprintSet(mi, afwDetect.Threshold(15))
621 objects = ds.getFootprints()
622 afwDetect.setMaskFromFootprintList(mi.getMask(), objects, 0x1)
624 self.assertEqual(mi.getMask()[4, 2, afwImage.LOCAL], 0x0)
625 self.assertEqual(mi.getMask()[3, 6, afwImage.LOCAL], 0x1)
627 self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 20)
628 for ft in objects:
629 ft.spans.setImage(mi.getImage(), 5.0)
630 self.assertEqual(mi.getImage()[4, 2, afwImage.LOCAL], 10)
631 self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 5)
633 if display:
634 afwDisplay.Display(frame=1).mtv(mi, title=self._testMethodName + " image")
635 #
636 # Check Footprint.contains() while we are about it
637 #
638 self.assertTrue(objects[0].contains(lsst.geom.Point2I(7, 5)))
639 self.assertFalse(objects[0].contains(lsst.geom.Point2I(10, 6)))
640 self.assertFalse(objects[0].contains(lsst.geom.Point2I(7, 6)))
641 self.assertFalse(objects[0].contains(lsst.geom.Point2I(4, 2)))
643 self.assertTrue(objects[1].contains(lsst.geom.Point2I(3, 6)))
645 # Verify the FootprintSet footprint list setter can accept inputs from
646 # the footprint list getter
647 # Create a copy of the ds' FootprintList
648 dsFpList = ds.getFootprints()
649 footprintListCopy = [afwDetect.Footprint().assign(f) for f in dsFpList]
650 # Use the FootprintList setter with the output from the getter
651 ds.setFootprints(ds.getFootprints()[:-1])
652 dsFpListNew = ds.getFootprints()
653 self.assertTrue(len(dsFpListNew) == len(footprintListCopy)-1)
654 for new, old in zip(dsFpListNew, footprintListCopy[:-1]):
655 self.assertEqual(new, old)
657 def testMakeFootprintSetXY0(self):
658 """Test setting mask/image pixels from a Footprint list"""
659 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
660 im = mi.getImage()
661 im.set(100)
663 mi.setXY0(lsst.geom.PointI(2, 2))
664 afwDetect.FootprintSet(mi, afwDetect.Threshold(1), "DETECTED")
666 bitmask = mi.getMask().getPlaneBitMask("DETECTED")
667 for y in range(im.getHeight()):
668 for x in range(im.getWidth()):
669 self.assertEqual(mi.getMask()[x, y, afwImage.LOCAL], bitmask)
671 def testTransform(self):
672 dims = lsst.geom.Extent2I(512, 512)
673 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dims)
674 radius = 5
675 offset = lsst.geom.Extent2D(123, 456)
676 crval = lsst.geom.SpherePoint(0, 0, lsst.geom.degrees)
677 crpix = lsst.geom.Point2D(0, 0)
678 cdMatrix = np.array([1.0e-5, 0.0, 0.0, 1.0e-5])
679 cdMatrix.shape = (2, 2)
680 source = afwGeom.makeSkyWcs(crval=crval, crpix=crpix, cdMatrix=cdMatrix)
681 target = afwGeom.makeSkyWcs(crval=crval, crpix=crpix + offset, cdMatrix=cdMatrix)
682 sourceSpanSet = afwGeom.SpanSet.fromShape(radius,
683 afwGeom.Stencil.CIRCLE)
684 sourceSpanSet = sourceSpanSet.shiftedBy(12, 34)
685 fpSource = afwDetect.Footprint(sourceSpanSet, bbox)
687 fpTarget = fpSource.transform(source, target, bbox)
689 self.assertEqual(len(fpSource.getSpans()), len(fpTarget.getSpans()))
690 self.assertEqual(fpSource.getArea(), fpTarget.getArea())
691 imSource = afwImage.ImageU(dims)
692 fpSource.spans.setImage(imSource, 1)
694 imTarget = afwImage.ImageU(dims)
695 fpTarget.spans.setImage(imTarget, 1)
697 subSource = imSource.Factory(imSource, fpSource.getBBox())
698 subTarget = imTarget.Factory(imTarget, fpTarget.getBBox())
699 self.assertTrue(np.all(subSource.getArray() == subTarget.getArray()))
701 # make a bbox smaller than the target footprint
702 bbox2 = lsst.geom.Box2I(fpTarget.getBBox())
703 bbox2.grow(-1)
704 fpTarget2 = fpSource.transform(source, target, bbox2) # this one clips
705 fpTarget3 = fpSource.transform(source, target, bbox2, False) # this one doesn't
706 self.assertTrue(bbox2.contains(fpTarget2.getBBox()))
707 self.assertFalse(bbox2.contains(fpTarget3.getBBox()))
708 self.assertNotEqual(fpTarget.getArea(), fpTarget2.getArea())
709 self.assertEqual(fpTarget.getArea(), fpTarget3.getArea())
711 # Test that peakCatalogs get Transformed correctly
712 truthList = [(x, y, 10) for x, y in zip(range(-2, 2), range(-1, 3))]
713 for value in truthList:
714 fpSource.addPeak(*value)
715 scaleFactor = 2
716 linTrans = lsst.geom.LinearTransform(np.array([[scaleFactor, 0],
717 [0, scaleFactor]],
718 dtype=float))
719 linTransFootprint = fpSource.transform(linTrans, fpSource.getBBox(),
720 False)
721 for peak, truth in zip(linTransFootprint.peaks, truthList):
722 # Multiplied by two because that is the linear transform scaling
723 # factor
724 self.assertEqual(peak.getIx(), truth[0]*scaleFactor)
725 self.assertEqual(peak.getIy(), truth[1]*scaleFactor)
727 def testCopyWithinFootprintImage(self):
728 W, H = 10, 10
729 dims = lsst.geom.Extent2I(W, H)
730 source = afwImage.ImageF(dims)
731 dest = afwImage.ImageF(dims)
732 sa = source.getArray()
733 for i in range(H):
734 for j in range(W):
735 sa[i, j] = 100*i + j
737 footSpans = [s for s in self.foot.spans]
738 footSpans.append(afwGeom.Span(4, 3, 6))
739 footSpans.append(afwGeom.Span(5, 2, 4))
740 self.foot.spans = afwGeom.SpanSet(footSpans)
742 self.foot.spans.copyImage(source, dest)
744 da = dest.getArray()
745 self.assertEqual(da[4, 2], 0)
746 self.assertEqual(da[4, 3], 403)
747 self.assertEqual(da[4, 4], 404)
748 self.assertEqual(da[4, 5], 405)
749 self.assertEqual(da[4, 6], 406)
750 self.assertEqual(da[4, 7], 0)
751 self.assertEqual(da[5, 1], 0)
752 self.assertEqual(da[5, 2], 502)
753 self.assertEqual(da[5, 3], 503)
754 self.assertEqual(da[5, 4], 504)
755 self.assertEqual(da[5, 5], 0)
756 self.assertTrue(np.all(da[:4, :] == 0))
757 self.assertTrue(np.all(da[6:, :] == 0))
759 def testCopyWithinFootprintOutside(self):
760 """Copy a footprint that is larger than the image"""
761 target = afwImage.ImageF(100, 100)
762 target.set(0)
763 subTarget = afwImage.ImageF(target, lsst.geom.Box2I(lsst.geom.Point2I(40, 40),
764 lsst.geom.Extent2I(20, 20)))
765 source = afwImage.ImageF(10, 30)
766 source.setXY0(45, 45)
767 source.set(1.0)
769 foot = afwDetect.Footprint()
770 spanList = [afwGeom.Span(*s) for s in (
771 (50, 50, 60), # Oversized on the source image, right; only some pixels overlap
772 (60, 0, 100), # Oversized on the source, left and right; and on sub-target image, top
773 (99, 0, 1000), # Oversized on the source image, top, left and right; aiming for segfault
774 )]
775 foot.spans = afwGeom.SpanSet(spanList)
777 foot.spans.clippedTo(subTarget.getBBox()).clippedTo(source.getBBox()).\
778 copyImage(source, subTarget)
780 expected = np.zeros((100, 100))
781 expected[50, 50:55] = 1.0
783 self.assertTrue(np.all(target.getArray() == expected))
785 def testCopyWithinFootprintMaskedImage(self):
786 W, H = 10, 10
787 dims = lsst.geom.Extent2I(W, H)
788 source = afwImage.MaskedImageF(dims)
789 dest = afwImage.MaskedImageF(dims)
790 sa = source.getImage().getArray()
791 sv = source.getVariance().getArray()
792 sm = source.getMask().getArray()
793 for i in range(H):
794 for j in range(W):
795 sa[i, j] = 100*i + j
796 sv[i, j] = 100*j + i
797 sm[i, j] = 1
799 footSpans = [s for s in self.foot.spans]
800 footSpans.append(afwGeom.Span(4, 3, 6))
801 footSpans.append(afwGeom.Span(5, 2, 4))
802 self.foot.spans = afwGeom.SpanSet(footSpans)
804 self.foot.spans.copyMaskedImage(source, dest)
806 da = dest.getImage().getArray()
807 dv = dest.getVariance().getArray()
808 dm = dest.getMask().getArray()
810 self.assertEqual(da[4, 2], 0)
811 self.assertEqual(da[4, 3], 403)
812 self.assertEqual(da[4, 4], 404)
813 self.assertEqual(da[4, 5], 405)
814 self.assertEqual(da[4, 6], 406)
815 self.assertEqual(da[4, 7], 0)
816 self.assertEqual(da[5, 1], 0)
817 self.assertEqual(da[5, 2], 502)
818 self.assertEqual(da[5, 3], 503)
819 self.assertEqual(da[5, 4], 504)
820 self.assertEqual(da[5, 5], 0)
821 self.assertTrue(np.all(da[:4, :] == 0))
822 self.assertTrue(np.all(da[6:, :] == 0))
824 self.assertEqual(dv[4, 2], 0)
825 self.assertEqual(dv[4, 3], 304)
826 self.assertEqual(dv[4, 4], 404)
827 self.assertEqual(dv[4, 5], 504)
828 self.assertEqual(dv[4, 6], 604)
829 self.assertEqual(dv[4, 7], 0)
830 self.assertEqual(dv[5, 1], 0)
831 self.assertEqual(dv[5, 2], 205)
832 self.assertEqual(dv[5, 3], 305)
833 self.assertEqual(dv[5, 4], 405)
834 self.assertEqual(dv[5, 5], 0)
835 self.assertTrue(np.all(dv[:4, :] == 0))
836 self.assertTrue(np.all(dv[6:, :] == 0))
838 self.assertTrue(np.all(dm[4, 3:7] == 1))
839 self.assertTrue(np.all(dm[5, 2:5] == 1))
840 self.assertTrue(np.all(dm[:4, :] == 0))
841 self.assertTrue(np.all(dm[6:, :] == 0))
842 self.assertTrue(np.all(dm[4, :3] == 0))
843 self.assertTrue(np.all(dm[4, 7:] == 0))
845 def testMergeFootprints(self):
846 f1 = self.foot
847 f2 = afwDetect.Footprint()
849 spanList1 = [(10, 10, 20),
850 (10, 30, 40),
851 (10, 50, 60),
852 (11, 30, 50),
853 (12, 30, 50),
854 (13, 10, 20),
855 (13, 30, 40),
856 (13, 50, 60),
857 (15, 10, 20),
858 (15, 31, 40),
859 (15, 51, 60)]
860 spanSet1 = afwGeom.SpanSet([afwGeom.Span(*span) for span in spanList1])
861 f1.spans = spanSet1
863 spanList2 = [(8, 10, 20),
864 (9, 20, 30),
865 (10, 0, 9),
866 (10, 35, 65),
867 (10, 70, 80),
868 (13, 49, 54),
869 (14, 10, 30),
870 (15, 21, 30),
871 (15, 41, 50),
872 (15, 61, 70)]
873 spanSet2 = afwGeom.SpanSet([afwGeom.Span(*span) for span in spanList2])
874 f2.spans = spanSet2
876 fA = afwDetect.mergeFootprints(f1, f2)
877 fB = afwDetect.mergeFootprints(f2, f1)
879 ims = []
880 for i, f in enumerate([f1, f2, fA, fB]):
881 im1 = afwImage.ImageU(100, 100)
882 im1.set(0)
883 imbb = im1.getBBox()
884 f.setRegion(imbb)
885 f.spans.setImage(im1, 1)
886 ims.append(im1)
888 for i, merged in enumerate([ims[2], ims[3]]):
889 m = merged.getArray()
890 a1 = ims[0].getArray()
891 a2 = ims[1].getArray()
892 # Slightly looser tests to start...
893 # Every pixel in f1 is in f[AB]
894 self.assertTrue(np.all(m.flat[np.flatnonzero(a1)] == 1))
895 # Every pixel in f2 is in f[AB]
896 self.assertTrue(np.all(m.flat[np.flatnonzero(a2)] == 1))
897 # merged == a1 | a2.
898 self.assertTrue(np.all(m == np.maximum(a1, a2)))
900 def testPeakSort(self):
901 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
902 lsst.geom.Point2I(10, 10)))
903 footprint = afwDetect.Footprint(spanSet)
904 footprint.addPeak(4, 5, 1)
905 footprint.addPeak(3, 2, 5)
906 footprint.addPeak(7, 8, -2)
907 footprint.addPeak(5, 7, 4)
908 footprint.sortPeaks()
909 self.assertEqual([peak.getIx() for peak in footprint.getPeaks()],
910 [3, 5, 4, 7])
912 def testInclude(self):
913 """Test that we can expand a Footprint to include the union of itself and all others provided."""
914 region = lsst.geom.Box2I(lsst.geom.Point2I(-6, -6), lsst.geom.Point2I(6, 6))
915 parentSpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-2, -2),
916 lsst.geom.Point2I(2, 2)))
917 parent = afwDetect.Footprint(parentSpanSet, region)
918 parent.addPeak(0, 0, float("NaN"))
919 child1SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-3, 0),
920 lsst.geom.Point2I(0, 3)))
921 child1 = afwDetect.Footprint(child1SpanSet, region)
922 child1.addPeak(-1, 1, float("NaN"))
923 child2SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-4, -3),
924 lsst.geom.Point2I(-1, 0)))
925 child2 = afwDetect.Footprint(child2SpanSet, region)
926 child3SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(4, -1),
927 lsst.geom.Point2I(6, 1)))
928 child3 = afwDetect.Footprint(child3SpanSet)
929 merge123 = afwDetect.Footprint(parent)
930 merge123.spans = merge123.spans.union(child1.spans).union(child2.spans).union(child3.spans)
931 self.assertTrue(merge123.getBBox().contains(parent.getBBox()))
932 self.assertTrue(merge123.getBBox().contains(child1.getBBox()))
933 self.assertTrue(merge123.getBBox().contains(child2.getBBox()))
934 self.assertTrue(merge123.getBBox().contains(child3.getBBox()))
935 mask123a = afwImage.Mask(region)
936 mask123b = afwImage.Mask(region)
937 parent.spans.setMask(mask123a, 1)
938 child1.spans.setMask(mask123a, 1)
939 child2.spans.setMask(mask123a, 1)
940 child3.spans.setMask(mask123a, 1)
941 merge123.spans.setMask(mask123b, 1)
942 self.assertEqual(mask123a.getArray().sum(), merge123.getArea())
943 self.assertFloatsAlmostEqual(mask123a.getArray(), mask123b.getArray(),
944 rtol=0, atol=0)
946 # Test that ignoreSelf=True works for include
947 childOnly = afwDetect.Footprint()
948 childOnly.spans = childOnly.spans.union(child1.spans).union(child2.spans).union(child3.spans)
949 merge123 = afwDetect.Footprint(parent)
950 merge123.spans = child1.spans.union(child2.spans).union(child3.spans)
951 maskChildren = afwImage.Mask(region)
952 mask123 = afwImage.Mask(region)
953 childOnly.spans.setMask(maskChildren, 1)
954 merge123.spans.setMask(mask123, 1)
955 self.assertTrue(np.all(maskChildren.getArray() == mask123.getArray()))
957 def checkEdge(self, footprint):
958 """Check that Footprint::findEdgePixels() works"""
959 bbox = footprint.getBBox()
960 bbox.grow(3)
962 def makeImage(area):
963 """Make an ImageF with 1 in the footprint, and 0 elsewhere"""
964 ones = afwImage.ImageI(bbox)
965 ones.set(1)
966 image = afwImage.ImageI(bbox)
967 image.set(0)
968 if isinstance(area, afwDetect.Footprint):
969 area.spans.copyImage(ones, image)
970 if isinstance(area, afwGeom.SpanSet):
971 area.copyImage(ones, image)
972 return image
974 edges = self.foot.spans.findEdgePixels()
975 edgeImage = makeImage(edges)
977 # Find edges with an edge-detection kernel
978 image = makeImage(self.foot)
979 kernel = afwImage.ImageD(3, 3)
980 kernel[1, 1, afwImage.LOCAL] = 4
981 for x, y in [(1, 2), (0, 1), (1, 0), (2, 1)]:
982 kernel[x, y, afwImage.LOCAL] = -1
983 kernel.setXY0(1, 1)
984 result = afwImage.ImageI(bbox)
985 result.set(0)
986 afwMath.convolve(result, image, afwMath.FixedKernel(kernel),
987 afwMath.ConvolutionControl(False))
988 result.getArray().__imul__(image.getArray())
989 trueEdges = np.where(result.getArray() > 0, 1, 0)
991 self.assertTrue(np.all(trueEdges == edgeImage.getArray()))
993 def testEdge(self):
994 """Test for Footprint::findEdgePixels()"""
995 foot = afwDetect.Footprint()
996 spanList = [afwGeom.Span(*span) for span in ((3, 3, 9),
997 (4, 2, 4),
998 (4, 6, 7),
999 (4, 9, 11),
1000 (5, 3, 9),
1001 (6, 6, 7))]
1002 foot.spans = afwGeom.SpanSet(spanList)
1003 self.checkEdge(foot)
1005 # This footprint came from a very large Footprint in a deep HSC coadd patch
1006 self.checkEdge(afwDetect.Footprint.readFits(
1007 os.path.join(testPath, "testFootprintEdge.fits")))
1010class FootprintSetTestCase(unittest.TestCase):
1011 """A test case for FootprintSet"""
1013 def setUp(self):
1014 self.ms = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
1015 im = self.ms.getImage()
1016 #
1017 # Objects that we should detect
1018 #
1019 self.objects = []
1020 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1021 self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])]
1022 self.objects += [Object(20, [(6, 3, 3)])]
1024 self.ms.set((0, 0x0, 4.0)) # clear image; set variance
1025 for obj in self.objects:
1026 obj.insert(im)
1028 def tearDown(self):
1029 del self.ms
1031 def testGC(self):
1032 """Check that FootprintSets are automatically garbage collected (when MemoryTestCase runs)"""
1033 afwDetect.FootprintSet(afwImage.MaskedImageF(lsst.geom.Extent2I(10, 20)),
1034 afwDetect.Threshold(10))
1036 def testFootprints(self):
1037 """Check that we found the correct number of objects and that they are correct"""
1038 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1040 objects = ds.getFootprints()
1042 self.assertEqual(len(objects), len(self.objects))
1043 for i in range(len(objects)):
1044 self.assertEqual(objects[i], self.objects[i])
1046 def test_str(self):
1047 footprints = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1048 expect = ("3 footprints:"
1049 "\n5 peaks, area=5, centroid=(4, 2)"
1050 "\n5 peaks, area=5, centroid=(8.4, 5.4)"
1051 "\n1 peaks, area=1, centroid=(3, 6)")
1052 self.assertEqual(str(footprints), expect)
1054 def testFootprints2(self):
1055 """Check that we found the correct number of objects using FootprintSet"""
1056 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1058 objects = ds.getFootprints()
1060 self.assertEqual(len(objects), len(self.objects))
1061 for i in range(len(objects)):
1062 self.assertEqual(objects[i], self.objects[i])
1064 def testFootprints3(self):
1065 """Check that we found the correct number of objects using FootprintSet and PIXEL_STDEV"""
1066 threshold = 4.5 # in units of sigma
1068 self.ms[2, 4, afwImage.LOCAL] = (10, 0x0, 36) # not detected (high variance)
1070 y, x = self.objects[2].spans[0][0:2]
1071 self.ms[x, y, afwImage.LOCAL] = (threshold, 0x0, 1.0)
1073 ds = afwDetect.FootprintSet(self.ms,
1074 afwDetect.createThreshold(threshold, "pixel_stdev"), "OBJECT")
1076 objects = ds.getFootprints()
1078 self.assertEqual(len(objects), len(self.objects))
1079 for i in range(len(objects)):
1080 self.assertEqual(objects[i], self.objects[i])
1082 def testFootprintsMasks(self):
1083 """Check that detectionSets have the proper mask bits set"""
1084 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1085 objects = ds.getFootprints()
1087 if display:
1088 afwDisplay.Display(frame=1).mtv(self.ms, title=self._testMethodName + " image")
1090 mask = self.ms.getMask()
1091 for i in range(len(objects)):
1092 for sp in objects[i].getSpans():
1093 for x in range(sp.getX0(), sp.getX1() + 1):
1094 self.assertEqual(mask[x, sp.getY(), afwImage.LOCAL],
1095 mask.getPlaneBitMask("OBJECT"))
1097 def testFootprintsImageId(self):
1098 """Check that we can insert footprints into an Image"""
1099 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1100 objects = ds.getFootprints()
1102 idImage = afwImage.ImageU(self.ms.getDimensions())
1103 idImage.set(0)
1105 for i, foot in enumerate(objects):
1106 foot.spans.setImage(idImage, i + 1)
1108 for i in range(len(objects)):
1109 for sp in objects[i].getSpans():
1110 for x in range(sp.getX0(), sp.getX1() + 1):
1111 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1)
1113 def testFootprintSetImageId(self):
1114 """Check that we can insert a FootprintSet into an Image, setting relative IDs"""
1115 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1116 objects = ds.getFootprints()
1118 idImage = ds.insertIntoImage()
1119 if display:
1120 afwDisplay.Display(frame=2).mtv(idImage, title=self._testMethodName + " image")
1122 for i in range(len(objects)):
1123 for sp in objects[i].getSpans():
1124 for x in range(sp.getX0(), sp.getX1() + 1):
1125 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1)
1127 def testFootprintsImage(self):
1128 """Check that we can search Images as well as MaskedImages"""
1129 ds = afwDetect.FootprintSet(self.ms.getImage(), afwDetect.Threshold(10))
1131 objects = ds.getFootprints()
1133 self.assertEqual(len(objects), len(self.objects))
1134 for i in range(len(objects)):
1135 self.assertEqual(objects[i], self.objects[i])
1137 def testGrow2(self):
1138 """Grow some more interesting shaped Footprints. Informative with display, but no numerical tests"""
1139 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1141 idImage = afwImage.ImageU(self.ms.getDimensions())
1142 idImage.set(0)
1144 i = 1
1145 for foot in ds.getFootprints()[0:1]:
1146 foot.dilate(3, afwGeom.Stencil.MANHATTAN)
1147 foot.spans.setImage(idImage, i, doClip=True)
1148 i += 1
1150 if display:
1151 afwDisplay.Display(frame=0).mtv(self.ms, title=self._testMethodName + " self.ms")
1152 afwDisplay.Display(frame=1).mtv(idImage, title=self._testMethodName + " image")
1154 def testFootprintPeaks(self):
1155 """Test that we can extract the peaks from a Footprint"""
1156 fs = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1158 foot = fs.getFootprints()[0]
1160 self.assertEqual(len(foot.getPeaks()), 5)
1163class MaskFootprintSetTestCase(unittest.TestCase):
1164 """A test case for generating FootprintSet from Masks"""
1166 def setUp(self):
1167 self.mim = afwImage.MaskedImageF(lsst.geom.ExtentI(12, 8))
1168 #
1169 # Objects that we should detect
1170 #
1171 self.objects = []
1172 self.objects += [Object(0x2, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1173 self.objects += [Object(0x41, [(5, 7, 8), (6, 8, 8)])]
1174 self.objects += [Object(0x42, [(5, 10, 10)])]
1175 self.objects += [Object(0x82, [(6, 3, 3)])]
1177 self.mim.set((0, 0, 0)) # clear image
1178 for obj in self.objects:
1179 obj.insert(self.mim.getImage())
1180 obj.insert(self.mim.getMask())
1182 if display:
1183 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim")
1185 def tearDown(self):
1186 del self.mim
1188 def testFootprints(self):
1189 """Check that we found the correct number of objects using FootprintSet"""
1190 level = 0x2
1191 ds = afwDetect.FootprintSet(self.mim.getMask(),
1192 afwDetect.createThreshold(level, "bitmask"))
1194 objects = ds.getFootprints()
1196 if 0 and display:
1197 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim")
1199 self.assertEqual(len(objects),
1200 len([o for o in self.objects if (o.val & level)]))
1202 i = 0
1203 for o in self.objects:
1204 if o.val & level:
1205 self.assertEqual(o, objects[i])
1206 i += 1
1209class NaNFootprintSetTestCase(unittest.TestCase):
1210 """A test case for FootprintSet when the image contains NaNs"""
1212 def setUp(self):
1213 self.ms = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
1214 im = self.ms.getImage()
1215 #
1216 # Objects that we should detect
1217 #
1218 self.objects = []
1219 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1220 self.objects += [Object(20, [(5, 7, 8), (6, 8, 8)])]
1221 self.objects += [Object(20, [(5, 10, 10)])]
1222 self.objects += [Object(30, [(6, 3, 3)])]
1224 im.set(0) # clear image
1225 for obj in self.objects:
1226 obj.insert(im)
1228 self.NaN = float("NaN")
1229 im[3, 7, afwImage.LOCAL] = self.NaN
1230 im[0, 0, afwImage.LOCAL] = self.NaN
1231 im[8, 2, afwImage.LOCAL] = self.NaN
1233 # connects the two objects with value==20 together if NaN is detected
1234 im[9, 6, afwImage.LOCAL] = self.NaN
1236 def tearDown(self):
1237 del self.ms
1239 def testFootprints(self):
1240 """Check that we found the correct number of objects using FootprintSet"""
1241 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "DETECTED")
1243 objects = ds.getFootprints()
1245 if display:
1246 afwDisplay.Display(frame=0).mtv(self.ms, title=self._testMethodName + " self.ms")
1248 self.assertEqual(len(objects), len(self.objects))
1249 for i in range(len(objects)):
1250 self.assertEqual(objects[i], self.objects[i])
1253class MemoryTester(lsst.utils.tests.MemoryTestCase):
1254 pass
1257def setup_module(module):
1258 lsst.utils.tests.init()
1261if __name__ == "__main__": 1261 ↛ 1262line 1261 didn't jump to line 1262, because the condition on line 1261 was never true
1262 lsst.utils.tests.init()
1263 unittest.main()