Edit a FITS header

Authors

Adrian Price-Whelan, Adam Ginsburg, Stephanie T. Douglas, Kelle Cruz

Learning Goals

  • Read a FITS file

  • Retrieve FITS header metadata

  • Edit the FITS header

  • Write the modified file as a FITS file

Keywords

FITS, file input/output

Summary

This tutorial describes how to read in and edit a FITS header, and then write

it back out to disk. For this example we're going to change the OBJECT

keyword.

In [1]:
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.getdata() reads only the

data from a FITS file, but with the header=True keyword argument will

also read the header.

In [2]:
data, header = fits.getdata("https://sciencedata.dk/public/6e3ed434c0fa43df906ce2b6d1ba9fc6/astropy-tutorials/tutorials/FITS-header/input_file.fits", header=True)

There is also a dedicated function for reading only the

header:

In [3]:
hdu_number = 0 # HDU means header data unit

fits.getheader('https://sciencedata.dk/public/6e3ed434c0fa43df906ce2b6d1ba9fc6/astropy-tutorials/tutorials/FITS-header/input_file.fits', hdu_number)
Out[3]:
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  '                                                            

But getdata() can get both the data and the header, so it's 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.

In [4]:
# But hdu_number = 0 is the PRIMARY HDU.How many HDUs are in this file?

fits_inf = fits.open("https://sciencedata.dk/public/6e3ed434c0fa43df906ce2b6d1ba9fc6/astropy-tutorials/tutorials/FITS-header/input_file.fits")

fits_inf.info() 

fits_inf[0].header
Filename: /home/sciencedata/.astropy/cache/download/url/c5492dfad5eed79f614ca4ce27bf2a13/contents
No.    Name      Ver    Type      Cards   Dimensions   Format
  0  PRIMARY       1 PrimaryHDU       7   (100, 100)   float64   
  1                1 ImageHDU         7   (128, 128)   float64   
Out[4]:
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  '                                                            

Using fits.open allows us to look more generally at our data. fits_inf[0].header gives us the same output as fits.getheader. What will you learn if you type fits_inf[1].header? Based on fits_inf.info() can you guess what will happen if you type fits_inf[2].header?

Now let's change the header to give it the correct object:

In [5]:
header['OBJECT'] = "M31"

Finally, we have to write out the FITS file. Again, the convenience

function for this is the most useful command to remember:

In [6]:
fits.writeto('output_file.fits', data, header, overwrite=True)

That's it; you're done!

If you wish to keep the output file beyond the life of the pod this notebook is running in, you can write it back to sciencedata with the following (change tmp to some folder in your ScienceData homedir).

In [11]:
import requests
with open('output_file.fits', mode='rb') as f:
    requests.put('https://sciencedata/files/tmp/output_file.fits', data=f)
    f.close()

Two common and more complicated cases are worth mentioning (but if your needs

are much more complex, you should consult the full documentation http://docs.astropy.org/en/stable/io/fits/).

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:

In [12]:
data1, header1 = fits.getdata("https://sciencedata.dk/public/6e3ed434c0fa43df906ce2b6d1ba9fc6/astropy-tutorials/tutorials/FITS-header/input_file.fits", ext=1, header=True)

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:

In [13]:
fits.writeto('output_file.fits', data, header, overwrite=True)

A final example is if you want to make a small change to a FITS file, like updating a header keyword, but you don't want to read in and write out the whole file, which can take a while. Instead you can use the mode='update' read mode to do this. This requires the file to be avalable locally though, so we first download it to local/volatile pod storage.

In [14]:
import os
os.system("curl -LO https://sciencedata.dk/public/6e3ed434c0fa43df906ce2b6d1ba9fc6/astropy-tutorials/tutorials/FITS-header/input_file.fits")
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  213k  100  213k    0     0   119k      0  0:00:01  0:00:01 --:--:--  165k
Out[14]:
0
In [15]:
with fits.open('input_file.fits', mode='update') as filehandle:
    filehandle[0].header['MYHDRKW'] = "My Header Keyword"

Again, you can write it back to sciencedata (change tmp to some folder in your ScienceData homedir).

In [16]:
with open('output_file.fits', mode='rb') as f:
    requests.put('https://sciencedata/files/tmp/output_file.fits', data=f)
    f.close()

Exercise

Read in the file you just wrote and add three header keywords:

  1. 'RA' for the Right Ascension of M31

  2. 'DEC' for the Declination of M31

  3. 'RADECSRC' with text indicating where you found the RA/Dec (web URL, textbook name, your photographic memory, etc.)

Then write the updated header back out to a new file:

In [ ]: