Image File Processing (FITS and Android)

The Flexible Image Transport System (FITS) was designed specifically for astronomy images.  In this article we’ll look at how you can write programs to read the data and recreate the picture stored within it.

A detailed description of the FITS file format can be found on the NASA site. You can get quite complicated files, with multiple images and tables of data; but we’re going to use very basic versions that include a single image. These basic files are then actually easier to work with than more common types such as png or jpeg, which include various methods of compression (ways of reducing the file size).

We’ll be cutting some corners to make things as simple as possible. If you see a red box, then you can click on it to see information about the shortcut that’s been taken; but feel free to ignore them until after you have the program working.

 

Easy Ways to Work with FITS

We’ll be creating a basic, low-level program to work with FITS files.  If you want to work with more complicated data, or are more interested in creating a nice-looking image, then you would be better off using one of the existing tools.  The NASA page lists various tools that you could use; one of which is the ESA/ESO/NASA FITS Liberator plugin for PhotoShop.  There’s a nice (but old) tutorial on using FITS Liberator that shows you how to combine Hubble images to create stunning multi-colour images.  Even if you are writing your own software there are many libraries that you can use to make things easier – such as in the astropy library for Python.

 

What is FITS?

All of the files that we’re going to work with come from the MicroObservatory image directory. You could use any file, but it will be easier to follow this article if you use the image of the moon provided below.

Click here to download the Moon FITS file

Download the file, and then open it in a text editor – such as Notepad or TextEdit. You might need to open the application first, and then choose File > Open, and choose Any Type as the file format so that you can see the FITS file.

As you scroll through the file you should notice two different types of text. The top part should be readable (even if you’re not sure what it means), and the bottom part that looks like gibberish. The structure of FITS files is essentially like this:

two main sections: header unit and data unit

Diagram of FITS file structure

On this first pass we’re going to cheat and read the descriptive data ourselves. Look out for important keywords:

NAXIS How many axis of data – should be 2 for X and Y
NAXIS1 How much data along axis 1 – i.e. width of the image in this case
NAXIS2 How much data along axis 2 – i.e. height of the image in this case
BITPIX How many bits (binary 1’s and 0’s) are used to store the brightness of each pixel. It should be 16.

There’s some other information in there, but none that we need at the moment. Feel free to have a look through, see what you can make out, and move on when you’re ready.

 

Using Android

Why Android? – Click to expand
If you weren’t questioning the use of Android, then let me explain why it’s a bad idea.  If you’re working with images then you probably want two things: a large screen so that you can see the details, and a fast processor to work with the pixel data.  A mobile device is not a great choice; but I’m using Android here simply because there are other apps that I want to produce tutorials for, and I wouldn’t want to force people to download and learn multiple programming environments.  Plus Android can handle what we’re asking it to do, and we won’t be doing anything too language specific (so you could recreate it in another language if you wanted to).

We’ll be using Android Studio.  I’ll assume you know how to set-up a new project including a blank activity.  If not then the full Android Studio setup is explained in another post.

  • Open the View for the activity.
  • Select and delete the TextView – which currently says Hello World!
  • In the palette area to the left of the phone screen, select an ImageView.
  • Click on the phone screen to add an ImageView – ideally in the centre.
  • In the properties area, set the layout:width and layout:height to match the naxis1 and naxis2 values that you saw in the FITS file (650px and 500px respectively).

Android wants to use dp instead of pixels – Click to expand
Using dp (device pixels) helps to make your designs look similar across the huge range of screen sizes and resolutions that exist.  However in this case we need an exact pixel size.  There are ways of achieving both, but let’s keep it simple.

  • While you’re changing properties, change the id to imgDisplay. Everything gets default names, but setting your own names makes it easier to remember which is which.

That’s all we’re adding to the view for now.

 

Including the File

Our application needs to find the FITS file before we can process it. Let’s add it to our project.

  • In the Project area over on the far left, right-click on the app folder and choose New > Folder > Assets Folder.
  • Just click Finish in the window that appears.

Now that we have a suitable folder, we can copy the fits file into it.

  • Right-click on the assets folder and choose Show In Explorer
Adding an assets folder to the Android project

Adding an assets folder to the Android project

Now you know where the assets folder is on your computer. Copy the FITS file into it.

Loading Files – Click to expand
If we leave it this way, then it will be a fairly useless program that only ever processes a single file. In a future article we might load the file that you want from a location on the phone instead.

 

Simple / 5

If it isn’t open already, then find and open (double click) the MainActivity file:

Look for the OnCreate method. To begin with we’ll put the code in there, so that it runs as soon as the app starts.

Running lots of code in onCreate – Click to expand
This will add a delay to our app starting, and could look as though nothing is happening.  In fact we wouldn’t really want to run a big chunk of processing code within the main program anyway.  Something like an AsyncTask would be a better choice – for a later date :).

