Coverage for tests/test_undeblended.py: 19%

110 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-17 02:19 -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 

22"""Tests for measuring sources on undeblended images. 

23""" 

24 

25import sys 

26import unittest 

27 

28import numpy as np 

29 

30import lsst.geom 

31import lsst.afw.image as afwImage 

32import lsst.afw.table as afwTable 

33import lsst.afw.geom as afwGeom 

34import lsst.afw.detection as afwDetection 

35import lsst.afw.math as afwMath 

36import lsst.meas.base as measBase 

37import lsst.utils.tests 

38 

39 

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

41 def testUndeblendedMeasurement(self): 

42 """Check undeblended measurement and aperture correction. 

43 """ 

44 width, height = 100, 100 # Dimensions of image 

45 x0, y0 = 1234, 5678 # Offset of image 

46 radius = 3.0 # Aperture radius 

47 

48 # Position of first source; integer values, for convenience 

49 xCenter, yCenter = width//2, height//2 

50 xOffset, yOffset = 1, 1 # Offset from first source to second source 

51 instFlux1, instFlux2 = 1000, 1 # Flux of sources 

52 apCorrValue = 3.21 # Aperture correction value to apply 

53 

54 image = afwImage.MaskedImageF(lsst.geom.ExtentI(width, height)) 

55 image.setXY0(x0, y0) 

56 image.getVariance().set(1.0) 

57 

58 schema = afwTable.SourceTable.makeMinimalSchema() 

59 schema.addField("centroid_x", type=np.float64) 

60 schema.addField("centroid_y", type=np.float64) 

61 schema.addField("centroid_flag", type='Flag') 

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

63 

64 sfmConfig = measBase.SingleFrameMeasurementConfig() 

65 algName = "base_CircularApertureFlux" 

66 

67 for subConfig in (sfmConfig.plugins, sfmConfig.undeblended): 

68 subConfig.names = [algName] 

69 subConfig[algName].radii = [radius] 

70 # Disable sinc photometry because we're undersampled 

71 subConfig[algName].maxSincRadius = 0 

72 slots = sfmConfig.slots 

73 slots.centroid = "centroid" 

74 slots.shape = None 

75 slots.psfShape = None 

76 slots.apFlux = None 

77 slots.modelFlux = None 

78 slots.psfFlux = None 

79 slots.gaussianFlux = None 

80 slots.calibFlux = None 

81 

82 fieldName = lsst.meas.base.CircularApertureFluxAlgorithm.makeFieldPrefix(algName, radius) 

83 measBase.addApCorrName(fieldName) 

84 

85 apCorrConfig = measBase.ApplyApCorrConfig() 

86 apCorrConfig.proxies = {"undeblended_" + fieldName: fieldName} 

87 

88 sfm = measBase.SingleFrameMeasurementTask(config=sfmConfig, schema=schema) 

89 apCorr = measBase.ApplyApCorrTask(config=apCorrConfig, schema=schema) 

90 

91 cat = afwTable.SourceCatalog(schema) 

92 parent = cat.addNew() 

93 parent.set("centroid_x", x0 + xCenter) 

94 parent.set("centroid_y", y0 + yCenter) 

95 spanSetParent = afwGeom.SpanSet.fromShape(int(radius)) 

96 spanSetParent = spanSetParent.shiftedBy(x0 + xCenter, y0 + yCenter) 

97 parent.setFootprint(afwDetection.Footprint(spanSetParent)) 

98 

99 # First child is bright, dominating the blend 

100 child1 = cat.addNew() 

101 child1.set("centroid_x", parent.get("centroid_x")) 

102 child1.set("centroid_y", parent.get("centroid_y")) 

103 child1.setParent(parent.getId()) 

104 image[xCenter, yCenter, afwImage.LOCAL] = (instFlux1, 0, 0) 

105 spanSetChild1 = afwGeom.SpanSet.fromShape(1) 

