Coverage for tests/test_footprint1.py: 11%

820 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-15 02:49 -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/>. 

21 

22""" 

23Tests for Footprints, and FootprintSets 

24""" 

25 

26import math 

27import sys 

28import unittest 

29import os 

30 

31import numpy as np 

32 

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 

43 

44try: 

45 type(display) 

46except NameError: 

47 display = False 

48 

49testPath = os.path.abspath(os.path.dirname(__file__)) 

50 

51 

52def toString(*args): 

53 """toString written in python""" 

54 if len(args) == 1: 

55 args = args[0] 

56 

57 y, x0, x1 = args 

58 return f"{y}: {x0}..{x1}" 

59 

60 

61class Object: 

62 

63 def __init__(self, val, spans): 

64 self.val = val 

65 self.spans = spans 

66 

67 def __str__(self): 

68 return ", ".join([str(s) for s in self.spans]) 

69 

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 

76 

77 def __eq__(self, other): 

78 for osp, sp in zip(other.getSpans(), self.spans): 

79 if osp.toString() != toString(sp): 

80 return False 

81 

82 return True 

83 

84 

85class SpanTestCase(unittest.TestCase): 

86 

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) 

96 

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) 

102 

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) 

111 

112 

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``. 

118 

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") 

127 

128 with self.assertRaises(pexExcept.InvalidParameterError): 

129 afwDetect.createThreshold(3.4, "foo bar") 

130 

131 try: 

132 afwDetect.createThreshold(3.4, "variance") 

133 except Exception: 

134 self.fail("Failed to build Threshold with proper parameters") 

135 

136 try: 

137 afwDetect.createThreshold(3.4, "stdev") 

138 except Exception: 

139 self.fail("Failed to build Threshold with proper parameters") 

140 

141 try: 

142 afwDetect.createThreshold(3.4, "value") 

143 except Exception: 

144 self.fail("Failed to build Threshold with proper parameters") 

145 

146 try: 

147 afwDetect.createThreshold(3.4, "value", False) 

148 except Exception: 

149 self.fail("Failed to build Threshold with VALUE, False parameters") 

150 

151 try: 

152 afwDetect.createThreshold(0x4, "bitmask") 

153 except Exception: 

154 self.fail("Failed to build Threshold with BITMASK parameters") 

155 

156 try: 

157 afwDetect.createThreshold(5, "pixel_stdev") 

158 except Exception: 

159 self.fail("Failed to build Threshold with PIXEL_STDEV parameters") 

160 

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)") 

168 

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)") 

173 

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)") 

178 

179 

180class FootprintTestCase(lsst.utils.tests.TestCase): 

181 """A test case for Footprint""" 

182 

183 def setUp(self): 

184 self.foot = afwDetect.Footprint() 

185 

186 def tearDown(self): 

187 del self.foot 

188 

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)) 

193 

194 def testGC(self): 

195 """Check that Footprints are automatically garbage collected (when MemoryTestCase runs)""" 

196 

197 afwDetect.Footprint() 

198 

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) 

209 

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()) 

219 

220 self.assertEqual(fp.getArea(), maskBBox.getArea() - innerBBox.getArea()) 

221 

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()) 

241 

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 

248 

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) 

261 

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) 

268 

269 bbox = foot.getBBox() 

270 

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) 

277 

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() 

285 

286 dx, dy = 10, 20 

287 bbox.shift(lsst.geom.Extent2I(dx, dy)) 

288 

289 self.assertEqual(bbox.getMinX(), x0 + dx) 

290 self.assertEqual(foot.getBBox().getMinX(), x0) 

291 

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))) 

303 

304 idImage = afwImage.ImageU(lsst.geom.Extent2I( 

305 foot.getRegion().getWidth(), foot.getRegion().getHeight())) 

306 idImage.set(0) 

307 

308 foot.spans.setImage(idImage, 42) 

309 

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) 

317 

318 shape = foot.getShape() 

319 shape.scale(2) # <r^2> = 1/2 for a disk 

320 disp.dot(shape, *cen, ctype=afwDisplay.MAGENTA) 

321 

322 axes = afwGeom.ellipses.Axes(foot.getShape()) 

