Coverage for tests/test_footprint2.py: 11%

448 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-09 02:52 -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 

25Run with: 

26 python test_footprint2.py 

27or 

28 pytest test_footprint2.py 

29""" 

30 

31import unittest 

32 

33import lsst.utils.tests 

34import lsst.geom 

35import lsst.afw.table as afwTable 

36import lsst.afw.image as afwImage 

37import lsst.afw.geom as afwGeom 

38import lsst.afw.detection as afwDetect 

39import lsst.afw.display as afwDisplay 

40 

41afwDisplay.setDefaultMaskTransparency(75) 

42try: 

43 type(display) 

44except NameError: 

45 display = False 

46 

47 

48def toString(*args): 

49 """toString written in python""" 

50 if len(args) == 1: 

51 args = args[0] 

52 

53 y, x0, x1 = args 

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

55 

56 

57def peakFromImage(im, pos): 

58 """Function to extract the sort key of peak height. Sort by decreasing peak height.""" 

59 val = im[pos[0], pos[1], afwImage.LOCAL][0] 

60 return -1.0*val 

61 

62 

63class Object: 

64 

65 def __init__(self, val, spans): 

66 self.val = val 

67 self.spans = spans 

68 

69 def insert(self, im): 

70 """Insert self into an image""" 

71 for sp in self.spans: 

72 y, x0, x1 = sp 

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

74 im[x, y, afwImage.LOCAL] = self.val 

75 

76 def __eq__(self, other): 

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

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

79 return False 

80 

81 return True 

82 

83 

84class FootprintSetTestCase(unittest.TestCase): 

85 """A test case for FootprintSet""" 

86 

87 def setUp(self): 

88 self.im = afwImage.ImageU(lsst.geom.Extent2I(12, 8)) 

89 # 

90 # Objects that we should detect 

91 # 

92 self.objects = [] 

93 self.objects += [Object(10, [(1, 4, 4), (2, 3, 5), (3, 4, 4)])] 

94 self.objects += [Object(20, [(5, 7, 8), (5, 10, 10), (6, 8, 9)])] 

95 self.objects += [Object(20, [(6, 3, 3)])] 

96 

97 self.im.set(0) # clear image 

98 for obj in self.objects: 

99 obj.insert(self.im) 

100 

101 def tearDown(self): 

102 del self.im 

103 

104 def testGC(self): 

105 """Check that FootprintSets are automatically garbage collected (when MemoryTestCase runs)""" 

106 

107 afwDetect.FootprintSet(afwImage.ImageU(lsst.geom.Extent2I(10, 20)), 

108 afwDetect.Threshold(10)) 

109 

110 def testFootprints(self): 

111 """Check that we found the correct number of objects and that they are correct""" 

112 ds = afwDetect.FootprintSet(self.im, afwDetect.Threshold(10)) 

113 

114 objects = ds.getFootprints() 

115 

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

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

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

119 

120 def testFootprints2(self): 

121 """Check that we found the correct number of objects using FootprintSet""" 

122 ds = afwDetect.FootprintSet(self.im, afwDetect.Threshold(10)) 

123 

124 objects = ds.getFootprints() 

125 

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

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

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

129 

130 def testFootprintsImageId(self): 

131 """Check that we can insert footprints into an Image""" 

132 ds = afwDetect.FootprintSet(self.im, afwDetect.Threshold(10)) 

133 objects = ds.getFootprints() 

134 

135 idImage = afwImage.ImageU(self.im.getDimensions()) 

136 idImage.set(0) 

137 

138 for i, foot in enumerate(objects): 

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

140 

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

142 for sp in objects[i].getSpans(): 

143 for x in range(sp.getX0(), sp.getX1() + 1): 

144 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1) 

145 

146 def testFootprintSetImageId(self): 

147 """Check that we can insert a FootprintSet into an Image, setting relative IDs""" 

148 ds = afwDetect.FootprintSet(self.im, afwDetect.Threshold(10)) 

149 objects = ds.getFootprints() 

150 

151 idImage = ds.insertIntoImage() 

152 if display: 

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

154 

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

156 for sp in objects[i].getSpans(): 

157 for x in range(sp.getX0(), sp.getX1() + 1): 

158 self.assertEqual(idImage[x, sp.getY(), afwImage.LOCAL], i + 1) 

159 

160 def testFootprintsImage(self): 

161 """Check that we can search Images as well as MaskedImages""" 

162 ds = afwDetect.FootprintSet(self.im, afwDetect.Threshold(10)) 

163 

164 objects = ds.getFootprints() 

165 

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

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

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

169 

170 def testGrow2(self): 

171 """Grow some more interesting shaped Footprints. Informative with display, but no numerical tests""" 

172 # Can't set mask plane as the image is not a masked image. 

173 ds = afwDetect.FootprintSet(self.im, afwDetect.Threshold(10)) 

174 

175 idImage = afwImage.ImageU(self.im.getDimensions()) 

176 idImage.set(0) 

177 

178 i = 1 

179 for foot in ds.getFootprints()[0:1]: 

180 foot.dilate(3, afwGeom.Stencil.MANHATTAN) 

181 foot.spans.setImage(idImage, i, doClip=True) 

182 i += 1 

183 

184 if display: 

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

186 

187 def testGrow(self): 

188 """Grow footprints using the FootprintSet constructor""" 

189 fs = afwDetect.FootprintSet(self.im, afwDetect.Threshold(10)) 

190 self.assertEqual(len(fs.getFootprints()), len(self.objects)) 

191 for isotropic in (True, False, afwDetect.FootprintControl(True),): 

192 grown = afwDetect.FootprintSet(fs, 1, isotropic) 

193 self.assertEqual(len(fs.getFootprints()), len(self.objects)) 

194 

195 self.assertGreater(len(grown.getFootprints()), 0) 

196 self.assertLessEqual(len(grown.getFootprints()), 

197 len(fs.getFootprints())) 

198 

199 def testFootprintControl(self): 

200 """Test the FootprintControl constructor""" 

201 fctrl = afwDetect.FootprintControl() 

202 self.assertFalse(fctrl.isCircular()[0]) # not set 

203 self.assertFalse(fctrl.isIsotropic()[0]) # not set 

204 

205 fctrl.growIsotropic(False) 

206 self.assertTrue(fctrl.isCircular()[0]) 

207 self.assertTrue(fctrl.isIsotropic()[0]) 

208 self.assertTrue(fctrl.isCircular()[1]) 

209 self.assertFalse(fctrl.isIsotropic()[1]) 

210 

211 fctrl = afwDetect.FootprintControl() 

212 fctrl.growLeft(False) 

213 self.assertTrue(fctrl.isLeft()[0]) # it's now set 

214 self.assertFalse(fctrl.isLeft()[1]) # ... but False 

215 

216 fctrl = afwDetect.FootprintControl(True, False, False, False) 

217 self.assertTrue(fctrl.isLeft()[0]) 

218 self.assertTrue(fctrl.isRight()[0]) 

219 self.assertTrue(fctrl.isUp()[0]) 

220 self.assertTrue(fctrl.isDown()[0]) 

221 

222 self.assertTrue(fctrl.isLeft()[1]) 

223 self.assertFalse(fctrl.isRight()[1]) 

224 

225 def testGrowCircular(self): 

226 """Grow footprints in all 4 directions using the FootprintSet/FootprintControl constructor """ 

227 im = afwImage.MaskedImageF(11, 11) 

228 im[5, 5, afwImage.LOCAL] = (10, 0x0, 0.0) 

229 fs = afwDetect.FootprintSet(im, afwDetect.Threshold(10)) 

230 self.assertEqual(len(fs.getFootprints()), 1) 

231 

232 radius = 3 # How much to grow by 

233 for fctrl in (afwDetect.FootprintControl(), 

234 afwDetect.FootprintControl(True), 

235 afwDetect.FootprintControl(True, True), 

236 ): 

237 grown = afwDetect.FootprintSet(fs, radius, fctrl) 

238 afwDetect.setMaskFromFootprintList( 

239 im.getMask(), grown.getFootprints(), 0x10) 

240 

241 if display: 

242 afwDisplay.Display(frame=3).mtv(im, title=self._testMethodName + " image") 

243 

244 foot = grown.getFootprints()[0] 

245 

246 if not fctrl.isCircular()[0]: 

247 self.assertEqual(foot.getArea(), 1) 

248 elif fctrl.isCircular()[0]: 

249 assert radius == 3 

250 if fctrl.isIsotropic()[1]: 

251 self.assertEqual(foot.getArea(), 29) 

252 else: 

253 self.assertEqual(foot.getArea(), 25) 

254 

255 def testGrowLRUD(self): 

256 """Grow footprints in various directions using the FootprintSet/FootprintControl constructor """ 

257 im = afwImage.MaskedImageF(11, 11) 

258 x0, y0, ny = 5, 5, 3 

259 for y in range(y0 - ny//2, y0 + ny//2 + 1): 

260 im[x0, y, afwImage.LOCAL] = (10, 0x0, 0.0) 

261 fs = afwDetect.FootprintSet(im, afwDetect.Threshold(10)) 

262 self.assertEqual(len(fs.getFootprints()), 1) 

263 

264 ngrow = 2 # How much to grow by 

265 # 

266 # Test growing to the left and/or right 

267 # 

268 for fctrl in ( 

269 afwDetect.FootprintControl(False, True, False, False), 

270 afwDetect.FootprintControl(True, False, False, False), 

271 afwDetect.FootprintControl(True, True, False, False), 

272 ): 

273 fs = afwDetect.FootprintSet(im, afwDetect.Threshold(10)) 

274 grown = afwDetect.FootprintSet(fs, ngrow, fctrl) 

275 im.getMask().set(0) 

276 afwDetect.setMaskFromFootprintList( 

277 im.getMask(), grown.getFootprints(), 0x10) 

278 

279 if display: 

280 afwDisplay.Display(frame=3).mtv(im, title=self._testMethodName + " image") 

281 

282 foot = grown.getFootprints()[0] 

283 nextra = 0 

284 if fctrl.isLeft()[1]: 

285 nextra += ngrow 

286 for y in range(y0 - ny//2, y0 + ny//2 + 1): 

287 self.assertNotEqual(im.getMask()[x0 - 1, y, afwImage.LOCAL], 0) 

288 

289 if fctrl.isRight()[1]: 

290 nextra += ngrow 

291 for y in range(y0 - ny//2, y0 + ny//2 + 1): 

292 self.assertNotEqual(im.getMask()[x0 + 1, y, afwImage.LOCAL], 0) 

293 

294 self.assertEqual(foot.getArea(), (1 + nextra)*ny) 

295 # 

296 # Test growing to up and/or down 

297 # 

298 for fctrl in ( 

299 afwDetect.FootprintControl(False, False, True, False), 

300 afwDetect.FootprintControl(False, False, False, True), 

301 afwDetect.FootprintControl(False, False, True, True), 

302 ): 

303 grown = afwDetect.FootprintSet(fs, ngrow, fctrl) 

304 im.getMask().set(0) 

305 afwDetect.setMaskFromFootprintList( 

306 im.getMask(), grown.getFootprints(), 0x10) 

307 

308 if display: 

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

310 

311 foot = grown.getFootprints()[0] 

312 nextra = 0 

313 if fctrl.isUp()[1]: 

314 nextra += ngrow 

315 for y in range(y0 + ny//2 + 1, y0 + ny//2 + ngrow + 1): 

316 self.assertNotEqual(im.getMask()[x0, y, afwImage.LOCAL], 0) 

317 

318 if fctrl.isDown()[1]: 

319 nextra += ngrow 

320 for y in range(y0 - ny//2 - 1, y0 - ny//2 - ngrow - 1): 

321 self.assertNotEqual(im.getMask()[x0, y, afwImage.LOCAL], 0) 

322 

323 self.assertEqual(foot.getArea(), ny + nextra) 

324 

325 def testGrowLRUD2(self): 

326 """Grow footprints in various directions using the FootprintSet/FootprintControl constructor 

