1# -*- coding: utf-8 -*-
2# imageio is distributed under the terms of the (new) BSD License.
3
4"""Read FITS files.
5
6Backend Library: `Astropy <https://www.astropy.org/>`_
7
8.. note::
9 To use this plugin you have to install its backend::
10
11 pip install imageio[fits]
12
13Flexible Image Transport System (FITS) is an open standard defining a
14digital file format useful for storage, transmission and processing of
15scientific and other images. FITS is the most commonly used digital
16file format in astronomy.
17
18
19Parameters
20----------
21cache : bool
22 If the file name is a URL, `~astropy.utils.data.download_file` is used
23 to open the file. This specifies whether or not to save the file
24 locally in Astropy's download cache (default: `True`).
25uint : bool
26 Interpret signed integer data where ``BZERO`` is the
27 central value and ``BSCALE == 1`` as unsigned integer
28 data. For example, ``int16`` data with ``BZERO = 32768``
29 and ``BSCALE = 1`` would be treated as ``uint16`` data.
30
31 Note, for backward compatibility, the kwarg **uint16** may
32 be used instead. The kwarg was renamed when support was
33 added for integers of any size.
34ignore_missing_end : bool
35 Do not issue an exception when opening a file that is
36 missing an ``END`` card in the last header.
37checksum : bool or str
38 If `True`, verifies that both ``DATASUM`` and
39 ``CHECKSUM`` card values (when present in the HDU header)
40 match the header and data of all HDU's in the file. Updates to a
41 file that already has a checksum will preserve and update the
42 existing checksums unless this argument is given a value of
43 'remove', in which case the CHECKSUM and DATASUM values are not
44 checked, and are removed when saving changes to the file.
45disable_image_compression : bool, optional
46 If `True`, treats compressed image HDU's like normal
47 binary table HDU's.
48do_not_scale_image_data : bool
49 If `True`, image data is not scaled using BSCALE/BZERO values
50 when read.
51ignore_blank : bool
52 If `True`, the BLANK keyword is ignored if present.
53scale_back : bool
54 If `True`, when saving changes to a file that contained scaled
55 image data, restore the data to the original type and reapply the
56 original BSCALE/BZERO values. This could lead to loss of accuracy
57 if scaling back to integer values after performing floating point
58 operations on the data.
59
60"""
61
62from ..core import Format
63
64_fits = None # lazily loaded
65
66
67def load_lib():
68 global _fits
69 try:
70 from astropy.io import fits as _fits
71 except ImportError:
72 raise ImportError(
73 "The FITS format relies on the astropy package."
74 "Please refer to http://www.astropy.org/ "
75 "for further instructions."
76 )
77 return _fits
78
79
80class FitsFormat(Format):
81 """See :mod:`imageio.plugins.fits`"""
82
83 def _can_read(self, request):
84 # We return True if ext matches, because this is the only plugin
85 # that can. If astropy is not installed, a useful error follows.
86 return request.extension in self.extensions
87
88 def _can_write(self, request):
89 # No write support
90 return False
91
92 # -- reader
93
94 class Reader(Format.Reader):
95 def _open(self, cache=False, **kwargs):
96 if not _fits:
97 load_lib()
98 hdulist = _fits.open(self.request.get_file(), cache=cache, **kwargs)
99
100 self._index = []
101 allowed_hdu_types = (_fits.ImageHDU, _fits.PrimaryHDU, _fits.CompImageHDU)
102 for n, hdu in zip(range(len(hdulist)), hdulist):
103 if isinstance(hdu, allowed_hdu_types):
104 # Ignore (primary) header units with no data (use '.size'
105 # rather than '.data' to avoid actually loading the image):
106 if hdu.size > 0:
107 self._index.append(n)
108 self._hdulist = hdulist
109
110 def _close(self):
111 self._hdulist.close()
112
113 def _get_length(self):
114 return len(self._index)
115
116 def _get_data(self, index):
117 # Get data
118 if index < 0 or index >= len(self._index):
119 raise IndexError("Index out of range while reading from fits")
120 im = self._hdulist[self._index[index]].data
121 # Return array and empty meta data
122 return im, {}
123
124 def _get_meta_data(self, index):
125 # Get the meta data for the given index
126 raise RuntimeError("The fits format does not support meta data.")