323 axes.scale(2) # <r^2> = 1/2 for a disk 

324 

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}") 

330 

331 def testCopy(self): 

332 bbox = lsst.geom.BoxI(lsst.geom.PointI(0, 2), lsst.geom.PointI(5, 6)) 

333 

334 fp = afwDetect.Footprint(afwGeom.SpanSet(bbox), bbox) 

335 

336 # test copy construct 

337 fp2 = afwDetect.Footprint(fp) 

338 

339 self.assertEqual(fp2.getBBox(), bbox) 

340 self.assertEqual(fp2.getRegion(), bbox) 

341 self.assertEqual(fp2.getArea(), bbox.getArea()) 

342 

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 

349 

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()) 

356 

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 

363 

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 

368 

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) 

375 

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 

382 

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()) 

387 

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)) 

392 

393 # Without shifting the centroid 

394 self.assertEqual(shrunk.getCentroid(), foot.getCentroid()) 

395 

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()) 

401 

402 # Shrinking by a large amount leaves nothing. 

403 shrunkToNothing = afwDetect.Footprint().assign(foot) 

404 shrunkToNothing.erode(100) 

405 self.assertEqual(shrunkToNothing.getArea(), 0) 

406 

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 

413 

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) 

424 

425 foot.erode(nshrink, afwGeom.Stencil.MANHATTAN) 

426 footIsotropic.erode(nshrink) 

427 self.assertNotEqual(foot, footIsotropic) 

428 

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 

436 

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 

442 

443 box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), 

444 lsst.geom.Extent2I(imwidth, imheight)) 

445 

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) 

451 

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) 

457 

458 initial = afwDetect.mergeFootprints(f1, f2) 

459 initial.setRegion(f2.getRegion()) # merge does not propagate the region 

460 self.assertEqual(initial_npix, initial.getArea()) 

461 

462 shrunk = afwDetect.Footprint().assign(initial) 

463 shrunk.erode(nshrink) 

464 self.assertEqual(shrunk_npix, shrunk.getArea()) 

465 

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") 

472 

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) 

478 

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) 

484 

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))) 

494 

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) 

500 

501 bbox1 = foot1.getBBox() 

502 

503 self.assertEqual(bbox1.getMinX(), x0) 

504 self.assertEqual(bbox1.getMaxX(), x0 + width - 1) 

505 self.assertEqual(bbox1.getWidth(), width) 

506 

507 self.assertEqual(bbox1.getMinY(), y0) 

508 self.assertEqual(bbox1.getMaxY(), y0 + height - 1) 

509 self.assertEqual(bbox1.getHeight(), height) 

510 

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) 

517 

518 # Check that the grown footprint is bigger than the original 

519 self.assertGreater(foot2.getArea(), foot1.getArea()) 

520 

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)]) 

526 

527 bbox2 = foot2.getBBox() 

528 

529 # check bbox2 

530 self.assertEqual(bbox2.getMinX(), x0 - ngrow) 

531 self.assertEqual(bbox2.getWidth(), width + 2*ngrow) 

532 

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()) 

537 

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) 

547 

548 idImage = afwImage.ImageU(region.getDimensions()) 

549 idImage.set(0) 

550 

551 foot.spans.setImage(idImage, 1) 

552 if display: 

553 disp = afwDisplay.Display(frame=1) 

554 disp.mtv(idImage, title=self._testMethodName + " image") 

555 

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() 

562 

563 for y in range(y0, y1 + 1): 

564 for x in range(x0, x1 + 1): 

565 idImageFromBBox[x, y, afwImage.LOCAL] = 1 

566 

567 if display: 

568 x0 -= 0.5 

569 y0 -= 0.5 

570 x1 += 0.5 

571 y1 += 0.5 

572 

573 disp.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=afwDisplay.RED) 

574 

575 idImageFromBBox -= idImage # should be blank 

576 stats = afwMath.makeStatistics(idImageFromBBox, afwMath.MAX) 

577 

578 self.assertEqual(stats.getValue(), 0) 

579 

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) 

591 

592 openedFile = False 

593 if True: 

594 fd = open("/dev/null", "w") 

