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# 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"""Test warpExposure 

23""" 

24import os 

25import unittest 

26 

27import numpy as np 

28 

29import lsst.utils 

30import lsst.utils.tests 

31import lsst.daf.base as dafBase 

32from lsst.afw.coord import Observatory, Weather 

33import lsst.geom 

34import lsst.afw.geom as afwGeom 

35import lsst.afw.image as afwImage 

36import lsst.afw.math as afwMath 

37import lsst.afw.image.utils as imageUtils 

38import lsst.pex.exceptions as pexExcept 

39import lsst.afw.display as afwDisplay 

40from lsst.log import Log 

41 

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

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

44Log.getLogger("TRACE2.afw.math.warp").setLevel(Log.INFO) 

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

46 

47afwDisplay.setDefaultMaskTransparency(75) 

48 

49display = False 

50# set True to save afw-warped images as FITS files 

51SAVE_FITS_FILES = False 

52# set True to save failed afw-warped images as FITS files even if 

53# SAVE_FITS_FILES is False 

54SAVE_FAILED_FITS_FILES = True 

55 

56try: 

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

58except pexExcept.NotFoundError: 

59 afwdataDir = None 

60else: 

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

62 

63 originalExposureName = "medexp.fits" 

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

65 subExposureName = "medsub.fits" 

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

67 originalFullExposureName = os.path.join( 

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

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

70 

71 

72def makeVisitInfo(): 

73 """Return a non-NaN visitInfo.""" 

74 return afwImage.VisitInfo(exposureId=10313423, 

75 exposureTime=10.01, 

76 darkTime=11.02, 

77 date=dafBase.DateTime(65321.1, dafBase.DateTime.MJD, dafBase.DateTime.TAI), 

78 ut1=12345.1, 

79 era=45.1*lsst.geom.degrees, 

80 boresightRaDec=lsst.geom.SpherePoint(23.1, 73.2, lsst.geom.degrees), 

81 boresightAzAlt=lsst.geom.SpherePoint(134.5, 33.3, lsst.geom.degrees), 

82 boresightAirmass=1.73, 

83 boresightRotAngle=73.2*lsst.geom.degrees, 

84 rotType=afwImage.RotType.SKY, 

85 observatory=Observatory(11.1*lsst.geom.degrees, 22.2*lsst.geom.degrees, 0.333), 

86 weather=Weather(1.1, 2.2, 34.5), 

87 ) 

88 

89 

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

91 """Test case for warpExposure 

92 """ 

93 

94 def setUp(self): 

95 np.random.seed(0) 

96 

97 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

98 def testNullWarpExposure(self, interpLength=10): 

99 """Test that warpExposure maps an image onto itself. 

100 

101 Note: 

102 - NO_DATA and off-CCD pixels must be ignored 

103 - bad mask pixels get smeared out so we have to excluded all bad mask pixels 

104 from the output image when comparing masks. 

