Coverage for tests/test_1079.py: 17%

137 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-13 02:49 -0700

1# This file is part of afw. 

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# test1079 

23# @brief Test that the wcs of sub-images are written and read from disk correctly 

24# $Id$ 

25# @author Fergal Mullally 

26 

27import os.path 

28import unittest 

29import numbers 

30 

31import lsst.utils 

32import lsst.geom 

33import lsst.afw.image as afwImage 

34from lsst.afw.fits import readMetadata 

35import lsst.utils.tests 

36import lsst.pex.exceptions 

37import lsst.afw.display as afwDisplay 

38 

39try: 

40 type(display) 

41except NameError: 

42 display = False 

43 

44 

45class SavingSubImagesTest(unittest.TestCase): 

46 """Tests for changes made for ticket #1079. 

47 

48 In the LSST wcs transformations are done in terms of pixel position, which 

49 is measured from the lower left hand corner of the parent image from which 

50 this sub-image is drawn. However, when saving a sub-image to disk, the fits 

51 standards has no concept of parent- and sub- images, and specifies that the 

52 wcs is measured relative to the pixel index (i.e. the lower left hand corner 

53 of the sub-image). This test makes sure we're saving and reading wcs headers 

54 from sub-images correctly. 

55 """ 

56 

57 def setUp(self): 

58 path = lsst.utils.getPackageDir("afw") 

59 self.parentFile = os.path.join(path, "tests", "data", "parent.fits") 

60 

61 self.parent = afwImage.ExposureF(self.parentFile) 

62 self.llcParent = self.parent.getMaskedImage().getXY0() 

63 self.oParent = self.parent.getWcs().getPixelOrigin() 

64 

65 # A list of pixel positions to test 

66 self.testPositions = [] 

67 self.testPositions.append(lsst.geom.Point2D(128, 128)) 

68 self.testPositions.append(lsst.geom.Point2D(0, 0)) 

69 self.testPositions.append(lsst.geom.Point2D(20, 30)) 

70 self.testPositions.append(lsst.geom.Point2D(60, 50)) 

71 self.testPositions.append(lsst.geom.Point2D(80, 80)) 

72 self.testPositions.append(lsst.geom.Point2D(255, 255)) 

73 

74 self.parent.getMaskedImage().set(0) 

75 for p in self.testPositions: 

76 self.parent.image[lsst.geom.Point2I(p), afwImage.LOCAL] = 10 + p[0] 

77 

78 def tearDown(self): 

79 del self.parent 

80 del self.oParent 

81 del self.testPositions 

82 

83 def testInvarianceOfCrpix1(self): 

84 """Test that crpix is the same for parent and sub-image. 

85 

86 Also tests that llc of sub-image saved correctly. 

87 """ 

88 llc = lsst.geom.Point2I(20, 30) 

89 bbox = lsst.geom.Box2I(llc, lsst.geom.Extent2I(60, 50)) 

90 subImg = afwImage.ExposureF(self.parent, bbox, afwImage.LOCAL) 

91 

92 subImgLlc = subImg.getMaskedImage().getXY0() 

93 oSubImage = subImg.getWcs().getPixelOrigin() 

94 

95 # Useful for debugging 

96 if False: 

97 print(self.parent.getMaskedImage().getXY0()) 

98 print(subImg.getMaskedImage().getXY0()) 

99 print(self.oParent, oSubImage) 

100 

101 for i in range(2): 

102 self.assertEqual(llc[i], subImgLlc[i], "Corner of sub-image not correct") 

103 self.assertAlmostEqual( 

104 self.oParent[i], oSubImage[i], 6, "Crpix of sub-image not correct") 

105 

106 def testInvarianceOfCrpix2(self): 

107 """For sub-images loaded from disk, test that crpix is the same for parent and sub-image. 

108 

109 Also tests that llc of sub-image saved correctly. 

110 """ 

111 # Load sub-image directly off of disk 

112 llc = lsst.geom.Point2I(20, 30) 

113 bbox = lsst.geom.Box2I(llc, lsst.geom.Extent2I(60, 50)) 

