Coverage for tests/test_defects.py : 12%

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-2017 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#
23"""Test cases for lsst.cp.pipe.FindDefectsTask."""
25import unittest
26import numpy as np
27import copy
29import lsst.utils
30import lsst.utils.tests
32import lsst.ip.isr as ipIsr
33import lsst.cp.pipe as cpPipe
34from lsst.ip.isr import isrMock
35from lsst.geom import Box2I, Point2I, Extent2I
38class FindDefectsTaskTestCase(lsst.utils.tests.TestCase):
39 """A test case for the defect finding task."""
41 def setUp(self):
42 self.defaultConfig = cpPipe.defects.FindDefectsTask.ConfigClass()
44 self.flatMean = 2000
45 self.darkMean = 1
46 self.readNoiseAdu = 10
47 self.nSigmaBright = 8
48 self.nSigmaDark = 8
50 mockImageConfig = isrMock.IsrMock.ConfigClass()
52 # flatDrop is not really relevant as we replace the data
53 # but good to note it in case we change how this image is made
54 mockImageConfig.flatDrop = 0.99999
55 mockImageConfig.isTrimmed = True
57 self.flatExp = isrMock.FlatMock(config=mockImageConfig).run()
58 (shapeY, shapeX) = self.flatExp.getDimensions()
60 # x, y, size tuples
61 # always put edge defects at the start and change the value of nEdge
63 self.brightDefects = [(0, 15, 3, 3), (100, 123, 1, 1)]
65 self.darkDefects = [(5, 0, 1, 1), (7, 62, 2, 2)]
67 nEdge = 1 # NOTE: update if more edge defects are included
68 self.noEdges = slice(nEdge, None)
69 self.onlyEdges = slice(0, nEdge)
71 self.darkBBoxes = [Box2I(Point2I(x, y), Extent2I(sx, sy)) for (x, y, sx, sy) in self.darkDefects]
72 self.brightBBoxes = [Box2I(Point2I(x, y), Extent2I(sx, sy)) for (x, y, sx, sy) in self.brightDefects]
74 flatWidth = np.sqrt(self.flatMean) + self.readNoiseAdu
75 darkWidth = self.readNoiseAdu
76 self.rng = np.random.RandomState(0)
77 flatData = self.rng.normal(self.flatMean, flatWidth, (shapeX, shapeY))
78 darkData = self.rng.normal(self.darkMean, darkWidth, (shapeX, shapeY))
80 # NOTE: darks and flats have same defects applied deliberately to both
81 for defect in self.brightDefects:
82 y, x, sy, sx = defect
83 # are these actually the numbers we want?
84 flatData[x:x+sx, y:y+sy] += self.nSigmaBright * flatWidth
85 darkData[x:x+sx, y:y+sy] += self.nSigmaBright * darkWidth
87 for defect in self.darkDefects:
88 y, x, sy, sx = defect
89 # are these actually the numbers we want?
90 flatData[x:x+sx, y:y+sy] -= self.nSigmaDark * flatWidth
91 darkData[x:x+sx, y:y+sy] -= self.nSigmaDark * darkWidth
93 self.darkExp = self.flatExp.clone()
94 self.spareImage = self.flatExp.clone() # for testing edge bits and misc
96 self.flatExp.image.array[:] = flatData
97 self.darkExp.image.array[:] = darkData
99 self.defaultTask = cpPipe.defects.FindDefectsTask() # config=self.defaultConfig)
101 self.allDefectsList = ipIsr.Defects()
103 self.brightDefectsList = ipIsr.Defects()
104 for d in self.brightBBoxes:
105 self.brightDefectsList.append(d)
106 self.allDefectsList.append(d)
108 self.darkDefectsList = ipIsr.Defects()
109 for d in self.darkBBoxes:
110 self.darkDefectsList.append(d)
111 self.allDefectsList.append(d)
113 def check_maskBlocks(self, inputDefects, expectedDefects):
114 """A helper function for the tests of maskBlocksIfIntermitentBadPixelsInColumn.
115 """
116 config = copy.copy(self.defaultConfig)
117 config.measure.badOnAndOffPixelColumnThreshold = 10
118 config.measure.goodPixelColumnGapThreshold = 5
119 config.measure.nPixBorderUpDown = 0
120 config.measure.nPixBorderLeftRight = 0
122 task = cpPipe.defects.FindDefectsTask(config=config)
124 defectsWithColumns = task.measure.maskBlocksIfIntermitentBadPixelsInColumn(inputDefects)
125 boxesMeasured = []
126 for defect in defectsWithColumns:
127 boxesMeasured.append(defect.getBBox())
129 for boxInput in expectedDefects:
130 self.assertIn(boxInput, boxesMeasured)
132 # Check that the code did not mask anything extra by
133 # looking in both the input list and "expanded-column" list.
134 unionInputExpectedBoxes = []
135 for defect in inputDefects:
136 unionInputExpectedBoxes.append(defect.getBBox())
137 for defect in expectedDefects:
138 unionInputExpectedBoxes.append(defect)
140 # Check that code doesn't mask more than it is supposed to.
141 for boxMeas in boxesMeasured:
142 self.assertIn(boxMeas, unionInputExpectedBoxes)
144 def test_maskBlocks_full_column(self):
145 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
146 Tests that a contigous bad column does not get split by the code.
148 The mock flat has a size of 200X204 pixels. This column has a maximum length of 50
149 pixels, otherwise there would be a split along the mock amp boundary.
151 Plots can be found in DM-19903 on Jira.
152 """
154 defects = self.allDefectsList
155 defects.append(Box2I(corner=Point2I(15, 1), dimensions=Extent2I(1, 50)))
156 expectedDefects = [Box2I(corner=Point2I(15, 1), dimensions=Extent2I(1, 50))]
158 self.check_maskBlocks(defects, expectedDefects)
160 def test_maskBlocks_long_column(self):
161 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
162 Tests that a contigous bad column with Npix >= badOnAndOffPixelColumnThreshold (10)
163 does not get split by the code.
165 Plots can be found in DM-19903 on Jira.
166 """
168 expectedDefects = [Box2I(corner=Point2I(20, 1), dimensions=Extent2I(1, 25))]
169 defects = self.allDefectsList
170 defects.append(Box2I(corner=Point2I(20, 1), dimensions=Extent2I(1, 25)))
172 self.check_maskBlocks(defects, expectedDefects)
174 def test_maskBlocks_short_column(self):
175 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
176 Tests that a contigous bad column Npix < badOnAndOffPixelColumnThreshold (10)
177 does not get split by the code.
179 Plots can be found in DM-19903 on Jira.
180 """
182 expectedDefects = [Box2I(corner=Point2I(25, 1), dimensions=Extent2I(1, 8))]
183 defects = self.allDefectsList
184 defects.append(Box2I(corner=Point2I(25, 1), dimensions=Extent2I(1, 8)))
186 self.check_maskBlocks(defects, expectedDefects)
188 def test_maskBlocks_discontigous_to_single_block(self):
189 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
190 Npix discontiguous bad pixels in a column where Npix >= badOnAndOffPixelColumnThreshold (10)
191 and gaps of good pixels < goodPixelColumnGapThreshold (5). Under these conditions, the whole
192 block of bad pixels (including good gaps) should be masked.
194 Plots can be found in DM-19903 on Jira.
195 """
197 expectedDefects = [Box2I(corner=Point2I(30, 1), dimensions=Extent2I(1, 48))]
198 defects = self.allDefectsList
199 badPixels = [Box2I(corner=Point2I(30, 1), dimensions=Extent2I(1, 2)),
200 Box2I(corner=Point2I(30, 5), dimensions=Extent2I(1, 3)),
201 Box2I(corner=Point2I(30, 11), dimensions=Extent2I(1, 5)),
202 Box2I(corner=Point2I(30, 19), dimensions=Extent2I(1, 5)),
203 Box2I(corner=Point2I(30, 27), dimensions=Extent2I(1, 4)),
204 Box2I(corner=Point2I(30, 34), dimensions=Extent2I(1, 15))]
205 for badBox in badPixels:
206 defects.append(badBox)
208 self.check_maskBlocks(defects, expectedDefects)
210 def test_maskBlocks_discontigous_less_than_thresholds(self):
211 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
212 Npix discontiguous bad pixels in a column where Npix < badOnAndOffPixelColumnThreshold (10)
213 and gaps of good pixels < goodPixelColumnGapThreshold (5). Under these conditions,
214 the expected defect boxes should be the same as the input boxes.
216 Plots can be found in DM-19903 on Jira.
217 """
219 expectedDefects = [Box2I(corner=Point2I(35, 1), dimensions=Extent2I(1, 2)),
220 Box2I(corner=Point2I(35, 5), dimensions=Extent2I(1, 3)),
221 Box2I(corner=Point2I(35, 11), dimensions=Extent2I(1, 2))]
222 defects = self.allDefectsList
223 badPixels = [Box2I(corner=Point2I(35, 1), dimensions=Extent2I(1, 2)),
224 Box2I(corner=Point2I(35, 5), dimensions=Extent2I(1, 3)),
225 Box2I(corner=Point2I(35, 11), dimensions=Extent2I(1, 2))]
226 for badBox in badPixels:
227 defects.append(badBox)
229 self.check_maskBlocks(defects, expectedDefects)
231 def test_maskBlocks_more_than_thresholds(self):
232 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
233 Npix discontiguous bad pixels in a column where Npix < badOnAndOffPixelColumnThreshold (10)
234 and gaps of good pixels < goodPixelColumnGapThreshold (5).
235 Npix=34 (> 10) bad pixels total, 1 "good" gap with 13 pixels big enough
236 (13 >= 5 good pixels, from y=6 (1+5) to y=19).
238 Plots can be found in DM-19903 on Jira.
239 """
241 expectedDefects = [Box2I(corner=Point2I(40, 1), dimensions=Extent2I(1, 7)),
242 Box2I(corner=Point2I(40, 19), dimensions=Extent2I(1, 30))]
243 defects = self.allDefectsList
244 badPixels = [Box2I(corner=Point2I(40, 1), dimensions=Extent2I(1, 2)),
245 Box2I(corner=Point2I(40, 5), dimensions=Extent2I(1, 3)),
246 Box2I(corner=Point2I(40, 19), dimensions=Extent2I(1, 5)),
247 Box2I(corner=Point2I(40, 27), dimensions=Extent2I(1, 4)),
248 Box2I(corner=Point2I(40, 34), dimensions=Extent2I(1, 15))]
249 for badBox in badPixels:
250 defects.append(badBox)
252 self.check_maskBlocks(defects, expectedDefects)
254 def test_maskBlocks_not_enough_bad_pixels_in_column(self):
255 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
256 Npix discontiguous bad pixels in a column where Npix < badOnAndOffPixelColumnThreshold (10) and
257 and gaps of good pixels > goodPixelColumnGapThreshold (5). Since Npix <
258 badOnAndOffPixelColumnThreshold, then it doesn't matter that the number of good pixels in gap >
259 goodPixelColumnGapThreshold. 5<10 bad pixels total, 1 "good" gap big enough
260 (29>=5 good pixels, from y =12 (10+2) to y=30)
262 Plots can be found in DM-19903 on Jira.
263 """
265 expectedDefects = [Box2I(corner=Point2I(45, 10), dimensions=Extent2I(1, 2)),
266 Box2I(corner=Point2I(45, 30), dimensions=Extent2I(1, 3))]
267 defects = self.allDefectsList
268 badPixels = [Box2I(corner=Point2I(45, 10), dimensions=Extent2I(1, 2)),
269 Box2I(corner=Point2I(45, 30), dimensions=Extent2I(1, 3))]
270 for badBox in badPixels:
271 defects.append(badBox)
273 self.check_maskBlocks(defects, expectedDefects)
275 def test_maskBlocks_every_other_pixel_bad_greater_than_threshold(self):
276 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
277 Npix discontiguous bad pixels in a column where Npix > badOnAndOffPixelColumnThreshold (10)
278 and every other pixel is bad.
280 Plots can be found in DM-19903 on Jira.
281 """
283 expectedDefects = [Box2I(corner=Point2I(50, 10), dimensions=Extent2I(1, 31))]
284 defects = self.allDefectsList
285 badPixels = [Box2I(corner=Point2I(50, 10), dimensions=Extent2I(1, 1)),
286 Box2I(corner=Point2I(50, 12), dimensions=Extent2I(1, 1)),
287 Box2I(corner=Point2I(50, 14), dimensions=Extent2I(1, 1)),
288 Box2I(corner=Point2I(50, 16), dimensions=Extent2I(1, 1)),
289 Box2I(corner=Point2I(50, 18), dimensions=Extent2I(1, 1)),
290 Box2I(corner=Point2I(50, 20), dimensions=Extent2I(1, 1)),
291 Box2I(corner=Point2I(50, 22), dimensions=Extent2I(1, 1)),
292 Box2I(corner=Point2I(50, 24), dimensions=Extent2I(1, 1)),
293 Box2I(corner=Point2I(50, 26), dimensions=Extent2I(1, 1)),
294 Box2I(corner=Point2I(50, 28), dimensions=Extent2I(1, 1)),
295 Box2I(corner=Point2I(50, 30), dimensions=Extent2I(1, 1)),
296 Box2I(corner=Point2I(50, 32), dimensions=Extent2I(1, 1)),
297 Box2I(corner=Point2I(50, 34), dimensions=Extent2I(1, 1)),
298 Box2I(corner=Point2I(50, 36), dimensions=Extent2I(1, 1)),
299 Box2I(corner=Point2I(50, 38), dimensions=Extent2I(1, 1)),
300 Box2I(corner=Point2I(50, 40), dimensions=Extent2I(1, 1))]
301 for badBox in badPixels:
302 defects.append(badBox)
304 self.check_maskBlocks(defects, expectedDefects)
306 def test_maskBlocks_every_other_pixel_bad_less_than_threshold(self):
307 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
308 Npix discontiguous bad pixels in a column where Npix > badOnAndOffPixelColumnThreshold (10)
309 and every other pixel is bad.
311 Plots can be found in DM-19903 on Jira.
312 """
314 expectedDefects = [Box2I(corner=Point2I(55, 20), dimensions=Extent2I(1, 1)),
315 Box2I(corner=Point2I(55, 22), dimensions=Extent2I(1, 1)),
316 Box2I(corner=Point2I(55, 24), dimensions=Extent2I(1, 1)),
317 Box2I(corner=Point2I(55, 26), dimensions=Extent2I(1, 1)),
318 Box2I(corner=Point2I(55, 28), dimensions=Extent2I(1, 1)),
319 Box2I(corner=Point2I(55, 30), dimensions=Extent2I(1, 1))]
320 defects = self.allDefectsList
321 badPixels = [Box2I(corner=Point2I(55, 20), dimensions=Extent2I(1, 1)),
322 Box2I(corner=Point2I(55, 22), dimensions=Extent2I(1, 1)),
323 Box2I(corner=Point2I(55, 24), dimensions=Extent2I(1, 1)),
324 Box2I(corner=Point2I(55, 26), dimensions=Extent2I(1, 1)),
325 Box2I(corner=Point2I(55, 28), dimensions=Extent2I(1, 1)),
326 Box2I(corner=Point2I(55, 30), dimensions=Extent2I(1, 1))]
327 for badBox in badPixels:
328 defects.append(badBox)
330 self.check_maskBlocks(defects, expectedDefects)
332 def test_maskBlocks_blobs_one_side_good_less_than_threshold(self):
333 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
334 Npix discontiguous bad pixels in column with "blobs" of "m" bad pixels to one side,
335 m > badOnAndOffPixelColumnThreshold (10), number of good pixel in gaps between blobs <
336 goodPixelColumnGapThreshold (5).
338 Plots can be found in DM-19903 on Jira.
339 """
341 expectedDefects = [Box2I(corner=Point2I(60, 1), dimensions=Extent2I(1, 29)),
342 Box2I(corner=Point2I(61, 2), dimensions=Extent2I(2, 12))]
343 defects = self.allDefectsList
344 badPixels = [Box2I(corner=Point2I(60, 1), dimensions=Extent2I(1, 18)),
345 Box2I(corner=Point2I(60, 20), dimensions=Extent2I(1, 10)),
346 Box2I(corner=Point2I(61, 2), dimensions=Extent2I(2, 2)),
347 Box2I(corner=Point2I(61, 6), dimensions=Extent2I(2, 8))]
348 for badBox in badPixels:
349 defects.append(badBox)
351 self.check_maskBlocks(defects, expectedDefects)
353 def test_maskBlocks_blobs_other_side_good_less_than_threshold(self):
354 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
355 Npix discontiguous bad pixels in column with "blobs" of "m" bad pixels to the other side,
356 m > badOnAndOffPixelColumnThreshold (10), number of good pixel in gaps between blobs <
357 goodPixelColumnGapThreshold (5).
359 Plots can be found in DM-19903 on Jira.
360 """
362 expectedDefects = [Box2I(corner=Point2I(70, 1), dimensions=Extent2I(1, 29)),
363 Box2I(corner=Point2I(68, 2), dimensions=Extent2I(2, 12))]
364 defects = self.allDefectsList
365 badPixels = [Box2I(corner=Point2I(70, 1), dimensions=Extent2I(1, 18)),
366 Box2I(corner=Point2I(70, 20), dimensions=Extent2I(1, 10)),
367 Box2I(corner=Point2I(68, 2), dimensions=Extent2I(2, 2)),
368 Box2I(corner=Point2I(68, 6), dimensions=Extent2I(2, 8))]
369 for badBox in badPixels:
370 defects.append(badBox)
372 self.check_maskBlocks(defects, expectedDefects)
374 def test_maskBlocks_blob_both_sides_good_less_than_threshold(self):
375 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
376 Npix discontiguous bad pixels in column with "blobs" of "m" bad pixels to both sides,
377 m > badOnAndOffPixelColumnThreshold (10), number of good pixel in gaps between blobs <
378 goodPixelColumnGapThreshold (5).
380 Plots can be found in DM-19903 on Jira.
381 """
383 expectedDefects = [Box2I(corner=Point2I(75, 1), dimensions=Extent2I(1, 29)),
384 Box2I(corner=Point2I(73, 2), dimensions=Extent2I(2, 12)),
385 Box2I(corner=Point2I(76, 2), dimensions=Extent2I(2, 12))]
386 defects = self.allDefectsList
387 badPixels = [Box2I(corner=Point2I(75, 1), dimensions=Extent2I(1, 18)),
388 Box2I(corner=Point2I(75, 20), dimensions=Extent2I(1, 10)),
389 Box2I(corner=Point2I(73, 2), dimensions=Extent2I(2, 2)),
390 Box2I(corner=Point2I(73, 6), dimensions=Extent2I(2, 8)),
391 Box2I(corner=Point2I(76, 2), dimensions=Extent2I(2, 2)),
392 Box2I(corner=Point2I(76, 6), dimensions=Extent2I(2, 8))]
393 for badBox in badPixels:
394 defects.append(badBox)
396 self.check_maskBlocks(defects, expectedDefects)
398 def test_maskBlocks_blob_one_side_good_greater_than_threshold(self):
399 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
400 Npix discontiguous bad pixels in column with "blobs" of "m" bad pixels to one side,
401 m > badOnAndOffPixelColumnThreshold (10), number of good pixel in gaps between blobs >
402 goodPixelColumnGapThreshold (5).
404 Plots can be found in DM-19903 on Jira.
405 """
407 expectedDefects = [Box2I(corner=Point2I(80, 1), dimensions=Extent2I(1, 29)),
408 Box2I(corner=Point2I(81, 2), dimensions=Extent2I(2, 2)),
409 Box2I(corner=Point2I(81, 8), dimensions=Extent2I(2, 8))]
410 defects = self.allDefectsList
411 badPixels = [Box2I(corner=Point2I(80, 1), dimensions=Extent2I(1, 18)),
412 Box2I(corner=Point2I(80, 20), dimensions=Extent2I(1, 10)),
413 Box2I(corner=Point2I(81, 2), dimensions=Extent2I(2, 2)),
414 Box2I(corner=Point2I(81, 8), dimensions=Extent2I(2, 8))]
415 for badBox in badPixels:
416 defects.append(badBox)
418 self.check_maskBlocks(defects, expectedDefects)
420 def test_maskBlocks_other_side_good_greater_than_threshold(self):
421 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
422 Npix discontiguous bad pixels in column with "blobs" of "m" bad pixels to the other side,
423 m > badOnAndOffPixelColumnThreshold (10), number of good pixel in gaps between blobs >
424 goodPixelColumnGapThreshold (5).
426 Plots can be found in DM-19903 on Jira.
427 """
429 expectedDefects = [Box2I(corner=Point2I(87, 1), dimensions=Extent2I(1, 29)),
430 Box2I(corner=Point2I(85, 2), dimensions=Extent2I(2, 2)),
431 Box2I(corner=Point2I(85, 8), dimensions=Extent2I(2, 8))]
432 defects = self.allDefectsList
433 badPixels = [Box2I(corner=Point2I(87, 1), dimensions=Extent2I(1, 18)),
434 Box2I(corner=Point2I(87, 20), dimensions=Extent2I(1, 10)),
435 Box2I(corner=Point2I(85, 2), dimensions=Extent2I(2, 2)),
436 Box2I(corner=Point2I(85, 8), dimensions=Extent2I(2, 8))]
437 for badBox in badPixels:
438 defects.append(badBox)
440 self.check_maskBlocks(defects, expectedDefects)
442 def test_maskBlocks_both_sides_good_greater_than_threshold(self):
443 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
444 Npix discontiguous bad pixels in column with "blobs" of "m" bad pixels to both sides,
445 m > badOnAndOffPixelColumnThreshold (10), number of good pixel in gaps between blobs >
446 goodPixelColumnGapThreshold (5).
448 Plots can be found in DM-19903 on Jira.
449 """
451 expectedDefects = [Box2I(corner=Point2I(93, 1), dimensions=Extent2I(1, 34)),
452 Box2I(corner=Point2I(91, 2), dimensions=Extent2I(2, 7)),
453 Box2I(corner=Point2I(91, 18), dimensions=Extent2I(2, 9)),
454 Box2I(corner=Point2I(94, 2), dimensions=Extent2I(2, 7)),
455 Box2I(corner=Point2I(94, 18), dimensions=Extent2I(2, 9))]
456 defects = self.allDefectsList
457 badPixels = [Box2I(corner=Point2I(93, 1), dimensions=Extent2I(1, 12)),
458 Box2I(corner=Point2I(93, 15), dimensions=Extent2I(1, 20)),
459 Box2I(corner=Point2I(91, 2), dimensions=Extent2I(2, 2)),
460 Box2I(corner=Point2I(91, 7), dimensions=Extent2I(2, 2)),
461 Box2I(corner=Point2I(94, 2), dimensions=Extent2I(2, 2)),
462 Box2I(corner=Point2I(94, 7), dimensions=Extent2I(2, 2)),
463 Box2I(corner=Point2I(91, 18), dimensions=Extent2I(2, 3)),
464 Box2I(corner=Point2I(91, 24), dimensions=Extent2I(2, 3)),
465 Box2I(corner=Point2I(94, 18), dimensions=Extent2I(2, 3)),
466 Box2I(corner=Point2I(94, 24), dimensions=Extent2I(2, 3))]
467 for badBox in badPixels:
468 defects.append(badBox)
470 self.check_maskBlocks(defects, expectedDefects)
472 def test_defectFindingAllSensor(self):
473 config = copy.copy(self.defaultConfig)
474 config.measure.nPixBorderLeftRight = 0
475 config.measure.nPixBorderUpDown = 0
477 task = cpPipe.defects.FindDefectsTask(config=config)
479 defects = task.measure.findHotAndColdPixels(self.flatExp, [config.measure.nSigmaBright,
480 config.measure.nSigmaDark])
482 allBBoxes = self.darkBBoxes + self.brightBBoxes
484 boxesMeasured = []
485 for defect in defects:
486 boxesMeasured.append(defect.getBBox())
488 for expectedBBox in allBBoxes:
489 self.assertIn(expectedBBox, boxesMeasured)
491 def test_defectFindingEdgeIgnore(self):
492 config = copy.copy(self.defaultConfig)
493 config.measure.nPixBorderUpDown = 0
494 task = cpPipe.defects.FindDefectsTask(config=config)
495 defects = task.measure.findHotAndColdPixels(self.flatExp, [config.measure.nSigmaBright,
496 config.measure.nSigmaDark])
498 shouldBeFound = self.darkBBoxes[self.noEdges] + self.brightBBoxes[self.noEdges]
500 boxesMeasured = []
501 for defect in defects:
502 boxesMeasured.append(defect.getBBox())
504 for expectedBBox in shouldBeFound:
505 self.assertIn(expectedBBox, boxesMeasured)
507 shouldBeMissed = self.darkBBoxes[self.onlyEdges] + self.brightBBoxes[self.onlyEdges]
508 for boxMissed in shouldBeMissed:
509 self.assertNotIn(boxMissed, boxesMeasured)
511 def test_pixelCounting(self):
512 """Test that the number of defective pixels identified is as expected."""
513 config = copy.copy(self.defaultConfig)
514 config.measure.nPixBorderUpDown = 0
515 config.measure.nPixBorderLeftRight = 0
516 task = cpPipe.defects.FindDefectsTask(config=config)
517 defects = task.measure.findHotAndColdPixels(self.flatExp, [config.measure.nSigmaBright,
518 config.measure.nSigmaDark])
520 defectArea = 0
521 for defect in defects:
522 defectArea += defect.getBBox().getArea()
524 # The columnar code will cover blocks of a column
525 # with on-and-off pixels, thus creating more bad pixels
526 # that what initially placed in self.brightDefects and self.darkDefects.
527 # Thus, defectArea should be >= crossCheck.
528 crossCheck = 0
529 for x, y, sx, sy in self.brightDefects:
530 crossCheck += sx*sy
531 for x, y, sx, sy in self.darkDefects:
532 crossCheck += sx*sy
534 # Test the result of _nPixFromDefects()
535 # via two different ways of calculating area.
536 self.assertEqual(defectArea, task.measure._nPixFromDefects(defects))
537 # defectArea should be >= crossCheck
538 self.assertGreaterEqual(defectArea, crossCheck)
540 def test_getNumGoodPixels(self):
541 """Test the the number of pixels in the image not masked is as expected."""
542 testImage = self.flatExp.clone()
543 mi = testImage.maskedImage
545 imageSize = testImage.getBBox().getArea()
546 nGood = self.defaultTask.measure._getNumGoodPixels(mi)
548 self.assertEqual(imageSize, nGood)
550 NODATABIT = mi.mask.getPlaneBitMask("NO_DATA")
552 noDataBox = Box2I(Point2I(31, 49), Extent2I(3, 6))
553 testImage.mask[noDataBox] |= NODATABIT
555 self.assertEqual(imageSize - noDataBox.getArea(), self.defaultTask.measure._getNumGoodPixels(mi))
556 # check for misfire; we're setting NO_DATA here, not BAD
557 self.assertEqual(imageSize, self.defaultTask.measure._getNumGoodPixels(mi, 'BAD'))
559 testImage.mask[noDataBox] ^= NODATABIT # XOR to reset what we did
560 self.assertEqual(imageSize, nGood)
562 BADBIT = mi.mask.getPlaneBitMask("BAD")
563 badBox = Box2I(Point2I(85, 98), Extent2I(4, 7))
564 testImage.mask[badBox] |= BADBIT
566 self.assertEqual(imageSize - badBox.getArea(), self.defaultTask.measure._getNumGoodPixels(mi, 'BAD'))
568 def test_edgeMasking(self):
569 """Check that the right number of edge pixels are masked by _setEdgeBits()"""
570 testImage = self.flatExp.clone()
571 mi = testImage.maskedImage
573 self.assertEqual(cpPipe.utils.countMaskedPixels(mi, 'EDGE'), 0)
574 self.defaultTask.measure._setEdgeBits(mi)
576 hEdge = self.defaultConfig.measure.nPixBorderLeftRight
577 vEdge = self.defaultConfig.measure.nPixBorderUpDown
578 xSize, ySize = mi.getDimensions()
580 nEdge = xSize*vEdge*2 + ySize*hEdge*2 - hEdge*vEdge*4
582 self.assertEqual(cpPipe.utils.countMaskedPixels(mi, 'EDGE'), nEdge)
585class TestMemory(lsst.utils.tests.MemoryTestCase):
586 pass
589def setup_module(module):
590 lsst.utils.tests.init()
593if __name__ == "__main__": 593 ↛ 594line 593 didn't jump to line 594, because the condition on line 593 was never true
594 lsst.utils.tests.init()
595 unittest.main()