The basic structure of our code is going to be:

  1. Load the file.
  2. Loop through header unit: even though we won’t look at it initially, it’s still there.
  3. Loop over the data unit – drawing one pixel at a time.

All the code that you need is included below.  Simply replace your onCreate code with this.  It might look a bit long and scary, but that’s partly because there are so many comments explaining what’s going on.

When you first paste it in you’ll get a lot of underlined red – indicating errors.  Hopefully it’s only because you’re using bits of code (classes) without saying that you’re going to in advance (import).  Luckily there’s an easy option to fix this in Android Studio. Just click on the red text, and you should see a message that you can press Alt+Enter to fix the problem.  What it’s actually doing is going to the top of the program and adding import statements.

Android Studio's offered solution to missing libraries

Android Studio’s offered solution to missing imports

Run the program, and hopefully you get an image of the Moon:

First loading of FITS file

First loading of FITS file

 

Linear interpolation

At the moment the image will have very poor contrast.  The darkest areas are dark grey, and the light areas are light grey.  In this particular file the sky has a pixel value of around 590, and the brightest part of the Moon has a value of 1236.  By dividing by 5 this gives a range of 118 to 247, which is much narrower than the range of possible pixel values that we could use (0 to 255).

It might be easier to think about the problem if we use some graphs.  The two included below actually show the same values, but using different scales.  Our image at the moment is a bit like the graph on the left, with most of the pixel values near the middle, but with a broad scale.  If we change our scale so that the range is a better fit with our data, then the differences between each value will be easier to see.

two graphs with different scales

Demonstration of how changing the scale can make the difference in values easier to see

The solution is to use a system called linear interpolation.  Essentially we’ll say that the lowest pixel value should be black, and the highest should be white.  Everything else will fall on a scale between those two.  However, that will mean needing to know what the highest and lowest values are – which means changing the structure of our program to be:

  1. Load the file.
  2. Loop through header unit: even though we won’t look at it initially, it’s still there.
  3. Loop over the data unit – this time just storing the pixels instead of drawing, and importantly keeping track of the biggest and smallest that we see.
  4. Loop over the stored pixels – scaling each pixel based on the biggest and smallest values seen, and drawing them one-by-one.

Again, you can just replace the onCreate method that you have at the moment with the one below.

Run the program again, and you should see a much more well defined image.

 

Moon image displayed using linear interpolation

Moon image displayed using linear interpolation

 

Logarithmic Scale

The linear interpolation gave us a better result, but it isn’t always the best choice for astronomy images.  The massive contrast between the darkness of the night’s sky and the brightness of stars, can mean that faint objects get lost.  Let’s try a different file.

  • Download the Ring Nebula file below,
  • add it to the assets folder,
  • change the line InputStream input = assetManager.open(“moon.FITS”); to use the new file instead (ringnebula.FITS).

Click here to download the Ring Nebula FITS file

If you view this file at the moment then you won’t see very much.  Again we can use graphs to make things clearer.  Below left we see a graph where a single extreme value (e.g. a bright star) is stretching the scale and hiding detail in the lower values.  One possible solution is shown in the middle.  It has exactly the same values, but the scale has been cut-off at 35.  It gives a better view of the values, but how would we know to cut off at 35?  We could use some maths to make a good guess, but normally this would be achieved by allowing someone to dynamically change the value (e.g. with a slider) and seeing the result.  Another option would be to use logarithms.  I probably wouldn’t do a very good job of explaining what they are (here’s a Khan academy video on logarithms), but the result is shown in the graph on the right.  You apply a certain equation to each value, and it narrows the range – reducing extremes.  The result might not be as good, but we don’t need to worry about picking arbitrary numbers or building an interface for the user – so it’s what we’ll use.

three graphs

Examples of how we can deal with an extreme value.  Cut-off point (centre) and logarithmic scale (right)

Switching to a logarithmic scaling only requires very minor changes to the program.  In fact we only need to include Math.log10( ) in three different places.  First, change the lines that store the minimum and maximum values that we found:

Next scale the actual pixel value:

 

You should get a result similar to that shown below.  With logarithmic scaling applied on the right, you can see more stars and the faint ring nebula itself becomes visible (at the intersection of the two arrows).

Ring Nebula image with only linear interpolation (top) and logarithmic scaling (bottom)

Ring Nebula image with only linear interpolation (top) and logarithmic scaling (bottom)

 

Wrap-Up

There is a lot more work that we could do to make the app more usable, and to follow good programming practices.  However, we’ve covered a lot of the fundamental principles of loading and rendering images.  Hopefully you’ve also seen that choices in how we display images can be as important as the stored data itself.



Comments are closed.