105 """ 

106 imageUtils.defineFilter("i", 748.1) 

107 

108 originalExposure = afwImage.ExposureF(originalExposurePath) 

109 originalExposure.getInfo().setVisitInfo(makeVisitInfo()) 

110 originalFilter = afwImage.Filter("i") 

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

112 originalExposure.setFilter(originalFilter) 

113 originalExposure.setPhotoCalib(originalPhotoCalib) 

114 afwWarpedExposure = afwImage.ExposureF( 

115 originalExposure.getBBox(), 

116 originalExposure.getWcs()) 

117 warpingControl = afwMath.WarpingControl( 

118 "lanczos4", "", 0, interpLength) 

119 afwMath.warpExposure( 

120 afwWarpedExposure, originalExposure, warpingControl) 

121 if SAVE_FITS_FILES: 

122 afwWarpedExposure.writeFits("afwWarpedExposureNull.fits") 

123 

124 self.assertEqual(afwWarpedExposure.getFilter().getName(), 

125 originalFilter.getName()) 

126 self.assertEqual(afwWarpedExposure.getPhotoCalib(), originalPhotoCalib) 

127 self.assertEqual(afwWarpedExposure.getInfo().getVisitInfo(), 

128 originalExposure.getInfo().getVisitInfo()) 

129 

130 afwWarpedMaskedImage = afwWarpedExposure.getMaskedImage() 

131 afwWarpedMask = afwWarpedMaskedImage.getMask() 

132 noDataBitMask = afwWarpedMask.getPlaneBitMask("NO_DATA") 

133 afwWarpedMaskedImageArrSet = afwWarpedMaskedImage.getArrays() 

134 afwWarpedMaskArr = afwWarpedMaskedImageArrSet[1] 

135 

136 # compare all non-DATA pixels of image and variance, but relax specs a bit 

137 # because of minor noise introduced by bad pixels 

138 noDataMaskArr = afwWarpedMaskArr & noDataBitMask 

139 msg = "afw null-warped MaskedImage (all pixels, relaxed tolerance)" 

140 self.assertMaskedImagesAlmostEqual(afwWarpedMaskedImage, originalExposure.getMaskedImage(), 

141 doMask=False, skipMask=noDataMaskArr, atol=1e-5, msg=msg) 

142 

143 # compare good pixels (mask=0) of image, mask and variance using full 

144 # tolerance 

145 msg = "afw null-warped MaskedImage (good pixels, max tolerance)" 

146 self.assertMaskedImagesAlmostEqual(afwWarpedMaskedImage, originalExposure.getMaskedImage(), 

147 skipMask=afwWarpedMask, msg=msg) 

148 

149 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

150 def testNullWarpImage(self, interpLength=10): 

151 """Test that warpImage maps an image onto itself. 

152 """ 

153 originalExposure = afwImage.ExposureF(originalExposurePath) 

154 afwWarpedExposure = afwImage.ExposureF(originalExposurePath) 

155 originalImage = originalExposure.getMaskedImage().getImage() 

156 afwWarpedImage = afwWarpedExposure.getMaskedImage().getImage() 

157 originalWcs = originalExposure.getWcs() 

158 afwWarpedWcs = afwWarpedExposure.getWcs() 

159 warpingControl = afwMath.WarpingControl( 

160 "lanczos4", "", 0, interpLength) 

161 afwMath.warpImage(afwWarpedImage, afwWarpedWcs, 

162 originalImage, originalWcs, warpingControl) 

163 if SAVE_FITS_FILES: 

164 afwWarpedImage.writeFits("afwWarpedImageNull.fits") 

165 afwWarpedImageArr = afwWarpedImage.getArray() 

166 noDataMaskArr = np.isnan(afwWarpedImageArr) 

167 # relax specs a bit because of minor noise introduced by bad pixels 

168 msg = "afw null-warped Image" 

169 self.assertImagesAlmostEqual(originalImage, afwWarpedImage, skipMask=noDataMaskArr, 

170 atol=1e-5, msg=msg) 

171 

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

173 def testNullWcs(self, interpLength=10): 

174 """Cannot warp from or into an exposure without a Wcs. 

175 """ 

176 exposureWithWcs = afwImage.ExposureF(originalExposurePath) 

177 mi = exposureWithWcs.getMaskedImage() 

178 exposureWithoutWcs = afwImage.ExposureF(mi.getDimensions()) 

179 warpingControl = afwMath.WarpingControl( 

180 "bilinear", "", 0, interpLength) 

181 

182 with self.assertRaises(pexExcept.InvalidParameterError): 

183 afwMath.warpExposure(exposureWithWcs, exposureWithoutWcs, warpingControl) 

184 

185 with self.assertRaises(pexExcept.InvalidParameterError): 

186 afwMath.warpExposure(exposureWithoutWcs, exposureWithWcs, warpingControl) 

187 

188 def testWarpIntoSelf(self, interpLength=10): 

189 """Cannot warp in-place 

