Coverage for tests/test_warper.py : 34%

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
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
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)
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)
57class WarpExposureTestCase(lsst.utils.tests.TestCase):
58 """Test case for Warp
59 """
61 def testMatchSwarpLanczos2Exposure(self):
62 """Test that warpExposure matches swarp using a lanczos2 warping kernel.
63 """
64 self.compareToSwarp("lanczos2")
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)
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)
82 originalFilterLabel = afwImage.FilterLabel(band="i")
83 originalPhotoCalib = afwImage.PhotoCalib(1.0e5, 1.0e3)
84 originalExposure.setFilterLabel(originalFilterLabel)
85 originalExposure.setPhotoCalib(originalPhotoCalib)
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)
109 self.assertEqual(warpedExposure1.getFilterLabel().bandLabel,
110 originalFilterLabel.bandLabel)
111 self.assertEqual(warpedExposure1.getPhotoCalib(), originalPhotoCalib)
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)
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))
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
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"
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)
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.
176 Note that swarp only warps the image plane, so only test that plane.
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)
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()))
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()
209 afwWarpedMask = afwWarpedMaskedImage.getMask()
210 noDataBitMask = afwImage.Mask.getPlaneBitMask("NO_DATA")
211 noDataMask = afwWarpedMask.getArray() & noDataBitMask
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)
218class MemoryTester(lsst.utils.tests.MemoryTestCase):
219 pass
222def setup_module(module):
223 lsst.utils.tests.init()
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()