Coverage for tests/test_statisticsMasked.py: 20%

93 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-25 02:50 -0800

1# This file is part of afw. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

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 GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22""" 

23Tests for statisticsMasked 

24 

25Run with: 

26 python test_statisticsMasked.py 

27or 

28 pytest test_statisticsMasked.py 

29""" 

30 

31import unittest 

32 

33import numpy as np 

34 

35import lsst.utils.tests 

36import lsst.geom 

37import lsst.afw.image as afwImage 

38import lsst.afw.math as afwMath 

39 

40 

41class StatisticsTestCase(unittest.TestCase): 

42 

43 """A test case to check that special values (NaN and Masks) are begin handled in Statistics""" 

44 

45 def setUp(self): 

46 self.valL, self.valR = 10, 20 

47 self.nRow, self.nCol = 100, 200 

48 self.n = self.nRow*self.nCol 

49 

50 self.bboxL = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), 

51 lsst.geom.Point2I(self.nRow//2 - 1, self.nCol - 1)) 

52 self.bboxR = lsst.geom.Box2I(lsst.geom.Point2I(self.nRow//2, 0), 

53 lsst.geom.Point2I(self.nRow - 1, self.nCol - 1)) 

54 

55 # create masked images and set the left side to valL, and right to valR 

56 self.mimg = afwImage.MaskedImageF( 

57 lsst.geom.Extent2I(self.nRow, self.nCol)) 

58 self.mimg.set(0.0, 0x0, 0.0) 

59 self.mimgL = afwImage.MaskedImageF( 

60 self.mimg, self.bboxL, afwImage.LOCAL) 

61 self.mimgL.set(self.valL, 0x0, self.valL) 

62 self.mimgR = afwImage.MaskedImageF( 

63 self.mimg, self.bboxR, afwImage.LOCAL) 

64 self.mimgR.set(self.valR, 0x0, self.valR) 

65 

66 def tearDown(self): 

67 del self.mimg 

68 del self.mimgL 

69 del self.mimgR 

70 

71 # Verify that NaN values are being ignored 

72 # (by default, StatisticsControl.useNanSafe = True) 

73 # We'll set the L and R sides of an image to two different values and verify mean and stdev 

74 # ... then set R-side to NaN and try again ... we should get mean,stdev for L-side 

75 def testNaN(self): 

76 

77 # get the stats for the image with two values 

78 stats = afwMath.makeStatistics( 

79 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV) 

80 mean = 0.5*(self.valL + self.valR) 

81 nL, nR = (self.mimgL.getWidth()*self.mimgL.getHeight(), 

82 self.mimgR.getWidth()*self.mimgR.getHeight()) 

83 stdev = ((nL*(self.valL - mean)**2 + nR*(self.valR - mean)**2)/(nL + nR - 1))**0.5 

84 

85 self.assertEqual(stats.getValue(afwMath.NPOINT), self.n) 

86 self.assertEqual(stats.getValue(afwMath.MEAN), mean) 

87 self.assertEqual(stats.getValue(afwMath.STDEV), stdev) 

88 

89 # set the right side to NaN and stats should be just for the left side 

90 self.mimgR.set(np.nan, 0x0, self.valR) 

91 

92 statsNaN = afwMath.makeStatistics( 

93 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV) 

94 mean = self.valL 

95 stdev = 0.0 

96 self.assertEqual(statsNaN.getValue(afwMath.NPOINT), nL) 

97 self.assertEqual(statsNaN.getValue(afwMath.MEAN), mean) 

98 self.assertEqual(statsNaN.getValue(afwMath.STDEV), stdev) 

99 

100 # Verify that Masked pixels are being ignored according to the andMask 

101 # (by default, StatisticsControl.andMask = 0x0) 

102 # We'll set the L and R sides of an image to two different values and verify mean and stdev 

103 # ... then set R-side Mask and the andMask to 0x1 and try again ... we should get mean,stdev for L-side 

104 def testMasked(self): 

105 

106 # get the stats for the image with two values 

107 self.mimgR.set(self.valR, 0x0, self.valR) 

108 stats = afwMath.makeStatistics( 

109 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV) 

110 mean = 0.5*(self.valL + self.valR) 

111 nL, nR = (self.mimgL.getWidth()*self.mimgL.getHeight(), 

112 self.mimgR.getWidth()*self.mimgR.getHeight()) 

113 stdev = ((nL*(self.valL - mean)**2 + nR*(self.valR - mean)**2)/(nL + nR - 1))**0.5 

114 

115 self.assertEqual(stats.getValue(afwMath.NPOINT), self.n) 

116 self.assertEqual(stats.getValue(afwMath.MEAN), mean) 

117 self.assertEqual(stats.getValue(afwMath.STDEV), stdev) 

118 

119 # set the right side Mask and the StatisticsControl andMask to 0x1 

120 # Stats should be just for the left side! 

121 maskBit = 0x1 

122 self.mimgR.getMask().set(maskBit) 

123 

124 sctrl = afwMath.StatisticsControl() 

125 sctrl.setAndMask(maskBit) 

126 statsNaN = afwMath.makeStatistics( 

127 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV, sctrl) 

128 

129 mean = self.valL 

130 stdev = 0.0 

131 

132 self.assertEqual(statsNaN.getValue(afwMath.NPOINT), nL) 

133 self.assertEqual(statsNaN.getValue(afwMath.MEAN), mean) 

134 self.assertEqual(statsNaN.getValue(afwMath.STDEV), stdev) 

135 

136 # Verify that pixels are being weighted according to the variance plane (1/var) 

137 # We'll set the L and R sides of an image to two different values and verify mean 

138 # ... then set R-side Variance to equal the Image value, and set 'weighted' and try again ... 

139 def testWeighted(self): 

140 

141 self.mimgR.set(self.valR, 0x0, self.valR) 

142 sctrl = afwMath.StatisticsControl() 

143 sctrl.setWeighted(True) 

144 stats = afwMath.makeStatistics( 

145 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV, sctrl) 

146 nL, nR = self.mimgL.getWidth()*self.mimgL.getHeight(), self.mimgR.getWidth() * \ 

147 self.mimgR.getHeight() 

148 

149 mean = 1.0*(nL + nR)/(nL/self.valL + nR/self.valR) 

150 

151 # get the stats for the image with two values 

152 self.assertEqual(stats.getValue(afwMath.NPOINT), self.n) 

153 self.assertAlmostEqual(stats.getValue(afwMath.MEAN), mean, 10) 

154 

155 def testWeightedSimple(self): 

156 mimg = afwImage.MaskedImageF(lsst.geom.Extent2I(1, 2)) 

157 mimg[0, 0, afwImage.LOCAL] = (self.valR, 0x0, self.valR) 

158 mimg[0, 1, afwImage.LOCAL] = (self.valL, 0x0, self.valL) 

159 

160 sctrl = afwMath.StatisticsControl() 

161 sctrl.setWeighted(True) 

162 stats = afwMath.makeStatistics( 

163 mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV, sctrl) 

164 vsum = 2.0 

165 vsum2 = self.valR + self.valL 

166 wsum = 1.0/self.valR + 1.0/self.valL 

167 wwsum = 1.0/self.valR**2 + 1.0/self.valL**2 

168 mean = vsum/wsum 

169 variance = vsum2/wsum - mean**2 # biased variance 

170 

171 n = 2 

172 # original estimate; just a rewrite of the usual n/(n - 1) correction 

173 stddev = (1.0*(vsum2)/(wsum*(1.0-1.0/n)) - (vsum**2)/(wsum**2*(1.0-1.0/n)))**0.5 

174 self.assertAlmostEqual(stddev, np.sqrt(variance*n/(n - 1))) 

175 # 

176 # The correct formula: 

177 stddev = np.sqrt(variance*wsum**2/(wsum**2 - wwsum)) 

178 

179 # get the stats for the image with two values 

180 self.assertAlmostEqual(stats.getValue(afwMath.MEAN), mean, 10) 

181 self.assertAlmostEqual(stats.getValue(afwMath.STDEV), stddev, 10) 

182 

183 

184class TestMemory(lsst.utils.tests.MemoryTestCase): 

185 pass 

186 

187 

188def setup_module(module): 

189 lsst.utils.tests.init() 

190 

191 

192if __name__ == "__main__": 192 ↛ 193line 192 didn't jump to line 193, because the condition on line 192 was never true

193 lsst.utils.tests.init() 

194 unittest.main()