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# (http://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 <http://www.gnu.org/licenses/>. 

21 

22import unittest 

23 

24import os 

25import numpy as np 

26import astropy.io.fits 

27 

28import lsst.utils.tests 

29from lsst.daf.base import PropertyList 

30from lsst.geom import Box2I, Point2I, Extent2I, Point2D, Box2D, SpherePoint, degrees 

31from lsst.afw.geom import makeSkyWcs, Polygon 

32from lsst.afw.table import ExposureTable 

33from lsst.afw.image import (Image, Mask, Exposure, LOCAL, PARENT, MaskPixel, VariancePixel, 

34 ImageFitsReader, MaskFitsReader, MaskedImageFitsReader, ExposureFitsReader, 

35 Filter, FilterLabel, PhotoCalib, ApCorrMap, VisitInfo, TransmissionCurve, 

36 CoaddInputs, ExposureInfo, ExposureF) 

37from lsst.afw.image.utils import defineFilter 

38from lsst.afw.detection import GaussianPsf 

39from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

40 

41TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

42 

43 

44class FitsReaderTestCase(lsst.utils.tests.TestCase): 

45 

46 def setUp(self): 

47 self.dtypes = [np.dtype(t) for t in (np.uint16, np.int32, np.float32, np.float64)] 

48 self.bbox = Box2I(Point2I(2, 1), Extent2I(5, 7)) 

49 self.args = [ 

50 (), 

51 (Box2I(Point2I(3, 4), Extent2I(2, 1)),), 

52 (Box2I(Point2I(3, 4), Extent2I(2, 1)), PARENT), 

53 (Box2I(Point2I(1, 0), Extent2I(3, 2)), LOCAL), 

54 ] 

55 

56 def testImageFitsReader(self): 

57 for n, dtypeIn in enumerate(self.dtypes): 

58 with self.subTest(dtypeIn=dtypeIn): 

59 imageIn = Image(self.bbox, dtype=dtypeIn) 

60 imageIn.array[:, :] = np.random.randint(low=1, high=5, size=imageIn.array.shape) 

61 with lsst.utils.tests.getTempFilePath(".fits") as fileName: 

62 imageIn.writeFits(fileName) 

63 reader = ImageFitsReader(fileName) 

64 self.assertEqual(reader.readBBox(), self.bbox) 

65 self.assertEqual(reader.readDType(), dtypeIn) 

66 self.assertEqual(reader.fileName, fileName) 

67 for args in self.args: 

68 with self.subTest(args=args): 

69 array1 = reader.readArray(*args) 

70 image1 = reader.read(*args) 

71 subIn = imageIn.subset(*args) if args else imageIn 

72 self.assertEqual(dtypeIn, array1.dtype) 

73 self.assertTrue(np.all(subIn.array == array1)) 

74 self.assertEqual(subIn.getXY0(), reader.readXY0(*args)) 

75 self.assertImagesEqual(subIn, image1) 

76 for dtype2 in self.dtypes[n:]: 

77 for args in self.args: 

78 with self.subTest(dtype2=dtype2, args=args): 

79 subIn = imageIn.subset(*args) if args else imageIn 

80 array2 = reader.readArray(*args, dtype=dtype2) 

81 image2 = reader.read(*args, dtype=dtype2) 

82 self.assertEqual(dtype2, array2.dtype) 

83 self.assertTrue(np.all(subIn.array == array2)) 

84 self.assertEqual(subIn.getXY0(), reader.readXY0(*args)) 

85 self.assertEqual(subIn.getBBox(), image2.getBBox()) 

86 self.assertTrue(np.all(image2.array == array2)) 

87 

88 def testMaskFitsReader(self): 

89 maskIn = Mask(self.bbox, dtype=MaskPixel) 

90 maskIn.array[:, :] = np.random.randint(low=1, high=5, size=maskIn.array.shape) 

91 with lsst.utils.tests.getTempFilePath(".fits") as fileName: 

92 maskIn.writeFits(fileName) 

93 reader = MaskFitsReader(fileName) 

94 self.assertEqual(reader.readBBox(), self.bbox) 

95 self.assertEqual(reader.readDType(), MaskPixel) 

96 self.assertEqual(reader.fileName, fileName) 

97 for args in self.args: 

98 with self.subTest(args=args): 

99 array = reader.readArray(*args) 

100 mask = reader.read(*args) 

101 subIn = maskIn.subset(*args) if args else maskIn 

102 self.assertEqual(MaskPixel, array.dtype) 

103 self.assertTrue(np.all(subIn.array == array)) 

104 self.assertEqual(subIn.getXY0(), reader.readXY0(*args)) 

