Coverage for tests/test_strayFlux.py : 9%

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
24import numpy as np
25from functools import reduce
27import lsst.utils.tests
28import lsst.afw.detection as afwDet
29import lsst.geom as geom
30import lsst.afw.image as afwImage
31from lsst.log import Log
32from lsst.meas.deblender.baseline import deblend
33import lsst.meas.algorithms as measAlg
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__), 'stray%i.png')
42 print('Writing plots to', plotpat)
43else:
44 print('"doPlot" not set -- not making plots. To enable plots, edit', __file__)
46# Lower the level to Log.DEBUG to see debug messages
47Log.getLogger('meas.deblender.symmetrizeFootprint').setLevel(Log.INFO)
50def imExt(img):
51 bbox = img.getBBox()
52 return [bbox.getMinX(), bbox.getMaxX(),
53 bbox.getMinY(), bbox.getMaxY()]
56def doubleGaussianPsf(W, H, fwhm1, fwhm2, a2):
57 return measAlg.DoubleGaussianPsf(W, H, fwhm1, fwhm2, a2)
60def gaussianPsf(W, H, fwhm):
61 return measAlg.DoubleGaussianPsf(W, H, fwhm)
64class StrayFluxTestCase(lsst.utils.tests.TestCase):
66 def test1(self):
67 '''
68 A simple example: three overlapping blobs (detected as 1
69 footprint with three peaks). We artificially omit one of the
70 peaks, meaning that its flux is "stray". Assert that the
71 stray flux assigned to the other two peaks accounts for all
72 the flux in the parent.
73 '''
74 H, W = 100, 100
76 fpbb = geom.Box2I(geom.Point2I(0, 0),
77 geom.Point2I(W-1, H-1))
79 afwimg = afwImage.MaskedImageF(fpbb)
80 imgbb = afwimg.getBBox()
81 img = afwimg.getImage().getArray()
83 var = afwimg.getVariance().getArray()
84 var[:, :] = 1.
86 blob_fwhm = 10.
87 blob_psf = doubleGaussianPsf(99, 99, blob_fwhm, 3.*blob_fwhm, 0.03)
89 fakepsf_fwhm = 3.
90 fakepsf = gaussianPsf(11, 11, fakepsf_fwhm)
92 blobimgs = []
93 x = 75.
94 XY = [(x, 35.), (x, 65.), (50., 50.)]
95 flux = 1e6
96 for x, y in XY:
97 bim = blob_psf.computeImage(geom.Point2D(x, y))
98 bbb = bim.getBBox()
99 bbb.clip(imgbb)
101 bim = bim.Factory(bim, bbb)
102 bim2 = bim.getArray()
104 blobimg = np.zeros_like(img)
105 blobimg[bbb.getMinY():bbb.getMaxY()+1,
106 bbb.getMinX():bbb.getMaxX()+1] += flux * bim2
107 blobimgs.append(blobimg)
109 img[bbb.getMinY():bbb.getMaxY()+1,
110 bbb.getMinX():bbb.getMaxX()+1] += flux * bim2
112 # Run the detection code to get a ~ realistic footprint
113 thresh = afwDet.createThreshold(5., 'value', True)
114 fpSet = afwDet.FootprintSet(afwimg, thresh, 'DETECTED', 1)
115 fps = fpSet.getFootprints()
116 print('found', len(fps), 'footprints')
117 pks2 = []
118 for fp in fps:
119 print('peaks:', len(fp.getPeaks()))
120 for pk in fp.getPeaks():
121 print(' ', pk.getIx(), pk.getIy())
122 pks2.append((pk.getIx(), pk.getIy()))
124 # The first peak in this list is the one we want to omit.
125 fp0 = fps[0]
126 fakefp = afwDet.Footprint(fp0.getSpans(), fp0.getBBox())
127 for pk in fp0.getPeaks()[1:]:
128 fakefp.getPeaks().append(pk)
130 ima = dict(interpolation='nearest', origin='lower', cmap='gray',
131 vmin=0, vmax=1e3)
133 if doPlot:
134 plt.figure(figsize=(12, 6))
136 plt.clf()
137 plt.suptitle('strayFlux.py: test1 input')
138 plt.subplot(2, 2, 1)
139 plt.title('Image')
140 plt.imshow(img, **ima)
141 ax = plt.axis()
142 plt.plot([x for x, y in XY], [y for x, y in XY], 'r.')
143 plt.axis(ax)
144 for i, (b, (x, y)) in enumerate(zip(blobimgs, XY)):
145 plt.subplot(2, 2, 2+i)
146 plt.title('Blob %i' % i)
147 plt.imshow(b, **ima)
148 ax = plt.axis()
149 plt.plot(x, y, 'r.')
150 plt.axis(ax)
151 plt.savefig(plotpat % 1)
153 # Change verbose to False to quiet down the meas_deblender.baseline logger
154 deb = deblend(fakefp, afwimg, fakepsf, fakepsf_fwhm, verbose=True)
155 parent_img = afwImage.ImageF(fpbb)
156 fakefp.spans.copyImage(afwimg.getImage(), parent_img)
158 if doPlot:
159 def myimshow(*args, **kwargs):
160 plt.imshow(*args, **kwargs)
161 plt.xticks([])
162 plt.yticks([])
163 plt.axis(imExt(afwimg))
165 plt.clf()
166 plt.suptitle('strayFlux.py: test1 results')
167 # R,C = 3,5
168 R, C = 3, 4
169 plt.subplot(R, C, (2*C) + 1)
170 plt.title('Image')
171 myimshow(img, **ima)
172 ax = plt.axis()
173 plt.plot([x for x, y in XY], [y for x, y in XY], 'r.')
174 plt.axis(ax)
176 plt.subplot(R, C, (2*C) + 2)
177 plt.title('Parent footprint')
178 myimshow(parent_img.getArray(), **ima)
179 ax = plt.axis()
180 plt.plot([pk.getIx() for pk in fakefp.getPeaks()],
181 [pk.getIy() for pk in fakefp.getPeaks()], 'r.')
182 plt.axis(ax)
184 sumimg = None
185 for i, dpk in enumerate(deb.peaks):
186 plt.subplot(R, C, i*C + 1)
187 plt.title('ch%i symm' % i)
188 symm = dpk.templateImage
189 myimshow(symm.getArray(), extent=imExt(symm), **ima)
191 plt.subplot(R, C, i*C + 2)
192 plt.title('ch%i portion' % i)
193 port = dpk.fluxPortion.getImage()
194 myimshow(port.getArray(), extent=imExt(port), **ima)
196 himg = afwImage.ImageF(fpbb)
197 heavy = dpk.getFluxPortion(strayFlux=False)
198 heavy.insert(himg)
200 # plt.subplot(R, C, i*C + 3)
201 # plt.title('ch%i heavy' % i)
202 # myimshow(himg.getArray(), **ima)
203 # ax = plt.axis()
204 # plt.plot([x for x,y in XY], [y for x,y in XY], 'r.')
205 # plt.axis(ax)
207 simg = afwImage.ImageF(fpbb)
208 dpk.strayFlux.insert(simg)
210 plt.subplot(R, C, i*C + 3)
211 plt.title('ch%i stray' % i)
212 myimshow(simg.getArray(), **ima)
213 ax = plt.axis()
214 plt.plot([x for x, y in XY], [y for x, y in XY], 'r.')
215 plt.axis(ax)
217 himg2 = afwImage.ImageF(fpbb)
218 heavy = dpk.getFluxPortion(strayFlux=True)
219 heavy.insert(himg2)
221 if sumimg is None:
222 sumimg = himg2.getArray().copy()
223 else:
224 sumimg += himg2.getArray()
226 plt.subplot(R, C, i*C + 4)
227 myimshow(himg2.getArray(), **ima)
228 plt.title('ch%i total' % i)
229 ax = plt.axis()
230 plt.plot([x for x, y in XY], [y for x, y in XY], 'r.')
231 plt.axis(ax)
233 plt.subplot(R, C, (2*C) + C)
234 myimshow(sumimg, **ima)
235 ax = plt.axis()
236 plt.plot([x for x, y in XY], [y for x, y in XY], 'r.')
237 plt.axis(ax)
238 plt.title('Sum of deblends')
240 plt.savefig(plotpat % 2)
242 # Compute the sum-of-children image
243 sumimg = None
244 for i, dpk in enumerate(deb.deblendedParents[0].peaks):
245 himg2 = afwImage.ImageF(fpbb)
246 dpk.getFluxPortion().insert(himg2)
247 if sumimg is None:
248 sumimg = himg2.getArray().copy()
249 else:
250 sumimg += himg2.getArray()
252 # Sum of children ~= Original image inside footprint (parent_img)
254 absdiff = np.max(np.abs(sumimg - parent_img.getArray()))
255 print('Max abs diff:', absdiff)
256 imgmax = parent_img.getArray().max()
257 print('Img max:', imgmax)
258 self.assertLess(absdiff, imgmax*1e-6)
260 def test2(self):
261 '''
262 A 1-d example, to test the stray-flux assignment.
263 '''
264 H, W = 1, 100
266 fpbb = geom.Box2I(geom.Point2I(0, 0),
267 geom.Point2I(W-1, H-1))
268 afwimg = afwImage.MaskedImageF(fpbb)
269 img = afwimg.getImage().getArray()
271 var = afwimg.getVariance().getArray()
272 var[:, :] = 1.
274 y = 0
275 img[y, 1:-1] = 10.
277 img[0, 1] = 20.
278 img[0, -2] = 20.
280 fakepsf_fwhm = 1.
281 fakepsf = gaussianPsf(1, 1, fakepsf_fwhm)
283 # Run the detection code to get a ~ realistic footprint
284 thresh = afwDet.createThreshold(5., 'value', True)
285 fpSet = afwDet.FootprintSet(afwimg, thresh, 'DETECTED', 1)
286 fps = fpSet.getFootprints()
287 self.assertEqual(len(fps), 1)
288 fp = fps[0]
290 # WORKAROUND: the detection alg produces ONE peak, at (1,0),
291 # rather than two.
292 self.assertEqual(len(fp.getPeaks()), 1)
293 fp.addPeak(W-2, y, float("NaN"))
294 # print 'Added peak; peaks:', len(fp.getPeaks())
295 # for pk in fp.getPeaks():
296 # print ' ', pk.getFx(), pk.getFy()
298 # Change verbose to False to quiet down the meas_deblender.baseline logger
299 deb = deblend(fp, afwimg, fakepsf, fakepsf_fwhm, verbose=True,
300 fitPsfs=False, )
302 if doPlot:
303 XX = np.arange(W+1).repeat(2)[1:-1]
305 plt.clf()
306 p1 = plt.plot(XX, img[y, :].repeat(2), 'g-', lw=3, alpha=0.3)
308 for i, dpk in enumerate(deb.peaks):
309 print(dpk)
310 port = dpk.fluxPortion.getImage()
311 bb = port.getBBox()
312 YY = np.zeros(XX.shape)
313 YY[bb.getMinX()*2: (bb.getMaxX()+1)*2] = port.getArray()[0, :].repeat(2)
314 p2 = plt.plot(XX, YY, 'r-')
316 simg = afwImage.ImageF(fpbb)
317 dpk.strayFlux.insert(simg)
318 p3 = plt.plot(XX, simg.getArray()[y, :].repeat(2), 'b-')
320 plt.legend((p1[0], p2[0], p3[0]),
321 ('Parent Flux', 'Child portion', 'Child stray flux'))
322 plt.ylim(-2, 22)
323 plt.savefig(plotpat % 3)
325 strays = []
326 for i, dpk in enumerate(deb.deblendedParents[0].peaks):
327 simg = afwImage.ImageF(fpbb)
328 dpk.strayFlux.insert(simg)
329 strays.append(simg.getArray())
331 ssum = reduce(np.add, strays)
333 starget = np.zeros(W)
334 starget[2:-2] = 10.
336 self.assertFloatsEqual(ssum, starget)
338 X = np.arange(W)
339 dx1 = X - 1.
340 dx2 = X - (W-2)
341 f1 = (1. / (1. + dx1**2))
342 f2 = (1. / (1. + dx2**2))
343 strayclip = 0.001
344 fsum = f1 + f2
345 f1[f1 < strayclip * fsum] = 0.
346 f2[f2 < strayclip * fsum] = 0.
348 s1 = f1 / (f1+f2) * 10.
349 s2 = f2 / (f1+f2) * 10.
351 s1[:2] = 0.
352 s2[-2:] = 0.
354 if doPlot:
355 p4 = plt.plot(XX, s1.repeat(2), 'm-')
356 plt.plot(XX, s2.repeat(2), 'm-')
358 plt.legend((p1[0], p2[0], p3[0], p4[0]),
359 ('Parent Flux', 'Child portion', 'Child stray flux',
360 'Expected stray flux'))
361 plt.ylim(-2, 22)
362 plt.savefig(plotpat % 4)
364 # test abs diff
365 d = np.max(np.abs(s1 - strays[0]))
366 self.assertLess(d, 1e-6)
367 d = np.max(np.abs(s2 - strays[1]))
368 self.assertLess(d, 1e-6)
370 # test relative diff
371 self.assertLess(np.max(np.abs(s1 - strays[0])/np.maximum(1e-3, s1)), 1e-6)
372 self.assertLess(np.max(np.abs(s2 - strays[1])/np.maximum(1e-3, s2)), 1e-6)
375class TestMemory(lsst.utils.tests.MemoryTestCase):
376 pass
379def setup_module(module):
380 lsst.utils.tests.init()
383if __name__ == "__main__": 383 ↛ 384line 383 didn't jump to line 384, because the condition on line 383 was never true
384 lsst.utils.tests.init()
385 unittest.main()