Coverage for tests/test_defects.py: 13%
Shortcuts 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
Shortcuts 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
115 maskBlocksIfIntermitentBadPixelsInColumn.
117 """
118 config = copy.copy(self.defaultConfig)
119 config.measure.badOnAndOffPixelColumnThreshold = 10
120 config.measure.goodPixelColumnGapThreshold = 5
121 config.measure.nPixBorderUpDown = 0
122 config.measure.nPixBorderLeftRight = 0
124 task = cpPipe.defects.FindDefectsTask(config=config)
126 defectsWithColumns = task.measure.maskBlocksIfIntermitentBadPixelsInColumn(inputDefects)
127 boxesMeasured = []
128 for defect in defectsWithColumns:
129 boxesMeasured.append(defect.getBBox())
131 for boxInput in expectedDefects:
132 self.assertIn(boxInput, boxesMeasured)
134 # Check that the code did not mask anything extra by
135 # looking in both the input list and "expanded-column" list.
136 unionInputExpectedBoxes = []
137 for defect in inputDefects:
138 unionInputExpectedBoxes.append(defect.getBBox())
139 for defect in expectedDefects:
140 unionInputExpectedBoxes.append(defect)
142 # Check that code doesn't mask more than it is supposed to.
143 for boxMeas in boxesMeasured:
144 self.assertIn(boxMeas, unionInputExpectedBoxes)
146 def test_maskBlocks_full_column(self):
147 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
149 Tests that a contigous bad column does not get split by the
150 code.
152 The mock flat has a size of 200X204 pixels. This column has a
153 maximum length of 50 pixels, otherwise there would be a split
154 along the mock amp boundary.
156 Plots can be found in DM-19903 on Jira.
158 """
160 defects = self.allDefectsList
161 defects.append(Box2I(corner=Point2I(15, 1), dimensions=Extent2I(1, 50)))
162 expectedDefects = [Box2I(corner=Point2I(15, 1), dimensions=Extent2I(1, 50))]
164 self.check_maskBlocks(defects, expectedDefects)
166 def test_maskBlocks_long_column(self):
167 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
169 Tests that a contigous bad column with Npix >=
170 badOnAndOffPixelColumnThreshold (10) does not get split by the
171 code.
173 Plots can be found in DM-19903 on Jira.
175 """
177 expectedDefects = [Box2I(corner=Point2I(20, 1), dimensions=Extent2I(1, 25))]
178 defects = self.allDefectsList
179 defects.append(Box2I(corner=Point2I(20, 1), dimensions=Extent2I(1, 25)))
181 self.check_maskBlocks(defects, expectedDefects)
183 def test_maskBlocks_short_column(self):
184 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
186 Tests that a contigous bad column Npix <
187 badOnAndOffPixelColumnThreshold (10) does not get split by the
188 code.
190 Plots can be found in DM-19903 on Jira.
192 """
194 expectedDefects = [Box2I(corner=Point2I(25, 1), dimensions=Extent2I(1, 8))]
195 defects = self.allDefectsList
196 defects.append(Box2I(corner=Point2I(25, 1), dimensions=Extent2I(1, 8)))
198 self.check_maskBlocks(defects, expectedDefects)
200 def test_maskBlocks_discontigous_to_single_block(self):
201 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
203 Npix discontiguous bad pixels in a column where Npix >=
204 badOnAndOffPixelColumnThreshold (10) and gaps of good pixels <
205 goodPixelColumnGapThreshold (5). Under these conditions, the
206 whole block of bad pixels (including good gaps) should be
207 masked.
209 Plots can be found in DM-19903 on Jira.
211 """
213 expectedDefects = [Box2I(corner=Point2I(30, 1), dimensions=Extent2I(1, 48))]
214 defects = self.allDefectsList
215 badPixels = [Box2I(corner=Point2I(30, 1), dimensions=Extent2I(1, 2)),
216 Box2I(corner=Point2I(30, 5), dimensions=Extent2I(1, 3)),
217 Box2I(corner=Point2I(30, 11), dimensions=Extent2I(1, 5)),
218 Box2I(corner=Point2I(30, 19), dimensions=Extent2I(1, 5)),
219 Box2I(corner=Point2I(30, 27), dimensions=Extent2I(1, 4)),
220 Box2I(corner=Point2I(30, 34), dimensions=Extent2I(1, 15))]
221 for badBox in badPixels:
222 defects.append(badBox)
224 self.check_maskBlocks(defects, expectedDefects)
226 def test_maskBlocks_discontigous_less_than_thresholds(self):
227 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
229 Npix discontiguous bad pixels in a column where Npix <
230 badOnAndOffPixelColumnThreshold (10) and gaps of good pixels <
231 goodPixelColumnGapThreshold (5). Under these conditions, the
232 expected defect boxes should be the same as the input boxes.
234 Plots can be found in DM-19903 on Jira.
236 """
238 expectedDefects = [Box2I(corner=Point2I(35, 1), dimensions=Extent2I(1, 2)),
239 Box2I(corner=Point2I(35, 5), dimensions=Extent2I(1, 3)),
240 Box2I(corner=Point2I(35, 11), dimensions=Extent2I(1, 2))]
241 defects = self.allDefectsList
242 badPixels = [Box2I(corner=Point2I(35, 1), dimensions=Extent2I(1, 2)),
243 Box2I(corner=Point2I(35, 5), dimensions=Extent2I(1, 3)),
244 Box2I(corner=Point2I(35, 11), dimensions=Extent2I(1, 2))]
245 for badBox in badPixels:
246 defects.append(badBox)
248 self.check_maskBlocks(defects, expectedDefects)
250 def test_maskBlocks_more_than_thresholds(self):
251 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
253 Npix discontiguous bad pixels in a column where Npix <
254 badOnAndOffPixelColumnThreshold (10) and gaps of good pixels <
255 goodPixelColumnGapThreshold (5). Npix=34 (> 10) bad pixels
256 total, 1 "good" gap with 13 pixels big enough (13 >= 5 good
257 pixels, from y=6 (1+5) to y=19).
259 Plots can be found in DM-19903 on Jira.
261 """
263 expectedDefects = [Box2I(corner=Point2I(40, 1), dimensions=Extent2I(1, 7)),
264 Box2I(corner=Point2I(40, 19), dimensions=Extent2I(1, 30))]
265 defects = self.allDefectsList
266 badPixels = [Box2I(corner=Point2I(40, 1), dimensions=Extent2I(1, 2)),
267 Box2I(corner=Point2I(40, 5), dimensions=Extent2I(1, 3)),
268 Box2I(corner=Point2I(40, 19), dimensions=Extent2I(1, 5)),
269 Box2I(corner=Point2I(40, 27), dimensions=Extent2I(1, 4)),
270 Box2I(corner=Point2I(40, 34), dimensions=Extent2I(1, 15))]
271 for badBox in badPixels:
272 defects.append(badBox)
274 self.check_maskBlocks(defects, expectedDefects)
276 def test_maskBlocks_not_enough_bad_pixels_in_column(self):
277 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
279 Npix discontiguous bad pixels in a column where Npix <
280 badOnAndOffPixelColumnThreshold (10) and and gaps of good
281 pixels > goodPixelColumnGapThreshold (5). Since Npix <
282 badOnAndOffPixelColumnThreshold, then it doesn't matter that
283 the number of good pixels in gap >
284 goodPixelColumnGapThreshold. 5<10 bad pixels total, 1 "good"
285 gap big enough (29>=5 good pixels, from y =12 (10+2) to y=30)
287 Plots can be found in DM-19903 on Jira.
289 """
291 expectedDefects = [Box2I(corner=Point2I(45, 10), dimensions=Extent2I(1, 2)),
292 Box2I(corner=Point2I(45, 30), dimensions=Extent2I(1, 3))]
293 defects = self.allDefectsList
294 badPixels = [Box2I(corner=Point2I(45, 10), dimensions=Extent2I(1, 2)),
295 Box2I(corner=Point2I(45, 30), dimensions=Extent2I(1, 3))]
296 for badBox in badPixels:
297 defects.append(badBox)
299 self.check_maskBlocks(defects, expectedDefects)
301 def test_maskBlocks_every_other_pixel_bad_greater_than_threshold(self):
302 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
304 Npix discontiguous bad pixels in a column where Npix >
305 badOnAndOffPixelColumnThreshold (10) and every other pixel is
306 bad.
308 Plots can be found in DM-19903 on Jira.
310 """
312 expectedDefects = [Box2I(corner=Point2I(50, 10), dimensions=Extent2I(1, 31))]
313 defects = self.allDefectsList
314 badPixels = [Box2I(corner=Point2I(50, 10), dimensions=Extent2I(1, 1)),
315 Box2I(corner=Point2I(50, 12), dimensions=Extent2I(1, 1)),
316 Box2I(corner=Point2I(50, 14), dimensions=Extent2I(1, 1)),
317 Box2I(corner=Point2I(50, 16), dimensions=Extent2I(1, 1)),
318 Box2I(corner=Point2I(50, 18), dimensions=Extent2I(1, 1)),
319 Box2I(corner=Point2I(50, 20), dimensions=Extent2I(1, 1)),
320 Box2I(corner=Point2I(50, 22), dimensions=Extent2I(1, 1)),
321 Box2I(corner=Point2I(50, 24), dimensions=Extent2I(1, 1)),
322 Box2I(corner=Point2I(50, 26), dimensions=Extent2I(1, 1)),
323 Box2I(corner=Point2I(50, 28), dimensions=Extent2I(1, 1)),
324 Box2I(corner=Point2I(50, 30), dimensions=Extent2I(1, 1)),
325 Box2I(corner=Point2I(50, 32), dimensions=Extent2I(1, 1)),
326 Box2I(corner=Point2I(50, 34), dimensions=Extent2I(1, 1)),
327 Box2I(corner=Point2I(50, 36), dimensions=Extent2I(1, 1)),
328 Box2I(corner=Point2I(50, 38), dimensions=Extent2I(1, 1)),
329 Box2I(corner=Point2I(50, 40), dimensions=Extent2I(1, 1))]
330 for badBox in badPixels:
331 defects.append(badBox)
333 self.check_maskBlocks(defects, expectedDefects)
335 def test_maskBlocks_every_other_pixel_bad_less_than_threshold(self):
336 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
338 Npix discontiguous bad pixels in a column where Npix >
339 badOnAndOffPixelColumnThreshold (10) and every other pixel is
340 bad.
342 Plots can be found in DM-19903 on Jira.
344 """
346 expectedDefects = [Box2I(corner=Point2I(55, 20), dimensions=Extent2I(1, 1)),
347 Box2I(corner=Point2I(55, 22), dimensions=Extent2I(1, 1)),
348 Box2I(corner=Point2I(55, 24), dimensions=Extent2I(1, 1)),
349 Box2I(corner=Point2I(55, 26), dimensions=Extent2I(1, 1)),
350 Box2I(corner=Point2I(55, 28), dimensions=Extent2I(1, 1)),
351 Box2I(corner=Point2I(55, 30), dimensions=Extent2I(1, 1))]
352 defects = self.allDefectsList
353 badPixels = [Box2I(corner=Point2I(55, 20), dimensions=Extent2I(1, 1)),
354 Box2I(corner=Point2I(55, 22), dimensions=Extent2I(1, 1)),
355 Box2I(corner=Point2I(55, 24), dimensions=Extent2I(1, 1)),
356 Box2I(corner=Point2I(55, 26), dimensions=Extent2I(1, 1)),
357 Box2I(corner=Point2I(55, 28), dimensions=Extent2I(1, 1)),
358 Box2I(corner=Point2I(55, 30), dimensions=Extent2I(1, 1))]
359 for badBox in badPixels:
360 defects.append(badBox)
362 self.check_maskBlocks(defects, expectedDefects)
364 def test_maskBlocks_blobs_one_side_good_less_than_threshold(self):
365 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
367 Npix discontiguous bad pixels in column with "blobs" of "m"
368 bad pixels to one side, m > badOnAndOffPixelColumnThreshold
369 (10), number of good pixel in gaps between blobs <
370 goodPixelColumnGapThreshold (5).
372 Plots can be found in DM-19903 on Jira.
374 """
376 expectedDefects = [Box2I(corner=Point2I(60, 1), dimensions=Extent2I(1, 29)),
377 Box2I(corner=Point2I(61, 2), dimensions=Extent2I(2, 12))]
378 defects = self.allDefectsList
379 badPixels = [Box2I(corner=Point2I(60, 1), dimensions=Extent2I(1, 18)),
380 Box2I(corner=Point2I(60, 20), dimensions=Extent2I(1, 10)),
381 Box2I(corner=Point2I(61, 2), dimensions=Extent2I(2, 2)),
382 Box2I(corner=Point2I(61, 6), dimensions=Extent2I(2, 8))]
383 for badBox in badPixels:
384 defects.append(badBox)
386 self.check_maskBlocks(defects, expectedDefects)
388 def test_maskBlocks_blobs_other_side_good_less_than_threshold(self):
389 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
391 Npix discontiguous bad pixels in column with "blobs" of "m"
392 bad pixels to the other side, m >
393 badOnAndOffPixelColumnThreshold (10), number of good pixel in
394 gaps between blobs < goodPixelColumnGapThreshold (5).
396 Plots can be found in DM-19903 on Jira.
398 """
400 expectedDefects = [Box2I(corner=Point2I(70, 1), dimensions=Extent2I(1, 29)),
401 Box2I(corner=Point2I(68, 2), dimensions=Extent2I(2, 12))]
402 defects = self.allDefectsList
403 badPixels = [Box2I(corner=Point2I(70, 1), dimensions=Extent2I(1, 18)),
404 Box2I(corner=Point2I(70, 20), dimensions=Extent2I(1, 10)),
405 Box2I(corner=Point2I(68, 2), dimensions=Extent2I(2, 2)),
406 Box2I(corner=Point2I(68, 6), dimensions=Extent2I(2, 8))]
407 for badBox in badPixels:
408 defects.append(badBox)
410 self.check_maskBlocks(defects, expectedDefects)
412 def test_maskBlocks_blob_both_sides_good_less_than_threshold(self):
413 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
415 Npix discontiguous bad pixels in column with "blobs" of "m"
416 bad pixels to both sides, m > badOnAndOffPixelColumnThreshold
417 (10), number of good pixel in gaps between blobs <
418 goodPixelColumnGapThreshold (5).
420 Plots can be found in DM-19903 on Jira.
422 """
424 expectedDefects = [Box2I(corner=Point2I(75, 1), dimensions=Extent2I(1, 29)),
425 Box2I(corner=Point2I(73, 2), dimensions=Extent2I(2, 12)),
426 Box2I(corner=Point2I(76, 2), dimensions=Extent2I(2, 12))]
427 defects = self.allDefectsList
428 badPixels = [Box2I(corner=Point2I(75, 1), dimensions=Extent2I(1, 18)),
429 Box2I(corner=Point2I(75, 20), dimensions=Extent2I(1, 10)),
430 Box2I(corner=Point2I(73, 2), dimensions=Extent2I(2, 2)),
431 Box2I(corner=Point2I(73, 6), dimensions=Extent2I(2, 8)),
432 Box2I(corner=Point2I(76, 2), dimensions=Extent2I(2, 2)),
433 Box2I(corner=Point2I(76, 6), dimensions=Extent2I(2, 8))]
434 for badBox in badPixels:
435 defects.append(badBox)
437 self.check_maskBlocks(defects, expectedDefects)
439 def test_maskBlocks_blob_one_side_good_greater_than_threshold(self):
440 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
442 Npix discontiguous bad pixels in column with "blobs" of "m"
443 bad pixels to one side, m > badOnAndOffPixelColumnThreshold
444 (10), number of good pixel in gaps between blobs >
445 goodPixelColumnGapThreshold (5).
447 Plots can be found in DM-19903 on Jira.
449 """
451 expectedDefects = [Box2I(corner=Point2I(80, 1), dimensions=Extent2I(1, 29)),
452 Box2I(corner=Point2I(81, 2), dimensions=Extent2I(2, 2)),
453 Box2I(corner=Point2I(81, 8), dimensions=Extent2I(2, 8))]
454 defects = self.allDefectsList
455 badPixels = [Box2I(corner=Point2I(80, 1), dimensions=Extent2I(1, 18)),
456 Box2I(corner=Point2I(80, 20), dimensions=Extent2I(1, 10)),
457 Box2I(corner=Point2I(81, 2), dimensions=Extent2I(2, 2)),
458 Box2I(corner=Point2I(81, 8), dimensions=Extent2I(2, 8))]
459 for badBox in badPixels:
460 defects.append(badBox)
462 self.check_maskBlocks(defects, expectedDefects)
464 def test_maskBlocks_other_side_good_greater_than_threshold(self):
465 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
467 Npix discontiguous bad pixels in column with "blobs" of "m"
468 bad pixels to the other side, m >
469 badOnAndOffPixelColumnThreshold (10), number of good pixel in
470 gaps between blobs > goodPixelColumnGapThreshold (5).
472 Plots can be found in DM-19903 on Jira.
474 """
476 expectedDefects = [Box2I(corner=Point2I(87, 1), dimensions=Extent2I(1, 29)),
477 Box2I(corner=Point2I(85, 2), dimensions=Extent2I(2, 2)),
478 Box2I(corner=Point2I(85, 8), dimensions=Extent2I(2, 8))]
479 defects = self.allDefectsList
480 badPixels = [Box2I(corner=Point2I(87, 1), dimensions=Extent2I(1, 18)),
481 Box2I(corner=Point2I(87, 20), dimensions=Extent2I(1, 10)),
482 Box2I(corner=Point2I(85, 2), dimensions=Extent2I(2, 2)),
483 Box2I(corner=Point2I(85, 8), dimensions=Extent2I(2, 8))]
484 for badBox in badPixels:
485 defects.append(badBox)
487 self.check_maskBlocks(defects, expectedDefects)
489 def test_maskBlocks_both_sides_good_greater_than_threshold(self):
490 """A test for maskBlocksIfIntermitentBadPixelsInColumn.
492 Npix discontiguous bad pixels in column with "blobs" of "m"
493 bad pixels to both sides, m > badOnAndOffPixelColumnThreshold
494 (10), number of good pixel in gaps between blobs >
495 goodPixelColumnGapThreshold (5).
497 Plots can be found in DM-19903 on Jira.
499 """
501 expectedDefects = [Box2I(corner=Point2I(93, 1), dimensions=Extent2I(1, 34)),
502 Box2I(corner=Point2I(91, 2), dimensions=Extent2I(2, 7)),
503 Box2I(corner=Point2I(91, 18), dimensions=Extent2I(2, 9)),
504 Box2I(corner=Point2I(94, 2), dimensions=Extent2I(2, 7)),
505 Box2I(corner=Point2I(94, 18), dimensions=Extent2I(2, 9))]
506 defects = self.allDefectsList
507 badPixels = [Box2I(corner=Point2I(93, 1), dimensions=Extent2I(1, 12)),
508 Box2I(corner=Point2I(93, 15), dimensions=Extent2I(1, 20)),
509 Box2I(corner=Point2I(91, 2), dimensions=Extent2I(2, 2)),
510 Box2I(corner=Point2I(91, 7), dimensions=Extent2I(2, 2)),
511 Box2I(corner=Point2I(94, 2), dimensions=Extent2I(2, 2)),
512 Box2I(corner=Point2I(94, 7), dimensions=Extent2I(2, 2)),
513 Box2I(corner=Point2I(91, 18), dimensions=Extent2I(2, 3)),
514 Box2I(corner=Point2I(91, 24), dimensions=Extent2I(2, 3)),
515 Box2I(corner=Point2I(94, 18), dimensions=Extent2I(2, 3)),
516 Box2I(corner=Point2I(94, 24), dimensions=Extent2I(2, 3))]
517 for badBox in badPixels:
518 defects.append(badBox)
520 self.check_maskBlocks(defects, expectedDefects)
522 def test_defectFindingAllSensor(self):
523 config = copy.copy(self.defaultConfig)
524 config.measure.nPixBorderLeftRight = 0
525 config.measure.nPixBorderUpDown = 0
527 task = cpPipe.defects.FindDefectsTask(config=config)
529 defects = task.measure.findHotAndColdPixels(self.flatExp, [config.measure.nSigmaBright,
530 config.measure.nSigmaDark])
532 allBBoxes = self.darkBBoxes + self.brightBBoxes
534 boxesMeasured = []
535 for defect in defects:
536 boxesMeasured.append(defect.getBBox())
538 for expectedBBox in allBBoxes:
539 self.assertIn(expectedBBox, boxesMeasured)
541 def test_defectFindingEdgeIgnore(self):
542 config = copy.copy(self.defaultConfig)
543 config.measure.nPixBorderUpDown = 0
544 task = cpPipe.defects.FindDefectsTask(config=config)
545 defects = task.measure.findHotAndColdPixels(self.flatExp, [config.measure.nSigmaBright,
546 config.measure.nSigmaDark])
548 shouldBeFound = self.darkBBoxes[self.noEdges] + self.brightBBoxes[self.noEdges]
550 boxesMeasured = []
551 for defect in defects:
552 boxesMeasured.append(defect.getBBox())
554 for expectedBBox in shouldBeFound:
555 self.assertIn(expectedBBox, boxesMeasured)
557 shouldBeMissed = self.darkBBoxes[self.onlyEdges] + self.brightBBoxes[self.onlyEdges]
558 for boxMissed in shouldBeMissed:
559 self.assertNotIn(boxMissed, boxesMeasured)
561 def test_pixelCounting(self):
562 """Test that the number of defective pixels identified is as expected.
563 """
564 config = copy.copy(self.defaultConfig)
565 config.measure.nPixBorderUpDown = 0
566 config.measure.nPixBorderLeftRight = 0
567 task = cpPipe.defects.FindDefectsTask(config=config)
568 defects = task.measure.findHotAndColdPixels(self.flatExp, [config.measure.nSigmaBright,
569 config.measure.nSigmaDark])
571 defectArea = 0
572 for defect in defects:
573 defectArea += defect.getBBox().getArea()
575 # The columnar code will cover blocks of a column with
576 # on-and-off pixels, thus creating more bad pixels that what
577 # initially placed in self.brightDefects and self.darkDefects.
578 # Thus, defectArea should be >= crossCheck.
579 crossCheck = 0
580 for x, y, sx, sy in self.brightDefects:
581 crossCheck += sx*sy
582 for x, y, sx, sy in self.darkDefects:
583 crossCheck += sx*sy
585 # Test the result of _nPixFromDefects()
586 # via two different ways of calculating area.
587 self.assertEqual(defectArea, task.measure._nPixFromDefects(defects))
588 # defectArea should be >= crossCheck
589 self.assertGreaterEqual(defectArea, crossCheck)
591 def test_getNumGoodPixels(self):
592 """Test the the number of pixels in the image not masked is as
593 expected.
595 """
596 testImage = self.flatExp.clone()
597 mi = testImage.maskedImage
599 imageSize = testImage.getBBox().getArea()
600 nGood = self.defaultTask.measure._getNumGoodPixels(mi)
602 self.assertEqual(imageSize, nGood)
604 NODATABIT = mi.mask.getPlaneBitMask("NO_DATA")
606 noDataBox = Box2I(Point2I(31, 49), Extent2I(3, 6))
607 testImage.mask[noDataBox] |= NODATABIT
609 self.assertEqual(imageSize - noDataBox.getArea(), self.defaultTask.measure._getNumGoodPixels(mi))
610 # check for misfire; we're setting NO_DATA here, not BAD
611 self.assertEqual(imageSize, self.defaultTask.measure._getNumGoodPixels(mi, 'BAD'))
613 testImage.mask[noDataBox] ^= NODATABIT # XOR to reset what we did
614 self.assertEqual(imageSize, nGood)
616 BADBIT = mi.mask.getPlaneBitMask("BAD")
617 badBox = Box2I(Point2I(85, 98), Extent2I(4, 7))
618 testImage.mask[badBox] |= BADBIT
620 self.assertEqual(imageSize - badBox.getArea(), self.defaultTask.measure._getNumGoodPixels(mi, 'BAD'))
622 def test_edgeMasking(self):
623 """Check that the right number of edge pixels are masked by
624 _setEdgeBits().
626 """
627 testImage = self.flatExp.clone()
628 mi = testImage.maskedImage
630 self.assertEqual(cpPipe.utils.countMaskedPixels(mi, 'EDGE'), 0)
631 self.defaultTask.measure._setEdgeBits(mi)
633 hEdge = self.defaultConfig.measure.nPixBorderLeftRight
634 vEdge = self.defaultConfig.measure.nPixBorderUpDown
635 xSize, ySize = mi.getDimensions()
637 nEdge = xSize*vEdge*2 + ySize*hEdge*2 - hEdge*vEdge*4
639 self.assertEqual(cpPipe.utils.countMaskedPixels(mi, 'EDGE'), nEdge)
642class TestMemory(lsst.utils.tests.MemoryTestCase):
643 pass
646def setup_module(module):
647 lsst.utils.tests.init()
650if __name__ == "__main__": 650 ↛ 651line 650 didn't jump to line 651, because the condition on line 650 was never true
651 lsst.utils.tests.init()
652 unittest.main()