Read an image#

Reminder

  • Start the exercise with the Python template file in the src directory corresponding to the exercise.

    • Your code must be added to the main function.

    • If you write generic functions, put them into libraries to reuse them easily in other applications. Look at Library and Module documentation for more details on Python libraries.

    • You should carefully follow the division in steps proposed for every exercise and provide specific signatures for each step when this is specified.

  • Git operations:

    • Commit very frequently with relevant message. Focus on making clear the reasons why you made the change.

    • Push once a significant result is obtained.

  • Code quality:

    • Check frequently the PyCharm annotations about your code, for example the colored signals in the right vertical gutter.

    • Execute the check_commit_status.py script to check the validation tests.

    • Assess the quality of your code with SonarQube.

  • Documentation:

    • Add docstrings (""" ... """) at begining of ALL functions & classes.

    • Add one-line comments wherever this is useful to understand your code.

  • Refer to slides for detailed information on technical topics.

General goal#

This first exercise is about reading an image from a FITS file, with the help of the the astropy.io.fits module, and displaying the image with the help of the pyplot library.

Your data directory contains those images:

  • common.fits: an image common to all students. It is provided to help you to check your developments. For each exercice, you will be given the exact output that your program should produce, when applied to common.fits.

  • specific.fits: an image different for each student. For each exercice, you can apply your program to your image specific.fits, and check if it is running fine, but you will not know what is the expected output. Each time you push your code to the reference repository, the continuous integration system will apply your programs to your specific.fits, and the signatures will be compared to the secret exected ones, so to check that your code is doing the right thing. So to see the final diagnostic of the continuous integration, execute check_commit_status.py.

  • global.fits: the global image of the Bode’s galaxy. It covers a much larger region of the sky than the previous images: in fact, common.fits and the specific.fits of each student pair are a subset of this global image. You can use it as a third image, with different characteristics, to check that your programs are working correctly.

It is very usual, in the astronomical community, to format the file following the FITS conventions. A FITS file contains a sequence of data blocks, and each data block is made of two sections : the data and the meta-data. Such meta-data contains information about the conditions of the data acquisition (when, where, sky region, etc…).

Before starting the exercise below, you may want to review Fits slides for more details on FITS format, and the Pyplot slides for information about the construction of a graphic user interface with the PyPlot library.


Application description#

Following the instructions below, you will create a simple application to read a FITS image file and print some metadata from it. This must be implemented in a file called ex1_read_image.py.

Initial application#

As you can see, an initial file ex1_read_image.py is provided. It is importing our module npac/args, whose function get_file_name() will help you manage the name of the file to be processed.

This function looks if a file name was provided when the application has been invoked, on the command line. If there is such an argument, it is expected to be the name of the file image file to process. If the .fits extension was not present, it is automatically added. If no file name is provided on the command line, the function ask one interactively to the user. If the user types directly return, the default common.fits will be used. Also, all those files are searched by get_file_name() in the relevant directories, especially your data subdirectory.

Run this application several times, with or without command-line arguments, with different files names, with or without the unix path, with or without the .fits extension : get used to the way get_file_name() is working.

Reading the image#

So to extract information from the FITS file, you can rely on the astropy.io.fits module. Thus you need to import the module as follows:

from astropy.io import fits

The astropy.io.fits.open() function allows to open the file. The recommended and safest way to open a file in Python is to use a with statement which will take care of closing the file for you, even if something goes wrong during the operation:

with fits.open(path_to_file) as fits_blocks:
    block = fits_blocks[0]
    ...

The dedicated open function of the astropy.io.fits module directly returns a collection of data blocks, which is called fits_blocks above. Only the first data block, fits_blocks[0], is useful in this course.

Each data block is an object with two members:

  • header (the metadata)

  • data (the image pixels)

Signature#

This application is expected to print some results on the terminal. This is what we call the exercise signature. For the current exercise, it is made of CRPIX1, CRPIX2 elements from the image header. They represent the coordinate system reference pixel for the image. It is needed to convert any pixel position into a sky coordinate. Each image, representing a different portion of the sky, has a specific reference pixel.

To retrieve those values from the FITS first block header, use the header as if it was a python dictionary, and retrieve the fields associated with the keys CRPIX1 and CRPIX2. Then, print them on the terminal, one line each, using the following format :

signature_fmt_1 = 'RESULT: CRPIX1 = {:.0f}'
signature_fmt_2 = 'RESULT: CRPIX2 = {:.0f}'

When running your program with the common image common.fits, the expected signature is:

RESULT: CRPIX1 = 719
RESULT: CRPIX2 = 755

Handle properly a non existing file#

In Python, errors are often handled using exceptions: this avoid to have a lot of if statements in the code to do the error handling, which make the code harder to maintain. An introduction to exceptions handling is available in the Python Errors Notebook.

An example of exception is FileNotFoundError which is raised (that means which happens) if you try to open a non existing file. In the context of our application, this exception will be raised if you mispelled the file name. You can try to do it and you will see that the program exits in such circumstances.

Try to handle this exception, following the instructions in the exception introduction mentioned above, to either give an explicit message to the user before exiting or, better, to give him a chance to reenter the file name.

To exit the application, use:

exit()

Graphic application#

You are now asked to write a graphic application, within the file display.py. Refer to the graphics explanations and to the Pyplot slides for details on how to use matplotlib.pyplot.

Reuse previous code#

You must first reuse the code from the previous step, which you will place in a dedicated module called lib_fits.py, following the recommended conventions. We ask you to rewrite both display.py and ex1_read_image.py, so that they both rely on the new shared module lib_fits.py, this way:

import lib_fits

# read_first_image should return 2 values:
# - header (metadata of the first block)
# - pixels (data of the first block)
header, pixels = lib_fits.read_first_image(file_name)

Display the image#

After loading the image, the application display.py must display it, with the help of the module matplotlib.pyplot. You need this kind of import:

import matplotlib.pyplot as plt

The as keyword creates a short name alias for the module that will be used to refer to its functions, instead of the full name matplotlib.pyplot.

Thus, the main actions involved here are:

# Create the graphic context
# Returns a reference to the drawing region (fig) and an axes where the image will be drawn
fig, axes = plt.subplots()

# Send the image into the default display
# pixels from FITS file have the appropriate type and can be used directly
imgplot = axes.imshow(pixels)

# Launch the interactive session (open the window an draw what has been put into the image
plt.show()

You should get a window like this one:

_images/step-01.png

Warning

The graphical window with the plot display may be hidden behind the PyCharm window. You can see it using F3 key. You have to move aside the PyCharm window to see your plot and shift it next the PyCharm window!