Coverage for tests/test_warper.py: 36%

96 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-14 02:44 -0700

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22"""Basic test of Warp (the warping algorithm is thoroughly tested in lsst.afw.math) 

23""" 

24import os 

25import unittest 

26 

27import lsst.utils 

28import lsst.utils.tests 

29import lsst.geom 

30import lsst.afw.geom as afwGeom 

31import lsst.afw.image as afwImage 

32import lsst.afw.math as afwMath 

33from lsst.log import Log 

34 

35# Change the level to Log.DEBUG to see debug messages 

36Log.getLogger("lsst.afw.image.Mask").setLevel(Log.INFO) 

37Log.getLogger("TRACE3.lsst.afw.math.warp").setLevel(Log.INFO) 

38Log.getLogger("TRACE4.lsst.afw.math.warp").setLevel(Log.INFO) 

39 

40try: 

41 afwdataDir = lsst.utils.getPackageDir("afwdata") 

42except LookupError: 

43 afwdataDir = None 

44 dataDir = None 

45else: 

46 dataDir = os.path.join(afwdataDir, "data") 

47 originalExposureName = "medexp.fits" 

48 originalExposurePath = os.path.join(dataDir, originalExposureName) 

49 subExposureName = "medsub.fits" 

50 subExposurePath = os.path.join(dataDir, originalExposureName) 

51 originalFullExposureName = os.path.join( 

52 "CFHT", "D4", "cal-53535-i-797722_1.fits") 

53 originalFullExposurePath = os.path.join(dataDir, originalFullExposureName) 

54 

55 

56class WarpExposureTestCase(lsst.utils.tests.TestCase): 

57 """Test case for Warp 

58 """ 

59 

60 def testMatchSwarpLanczos2Exposure(self): 

61 """Test that warpExposure matches swarp using a lanczos2 warping kernel. 

62 """ 

63 self.compareToSwarp("lanczos2") 

64 

65 def testMatchSwarpLanczos2SubExposure(self): 

66 """Test that warpExposure matches swarp using a lanczos2 warping kernel with a subexposure 

67 """ 

68 for useDeepCopy in (False, True): 

69 self.compareToSwarp("lanczos2", useSubregion=True, 

70 useDeepCopy=useDeepCopy) 

71 

72 @unittest.skipIf(dataDir is None, "afwdata not setup") 

73 def testBBox(self): 

74 """Test that the default bounding box includes all warped pixels 

75 """ 

76 kernelName = "lanczos2" 

77 warper = afwMath.Warper(kernelName) 

78 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

79 kernelName=kernelName, useSubregion=True, useDeepCopy=False) 

80 

81 originalFilterLabel = afwImage.FilterLabel(band="i") 

82 originalPhotoCalib = afwImage.PhotoCalib(1.0e5, 1.0e3) 

83 originalExposure.setFilter(originalFilterLabel) 

84 originalExposure.setPhotoCalib(originalPhotoCalib) 

85 

86 warpedExposure1 = warper.warpExposure( 

87 destWcs=swarpedWcs, srcExposure=originalExposure) 

88 # the default size must include all good pixels, so growing the bbox 

89 # should not add any 

90 warpedExposure2 = warper.warpExposure( 

91 destWcs=swarpedWcs, srcExposure=originalExposure, border=1) 

92 # a bit of excess border is allowed, but surely not as much as 10 (in 

93 # fact it is approx. 5) 

94 warpedExposure3 = warper.warpExposure( 

95 destWcs=swarpedWcs, srcExposure=originalExposure, border=-10) 

96 # assert that warpedExposure and warpedExposure2 have the same number of non-no_data pixels 

97 # and that warpedExposure3 has fewer 

98 noDataBitMask = afwImage.Mask.getPlaneBitMask("NO_DATA") 

99 mask1Arr = warpedExposure1.getMaskedImage().getMask().getArray() 

100 mask2Arr = warpedExposure2.getMaskedImage().getMask().getArray() 

101 mask3Arr = warpedExposure3.getMaskedImage().getMask().getArray() 

102 nGood1 = (mask1Arr & noDataBitMask == 0).sum() 

103 nGood2 = (mask2Arr & noDataBitMask == 0).sum() 

104 nGood3 = (mask3Arr & noDataBitMask == 0).sum() 

105 self.assertEqual(nGood1, nGood2) 

106 self.assertLess(nGood3, nGood1) 

107 

108 self.assertEqual(warpedExposure1.getFilter().bandLabel, 

109 originalFilterLabel.bandLabel) 

110 self.assertEqual(warpedExposure1.getPhotoCalib(), originalPhotoCalib) 

111 

112 @unittest.skipIf(dataDir is None, "afwdata not setup") 

113 def testDestBBox(self): 

114 """Test that the destBBox argument works 

115 """ 

116 kernelName = "lanczos2" 

117 warper = afwMath.Warper(kernelName) 

118 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

119 kernelName=kernelName, useSubregion=True, useDeepCopy=False) 

120 

121 bbox = lsst.geom.Box2I(lsst.geom.Point2I(100, 25), lsst.geom.Extent2I(3, 7)) 

