Coverage for tests/test_footprint1.py: 9%
826 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-31 02:57 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-31 02:57 -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 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)")
179 threshold = afwDetect.createThreshold(5, "stdev")
180 self.assertEqual(str(threshold), "STDEV value=5 (positive)")
181 self.assertEqual(repr(threshold),
182 "Threshold(value=5, type=STDEV, polarity=1, includeMultiplier=1)")
184 threshold = afwDetect.createThreshold(4, "variance")
185 self.assertEqual(str(threshold), "VARIANCE value=4 (positive)")
186 self.assertEqual(repr(threshold),
187 "Threshold(value=4, type=VARIANCE, polarity=1, includeMultiplier=1)")
190class FootprintTestCase(lsst.utils.tests.TestCase):
191 """A test case for Footprint"""
193 def setUp(self):
194 self.foot = afwDetect.Footprint()
196 def tearDown(self):
197 del self.foot
199 def testToString(self):
200 y, x0, x1 = 10, 100, 101
201 s = afwGeom.Span(y, x0, x1)
202 self.assertEqual(s.toString(), toString(y, x0, x1))
204 def testGC(self):
205 """Check that Footprints are automatically garbage collected (when MemoryTestCase runs)"""
207 afwDetect.Footprint()
209 def testIntersectMask(self):
210 bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 0), lsst.geom.ExtentI(10))
211 fp = afwDetect.Footprint(afwGeom.SpanSet(bbox))
212 maskBBox = lsst.geom.BoxI(bbox)
213 maskBBox.grow(-2)
214 mask = afwImage.Mask(maskBBox)
215 innerBBox = lsst.geom.BoxI(maskBBox)
216 innerBBox.grow(-2)
217 subMask = mask.Factory(mask, innerBBox)
218 subMask.set(1)
220 # We only want the pixels that are unmasked, and lie in the bounding box
221 # of the mask, so not the mask (selecting only zero values) and clipped
222 fp.spans = fp.spans.intersectNot(mask).clippedTo(mask.getBBox())
223 fp.removeOrphanPeaks()
224 fpBBox = fp.getBBox()
225 self.assertEqual(fpBBox.getMinX(), maskBBox.getMinX())
226 self.assertEqual(fpBBox.getMinY(), maskBBox.getMinY())
227 self.assertEqual(fpBBox.getMaxX(), maskBBox.getMaxX())
228 self.assertEqual(fpBBox.getMaxY(), maskBBox.getMaxY())
230 self.assertEqual(fp.getArea(), maskBBox.getArea() - innerBBox.getArea())
232 def testTablePersistence(self):
233 ellipse = afwGeom.Ellipse(afwGeomEllipses.Axes(8, 6, 0.25),
234 lsst.geom.Point2D(9, 15))
235 fp1 = afwDetect.Footprint(afwGeom.SpanSet.fromShape(ellipse))
236 fp1.addPeak(6, 7, 2)
237 fp1.addPeak(8, 9, 3)
238 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
239 fp1.writeFits(tmpFile)
240 fp2 = afwDetect.Footprint.readFits(tmpFile)
241 self.assertEqual(fp1.getArea(), fp2.getArea())
242 self.assertEqual(list(fp1.getSpans()), list(fp2.getSpans()))
243 # can't use Peak operator== for comparison because it compares IDs, not positions/values
244 self.assertEqual(len(fp1.getPeaks()), len(fp2.getPeaks()))
245 for peak1, peak2 in zip(fp1.getPeaks(), fp2.getPeaks()):
246 self.assertEqual(peak1.getIx(), peak2.getIx())
247 self.assertEqual(peak1.getIy(), peak2.getIy())
248 self.assertEqual(peak1.getFx(), peak2.getFx())
249 self.assertEqual(peak1.getFy(), peak2.getFy())
250 self.assertEqual(peak1.getPeakValue(), peak2.getPeakValue())
252 def testBbox(self):
253 """Add Spans and check bounding box"""
254 foot = afwDetect.Footprint()
255 spanLists = [afwGeom.Span(10, 100, 105), afwGeom.Span(11, 99, 104)]
256 spanSet = afwGeom.SpanSet(spanLists)
257 foot.spans = spanSet
259 bbox = foot.getBBox()
260 self.assertEqual(bbox.getWidth(), 7)
261 self.assertEqual(bbox.getHeight(), 2)
262 self.assertEqual(bbox.getMinX(), 99)
263 self.assertEqual(bbox.getMinY(), 10)
264 self.assertEqual(bbox.getMaxX(), 105)
265 self.assertEqual(bbox.getMaxY(), 11)
266 # clip with a bbox that doesn't overlap at all
267 bbox2 = lsst.geom.Box2I(lsst.geom.Point2I(5, 90), lsst.geom.Extent2I(1, 2))
268 foot.clipTo(bbox2)
269 self.assertTrue(foot.getBBox().isEmpty())
270 self.assertEqual(foot.getArea(), 0)
272 def testFootprintFromBBox1(self):
273 """Create a rectangular Footprint"""
274 x0, y0, w, h = 9, 10, 7, 4
275 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
276 lsst.geom.Extent2I(w, h)))
277 foot = afwDetect.Footprint(spanSet)
279 bbox = foot.getBBox()
281 self.assertEqual(bbox.getWidth(), w)
282 self.assertEqual(bbox.getHeight(), h)
283 self.assertEqual(bbox.getMinX(), x0)
284 self.assertEqual(bbox.getMinY(), y0)
285 self.assertEqual(bbox.getMaxX(), x0 + w - 1)
286 self.assertEqual(bbox.getMaxY(), y0 + h - 1)
288 def testGetBBox(self):
289 """Check that Footprint.getBBox() returns a copy"""
290 x0, y0, w, h = 9, 10, 7, 4
291 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
292 lsst.geom.Extent2I(w, h)))
293 foot = afwDetect.Footprint(spanSet)
294 bbox = foot.getBBox()
296 dx, dy = 10, 20
297 bbox.shift(lsst.geom.Extent2I(dx, dy))
299 self.assertEqual(bbox.getMinX(), x0 + dx)
300 self.assertEqual(foot.getBBox().getMinX(), x0)
302 def testFootprintFromEllipse(self):
303 """Create an elliptical Footprint"""
304 cen = lsst.geom.Point2D(23, 25)
305 a, b, theta = 25, 15, 30
306 ellipse = afwGeom.Ellipse(
307 afwGeomEllipses.Axes(a, b, math.radians(theta)),
308 cen)
309 spanSet = afwGeom.SpanSet.fromShape(ellipse)
310 foot = afwDetect.Footprint(spanSet,
311 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
312 lsst.geom.Extent2I(50, 60)))
314 idImage = afwImage.ImageU(lsst.geom.Extent2I(
315 foot.getRegion().getWidth(), foot.getRegion().getHeight()))
316 idImage.set(0)
318 foot.spans.setImage(idImage, 42)
320 if display:
321 disp = afwDisplay.Display(frame=2)
322 disp.mtv(idImage, title=self._testMethodName + " image")
323 afwDisplay.utils.drawFootprint(foot, frame=2)
324 shape = foot.getShape()
325 shape.scale(2) # <r^2> = 1/2 for a disk
326 disp.dot(shape, *cen, ctype=afwDisplay.RED)
328 shape = foot.getShape()
329 shape.scale(2) # <r^2> = 1/2 for a disk
330 disp.dot(shape, *cen, ctype=afwDisplay.MAGENTA)
332 axes = afwGeom.ellipses.Axes(foot.getShape())
333 axes.scale(2) # <r^2> = 1/2 for a disk
335 self.assertEqual(foot.getCentroid(), cen)
336 self.assertLess(abs(a - axes.getA()), 0.15, f"a: {a:g} vs. {axes.getA():g}")
337 self.assertLess(abs(b - axes.getB()), 0.02, f"b: {b:g} va. {axes.getB():g}")
338 self.assertLess(abs(theta - math.degrees(axes.getTheta())), 0.2,
339 f"theta: {theta:g} vs. {math.degrees(axes.getTheta()):g}")
341 def testCopy(self):
342 bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 2), lsst.geom.PointI(5, 6))
344 fp = afwDetect.Footprint(afwGeom.SpanSet(bbox), bbox)
346 # test copy construct
347 fp2 = afwDetect.Footprint(fp)
349 self.assertEqual(fp2.getBBox(), bbox)
350 self.assertEqual(fp2.getRegion(), bbox)
351 self.assertEqual(fp2.getArea(), bbox.getArea())
353 y = bbox.getMinY()
354 for s in fp2.getSpans():
355 self.assertEqual(s.getY(), y)
356 self.assertEqual(s.getX0(), bbox.getMinX())
357 self.assertEqual(s.getX1(), bbox.getMaxX())
358 y += 1
360 # test assignment
361 fp3 = afwDetect.Footprint()
362 fp3.assign(fp)
363 self.assertEqual(fp3.getBBox(), bbox)
364 self.assertEqual(fp3.getRegion(), bbox)
365 self.assertEqual(fp3.getArea(), bbox.getArea())
367 y = bbox.getMinY()
368 for s in fp3.getSpans():
369 self.assertEqual(s.getY(), y)
370 self.assertEqual(s.getX0(), bbox.getMinX())
371 self.assertEqual(s.getX1(), bbox.getMaxX())
372 y += 1
374 def testShrink(self):
375 width, height = 5, 10 # Size of footprint
376 x0, y0 = 50, 50 # Position of footprint
377 imwidth, imheight = 100, 100 # Size of image
379 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
380 lsst.geom.Extent2I(width, height)))
381 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
382 lsst.geom.Extent2I(imwidth, imheight))
383 foot = afwDetect.Footprint(spanSet, region)
384 self.assertEqual(foot.getArea(), width*height)
386 # Add some peaks to the original footprint and check that those lying outside
387 # the shrunken footprint are omitted from the returned shrunken footprint.
388 foot.addPeak(50, 50, 1) # should be omitted in shrunken footprint
389 foot.addPeak(52, 52, 2) # should be kept in shrunken footprint
390 foot.addPeak(50, 59, 3) # should be omitted in shrunken footprint
391 self.assertEqual(len(foot.getPeaks()), 3) # check that all three peaks were added
393 # Shrinking by one pixel makes each dimension *two* pixels shorter.
394 shrunk = afwDetect.Footprint().assign(foot)
395 shrunk.erode(1)
396 self.assertEqual(3*8, shrunk.getArea())
398 # Shrunken footprint should now only contain one peak at (52, 52)
399 self.assertEqual(len(shrunk.getPeaks()), 1)
400 peak = shrunk.getPeaks()[0]
401 self.assertEqual((peak.getIx(), peak.getIy()), (52, 52))
403 # Without shifting the centroid
404 self.assertEqual(shrunk.getCentroid(), foot.getCentroid())
406 # Get the same result from a Manhattan shrink
407 shrunk = afwDetect.Footprint().assign(foot)
408 shrunk.erode(1, afwGeom.Stencil.MANHATTAN)
409 self.assertEqual(3*8, shrunk.getArea())
410 self.assertEqual(shrunk.getCentroid(), foot.getCentroid())
412 # Shrinking by a large amount leaves nothing.
413 shrunkToNothing = afwDetect.Footprint().assign(foot)
414 shrunkToNothing.erode(100)
415 self.assertEqual(shrunkToNothing.getArea(), 0)
417 def testShrinkIsoVsManhattan(self):
418 # Demonstrate that isotropic and Manhattan shrinks are different.
419 radius = 8
420 imwidth, imheight = 100, 100
421 x0, y0 = imwidth//2, imheight//2
422 nshrink = 4
424 ellipse = afwGeom.Ellipse(
425 afwGeomEllipses.Axes(1.5*radius, 2*radius, 0),
426 lsst.geom.Point2D(x0, y0))
427 spanSet = afwGeom.SpanSet.fromShape(ellipse)
428 foot = afwDetect.Footprint(
429 spanSet,
430 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
431 lsst.geom.Extent2I(imwidth, imheight)))
432 footIsotropic = afwDetect.Footprint()
433 footIsotropic.assign(foot)
435 foot.erode(nshrink, afwGeom.Stencil.MANHATTAN)
436 footIsotropic.erode(nshrink)
437 self.assertNotEqual(foot, footIsotropic)
439 def _fig8Test(self, x1, y1, x2, y2):
440 # Construct a "figure of 8" consisting of two circles touching at the
441 # centre of an image, then demonstrate that it shrinks correctly.
442 # (Helper method for tests below.)
443 radius = 3
444 imwidth, imheight = 100, 100
445 nshrink = 1
447 # These are the correct values for footprint sizes given the paramters
448 # above.
449 circle_npix = 29
450 initial_npix = circle_npix*2 - 1 # touch at one pixel
451 shrunk_npix = 26
453 box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
454 lsst.geom.Extent2I(imwidth, imheight))
456 e1 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0),
457 lsst.geom.Point2D(x1, y1))
458 spanSet1 = afwGeom.SpanSet.fromShape(e1)
459 f1 = afwDetect.Footprint(spanSet1, box)
460 self.assertEqual(f1.getArea(), circle_npix)
462 e2 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0),
463 lsst.geom.Point2D(x2, y2))
464 spanSet2 = afwGeom.SpanSet.fromShape(e2)
465 f2 = afwDetect.Footprint(spanSet2, box)
466 self.assertEqual(f2.getArea(), circle_npix)
468 initial = afwDetect.mergeFootprints(f1, f2)
469 initial.setRegion(f2.getRegion()) # merge does not propagate the region
470 self.assertEqual(initial_npix, initial.getArea())
472 shrunk = afwDetect.Footprint().assign(initial)
473 shrunk.erode(nshrink)
474 self.assertEqual(shrunk_npix, shrunk.getArea())
476 if display:
477 idImage = afwImage.ImageU(imwidth, imheight)
478 for i, foot in enumerate([initial, shrunk]):
479 print(foot.getArea())
480 foot.spans.setImage(idImage, i + 1)
481 afwDisplay.Display(frame=1).mtv(idImage, title=self._testMethodName + " image")
483 def testShrinkEightVertical(self):
484 # Test a "vertical" figure of 8.
485 radius = 3
486 imwidth, imheight = 100, 100
487 self._fig8Test(imwidth//2, imheight//2 - radius, imwidth//2, imheight//2 + radius)
489 def testShrinkEightHorizontal(self):
490 # Test a "horizontal" figure of 8.
491 radius = 3
492 imwidth, imheight = 100, 100
493 self._fig8Test(imwidth//2 - radius, imheight//2, imwidth//2 + radius, imheight//2)
495 def testGrow(self):
496 """Test growing a footprint"""
497 x0, y0 = 20, 20
498 width, height = 20, 30
499 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
500 lsst.geom.Extent2I(width, height)))
501 foot1 = afwDetect.Footprint(spanSet,
502 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
503 lsst.geom.Extent2I(100, 100)))
505 # Add some peaks and check that they get copied into the new grown footprint
506 foot1.addPeak(20, 20, 1)
507 foot1.addPeak(30, 35, 2)
508 foot1.addPeak(25, 45, 3)
509 self.assertEqual(len(foot1.getPeaks()), 3)
511 bbox1 = foot1.getBBox()
513 self.assertEqual(bbox1.getMinX(), x0)
514 self.assertEqual(bbox1.getMaxX(), x0 + width - 1)
515 self.assertEqual(bbox1.getWidth(), width)
517 self.assertEqual(bbox1.getMinY(), y0)
518 self.assertEqual(bbox1.getMaxY(), y0 + height - 1)
519 self.assertEqual(bbox1.getHeight(), height)
521 ngrow = 5
522 for isotropic in (True, False):
523 foot2 = afwDetect.Footprint().assign(foot1)
524 stencil = afwGeom.Stencil.CIRCLE if isotropic else \
525 afwGeom.Stencil.MANHATTAN
526 foot2.dilate(ngrow, stencil)
528 # Check that the grown footprint is bigger than the original
529 self.assertGreater(foot2.getArea(), foot1.getArea())
531 # Check that peaks got copied into grown footprint
532 self.assertEqual(len(foot2.getPeaks()), 3)
533 for peak in foot2.getPeaks():
534 self.assertIn((peak.getIx(), peak.getIy()),
535 [(20, 20), (30, 35), (25, 45)])
537 bbox2 = foot2.getBBox()
539 # check bbox2
540 self.assertEqual(bbox2.getMinX(), x0 - ngrow)
541 self.assertEqual(bbox2.getWidth(), width + 2*ngrow)
543 self.assertEqual(bbox2.getMinY(), y0 - ngrow)
544 self.assertEqual(bbox2.getHeight(), height + 2*ngrow)
545 # Check that region was preserved
546 self.assertEqual(foot1.getRegion(), foot2.getRegion())
548 def testFootprintToBBoxList(self):
549 """Test footprintToBBoxList"""
550 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(12, 10))
551 foot = afwDetect.Footprint(afwGeom.SpanSet(), region)
552 spanList = [afwGeom.Span(*span) for span in ((3, 3, 5), (3, 7, 7),
553 (4, 2, 3), (4, 5, 7),
554 (5, 2, 3), (5, 5, 8),
555 (6, 3, 5))]
556 foot.spans = afwGeom.SpanSet(spanList)
558 idImage = afwImage.ImageU(region.getDimensions())
559 idImage.set(0)
561 foot.spans.setImage(idImage, 1)
562 if display:
563 disp = afwDisplay.Display(frame=1)
564 disp.mtv(idImage, title=self._testMethodName + " image")
566 idImageFromBBox = idImage.Factory(idImage, True)
567 idImageFromBBox.set(0)
568 bboxes = afwDetect.footprintToBBoxList(foot)
569 for bbox in bboxes:
570 x0, y0, x1, y1 = bbox.getMinX(), bbox.getMinY(), \
571 bbox.getMaxX(), bbox.getMaxY()
573 for y in range(y0, y1 + 1):
574 for x in range(x0, x1 + 1):
575 idImageFromBBox[x, y, afwImage.LOCAL] = 1
577 if display:
578 x0 -= 0.5
579 y0 -= 0.5
580 x1 += 0.5
581 y1 += 0.5
583 disp.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=afwDisplay.RED)
585 idImageFromBBox -= idImage # should be blank
586 stats = afwMath.makeStatistics(idImageFromBBox, afwMath.MAX)
588 self.assertEqual(stats.getValue(), 0)
590 def testWriteDefect(self):
591 """Write a Footprint as a set of Defects"""
592 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(12, 10))
593 spanSet = afwGeom.SpanSet([afwGeom.Span(*span) for span in [(3, 3, 5),
594 (3, 7, 7),
595 (4, 2, 3),
596 (4, 5, 7),
597 (5, 2, 3),
598 (5, 5, 8),
599 (6, 3, 5)]])
600 foot = afwDetect.Footprint(spanSet, region)
602 openedFile = False
603 if True:
604 fd = open("/dev/null", "w")
605 openedFile = True
606 else:
607 fd = sys.stdout
609 afwDetectUtils.writeFootprintAsDefects(fd, foot)
610 if openedFile:
611 fd.close()
613 def testSetFromFootprint(self):
614 """Test setting mask/image pixels from a Footprint list"""
615 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
616 im = mi.getImage()
617 #
618 # Objects that we should detect
619 #
620 self.objects = []
621 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
622 self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])]
623 self.objects += [Object(20, [(6, 3, 3)])]
625 im.set(0) # clear image
626 for obj in self.objects:
627 obj.insert(im)
629 ds = afwDetect.FootprintSet(mi, afwDetect.Threshold(15))
631 objects = ds.getFootprints()
632 afwDetect.setMaskFromFootprintList(mi.getMask(), objects, 0x1)
634 self.assertEqual(mi.getMask()[4, 2, afwImage.LOCAL], 0x0)
635 self.assertEqual(mi.getMask()[3, 6, afwImage.LOCAL], 0x1)
637 self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 20)
638 for ft in objects:
639 ft.spans.setImage(mi.getImage(), 5.0)
640 self.assertEqual(mi.getImage()[4, 2, afwImage.LOCAL], 10)
641 self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 5)
643 if display:
644 afwDisplay.Display(frame=1).mtv(mi, title=self._testMethodName + " image")
645 #
646 # Check Footprint.contains() while we are about it
647 #
648 self.assertTrue(objects[0].contains(lsst.geom.Point2I(7, 5)))
649 self.assertFalse(objects[0].contains(lsst.geom.Point2I(10, 6)))
650 self.assertFalse(objects[0].contains(lsst.geom.Point2I(7, 6)))
651 self.assertFalse(objects[0].contains(lsst.geom.Point2I(4, 2)))
653 self.assertTrue(objects[1].contains(lsst.geom.Point2I(3, 6)))
655 # Verify the FootprintSet footprint list setter can accept inputs from
656 # the footprint list getter
657 # Create a copy of the ds' FootprintList
658 dsFpList = ds.getFootprints()
659 footprintListCopy = [afwDetect.Footprint().assign(f) for f in dsFpList]
660 # Use the FootprintList setter with the output from the getter
661 ds.setFootprints(ds.getFootprints()[:-1])
662 dsFpListNew = ds.getFootprints()
663 self.assertTrue(len(dsFpListNew) == len(footprintListCopy)-1)
664 for new, old in zip(dsFpListNew, footprintListCopy[:-1]):
665 self.assertEqual(new, old)
667 def testMakeFootprintSetXY0(self):
668 """Test setting mask/image pixels from a Footprint list"""
669 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
670 im = mi.getImage()
671 im.set(100)
673 mi.setXY0(lsst.geom.PointI(2, 2))
674 afwDetect.FootprintSet(mi, afwDetect.Threshold(1), "DETECTED")
676 bitmask = mi.getMask().getPlaneBitMask("DETECTED")
677 for y in range(im.getHeight()):
678 for x in range(im.getWidth()):
679 self.assertEqual(mi.getMask()[x, y, afwImage.LOCAL], bitmask)
681 def testTransform(self):
682 dims = lsst.geom.Extent2I(512, 512)
683 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dims)
684 radius = 5
685 offset = lsst.geom.Extent2D(123, 456)
686 crval = lsst.geom.SpherePoint(0, 0, lsst.geom.degrees)
687 crpix = lsst.geom.Point2D(0, 0)
688 cdMatrix = np.array([1.0e-5, 0.0, 0.0, 1.0e-5])
689 cdMatrix.shape = (2, 2)
690 source = afwGeom.makeSkyWcs(crval=crval, crpix=crpix, cdMatrix=cdMatrix)
691 target = afwGeom.makeSkyWcs(crval=crval, crpix=crpix + offset, cdMatrix=cdMatrix)
692 sourceSpanSet = afwGeom.SpanSet.fromShape(radius,
693 afwGeom.Stencil.CIRCLE)
694 sourceSpanSet = sourceSpanSet.shiftedBy(12, 34)
695 fpSource = afwDetect.Footprint(sourceSpanSet, bbox)
697 fpTarget = fpSource.transform(source, target, bbox)
699 self.assertEqual(len(fpSource.getSpans()), len(fpTarget.getSpans()))
700 self.assertEqual(fpSource.getArea(), fpTarget.getArea())
701 imSource = afwImage.ImageU(dims)
702 fpSource.spans.setImage(imSource, 1)
704 imTarget = afwImage.ImageU(dims)
705 fpTarget.spans.setImage(imTarget, 1)
707 subSource = imSource.Factory(imSource, fpSource.getBBox())
708 subTarget = imTarget.Factory(imTarget, fpTarget.getBBox())
709 self.assertTrue(np.all(subSource.getArray() == subTarget.getArray()))
711 # make a bbox smaller than the target footprint
712 bbox2 = lsst.geom.Box2I(fpTarget.getBBox())
713 bbox2.grow(-1)
714 fpTarget2 = fpSource.transform(source, target, bbox2) # this one clips
715 fpTarget3 = fpSource.transform(source, target, bbox2, False) # this one doesn't
716 self.assertTrue(bbox2.contains(fpTarget2.getBBox()))
717 self.assertFalse(bbox2.contains(fpTarget3.getBBox()))
718 self.assertNotEqual(fpTarget.getArea(), fpTarget2.getArea())
719 self.assertEqual(fpTarget.getArea(), fpTarget3.getArea())
721 # Test that peakCatalogs get Transformed correctly
722 truthList = [(x, y, 10) for x, y in zip(range(-2, 2), range(-1, 3))]
723 for value in truthList:
724 fpSource.addPeak(*value)
725 scaleFactor = 2
726 linTrans = lsst.geom.LinearTransform(np.array([[scaleFactor, 0],
727 [0, scaleFactor]],
728 dtype=float))
729 linTransFootprint = fpSource.transform(linTrans, fpSource.getBBox(),
730 False)
731 for peak, truth in zip(linTransFootprint.peaks, truthList):
732 # Multiplied by two because that is the linear transform scaling
733 # factor
734 self.assertEqual(peak.getIx(), truth[0]*scaleFactor)
735 self.assertEqual(peak.getIy(), truth[1]*scaleFactor)
737 def testCopyWithinFootprintImage(self):
738 W, H = 10, 10
739 dims = lsst.geom.Extent2I(W, H)
740 source = afwImage.ImageF(dims)
741 dest = afwImage.ImageF(dims)
742 sa = source.getArray()
743 for i in range(H):
744 for j in range(W):
745 sa[i, j] = 100*i + j
747 footSpans = [s for s in self.foot.spans]
748 footSpans.append(afwGeom.Span(4, 3, 6))
749 footSpans.append(afwGeom.Span(5, 2, 4))
750 self.foot.spans = afwGeom.SpanSet(footSpans)
752 self.foot.spans.copyImage(source, dest)
754 da = dest.getArray()
755 self.assertEqual(da[4, 2], 0)
756 self.assertEqual(da[4, 3], 403)
757 self.assertEqual(da[4, 4], 404)
758 self.assertEqual(da[4, 5], 405)
759 self.assertEqual(da[4, 6], 406)
760 self.assertEqual(da[4, 7], 0)
761 self.assertEqual(da[5, 1], 0)
762 self.assertEqual(da[5, 2], 502)
763 self.assertEqual(da[5, 3], 503)
764 self.assertEqual(da[5, 4], 504)
765 self.assertEqual(da[5, 5], 0)
766 self.assertTrue(np.all(da[:4, :] == 0))
767 self.assertTrue(np.all(da[6:, :] == 0))
769 def testCopyWithinFootprintOutside(self):
770 """Copy a footprint that is larger than the image"""
771 target = afwImage.ImageF(100, 100)
772 target.set(0)
773 subTarget = afwImage.ImageF(target, lsst.geom.Box2I(lsst.geom.Point2I(40, 40),
774 lsst.geom.Extent2I(20, 20)))
775 source = afwImage.ImageF(10, 30)
776 source.setXY0(45, 45)
777 source.set(1.0)
779 foot = afwDetect.Footprint()
780 spanList = [afwGeom.Span(*s) for s in (
781 (50, 50, 60), # Oversized on the source image, right; only some pixels overlap
782 (60, 0, 100), # Oversized on the source, left and right; and on sub-target image, top
783 (99, 0, 1000), # Oversized on the source image, top, left and right; aiming for segfault
784 )]
785 foot.spans = afwGeom.SpanSet(spanList)
787 foot.spans.clippedTo(subTarget.getBBox()).clippedTo(source.getBBox()).\
788 copyImage(source, subTarget)
790 expected = np.zeros((100, 100))
791 expected[50, 50:55] = 1.0
793 self.assertTrue(np.all(target.getArray() == expected))
795 def testCopyWithinFootprintMaskedImage(self):
796 W, H = 10, 10
797 dims = lsst.geom.Extent2I(W, H)
798 source = afwImage.MaskedImageF(dims)
799 dest = afwImage.MaskedImageF(dims)
800 sa = source.getImage().getArray()
801 sv = source.getVariance().getArray()
802 sm = source.getMask().getArray()
803 for i in range(H):
804 for j in range(W):
805 sa[i, j] = 100*i + j
806 sv[i, j] = 100*j + i
807 sm[i, j] = 1
809 footSpans = [s for s in self.foot.spans]
810 footSpans.append(afwGeom.Span(4, 3, 6))
811 footSpans.append(afwGeom.Span(5, 2, 4))
812 self.foot.spans = afwGeom.SpanSet(footSpans)
814 self.foot.spans.copyMaskedImage(source, dest)
816 da = dest.getImage().getArray()
817 dv = dest.getVariance().getArray()
818 dm = dest.getMask().getArray()
820 self.assertEqual(da[4, 2], 0)
821 self.assertEqual(da[4, 3], 403)
822 self.assertEqual(da[4, 4], 404)
823 self.assertEqual(da[4, 5], 405)
824 self.assertEqual(da[4, 6], 406)
825 self.assertEqual(da[4, 7], 0)
826 self.assertEqual(da[5, 1], 0)
827 self.assertEqual(da[5, 2], 502)
828 self.assertEqual(da[5, 3], 503)
829 self.assertEqual(da[5, 4], 504)
830 self.assertEqual(da[5, 5], 0)
831 self.assertTrue(np.all(da[:4, :] == 0))
832 self.assertTrue(np.all(da[6:, :] == 0))
834 self.assertEqual(dv[4, 2], 0)
835 self.assertEqual(dv[4, 3], 304)
836 self.assertEqual(dv[4, 4], 404)
837 self.assertEqual(dv[4, 5], 504)
838 self.assertEqual(dv[4, 6], 604)
839 self.assertEqual(dv[4, 7], 0)
840 self.assertEqual(dv[5, 1], 0)
841 self.assertEqual(dv[5, 2], 205)
842 self.assertEqual(dv[5, 3], 305)
843 self.assertEqual(dv[5, 4], 405)
844 self.assertEqual(dv[5, 5], 0)
845 self.assertTrue(np.all(dv[:4, :] == 0))
846 self.assertTrue(np.all(dv[6:, :] == 0))
848 self.assertTrue(np.all(dm[4, 3:7] == 1))
849 self.assertTrue(np.all(dm[5, 2:5] == 1))
850 self.assertTrue(np.all(dm[:4, :] == 0))
851 self.assertTrue(np.all(dm[6:, :] == 0))
852 self.assertTrue(np.all(dm[4, :3] == 0))
853 self.assertTrue(np.all(dm[4, 7:] == 0))
855 def testMergeFootprints(self):
856 f1 = self.foot
857 f2 = afwDetect.Footprint()
859 spanList1 = [(10, 10, 20),
860 (10, 30, 40),
861 (10, 50, 60),
862 (11, 30, 50),
863 (12, 30, 50),
864 (13, 10, 20),
865 (13, 30, 40),
866 (13, 50, 60),
867 (15, 10, 20),
868 (15, 31, 40),
869 (15, 51, 60)]
870 spanSet1 = afwGeom.SpanSet([afwGeom.Span(*span) for span in spanList1])
871 f1.spans = spanSet1
873 spanList2 = [(8, 10, 20),
874 (9, 20, 30),
875 (10, 0, 9),
876 (10, 35, 65),
877 (10, 70, 80),
878 (13, 49, 54),
879 (14, 10, 30),
880 (15, 21, 30),
881 (15, 41, 50),
882 (15, 61, 70)]
883 spanSet2 = afwGeom.SpanSet([afwGeom.Span(*span) for span in spanList2])
884 f2.spans = spanSet2
886 fA = afwDetect.mergeFootprints(f1, f2)
887 fB = afwDetect.mergeFootprints(f2, f1)
889 ims = []
890 for i, f in enumerate([f1, f2, fA, fB]):
891 im1 = afwImage.ImageU(100, 100)
892 im1.set(0)
893 imbb = im1.getBBox()
894 f.setRegion(imbb)
895 f.spans.setImage(im1, 1)
896 ims.append(im1)
898 for i, merged in enumerate([ims[2], ims[3]]):
899 m = merged.getArray()
900 a1 = ims[0].getArray()
901 a2 = ims[1].getArray()
902 # Slightly looser tests to start...
903 # Every pixel in f1 is in f[AB]
904 self.assertTrue(np.all(m.flat[np.flatnonzero(a1)] == 1))
905 # Every pixel in f2 is in f[AB]
906 self.assertTrue(np.all(m.flat[np.flatnonzero(a2)] == 1))
907 # merged == a1 | a2.
908 self.assertTrue(np.all(m == np.maximum(a1, a2)))
910 def testPeakSort(self):
911 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
912 lsst.geom.Point2I(10, 10)))
913 footprint = afwDetect.Footprint(spanSet)
914 footprint.addPeak(4, 5, 1)
915 footprint.addPeak(3, 2, 5)
916 footprint.addPeak(7, 8, -2)
917 footprint.addPeak(5, 7, 4)
918 footprint.sortPeaks()
919 self.assertEqual([peak.getIx() for peak in footprint.getPeaks()],
920 [3, 5, 4, 7])
922 def testInclude(self):
923 """Test that we can expand a Footprint to include the union of itself and all others provided."""
924 region = lsst.geom.Box2I(lsst.geom.Point2I(-6, -6), lsst.geom.Point2I(6, 6))
925 parentSpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-2, -2),
926 lsst.geom.Point2I(2, 2)))
927 parent = afwDetect.Footprint(parentSpanSet, region)
928 parent.addPeak(0, 0, float("NaN"))
929 child1SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-3, 0),
930 lsst.geom.Point2I(0, 3)))
931 child1 = afwDetect.Footprint(child1SpanSet, region)
932 child1.addPeak(-1, 1, float("NaN"))
933 child2SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-4, -3),
934 lsst.geom.Point2I(-1, 0)))
935 child2 = afwDetect.Footprint(child2SpanSet, region)
936 child3SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(4, -1),
937 lsst.geom.Point2I(6, 1)))
938 child3 = afwDetect.Footprint(child3SpanSet)
939 merge123 = afwDetect.Footprint(parent)
940 merge123.spans = merge123.spans.union(child1.spans).union(child2.spans).union(child3.spans)
941 self.assertTrue(merge123.getBBox().contains(parent.getBBox()))
942 self.assertTrue(merge123.getBBox().contains(child1.getBBox()))
943 self.assertTrue(merge123.getBBox().contains(child2.getBBox()))
944 self.assertTrue(merge123.getBBox().contains(child3.getBBox()))
945 mask123a = afwImage.Mask(region)
946 mask123b = afwImage.Mask(region)
947 parent.spans.setMask(mask123a, 1)
948 child1.spans.setMask(mask123a, 1)
949 child2.spans.setMask(mask123a, 1)
950 child3.spans.setMask(mask123a, 1)
951 merge123.spans.setMask(mask123b, 1)
952 self.assertEqual(mask123a.getArray().sum(), merge123.getArea())
953 self.assertFloatsAlmostEqual(mask123a.getArray(), mask123b.getArray(),
954 rtol=0, atol=0)
956 # Test that ignoreSelf=True works for include
957 childOnly = afwDetect.Footprint()
958 childOnly.spans = childOnly.spans.union(child1.spans).union(child2.spans).union(child3.spans)
959 merge123 = afwDetect.Footprint(parent)
960 merge123.spans = child1.spans.union(child2.spans).union(child3.spans)
961 maskChildren = afwImage.Mask(region)
962 mask123 = afwImage.Mask(region)
963 childOnly.spans.setMask(maskChildren, 1)
964 merge123.spans.setMask(mask123, 1)
965 self.assertTrue(np.all(maskChildren.getArray() == mask123.getArray()))
967 def checkEdge(self, footprint):
968 """Check that Footprint::findEdgePixels() works"""
969 bbox = footprint.getBBox()
970 bbox.grow(3)
972 def makeImage(area):
973 """Make an ImageF with 1 in the footprint, and 0 elsewhere"""
974 ones = afwImage.ImageI(bbox)
975 ones.set(1)
976 image = afwImage.ImageI(bbox)
977 image.set(0)
978 if isinstance(area, afwDetect.Footprint):
979 area.spans.copyImage(ones, image)
980 if isinstance(area, afwGeom.SpanSet):
981 area.copyImage(ones, image)
982 return image
984 edges = self.foot.spans.findEdgePixels()
985 edgeImage = makeImage(edges)
987 # Find edges with an edge-detection kernel
988 image = makeImage(self.foot)
989 kernel = afwImage.ImageD(3, 3)
990 kernel[1, 1, afwImage.LOCAL] = 4
991 for x, y in [(1, 2), (0, 1), (1, 0), (2, 1)]:
992 kernel[x, y, afwImage.LOCAL] = -1
993 kernel.setXY0(1, 1)
994 result = afwImage.ImageI(bbox)
995 result.set(0)
996 afwMath.convolve(result, image, afwMath.FixedKernel(kernel),
997 afwMath.ConvolutionControl(False))
998 result.getArray().__imul__(image.getArray())
999 trueEdges = np.where(result.getArray() > 0, 1, 0)
1001 self.assertTrue(np.all(trueEdges == edgeImage.getArray()))
1003 def testEdge(self):
1004 """Test for Footprint::findEdgePixels()"""
1005 foot = afwDetect.Footprint()
1006 spanList = [afwGeom.Span(*span) for span in ((3, 3, 9),
1007 (4, 2, 4),
1008 (4, 6, 7),
1009 (4, 9, 11),
1010 (5, 3, 9),
1011 (6, 6, 7))]
1012 foot.spans = afwGeom.SpanSet(spanList)
1013 self.checkEdge(foot)
1015 # This footprint came from a very large Footprint in a deep HSC coadd patch
1016 self.checkEdge(afwDetect.Footprint.readFits(
1017 os.path.join(testPath, "testFootprintEdge.fits")))
1020class FootprintSetTestCase(unittest.TestCase):
1021 """A test case for FootprintSet"""
1023 def setUp(self):
1024 self.ms = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
1025 im = self.ms.getImage()
1026 #
1027 # Objects that we should detect
1028 #
1029 self.objects = []
1030 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1031 self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])]
1032 self.objects += [Object(20, [(6, 3, 3)])]
1034 self.ms.set((0, 0x0, 4.0)) # clear image; set variance
1035 for obj in self.objects:
1036 obj.insert(im)
1038 def tearDown(self):
1039 del self.ms
1041 def testGC(self):
1042 """Check that FootprintSets are automatically garbage collected (when MemoryTestCase runs)"""
1043 afwDetect.FootprintSet(afwImage.MaskedImageF(lsst.geom.Extent2I(10, 20)),
1044 afwDetect.Threshold(10))
1046 def testFootprints(self):
1047 """Check that we found the correct number of objects and that they are correct"""
1048 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1050 objects = ds.getFootprints()
1052 self.assertEqual(len(objects), len(self.objects))
1053 for i in range(len(objects)):
1054 self.assertEqual(objects[i], self.objects[i])
1056 def test_str(self):
1057 footprints = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1058 expect = ("3 footprints:"
1059 "\n5 peaks, area=5, centroid=(4, 2)"
1060 "\n5 peaks, area=5, centroid=(8.4, 5.4)"
1061 "\n1 peaks, area=1, centroid=(3, 6)")
1062 self.assertEqual(str(footprints), expect)
1064 def testFootprints2(self):
1065 """Check that we found the correct number of objects using FootprintSet"""
1066 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1068 objects = ds.getFootprints()
1070 self.assertEqual(len(objects), len(self.objects))
1071 for i in range(len(objects)):
1072 self.assertEqual(objects[i], self.objects[i])
1074 def testFootprints3(self):
1075 """Check that we found the correct number of objects using FootprintSet and PIXEL_STDEV"""
1076 threshold = 4.5 # in units of sigma
1078 self.ms[2, 4, afwImage.LOCAL] = (10, 0x0, 36) # not detected (high variance)
1080 y, x = self.objects[2].spans[0][0:2]
1081 self.ms[x, y, afwImage.LOCAL] = (threshold, 0x0, 1.0)
1083 ds = afwDetect.FootprintSet(self.ms,
1084 afwDetect.createThreshold(threshold, "pixel_stdev"), "OBJECT")
1086 objects = ds.getFootprints()
1088 self.assertEqual(len(objects), len(self.objects))
1089 for i in range(len(objects)):
1090 self.assertEqual(objects[i], self.objects[i])
1092 def testFootprintsMasks(self):
1093 """Check that detectionSets have the proper mask bits set"""
1094 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1095 objects = ds.getFootprints()
1097 if display:
1098 afwDisplay.Display(frame=1).mtv(self.ms, title=self._testMethodName + " image")
1100 mask = self.ms.getMask()
1101 for i in range(len(objects)):
1102 for sp in objects[i].getSpans():
1103 for x in range(sp.getX0(), sp.getX1() + 1):
1104 self.assertEqual(mask[x, sp.getY(), afwImage.LOCAL],
1105 mask.getPlaneBitMask("OBJECT"))
1107 def testFootprintsImageId(self):
1108 """Check that we can insert footprints into an Image"""
1109 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1110 objects = ds.getFootprints()
1112 idImage = afwImage.ImageU(self.ms.getDimensions())
1113 idImage.set(0)
1115 for i, foot in enumerate(objects):
1116 foot.spans.setImage(idImage, i + 1)
1118 for i in range(len(objects)):
1119 for sp in objects[i].getSpans():
1120 for x in range(sp.getX0(), sp.getX1() + 1):
1121 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1)
1123 def testFootprintSetImageId(self):
1124 """Check that we can insert a FootprintSet into an Image, setting relative IDs"""
1125 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1126 objects = ds.getFootprints()
1128 idImage = ds.insertIntoImage()
1129 if display:
1130 afwDisplay.Display(frame=2).mtv(idImage, title=self._testMethodName + " image")
1132 for i in range(len(objects)):
1133 for sp in objects[i].getSpans():
1134 for x in range(sp.getX0(), sp.getX1() + 1):
1135 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1)
1137 def testFootprintsImage(self):
1138 """Check that we can search Images as well as MaskedImages"""
1139 ds = afwDetect.FootprintSet(self.ms.getImage(), afwDetect.Threshold(10))
1141 objects = ds.getFootprints()
1143 self.assertEqual(len(objects), len(self.objects))
1144 for i in range(len(objects)):
1145 self.assertEqual(objects[i], self.objects[i])
1147 def testGrow2(self):
1148 """Grow some more interesting shaped Footprints. Informative with display, but no numerical tests"""
1149 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1151 idImage = afwImage.ImageU(self.ms.getDimensions())
1152 idImage.set(0)
1154 i = 1
1155 for foot in ds.getFootprints()[0:1]:
1156 foot.dilate(3, afwGeom.Stencil.MANHATTAN)
1157 foot.spans.setImage(idImage, i, doClip=True)
1158 i += 1
1160 if display:
1161 afwDisplay.Display(frame=0).mtv(self.ms, title=self._testMethodName + " self.ms")
1162 afwDisplay.Display(frame=1).mtv(idImage, title=self._testMethodName + " image")
1164 def testFootprintPeaks(self):
1165 """Test that we can extract the peaks from a Footprint"""
1166 fs = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1168 foot = fs.getFootprints()[0]
1170 self.assertEqual(len(foot.getPeaks()), 5)
1173class MaskFootprintSetTestCase(unittest.TestCase):
1174 """A test case for generating FootprintSet from Masks"""
1176 def setUp(self):
1177 self.mim = afwImage.MaskedImageF(lsst.geom.ExtentI(12, 8))
1178 #
1179 # Objects that we should detect
1180 #
1181 self.objects = []
1182 self.objects += [Object(0x2, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1183 self.objects += [Object(0x41, [(5, 7, 8), (6, 8, 8)])]
1184 self.objects += [Object(0x42, [(5, 10, 10)])]
1185 self.objects += [Object(0x82, [(6, 3, 3)])]
1187 self.mim.set((0, 0, 0)) # clear image
1188 for obj in self.objects:
1189 obj.insert(self.mim.getImage())
1190 obj.insert(self.mim.getMask())
1192 if display:
1193 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim")
1195 def tearDown(self):
1196 del self.mim
1198 def testFootprints(self):
1199 """Check that we found the correct number of objects using FootprintSet"""
1200 level = 0x2
1201 ds = afwDetect.FootprintSet(self.mim.getMask(),
1202 afwDetect.createThreshold(level, "bitmask"))
1204 objects = ds.getFootprints()
1206 if 0 and display:
1207 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim")
1209 self.assertEqual(len(objects),
1210 len([o for o in self.objects if (o.val & level)]))
1212 i = 0
1213 for o in self.objects:
1214 if o.val & level:
1215 self.assertEqual(o, objects[i])
1216 i += 1
1219class NaNFootprintSetTestCase(unittest.TestCase):
1220 """A test case for FootprintSet when the image contains NaNs"""
1222 def setUp(self):
1223 self.ms = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
1224 im = self.ms.getImage()
1225 #
1226 # Objects that we should detect
1227 #
1228 self.objects = []
1229 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1230 self.objects += [Object(20, [(5, 7, 8), (6, 8, 8)])]
1231 self.objects += [Object(20, [(5, 10, 10)])]
1232 self.objects += [Object(30, [(6, 3, 3)])]
1234 im.set(0) # clear image
1235 for obj in self.objects:
1236 obj.insert(im)
1238 self.NaN = float("NaN")
1239 im[3, 7, afwImage.LOCAL] = self.NaN
1240 im[0, 0, afwImage.LOCAL] = self.NaN
1241 im[8, 2, afwImage.LOCAL] = self.NaN
1243 # connects the two objects with value==20 together if NaN is detected
1244 im[9, 6, afwImage.LOCAL] = self.NaN
1246 def tearDown(self):
1247 del self.ms
1249 def testFootprints(self):
1250 """Check that we found the correct number of objects using FootprintSet"""
1251 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "DETECTED")
1253 objects = ds.getFootprints()
1255 if display:
1256 afwDisplay.Display(frame=0).mtv(self.ms, title=self._testMethodName + " self.ms")
1258 self.assertEqual(len(objects), len(self.objects))
1259 for i in range(len(objects)):
1260 self.assertEqual(objects[i], self.objects[i])
1263class MemoryTester(lsst.utils.tests.MemoryTestCase):
1264 pass
1267def setup_module(module):
1268 lsst.utils.tests.init()
1271if __name__ == "__main__": 1271 ↛ 1272line 1271 didn't jump to line 1272, because the condition on line 1271 was never true
1272 lsst.utils.tests.init()
1273 unittest.main()