114 subImg = afwImage.ExposureF(self.parentFile, bbox, afwImage.LOCAL) 

115 oSubImage = subImg.getWcs().getPixelOrigin() 

116 subImgLlc = subImg.getMaskedImage().getXY0() 

117 

118 # Useful for debugging 

119 if False: 

120 print(self.parent.getMaskedImage().getXY0()) 

121 print(subImg.getMaskedImage().getXY0()) 

122 print(self.oParent, oSubImage) 

123 

124 for i in range(2): 

125 self.assertEqual(llc[i], subImgLlc[i], "Corner of sub-image not correct") 

126 self.assertAlmostEqual( 

127 self.oParent[i], oSubImage[i], 6, "Crpix of sub-image not correct") 

128 

129 def testInvarianceOfPixelToSky(self): 

130 for deep in (True, False): 

131 llc = lsst.geom.Point2I(20, 30) 

132 bbox = lsst.geom.Box2I(llc, lsst.geom.Extent2I(60, 50)) 

133 subImg = afwImage.ExposureF(self.parent, bbox, afwImage.LOCAL, deep) 

134 

135 xy0 = subImg.getMaskedImage().getXY0() 

136 

137 if display: 

138 afwDisplay.Display(frame=0).mtv(self.parent, title=self._testMethodName + ": parent") 

139 afwDisplay.Display(frame=1).mtv(subImg, title=self._testMethodName + ": subImg") 

140 

141 for p in self.testPositions: 

142 subP = p - lsst.geom.Extent2D(llc[0], llc[1]) # pixel in subImg 

143 

144 if subP[0] < 0 or subP[0] >= bbox.getWidth() or subP[1] < 0 or subP[1] >= bbox.getHeight(): 

145 continue 

146 

147 adParent = self.parent.getWcs().pixelToSky(p) 

148 adSub = subImg.getWcs().pixelToSky(subP + lsst.geom.Extent2D(xy0[0], xy0[1])) 

149 # 

150 # Check that we're talking about the same pixel 

151 # 

152 self.assertEqual(self.parent.maskedImage[lsst.geom.Point2I(p), afwImage.LOCAL], 

153 subImg.maskedImage[lsst.geom.Point2I(subP), afwImage.LOCAL]) 

154 

155 self.assertEqual(adParent[0], adSub[0], f"RAs are equal; deep = {deep}") 

156 self.assertEqual(adParent[1], adSub[1], f"DECs are equal; deep = {deep}") 

157 

158 def testSubSubImage(self): 

159 """Check that a sub-image of a sub-image is equivalent to a sub image. 

160 

161 I.e. that the parent is an invarient. 

162 """ 

163 

164 llc1 = lsst.geom.Point2I(20, 30) 

165 bbox = lsst.geom.Box2I(llc1, lsst.geom.Extent2I(60, 50)) 

166 subImg = afwImage.ExposureF(self.parentFile, bbox, afwImage.LOCAL) 

167 

168 llc2 = lsst.geom.Point2I(22, 23) 

169 

170 # This subsub image should fail. Although it's big enough to fit in the parent image 

171 # it's too small for the sub-image 

172 bbox = lsst.geom.Box2I(llc2, lsst.geom.Extent2I(100, 110)) 

173 self.assertRaises(lsst.pex.exceptions.Exception, 

174 afwImage.ExposureF, subImg, bbox, afwImage.LOCAL) 

175 

176 bbox = lsst.geom.Box2I(llc2, lsst.geom.Extent2I(10, 11)) 

177 subSubImg = afwImage.ExposureF(subImg, bbox, afwImage.LOCAL) 

178 

179 sub0 = subImg.getMaskedImage().getXY0() 

180 subsub0 = subSubImg.getMaskedImage().getXY0() 

181 

182 if False: 

183 print(sub0) 

184 print(subsub0) 

185 

186 for i in range(2): 

187 self.assertEqual(llc1[i], sub0[i], "XY0 don't match (1)") 

188 self.assertEqual(llc1[i] + llc2[i], subsub0[i], "XY0 don't match (2)") 

189 

