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.math as afwMath 

33import lsst.pex.exceptions as pexExcept 

34from lsst.log import Log 

35 

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

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

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

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

40 

41try: 

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

43except pexExcept.NotFoundError: 

44 afwdataDir = None 

45 dataDir = None 

46else: 

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

48 originalExposureName = "medexp.fits" 

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

50 subExposureName = "medsub.fits" 

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

52 originalFullExposureName = os.path.join( 

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

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

55 

56 

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

58 """Test case for Warp 

59 """ 

60 

61 def testMatchSwarpLanczos2Exposure(self): 

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

63 """ 

64 self.compareToSwarp("lanczos2") 

65 

66 def testMatchSwarpLanczos2SubExposure(self): 

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

68 """ 

69 for useDeepCopy in (False, True): 

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

71 useDeepCopy=useDeepCopy) 

72 

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

74 def testBBox(self): 

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

76 """ 

77 kernelName = "lanczos2" 

78 warper = afwMath.Warper(kernelName) 

79 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

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

81 

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

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

84 originalExposure.setFilterLabel(originalFilterLabel) 

85 originalExposure.setPhotoCalib(originalPhotoCalib) 

86 

87 warpedExposure1 = warper.warpExposure( 

88 destWcs=swarpedWcs, srcExposure=originalExposure) 

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

90 # should not add any 

91 warpedExposure2 = warper.warpExposure( 

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

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

94 # fact it is approx. 5) 

95 warpedExposure3 = warper.warpExposure( 

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

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

98 # and that warpedExposure3 has fewer 

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

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

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

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

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

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

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

106 self.assertEqual(nGood1, nGood2) 

107 self.assertLess(nGood3, nGood1) 

108 

109 self.assertEqual(warpedExposure1.getFilterLabel().bandLabel, 

110 originalFilterLabel.bandLabel) 

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

112 

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

114 def testDestBBox(self): 

115 """Test that the destBBox argument works 

116 """ 

117 kernelName = "lanczos2" 

118 warper = afwMath.Warper(kernelName) 

119 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

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

121 

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

123 warpedExposure = warper.warpExposure( 

124 destWcs=swarpedWcs, 

125 srcExposure=originalExposure, 

126 destBBox=bbox, 

127 # should be ignored 

128 border=-2, 

129 # should be ignored 

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

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

132 ) 

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

134 

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

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

137 """ 

138 Inputs: 

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

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

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

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

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

144 

145 Returns: 

146 - originalExposure 

147 - swarpedImage 

148 - swarpedWcs 

149 """ 

150 if useSubregion: 

151 originalFullExposure = afwImage.ExposureF(originalExposurePath) 

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

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

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

155 originalExposure = afwImage.ExposureF( 

156 originalFullExposure, bbox, afwImage.LOCAL, useDeepCopy) 

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

158 else: 

159 originalExposure = afwImage.ExposureF(originalExposurePath) 

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

161 

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

163 swarpedDecoratedImage = afwImage.DecoratedImageF(swarpedImagePath) 

164 swarpedImage = swarpedDecoratedImage.getImage() 

165 swarpedMetadata = swarpedDecoratedImage.getMetadata() 

166 swarpedWcs = afwGeom.makeSkyWcs(swarpedMetadata) 

167 return (originalExposure, swarpedImage, swarpedWcs) 

168 

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

170 def compareToSwarp(self, kernelName, 

171 useSubregion=False, useDeepCopy=False, 

172 interpLength=10, cacheSize=100000, 

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

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

175 

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

177 

178 Inputs: 

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

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

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

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

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

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

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

186 0 disables the cache 

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

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

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

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

191 """ 

192 warper = afwMath.Warper(kernelName) 

193 

194 originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage( 

195 kernelName=kernelName, useSubregion=useSubregion, useDeepCopy=useDeepCopy) 

196 maxBBox = lsst.geom.Box2I( 

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

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

199 

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

201 # to hold all of the warped pixels 

202 afwWarpedExposure = warper.warpExposure( 

203 destWcs=swarpedWcs, 

204 srcExposure=originalExposure, 

205 maxBBox=maxBBox, 

206 ) 

207 afwWarpedMaskedImage = afwWarpedExposure.getMaskedImage() 

208 

209 afwWarpedMask = afwWarpedMaskedImage.getMask() 

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

211 noDataMask = afwWarpedMask.getArray() & noDataBitMask 

212 

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

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

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

216 

217 

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

219 pass 

220 

221 

222def setup_module(module): 

223 lsst.utils.tests.init() 

224 

225 

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

227 lsst.utils.tests.init() 

228 unittest.main()