Coverage for tests/test_defects.py: 16%

126 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-18 12:25 +0000

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 os 

23import unittest 

24 

25import lsst.geom 

26import lsst.afw.image as afwImage 

27import lsst.meas.algorithms as algorithms 

28import lsst.utils.tests 

29from lsst.daf.base import PropertyList 

30from lsst.ip.isr import Defects 

31 

32try: 

33 type(display) 

34except NameError: 

35 display = False 

36else: 

37 import lsst.afw.display as afwDisplay 

38 afwDisplay.setDefaultMaskTransparency(75) 

39 

40# Determine if we have afwdata 

41try: 

42 afwdataDir = lsst.utils.getPackageDir('afwdata') 

43except Exception: 

44 afwdataDir = None 

45 

46TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

47 

48 

49class DefectsTestCase(lsst.utils.tests.TestCase): 

50 """Tests for collections of Defect.""" 

51 

52 def assertMetadata(self, first, second): 

53 """Compare the metadata associated with Defects""" 

54 

55 # Must strip out DATE metadata before comparison 

56 meta1 = first.getMetadata() 

57 meta2 = second.getMetadata() 

58 for d in (meta1, meta2): 

59 for k in ("DATE", "CALIB_CREATION_DATE", "CALIB_CREATION_TIME"): 

60 if k in d: 

61 del d[k] 

62 

63 self.assertEqual(meta1, meta2) 

64 meta1["NEW"] = "additional header" 

65 self.assertNotEqual(first.getMetadata(), second.getMetadata()) 

66 del meta1["NEW"] 

67 

68 def test_defects(self): 

69 defects = Defects() 

70 

71 defects.append(algorithms.Defect(lsst.geom.Box2I(lsst.geom.Point2I(5, 6), 

72 lsst.geom.Point2I(41, 50)))) 

73 

74 defects.append(lsst.geom.Box2I(lsst.geom.Point2I(0, 0), 

75 lsst.geom.Point2I(4, 5))) 

76 defects.append(lsst.geom.Point2I(50, 50)) 

77 defects.append(afwImage.DefectBase(lsst.geom.Box2I(lsst.geom.Point2I(100, 200), 

78 lsst.geom.Extent2I(5, 5)))) 

79 self.assertEqual(len(defects), 4) 

80 

81 for d in defects: 

82 self.assertIsInstance(d, algorithms.Defect) 

83 

84 # Transposition 

85 transposed = defects.transpose() 

86 self.assertEqual(len(transposed), len(defects)) 

87 

88 # Check that an individual defect is found properly transposed within 

89 # the outputs. 

90 found = False 

91 for defect in transposed: 

92 if defect.getBBox() == lsst.geom.Box2I(lsst.geom.Point2I(6, 5), lsst.geom.Extent2I(45, 37)): 

93 found = True 

94 break 

95 self.assertTrue(found) 

96 

97 # Serialization round trip 

98 meta = PropertyList() 

99 meta["TESTHDR"] = "testing" 

100 defects.setMetadata(meta) 

101 

102 table = defects.toFitsRegionTable() 

103 

104 defects2 = Defects.fromTable([table]) 

105 

106 self.assertEqual(defects2, defects) 

107 

108 # via FITS 

109 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

110 defects.writeFits(tmpFile) 

111 defects2 = Defects.readFits(tmpFile) 

112 

113 # Equality tests the bounding boxes so metadata is tested separately. 

114 self.assertEqual(defects2, defects) 

115 self.assertMetadata(defects2, defects) 

116 

117 # via text file 

118 with lsst.utils.tests.getTempFilePath(".ecsv") as tmpFile: 

119 defects.writeText(tmpFile) 

120 defects2 = Defects.readText(tmpFile) 

121 

122 # Equality tests the bounding boxes so metadata is tested separately. 

123 self.assertEqual(defects2, defects) 

124 self.assertMetadata(defects2, defects) 

125 

126 # Check bad values 

127 with self.assertRaises(ValueError): 

128 defects.append(lsst.geom.Box2D(lsst.geom.Point2D(0., 0.), 

129 lsst.geom.Point2D(3.1, 3.1))) 

130 with self.assertRaises(ValueError): 

131 defects.append("defect") 

132 

133 def testAstropyRegion(self): 

134 """Read a FITS region file created by Astropy regions.""" 

135 # The file contains three regions: 

136 # 

137 # - Point2I(340, 344) 

138 # - Point2I(340, 344) 

139 # - Box2I(minimum=Point2I(5, -5), dimensions=Extent2I(10, 20)) 

140 # 

141 # The two coincident points are combined on read, so we end up with two 

142 # defects. 

143 

144 with self.assertLogs(): 

145 defects = Defects.readFits(os.path.join(TESTDIR, "data", "fits_region.fits"), 

146 normalize_on_init=True) 

147 

