Coverage for tests/test_Variance.py: 19%

129 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-03 03:00 -0700

1# This file is part of meas_base. 

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 

22import unittest 

23 

24import numpy as np 

25 

26import lsst.geom 

27import lsst.afw.geom as afwGeom 

28import lsst.afw.table as afwTable 

29import lsst.afw.image as afwImage 

30import lsst.afw.detection as afwDetection 

31import lsst.meas.base as measBase 

32import lsst.utils.tests 

33import testLib # noqa: F401 need this for SillyCentroid 

34 

35try: 

36 display 

37except NameError: 

38 display = False 

39 

40 

41class VarianceTest(lsst.utils.tests.TestCase): 

42 

43 def setUp(self): 

44 size = 128 # size of image (pixels) 

45 center = lsst.geom.Point2D(size//2, size//2) # object center 

46 width = 2 # PSF width 

47 flux = 10.0 # Flux of object 

48 variance = 1.0 # Mean variance value 

49 varianceStd = 0.1 # Standard deviation of the variance value 

50 

51 # Set a seed for predictable randomness 

52 np.random.seed(300) 

53 

54 # Create a random image to be used as variance plane 

55 variancePlane = np.random.normal(variance, varianceStd, size*size).reshape(size, size) 

56 

57 # Initial setup of an image 

58 exp = afwImage.ExposureF(size, size) 

59 image = exp.image 

60 mask = exp.mask 

61 var = exp.variance 

62 image.set(0.0) 

63 mask.set(0) 

64 var.array[:, :] = variancePlane 

65 

66 # Put down a PSF 

67 psfSize = int(6*width + 1) # Size of PSF image; must be odd 

68 psf = afwDetection.GaussianPsf(psfSize, psfSize, width) 

69 exp.setPsf(psf) 

70 psfImage = psf.computeImage(center).convertF() 

71 psfImage *= flux 

72 image.Factory(image, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage) 

73 var.Factory(var, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage) 

74 

75 # Put in some bad pixels to ensure they're ignored 

76 for i in range(-5, 6): 

77 bad = size//2 + i*width 

78 var.array[bad, :] = float("nan") 

79 mask.array[bad, :] = mask.getPlaneBitMask("BAD") 

80 var.array[:, bad] = float("nan") 

81 mask.array[:, bad] = mask.getPlaneBitMask("BAD") 

82 

83 # Put in some unmasked bad pixels outside the expected aperture, to 

84 # ensure the aperture is working 

85 var.array[0, 0] = float("nan") 

86 var.array[0, -1] = float("nan") 

87 var.array[-1, 0] = float("nan") 

88 var.array[-1, -1] = float("nan") 

89 

90 if display: 

91 import lsst.afw.display as afwDisplay 

92 afwDisplay.getDisplay(1).mtv(image) 

93 afwDisplay.getDisplay(2).mtv(mask) 

94 afwDisplay.getDisplay(3).mtv(var) 

95 

96 config = measBase.SingleFrameMeasurementConfig() 

97 config.plugins.names = ["testLib_SillyCentroid", "base_SdssShape", "base_Variance"] 

98 config.slots.centroid = "testLib_SillyCentroid" 

99 config.slots.psfFlux = None 

100 config.slots.apFlux = None 

101 config.slots.modelFlux = None 

102 config.slots.gaussianFlux = None 

103 config.slots.calibFlux = None 

104 config.slots.shape = "base_SdssShape" 

105 config.slots.psfShape = None 

106 config.plugins["base_Variance"].mask = ["BAD", "SAT"] 

107 

108 config.validate() 

109 schema = afwTable.SourceTable.makeMinimalSchema() 

110 

111 task = measBase.SingleFrameMeasurementTask(schema, config=config) 

112 catalog = afwTable.SourceCatalog(schema) 

113 

114 spans = afwGeom.SpanSet.fromShape(int(width)) 

115 spans = spans.shiftedBy(int(center.getX()), int(center.getY())) 

116 foot = afwDetection.Footprint(spans) 

117 peak = foot.getPeaks().addNew() 

118 peak.setIx(int(center.getX())) 

119 peak.setIy(int(center.getY())) 

120 peak.setFx(center.getX()) 

121 peak.setFy(center.getY()) 

122 peak.setPeakValue(flux) 

123 

124 source = catalog.addNew() 

125 source.setFootprint(foot) 

126 

127 self.variance = variance 

128 self.varianceStd = varianceStd 

129 self.mask = mask 

130 self.catalog = catalog 

131 self.exp = exp 

132 self.task = task 

133 self.source = source 

134 

135 def tearDown(self): 

136 del self.mask 

137 del self.catalog 

138 del self.exp 

139 del self.task 

140 del self.source 

141 

142 def testVariance(self): 

143 self.task.run(self.catalog, self.exp) 

144 

145 self.assertLess(np.abs(self.source.get("base_Variance_value") - self.variance), self.varianceStd) 

146 

147 # flag_emptyFootprint should not have been set since the footprint has 

148 # non-masked pixels at this point. 

149 self.assertFalse(self.source.get("base_Variance_flag_emptyFootprint")) 

150 

151 def testEmptyFootprint(self): 

152 # Set the pixel mask for all pixels to ``BAD`` and remeasure. 

153 self.mask.array[:, :] = self.mask.getPlaneBitMask("BAD") 

154 self.task.run(self.catalog, self.exp) 

155 

156 # The computed variance should be NaN and flag_emptyFootprint should 

157 # have been set since the footprint has all masked pixels at this 

158 # point. 

159 self.assertTrue(np.isnan(self.source.get("base_Variance_value"))) 

160 self.assertTrue(self.source.get("base_Variance_flag_emptyFootprint")) 

161 

162 

163class BadCentroidTest(lsst.utils.tests.TestCase): 

164 

165 def testBadCentroid(self): 

166 """Test propagation of flags to ``badCentroid``. 

167 

168 If the centroid is flagged as bad, the ``badCentroid`` flag should be 

169 set on the variance measurement. 

170 """ 

171 schema = afwTable.SourceTable.makeMinimalSchema() 

172 measBase.SingleFramePeakCentroidPlugin(measBase.SingleFramePeakCentroidConfig(), 

173 "centroid", schema, None) 

174 schema.getAliasMap().set("slot_Centroid", "centroid") 

175 variance = measBase.SingleFrameVariancePlugin(measBase.VarianceConfig(), 

176 "variance", schema, None) 

177 catalog = afwTable.SourceCatalog(schema) 

178 

179 # The centroid is not flagged as bad, but there's no way the algorithm 

180 # can run without valid data in the SourceRecord and Exposure: this 

181 # should throw a logic error. 

182 record = catalog.addNew() 

183 record.set("centroid_flag", False) 

184 with self.assertRaises(measBase.MeasurementError) as measErr: 

185 variance.measure(record, None) 

186 variance.fail(record, measErr.exception) 

187 self.assertTrue(record.get("variance_flag")) 

188 self.assertFalse(record.get("variance_flag_badCentroid")) 

189 

190 # The centroid is flagged as bad, so we should get a MeasurementError 

191 # indicating an expected failure. 

192 record = catalog.addNew() 

193 record.set("centroid_flag", True) 

194 with self.assertRaises(measBase.MeasurementError) as measErr: 

195 variance.measure(record, None) 

196 variance.fail(record, measErr.exception) 

197 self.assertTrue(record.get("variance_flag")) 

198 self.assertTrue(record.get("variance_flag_badCentroid")) 

199 

200 

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

202 pass 

203 

204 

205def setup_module(module): 

206 lsst.utils.tests.init() 

207 

208 

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

210 lsst.utils.tests.init() 

211 unittest.main()