Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# 

2# LSST Data Management System 

3# 

4# Copyright 2008-2016 AURA/LSST. 

5# 

6# This product includes software developed by the 

7# LSST Project (http://www.lsst.org/). 

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 LSST License Statement and 

20# the GNU General Public License along with this program. If not, 

21# see <https://www.lsstcorp.org/LegalNotices/>. 

22# 

23import unittest 

24 

25import numpy as np 

26 

27import lsst.utils.tests 

28import lsst.afw.detection as afwDet 

29import lsst.geom as geom 

30import lsst.afw.image as afwImage 

31import lsst.meas.algorithms as measAlg 

32from lsst.log import Log 

33from lsst.meas.deblender.baseline import deblend 

34 

35doPlot = False 

36if doPlot: 36 ↛ 37line 36 didn't jump to line 37, because the condition on line 36 was never true

37 import matplotlib 

38 matplotlib.use('Agg') 

39 import pylab as plt 

40 import os.path 

41 plotpat = os.path.join(os.path.dirname(__file__), 'edge%i.png') 

42 print('Writing plots to', plotpat) 

43else: 

44 print('"doPlot" not set -- not making plots. To enable plots, edit', __file__) 

45 

46# Lower the level to Log.DEBUG to see debug messages 

47Log.getLogger('meas.deblender.symmetrizeFootprint').setLevel(Log.INFO) 

48Log.getLogger('meas.deblender.symmetricFootprint').setLevel(Log.INFO) 

49 

50 

51def imExt(img): 

52 bbox = img.getBBox() 

53 return [bbox.getMinX(), bbox.getMaxX(), 

54 bbox.getMinY(), bbox.getMaxY()] 

55 

56 

57def doubleGaussianPsf(W, H, fwhm1, fwhm2, a2): 

58 return measAlg.DoubleGaussianPsf(W, H, fwhm1, fwhm2, a2) 

59 

60 

61def gaussianPsf(W, H, fwhm): 

62 return measAlg.DoubleGaussianPsf(W, H, fwhm) 

63 

64 

65class RampEdgeTestCase(lsst.utils.tests.TestCase): 

66 

67 def test1(self): 

68 ''' 

69 In this test, we create a test image containing two blobs, one 

70 of which is truncated by the edge of the image. 

71 

72 We run the detection code to get realistic peaks and 

73 footprints. 

74 

75 We then test out the different edge treatments and assert that 

76 they do what they claim. We also make plots, tests/edge*.png 

77 ''' 

78 

79 # Create fake image... 

80 H, W = 100, 100 

81 fpbb = geom.Box2I(geom.Point2I(0, 0), 

82 geom.Point2I(W-1, H-1)) 

83 afwimg = afwImage.MaskedImageF(fpbb) 

84 imgbb = afwimg.getBBox() 

85 img = afwimg.getImage().getArray() 

86 

87 var = afwimg.getVariance().getArray() 

88 var[:, :] = 1. 

89 

90 blob_fwhm = 15. 

91 blob_psf = doubleGaussianPsf(201, 201, blob_fwhm, 3.*blob_fwhm, 0.03) 

92 fakepsf_fwhm = 5. 

93 S = int(np.ceil(fakepsf_fwhm * 2.)) * 2 + 1 

94 print('S', S) 

95 fakepsf = gaussianPsf(S, S, fakepsf_fwhm) 

96 

97 # Create and save blob images, and add to image to deblend. 

98 blobimgs = [] 

99 XY = [(50., 50.), (90., 50.)] 

100 flux = 1e6 

101 for x, y in XY: 

102 bim = blob_psf.computeImage(geom.Point2D(x, y)) 

103 bbb = bim.getBBox() 

104 bbb.clip(imgbb) 

105 

106 bim = bim.Factory(bim, bbb) 

107 bim2 = bim.getArray() 

108 

109 blobimg = np.zeros_like(img) 

110 blobimg[bbb.getMinY():bbb.getMaxY()+1, 

111 bbb.getMinX():bbb.getMaxX()+1] += flux * bim2 

112 blobimgs.append(blobimg) 

113 

114 img[bbb.getMinY():bbb.getMaxY()+1, 

115 bbb.getMinX():bbb.getMaxX()+1] += flux * bim2 

116 

117 # Run the detection code to get a ~ realistic footprint 

118 thresh = afwDet.createThreshold(10., 'value', True) 

119 fpSet = afwDet.FootprintSet(afwimg, thresh, 'DETECTED', 1) 

120 fps = fpSet.getFootprints() 

121 print('found', len(fps), 'footprints') 

122 

123 # set EDGE bit on edge pixels. 

124 margin = 5 

125 lo = imgbb.getMin() 

126 lo.shift(geom.Extent2I(margin, margin)) 

127 hi = imgbb.getMax() 

128 hi.shift(geom.Extent2I(-margin, -margin)) 

129 goodbbox = geom.Box2I(lo, hi) 

130 print('Good bbox for setting EDGE pixels:', goodbbox) 

131 print('image bbox:', imgbb) 