105 self.assertImagesEqual(subIn, mask) 

106 

107 def checkMultiPlaneReader(self, reader, objectIn, fileName, dtypesOut, compare): 

108 """Test operations common to MaskedImageFitsReader and ExposureFitsReader. 

109 

110 Parameters 

111 ---------- 

112 reader : `MaskedImageFitsReader` or `ExposureFitsReader` instance 

113 Reader object to test. 

114 objectIn : `MaskedImage` or `Exposure` 

115 Object originally saved, to compare against. 

116 fileName : `str` 

117 Name of the file the reader is reading. 

118 dtypesOut : sequence of `numpy.dype` 

119 Compatible image pixel types to try to read in. 

120 compare : callable 

121 Callable that compares objects of the same type as objectIn and 

122 asserts if they are not equal. 

123 """ 

124 dtypeIn = objectIn.image.dtype 

125 self.assertEqual(reader.readBBox(), self.bbox) 

126 self.assertEqual(reader.readImageDType(), dtypeIn) 

127 self.assertEqual(reader.readMaskDType(), MaskPixel) 

128 self.assertEqual(reader.readVarianceDType(), VariancePixel) 

129 self.assertEqual(reader.fileName, fileName) 

130 for args in self.args: 

131 with self.subTest(args=args): 

132 object1 = reader.read(*args) 

133 subIn = objectIn.subset(*args) if args else objectIn 

134 self.assertEqual(object1.image.array.dtype, dtypeIn) 

135 self.assertEqual(object1.mask.array.dtype, MaskPixel) 

136 self.assertEqual(object1.variance.array.dtype, VariancePixel) 

137 self.assertImagesEqual(subIn.image, reader.readImage(*args)) 

138 self.assertImagesEqual(subIn.mask, reader.readMask(*args)) 

139 self.assertImagesEqual(subIn.variance, reader.readVariance(*args)) 

140 compare(subIn, object1) 

141 for dtype2 in dtypesOut: 

142 with self.subTest(dtype2=dtype2, args=args): 

143 object2 = reader.read(*args, dtype=dtype2) 

144 image2 = reader.readImage(*args, dtype=dtype2) 

145 self.assertEqual(object2.image.array.dtype, dtype2) 

146 self.assertEqual(object2.mask.array.dtype, MaskPixel) 

147 self.assertEqual(object2.variance.array.dtype, VariancePixel) 

148 self.assertImagesEqual(subIn.image, Image(image2, deep=True, dtype=dtypeIn)) 

149 self.assertImagesEqual(image2, object2.image) 

150 compare(subIn, object2) 

151 

152 def checkMaskedImageFitsReader(self, exposureIn, fileName, dtypesOut): 

153 """Test MaskedImageFitsReader. 

154 

155 Parameters 

156 ---------- 

157 exposureIn : `Exposure` 

158 Object originally saved, to compare against. 

159 fileName : `str` 

160 Name of the file the reader is reading. 

161 dtypesOut : sequence of `numpy.dype` 

162 Compatible image pixel types to try to read in. 

163 """ 

164 reader = MaskedImageFitsReader(fileName) 

165 self.checkMultiPlaneReader(reader, exposureIn.maskedImage, fileName, dtypesOut, 

166 compare=self.assertMaskedImagesEqual) 

167 

168 def checkExposureFitsReader(self, exposureIn, fileName, dtypesOut): 

169 """Test ExposureFitsReader. 

170 

171 Parameters 

172 ---------- 

173 exposureIn : `Exposure` 

174 Object originally saved, to compare against. 

175 fileName : `str` 

176 Name of the file the reader is reading. 

177 dtypesOut : sequence of `numpy.dype` 

178 Compatible image pixel types to try to read in. 

179 """ 

180 reader = ExposureFitsReader(fileName) 

181 self.assertIn('EXPINFO_V', reader.readMetadata().toDict(), "metadata is automatically versioned") 

182 reader.readMetadata().remove('EXPINFO_V') 

183 # ensure EXTNAMEs can be read and make sense 

184 extnames = set(('PRIMARY', 'IMAGE', 'MASK', 'VARIANCE', 'ARCHIVE_INDEX', 

185 'Detector', 'TransformMap', 'TransformPoint2ToPoint2', 

186 'FilterLabel', 'SkyWcs', 'ApCorrMap', 'PhotoCalib', 

187 'ChebyshevBoundedField', 'CoaddInputs', 'GaussianPsf', 

188 'Polygon', 'VisitInfo')) 

189 with astropy.io.fits.open(fileName) as astropyReadFile: 