190 """ 

191 wcs = afwGeom.makeSkyWcs( 

192 crpix=lsst.geom.Point2D(0, 0), 

193 crval=lsst.geom.SpherePoint(359, 0, lsst.geom.degrees), 

194 cdMatrix=afwGeom.makeCdMatrix(1.0e-8*lsst.geom.degrees), 

195 ) 

196 exposure = afwImage.ExposureF(lsst.geom.Extent2I(100, 100), wcs) 

197 maskedImage = exposure.getMaskedImage() 

198 warpingControl = afwMath.WarpingControl( 

199 "bilinear", "", 0, interpLength) 

200 

201 with self.assertRaises(pexExcept.InvalidParameterError): 

202 afwMath.warpExposure(exposure, exposure, warpingControl) 

203 

204 with self.assertRaises(pexExcept.InvalidParameterError): 

205 afwMath.warpImage(maskedImage, wcs, maskedImage, wcs, warpingControl) 

206 

207 with self.assertRaises(pexExcept.InvalidParameterError): 

208 afwMath.warpImage(maskedImage.getImage(), wcs, maskedImage.getImage(), wcs, warpingControl) 

209 

210 def testWarpingControl(self): 

211 """Test the basic mechanics of WarpingControl 

212 """ 

213 for interpLength in (0, 1, 52): 

214 wc = afwMath.WarpingControl("lanczos3", "", 0, interpLength) 

215 self.assertFalse(wc.hasMaskWarpingKernel()) 

216 self.assertEqual(wc.getInterpLength(), interpLength) 

217 for newInterpLength in (3, 7, 9): 

218 wc.setInterpLength(newInterpLength) 

219 self.assertEqual(wc.getInterpLength(), newInterpLength) 

220 

221 for cacheSize in (0, 100): 

222 wc = afwMath.WarpingControl("lanczos3", "bilinear", cacheSize) 

223 self.assertTrue(wc.hasMaskWarpingKernel()) 

224 self.assertEqual(wc.getCacheSize(), cacheSize) 

225 self.assertEqual(wc.getWarpingKernel().getCacheSize(), cacheSize) 

226 self.assertEqual( 

227 wc.getMaskWarpingKernel().getCacheSize(), cacheSize) 

228 for newCacheSize in (1, 50): 

229 wc.setCacheSize(newCacheSize) 

230 self.assertEqual(wc.getCacheSize(), newCacheSize) 

231 self.assertEqual( 

232 wc.getWarpingKernel().getCacheSize(), newCacheSize) 

233 self.assertEqual( 

234 wc.getMaskWarpingKernel().getCacheSize(), newCacheSize) 

235 

236 def testWarpingControlError(self): 

237 """Test error handling of WarpingControl 

238 """ 

239 # error: mask kernel smaller than main kernel 

240 for kernelName, maskKernelName in ( 

241 ("bilinear", "lanczos3"), 

242 ("bilinear", "lanczos4"), 

243 ("lanczos3", "lanczos4"), 

244 ): 

245 with self.assertRaises(pexExcept.InvalidParameterError): 

246 afwMath.WarpingControl(kernelName, maskKernelName) 

247 

248 # error: new mask kernel larger than main kernel 

249 warpingControl = afwMath.WarpingControl("bilinear") 

250 for maskKernelName in ("lanczos3", "lanczos4"): 

251 with self.assertRaises(pexExcept.InvalidParameterError): 

252 warpingControl.setMaskWarpingKernelName(maskKernelName) 

253 

254 # error: new kernel smaller than mask kernel 

255 warpingControl = afwMath.WarpingControl("lanczos4", "lanczos4") 

256 for kernelName in ("bilinear", "lanczos3"): 

257 with self.assertRaises(pexExcept.InvalidParameterError): 

258 warpingControl.setWarpingKernelName(kernelName) 

259 

260 # OK: main kernel at least as big as mask kernel 

261 for kernelName, maskKernelName in ( 

262 ("bilinear", "bilinear"), 

263 ("lanczos3", "lanczos3"), 

264 ("lanczos3", "bilinear"), 

265 ("lanczos4", "lanczos3"), 

266 ): 

267 # this should not raise any exception 

268 afwMath.WarpingControl(kernelName, maskKernelName) 

269 

270 # invalid kernel names 

271 for kernelName, maskKernelName in ( 

272 ("badname", ""), 

273 ("lanczos", ""), # no digit after lanczos 

274 ("lanczos3", "badname"), 

275 ("lanczos3", "lanczos"), 

276 ): 

277 with self.assertRaises(pexExcept.InvalidParameterError): 

278 afwMath.WarpingControl(kernelName, maskKernelName) 

279 

280 def testWarpMask(self): 

281 """Test that warping the mask plane with a different kernel does the right thing 