148 self.assertEqual(len(defects), 2) 

149 

150 def testLsstTextfile(self): 

151 """Read legacy LSST text file format""" 

152 with lsst.utils.tests.getTempFilePath(".txt") as tmpFile: 

153 with open(tmpFile, "w") as fh: 

154 print("""# X0 Y0 width height 

155 996 0 56 24 

156 0 4156 2048 20 

157 0 0 17 4176 

158 1998 4035 50 141 

159 1023 0 2 4176 

160 2027 0 21 4176 

161 0 4047 37 129 

162# Some rows without fixed column widths 

16314 20 2000 50 

16410 10 10 10 

165""", file=fh) 

166 

167 defects = Defects.readLsstDefectsFile(tmpFile, normalize_on_init=True) 

168 

169 # Although there are 9 defects listed above, we record 11 after 

170 # normalization. This is due to non-optimal behaviour in 

171 # Defects.fromMask; see DM-24781. 

172 self.assertEqual(len(defects), 11) 

173 

174 def test_normalize_defects(self): 

175 """A test for the lsst.meas.algorithms.Defect.normalize() method. 

176 """ 

177 defects = Defects() 

178 

179 # First series of 1-pixel contiguous defects 

180 for yPix in range(1, 6): 

181 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(15, yPix), 

182 dimensions=lsst.geom.Extent2I(1, 1))) 

183 

184 # Defects are normalized as they are added; check that the above have 

185 # been merged into a single bounding box. 

186 self.assertEqual(len(defects), 1) 

187 

188 # Second series of 1-pixel contiguous defects in bulk mode 

189 with defects.bulk_update(): 

190 for yPix in range(11, 16): 

191 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(20, yPix), 

192 dimensions=lsst.geom.Extent2I(1, 1))) 

193 # In bulk mode, defects are not normalized. 

194 self.assertEqual(len(defects), 6) 

195 

196 # Normalization applied on exiting bulk mode. 

197 self.assertEqual(len(defects), 2) 

198 

199 boxesMeasured = [] 

200 for defect in defects: 

201 boxesMeasured.append(defect.getBBox()) 

202 

203 # The normalizing function should have created the following two boxes 

204 # out of the individual 1-pixel defects from above 

205 expectedDefects = [lsst.geom.Box2I(corner=lsst.geom.Point2I(15, 1), 

206 dimensions=lsst.geom.Extent2I(1, 5)), 

207 lsst.geom.Box2I(corner=lsst.geom.Point2I(20, 11), 

208 dimensions=lsst.geom.Extent2I(1, 5))] 

209 

210 self.assertEqual(len(expectedDefects), len(boxesMeasured)) 

211 for expDef, measDef in zip(expectedDefects, boxesMeasured): 

212 self.assertEqual(expDef, measDef) 

213 

214 # Normalize two distinct sets of Defects and ensure they compare to the 

215 # same thing. 

216 defects = Defects() 

217 # Set 1 

218 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 1), dimensions=lsst.geom.Extent2I(1, 1))) 

219 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 2), dimensions=lsst.geom.Extent2I(1, 1))) 

220 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 3), dimensions=lsst.geom.Extent2I(1, 1))) 

221 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 4), dimensions=lsst.geom.Extent2I(1, 1))) 

222 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 5), dimensions=lsst.geom.Extent2I(1, 1))) 

223 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 6), dimensions=lsst.geom.Extent2I(1, 1))) 

224 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 7), dimensions=lsst.geom.Extent2I(1, 1))) 

225 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 8), dimensions=lsst.geom.Extent2I(1, 1))) 

226 

227 # Set 2 

228 defects2 = Defects() 

229 defects2.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 1), dimensions=lsst.geom.Extent2I(1, 5))) 

230 defects2.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 5), dimensions=lsst.geom.Extent2I(1, 4))) 

231 

232 self.assertEqual(defects, defects2) 

233 

234 boxesMeasured, boxesMeasured2 = [], [] 

235 for defect, defect2 in zip(defects, defects2): 

236 boxesMeasured.append(defect.getBBox()) 

237 boxesMeasured2.append(defect2.getBBox()) 

238 

239 expectedDefects = [lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 1), 

240 dimensions=lsst.geom.Extent2I(1, 8))] 

241 

242 self.assertEqual(len(expectedDefects), len(boxesMeasured)) 

243 for expDef, measDef in zip(expectedDefects, boxesMeasured): 

244 self.assertEqual(expDef, measDef) 

245 

246 self.assertEqual(len(expectedDefects), len(boxesMeasured2)) 

247 for expDef, measDef in zip(expectedDefects, boxesMeasured2): 

248 self.assertEqual(expDef, measDef) 

249 

250 

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

252 pass 

253 

254 

255def setup_module(module): 

256 lsst.utils.tests.init() 

257 

258 

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

260 lsst.utils.tests.init() 

261 unittest.main()