190 for hdu in astropyReadFile: 

191 self.assertIn(hdu.name, extnames) 

192 self.assertIn('EXTNAME', reader.readMetadata().toDict(), "EXTNAME is added upon writing") 

193 reader.readMetadata().remove('EXTNAME') 

194 self.assertGreaterEqual(reader.readSerializationVersion(), 0) 

195 self.assertEqual(exposureIn.getMetadata().toDict(), reader.readMetadata().toDict()) 

196 self.assertWcsAlmostEqualOverBBox(exposureIn.getWcs(), reader.readWcs(), self.bbox, 

197 maxDiffPix=0, maxDiffSky=0*degrees) 

198 self.assertWcsAlmostEqualOverBBox(exposureIn.getWcs(), 

199 reader.readComponent(ExposureInfo.KEY_WCS), 

200 self.bbox, 

201 maxDiffPix=0, maxDiffSky=0*degrees) 

202 self.assertEqual(exposureIn.getFilter(), reader.readFilter()) 

203 self.assertEqual(exposureIn.getFilterLabel(), reader.readFilterLabel()) 

204 self.assertEqual(exposureIn.getFilterLabel(), 

205 reader.readComponent(ExposureInfo.KEY_FILTER)) 

206 self.assertEqual(exposureIn.getPhotoCalib(), reader.readPhotoCalib()) 

207 self.assertEqual(exposureIn.getPhotoCalib(), 

208 reader.readComponent(ExposureInfo.KEY_PHOTO_CALIB)) 

209 center = exposureIn.getBBox().getCenter() 

210 self.assertImagesEqual(exposureIn.getPsf().computeImage(center), 

211 reader.readPsf().computeImage(center)) 

212 self.assertImagesEqual(exposureIn.getPsf().computeImage(center), 

213 reader.readComponent('PSF').computeImage(center)) 

214 self.assertEqual(exposureIn.getInfo().getValidPolygon(), reader.readValidPolygon()) 

215 self.assertEqual(exposureIn.getInfo().getValidPolygon(), 

216 reader.readComponent(ExposureInfo.KEY_VALID_POLYGON)) 

217 self.assertCountEqual(exposureIn.getInfo().getApCorrMap(), reader.readApCorrMap()) 

218 self.assertCountEqual(exposureIn.getInfo().getApCorrMap(), 

219 reader.readComponent(ExposureInfo.KEY_AP_CORR_MAP)) 

220 self.assertEqual(exposureIn.getInfo().getVisitInfo().getExposureTime(), 

221 reader.readVisitInfo().getExposureTime()) 

222 point = Point2D(2.3, 3.1) 

223 wavelengths = np.linspace(4000, 5000, 5) 

224 self.assertFloatsEqual(exposureIn.getInfo().getTransmissionCurve().sampleAt(point, wavelengths), 

225 reader.readTransmissionCurve().sampleAt(point, wavelengths)) 

226 # Note: readComponent(ExposureInfo.KEY_TRANSMISSION_CURVE) returns a generic Storable 

227 # rather than a TransmissionCurve object. 

228 

229 # Because we persisted the same instances, we should get back the same 

230 # instances for *archive* components, and hence equality comparisons 

231 # should work even if it just amounts to C++ pointer equality. 

232 record = reader.readCoaddInputs().ccds[0] 

233 self.assertEqual(record.getWcs(), reader.readWcs()) 

234 self.assertEqual(record.getPsf(), reader.readPsf()) 

235 self.assertEqual(record.getValidPolygon(), reader.readValidPolygon()) 

236 self.assertEqual(record.getApCorrMap(), reader.readApCorrMap()) 

237 self.assertEqual(record.getPhotoCalib(), reader.readPhotoCalib()) 

238 self.assertEqual(record.getDetector(), reader.readDetector()) 

239 self.checkMultiPlaneReader( 

240 reader, exposureIn, fileName, dtypesOut, 

241 compare=lambda a, b: self.assertMaskedImagesEqual(a.maskedImage, b.maskedImage) 

242 ) 

243 

244 def testCompressedSinglePlaneExposureFitsReader(self): 

245 """Test that a compressed single plane image can be read as exposure. 

246 """ 

247 uncompressed_file = os.path.join(TESTDIR, "data", "ticketdm26260.fits") 

248 compressed_file = os.path.join(TESTDIR, "data", "ticketdm26260.fits.fz") 

249 uncompressed = ExposureFitsReader(uncompressed_file).read() 

250 compressed = ExposureFitsReader(compressed_file).read() 

251 

252 self.assertMaskedImagesEqual(uncompressed.maskedImage, compressed.maskedImage) 