282 """ 

283 for kernelName, maskKernelName in ( 

284 ("bilinear", "bilinear"), 

285 ("lanczos3", "lanczos3"), 

286 ("lanczos3", "bilinear"), 

287 ("lanczos4", "lanczos3"), 

288 ): 

289 for growFullMask in (0, 1, 3, 0xFFFF): 

290 self.verifyMaskWarp( 

291 kernelName=kernelName, 

292 maskKernelName=maskKernelName, 

293 growFullMask=growFullMask, 

294 ) 

295 

296 def testMatchSwarpBilinearImage(self): 

297 """Test that warpExposure matches swarp using a bilinear warping kernel 

298 """ 

299 self.compareToSwarp("bilinear", useWarpExposure=False, atol=0.15) 

300 

301 def testMatchSwarpBilinearExposure(self): 

302 """Test that warpExposure matches swarp using a bilinear warping kernel 

303 """ 

304 self.compareToSwarp("bilinear", useWarpExposure=True, 

305 useSubregion=False, useDeepCopy=True) 

306 

307 def testMatchSwarpLanczos2Image(self): 

308 """Test that warpExposure matches swarp using a lanczos2 warping kernel 

309 """ 

310 self.compareToSwarp("lanczos2", useWarpExposure=False) 

311 

312 def testMatchSwarpLanczos2Exposure(self): 

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

314 """ 

315 self.compareToSwarp("lanczos2", useWarpExposure=True) 

316 

317 def testMatchSwarpLanczos2SubExposure(self): 

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

319 """ 

320 for useDeepCopy in (False, True): 

321 self.compareToSwarp("lanczos2", useWarpExposure=True, 

322 useSubregion=True, useDeepCopy=useDeepCopy) 

323 

324 def testMatchSwarpLanczos3Image(self): 

325 """Test that warpExposure matches swarp using a lanczos2 warping kernel 

326 """ 

327 self.compareToSwarp("lanczos3", useWarpExposure=False) 

328 

329 def testMatchSwarpLanczos3(self): 

330 """Test that warpExposure matches swarp using a lanczos4 warping kernel. 

331 """ 

332 self.compareToSwarp("lanczos3", useWarpExposure=True) 

333 

334 def testMatchSwarpLanczos4Image(self): 

335 """Test that warpExposure matches swarp using a lanczos2 warping kernel 

336 """ 

337 self.compareToSwarp("lanczos4", useWarpExposure=False) 

338 

339 def testMatchSwarpLanczos4(self): 

340 """Test that warpExposure matches swarp using a lanczos4 warping kernel. 

341 """ 

342 self.compareToSwarp("lanczos4", useWarpExposure=True) 

343 

344 def testMatchSwarpNearestExposure(self): 

345 """Test that warpExposure matches swarp using a nearest neighbor warping kernel 

346 """ 

347 self.compareToSwarp("nearest", useWarpExposure=True, atol=60) 

348 

349 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

350 def testTransformBasedWarp(self): 

351 """Test warping using TransformPoint2ToPoint2 