327 

328 Check that overlapping grown Footprints give the expected answers 

329 """ 

330 ngrow = 3 # How much to grow by 

331 for fctrl, xy in [ 

332 (afwDetect.FootprintControl(True, True, 

333 False, False), [(4, 5), (5, 6), (6, 5)]), 

334 (afwDetect.FootprintControl(False, False, 

335 True, True), [(5, 4), (6, 5), (5, 6)]), 

336 ]: 

337 im = afwImage.MaskedImageF(11, 11) 

338 for x, y in xy: 

339 im[x, y, afwImage.LOCAL] = (10, 0x0, 0.0) 

340 fs = afwDetect.FootprintSet(im, afwDetect.Threshold(10)) 

341 self.assertEqual(len(fs.getFootprints()), 1) 

342 

343 grown = afwDetect.FootprintSet(fs, ngrow, fctrl) 

344 im.getMask().set(0) 

345 afwDetect.setMaskFromFootprintList( 

346 im.getMask(), grown.getFootprints(), 0x10) 

347 

348 if display: 

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

350 

351 self.assertEqual(len(grown.getFootprints()), 1) 

352 foot = grown.getFootprints()[0] 

353 

354 npix = 1 + 2*ngrow 

355 npix += 3 + 2*ngrow # 3: distance between pair of set pixels 000X0X000 

356 self.assertEqual(foot.getArea(), npix) 

357 

358 def testInf(self): 

359 """Test detection for images with Infs""" 

360 

361 im = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 20)) 

362 im.set(0) 

363 

364 import numpy 

365 for x in range(im.getWidth()): 

366 im[x, -1, afwImage.LOCAL] = (numpy.Inf, 0x0, 0) 

367 

368 ds = afwDetect.FootprintSet(im, afwDetect.createThreshold(100)) 

369 

370 objects = ds.getFootprints() 

371 afwDetect.setMaskFromFootprintList(im.getMask(), objects, 0x10) 

372 

373 if display: 

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

375 

376 self.assertEqual(len(objects), 1) 

377 

378 

379class PeaksInFootprintsTestCase(unittest.TestCase): 

380 """A test case for detecting Peaks within Footprints""" 

381 

382 def doSetUp(self, dwidth=0, dheight=0, x0=0, y0=0): 

383 width, height = 14 + x0 + dwidth, 10 + y0 + dheight 

384 self.im = afwImage.MaskedImageF(lsst.geom.Extent2I(width, height)) 

385 # 

386 # Objects that we should detect 

387 # 

388 self.objects, self.peaks = [], [] 

389 self.objects.append( 

390 [[4, 1, 10], [3, 2, 10], [4, 2, 20], [5, 2, 10], [4, 3, 10], ]) 

391 self.peaks.append([[4, 2]]) 

392 self.objects.append( 

393 [[9, 7, 30], [10, 7, 29], [12, 7, 25], [10, 8, 27], [11, 8, 26], ]) 

394 self.peaks.append([[9, 7]]) 

395 self.objects.append([[3, 8, 10], [4, 8, 10], ]) 

396 self.peaks.append([[3, 8], [4, 8], ]) 

397 

398 for pp in self.peaks: # allow for x0, y0 

399 for p in pp: 

400 p[0] += x0 

401 p[1] += y0 

402 for oo in self.objects: 

403 for o in oo: 

404 o[0] += x0 

405 o[1] += y0 

406 

407 self.im.set((0, 0x0, 0)) # clear image 

408 for obj in self.objects: 

409 for x, y, I in obj: 

410 self.im.getImage()[x, y, afwImage.LOCAL] = I 

411 

412 def setUp(self): 

413 self.im, self.fs = None, None 

414 

415 def tearDown(self): 

416 del self.im 

417 del self.fs 

418 

419 def doTestPeaks(self, dwidth=0, dheight=0, x0=0, y0=0, threshold=10, callback=None, polarity=True, 

420 grow=0): 

421 """Worker routine for tests 

