Coverage for tests/test_Variance.py: 18%

128 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 02:21 -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 

33 

34try: 

35 display 

36except NameError: 

37 display = False 

38 

39 

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

41 

42 def setUp(self): 

43 size = 128 # size of image (pixels) 

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

45 width = 2 # PSF width 

46 flux = 10.0 # Flux of object 

47 variance = 1.0 # Mean variance value 

48 varianceStd = 0.1 # Standard deviation of the variance value 

49 

50 # Set a seed for predictable randomness 

51 np.random.seed(300) 

52 

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

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

55 

56 # Initial setup of an image 

57 exp = afwImage.ExposureF(size, size) 

58 image = exp.getMaskedImage().getImage() 

59 mask = exp.getMaskedImage().getMask() 

60 var = exp.getMaskedImage().getVariance() 

61 image.set(0.0) 

62 mask.set(0) 

63 var.getArray()[:, :] = variancePlane 

64 

65 # Put down a PSF 

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

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

68 exp.setPsf(psf) 

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

70 psfImage *= flux 

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

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

73 

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

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

76 bad = size//2 + i*width 

77 var.getArray()[bad, :] = float("nan") 

78 mask.getArray()[bad, :] = mask.getPlaneBitMask("BAD") 

79 var.getArray()[:, bad] = float("nan") 

80 mask.getArray()[:, bad] = mask.getPlaneBitMask("BAD") 

81 

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

83 # ensure the aperture is working 

84 var.getArray()[0, 0] = float("nan") 

85 var.getArray()[0, -1] = float("nan") 

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

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

88 

89 if display: 

90 import lsst.afw.display as afwDisplay 

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

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

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

94 

95 config = measBase.SingleFrameMeasurementConfig() 

96 config.plugins.names = ["base_NaiveCentroid", "base_SdssShape", "base_Variance"] 

97 config.slots.centroid = "base_NaiveCentroid" 

98 config.slots.psfFlux = None 

99 config.slots.apFlux = None 

100 config.slots.modelFlux = None 

101 config.slots.gaussianFlux = None 

102 config.slots.calibFlux = None 

103 config.slots.shape = "base_SdssShape" 

104 config.slots.psfShape = None 

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

106 

107 config.validate() 

108 schema = afwTable.SourceTable.makeMinimalSchema() 

109 

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

111 catalog = afwTable.SourceCatalog(schema) 

112 

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

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

115 foot = afwDetection.Footprint(spans) 

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

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

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

119 peak.setFx(center.getX()) 

120 peak.setFy(center.getY()) 

121 peak.setPeakValue(flux) 

122 

123 source = catalog.addNew() 

124 source.setFootprint(foot) 

125 

126 self.variance = variance 

127 self.varianceStd = varianceStd 

128 self.mask = mask 

129 self.catalog = catalog 

130 self.exp = exp 

131 self.task = task 

132 self.source = source 

133 

134 def tearDown(self): 

135 del self.mask 

136 del self.catalog 

137 del self.exp 

138 del self.task 

139 del self.source 

140 

141 def testVariance(self): 

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

143 

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

145 

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

147 # non-masked pixels at this point. 

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

149 

150 def testEmptyFootprint(self): 

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

152 self.mask.getArray()[:, :] = self.mask.getPlaneBitMask("BAD") 

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

154 

155 # The computed variance should be NaN and flag_emptyFootprint should 

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

157 # point. 

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

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

160 

161 

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

163 

164 def testBadCentroid(self): 

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

166 

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

168 set on the variance measurement. 

169 """ 

170 schema = afwTable.SourceTable.makeMinimalSchema() 

171 measBase.SingleFramePeakCentroidPlugin(measBase.SingleFramePeakCentroidConfig(), 

172 "centroid", schema, None) 

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

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

175 "variance", schema, None) 

176 catalog = afwTable.SourceCatalog(schema) 

177 

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

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

180 # should throw a logic error. 

181 record = catalog.addNew() 

182 record.set("centroid_flag", False) 

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

184 variance.measure(record, None) 

185 variance.fail(record, measErr.exception) 

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

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

188 

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

190 # indicating an expected failure. 

191 record = catalog.addNew() 

192 record.set("centroid_flag", True) 

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

194 variance.measure(record, None) 

195 variance.fail(record, measErr.exception) 

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

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

198 

199 

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

201 pass 

202 

203 

204def setup_module(module): 

205 lsst.utils.tests.init() 

206 

207 

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

209 lsst.utils.tests.init() 

210 unittest.main()