122 warpedExposure = warper.warpExposure( 

123 destWcs=swarpedWcs, 

124 srcExposure=originalExposure, 

125 destBBox=bbox, 

126 # should be ignored 

127 border=-2, 

128 # should be ignored 

129 maxBBox=lsst.geom.Box2I(lsst.geom.Point2I(1, 2), 

130 lsst.geom.Extent2I(8, 9)), 

131 ) 

132 self.assertEqual(bbox, warpedExposure.getBBox(afwImage.PARENT)) 

133 

134 @unittest.skipIf(dataDir is None, "afwdata not setup") 

135 def getSwarpedImage(self, kernelName, useSubregion=False, useDeepCopy=False): 

136 """ 

137 Inputs: 

138 - kernelName: name of kernel in the form used by afwImage.makeKernel 

139 - useSubregion: if True then the original source exposure (from which the usual 

140 test exposure was extracted) is read and the correct subregion extracted 

141 - useDeepCopy: if True then the copy of the subimage is a deep copy, 

142 else it is a shallow copy; ignored if useSubregion is False 

143 

144 Returns: 

145 - originalExposure 

146 - swarpedImage 

147 - swarpedWcs 

148 """ 

149 if useSubregion: 

150 originalFullExposure = afwImage.ExposureF(originalExposurePath) 

151 # "medsub" is a subregion of med starting at 0-indexed pixel (40, 150) of size 145 x 200 

152 bbox = lsst.geom.Box2I(lsst.geom.Point2I(40, 150), 

153 lsst.geom.Extent2I(145, 200)) 

154 originalExposure = afwImage.ExposureF( 

155 originalFullExposure, bbox, afwImage.LOCAL, useDeepCopy) 

156 swarpedImageName = f"medsubswarp1{kernelName}.fits" 

157 else: 

158 originalExposure = afwImage.ExposureF(originalExposurePath) 

159 swarpedImageName = f"medswarp1{kernelName}.fits" 

160 

161 swarpedImagePath = os.path.join(dataDir, swarpedImageName) 

162 swarpedDecoratedImage = afwImage.DecoratedImageF(swarpedImagePath) 

163 swarpedImage = swarpedDecoratedImage.getImage() 

164 swarpedMetadata = swarpedDecoratedImage.getMetadata() 

165 swarpedWcs = afwGeom.makeSkyWcs(swarpedMetadata) 

166 return (originalExposure, swarpedImage, swarpedWcs) 

167 

168 @unittest.skipIf(dataDir is None, "afwdata not setup") 

169 def compareToSwarp(self, kernelName, 

170 useSubregion=False, useDeepCopy=False, 

171 interpLength=10, cacheSize=100000, 

172 rtol=4e-05, atol=1e-2): 

173 """Compare warpExposure to swarp for given warping kernel. 

174 

175 Note that swarp only warps the image plane, so only test that plane. 

176 

177 Inputs: 

178 - kernelName: name of kernel in the form used by afwImage.makeKernel 

179 - useSubregion: if True then the original source exposure (from which the usual 

180 test exposure was extracted) is read and the correct subregion extracted 

181 - useDeepCopy: if True then the copy of the subimage is a deep copy, 

182 else it is a shallow copy; ignored if useSubregion is False 

183 - interpLength: interpLength argument for lsst.afw.math.warpExposure 

184 - cacheSize: cacheSize argument for lsst.afw.math.SeparableKernel.computeCache; 

185 0 disables the cache 

186 10000 gives some speed improvement but less accurate results (atol must be increased) 

187 100000 gives better accuracy but no speed improvement in this test 

188 - rtol: relative tolerance as used by numpy.allclose 

189 - atol: absolute tolerance as used by numpy.allclose 

190 """ 

191 warper = afwMath.Warper(kernelName) 

192 

193 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

194 kernelName=kernelName, useSubregion=useSubregion, useDeepCopy=useDeepCopy) 

195 maxBBox = lsst.geom.Box2I( 

196 lsst.geom.Point2I(swarpedImage.getX0(), swarpedImage.getY0()), 

197 lsst.geom.Extent2I(swarpedImage.getWidth(), swarpedImage.getHeight())) 

198 

199 # warning: this test assumes that the swarped image is smaller than it needs to be 

200 # to hold all of the warped pixels 

201 afwWarpedExposure = warper.warpExposure( 

202 destWcs=swarpedWcs, 

203 srcExposure=originalExposure, 

204 maxBBox=maxBBox, 

205 ) 

206 afwWarpedMaskedImage = afwWarpedExposure.getMaskedImage() 

207 

208 afwWarpedMask = afwWarpedMaskedImage.getMask() 

209 noDataBitMask = afwImage.Mask.getPlaneBitMask("NO_DATA") 

210 noDataMask = afwWarpedMask.getArray() & noDataBitMask 

211 

212 msg = "afw and swarp %s-warped %s (ignoring bad pixels)" 

213 self.assertImagesAlmostEqual(afwWarpedMaskedImage.getImage(), swarpedImage, 

214 skipMask=noDataMask, rtol=rtol, atol=atol, msg=msg) 

215 

216 

217class MemoryTester(lsst.utils.tests.MemoryTestCase): 

218 pass 

219 

220 

221def setup_module(module): 

222 lsst.utils.tests.init() 

223 

224 

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

226 lsst.utils.tests.init() 

227 unittest.main()