Coverage for tests/test_edges.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
25import numpy as np
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
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__)
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)
51def imExt(img):
52 bbox = img.getBBox()
53 return [bbox.getMinX(), bbox.getMaxX(),
54 bbox.getMinY(), bbox.getMaxY()]
57def doubleGaussianPsf(W, H, fwhm1, fwhm2, a2):
58 return measAlg.DoubleGaussianPsf(W, H, fwhm1, fwhm2, a2)
61def gaussianPsf(W, H, fwhm):
62 return measAlg.DoubleGaussianPsf(W, H, fwhm)
65class RampEdgeTestCase(lsst.utils.tests.TestCase):
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.
72 We run the detection code to get realistic peaks and
73 footprints.
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 '''
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()
87 var = afwimg.getVariance().getArray()
88 var[:, :] = 1.
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)
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)
106 bim = bim.Factory(bim, bbb)
107 bim2 = bim.getArray()
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)
114 img[bbb.getMinY():bbb.getMaxY()+1,
115 bbb.getMinX():bbb.getMaxX()+1] += flux * bim2
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')
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)
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')
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)
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)
164 ima = dict(interpolation='nearest', origin='lower', # cmap='gray',
165 cmap='jet',
166 vmin=0, vmax=400)
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'
180 parent_img = afwImage.ImageF(fpbb)
181 fp.spans.copyImage(afwimg.getImage(), parent_img)
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()]
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)
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)
207 mono = afwImage.ImageF(fpbb)
208 dpk.templateFootprint.spans.copyImage(dpk.templateImage, mono)
209 monos.append(mono)
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)
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])
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.))
244 if not doPlot:
245 continue
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)
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))
268 plt.clf()
270 pa = dict(color='m', marker='.', linestyle='None', zorder=30)
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')
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')
287 sumimg = None
288 for i, dpk in enumerate(deb.deblendedParents[0].peaks):
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')
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')
305 # monotonic template
306 mimg = afwImage.ImageF(fpbb)
307 afwDet.copyWithinFootprintImage(dpk.templateFootprint,
308 dpk.templateImage, mimg)
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')
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)
325 if dpk.strayFlux is not None:
326 simg = afwImage.ImageF(fpbb)
327 dpk.strayFlux.insert(simg)
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)
336 himg2 = afwImage.ImageF(fpbb)
337 portion = dpk.getFluxPortion()
338 portion.insert(himg2)
340 if sumimg is None:
341 sumimg = himg2.getArray().copy()
342 else:
343 sumimg += himg2.getArray()
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)
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')
359 plt.suptitle(tt)
360 fn = plotpat % (2*j + 1)
361 plt.savefig(fn)
362 print('Wrote', fn)
365class TestMemory(lsst.utils.tests.MemoryTestCase):
366 pass
369def setup_module(module):
370 lsst.utils.tests.init()
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()