Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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.image.utils as imageUtils 

33import lsst.afw.math as afwMath 

34import lsst.pex.exceptions as pexExcept 

35from lsst.log import Log 

36 

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

38Log.getLogger("afw.image.Mask").setLevel(Log.INFO) 

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

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

41 

42try: 

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

44except pexExcept.NotFoundError: 

45 afwdataDir = None 

46 dataDir = None 

47else: 

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

49 originalExposureName = "medexp.fits" 

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

51 subExposureName = "medsub.fits" 

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

53 originalFullExposureName = os.path.join( 

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

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

56 

57 

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

59 """Test case for Warp 

60 """ 

61 

62 def testMatchSwarpLanczos2Exposure(self): 

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

64 """ 

65 self.compareToSwarp("lanczos2") 

66 

67 def testMatchSwarpLanczos2SubExposure(self): 

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

69 """ 

70 for useDeepCopy in (False, True): 

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

72 useDeepCopy=useDeepCopy) 

73 

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

75 def testBBox(self): 

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

77 """ 

78 kernelName = "lanczos2" 

79 warper = afwMath.Warper(kernelName) 

80 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

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

82 

83 imageUtils.defineFilter("i", 748.1) 

84 

85 originalFilter = afwImage.Filter("i") 

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

87 originalExposure.setFilter(originalFilter) 

88 originalExposure.setPhotoCalib(originalPhotoCalib) 

89 

90 warpedExposure1 = warper.warpExposure( 

91 destWcs=swarpedWcs, srcExposure=originalExposure) 

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

93 # should not add any 

94 warpedExposure2 = warper.warpExposure( 

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

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

97 # fact it is approx. 5) 

98 warpedExposure3 = warper.warpExposure( 

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

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

101 # and that warpedExposure3 has fewer 

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

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

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

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

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

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

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

109 self.assertEqual(nGood1, nGood2) 

110 self.assertLess(nGood3, nGood1) 

111 

112 self.assertEqual(warpedExposure1.getFilter().getName(), 

113 originalFilter.getName()) 

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

115 

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

117 def testDestBBox(self): 

118 """Test that the destBBox argument works 

119 """ 

120 kernelName = "lanczos2" 

121 warper = afwMath.Warper(kernelName) 

122 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

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

124 

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

126 warpedExposure = warper.warpExposure( 

127 destWcs=swarpedWcs, 

128 srcExposure=originalExposure, 

129 destBBox=bbox, 

130 # should be ignored 

131 border=-2, 

132 # should be ignored 

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

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

135 ) 

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

137 

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

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

140 """ 

141 Inputs: 

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

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

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

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

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

147 

148 Returns: 

149 - originalExposure 

150 - swarpedImage 

151 - swarpedWcs 

152 """ 

153 if useSubregion: 

154 originalFullExposure = afwImage.ExposureF(originalExposurePath) 

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

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

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

158 originalExposure = afwImage.ExposureF( 

159 originalFullExposure, bbox, afwImage.LOCAL, useDeepCopy) 

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

161 else: 

162 originalExposure = afwImage.ExposureF(originalExposurePath) 

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

164 

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

166 swarpedDecoratedImage = afwImage.DecoratedImageF(swarpedImagePath) 

167 swarpedImage = swarpedDecoratedImage.getImage() 

168 swarpedMetadata = swarpedDecoratedImage.getMetadata() 

169 swarpedWcs = afwGeom.makeSkyWcs(swarpedMetadata) 

170 return (originalExposure, swarpedImage, swarpedWcs) 

171 

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

173 def compareToSwarp(self, kernelName, 

174 useSubregion=False, useDeepCopy=False, 

175 interpLength=10, cacheSize=100000, 

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

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

178 

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

180 

181 Inputs: 

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

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

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

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

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

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

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

189 0 disables the cache 

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

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

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

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

194 """ 

195 warper = afwMath.Warper(kernelName) 

196 

197 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

198 kernelName=kernelName, useSubregion=useSubregion, useDeepCopy=useDeepCopy) 

199 maxBBox = lsst.geom.Box2I( 

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

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

202 

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

204 # to hold all of the warped pixels 

205 afwWarpedExposure = warper.warpExposure( 

206 destWcs=swarpedWcs, 

207 srcExposure=originalExposure, 

208 maxBBox=maxBBox, 

209 ) 

210 afwWarpedMaskedImage = afwWarpedExposure.getMaskedImage() 

211 

212 afwWarpedMask = afwWarpedMaskedImage.getMask() 

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

214 noDataMask = afwWarpedMask.getArray() & noDataBitMask 

215 

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

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

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

219 

220 

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

222 pass 

223 

224 

225def setup_module(module): 

226 lsst.utils.tests.init() 

227 

228 

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

230 lsst.utils.tests.init() 

231 unittest.main()