Coverage for tests/test_snapCombine.py: 16%
102 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-11 10:41 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-11 10:41 +0000
1#
2# LSST Data Management System
3# Copyright 2008, 2009, 2010 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
22import unittest
24import numpy as np
26import lsst.utils.tests
27import lsst.afw.image as afwImage
28from lsst.coadd.utils import setCoaddEdgeBits
29from lsst.pipe.tasks.snapCombine import SnapCombineTask
31np.random.seed(1)
34def makeRandomExposure(width, height, imMean, varMean, maxMask):
35 """Make a random exposure with Poisson distribution for image and variance
37 @param[in] width image width (pixels)
38 @param[in] height image height (pixels)
39 @param[in] imMean mean of image plane
40 @param[in] varMean mean of variance plane
41 @param[in] maxMask maximum mask value; values will be uniformly chosen in [0, maxMask]
42 """
43 exp = afwImage.ExposureF(width, height)
44 exp.image.array[:, :] = np.random.poisson(imMean, size=exp.image.array.shape)
45 exp.variance.array[:, :] = np.random.poisson(varMean, size=exp.variance.array.shape)
46 exp.mask.array[:, :] = np.random.randint(0, maxMask + 1, size=exp.mask.array.shape)
48 return exp
51def simpleAdd(exp0, exp1, badPixelMask):
52 """Add two exposures, avoiding bad pixels
53 """
54 expRes = exp0.Factory(exp0, True)
55 weightMap = afwImage.ImageF(exp0.getDimensions())
57 good0 = np.bitwise_and(exp0.mask.array, badPixelMask) == 0
58 good1 = np.bitwise_and(exp1.mask.array, badPixelMask) == 0
60 expRes.image.array[:, :] = np.where(good0, exp0.image.array, 0) + np.where(good1, exp1.image.array, 0)
61 expRes.variance.array[:, :] = np.where(good0, exp0.variance.array, 0) + \
62 np.where(good1, exp1.variance.array, 0)
63 expRes.mask.array[:, :] = np.bitwise_or(np.where(good0, exp0.mask.array, 0),
64 np.where(good1, exp1.mask.array, 0))
65 weightMap.array[:, :] = np.where(good0, 1, 0) + np.where(good1, 1, 0)
67 expRes.maskedImage /= weightMap
68 expRes.maskedImage *= 2 # want addition, not mean, where both pixels are valid
70 setCoaddEdgeBits(expRes.mask, weightMap)
72 return expRes
75class SnapCombineTestCase(lsst.utils.tests.TestCase):
77 """A test case for SnapCombineTask."""
79 def testAddition(self):
80 """Test addition with bad pixels
81 """
82 config = SnapCombineTask.ConfigClass()
83 config.doRepair = False
84 config.doDiffIm = False
85 config.badMaskPlanes = ("BAD", "SAT", "NO_DATA", "CR")
86 badPixelMask = afwImage.MaskX.getPlaneBitMask(config.badMaskPlanes)
87 task = SnapCombineTask(config=config)
89 snap0 = makeRandomExposure(25, 25, 10000, 5000, badPixelMask)
90 snap1 = makeRandomExposure(25, 25, 10000, 5000, badPixelMask)
91 resExp = task.run(snap0, snap1).exposure
92 resMi = resExp.maskedImage
94 predExp = simpleAdd(snap0, snap1, badPixelMask)
95 predMi = predExp.maskedImage
96 self.assertMaskedImagesAlmostEqual(resMi, predMi)
98 def testAdditionAllGood(self):
99 """Test the case where all pixels are valid
100 """
101 config = SnapCombineTask.ConfigClass()
102 config.doRepair = False
103 config.doDiffIm = False
104 task = SnapCombineTask(config=config)
106 snap0 = makeRandomExposure(25, 25, 10000, 5000, 0)
107 snap1 = makeRandomExposure(25, 25, 10000, 5000, 0)
108 resExp = task.run(snap0, snap1).exposure
109 resMi = resExp.maskedImage
111 predMi = snap0.maskedImage.Factory(snap0.maskedImage, True)
112 predMi += snap1.maskedImage
113 self.assertMaskedImagesAlmostEqual(resMi, predMi)
115 def testMetadata(self):
116 """Test more advanced metadata handling
117 """
118 config = SnapCombineTask.ConfigClass()
119 config.doRepair = False
120 config.doDiffIm = False
121 # the MISS<N> keys are missing from metadata<N>
122 # and so cannot be summed or averaged
123 # MISS0 keys will be copied without alterate
124 # MISS1 keys will be missing from the result
125 config.averageKeys = ("AVG0", "AVG1", "MISS0AVG", "MISS1AVG")
126 config.sumKeys = ("SUM0", "SUM1", "MISS0SUM", "MISS1SUM")
127 task = SnapCombineTask(config=config)
129 snap0 = makeRandomExposure(5, 5, 10000, 5000, 0)
130 snap1 = makeRandomExposure(5, 5, 10000, 5000, 0)
132 metadata0 = snap0.getMetadata()
133 metadata1 = snap1.getMetadata()
134 metadata0.set("NUM0", 45.2)
135 metadata0.set("ASTR", "this is a string")
136 metadata0.set("AVG0", 10.5)
137 metadata1.set("AVG0", 9.5)
138 metadata0.set("AVG1", -0.7)
139 metadata1.set("AVG1", 0.2)
140 metadata0.set("MISS0AVG", 2.3523)
141 metadata1.set("MISS1AVG", 8.23)
142 metadata0.set("SUM0", 1.23)
143 metadata1.set("SUM0", 4.56)
144 metadata0.set("SUM1", 9814)
145 metadata1.set("SUM1", 3)
146 metadata0.set("MISS0SUM", 75.4)
147 metadata1.set("MISS1SUM", -234.3)
149 allKeys = set(metadata0.names()) | set(metadata1.names())
150 miss0Keys = set(key for key in allKeys if key.startswith("MISS0"))
151 miss1Keys = set(key for key in allKeys if key.startswith("MISS1"))
152 missKeys = miss0Keys | miss1Keys
153 avgKeys = set(config.averageKeys) - missKeys # keys that will be averaged
154 sumKeys = set(config.sumKeys) - missKeys # keys that will be summed
155 sameKeys = allKeys - (avgKeys | sumKeys | miss1Keys) # keys that will be the same
157 resExp = task.run(snap0, snap1).exposure
158 resMetadata = resExp.getMetadata()
160 for key in sameKeys:
161 self.assertEqual(resMetadata.getScalar(key), metadata0.getScalar(key))
162 for key in avgKeys:
163 self.assertAlmostEqual(resMetadata.getScalar(key),
164 (metadata0.getScalar(key) + metadata1.getScalar(key)) / 2.0)
165 for key in sumKeys:
166 self.assertAlmostEqual(resMetadata.getScalar(key),
167 metadata0.getScalar(key) + metadata1.getScalar(key))
168 for key in miss1Keys:
169 self.assertFalse(resMetadata.exists(key))
172class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
173 pass
176def setup_module(module):
177 lsst.utils.tests.init()
180if __name__ == "__main__": 180 ↛ 181line 180 didn't jump to line 181, because the condition on line 180 was never true
181 lsst.utils.tests.init()
182 unittest.main()