422 polarity: True if should search for +ve pixels""" 

423 

424 self.doSetUp(dwidth, dheight, x0, y0) 

425 if not polarity: 

426 self.im *= -1 

427 

428 if callback: 

429 callback() 

430 

431 def peakDescending(p): 

432 """Sort self.peaks in decreasing peak height to match Footprint.getPeaks()""" 

433 return p[2]*-1.0 

434 for i, peaks in enumerate(self.peaks): 

435 self.peaks[i] = sorted([(x, y, self.im.getImage()[x, y, afwImage.LOCAL]) for x, y in peaks], 

436 key=peakDescending) 

437 

438 threshold = afwDetect.Threshold( 

439 threshold, afwDetect.Threshold.VALUE, polarity) 

440 fs = afwDetect.FootprintSet(self.im, threshold, "BINNED1") 

441 

442 if grow: 

443 fs = afwDetect.FootprintSet(fs, grow, True) 

444 msk = self.im.getMask() 

445 afwDetect.setMaskFromFootprintList( 

446 msk, fs.getFootprints(), msk.getPlaneBitMask("DETECTED")) 

447 del msk 

448 

449 self.fs = fs 

450 self.checkPeaks(dwidth, dheight, frame=3) 

451 

452 def checkPeaks(self, dwidth=0, dheight=0, frame=3): 

453 """Check that we got the peaks right""" 

454 feet = self.fs.getFootprints() 

455 # 

456 # Check that we found all the peaks 

457 # 

458 self.assertEqual(sum([len(f.getPeaks()) for f in feet]), 

459 sum([len(f.getPeaks()) for f in feet])) 

460 

461 if display: 

462 disp = afwDisplay.Display(frame=frame) 

463 disp.mtv(self.im, title=self._testMethodName + " image") 

464 

465 with disp.Buffering(): 

466 for i, foot in enumerate(feet): 

467 for p in foot.getPeaks(): 

468 disp.dot("+", p.getIx(), p.getIy(), size=0.4) 

469 

470 if i < len(self.peaks): 

471 for trueX, trueY, peakVal in self.peaks[i]: 

472 disp.dot("x", trueX, trueY, size=0.4, ctype=afwDisplay.RED) 

473 

474 for i, foot in enumerate(feet): 

475 npeak = None 

476 # 

477 # Peaks that touch the edge are handled differently, as only the single highest/lowest pixel 

478 # is treated as a Peak 

479 # 

480 if (dwidth != 0 or dheight != 0): 

481 if (foot.getBBox().getMinX() == 0 

482 or foot.getBBox().getMaxX() == self.im.getWidth() - 1 

483 or foot.getBBox().getMinY() == 0 

484 or foot.getBBox().getMaxY() == self.im.getHeight() - 1): 

485 npeak = 1 

486 

487 if npeak is None: 

488 npeak = len(self.peaks[i]) 

489 

490 self.assertEqual(len(foot.getPeaks()), npeak) 

491 

492 for j, p in enumerate(foot.getPeaks()): 

493 trueX, trueY, peakVal = self.peaks[i][j] 

494 self.assertEqual((p.getIx(), p.getIy()), (trueX, trueY)) 

495 

496 def testSinglePeak(self): 

497 """Test that we can find single Peaks in Footprints""" 

498 

499 self.doTestPeaks() 

500 

501 def testSingleNegativePeak(self): 

502 """Test that we can find single Peaks in Footprints when looking for -ve detections""" 

503 

504 self.doTestPeaks(polarity=False) 

505 

506 def testSinglePeakAtEdge(self): 

507 """Test that we handle Peaks correctly at the edge""" 

508 

509 self.doTestPeaks(dheight=-1) 

510 

511 def testSingleNegativePeakAtEdge(self): 

512 """Test that we handle -ve Peaks correctly at the edge""" 

513 

514 self.doTestPeaks(dheight=-1, polarity=False) 

515 

516 def testMultiPeak(self): 

517 """Test that multiple peaks are handled correctly""" 

518 def callback(): 

519 x, y = 12, 7 

520 self.im.getImage()[x, y, afwImage.LOCAL] = 100 

521 self.peaks[1].append((x, y)) 

522 

523 self.doTestPeaks(callback=callback) 

524 

525 def testMultiNegativePeak(self): 

526 """Test that multiple negative peaks are handled correctly""" 

527 def callback(): 

528 x, y = 12, 7 

529 self.im.getImage()[x, y, afwImage.LOCAL] = -100 

530 self.peaks[1].append((x, y)) 

531 

532 self.doTestPeaks(polarity=False, callback=callback) 

533 

534 def testGrowFootprints(self): 

535 """Test that we can grow footprints, correctly merging those that now touch""" 

536 def callback(): 

537 self.im.getImage()[10, 4, afwImage.LOCAL] = 20 

538 self.peaks[-2].append((10, 4,)) 

539 

540 self.doTestPeaks(dwidth=1, dheight=1, callback=callback, grow=1) 

541 

542 def testGrowFootprints2(self): 

543 """Test that we can grow footprints, correctly merging those that now overlap 