253 

254 def testMultiPlaneFitsReaders(self): 

255 """Run tests for MaskedImageFitsReader and ExposureFitsReader. 

256 """ 

257 metadata = PropertyList() 

258 metadata.add("FIVE", 5) 

259 metadata.add("SIX", 6.0) 

260 wcs = makeSkyWcs(Point2D(2.5, 3.75), SpherePoint(40.0*degrees, 50.0*degrees), 

261 np.array([[1E-5, 0.0], [0.0, -1E-5]])) 

262 defineFilter("test_readers_filter", lambdaEff=470.0) 

263 calib = PhotoCalib(2.5E4) 

264 psf = GaussianPsf(21, 21, 8.0) 

265 polygon = Polygon(Box2D(self.bbox)) 

266 apCorrMap = ApCorrMap() 

267 visitInfo = VisitInfo(exposureTime=5.0) 

268 transmissionCurve = TransmissionCurve.makeIdentity() 

269 coaddInputs = CoaddInputs(ExposureTable.makeMinimalSchema(), ExposureTable.makeMinimalSchema()) 

270 detector = DetectorWrapper().detector 

271 record = coaddInputs.ccds.addNew() 

272 record.setWcs(wcs) 

273 record.setPhotoCalib(calib) 

274 record.setPsf(psf) 

275 record.setValidPolygon(polygon) 

276 record.setApCorrMap(apCorrMap) 

277 record.setVisitInfo(visitInfo) 

278 record.setTransmissionCurve(transmissionCurve) 

279 record.setDetector(detector) 

280 for n, dtypeIn in enumerate(self.dtypes): 

281 with self.subTest(dtypeIn=dtypeIn): 

282 exposureIn = Exposure(self.bbox, dtype=dtypeIn) 

283 shape = exposureIn.image.array.shape 

284 exposureIn.image.array[:, :] = np.random.randint(low=1, high=5, size=shape) 

285 exposureIn.mask.array[:, :] = np.random.randint(low=1, high=5, size=shape) 

286 exposureIn.variance.array[:, :] = np.random.randint(low=1, high=5, size=shape) 

287 exposureIn.setMetadata(metadata) 

288 exposureIn.setWcs(wcs) 

289 exposureIn.setFilter(Filter("test_readers_filter")) 

290 exposureIn.setFilterLabel(FilterLabel(physical="test_readers_filter")) 

291 exposureIn.setPhotoCalib(calib) 

292 exposureIn.setPsf(psf) 

293 exposureIn.getInfo().setValidPolygon(polygon) 

294 exposureIn.getInfo().setApCorrMap(apCorrMap) 

295 exposureIn.getInfo().setVisitInfo(visitInfo) 

296 exposureIn.getInfo().setTransmissionCurve(transmissionCurve) 

297 exposureIn.getInfo().setCoaddInputs(coaddInputs) 

298 exposureIn.setDetector(detector) 

299 with lsst.utils.tests.getTempFilePath(".fits") as fileName: 

300 exposureIn.writeFits(fileName) 

301 self.checkMaskedImageFitsReader(exposureIn, fileName, self.dtypes[n:]) 

302 self.checkExposureFitsReader(exposureIn, fileName, self.dtypes[n:]) 

303 

304 def test31035(self): 

305 """Test that illegal values in the header can be round-tripped.""" 

306 with lsst.utils.tests.getTempFilePath(".fits") as fileName: 

307 exp = ExposureF(width=100, height=100) 

308 md = exp.getMetadata() 

309 md['BORE-RA'] = 'NaN' 

310 md['BORE-DEC'] = 'NaN' 

311 md['BORE-AZ'] = 'NaN' 

312 md['BORE-ALT'] = 'NaN' 

313 md['BORE-AIRMASS'] = 'NaN' 

314 md['BORE-ROTANG'] = 'NaN' 

315 md['OBS-LONG'] = 'NaN' 

316 md['OBS-LAT'] = 'NaN' 

317 md['OBS-ELEV'] = 'NaN' 

318 md['AIRTEMP'] = 'NaN' 

319 md['AIRPRESS'] = 'NaN' 

320 md['HUMIDITY'] = 'NaN' 

321 

322 exp.writeFits(fileName) 

323 

324 _ = ExposureF.readFits(fileName) 

325 

326 

327class TestMemory(lsst.utils.tests.MemoryTestCase): 

328 pass 

329 

330 

331def setup_module(module): 

332 lsst.utils.tests.init() 

333 

334 

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

336 import sys 

337 setup_module(sys.modules[__name__]) 

338 unittest.main()