106 spanSetChild1 = spanSetChild1.shiftedBy(x0 + xCenter, y0 + yCenter) 

107 foot1 = afwDetection.Footprint(spanSetChild1) 

108 child1.setFootprint(afwDetection.HeavyFootprintF(foot1, image)) 

109 

110 # Second child is fainter, but we want to be able to measure it! 

111 child2 = cat.addNew() 

112 child2.set("centroid_x", parent.get("centroid_x") + xOffset) 

113 child2.set("centroid_y", parent.get("centroid_y") + yOffset) 

114 child2.setParent(parent.getId()) 

115 image[xCenter + xOffset, yCenter + yOffset, afwImage.LOCAL] = (instFlux2, 0, 0) 

116 spanSetChild2 = afwGeom.SpanSet.fromShape(1) 

117 tmpPoint = (x0 + xCenter + xOffset, y0 + yCenter + yOffset) 

118 spanSetChild2 = spanSetChild2.shiftedBy(*tmpPoint) 

119 foot2 = afwDetection.Footprint(spanSetChild2) 

120 child2.setFootprint(afwDetection.HeavyFootprintF(foot2, image)) 

121 

122 spans = foot1.spans.union(foot2.spans) 

123 bbox = lsst.geom.Box2I() 

124 bbox.include(foot1.getBBox()) 

125 bbox.include(foot2.getBBox()) 

126 parent.setFootprint(afwDetection.Footprint(spans, bbox)) 

127 

128 exposure = afwImage.makeExposure(image) 

129 

130 sfm.run(cat, exposure) 

131 

132 def checkSource(source, baseName, expectedFlux): 

133 """Check that we get the expected results. 

134 """ 

135 self.assertEqual(source.get(baseName + "_instFlux"), expectedFlux) 

136 self.assertGreater(source.get(baseName + "_instFluxErr"), 0) 

137 self.assertFalse(source.get(baseName + "_flag")) 

138 

139 # Deblended 

140 checkSource(child1, fieldName, instFlux1) 

141 checkSource(child2, fieldName, instFlux2) 

142 

143 # Undeblended 

144 checkSource(child1, "undeblended_" + fieldName, instFlux1 + instFlux2) 

145 checkSource(child2, "undeblended_" + fieldName, instFlux1 + instFlux2) 

146 

147 # Apply aperture correction 

148 apCorrMap = afwImage.ApCorrMap() 

149 apCorrMap[fieldName + "_instFlux"] = afwMath.ChebyshevBoundedField( 

150 image.getBBox(), 

151 apCorrValue*np.ones((1, 1), dtype=np.float64) 

152 ) 

153 apCorrMap[fieldName + "_instFluxErr"] = afwMath.ChebyshevBoundedField( 

154 image.getBBox(), 

155 apCorrValue*np.zeros((1, 1), dtype=np.float64) 

156 ) 

157 

158 apCorr.run(cat, apCorrMap) 

159 

160 # Deblended 

161 checkSource(child1, fieldName, instFlux1*apCorrValue) 

162 checkSource(child2, fieldName, instFlux2*apCorrValue) 

163 

164 # Undeblended 

165 checkSource(child1, "undeblended_" + fieldName, (instFlux1 + instFlux2)*apCorrValue) 

166 checkSource(child2, "undeblended_" + fieldName, (instFlux1 + instFlux2)*apCorrValue) 

167 

168 self.assertIn(fieldName + "_apCorr", schema) 

169 self.assertIn(fieldName + "_apCorrErr", schema) 

170 self.assertIn("undeblended_" + fieldName + "_apCorr", schema) 

171 self.assertIn("undeblended_" + fieldName + "_apCorrErr", schema) 

172 

173 

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

175 pass 

176 

177 

178def setup_module(module): 

179 lsst.utils.tests.init() 

180 

181 

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

183 setup_module(sys.modules[__name__]) 

184 unittest.main()