544 N.b. this caused RHL's initial implementation to crash 

545 """ 

546 def callback(): 

547 self.im.getImage()[10, 4, afwImage.LOCAL] = 20 

548 self.peaks[-2].append((10, 4, )) 

549 

550 def peaksSortKey(p): 

551 return peakFromImage(self.im, p) 

552 self.peaks[0] = sorted(sum(self.peaks, []), key=peaksSortKey) 

553 

554 self.doTestPeaks(x0=0, y0=2, dwidth=2, dheight=2, 

555 callback=callback, grow=2) 

556 

557 def testGrowFootprints3(self): 

558 """Test that we can grow footprints, correctly merging those that now totally overwritten""" 

559 

560 self.im = afwImage.MaskedImageF(14, 11) 

561 

562 self.im.getImage().set(0) 

563 self.peaks = [] 

564 

565 value = 11 

566 for x, y in [(4, 7), (5, 7), (6, 7), (7, 7), (8, 7), 

567 (4, 6), (8, 6), 

568 (4, 5), (8, 5), 

569 (4, 4), (8, 4), 

570 (4, 3), (8, 3), 

571 ]: 

572 self.im.getImage()[x, y, afwImage.LOCAL] = value 

573 value -= 1e-3 

574 

575 self.im.getImage()[4, 7, afwImage.LOCAL] = 15 

576 self.peaks.append([(4, 7,), ]) 

577 

578 self.im.getImage()[6, 5, afwImage.LOCAL] = 30 

579 self.peaks[0].append((6, 5,)) 

580 

581 self.fs = afwDetect.FootprintSet( 

582 self.im, afwDetect.Threshold(10), "BINNED1") 

583 # 

584 # The disappearing Footprint special case only shows up if the outer Footprint is grown 

585 # _after_ the inner one. So arrange the order properly 

586 feet = self.fs.getFootprints() 

587 feet[0], feet[1] = feet[1], feet[0] 

588 

589 msk = self.im.getMask() 

590 

591 grow = 2 

592 self.fs = afwDetect.FootprintSet(self.fs, grow, False) 

593 afwDetect.setMaskFromFootprintList(msk, self.fs.getFootprints(), 

594 msk.getPlaneBitMask("DETECTED_NEGATIVE")) 

595 

596 if display: 

597 frame = 0 

598 

599 disp = afwDisplay.Display(frame=frame) 

600 disp.mtv(self.im, title=self._testMethodName + " image") 

601 

602 with disp.Buffering(): 

603 for i, foot in enumerate(self.fs.getFootprints()): 

604 for p in foot.getPeaks(): 

605 disp.dot("+", p.getIx(), p.getIy(), size=0.4) 

606 

607 if i < len(self.peaks): 

608 for trueX, trueY in self.peaks[i]: 

609 disp.dot("x", trueX, trueY, size=0.4, ctype=afwDisplay.RED) 

610 

611 self.assertEqual(len(self.fs.getFootprints()), 1) 

612 self.assertEqual(len(self.fs.getFootprints()[ 

613 0].getPeaks()), len(self.peaks[0])) 

614 

615 def testMergeFootprints(self): # YYYY 

616 """Merge positive and negative Footprints""" 

617 x0, y0 = 5, 6 

618 dwidth, dheight = 6, 7 

619 

620 def callback(): 

621 x, y, value = x0 + 10, y0 + 4, -20 

622 self.im.getImage()[x, y, afwImage.LOCAL] = value 

623 peaks2.append((x, y, value)) 

624 

625 for grow1, grow2 in [(1, 1), (3, 3), (6, 6), ]: 

626 peaks2 = [] 

627 self.doTestPeaks(threshold=10, callback=callback, grow=0, 

628 x0=x0, y0=y0, dwidth=dwidth, dheight=dheight) 

629 

630 threshold = afwDetect.Threshold( 

631 10, afwDetect.Threshold.VALUE, False) 

632 fs2 = afwDetect.FootprintSet(self.im, threshold) 

633 

634 msk = self.im.getMask() 

635 afwDetect.setMaskFromFootprintList( 

636 msk, fs2.getFootprints(), msk.getPlaneBitMask("DETECTED_NEGATIVE")) 

637 

638 self.fs.merge(fs2, grow1, grow2) 

639 self.peaks[-2] += peaks2 

640 

641 if grow1 + grow2 > 2: # grow merged all peaks 

642 def peaksSortKey(p): 

643 return peakFromImage(self.im, p) 

644 self.peaks[0] = sorted(sum(self.peaks, []), key=peaksSortKey) 

645 

646 afwDetect.setMaskFromFootprintList( 

647 msk, self.fs.getFootprints(), msk.getPlaneBitMask("EDGE")) 

648 

649 self.checkPeaks(frame=3) 

650 

651 def testMergeFootprintPeakSchemas(self): 

652 """Test that merging footprints preserves fields in the peak schemas. 

