Coverage for tests/test_snapCombine.py: 16%

102 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-26 16:57 +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 

23 

24import numpy as np 

25 

26import lsst.utils.tests 

27import lsst.afw.image as afwImage 

28from lsst.coadd.utils import setCoaddEdgeBits 

29from lsst.pipe.tasks.snapCombine import SnapCombineTask 

30 

31np.random.seed(1) 

32 

33 

34def makeRandomExposure(width, height, imMean, varMean, maxMask): 

35 """Make a random exposure with Poisson distribution for image and variance 

36 

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) 

47 

48 return exp 

49 

50 

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()) 

56 

57 good0 = np.bitwise_and(exp0.mask.array, badPixelMask) == 0 

58 good1 = np.bitwise_and(exp1.mask.array, badPixelMask) == 0 

59 

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) 

66 

67 expRes.maskedImage /= weightMap 

68 expRes.maskedImage *= 2 # want addition, not mean, where both pixels are valid 

69 

70 setCoaddEdgeBits(expRes.mask, weightMap) 

71 

72 return expRes 

73 

74 

75class SnapCombineTestCase(lsst.utils.tests.TestCase): 

76 

77 """A test case for SnapCombineTask.""" 

78 

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) 

88 

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 

93 

94 predExp = simpleAdd(snap0, snap1, badPixelMask) 

95 predMi = predExp.maskedImage 

96 self.assertMaskedImagesAlmostEqual(resMi, predMi) 

97 

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) 

105 

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 

110 

111 predMi = snap0.maskedImage.Factory(snap0.maskedImage, True) 

112 predMi += snap1.maskedImage 

113 self.assertMaskedImagesAlmostEqual(resMi, predMi) 

114 

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) 

128 

129 snap0 = makeRandomExposure(5, 5, 10000, 5000, 0) 

130 snap1 = makeRandomExposure(5, 5, 10000, 5000, 0) 

131 

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) 

148 

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 

156 

157 resExp = task.run(snap0, snap1).exposure 

158 resMetadata = resExp.getMetadata() 

159 

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)) 

170 

171 

172class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

173 pass 

174 

175 

176def setup_module(module): 

177 lsst.utils.tests.init() 

178 

179 

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()