1. What are FITS images?
FITS stands for Flexible Image Transport System. The complete documentation can be found on this webpage hosted by NASA.
In brief, FITS is a binary format to store astronomical data (either image(s) or certain types of table(s)). The reason why we need it is that our files are usually huge, making the ASCII format impractical. In addition, there usually needs some meta-data to explain how the data were taken and were processed (e.g., the angular resolution or spectral resolution of the image(s) stored in a specific FITS file, the date of the observations, etc), such that the data file can be self-contained without depending on external documentation.
In light of this, a FITS file usually contains two parts:
- one or more binary data table
- one or more header files that contain the necessary information to understand the data.
Since the binary data are not human readable, we cannot handle the FITS files with a text editor (e.g., vim) or other commercial or open resource software like MS Excel or Open Office. Here I provide some simple tips for manipulating FITS images that can be useful to radio astronomers. If you are a graduate student or researcher, I recommend at least installing the ds9, CASA, Miriad software and the Astropy and APLpy Python packages. If you have access to IDL, you should also install the IDL Astronomy User’s Library.
If you know something that is very useful, please recommend it to me.
2. (Pre)Viewers
The following software may be useful for a quick preview of your FITS images or tables, which are particularly handy during a casual discussion with other colleagues. The links to them are provided. They are all reasonably easy to install.
2.1 ds9
For any astronomer, ds9 would be one of the most handy FITS image viewers. It is trivial to install no matter whether you are using Linux, Windows, or Mac OSX. There is no reason not using it. After installation, you can launch it by double-clicking its icon, otherwise, by typing
> ds9
in a Linux or OSX comand line.
2.2 fv
fv is a simple graphical interface for you to preview FITS tables. It can also export data to ASCII format. It is also very easy to install.
2.3 CASA-Viewer and CARTA
If you are a radio astronomer, it is most likely that you have installed the CASA Software Package. The viewer
function of CASA is handy when you are previewing the radio interferometric images, although it crashes once in a while. It may be replaced by CARTA at some point although I have not adapted myself to it. If you are new to astronomy, I recommend beginning with CARTA. When your image file is large (e.g., >100 GB), presently, only CARTA allows you to preview the images reasonably smoothly.
2.4 Karma kvis
We used the karma package a lot in the old days. The kvis
function is very useful when you are interactively examining the gas kinematics of an astronomical object with a spectral line image cube. I myself still use it once in a while although not everybody uses it nowadays.
3. Converting images in various formats
3.1 Miriad
After sourcing the Miriad software package, which is often used in the imaging of the SMA data, you can use the following commands to convert between the Miriad and FITS format images:
# The file names in in='' and out='' (string variables)
# need to be replaced with your filenames
# converting from Miriad format to FITS format
fits in='mytarget.image.miriad' op=xyout out='mytarget.image.fits'
# converting from FITS to Miriad format
fits in='mytarget.image.fits' op=xyin out='mytarget.image.miriad'
They can be executed either by directly entering in a Linux/OSX command line or by executing as a BASH or CSH script. We can then program other software languages to manipulate or analyze the FITS format images, such as C/Fortran, IDL, or Python. For online documentation of the Miriad tasks please check the Miriad task index.
You can regrid two Miriad images or image cubes to have identical spatial or spectral coordinates using the following command:
# The '\' symbols denotes line-breaking
regrid in='image1.image.miriad' \
out='image1.regrided.image.miriad' \
axes=1,2 \
tin='image2.image.miriad'
This is useful when you want to align the pixels of two images before any further analyses. In this example, axes=1,2
means that we are regridding the two spatial axes. When you want to also regrid the spectral axis, you can use axes=1,2,3
. It loads ‘image1.image.miriad’ and regrids it according to the header information in ‘image2.image.miriad’, and then outout the regridded image1 as a new file ‘image1.image.miriad’.
3.2 CASA
After launching CASA, you can type viewer
in the CASA command line environment to launch the GUI image previewer.
You can use the following CASA Software Package commands to convert between the CASA and FITS format images:
# The file names (string variables) in imagename and fitsimage need
# to be replaced with your filenames.
# converting from CASA format to FITS format
exportfits(
imagename = 'mytarget.image',
fitsimage = 'mytarget.image.fits',
overwrite = True
)
# converting from CASA format to FITS format
importfits(
imagename = 'mytarget.image',
fitsimage = 'mytarget.image.fits',
overwrite = True
)
# If your CASA image have redundant spectral or polarization dimension,
# and if you want to drop those redundant dimension, you can include the
# relevant options (dropstokes or dropdeg) like:
exportfits(
imagename = 'mytarget.image',
fitsimage = 'mytarget.image.fits',
overwrite = True, dropdeg = True, dropstokes = True
)
# This can be very useful since not all Python package can recognize
# high-dimensional FITS images.
To crop a sub-image in CASA, you can use the CASA imsubimage
task like:
# # box[[x1, y1], [x2, y2]] region
subimregion = "box[[1500pix, 1500pix], [4499pix, 4499pix]]"
img_name = 'mytarget'
imsubimage(imagename = img_name + '.image',
outfile = img_name + '.subim.image',
region = subimregion
)
We also often use the CASA imregrid
and imsmooth
tasks to regrid a set of CASA format images to the same coordiante system and then smooth same synthesized beam size before exporting to FITS files for further analyses. Example of these commands are:
imregrid(
# name of the image to be regrided
imagename = imagename,
# an existing image to serve as a template of coordiante grids
template = template_imagename,
# output filename
output = imagename + '.regrid',
# options in the regridding
axes = [0, 1], interpolation = 'linear',
# whether or not overwritting the output file
overwrite = True
)
imsmooth(
# name of the image to be smoothed
imagename = image,
# Shape of the smoothing kernel or final synthesized beam.
# This example specified that the final synthesized beam should
# be a gaussian beam with 0.53 arcsecond FWHM in the major and minor axes
kernel = 'gauss',
major = '0.53arcsec', minor = '0.53arcsec', pa = '0deg',
targetres = True,
# output file name
outfile = image + '.smooth',
# whether or not overwritting the output file
overwrite = True
)
These CASA commands can be executed either by directly entering in a Linux/OSX command line or by executing as a Python3 script.
To see a full list of available CASA tasks, please check this webpage. For online documentation of the CASA tasks please check the CASA docs. In the CASA command line, you can also check the documentation and input variables by entering help taskname
and inp
, e.g., in this case:
# check the documentation for importfits/exportfits
CASA> help importfits
CASA> help exportfits
# load the task importfits
CASA> tget importfits
# check the input variable of the loaded task
CASA> inp
After obtaining the FITS images, we can then program other software languages to manipulate or analyze the FITS format images, such as C/Fortran, IDL, or Python.
4. Working with FITS using software languages
If you are programming C or Fortran, then CFITSIO is the package you need. I do not think this is the way you will be going but am providing a reference to it just in case (e.g., sometimes you may need to check other people’s programs which utilized this package).
4.1 Python
If you are analyzing images, you might build a conda environment by the following Linux/OSX command line commands:
conda create --name astroimgAna python=3.9
pip install --upgrade pip
pip install jupyter
pip install astropy scipy
pip install jupyter matplotlib h5py aplpy pyregion PyAVM healpy
pip install pandas
In 2023, after updating astropy to version >5.0, it may become impossible to install aplpy successfully. If that is what happens to you, you can try the following command:
conda create --name test python=3.7
pip install --upgrade pip
pip install numpy
pip install astropy==4.3.1
pip install aplpy==2.0.3
pip install jupyter
pip install scipy
pip install matplotlib h5py
conda install -c conda-forge pyregion
pip install PyAVM healpy
pip install pandas
I often make most of the following imports:
import os
import numpy as np
from astropy.io.fits import getdata
from astropy import wcs
from astropy.io import fits
from astropy import units as u
from astropy import constants as con
from astropy.coordinates import SkyCoord
import matplotlib
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib
import aplpy
matplotlib.use('PDF')
The following code is an example of importing FITS image as a Header Data Unit (HDU) and extracting certain header information in Python:
intensitymap = 'mytarget_StokesI.fits'
intensity_scale = 1000.0
if_success = False
try:
# importing FITS image to a HDU
Ihdu = fits.open(intensitymap)
# editing the FITS image by multiplying a scaling factor
Ihdu[0].data = Ihdu[0].data * intensity_scale
if_success = True
except:
print('Unable to read the intensity FITS image. Please double check the image file.')
print(intensitymap)
if ( if_success == True ):
# Reading FITS header
try:
naxis1 = Ihdu[0].header['naxis1']
naxis2 = Ihdu[0].header['naxis2']
crval1 = Ihdu[0].header['crval1']
crpix1 = Ihdu[0].header['crpix1']
cdelt1 = Ihdu[0].header['cdelt1']
crval2 = Ihdu[0].header['crval2']
crpix2 = Ihdu[0].header['crpix2']
cdelt2 = Ihdu[0].header['cdelt2']
hduwcs = wcs.WCS( Ihdu[0].header)
except:
print( 'Warning. No coordinate headers' )
try:
bmaj = Ihdu[0].header['bmaj']
bmin = Ihdu[0].header['bmin']
bpa = Ihdu[0].header['bpa']
except:
print('Warnning. No header for synthesized beam size')
After having these header information, we can make conversion between the world coordiante system (i.e., astronomical coordinate systems) and the pixel coordinate system using the some methods of astropy.wcs. The following example convert the central pixel in the image to the R.A. and Decl. values and then convert them back ot the x and y pixel numbers:
xpix_temp = int(round( naxis1/2.0 ))
ypix_temp = int(round( naxis2/2.0 ))
world = hduwcs.wcs_pix2world([ [xpix_temp, ypix_temp] ], 0)
ra_center = world[0][0]
dec_center = world[0][1]
pixcrd = hduwcs.wcs_world2pix([ [ra_center, dec_center] ], 0)
ra_center_pix = pixcrd[0][0]
dec_center_pix = pixcrd[0][1]
The value of a certain pixel in a (two dimensional image) is simply:
value = Ihdu[0].data[ypix][xpix]
This allows you to analyze the image, for example, measuring aperature photometry.
The (two-dimensional) image can be output as a plot using the APLpy commands like:
figsize = [9.0, 9.0]
cmap = 'viridis'
plot_ticks = True
tick_font = 25
tick_ypad = 0
plot_colorbar = True
colorbar_location = 'top'
colorbar_width = 0.3
colorbar_pad = 0.3
colorbar_font = 15
colorbar_label = 'Colorbar'
colorbar_labelpad = 0.3
colorbar_labelfont = 20
width = 1.0
height = 1.0
plot_scalebar = True
distance = 140.0
scalebar_size = 100.0
scalebar_text = '100 au'
scalebar_color = (0,0,0)
scalebar_font = 25.0
scalebar_linewidth = 3.0
plot_beam = True
beam_color = 'black'
if_plot = False
try:
self.fig = aplpy.FITSFigure(Ihdu, figsize=(figsize[0], figsize[1]))
if_plot = True
except:
print('Unable to load or plot hdu. Please double check the image file.')
if ( if_plot == True ):
vmax = np.nanmax(Ihdu[0].data)*1.2
vmin = 0.0
fig.show_colorscale(
vmax = vmax, vmin = vmin,
interpolation = 'bicubic',
cmap = cmap
)
# ticks
fig.tick_labels.set_font(size=tick_font)
fig.axis_labels.set_font(size=tick_font)
fig.axis_labels.set_ypad(tick_ypad)
if (plot_ticks != True):
fig.axis_labels.hide_x()
fig.axis_labels.hide_y()
fig.tick_labels.hide_x()
fig.tick_labels.hide_y()
# recentering
if ( (ra_center != 0) or (dec_center !=0) ):
print('recentering')
fig.recenter(ra_center, dec_center, width=width, height=height)
# plot color bar
if (plot_colorbar == True):
fig.add_colorbar()
fig.colorbar.show()
fig.colorbar.set_location(colorbar_location)
fig.colorbar.set_width(colorbar_width)
fig.colorbar.set_pad(colorbar_pad)
fig.colorbar.set_font(size=colorbar_font, weight='medium', \
stretch='normal', family='sans-serif', \
style='normal', variant='normal')
fig.colorbar.set_axis_label_text(colorbar_label)
fig.colorbar.set_axis_label_font(size=colorbar_labelfont)
fig.colorbar.set_axis_label_pad(colorbar_labelpad)
# plot scalebar
if (plot_scalebar == True):
fig.add_scalebar( scalebar_size * (1.0/distance) / 3600.0)
fig.scalebar.set_label(scalebar_text)
fig.scalebar.set_color(scalebar_color)
fig.scalebar.set_font(size=scalebar_font)
fig.scalebar.set_linewidth(scalebar_linewidth)
If you would like to overplot another image as contours, try loading as another HDU (e.g., chdu
in the following example) and complete the commands like:
fig.show_contour(chdu, colors=ccolor, linestyles=clinestyle,
levels=clevels,
linewidths=clinewidth)
Finally, to save an output figure, try ty complete the command like:
fig.save(outfigname, transparent=True)
You will make plots routinely. It is recommended to integrate the above commands in a script or a wrapper in a way that is convenient to you. If anything in this part is not clear to you, you can check the documentation for APLpy. You might need some patience and may need to try some things since not everything has been very clearly documented.
We can also use APLpy to create 3-color (RGB) figures, for example, see this page. However, my experience of trying this function was not very smooth. Following are some tips. First, we need three independent FITS images (e.g., the observations on the same area in the sky, at different wavelengths). Before using APLpy to generate a 3-color figure from them, we need to first regrid them to the same dimension (using either the CASA imregrid task or the Miriad regrid task, see above). Then we have to use the following CASA commands to produce the subimages (the area to be presented) and to remove the frequency/velocity and/or Stokes axes:
import os
path = './'
Rmap = path = '5ch3oh0_2_mnt0.img' # my red image
Gmap = path = '5ch3oh2_2_mnt0.img' # my green image
Bmap = path = '5ch3oh3_2_mnt0.img' # my blue image
for imname in [Rmap, Gmap, Bmap]:
importfits(
fitsimage = imname + '.FITS',
imagename = imname + '.image',
overwrite = True
)
imsubimage(
imagename = imname + 'image',
outfile = imname + '.subim.image',
box = '174,174,324,324', # the box sub-region to be plotted (blcx, blcy, trcx, trcy),
dropdeg = True, overwrite = True
)
exportfits(
imagename = imname + 'subim.image',
fitsimage = imname + '.dropdeg.fits',
overwrite = True, dropdeg = True, dropstokes = True
)
Then the following Python code and produce the PDF figure:
# importing packages
import os
import numpy as np
from astropy.io.fits import getdata
from astropy import wcs
from astropy.io import fits
from astropy import units as u
from astropy import constants as con
from astropy.coordinates import SkyCoord
import matplotlib
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib
import aplpy
from astropy.io import fits
matplotlib.use('PDF')
# defining input image names
fits_r = '5CH3OH0_2.MNT0.IMG.dropdeg.fits'
fits_g = '5CH3OH2_2.MNT0.IMG.dropdeg.fits'
fits_b = '5CH3OH3_2.MNT0.IMG.dropdeg.fits'
# Combing the 3 independent FITS images to a single FITS image cube
os.system('rm -rf g10_rgb_cube.fits')
aplpy.make_rgb_cube([fits_r, fits_g, fits_b], 'g10_rgb_cube.fits')
# Make an RGB image (PNG figure)
aplpy.make_rgb_image('g10_rgb_cube.fits', 'g10_rgb.png')
# Insert a header item CTYPE3 such that APLpy will know what we are trying to do
fits.setval('g10_rgb_cube.fits','CTYPE3',value='RGB')
# Plot the RGB image using the 2d image to indicate the projection
fig = plt.figure(figsize=(7, 7))
f = aplpy.FITSFigure('g10_rgb_cube.fits', dimensions=[0,1], slices=[2],
figure=fig, subplot=[0.1, 0.1, 0.8, 0.8]
)
f.show_rgb('g10_rgb.png')
f.savefig('g10_rgb.pdf')
The commands to change/include the fontsize, ticks, labeling, beam, etc are identical to those for the single-color image, which have been introduced above.
4.2 IDL
(I used to use IDL to make plots. I just found that most of my old IDL codes can not anymore be compiled. Let’s forget about this part unless you are interested in archaeology or anthropology.)