653 """ 

654 self.doSetUp() 

655 self.im.getImage()[3, 4, afwImage.LOCAL] = 100 

656 self.im.getImage()[3, 5, afwImage.LOCAL] = 200 

657 self.im.getImage()[6, 7, afwImage.LOCAL] = 400 

658 

659 threshold = afwDetect.Threshold(10, afwDetect.Threshold.VALUE, True) 

660 fs1 = afwDetect.FootprintSet(self.im, threshold) 

661 threshold = afwDetect.Threshold(150, afwDetect.Threshold.VALUE, True) 

662 fs2 = afwDetect.FootprintSet(self.im, threshold) 

663 

664 def addSignificance(footprints): 

665 """Return a new FootprintSet with a significance field added.""" 

666 mapper = afwTable.SchemaMapper(footprints.getFootprints()[0].peaks.schema) 

667 mapper.addMinimalSchema(footprints.getFootprints()[0].peaks.schema) 

668 mapper.addOutputField("significance", type=float, 

669 doc="Ratio of peak value to configured standard deviation.") 

670 

671 newFootprints = afwDetect.FootprintSet(footprints) 

672 for old, new in zip(footprints.getFootprints(), newFootprints.getFootprints()): 

673 newPeaks = afwDetect.PeakCatalog(mapper.getOutputSchema()) 

674 newPeaks.extend(old.peaks, mapper=mapper) 

675 new.getPeaks().clear() 

676 new.setPeakCatalog(newPeaks) 

677 

678 for footprint in newFootprints.getFootprints(): 

679 for peak in footprint.peaks: 

680 peak['significance'] = 10 

681 

682 return newFootprints 

683 

684 def checkPeakSignificance(footprints): 

685 """Check that all peaks have significance=10.""" 

686 for footprint in footprints.getFootprints(): 

687 self.assertIn("significance", footprint.peaks.schema) 

688 for peak in footprint.peaks: 

689 self.assertEqual(peak["significance"], 10) 

690 

691 newFs1 = addSignificance(fs1) 

692 

693 # Check that mismatched peak schemas raise an exception in merge(). 

694 with self.assertRaisesRegex(RuntimeError, 

695 "FootprintSets to be merged must have identical peak schemas"): 

696 fs1.merge(newFs1) 

697 

698 with self.assertRaisesRegex(RuntimeError, 

699 "FootprintSets to be merged must have identical peak schemas"): 

700 newFs1.merge(fs2) 

701 

702 newFs2 = addSignificance(fs2) 

703 

704 # Check that the field was added correctly. 

705 checkPeakSignificance(newFs1) 

706 checkPeakSignificance(newFs2) 

707 

708 # Does merging footprints preserve the added field? 

709 newFs1.merge(newFs2) 

710 checkPeakSignificance(newFs1) 

711 checkPeakSignificance(newFs2) 

712 

713 def testMergeFootprintsEngulf(self): 

714 """Merge two Footprints when growing one Footprint totally replaces the other""" 

715 def callback(): 

716 self.im.set(0) 

717 self.peaks, self.objects = [], [] 

718 

719 for x, y, I in [[6, 4, 20], [6, 5, 10]]: 

720 self.im.getImage()[x, y, afwImage.LOCAL] = I 

721 self.peaks.append([[6, 4]]) 

722 

723 x, y, value = 8, 4, -20 

724 self.im.getImage()[x, y, afwImage.LOCAL] = value 

725 peaks2.append((x, y, value)) 

726 

727 grow1, grow2 = 0, 3 

728 peaks2 = [] 

729 self.doTestPeaks(threshold=10, callback=callback, grow=0) 

730 

731 threshold = afwDetect.Threshold(10, afwDetect.Threshold.VALUE, False) 

732 fs2 = afwDetect.FootprintSet(self.im, threshold) 

733 

734 msk = self.im.getMask() 

735 afwDetect.setMaskFromFootprintList( 

736 msk, fs2.getFootprints(), msk.getPlaneBitMask("DETECTED_NEGATIVE")) 

737 

738 self.fs.merge(fs2, grow1, grow2) 

739 self.peaks[0] += peaks2 

740 

741 def peaksSortKey(p): 

742 return peakFromImage(self.im, p) 

743 self.peaks[0] = sorted(sum(self.peaks, []), key=peaksSortKey) 

744 

745 afwDetect.setMaskFromFootprintList( 

746 msk, self.fs.getFootprints(), msk.getPlaneBitMask("EDGE")) 

747 

748 self.checkPeaks(frame=3) 

749 

750 

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

752 pass 

753 

754 

755def setup_module(module): 

756 lsst.utils.tests.init() 

757 

758 

759if __name__ == "__main__": 759 ↛ 760line 759 didn't jump to line 760, because the condition on line 759 was never true

760 lsst.utils.tests.init() 

761 unittest.main()