595 openedFile = True 

596 else: 

597 fd = sys.stdout 

598 

599 afwDetectUtils.writeFootprintAsDefects(fd, foot) 

600 if openedFile: 

601 fd.close() 

602 

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)])] 

614 

615 im.set(0) # clear image 

616 for obj in self.objects: 

617 obj.insert(im) 

618 

619 ds = afwDetect.FootprintSet(mi, afwDetect.Threshold(15)) 

620 

621 objects = ds.getFootprints() 

622 afwDetect.setMaskFromFootprintList(mi.getMask(), objects, 0x1) 

623 

624 self.assertEqual(mi.getMask()[4, 2, afwImage.LOCAL], 0x0) 

625 self.assertEqual(mi.getMask()[3, 6, afwImage.LOCAL], 0x1) 

626 

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) 

632 

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))) 

642 

643 self.assertTrue(objects[1].contains(lsst.geom.Point2I(3, 6))) 

644 

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) 

656 

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) 

662 

663 mi.setXY0(lsst.geom.PointI(2, 2)) 

664 afwDetect.FootprintSet(mi, afwDetect.Threshold(1), "DETECTED") 

665 

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) 

670 

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) 

686 

687 fpTarget = fpSource.transform(source, target, bbox) 

688 

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) 

693 

694 imTarget = afwImage.ImageU(dims) 

695 fpTarget.spans.setImage(imTarget, 1) 

696 

697 subSource = imSource.Factory(imSource, fpSource.getBBox()) 

698 subTarget = imTarget.Factory(imTarget, fpTarget.getBBox()) 

699 self.assertTrue(np.all(subSource.getArray() == subTarget.getArray())) 

700 

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()) 

710 

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) 

726 

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 

736 

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) 

741 

742 self.foot.spans.copyImage(source, dest) 

743 

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)) 

758 

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) 

768 

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) 

776 

777 foot.spans.clippedTo(subTarget.getBBox()).clippedTo(source.getBBox()).\ 

778 copyImage(source, subTarget) 

779 

780 expected = np.zeros((100, 100)) 

781 expected[50, 50:55] = 1.0 

782 

783 self.assertTrue(np.all(target.getArray() == expected)) 

784 

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 

798 

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) 

803 

804 self.foot.spans.copyMaskedImage(source, dest) 

805 

806 da = dest.getImage().getArray() 

807 dv = dest.getVariance().getArray() 

808 dm = dest.getMask().getArray() 

809 

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)) 

823 

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)) 

837 

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)) 

844 

845 def testMergeFootprints(self): 

846 f1 = self.foot 

847 f2 = afwDetect.Footprint() 

848 

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 

862 

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 

875 

876 fA = afwDetect.mergeFootprints(f1, f2) 

877 fB = afwDetect.mergeFootprints(f2, f1) 

878 

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) 

887 

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))) 

899 

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]) 

911 

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) 

945 

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())) 

956 

957 def checkEdge(self, footprint): 

958 """Check that Footprint::findEdgePixels() works""" 

959 bbox = footprint.getBBox() 

960 bbox.grow(3) 

961 

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 

973 

974 edges = self.foot.spans.findEdgePixels() 

975 edgeImage = makeImage(edges) 

976 

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) 

990 

991 self.assertTrue(np.all(trueEdges == edgeImage.getArray())) 

992 

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) 

1004 

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"))) 

1008 

1009 

1010class FootprintSetTestCase(unittest.TestCase): 

1011 """A test case for FootprintSet""" 

1012 

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)])] 

1023 

1024 self.ms.set((0, 0x0, 4.0)) # clear image; set variance 

1025 for obj in self.objects: 

1026 obj.insert(im) 

1027 

1028 def tearDown(self): 

1029 del self.ms 

1030 

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)) 

1035 

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)) 

1039 

1040 objects = ds.getFootprints() 

1041 

1042 self.assertEqual(len(objects), len(self.objects)) 

1043 for i in range(len(objects)): 

1044 self.assertEqual(objects[i], self.objects[i]) 

1045 

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) 

1053 

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)) 

1057 

1058 objects = ds.getFootprints() 

1059 

1060 self.assertEqual(len(objects), len(self.objects)) 

1061 for i in range(len(objects)): 

1062 self.assertEqual(objects[i], self.objects[i]) 

1063 

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 

1067 

1068 self.ms[2, 4, afwImage.LOCAL] = (10, 0x0, 36) # not detected (high variance) 

1069 

1070 y, x = self.objects[2].spans[0][0:2] 

1071 self.ms[x, y, afwImage.LOCAL] = (threshold, 0x0, 1.0) 

1072 

1073 ds = afwDetect.FootprintSet(self.ms, 

1074 afwDetect.createThreshold(threshold, "pixel_stdev"), "OBJECT") 

1075 

1076 objects = ds.getFootprints() 

1077 

1078 self.assertEqual(len(objects), len(self.objects)) 

1079 for i in range(len(objects)): 

1080 self.assertEqual(objects[i], self.objects[i]) 

1081 

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() 

1086 

1087 if display: 

1088 afwDisplay.Display(frame=1).mtv(self.ms, title=self._testMethodName + " image") 

1089 

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")) 

1096 

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() 

1101 

1102 idImage = afwImage.ImageU(self.ms.getDimensions()) 

1103 idImage.set(0) 

1104 

1105 for i, foot in enumerate(objects): 

1106 foot.spans.setImage(idImage, i + 1) 

1107 

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) 

1112 

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() 

1117 

1118 idImage = ds.insertIntoImage() 

1119 if display: 

1120 afwDisplay.Display(frame=2).mtv(idImage, title=self._testMethodName + " image") 

1121 

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) 

1126 

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)) 

1130 

1131 objects = ds.getFootprints() 

1132 

1133 self.assertEqual(len(objects), len(self.objects)) 

1134 for i in range(len(objects)): 

1135 self.assertEqual(objects[i], self.objects[i]) 

1136 

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") 

1140 

1141 idImage = afwImage.ImageU(self.ms.getDimensions()) 

1142 idImage.set(0) 

1143 

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 

1149 

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") 

1153 

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") 

1157 

1158 foot = fs.getFootprints()[0] 

1159 

1160 self.assertEqual(len(foot.getPeaks()), 5) 

1161 

1162 

1163class MaskFootprintSetTestCase(unittest.TestCase): 

1164 """A test case for generating FootprintSet from Masks""" 

1165 

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)])] 

1176 

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()) 

1181 

1182 if display: 

1183 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim") 

1184 

1185 def tearDown(self): 

1186 del self.mim 

1187 

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")) 

1193 

1194 objects = ds.getFootprints() 

1195 

1196 if 0 and display: 

1197 afwDisplay.Display(frame=0).mtv(self.mim, title=self._testMethodName + " self.mim") 

1198 

1199 self.assertEqual(len(objects), 

1200 len([o for o in self.objects if (o.val & level)])) 

1201 

1202 i = 0 

1203 for o in self.objects: 

1204 if o.val & level: 

1205 self.assertEqual(o, objects[i]) 

1206 i += 1 

1207 

1208 

1209class NaNFootprintSetTestCase(unittest.TestCase): 

1210 """A test case for FootprintSet when the image contains NaNs""" 

1211 

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)])] 

1223 

1224 im.set(0) # clear image 

1225 for obj in self.objects: 

1226 obj.insert(im) 

1227 

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 

1232 

1233 # connects the two objects with value==20 together if NaN is detected 

1234 im[9, 6, afwImage.LOCAL] = self.NaN 

1235 

1236 def tearDown(self): 

1237 del self.ms 

1238 

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") 

1242 

1243 objects = ds.getFootprints() 

1244 

1245 if display: 

1246 afwDisplay.Display(frame=0).mtv(self.ms, title=self._testMethodName + " self.ms") 

1247 

1248 self.assertEqual(len(objects), len(self.objects)) 

1249 for i in range(len(objects)): 

1250 self.assertEqual(objects[i], self.objects[i]) 

1251 

1252 

1253class MemoryTester(lsst.utils.tests.MemoryTestCase): 

1254 pass 

1255 

1256 

1257def setup_module(module): 

1258 lsst.utils.tests.init() 

1259 

1260 

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()