352 """ 

353 for interpLength in (0, 1, 2, 4): 

354 kernelName = "lanczos3" 

355 rtol = 4e-5 

356 atol = 1e-2 

357 warpingControl = afwMath.WarpingControl( 

358 warpingKernelName=kernelName, 

359 interpLength=interpLength, 

360 ) 

361 

362 originalExposure = afwImage.ExposureF(originalExposurePath) 

363 originalMetadata = afwImage.DecoratedImageF(originalExposurePath).getMetadata() 

364 originalSkyWcs = afwGeom.makeSkyWcs(originalMetadata) 

365 

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

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

368 swarpedDecoratedImage = afwImage.DecoratedImageF(swarpedImagePath) 

369 swarpedImage = swarpedDecoratedImage.getImage() 

370 

371 swarpedMetadata = swarpedDecoratedImage.getMetadata() 

372 warpedSkyWcs = afwGeom.makeSkyWcs(swarpedMetadata) 

373 

374 # original image is source, warped image is destination 

375 srcToDest = afwGeom.makeWcsPairTransform(originalSkyWcs, warpedSkyWcs) 

376 

377 afwWarpedMaskedImage = afwImage.MaskedImageF(swarpedImage.getDimensions()) 

378 originalMaskedImage = originalExposure.getMaskedImage() 

379 

380 numGoodPix = afwMath.warpImage(afwWarpedMaskedImage, originalMaskedImage, 

381 srcToDest, warpingControl) 

382 self.assertGreater(numGoodPix, 50) 

383 

384 afwWarpedImage = afwWarpedMaskedImage.getImage() 

385 afwWarpedImageArr = afwWarpedImage.getArray() 

386 noDataMaskArr = np.isnan(afwWarpedImageArr) 

387 self.assertImagesAlmostEqual(afwWarpedImage, swarpedImage, 

388 skipMask=noDataMaskArr, rtol=rtol, atol=atol) 

389 

390 def testTicket2441(self): 

391 """Test ticket 2441: warpExposure sometimes mishandles zero-extent dest exposures""" 

392 fromWcs = afwGeom.makeSkyWcs( 

393 crpix=lsst.geom.Point2D(0, 0), 

394 crval=lsst.geom.SpherePoint(359, 0, lsst.geom.degrees), 

395 cdMatrix=afwGeom.makeCdMatrix(scale=1.0e-8*lsst.geom.degrees), 

396 ) 

397 fromExp = afwImage.ExposureF(afwImage.MaskedImageF(10, 10), fromWcs) 

398 

399 toWcs = afwGeom.makeSkyWcs( 

400 crpix=lsst.geom.Point2D(410000, 11441), 

401 crval=lsst.geom.SpherePoint(45, 0, lsst.geom.degrees), 

402 cdMatrix=afwGeom.makeCdMatrix(scale=0.00011*lsst.geom.degrees, flipX=True), 

403 projection="CEA", 

404 ) 

405 toExp = afwImage.ExposureF(afwImage.MaskedImageF(0, 0), toWcs) 

406 

407 warpControl = afwMath.WarpingControl("lanczos3") 

408 # if a bug described in ticket #2441 is present, this will raise an 

409 # exception: 

410 numGoodPix = afwMath.warpExposure(toExp, fromExp, warpControl) 

411 self.assertEqual(numGoodPix, 0) 

412 

413 def testTicketDM4063(self): 

414 """Test that a uint16 array can be cast to a bool array, to avoid DM-4063 

415 """ 

416 a = np.array([0, 1, 0, 23], dtype=np.uint16) 

417 b = np.array([True, True, False, False], dtype=bool) 

418 acast = np.array(a != 0, dtype=bool) 

419 orArr = acast | b 

420 desOrArr = np.array([True, True, False, True], dtype=bool) 

421 # Note: assertEqual(bool arr, bool arr) fails with: 

422 # ValueError: The truth value of an array with more than one element is 

423 # ambiguous 

424 try: 

425 self.assertTrue(np.all(orArr == desOrArr)) 

426 except Exception as e: 

427 print(f"Failed: {orArr!r} != {desOrArr!r}: {e}") 

428 raise 

429 

430 def testSmallSrc(self): 

431 """Verify that a source image that is too small will not raise an exception 

432 

433 This tests another bug that was fixed in ticket #2441 