190 subCrpix = subImg.getWcs().getPixelOrigin() 

191 subsubCrpix = subSubImg.getWcs().getPixelOrigin() 

192 

193 for i in range(2): 

194 self.assertAlmostEqual( 

195 subCrpix[i], subsubCrpix[i], 6, "crpix don't match") 

196 

197 def testRoundTrip(self): 

198 """Test that saving and retrieving an image doesn't alter the metadata. 

199 """ 

200 llc = lsst.geom.Point2I(20, 30) 

201 bbox = lsst.geom.Box2I(llc, lsst.geom.Extent2I(60, 50)) 

202 for deep in (False, True): 

203 subImg = afwImage.ExposureF(self.parent, bbox, afwImage.LOCAL, deep) 

204 

205 with lsst.utils.tests.getTempFilePath(f"_{deep}.fits") as outFile: 

206 subImg.writeFits(outFile) 

207 newImg = afwImage.ExposureF(outFile) 

208 

209 subXY0 = subImg.getMaskedImage().getXY0() 

210 newXY0 = newImg.getMaskedImage().getXY0() 

211 

212 self.parent.getWcs().getPixelOrigin() 

213 subCrpix = subImg.getWcs().getPixelOrigin() 

214 newCrpix = newImg.getWcs().getPixelOrigin() 

215 

216 for i in range(2): 

217 self.assertEqual( 

218 subXY0[i], newXY0[i], f"Origin has changed; deep = {deep}") 

219 self.assertAlmostEqual( 

220 subCrpix[i], newCrpix[i], 6, f"crpix has changed; deep = {deep}") 

221 

222 def testFitsHeader(self): 

223 """Test that XY0 and crpix are written to the header as expected. 

224 """ 

225 # getPixelOrigin() returns origin in lsst coordinates, so need to add 1 to 

226 # compare to values stored in fits headers 

227 parentCrpix = self.parent.getWcs().getPixelOrigin() 

228 

229 # Make a sub-image 

230 x0, y0 = 20, 30 

231 llc = lsst.geom.Point2I(x0, y0) 

232 bbox = lsst.geom.Box2I(llc, lsst.geom.Extent2I(60, 50)) 

233 deep = False 

234 subImg = afwImage.ExposureF(self.parent, bbox, afwImage.LOCAL, deep) 

235 

236 with lsst.utils.tests.getTempFilePath(".fits") as outFile: 

237 subImg.writeFits(outFile) 

238 hdr = readMetadata(outFile) 

239 

240 def checkLtvHeader(hdr, name, value): 

241 # Per DM-4133, LTVn headers are required to be floating point 

242 self.assertTrue(hdr.exists(name), name + " not saved to FITS header") 

243 self.assertIsInstance( 

244 hdr.getScalar(name), numbers.Real, name + " is not numeric") 

245 self.assertNotIsInstance( 

246 hdr.getScalar(name), numbers.Integral, name + " is an int") 

247 self.assertEqual(hdr.getScalar(name), value, name + " has wrong value") 

248 

249 checkLtvHeader(hdr, "LTV1", -1*x0) 

250 checkLtvHeader(hdr, "LTV2", -1*y0) 

251 

252 self.assertTrue(hdr.exists("CRPIX1"), "CRPIX1 not saved to fits header") 

253 self.assertTrue(hdr.exists("CRPIX2"), "CRPIX2 not saved to fits header") 

254 

255 fitsCrpix = [hdr.getScalar("CRPIX1"), hdr.getScalar("CRPIX2")] 

256 self.assertAlmostEqual( 

257 fitsCrpix[0] - hdr.getScalar("LTV1"), parentCrpix[0] + 1, 6, "CRPIX1 saved wrong") 

258 self.assertAlmostEqual( 

259 fitsCrpix[1] - hdr.getScalar("LTV2"), parentCrpix[1] + 1, 6, "CRPIX2 saved wrong") 

260 

261 

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

263 pass 

264 

265 

266def setup_module(module): 

267 lsst.utils.tests.init() 

268 

269 

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

271 lsst.utils.tests.init() 

272 unittest.main()