This tutorial describes how to read in, edit a FITS header, and then write
it back out to disk. For this example we're going to change the OBJECT
keyword.
This tutorial uses astropy.io.fits,
which was formerly released separately as pyfits
. If you have used
pyfits
to manipulate FITS files then you may already be familiar
with the features and syntax of the package. We start by importing
the subpackage into our local namespace, and allows us to access the
functions and classes as fits.name_of_function()
. For example,
to access the getdata()
function, we don't have to do
astropy.io.fits.getdata()
and can instead simple use fits.getdata()
.
You may run across old documentation or tutorials that use the name
pyfits
. Such examples will begin with import pyfits
and then
the command fits.getdata()
(for example) would be written as
pyfits.getdata()
.
from astropy.io import fits
astropy.io.fits
provides a lot of flexibility for reading FITS
files and headers, but most of the time the convenience functions are
the easiest way to access the data. fits.info()
displays information about the FITS file:
fits.info("input_file.fits")
Filename: input_file.fits No. Name Type Cards Dimensions Format 0 PRIMARY PrimaryHDU 7 (100, 100) float64 1 ImageHDU 7 (128, 128) float64
fits.getheader("input_file.fits")
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T OBJECT = 'KITTEN '
You can display the value of specified KEYWORD, such as OBJECT:
fits.getval('input_file.fits', 'OBJECT')
'KITTEN'
You can change the value and the comment:
fits.setval('input_file.fits', 'OBJECT',value='M31',comment='updated object')
fits.getheader("input_file.fits")
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T OBJECT = 'M31 ' / updated object
If you want to insert a new KEYWORD 'NEWKEY' before the KEYWORD 'OBJECT':
fits.setval('input_file.fits', 'NEWKEY',value='new1',comment='test new card',before='OBJECT')
fits.getheader("input_file.fits")
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T NEWKEY = 'new1 ' / test new card OBJECT = 'M31 ' / updated object
Or insert keyword 'NEWKEY2' it before the position 7:
fits.setval('input_file.fits', 'NEWKEY2',value='new2',comment='test new card 2',before=7)
fits.getheader("input_file.fits")
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T NEWKEY = 'new1 ' / test new card NEWKEY2 = 'new2 ' / test new card 2 OBJECT = 'M31 ' / updated object
Delete keyword 'NEWKEY2':
fits.delval('input_file.fits','NEWKEY2')
fits.getheader("input_file.fits")
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T NEWKEY = 'new1 ' / test new card OBJECT = 'M31 ' / updated object
Now let's do it without overwriting the input fits file
data, header = fits.getdata("input_file2.fits",header=True)
fits.getdata()
reads just the
data from a FITS file, but with the header=True
keyword argument will
also read the header.
data, header
(array([[ 0.86940162, 0.04857379, 0.14382985, ..., 0.0731545 , 0.79737877, 0.66019435], [ 0.20601479, 0.13230429, 0.43245126, ..., 0.39470099, 0.39051394, 0.05887787], [ 0.98765689, 0.76027805, 0.25611188, ..., 0.1783573 , 0.96858554, 0.47004561], ..., [ 0.9602551 , 0.70386557, 0.38782809, ..., 0.38015734, 0.0727644 , 0.74643668], [ 0.25389285, 0.25654266, 0.9218713 , ..., 0.35035566, 0.70537618, 0.32109915], [ 0.6253657 , 0.4688868 , 0.33427645, ..., 0.87217918, 0.57015396, 0.53144525]]), SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T OBJECT = 'KITTEN ' )
Let's add OBJECT again and give it the value 'M31'. If OBJECT was already existing it would have replaced the value by 'M31':
header['OBJECT'] = 'M31'
header
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T OBJECT = 'M31 '
del header['OBJECT']
header
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T
It is also possible to update both the value and comment associated with a keyword by assigning them as a tuple:
header['OBJECT'] = ('M31','the new object')
header
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 EXTEND = T OBJECT = 'M31 ' / the new object
Finally, we have to write out the FITS file. Again, the convenience function for this is the most useful command to remember:
fits.writeto('output_file.fits', data, header)
That's it; you're done! We can check that it worked by displaying the header only
#hdu_number = 0
#fits.getheader('output_file.fits', hdu_number)
fits.getheader('output_file.fits')
SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 100 NAXIS2 = 100 OBJECT = 'M31 ' / the new object
fits.getheader()
is a dedicated function for reading just the header but fits.getdata()
can get both the data and the header, so it is a useful command to remember. Since the primary HDU of a FITS file must contain image data, the data is now stored in a numpy array. The header is stored in an object that acts like a standard Python dictionary.
Two common more complicated cases are worth mentioning (but if your needs are much more complex, you should consult the full documentation).
The first complication is that the FITS file you're examining and editing might have multiple HDU's (extensions), in which case you can specify the extension like this:
data,header = fits.getdata("input_file2.fits", ext=1, header=True)
#data, header
This will get you the data and header associated with the index=1 extension
in the FITS file. Without specifying a number, getdata() will get the
0th extension (equivalent to saying ext=0
).
Another useful tip is if you want to overwrite an existing FITS
file. By default, writeto() won't let you do this, so you need to
explicitly give it permission using the clobber
keyword argument:
fits.writeto('output_file.fits', data, header, clobber=True)