132 edgebit = afwimg.getMask().getPlaneBitMask("EDGE") 

133 print('edgebit:', edgebit) 

134 measAlg.SourceDetectionTask.setEdgeBits(afwimg, goodbbox, edgebit) 

135 

136 if False: 

137 plt.clf() 

138 plt.imshow(afwimg.getMask().getArray(), 

139 interpolation='nearest', origin='lower') 

140 plt.colorbar() 

141 plt.title('Mask') 

142 plt.savefig('mask.png') 

143 

144 M = afwimg.getMask().getArray() 

145 for bit in range(32): 

146 mbit = (1 << bit) 

147 if not np.any(M & mbit): 

148 continue 

149 plt.clf() 

150 plt.imshow(M & mbit, 

151 interpolation='nearest', origin='lower') 

152 plt.colorbar() 

153 plt.title('Mask bit %i (0x%x)' % (bit, mbit)) 

154 plt.savefig('mask-%02i.png' % bit) 

155 

156 for fp in fps: 

157 print('peaks:', len(fp.getPeaks())) 

158 for pk in fp.getPeaks(): 

159 print(' ', pk.getIx(), pk.getIy()) 

160 assert(len(fps) == 1) 

161 fp = fps[0] 

162 assert(len(fp.getPeaks()) == 2) 

163 

164 ima = dict(interpolation='nearest', origin='lower', # cmap='gray', 

165 cmap='jet', 

166 vmin=0, vmax=400) 

167 

168 for j, (tt, kwa) in enumerate([ 

169 ('No edge treatment', dict()), 

170 ('Ramp by PSF', dict(rampFluxAtEdge=True)), 

171 ('No clip at edge', dict(patchEdges=True)), 

172 ]): 

173 # print 'Deblending...' 

174 # Change verbose to False to quiet down the meas_deblender.baseline logger 

175 deb = deblend(fp, afwimg, fakepsf, fakepsf_fwhm, verbose=True, 

176 **kwa) 

177 # print 'Result:', deb 

178 # print len(deb.peaks), 'deblended peaks' 

179 

180 parent_img = afwImage.ImageF(fpbb) 

181 fp.spans.copyImage(afwimg.getImage(), parent_img) 

182 

183 X = [x for x, y in XY] 

184 Y = [y for x, y in XY] 

185 PX = [pk.getIx() for pk in fp.getPeaks()] 

186 PY = [pk.getIy() for pk in fp.getPeaks()] 

187 

188 # Grab 1-d slices to make assertion about. 

189 symms = [] 

190 monos = [] 

191 symm1ds = [] 

192 mono1ds = [] 

193 yslice = H//2 

194 parent1d = img[yslice, :] 

195 for i, dpk in enumerate(deb.deblendedParents[0].peaks): 

196 symm = dpk.origTemplate 

197 symms.append(symm) 

198 

199 bbox = symm.getBBox() 

200 x0, y0 = bbox.getMinX(), bbox.getMinY() 

201 im = symm.getArray() 

202 h, w = im.shape 

203 oned = np.zeros(W) 

204 oned[x0: x0+w] = im[yslice-y0, :] 

205 symm1ds.append(oned) 

206 

207 mono = afwImage.ImageF(fpbb) 

208 dpk.templateFootprint.spans.copyImage(dpk.templateImage, mono) 

209 monos.append(mono) 

210 

211 im = mono.getArray() 

212 bbox = mono.getBBox() 

213 x0, y0 = bbox.getMinX(), bbox.getMinY() 

214 h, w = im.shape 

215 oned = np.zeros(W) 

216 oned[x0: x0+w] = im[yslice-y0, :] 

217 mono1ds.append(oned) 

218 

219 for i, (symm, mono) in enumerate(zip(symm1ds, mono1ds)): 

220 # for the first two cases, the basic symmetric 

221 # template for the second source drops to zero at < 

222 # ~75 where the symmetric part is outside the 

223 # footprint. 

224 if i == 1 and j in [0, 1]: 

225 self.assertFloatsEqual(symm[:74], 0.0) 

226 if i == 1 and j == 2: 

227 # For the third case, the 'symm' template gets 

228 # "patched" with the parent's value 

229 self.assertFloatsEqual(symm[:74], parent1d[:74]) 

230 

231 if i == 1 and j == 0: 

232 # No edge handling: mono template == 0 

233 self.assertFloatsEqual(mono[:74], 0.0) 

234 if i == 1 and j == 1: 

235 # ramp by psf: zero up to ~65, ramps up 

236 self.assertFloatsEqual(mono[:64], 0.0) 

237 self.assertTrue(np.any(mono[65:74] > 0)) 

238 self.assertTrue(np.all(np.diff(mono)[60:80] >= 0.)) 

239 if i == 1 and j == 2: 

240 # no edge clipping: profile is monotonic and positive. 

241 self.assertTrue(np.all(np.diff(mono)[:85] >= 0.)) 

242 self.assertTrue(np.all(mono[:85] > 0.)) 

243 

244 if not doPlot: 

245 continue 

246 

247 plt.clf() 