434 """ 

435 fromWcs = afwGeom.makeSkyWcs( 

436 crpix=lsst.geom.Point2D(0, 0), 

437 crval=lsst.geom.SpherePoint(359, 0, lsst.geom.degrees), 

438 cdMatrix=afwGeom.makeCdMatrix(scale=1.0e-8*lsst.geom.degrees), 

439 ) 

440 fromExp = afwImage.ExposureF(afwImage.MaskedImageF(1, 1), fromWcs) 

441 

442 toWcs = afwGeom.makeSkyWcs( 

443 crpix=lsst.geom.Point2D(0, 0), 

444 crval=lsst.geom.SpherePoint(358, 0, lsst.geom.degrees), 

445 cdMatrix=afwGeom.makeCdMatrix(scale=1.1e-8*lsst.geom.degrees), 

446 ) 

447 toExp = afwImage.ExposureF(afwImage.MaskedImageF(10, 10), toWcs) 

448 

449 warpControl = afwMath.WarpingControl("lanczos3") 

450 # if a bug described in ticket #2441 is present, this will raise an 

451 # exception: 

452 numGoodPix = afwMath.warpExposure(toExp, fromExp, warpControl) 

453 self.assertEqual(numGoodPix, 0) 

454 imArr, maskArr, varArr = toExp.getMaskedImage().getArrays() 

455 self.assertTrue(np.all(np.isnan(imArr))) 

456 self.assertTrue(np.all(np.isinf(varArr))) 

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

458 self.assertTrue(np.all(maskArr == noDataBitMask)) 

459 

460 def verifyMaskWarp(self, kernelName, maskKernelName, growFullMask, interpLength=10, cacheSize=100000, 

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

462 """Verify that using a separate mask warping kernel produces the correct results 

463 

464 Inputs: 

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

466 - maskKernelName: name of mask warping kernel in the form used by afwImage.makeKernel 

467 - interpLength: interpLength argument for lsst.afw.math.WarpingControl 

468 - cacheSize: cacheSize argument for lsst.afw.math.WarpingControl; 

469 0 disables the cache 

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

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

472 - rtol: relative tolerance as used by np.allclose 

473 - atol: absolute tolerance as used by np.allclose 

