Coverage for tests/test_negative.py: 17%

109 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-06 04:25 -0700

1# This file is part of meas_algorithms. 

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 lsst.geom 

25import lsst.afw.math as afwMath 

26import lsst.afw.table as afwTable 

27import lsst.daf.base as dafBase 

28from lsst.meas.algorithms import SourceDetectionTask 

29from lsst.meas.base import SingleFrameMeasurementTask as SourceMeasurementTask 

30from lsst.meas.algorithms.testUtils import plantSources 

31import lsst.utils.tests 

32 

33try: 

34 display 

35except NameError: 

36 display = False 

37else: 

38 import lsst.afw.display as afwDisplay 

39 afwDisplay.setDefaultMaskTransparency(75) 

40 

41 

42class NegativeMeasurementTestCase(lsst.utils.tests.TestCase): 

43 """Testing detection and measurement on negative objects. 

44 """ 

45 

46 def _create_exposure(self): 

47 bbox = lsst.geom.Box2I(lsst.geom.Point2I(256, 100), lsst.geom.Extent2I(128, 127)) 

48 minCounts = 2000 

49 maxCounts = 20000 

50 starSigma = 1.5 

51 numX = 4 

52 numY = 4 

53 coordList = self.makeCoordList( 

54 bbox=bbox, 

55 numX=numX, 

56 numY=numY, 

57 minCounts=minCounts, 

58 maxCounts=maxCounts, 

59 sigma=starSigma, 

60 ) 

61 kwid = 11 

62 sky = 2000 

63 addPoissonNoise = True 

64 exposure = plantSources(bbox=bbox, kwid=kwid, sky=sky, coordList=coordList, 

65 addPoissonNoise=addPoissonNoise) 

66 return exposure, numX, numY 

67 

68 def test_detection_stdev(self): 

69 """Test detection and measurement on an exposure with negative sources 

70 for thresholdType="stdev". 

71 """ 

72 exposure, numX, numY = self._create_exposure() 

73 

74 if display: 

75 disp = afwDisplay.Display(frame=1) 

76 disp.mtv(exposure, title=self._testMethodName + ": image with -ve sources") 

77 

78 schema = afwTable.SourceTable.makeMinimalSchema() 

79 config = SourceDetectionTask.ConfigClass() 

80 config.reEstimateBackground = False 

81 config.thresholdType = "stdev" 

82 config.thresholdPolarity = 'both' 

83 detection = SourceDetectionTask(config=config, schema=schema) 

84 algMetadata = dafBase.PropertyList() 

85 measurement = SourceMeasurementTask(schema=schema, algMetadata=algMetadata) 

86 

87 table = afwTable.SourceTable.make(schema) 

88 detections = detection.run(table, exposure) 

89 sources = detections.sources 

90 

91 self.assertEqual(len(sources), numX*numY) 

92 self.assertEqual(detections.numPos, numX*numY/2) 

93 self.assertEqual(detections.numNeg, numX*numY/2) 

94 

95 measurement.run(sources, exposure) 

96 

97 nGoodCent = 0 

98 nGoodShape = 0 

99 for s in sources: 

100 cent = s.getCentroid() 

101 shape = s.getShape() 

102 

103 if cent[0] == cent[0] and cent[1] == cent[1]: 

104 nGoodCent += 1 

105 

106 if (shape.getIxx() == shape.getIxx() 

107 and shape.getIyy() == shape.getIyy() 

108 and shape.getIxy() == shape.getIxy()): 

109 nGoodShape += 1 

110 

111 if display: 

112 xy = cent[0], cent[1] 

113 disp.dot('+', *xy) 

114 disp.dot(shape, *xy, ctype=afwDisplay.RED) 

115 

116 self.assertEqual(nGoodCent, numX*numY) 

117 self.assertEqual(nGoodShape, numX*numY) 

118 

119 def test_significance(self): 

120 """Test that negative peaks have the right significance for 

121 thresholdType='stdev' for the non-convolved, non-local-background case. 

122 """ 

123 exposure, numX, numY = self._create_exposure() 

124 

125 schema = afwTable.SourceTable.makeMinimalSchema() 

126 config = SourceDetectionTask.ConfigClass() 

127 config.thresholdType = "stdev" 

128 config.thresholdPolarity = 'both' 

129 # don't modify the image after detection. 

130 config.reEstimateBackground = False 

131 config.doTempLocalBackground = False 

132 detection = SourceDetectionTask(config=config, schema=schema) 

133 result = detection.detectFootprints(exposure, doSmooth=False) 

134 

135 bad = exposure.mask.getPlaneBitMask(config.statsMask) 

136 sctrl = afwMath.StatisticsControl() 

137 sctrl.setAndMask(bad) 

138 stats = afwMath.makeStatistics(exposure.maskedImage, afwMath.STDEVCLIP, sctrl) 

139 stddev = stats.getValue(afwMath.STDEVCLIP) 

140 # Don't bother checking positive footprints: those are tested more 

141 # thoroughly in test_detection.py. 

142 for footprint in result.negative.getFootprints(): 

143 for peak in footprint.peaks: 

144 point = lsst.geom.Point2I(peak.getIx(), peak.getIy()) 

145 value = exposure.image[point] 

146 with self.subTest(str(point)): 

147 self.assertFloatsAlmostEqual(peak["significance"], 

148 -value/stddev, # S/N for negative peak 

149 rtol=1e-7, 

150 msg=str(point)) 

151 

152 def makeCoordList(self, bbox, numX, numY, minCounts, maxCounts, sigma): 

153 """Make a coordList for makeExposure. 

154 """ 

155 dX = bbox.getWidth()/float(numX) 

156 dY = bbox.getHeight()/float(numY) 

157 minX = bbox.getMinX() + (dX/2.0) 

158 minY = bbox.getMinY() + (dY/2.0) 

159 dCounts = (maxCounts - minCounts)/(numX*numY/2 - 1) 

160 

161 coordList = [] 

162 counts = minCounts 

163 for i in range(numX): 

164 x = minX + (dX*i) 

165 for j in range(numY): 

166 y = minY + (dY*j) 

167 if j%2 == 0: 

168 coordList.append([x, y, counts, sigma]) 

169 else: 

170 coordList.append([x, y, -counts, sigma]) 

171 counts += dCounts 

172 return coordList 

173 

174 

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

176 pass 

177 

178 

179def setup_module(module): 

180 lsst.utils.tests.init() 

181 

182 

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

184 lsst.utils.tests.init() 

185 unittest.main()