248 p1 = plt.plot(parent1d, 'b-', lw=3, alpha=0.5) 

249 for i, (symm, mono) in enumerate(zip(symm1ds, mono1ds)): 

250 p2 = plt.plot(symm, 'r-', lw=2, alpha=0.7) 

251 p3 = plt.plot(mono, 'g-') 

252 plt.legend((p1[0], p2[0], p3[0]), ('Parent', 'Symm template', 'Mono template'), 

253 loc='upper left') 

254 plt.title('1-d slice: %s' % tt) 

255 fn = plotpat % (2*j+0) 

256 plt.savefig(fn) 

257 print('Wrote', fn) 

258 

259 def myimshow(*args, **kwargs): 

260 x0, x1, y0, y1 = imExt(afwimg) 

261 plt.fill([x0, x0, x1, x1, x0], [y0, y1, y1, y0, y0], color=(1, 1, 0.8), 

262 zorder=20) 

263 plt.imshow(*args, zorder=25, **kwargs) 

264 plt.xticks([]) 

265 plt.yticks([]) 

266 plt.axis(imExt(afwimg)) 

267 

268 plt.clf() 

269 

270 pa = dict(color='m', marker='.', linestyle='None', zorder=30) 

271 

272 R, C = 3, 6 

273 plt.subplot(R, C, (2*C) + 1) 

274 myimshow(img, **ima) 

275 ax = plt.axis() 

276 plt.plot(X, Y, **pa) 

277 plt.axis(ax) 

278 plt.title('Image') 

279 

280 plt.subplot(R, C, (2*C) + 2) 

281 myimshow(parent_img.getArray(), **ima) 

282 ax = plt.axis() 

283 plt.plot(PX, PY, **pa) 

284 plt.axis(ax) 

285 plt.title('Footprint') 

286 

287 sumimg = None 

288 for i, dpk in enumerate(deb.deblendedParents[0].peaks): 

289 

290 plt.subplot(R, C, i*C + 1) 

291 myimshow(blobimgs[i], **ima) 

292 ax = plt.axis() 

293 plt.plot(PX[i], PY[i], **pa) 

294 plt.axis(ax) 

295 plt.title('true') 

296 

297 plt.subplot(R, C, i*C + 2) 

298 t = dpk.origTemplate 

299 myimshow(t.getArray(), extent=imExt(t), **ima) 

300 ax = plt.axis() 

301 plt.plot(PX[i], PY[i], **pa) 

302 plt.axis(ax) 

303 plt.title('symm') 

304 

305 # monotonic template 

306 mimg = afwImage.ImageF(fpbb) 

307 afwDet.copyWithinFootprintImage(dpk.templateFootprint, 

308 dpk.templateImage, mimg) 

309 

310 plt.subplot(R, C, i*C + 3) 

311 myimshow(mimg.getArray(), extent=imExt(mimg), **ima) 

312 ax = plt.axis() 

313 plt.plot(PX[i], PY[i], **pa) 

314 plt.axis(ax) 

315 plt.title('monotonic') 

316 

317 plt.subplot(R, C, i*C + 4) 

318 port = dpk.fluxPortion.getImage() 

319 myimshow(port.getArray(), extent=imExt(port), **ima) 

320 plt.title('portion') 

321 ax = plt.axis() 

322 plt.plot(PX[i], PY[i], **pa) 

323 plt.axis(ax) 

324 

325 if dpk.strayFlux is not None: 

326 simg = afwImage.ImageF(fpbb) 

327 dpk.strayFlux.insert(simg) 

328 

329 plt.subplot(R, C, i*C + 5) 

330 myimshow(simg.getArray(), **ima) 

331 plt.title('stray') 

332 ax = plt.axis() 

333 plt.plot(PX, PY, **pa) 

334 plt.axis(ax) 

335 

336 himg2 = afwImage.ImageF(fpbb) 

337 portion = dpk.getFluxPortion() 

338 portion.insert(himg2) 

339 

340 if sumimg is None: 

341 sumimg = himg2.getArray().copy() 

342 else: 

343 sumimg += himg2.getArray() 

344 

345 plt.subplot(R, C, i*C + 6) 

346 myimshow(himg2.getArray(), **ima) 

347 plt.title('portion+stray') 

348 ax = plt.axis() 

349 plt.plot(PX, PY, **pa) 

350 plt.axis(ax) 

351 

352 plt.subplot(R, C, (2*C) + C) 

353 myimshow(sumimg, **ima) 

354 ax = plt.axis() 

355 plt.plot(X, Y, **pa) 

356 plt.axis(ax) 

357 plt.title('Sum of deblends') 

358 

359 plt.suptitle(tt) 

360 fn = plotpat % (2*j + 1) 

361 plt.savefig(fn) 

362 print('Wrote', fn) 

363 

364 

365class TestMemory(lsst.utils.tests.MemoryTestCase): 

366 pass 

367 

368 

369def setup_module(module): 

370 lsst.utils.tests.init() 

371 

372 

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

374 lsst.utils.tests.init() 

375 unittest.main()