474 """ 

475 srcWcs = afwGeom.makeSkyWcs( 

476 crpix=lsst.geom.Point2D(10, 11), 

477 crval=lsst.geom.SpherePoint(41.7, 32.9, lsst.geom.degrees), 

478 cdMatrix=afwGeom.makeCdMatrix(scale=0.2*lsst.geom.degrees), 

479 ) 

480 destWcs = afwGeom.makeSkyWcs( 

481 crpix=lsst.geom.Point2D(9, 10), 

482 crval=lsst.geom.SpherePoint(41.65, 32.95, lsst.geom.degrees), 

483 cdMatrix=afwGeom.makeCdMatrix(scale=0.17*lsst.geom.degrees), 

484 ) 

485 

486 srcMaskedImage = afwImage.MaskedImageF(100, 101) 

487 srcExposure = afwImage.ExposureF(srcMaskedImage, srcWcs) 

488 

489 srcArrays = srcMaskedImage.getArrays() 

490 shape = srcArrays[0].shape 

491 srcArrays[0][:] = np.random.normal(10000, 1000, size=shape) 

492 srcArrays[2][:] = np.random.normal(9000, 900, size=shape) 

493 srcArrays[1][:] = np.reshape( 

494 np.arange(0, shape[0] * shape[1], 1, dtype=np.uint16), shape) 

495 

496 warpControl = afwMath.WarpingControl( 

497 kernelName, 

498 maskKernelName, 

499 cacheSize, 

500 interpLength, 

501 growFullMask 

502 ) 

503 destMaskedImage = afwImage.MaskedImageF(110, 121) 

504 destExposure = afwImage.ExposureF(destMaskedImage, destWcs) 

505 afwMath.warpExposure(destExposure, srcExposure, warpControl) 

506 

507 # now compute with two separate mask planes 

508 warpControl.setGrowFullMask(0) 

509 narrowMaskedImage = afwImage.MaskedImageF(110, 121) 

510 narrowExposure = afwImage.ExposureF(narrowMaskedImage, destWcs) 

511 afwMath.warpExposure(narrowExposure, srcExposure, warpControl) 

512 narrowArrays = narrowExposure.getMaskedImage().getArrays() 

513 

514 warpControl.setMaskWarpingKernelName("") 

515 broadMaskedImage = afwImage.MaskedImageF(110, 121) 

516 broadExposure = afwImage.ExposureF(broadMaskedImage, destWcs) 

517 afwMath.warpExposure(broadExposure, srcExposure, warpControl) 

518 broadArrays = broadExposure.getMaskedImage().getArrays() 

519 

520 if (kernelName != maskKernelName) and (growFullMask != 0xFFFF): 

521 # we expect the mask planes to differ 

522 if np.all(narrowArrays[1] == broadArrays[1]): 

523 self.fail("No difference between broad and narrow mask") 

524 

525 predMask = (broadArrays[1] & growFullMask) | ( 

526 narrowArrays[1] & ~growFullMask).astype(np.uint16) 

527 predArraySet = (broadArrays[0], predMask, broadArrays[2]) 

528 predExposure = afwImage.makeMaskedImageFromArrays(*predArraySet) 

529 

530 msg = f"Separate mask warping failed; warpingKernel={kernelName}; maskWarpingKernel={maskKernelName}" 

531 self.assertMaskedImagesAlmostEqual(destExposure.getMaskedImage(), predExposure, 

532 doImage=True, doMask=True, doVariance=True, 

533 rtol=rtol, atol=atol, msg=msg) 

534 

535 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

536 def compareToSwarp(self, kernelName, 

537 useWarpExposure=True, useSubregion=False, useDeepCopy=False, 

538 interpLength=10, cacheSize=100000, 

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

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

541 

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

543 

544 Inputs: 

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

546 - useWarpExposure: if True, call warpExposure to warp an ExposureF, 

547 else call warpImage to warp an ImageF and also call the Transform version 

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

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

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

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

552 - interpLength: interpLength argument for lsst.afw.math.WarpingControl 

553 - cacheSize: cacheSize argument for lsst.afw.math.WarpingControl; 

554 0 disables the cache 

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

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

557 - rtol: relative tolerance as used by np.allclose 

558 - atol: absolute tolerance as used by np.allclose 

559 """ 

560 warpingControl = afwMath.WarpingControl( 

561 kernelName, 

562 "", # there is no point to a separate mask kernel since we aren't testing the mask plane 

563 cacheSize, 

564 interpLength, 

565 ) 

566 if useSubregion: 

567 originalFullExposure = afwImage.ExposureF(originalExposurePath) 

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

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

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

571 originalExposure = afwImage.ExposureF( 

572 originalFullExposure, bbox, afwImage.LOCAL, useDeepCopy) 

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

574 else: 

575 originalExposure = afwImage.ExposureF(originalExposurePath) 

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

577 

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

579 swarpedDecoratedImage = afwImage.DecoratedImageF(swarpedImagePath) 

580 swarpedImage = swarpedDecoratedImage.getImage() 

581 swarpedMetadata = swarpedDecoratedImage.getMetadata() 

582 warpedWcs = afwGeom.makeSkyWcs(swarpedMetadata) 

583 

584 if useWarpExposure: 

585 # path for saved afw-warped image 

586 afwWarpedImagePath = f"afwWarpedExposure1{kernelName}.fits" 

587 

588 afwWarpedMaskedImage = afwImage.MaskedImageF( 

589 swarpedImage.getDimensions()) 

590 afwWarpedExposure = afwImage.ExposureF( 

591 afwWarpedMaskedImage, warpedWcs) 

592 afwMath.warpExposure( 

593 afwWarpedExposure, originalExposure, warpingControl) 

594 afwWarpedMask = afwWarpedMaskedImage.getMask() 

595 if SAVE_FITS_FILES: 

