Coverage for tests/test_footprint1.py: 9%
808 statements
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-05 17:50 -0800
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-05 17:50 -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
25Run with:
26 python test_footprint1.py
27or
28 pytest test_footprint1.py
29"""
31import math
32import sys
33import unittest
34import os
36import numpy as np
38import lsst.utils.tests
39import lsst.geom
40import lsst.afw.geom as afwGeom
41import lsst.afw.geom.ellipses as afwGeomEllipses
42import lsst.afw.image as afwImage
43import lsst.afw.math as afwMath
44import lsst.afw.detection as afwDetect
45import lsst.afw.detection.utils as afwDetectUtils
46import lsst.afw.display as afwDisplay
48try:
49 type(display)
50except NameError:
51 display = False
53testPath = os.path.abspath(os.path.dirname(__file__))
56def toString(*args):
57 """toString written in python"""
58 if len(args) == 1:
59 args = args[0]
61 y, x0, x1 = args
62 return f"{y}: {x0}..{x1}"
65class Object:
67 def __init__(self, val, spans):
68 self.val = val
69 self.spans = spans
71 def __str__(self):
72 return ", ".join([str(s) for s in self.spans])
74 def insert(self, im):
75 """Insert self into an image"""
76 for sp in self.spans:
77 y, x0, x1 = sp
78 for x in range(x0, x1 + 1):
79 im[x, y, afwImage.LOCAL] = self.val
81 def __eq__(self, other):
82 for osp, sp in zip(other.getSpans(), self.spans):
83 if osp.toString() != toString(sp):
84 return False
86 return True
89class SpanTestCase(unittest.TestCase):
91 def testLessThan(self):
92 span1 = afwGeom.Span(42, 0, 100)
93 span2 = afwGeom.Span(41, 0, 100)
94 span3 = afwGeom.Span(43, 0, 100)
95 span4 = afwGeom.Span(42, -100, 100)
96 span5 = afwGeom.Span(42, 100, 200)
97 span6 = afwGeom.Span(42, 0, 10)
98 span7 = afwGeom.Span(42, 0, 200)
99 span8 = afwGeom.Span(42, 0, 100)
101 # Cannot use assertLess and friends here
102 # because Span only has operator <
103 def assertOrder(x1, x2):
104 self.assertTrue(x1 < x2)
105 self.assertFalse(x2 < x1)
107 assertOrder(span2, span1)
108 assertOrder(span1, span3)
109 assertOrder(span4, span1)
110 assertOrder(span1, span5)
111 assertOrder(span6, span1)
112 assertOrder(span1, span7)
113 self.assertFalse(span1 < span8)
114 self.assertFalse(span8 < span1)
117class ThresholdTestCase(unittest.TestCase):
119 def testThresholdFactory(self):
120 """
121 Test the creation of a Threshold object
123 This is a white-box test.
124 -tests missing parameters
125 -tests mal-formed parameters
126 """
127 try:
128 afwDetect.createThreshold(3.4)
129 except Exception:
130 self.fail("Failed to build Threshold with proper parameters")
132 try:
133 afwDetect.createThreshold(3.4, "foo bar")
134 except Exception:
135 pass
136 else:
137 self.fail("Threhold parameters not properly validated")
139 try:
140 afwDetect.createThreshold(3.4, "variance")
141 except Exception:
142 self.fail("Failed to build Threshold with proper parameters")
144 try:
145 afwDetect.createThreshold(3.4, "stdev")
146 except Exception:
147 self.fail("Failed to build Threshold with proper parameters")
149 try:
150 afwDetect.createThreshold(3.4, "value")
151 except Exception:
152 self.fail("Failed to build Threshold with proper parameters")
154 try:
155 afwDetect.createThreshold(3.4, "value", False)
156 except Exception:
157 self.fail("Failed to build Threshold with VALUE, False parameters")
159 try:
160 afwDetect.createThreshold(0x4, "bitmask")
161 except Exception:
162 self.fail("Failed to build Threshold with BITMASK parameters")
164 try:
165 afwDetect.createThreshold(5, "pixel_stdev")
166 except Exception:
167 self.fail("Failed to build Threshold with PIXEL_STDEV parameters")
170class FootprintTestCase(lsst.utils.tests.TestCase):
171 """A test case for Footprint"""
173 def setUp(self):
174 self.foot = afwDetect.Footprint()
176 def tearDown(self):
177 del self.foot
179 def testToString(self):
180 y, x0, x1 = 10, 100, 101
181 s = afwGeom.Span(y, x0, x1)
182 self.assertEqual(s.toString(), toString(y, x0, x1))
184 def testGC(self):
185 """Check that Footprints are automatically garbage collected (when MemoryTestCase runs)"""
187 afwDetect.Footprint()
189 def testIntersectMask(self):
190 bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 0), lsst.geom.ExtentI(10))
191 fp = afwDetect.Footprint(afwGeom.SpanSet(bbox))
192 maskBBox = lsst.geom.BoxI(bbox)
193 maskBBox.grow(-2)
194 mask = afwImage.Mask(maskBBox)
195 innerBBox = lsst.geom.BoxI(maskBBox)
196 innerBBox.grow(-2)
197 subMask = mask.Factory(mask, innerBBox)
198 subMask.set(1)
200 # We only want the pixels that are unmasked, and lie in the bounding box
201 # of the mask, so not the mask (selecting only zero values) and clipped
202 fp.spans = fp.spans.intersectNot(mask).clippedTo(mask.getBBox())
203 fp.removeOrphanPeaks()
204 fpBBox = fp.getBBox()
205 self.assertEqual(fpBBox.getMinX(), maskBBox.getMinX())
206 self.assertEqual(fpBBox.getMinY(), maskBBox.getMinY())
207 self.assertEqual(fpBBox.getMaxX(), maskBBox.getMaxX())
208 self.assertEqual(fpBBox.getMaxY(), maskBBox.getMaxY())
210 self.assertEqual(fp.getArea(), maskBBox.getArea() - innerBBox.getArea())
212 def testTablePersistence(self):
213 ellipse = afwGeom.Ellipse(afwGeomEllipses.Axes(8, 6, 0.25),
214 lsst.geom.Point2D(9, 15))
215 fp1 = afwDetect.Footprint(afwGeom.SpanSet.fromShape(ellipse))
216 fp1.addPeak(6, 7, 2)
217 fp1.addPeak(8, 9, 3)
218 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
219 fp1.writeFits(tmpFile)
220 fp2 = afwDetect.Footprint.readFits(tmpFile)
221 self.assertEqual(fp1.getArea(), fp2.getArea())
222 self.assertEqual(list(fp1.getSpans()), list(fp2.getSpans()))
223 # can't use Peak operator== for comparison because it compares IDs, not positions/values
224 self.assertEqual(len(fp1.getPeaks()), len(fp2.getPeaks()))
225 for peak1, peak2 in zip(fp1.getPeaks(), fp2.getPeaks()):
226 self.assertEqual(peak1.getIx(), peak2.getIx())
227 self.assertEqual(peak1.getIy(), peak2.getIy())
228 self.assertEqual(peak1.getFx(), peak2.getFx())
229 self.assertEqual(peak1.getFy(), peak2.getFy())
230 self.assertEqual(peak1.getPeakValue(), peak2.getPeakValue())
232 def testBbox(self):
233 """Add Spans and check bounding box"""
234 foot = afwDetect.Footprint()
235 spanLists = [afwGeom.Span(10, 100, 105), afwGeom.Span(11, 99, 104)]
236 spanSet = afwGeom.SpanSet(spanLists)
237 foot.spans = spanSet
239 bbox = foot.getBBox()
240 self.assertEqual(bbox.getWidth(), 7)
241 self.assertEqual(bbox.getHeight(), 2)
242 self.assertEqual(bbox.getMinX(), 99)
243 self.assertEqual(bbox.getMinY(), 10)
244 self.assertEqual(bbox.getMaxX(), 105)
245 self.assertEqual(bbox.getMaxY(), 11)
246 # clip with a bbox that doesn't overlap at all
247 bbox2 = lsst.geom.Box2I(lsst.geom.Point2I(5, 90), lsst.geom.Extent2I(1, 2))
248 foot.clipTo(bbox2)
249 self.assertTrue(foot.getBBox().isEmpty())
250 self.assertEqual(foot.getArea(), 0)
252 def testFootprintFromBBox1(self):
253 """Create a rectangular Footprint"""
254 x0, y0, w, h = 9, 10, 7, 4
255 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
256 lsst.geom.Extent2I(w, h)))
257 foot = afwDetect.Footprint(spanSet)
259 bbox = foot.getBBox()
261 self.assertEqual(bbox.getWidth(), w)
262 self.assertEqual(bbox.getHeight(), h)
263 self.assertEqual(bbox.getMinX(), x0)
264 self.assertEqual(bbox.getMinY(), y0)
265 self.assertEqual(bbox.getMaxX(), x0 + w - 1)
266 self.assertEqual(bbox.getMaxY(), y0 + h - 1)
268 def testGetBBox(self):
269 """Check that Footprint.getBBox() returns a copy"""
270 x0, y0, w, h = 9, 10, 7, 4
271 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
272 lsst.geom.Extent2I(w, h)))
273 foot = afwDetect.Footprint(spanSet)
274 bbox = foot.getBBox()
276 dx, dy = 10, 20
277 bbox.shift(lsst.geom.Extent2I(dx, dy))
279 self.assertEqual(bbox.getMinX(), x0 + dx)
280 self.assertEqual(foot.getBBox().getMinX(), x0)
282 def testFootprintFromEllipse(self):
283 """Create an elliptical Footprint"""
284 cen = lsst.geom.Point2D(23, 25)
285 a, b, theta = 25, 15, 30
286 ellipse = afwGeom.Ellipse(
287 afwGeomEllipses.Axes(a, b, math.radians(theta)),
288 cen)
289 spanSet = afwGeom.SpanSet.fromShape(ellipse)
290 foot = afwDetect.Footprint(spanSet,
291 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
292 lsst.geom.Extent2I(50, 60)))
294 idImage = afwImage.ImageU(lsst.geom.Extent2I(
295 foot.getRegion().getWidth(), foot.getRegion().getHeight()))
296 idImage.set(0)
298 foot.spans.setImage(idImage, 42)
300 if display:
301 disp = afwDisplay.Display(frame=2)
302 disp.mtv(idImage, title=self._testMethodName + " image")
303 afwDisplay.utils.drawFootprint(foot, frame=2)
304 shape = foot.getShape()
305 shape.scale(2) # <r^2> = 1/2 for a disk
306 disp.dot(shape, *cen, ctype=afwDisplay.RED)
308 shape = foot.getShape()
309 shape.scale(2) # <r^2> = 1/2 for a disk
310 disp.dot(shape, *cen, ctype=afwDisplay.MAGENTA)
312 axes = afwGeom.ellipses.Axes(foot.getShape())
313 axes.scale(2) # <r^2> = 1/2 for a disk
315 self.assertEqual(foot.getCentroid(), cen)
316 self.assertLess(abs(a - axes.getA()), 0.15, f"a: {a:g} vs. {axes.getA():g}")
317 self.assertLess(abs(b - axes.getB()), 0.02, f"b: {b:g} va. {axes.getB():g}")
318 self.assertLess(abs(theta - math.degrees(axes.getTheta())), 0.2,
319 f"theta: {theta:g} vs. {math.degrees(axes.getTheta()):g}")
321 def testCopy(self):
322 bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 2), lsst.geom.PointI(5, 6))
324 fp = afwDetect.Footprint(afwGeom.SpanSet(bbox), bbox)
326 # test copy construct
327 fp2 = afwDetect.Footprint(fp)
329 self.assertEqual(fp2.getBBox(), bbox)
330 self.assertEqual(fp2.getRegion(), bbox)
331 self.assertEqual(fp2.getArea(), bbox.getArea())
333 y = bbox.getMinY()
334 for s in fp2.getSpans():
335 self.assertEqual(s.getY(), y)
336 self.assertEqual(s.getX0(), bbox.getMinX())
337 self.assertEqual(s.getX1(), bbox.getMaxX())
338 y += 1
340 # test assignment
341 fp3 = afwDetect.Footprint()
342 fp3.assign(fp)
343 self.assertEqual(fp3.getBBox(), bbox)
344 self.assertEqual(fp3.getRegion(), bbox)
345 self.assertEqual(fp3.getArea(), bbox.getArea())
347 y = bbox.getMinY()
348 for s in fp3.getSpans():
349 self.assertEqual(s.getY(), y)
350 self.assertEqual(s.getX0(), bbox.getMinX())
351 self.assertEqual(s.getX1(), bbox.getMaxX())
352 y += 1
354 def testShrink(self):
355 width, height = 5, 10 # Size of footprint
356 x0, y0 = 50, 50 # Position of footprint
357 imwidth, imheight = 100, 100 # Size of image
359 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
360 lsst.geom.Extent2I(width, height)))
361 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
362 lsst.geom.Extent2I(imwidth, imheight))
363 foot = afwDetect.Footprint(spanSet, region)
364 self.assertEqual(foot.getArea(), width*height)
366 # Add some peaks to the original footprint and check that those lying outside
367 # the shrunken footprint are omitted from the returned shrunken footprint.
368 foot.addPeak(50, 50, 1) # should be omitted in shrunken footprint
369 foot.addPeak(52, 52, 2) # should be kept in shrunken footprint
370 foot.addPeak(50, 59, 3) # should be omitted in shrunken footprint
371 self.assertEqual(len(foot.getPeaks()), 3) # check that all three peaks were added
373 # Shrinking by one pixel makes each dimension *two* pixels shorter.
374 shrunk = afwDetect.Footprint().assign(foot)
375 shrunk.erode(1)
376 self.assertEqual(3*8, shrunk.getArea())
378 # Shrunken footprint should now only contain one peak at (52, 52)
379 self.assertEqual(len(shrunk.getPeaks()), 1)
380 peak = shrunk.getPeaks()[0]
381 self.assertEqual((peak.getIx(), peak.getIy()), (52, 52))
383 # Without shifting the centroid
384 self.assertEqual(shrunk.getCentroid(), foot.getCentroid())
386 # Get the same result from a Manhattan shrink
387 shrunk = afwDetect.Footprint().assign(foot)
388 shrunk.erode(1, afwGeom.Stencil.MANHATTAN)
389 self.assertEqual(3*8, shrunk.getArea())
390 self.assertEqual(shrunk.getCentroid(), foot.getCentroid())
392 # Shrinking by a large amount leaves nothing.
393 shrunkToNothing = afwDetect.Footprint().assign(foot)
394 shrunkToNothing.erode(100)
395 self.assertEqual(shrunkToNothing.getArea(), 0)
397 def testShrinkIsoVsManhattan(self):
398 # Demonstrate that isotropic and Manhattan shrinks are different.
399 radius = 8
400 imwidth, imheight = 100, 100
401 x0, y0 = imwidth//2, imheight//2
402 nshrink = 4
404 ellipse = afwGeom.Ellipse(
405 afwGeomEllipses.Axes(1.5*radius, 2*radius, 0),
406 lsst.geom.Point2D(x0, y0))
407 spanSet = afwGeom.SpanSet.fromShape(ellipse)
408 foot = afwDetect.Footprint(
409 spanSet,
410 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
411 lsst.geom.Extent2I(imwidth, imheight)))
412 footIsotropic = afwDetect.Footprint()
413 footIsotropic.assign(foot)
415 foot.erode(nshrink, afwGeom.Stencil.MANHATTAN)
416 footIsotropic.erode(nshrink)
417 self.assertNotEqual(foot, footIsotropic)
419 def _fig8Test(self, x1, y1, x2, y2):
420 # Construct a "figure of 8" consisting of two circles touching at the
421 # centre of an image, then demonstrate that it shrinks correctly.
422 # (Helper method for tests below.)
423 radius = 3
424 imwidth, imheight = 100, 100
425 nshrink = 1
427 # These are the correct values for footprint sizes given the paramters
428 # above.
429 circle_npix = 29
430 initial_npix = circle_npix*2 - 1 # touch at one pixel
431 shrunk_npix = 26
433 box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
434 lsst.geom.Extent2I(imwidth, imheight))
436 e1 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0),
437 lsst.geom.Point2D(x1, y1))
438 spanSet1 = afwGeom.SpanSet.fromShape(e1)
439 f1 = afwDetect.Footprint(spanSet1, box)
440 self.assertEqual(f1.getArea(), circle_npix)
442 e2 = afwGeom.Ellipse(afwGeomEllipses.Axes(radius, radius, 0),
443 lsst.geom.Point2D(x2, y2))
444 spanSet2 = afwGeom.SpanSet.fromShape(e2)
445 f2 = afwDetect.Footprint(spanSet2, box)
446 self.assertEqual(f2.getArea(), circle_npix)
448 initial = afwDetect.mergeFootprints(f1, f2)
449 initial.setRegion(f2.getRegion()) # merge does not propagate the region
450 self.assertEqual(initial_npix, initial.getArea())
452 shrunk = afwDetect.Footprint().assign(initial)
453 shrunk.erode(nshrink)
454 self.assertEqual(shrunk_npix, shrunk.getArea())
456 if display:
457 idImage = afwImage.ImageU(imwidth, imheight)
458 for i, foot in enumerate([initial, shrunk]):
459 print(foot.getArea())
460 foot.spans.setImage(idImage, i + 1)
461 afwDisplay.Display(frame=1).mtv(idImage, title=self._testMethodName + " image")
463 def testShrinkEightVertical(self):
464 # Test a "vertical" figure of 8.
465 radius = 3
466 imwidth, imheight = 100, 100
467 self._fig8Test(imwidth//2, imheight//2 - radius, imwidth//2, imheight//2 + radius)
469 def testShrinkEightHorizontal(self):
470 # Test a "horizontal" figure of 8.
471 radius = 3
472 imwidth, imheight = 100, 100
473 self._fig8Test(imwidth//2 - radius, imheight//2, imwidth//2 + radius, imheight//2)
475 def testGrow(self):
476 """Test growing a footprint"""
477 x0, y0 = 20, 20
478 width, height = 20, 30
479 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(x0, y0),
480 lsst.geom.Extent2I(width, height)))
481 foot1 = afwDetect.Footprint(spanSet,
482 lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
483 lsst.geom.Extent2I(100, 100)))
485 # Add some peaks and check that they get copied into the new grown footprint
486 foot1.addPeak(20, 20, 1)
487 foot1.addPeak(30, 35, 2)
488 foot1.addPeak(25, 45, 3)
489 self.assertEqual(len(foot1.getPeaks()), 3)
491 bbox1 = foot1.getBBox()
493 self.assertEqual(bbox1.getMinX(), x0)
494 self.assertEqual(bbox1.getMaxX(), x0 + width - 1)
495 self.assertEqual(bbox1.getWidth(), width)
497 self.assertEqual(bbox1.getMinY(), y0)
498 self.assertEqual(bbox1.getMaxY(), y0 + height - 1)
499 self.assertEqual(bbox1.getHeight(), height)
501 ngrow = 5
502 for isotropic in (True, False):
503 foot2 = afwDetect.Footprint().assign(foot1)
504 stencil = afwGeom.Stencil.CIRCLE if isotropic else \
505 afwGeom.Stencil.MANHATTAN
506 foot2.dilate(ngrow, stencil)
508 # Check that the grown footprint is bigger than the original
509 self.assertGreater(foot2.getArea(), foot1.getArea())
511 # Check that peaks got copied into grown footprint
512 self.assertEqual(len(foot2.getPeaks()), 3)
513 for peak in foot2.getPeaks():
514 self.assertIn((peak.getIx(), peak.getIy()),
515 [(20, 20), (30, 35), (25, 45)])
517 bbox2 = foot2.getBBox()
519 # check bbox2
520 self.assertEqual(bbox2.getMinX(), x0 - ngrow)
521 self.assertEqual(bbox2.getWidth(), width + 2*ngrow)
523 self.assertEqual(bbox2.getMinY(), y0 - ngrow)
524 self.assertEqual(bbox2.getHeight(), height + 2*ngrow)
525 # Check that region was preserved
526 self.assertEqual(foot1.getRegion(), foot2.getRegion())
528 def testFootprintToBBoxList(self):
529 """Test footprintToBBoxList"""
530 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(12, 10))
531 foot = afwDetect.Footprint(afwGeom.SpanSet(), region)
532 spanList = [afwGeom.Span(*span) for span in ((3, 3, 5), (3, 7, 7),
533 (4, 2, 3), (4, 5, 7),
534 (5, 2, 3), (5, 5, 8),
535 (6, 3, 5))]
536 foot.spans = afwGeom.SpanSet(spanList)
538 idImage = afwImage.ImageU(region.getDimensions())
539 idImage.set(0)
541 foot.spans.setImage(idImage, 1)
542 if display:
543 disp = afwDisplay.Display(frame=1)
544 disp.mtv(idImage, title=self._testMethodName + " image")
546 idImageFromBBox = idImage.Factory(idImage, True)
547 idImageFromBBox.set(0)
548 bboxes = afwDetect.footprintToBBoxList(foot)
549 for bbox in bboxes:
550 x0, y0, x1, y1 = bbox.getMinX(), bbox.getMinY(), \
551 bbox.getMaxX(), bbox.getMaxY()
553 for y in range(y0, y1 + 1):
554 for x in range(x0, x1 + 1):
555 idImageFromBBox[x, y, afwImage.LOCAL] = 1
557 if display:
558 x0 -= 0.5
559 y0 -= 0.5
560 x1 += 0.5
561 y1 += 0.5
563 disp.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=afwDisplay.RED)
565 idImageFromBBox -= idImage # should be blank
566 stats = afwMath.makeStatistics(idImageFromBBox, afwMath.MAX)
568 self.assertEqual(stats.getValue(), 0)
570 def testWriteDefect(self):
571 """Write a Footprint as a set of Defects"""
572 region = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(12, 10))
573 spanSet = afwGeom.SpanSet([afwGeom.Span(*span) for span in [(3, 3, 5),
574 (3, 7, 7),
575 (4, 2, 3),
576 (4, 5, 7),
577 (5, 2, 3),
578 (5, 5, 8),
579 (6, 3, 5)]])
580 foot = afwDetect.Footprint(spanSet, region)
582 openedFile = False
583 if True:
584 fd = open("/dev/null", "w")
585 openedFile = True
586 else:
587 fd = sys.stdout
589 afwDetectUtils.writeFootprintAsDefects(fd, foot)
590 if openedFile:
591 fd.close()
593 def testSetFromFootprint(self):
594 """Test setting mask/image pixels from a Footprint list"""
595 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
596 im = mi.getImage()
597 #
598 # Objects that we should detect
599 #
600 self.objects = []
601 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
602 self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])]
603 self.objects += [Object(20, [(6, 3, 3)])]
605 im.set(0) # clear image
606 for obj in self.objects:
607 obj.insert(im)
609 ds = afwDetect.FootprintSet(mi, afwDetect.Threshold(15))
611 objects = ds.getFootprints()
612 afwDetect.setMaskFromFootprintList(mi.getMask(), objects, 0x1)
614 self.assertEqual(mi.getMask()[4, 2, afwImage.LOCAL], 0x0)
615 self.assertEqual(mi.getMask()[3, 6, afwImage.LOCAL], 0x1)
617 self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 20)
618 for ft in objects:
619 ft.spans.setImage(mi.getImage(), 5.0)
620 self.assertEqual(mi.getImage()[4, 2, afwImage.LOCAL], 10)
621 self.assertEqual(mi.getImage()[3, 6, afwImage.LOCAL], 5)
623 if display:
624 afwDisplay.Display(frame=1).mtv(mi, title=self._testMethodName + " image")
625 #
626 # Check Footprint.contains() while we are about it
627 #
628 self.assertTrue(objects[0].contains(lsst.geom.Point2I(7, 5)))
629 self.assertFalse(objects[0].contains(lsst.geom.Point2I(10, 6)))
630 self.assertFalse(objects[0].contains(lsst.geom.Point2I(7, 6)))
631 self.assertFalse(objects[0].contains(lsst.geom.Point2I(4, 2)))
633 self.assertTrue(objects[1].contains(lsst.geom.Point2I(3, 6)))
635 # Verify the FootprintSet footprint list setter can accept inputs from
636 # the footprint list getter
637 # Create a copy of the ds' FootprintList
638 dsFpList = ds.getFootprints()
639 footprintListCopy = [afwDetect.Footprint().assign(f) for f in dsFpList]
640 # Use the FootprintList setter with the output from the getter
641 ds.setFootprints(ds.getFootprints()[:-1])
642 dsFpListNew = ds.getFootprints()
643 self.assertTrue(len(dsFpListNew) == len(footprintListCopy)-1)
644 for new, old in zip(dsFpListNew, footprintListCopy[:-1]):
645 self.assertEqual(new, old)
647 def testMakeFootprintSetXY0(self):
648 """Test setting mask/image pixels from a Footprint list"""
649 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
650 im = mi.getImage()
651 im.set(100)
653 mi.setXY0(lsst.geom.PointI(2, 2))
654 afwDetect.FootprintSet(mi, afwDetect.Threshold(1), "DETECTED")
656 bitmask = mi.getMask().getPlaneBitMask("DETECTED")
657 for y in range(im.getHeight()):
658 for x in range(im.getWidth()):
659 self.assertEqual(mi.getMask()[x, y, afwImage.LOCAL], bitmask)
661 def testTransform(self):
662 dims = lsst.geom.Extent2I(512, 512)
663 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), dims)
664 radius = 5
665 offset = lsst.geom.Extent2D(123, 456)
666 crval = lsst.geom.SpherePoint(0, 0, lsst.geom.degrees)
667 crpix = lsst.geom.Point2D(0, 0)
668 cdMatrix = np.array([1.0e-5, 0.0, 0.0, 1.0e-5])
669 cdMatrix.shape = (2, 2)
670 source = afwGeom.makeSkyWcs(crval=crval, crpix=crpix, cdMatrix=cdMatrix)
671 target = afwGeom.makeSkyWcs(crval=crval, crpix=crpix + offset, cdMatrix=cdMatrix)
672 sourceSpanSet = afwGeom.SpanSet.fromShape(radius,
673 afwGeom.Stencil.CIRCLE)
674 sourceSpanSet = sourceSpanSet.shiftedBy(12, 34)
675 fpSource = afwDetect.Footprint(sourceSpanSet, bbox)
677 fpTarget = fpSource.transform(source, target, bbox)
679 self.assertEqual(len(fpSource.getSpans()), len(fpTarget.getSpans()))
680 self.assertEqual(fpSource.getArea(), fpTarget.getArea())
681 imSource = afwImage.ImageU(dims)
682 fpSource.spans.setImage(imSource, 1)
684 imTarget = afwImage.ImageU(dims)
685 fpTarget.spans.setImage(imTarget, 1)
687 subSource = imSource.Factory(imSource, fpSource.getBBox())
688 subTarget = imTarget.Factory(imTarget, fpTarget.getBBox())
689 self.assertTrue(np.all(subSource.getArray() == subTarget.getArray()))
691 # make a bbox smaller than the target footprint
692 bbox2 = lsst.geom.Box2I(fpTarget.getBBox())
693 bbox2.grow(-1)
694 fpTarget2 = fpSource.transform(source, target, bbox2) # this one clips
695 fpTarget3 = fpSource.transform(source, target, bbox2, False) # this one doesn't
696 self.assertTrue(bbox2.contains(fpTarget2.getBBox()))
697 self.assertFalse(bbox2.contains(fpTarget3.getBBox()))
698 self.assertNotEqual(fpTarget.getArea(), fpTarget2.getArea())
699 self.assertEqual(fpTarget.getArea(), fpTarget3.getArea())
701 # Test that peakCatalogs get Transformed correctly
702 truthList = [(x, y, 10) for x, y in zip(range(-2, 2), range(-1, 3))]
703 for value in truthList:
704 fpSource.addPeak(*value)
705 scaleFactor = 2
706 linTrans = lsst.geom.LinearTransform(np.array([[scaleFactor, 0],
707 [0, scaleFactor]],
708 dtype=float))
709 linTransFootprint = fpSource.transform(linTrans, fpSource.getBBox(),
710 False)
711 for peak, truth in zip(linTransFootprint.peaks, truthList):
712 # Multiplied by two because that is the linear transform scaling
713 # factor
714 self.assertEqual(peak.getIx(), truth[0]*scaleFactor)
715 self.assertEqual(peak.getIy(), truth[1]*scaleFactor)
717 def testCopyWithinFootprintImage(self):
718 W, H = 10, 10
719 dims = lsst.geom.Extent2I(W, H)
720 source = afwImage.ImageF(dims)
721 dest = afwImage.ImageF(dims)
722 sa = source.getArray()
723 for i in range(H):
724 for j in range(W):
725 sa[i, j] = 100*i + j
727 footSpans = [s for s in self.foot.spans]
728 footSpans.append(afwGeom.Span(4, 3, 6))
729 footSpans.append(afwGeom.Span(5, 2, 4))
730 self.foot.spans = afwGeom.SpanSet(footSpans)
732 self.foot.spans.copyImage(source, dest)
734 da = dest.getArray()
735 self.assertEqual(da[4, 2], 0)
736 self.assertEqual(da[4, 3], 403)
737 self.assertEqual(da[4, 4], 404)
738 self.assertEqual(da[4, 5], 405)
739 self.assertEqual(da[4, 6], 406)
740 self.assertEqual(da[4, 7], 0)
741 self.assertEqual(da[5, 1], 0)
742 self.assertEqual(da[5, 2], 502)
743 self.assertEqual(da[5, 3], 503)
744 self.assertEqual(da[5, 4], 504)
745 self.assertEqual(da[5, 5], 0)
746 self.assertTrue(np.all(da[:4, :] == 0))
747 self.assertTrue(np.all(da[6:, :] == 0))
749 def testCopyWithinFootprintOutside(self):
750 """Copy a footprint that is larger than the image"""
751 target = afwImage.ImageF(100, 100)
752 target.set(0)
753 subTarget = afwImage.ImageF(target, lsst.geom.Box2I(lsst.geom.Point2I(40, 40),
754 lsst.geom.Extent2I(20, 20)))
755 source = afwImage.ImageF(10, 30)
756 source.setXY0(45, 45)
757 source.set(1.0)
759 foot = afwDetect.Footprint()
760 spanList = [afwGeom.Span(*s) for s in (
761 (50, 50, 60), # Oversized on the source image, right; only some pixels overlap
762 (60, 0, 100), # Oversized on the source, left and right; and on sub-target image, top
763 (99, 0, 1000), # Oversized on the source image, top, left and right; aiming for segfault
764 )]
765 foot.spans = afwGeom.SpanSet(spanList)
767 foot.spans.clippedTo(subTarget.getBBox()).clippedTo(source.getBBox()).\
768 copyImage(source, subTarget)
770 expected = np.zeros((100, 100))
771 expected[50, 50:55] = 1.0
773 self.assertTrue(np.all(target.getArray() == expected))
775 def testCopyWithinFootprintMaskedImage(self):
776 W, H = 10, 10
777 dims = lsst.geom.Extent2I(W, H)
778 source = afwImage.MaskedImageF(dims)
779 dest = afwImage.MaskedImageF(dims)
780 sa = source.getImage().getArray()
781 sv = source.getVariance().getArray()
782 sm = source.getMask().getArray()
783 for i in range(H):
784 for j in range(W):
785 sa[i, j] = 100*i + j
786 sv[i, j] = 100*j + i
787 sm[i, j] = 1
789 footSpans = [s for s in self.foot.spans]
790 footSpans.append(afwGeom.Span(4, 3, 6))
791 footSpans.append(afwGeom.Span(5, 2, 4))
792 self.foot.spans = afwGeom.SpanSet(footSpans)
794 self.foot.spans.copyMaskedImage(source, dest)
796 da = dest.getImage().getArray()
797 dv = dest.getVariance().getArray()
798 dm = dest.getMask().getArray()
800 self.assertEqual(da[4, 2], 0)
801 self.assertEqual(da[4, 3], 403)
802 self.assertEqual(da[4, 4], 404)
803 self.assertEqual(da[4, 5], 405)
804 self.assertEqual(da[4, 6], 406)
805 self.assertEqual(da[4, 7], 0)
806 self.assertEqual(da[5, 1], 0)
807 self.assertEqual(da[5, 2], 502)
808 self.assertEqual(da[5, 3], 503)
809 self.assertEqual(da[5, 4], 504)
810 self.assertEqual(da[5, 5], 0)
811 self.assertTrue(np.all(da[:4, :] == 0))
812 self.assertTrue(np.all(da[6:, :] == 0))
814 self.assertEqual(dv[4, 2], 0)
815 self.assertEqual(dv[4, 3], 304)
816 self.assertEqual(dv[4, 4], 404)
817 self.assertEqual(dv[4, 5], 504)
818 self.assertEqual(dv[4, 6], 604)
819 self.assertEqual(dv[4, 7], 0)
820 self.assertEqual(dv[5, 1], 0)
821 self.assertEqual(dv[5, 2], 205)
822 self.assertEqual(dv[5, 3], 305)
823 self.assertEqual(dv[5, 4], 405)
824 self.assertEqual(dv[5, 5], 0)
825 self.assertTrue(np.all(dv[:4, :] == 0))
826 self.assertTrue(np.all(dv[6:, :] == 0))
828 self.assertTrue(np.all(dm[4, 3:7] == 1))
829 self.assertTrue(np.all(dm[5, 2:5] == 1))
830 self.assertTrue(np.all(dm[:4, :] == 0))
831 self.assertTrue(np.all(dm[6:, :] == 0))
832 self.assertTrue(np.all(dm[4, :3] == 0))
833 self.assertTrue(np.all(dm[4, 7:] == 0))
835 def testMergeFootprints(self):
836 f1 = self.foot
837 f2 = afwDetect.Footprint()
839 spanList1 = [(10, 10, 20),
840 (10, 30, 40),
841 (10, 50, 60),
842 (11, 30, 50),
843 (12, 30, 50),
844 (13, 10, 20),
845 (13, 30, 40),
846 (13, 50, 60),
847 (15, 10, 20),
848 (15, 31, 40),
849 (15, 51, 60)]
850 spanSet1 = afwGeom.SpanSet([afwGeom.Span(*span) for span in spanList1])
851 f1.spans = spanSet1
853 spanList2 = [(8, 10, 20),
854 (9, 20, 30),
855 (10, 0, 9),
856 (10, 35, 65),
857 (10, 70, 80),
858 (13, 49, 54),
859 (14, 10, 30),
860 (15, 21, 30),
861 (15, 41, 50),
862 (15, 61, 70)]
863 spanSet2 = afwGeom.SpanSet([afwGeom.Span(*span) for span in spanList2])
864 f2.spans = spanSet2
866 fA = afwDetect.mergeFootprints(f1, f2)
867 fB = afwDetect.mergeFootprints(f2, f1)
869 ims = []
870 for i, f in enumerate([f1, f2, fA, fB]):
871 im1 = afwImage.ImageU(100, 100)
872 im1.set(0)
873 imbb = im1.getBBox()
874 f.setRegion(imbb)
875 f.spans.setImage(im1, 1)
876 ims.append(im1)
878 for i, merged in enumerate([ims[2], ims[3]]):
879 m = merged.getArray()
880 a1 = ims[0].getArray()
881 a2 = ims[1].getArray()
882 # Slightly looser tests to start...
883 # Every pixel in f1 is in f[AB]
884 self.assertTrue(np.all(m.flat[np.flatnonzero(a1)] == 1))
885 # Every pixel in f2 is in f[AB]
886 self.assertTrue(np.all(m.flat[np.flatnonzero(a2)] == 1))
887 # merged == a1 | a2.
888 self.assertTrue(np.all(m == np.maximum(a1, a2)))
890 def testPeakSort(self):
891 spanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
892 lsst.geom.Point2I(10, 10)))
893 footprint = afwDetect.Footprint(spanSet)
894 footprint.addPeak(4, 5, 1)
895 footprint.addPeak(3, 2, 5)
896 footprint.addPeak(7, 8, -2)
897 footprint.addPeak(5, 7, 4)
898 footprint.sortPeaks()
899 self.assertEqual([peak.getIx() for peak in footprint.getPeaks()],
900 [3, 5, 4, 7])
902 def testInclude(self):
903 """Test that we can expand a Footprint to include the union of itself and all others provided."""
904 region = lsst.geom.Box2I(lsst.geom.Point2I(-6, -6), lsst.geom.Point2I(6, 6))
905 parentSpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-2, -2),
906 lsst.geom.Point2I(2, 2)))
907 parent = afwDetect.Footprint(parentSpanSet, region)
908 parent.addPeak(0, 0, float("NaN"))
909 child1SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-3, 0),
910 lsst.geom.Point2I(0, 3)))
911 child1 = afwDetect.Footprint(child1SpanSet, region)
912 child1.addPeak(-1, 1, float("NaN"))
913 child2SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(-4, -3),
914 lsst.geom.Point2I(-1, 0)))
915 child2 = afwDetect.Footprint(child2SpanSet, region)
916 child3SpanSet = afwGeom.SpanSet(lsst.geom.Box2I(lsst.geom.Point2I(4, -1),
917 lsst.geom.Point2I(6, 1)))
918 child3 = afwDetect.Footprint(child3SpanSet)
919 merge123 = afwDetect.Footprint(parent)
920 merge123.spans = merge123.spans.union(child1.spans).union(child2.spans).union(child3.spans)
921 self.assertTrue(merge123.getBBox().contains(parent.getBBox()))
922 self.assertTrue(merge123.getBBox().contains(child1.getBBox()))
923 self.assertTrue(merge123.getBBox().contains(child2.getBBox()))
924 self.assertTrue(merge123.getBBox().contains(child3.getBBox()))
925 mask123a = afwImage.Mask(region)
926 mask123b = afwImage.Mask(region)
927 parent.spans.setMask(mask123a, 1)
928 child1.spans.setMask(mask123a, 1)
929 child2.spans.setMask(mask123a, 1)
930 child3.spans.setMask(mask123a, 1)
931 merge123.spans.setMask(mask123b, 1)
932 self.assertEqual(mask123a.getArray().sum(), merge123.getArea())
933 self.assertFloatsAlmostEqual(mask123a.getArray(), mask123b.getArray(),
934 rtol=0, atol=0)
936 # Test that ignoreSelf=True works for include
937 childOnly = afwDetect.Footprint()
938 childOnly.spans = childOnly.spans.union(child1.spans).union(child2.spans).union(child3.spans)
939 merge123 = afwDetect.Footprint(parent)
940 merge123.spans = child1.spans.union(child2.spans).union(child3.spans)
941 maskChildren = afwImage.Mask(region)
942 mask123 = afwImage.Mask(region)
943 childOnly.spans.setMask(maskChildren, 1)
944 merge123.spans.setMask(mask123, 1)
945 self.assertTrue(np.all(maskChildren.getArray() == mask123.getArray()))
947 def checkEdge(self, footprint):
948 """Check that Footprint::findEdgePixels() works"""
949 bbox = footprint.getBBox()
950 bbox.grow(3)
952 def makeImage(area):
953 """Make an ImageF with 1 in the footprint, and 0 elsewhere"""
954 ones = afwImage.ImageI(bbox)
955 ones.set(1)
956 image = afwImage.ImageI(bbox)
957 image.set(0)
958 if isinstance(area, afwDetect.Footprint):
959 area.spans.copyImage(ones, image)
960 if isinstance(area, afwGeom.SpanSet):
961 area.copyImage(ones, image)
962 return image
964 edges = self.foot.spans.findEdgePixels()
965 edgeImage = makeImage(edges)
967 # Find edges with an edge-detection kernel
968 image = makeImage(self.foot)
969 kernel = afwImage.ImageD(3, 3)
970 kernel[1, 1, afwImage.LOCAL] = 4
971 for x, y in [(1, 2), (0, 1), (1, 0), (2, 1)]:
972 kernel[x, y, afwImage.LOCAL] = -1
973 kernel.setXY0(1, 1)
974 result = afwImage.ImageI(bbox)
975 result.set(0)
976 afwMath.convolve(result, image, afwMath.FixedKernel(kernel),
977 afwMath.ConvolutionControl(False))
978 result.getArray().__imul__(image.getArray())
979 trueEdges = np.where(result.getArray() > 0, 1, 0)
981 self.assertTrue(np.all(trueEdges == edgeImage.getArray()))
983 def testEdge(self):
984 """Test for Footprint::findEdgePixels()"""
985 foot = afwDetect.Footprint()
986 spanList = [afwGeom.Span(*span) for span in ((3, 3, 9),
987 (4, 2, 4),
988 (4, 6, 7),
989 (4, 9, 11),
990 (5, 3, 9),
991 (6, 6, 7))]
992 foot.spans = afwGeom.SpanSet(spanList)
993 self.checkEdge(foot)
995 # This footprint came from a very large Footprint in a deep HSC coadd patch
996 self.checkEdge(afwDetect.Footprint.readFits(
997 os.path.join(testPath, "testFootprintEdge.fits")))
1000class FootprintSetTestCase(unittest.TestCase):
1001 """A test case for FootprintSet"""
1003 def setUp(self):
1004 self.ms = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
1005 im = self.ms.getImage()
1006 #
1007 # Objects that we should detect
1008 #
1009 self.objects = []
1010 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1011 self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])]
1012 self.objects += [Object(20, [(6, 3, 3)])]
1014 self.ms.set((0, 0x0, 4.0)) # clear image; set variance
1015 for obj in self.objects:
1016 obj.insert(im)
1018 def tearDown(self):
1019 del self.ms
1021 def testGC(self):
1022 """Check that FootprintSets are automatically garbage collected (when MemoryTestCase runs)"""
1023 afwDetect.FootprintSet(afwImage.MaskedImageF(lsst.geom.Extent2I(10, 20)),
1024 afwDetect.Threshold(10))
1026 def testFootprints(self):
1027 """Check that we found the correct number of objects and that they are correct"""
1028 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1030 objects = ds.getFootprints()
1032 self.assertEqual(len(objects), len(self.objects))
1033 for i in range(len(objects)):
1034 self.assertEqual(objects[i], self.objects[i])
1036 def testFootprints2(self):
1037 """Check that we found the correct number of objects using FootprintSet"""
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 testFootprints3(self):
1047 """Check that we found the correct number of objects using FootprintSet and PIXEL_STDEV"""
1048 threshold = 4.5 # in units of sigma
1050 self.ms[2, 4, afwImage.LOCAL] = (10, 0x0, 36) # not detected (high variance)
1052 y, x = self.objects[2].spans[0][0:2]
1053 self.ms[x, y, afwImage.LOCAL] = (threshold, 0x0, 1.0)
1055 ds = afwDetect.FootprintSet(self.ms,
1056 afwDetect.createThreshold(threshold, "pixel_stdev"), "OBJECT")
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 testFootprintsMasks(self):
1065 """Check that detectionSets have the proper mask bits set"""
1066 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1067 objects = ds.getFootprints()
1069 if display:
1070 afwDisplay.Display(frame=1).mtv(self.ms, title=self._testMethodName + " image")
1072 mask = self.ms.getMask()
1073 for i in range(len(objects)):
1074 for sp in objects[i].getSpans():
1075 for x in range(sp.getX0(), sp.getX1() + 1):
1076 self.assertEqual(mask[x, sp.getY(), afwImage.LOCAL],
1077 mask.getPlaneBitMask("OBJECT"))
1079 def testFootprintsImageId(self):
1080 """Check that we can insert footprints into an Image"""
1081 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1082 objects = ds.getFootprints()
1084 idImage = afwImage.ImageU(self.ms.getDimensions())
1085 idImage.set(0)
1087 for i, foot in enumerate(objects):
1088 foot.spans.setImage(idImage, i + 1)
1090 for i in range(len(objects)):
1091 for sp in objects[i].getSpans():
1092 for x in range(sp.getX0(), sp.getX1() + 1):
1093 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1)
1095 def testFootprintSetImageId(self):
1096 """Check that we can insert a FootprintSet into an Image, setting relative IDs"""
1097 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10))
1098 objects = ds.getFootprints()
1100 idImage = ds.insertIntoImage()
1101 if display:
1102 afwDisplay.Display(frame=2).mtv(idImage, title=self._testMethodName + " image")
1104 for i in range(len(objects)):
1105 for sp in objects[i].getSpans():
1106 for x in range(sp.getX0(), sp.getX1() + 1):
1107 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1)
1109 def testFootprintsImage(self):
1110 """Check that we can search Images as well as MaskedImages"""
1111 ds = afwDetect.FootprintSet(self.ms.getImage(), afwDetect.Threshold(10))
1113 objects = ds.getFootprints()
1115 self.assertEqual(len(objects), len(self.objects))
1116 for i in range(len(objects)):
1117 self.assertEqual(objects[i], self.objects[i])
1119 def testGrow2(self):
1120 """Grow some more interesting shaped Footprints. Informative with display, but no numerical tests"""
1121 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1123 idImage = afwImage.ImageU(self.ms.getDimensions())
1124 idImage.set(0)
1126 i = 1
1127 for foot in ds.getFootprints()[0:1]:
1128 foot.dilate(3, afwGeom.Stencil.MANHATTAN)
1129 foot.spans.setImage(idImage, i, doClip=True)
1130 i += 1
1132 if display:
1133 afwDisplay.Display(frame=0).mtv(self.ms, title=self._testMethodName + " self.ms")
1134 afwDisplay.Display(frame=1).mtv(idImage, title=self._testMethodName + " image")
1136 def testFootprintPeaks(self):
1137 """Test that we can extract the peaks from a Footprint"""
1138 fs = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "OBJECT")
1140 foot = fs.getFootprints()[0]
1142 self.assertEqual(len(foot.getPeaks()), 5)
1145class MaskFootprintSetTestCase(unittest.TestCase):
1146 """A test case for generating FootprintSet from Masks"""
1148 def setUp(self):
1149 self.mim = afwImage.MaskedImageF(lsst.geom.ExtentI(12, 8))
1150 #
1151 # Objects that we should detect
1152 #
1153 self.objects = []
1154 self.objects += [Object(0x2, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1155 self.objects += [Object(0x41, [(5, 7, 8), (6, 8, 8)])]
1156 self.objects += [Object(0x42, [(5, 10, 10)])]
1157 self.objects += [Object(0x82, [(6, 3, 3)])]
1159 self.mim.set((0, 0, 0)) # clear image
1160 for obj in self.objects:
1161 obj.insert(self.mim.getImage())
1162 obj.insert(self.mim.getMask())
1164 if display:
1165 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim")
1167 def tearDown(self):
1168 del self.mim
1170 def testFootprints(self):
1171 """Check that we found the correct number of objects using FootprintSet"""
1172 level = 0x2
1173 ds = afwDetect.FootprintSet(self.mim.getMask(),
1174 afwDetect.createThreshold(level, "bitmask"))
1176 objects = ds.getFootprints()
1178 if 0 and display:
1179 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim")
1181 self.assertEqual(len(objects),
1182 len([o for o in self.objects if (o.val & level)]))
1184 i = 0
1185 for o in self.objects:
1186 if o.val & level:
1187 self.assertEqual(o, objects[i])
1188 i += 1
1191class NaNFootprintSetTestCase(unittest.TestCase):
1192 """A test case for FootprintSet when the image contains NaNs"""
1194 def setUp(self):
1195 self.ms = afwImage.MaskedImageF(lsst.geom.Extent2I(12, 8))
1196 im = self.ms.getImage()
1197 #
1198 # Objects that we should detect
1199 #
1200 self.objects = []
1201 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])]
1202 self.objects += [Object(20, [(5, 7, 8), (6, 8, 8)])]
1203 self.objects += [Object(20, [(5, 10, 10)])]
1204 self.objects += [Object(30, [(6, 3, 3)])]
1206 im.set(0) # clear image
1207 for obj in self.objects:
1208 obj.insert(im)
1210 self.NaN = float("NaN")
1211 im[3, 7, afwImage.LOCAL] = self.NaN
1212 im[0, 0, afwImage.LOCAL] = self.NaN
1213 im[8, 2, afwImage.LOCAL] = self.NaN
1215 # connects the two objects with value==20 together if NaN is detected
1216 im[9, 6, afwImage.LOCAL] = self.NaN
1218 def tearDown(self):
1219 del self.ms
1221 def testFootprints(self):
1222 """Check that we found the correct number of objects using FootprintSet"""
1223 ds = afwDetect.FootprintSet(self.ms, afwDetect.Threshold(10), "DETECTED")
1225 objects = ds.getFootprints()
1227 if display:
1228 afwDisplay.Display(frame=0).mtv(self.ms, title=self._testMethodName + " self.ms")
1230 self.assertEqual(len(objects), len(self.objects))
1231 for i in range(len(objects)):
1232 self.assertEqual(objects[i], self.objects[i])
1235class MemoryTester(lsst.utils.tests.MemoryTestCase):
1236 pass
1239def setup_module(module):
1240 lsst.utils.tests.init()
1243if __name__ == "__main__": 1243 ↛ 1244line 1243 didn't jump to line 1244, because the condition on line 1243 was never true
1244 lsst.utils.tests.init()
1245 unittest.main()