596 afwWarpedExposure.writeFits(afwWarpedImagePath) 

597 if display: 

598 afwDisplay.Display(frame=1).mtv(afwWarpedExposure, title="Warped") 

599 

600 swarpedMaskedImage = afwImage.MaskedImageF(swarpedImage) 

601 

602 if display: 

603 afwDisplay.Display(frame=2).mtv(swarpedMaskedImage, title="SWarped") 

604 

605 msg = f"afw and swarp {kernelName}-warped differ (ignoring bad pixels)" 

606 try: 

607 self.assertMaskedImagesAlmostEqual(afwWarpedMaskedImage, swarpedMaskedImage, 

608 doImage=True, doMask=False, doVariance=False, 

609 skipMask=afwWarpedMask, rtol=rtol, atol=atol, msg=msg) 

610 except Exception: 

611 if SAVE_FAILED_FITS_FILES: 

612 afwWarpedExposure.writeFits(afwWarpedImagePath) 

613 print(f"Saved failed afw-warped exposure as: {afwWarpedImagePath}") 

614 raise 

615 else: 

616 # path for saved afw-warped image 

617 afwWarpedImagePath = f"afwWarpedImage1{kernelName}.fits" 

618 afwWarpedImage2Path = f"afwWarpedImage1{kernelName}_xyTransform.fits" 

619 

620 afwWarpedImage = afwImage.ImageF(swarpedImage.getDimensions()) 

621 originalImage = originalExposure.getMaskedImage().getImage() 

622 originalWcs = originalExposure.getWcs() 

623 afwMath.warpImage(afwWarpedImage, warpedWcs, originalImage, 

624 originalWcs, warpingControl) 

625 if display: 

626 afwDisplay.Display(frame=1).mtv(afwWarpedImage, title="Warped") 

627 afwDisplay.Display(frame=2).mtv(swarpedImage, title="SWarped") 

628 diff = swarpedImage.Factory(swarpedImage, True) 

629 diff -= afwWarpedImage 

630 afwDisplay.Display(frame=3).mtv(diff, title="swarp - afw") 

631 if SAVE_FITS_FILES: 

632 afwWarpedImage.writeFits(afwWarpedImagePath) 

633 

634 afwWarpedImageArr = afwWarpedImage.getArray() 

635 noDataMaskArr = np.isnan(afwWarpedImageArr) 

636 msg = f"afw and swarp {kernelName}-warped images do not match (ignoring NaN pixels)" 

637 try: 

638 self.assertImagesAlmostEqual(afwWarpedImage, swarpedImage, 

639 skipMask=noDataMaskArr, rtol=rtol, atol=atol, msg=msg) 

640 except Exception: 

641 if SAVE_FAILED_FITS_FILES: 

642 # save the image anyway 

643 afwWarpedImage.writeFits(afwWarpedImagePath) 

644 print(f"Saved failed afw-warped image as: {afwWarpedImagePath}") 

645 raise 

646 

647 afwWarpedImage2 = afwImage.ImageF(swarpedImage.getDimensions()) 

648 srcToDest = afwGeom.makeWcsPairTransform(originalWcs, warpedWcs) 

649 afwMath.warpImage(afwWarpedImage2, originalImage, 

650 srcToDest, warpingControl) 

651 msg = f"afw transform-based and WCS-based {kernelName}-warped images do not match" 

652 try: 

653 self.assertImagesAlmostEqual(afwWarpedImage2, afwWarpedImage, 

654 rtol=rtol, atol=atol, msg=msg) 

655 except Exception: 

656 if SAVE_FAILED_FITS_FILES: 

657 # save the image anyway 

658 afwWarpedImage.writeFits(afwWarpedImage2) 

659 print(f"Saved failed afw-warped image as: {afwWarpedImage2Path}") 

660 raise 

661 

662 

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

664 pass 

665 

666 

667def setup_module(module): 

668 lsst.utils.tests.init() 

669 

670 

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

672 lsst.utils.tests.init() 

673 unittest.main()