Subject: Converting FITS to other image formats?

From: anderson at stsci.edu
Date: Sun, 21 Nov 1993 23:16:52 GMT

I hope this isn't a FAQ (didn't see it in the "FITS Basics and Info"
posting):

Does anyone know of any tools for converting FITS image files to any of
the more common display standards, such as GIF, TIFF, JPEG, etc.?

Thanks in advance,

------------------------------------------------------------------------
Chris Anderson			Science Operations Specialist
ANDERSON at STSCI.EDU		Science Planning & Scheduling
				Computer Sciences Corporation
				Space Telescope Science Institute

---

From: oneel at athena.gsfc.nasa.gov (Bruce O'Neel)
Date: Mon, 22 Nov 1993 08:59:18 -0500 (EST)

> the more common display standards, such as GIF, TIFF, JPEG, etc.?

Hi,
  The PBM toolkit has a fits reader and a number of different writers
(gif and tiff at least).  Available at better FTP sites.   I've
written a fitstoppm converter as well using Bill Pence's FTISIO
library which seems to work ok.  It seems to work with more fits
images than the PBM toolkit version.

bruce

-- 
As I understand it from eyewitness reports, the Scheme initiation ritual is 
to sit in a circle around the Common Lisp manual and make gagging sounds and 
jokes about how thick it is.  -- Scott E. Fahlman sef+ at cs.cmu.edu

---

From: drtr at mail.ast.cam.ac.uk (David Robinson)
Date: Wed, 24 Nov 1993 13:31:53 GMT

In article <1993Nov21.181652.1 at stsci.edu>, anderson at stsci.edu writes:
|> I hope this isn't a FAQ (didn't see it in the "FITS Basics and Info"
|> posting):
|> 
|> Does anyone know of any tools for converting FITS image files to any of
|> the more common display standards, such as GIF, TIFF, JPEG, etc.?
|> 
|> Thanks in advance,
|> 
|> ------------------------------------------------------------------------
|> Chris Anderson			Science Operations Specialist
|> ANDERSON at STSCI.EDU		Science Planning & Scheduling
|> 				Computer Sciences Corporation
|> 				Space Telescope Science Institute

A good solution is to use a patched version of the xv program.
xv version 3.00a can load and save GIF, PM, PBM (+PGM PPM etc.) X11 bitmap
Sun Rasterfile, BMP, IRIS, JPEG, TIFF and PostScript. xv is available by
anonymous ftp from ftp.cis.upenn.edu in /pub/xv/xv-3.00a.tar.Z.

At the moment FITS support in xv is provided by a set of patches.
The FITS support is more robust than that provided by the pbm library;
it will work on any machine irrespective of its floating point format -
including Vax VMS.

The patches are available by anonymous ftp from ftp-hst.ast.cam.ac.uk in
/pub/software/xv-patches/FITS-v2.tar.Z.

 David Robinson. (drtr at mail.ast.cam.ac.uk)

---

From: sla at umbra.UCSC.EDU (Steve Allen)
Date: 24 Nov 1993 16:17:28 GMT

>anonymous ftp from ftp.cis.upenn.edu in /pub/xv/xv-3.00a.tar.Z.

Folks who get xv 3.00 should take care to read the copyright statements
which accompany it.  Site managers may not install it unless they
purchase the right to do so.
--  --  --  --  --  -+-   --  --  --  --  --  --  --  -+-  --  --  --  --  --
Steve Allen          | "Shameless Morris Enthusiasm"   |   sla at lick.ucsc.edu
UCO/Lick Observatory | Yep, that's me.  6 up for       |   +1 408 459 3046
Santa Cruz, CA 95064 | Orange in Bloom, Sherborne, eh? |   Seabright Morris

---

From: cristy at eplrx7.es.duPont.com (Cristy)
Date: Wed, 24 Nov 1993 17:12:49 GMT

>purchase the right to do so.

Try ImageMagick.  It can display and convert FITS (and other images
formats) but is not encumbered with a restrictive license.

---

ImageMagick is a collection of X11 image processing and display utilities.  It
is available on ftp.x.org as contrib/ImageMagick.tar.Z.

Display

     Display is a machine architecture independent image
     processing and display program.  It can display an image on
     any workstation display running an X server.  Display first
     determines the hardware capabilities of the workstation.  If
     the number of unique colors in the image is less than or
     equal to the number the workstation can support, the image
     is displayed in an X window.  Otherwise the number of colors
     in the image is first reduced to match the color resolution
     of the workstation before it is displayed.

     This means that a continuous-tone 24 bits/pixel image can
     display on a 8 bit pseudo-color device or monochrome device.
     In most instances the reduced color image closely resembles
     the original.  Alternatively, a monochrome or pseudo-color
     image can display on a continuous-tone 24 bits/pixels
     device.

Import

     Import reads an image from any visible window on an X server
     and outputs it as an image file.  You can capture a single
     window, the entire screen, or any rectangular portion of the
     screen.  You can use display (see display(1)) utility for
     redisplay, printing, editing, formatting, archiving, image
     processing, etc. of the captured image.

     The target window can be specified by id, name, or may be
     selected by clicking the mouse in the desired window.  If
     you press a button and then drag, a rectangle will form
     which expands and contracts as the mouse moves.  To save the
     portion of the screen  defined by the rectangle, just
     release the button.  The keyboard bell is rung once at the
     beginning of the screen capture and twice when it completes.

Animate

     Animate displays a sequence of images on any workstation
     display running an X server.  Animate first determines the
     hardware capabilities of the workstation.  If the number of
     unique colors in an image is less than or equal to the
     number the workstation can support, the image is displayed
     in an X window.  Otherwise the number of colors in the image
     is first reduced to match the color resolution of the
     workstation before it is displayed.

     This means that a continuous-tone 24 bits/pixel image can
     display on a 8 bit pseudo-color device or monochrome device.
     In most instances the reduced color image closely resembles
     the original.  Alternatively, a monochrome or pseudo-color
     image sequence can display on a continuous-tone 24
     bits/pixels device.

Montage

     Montage creates a composite image by combining several
     separate images.  The images are tiled on the composite
     image with the name of the image optionally appearing just
     below the individual tile.

Mogrify

     Mogrify transforms an image or a sequence of images.  These
     transforms include image scaling, image rotation, color
     reduction, and others.  The transmogrified image overwrites
     the original image.

Convert

     Convert converts an input file using one image format to an
     output file with a differing image format. By default, the
     image format is determined by it's magic number. To specify
     a particular image format, precede the filename with an
     image format name and a colon (i.e.  ps:image) or specify
     the image type as the filename suffix (i.e. image.ps).
     Specify file as - for standard input or output.  If file has
     the extension .Z, the file is decoded with uncompress.

     Convert recognizes the following image formats:

       Tag       Description
       ----------------------------------------------------
       ALPHA     Raw alpha bytes
       AVS       AVS X image file
       BMP       Microsoft Windows bitmap image file
       CMYK      Raw cyan, magenta, yellow, and black bytes
       EPS       Adobe Encapsulated Postscript
       FAX       Group 3
       FITS      Flexible Image Transport System
       GIF       Compuserve Graphics image file
       GRAY      Raw gray bytes
       JPEG
       MIFF      Magick image file format
       MTV
       PCX       ZSoft IBM PC Paintbrush file
       PICT      Apple Macintosh QuickDraw/PICT file
       PNM       Portable bitmap
       PS        Adobe PostScript file
       PS2       Adobe PostScript Level II file
       RGB       Raw red, green, and blue bytes
       RLE       Utah Raster Toolkit
       SUN       SUN raster
       TGA       Truevision Targa image file
       TEXT      raw text file;  read only
       TIFF      Tagged Image File Format
       VICAR
       VIFF      Khoros Visualization image file.
       X         select image from X server screen
       XC        constant image of X server background color
       XBM       X11 bitmap
       XPM       X11 pixmap
       XWD       X11 window dump
       YUV       Raw Y, U, and V bytes

Combine

     Combine combines images to create new images.

Segment

     Segment segments an image by analyzing the histograms of the
     color components and identifying units that are homogeneous
     with the fuzzy c-means technique.  The scale-space filter
     analyzes the histograms of the three color components of the
     image and identifies a set of classes.  The extents of each
     class is used to coarsely segment the image with
     thresholding.  The color associated with each class is
     determined by the mean color of all pixels within the
     extents of a particular class.  Finally, any unclassified
     pixels are assigned to the closest class with the fuzzy c-
     means technique.
-- 
cristy at dupont.com

---

From: dbriggs at Mr-Hyde.aoc.nrao.edu (Daniel Briggs)
Date: Thu, 25 Nov 93 09:27:25 GMT

In article <1993Nov24.133153.15184 at infodev.cam.ac.uk>

>it will work on any machine irrespective of its floating point format -

The xv patches are good stuff.  But for completeness, here's a hacked up
version of fitstopgm that I cobbled together.  I never got around to adding
architecture independent floating point, but if you're on something that
speaks IEEE-754, it works just fine.

-----fitstopgm.1-----
.TH fitstopgm 1 "21 June 93"
.IX fitstopgm
.SH NAME
fitstopgm - convert a FITS file into a portable graymap
.SH SYNOPSIS
.B fitstopgm
.RB [ -image
.IR N ]
.RB [ -noraw ]
.RB [ -scanmax ]
.RB [ -printmax ]
.RB [ -min
.IR f ]
.RB [ -max
.IR f ]
.RI [ FITSfile ]
.SH DESCRIPTION
Reads a FITS file as input.
.IX FITS
Produces a portable graymap as output.
The results may need to be flipped top for bottom; if so, just
pipe the output through
.B pnmflip -tb.
.IX pnmflip
This version of the program will accept integer FITS, and floating point
FITS if the host machine architecture is IEEE 754 compliant.
.SH OPTIONS
.PP
The
.B -image
option is for FITS files with three axes.
The assumption is that the third axis is for multiple images,
and this option lets you select which one you want.  Additional
axes will generate a warning message, but will otherwise be
ignored.
.PP
If the pbm package has been compiled with the RAWBITS option, the output
will be RAW PGM format.  The
.B -noraw
option forces the output to ASCII.
.PP
The program assumes that the input file has been scaled to allow for
maximum dynamic range, which is true for many FITS files.  If not,
the
.B -scanmax
option
forces the program to prescan the file and determine the most
appropriate scaling constants.
The
.B -printmax
option causes the program to print the minimum and maximum values
encountered and exit.  
A portion of the data range can
be selected with
the options 
.B -min
.I f
and
.B -max
.I f.
Data beyond this range will be clipped appropriately.
.PP
All flags can be abbreviated to their shortest unique prefix.
.SH REFERENCES
FITS stands for Flexible Image Transport System.  A full description
of the basic image format can be found in Astronomy & Astrophysics Supplement
Series 44 (1981), page 363.  The full standard, including the "Floating
Point Agreement for FITS" is available from the NASA/OSSA Office of
Standards and Technology.  The standards are also available for ftp
on the host nssdca.gsfc.nasa.gov.
.SH "SEE ALSO"
pgmtofits(1), pgm(5), pnmflip(1)
.SH AUTHOR
Copyright (C) 1989 by Jef Poskanzer.
.PP
Additional code contributed by Daniel Briggs
.\" Permission to use, copy, modify, and distribute this software and its
.\" documentation for any purpose and without fee is hereby granted, provided
.\" that the above copyright notice appear in all copies and that both that
.\" copyright notice and this permission notice appear in supporting
.\" documentation.  This software is provided "as is" without express or
.\" implied warranty.

-----fitstopgm.c-----
/* fitstopgm.c - read a FITS file and produce a portable graymap
 **
 ** Copyright (C) 1989 by Jef Poskanzer.
 **
 ** Permission to use, copy, modify, and distribute this software and its
 ** documentation for any purpose and without fee is hereby granted, provided
 ** that the above copyright notice appear in all copies and that both that
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
 **
 ** Hacked up version by Daniel Briggs  (dbriggs at nrao.edu)  20-Oct-92
 **
 ** Include floating point formats, more or less.  Will only work on
 ** machines that understand IEEE-754.  Added -scanmax -printmax
 ** -min -max and -noraw.  Ignore axes past 3, instead of error (many packages
 ** use pseudo axes).  Use a finite scale when max=min.  NB: Min and max
 ** are the real world FITS values (scaled), so watch out when bzer & bscale
 ** are not 0 & 1.  Datamin & datamax interpreted correctly in scaled case,
 ** and initialization changed to less likely values.  If datamin & max are
 ** not present in the header, the a first pass is made to determine them
 ** from the array values.
 */

#include "pgm.h"

/* Do what you have to, to get a sensible value here.  This may not be so */
/* portable as the rest of the program.  We want to use MAXFLOAT and */
/* MAXDOUBLE, so you could define them manually if you have to. */
#include <values.h>

struct FITS_Header {
  int simple;		/* basic format or not */
  int bitpix;		/* number of bits per pixel */
  int naxis;		/* number of axes */
  int naxis1;		/* number of points on axis 1 */
  int naxis2;		/* number of points on axis 2 */
  int naxis3;		/* number of points on axis 3 */
  double datamin;	/* min # (Physical value!) */
  double datamax;	/* max #     "       "     */
  double bzer;		/* Physical value = Array value*bscale + bzero */
  double bscale;
};

static void read_fits_header ARGS(( FILE* fp, struct FITS_Header* hP ));
static void read_card ARGS(( FILE* fp, char* buf ));
static void read_val ARGS(( FILE* fp, int bitpix, double* vp ));
     
void
main( argc, argv )
int argc;
char* argv[];
{
  FILE* ifp;
  gray* grayrow;
  register gray* gP;
  int argn, imagenum, image, row;
  register int col;
  gray maxval;
  double val, fmaxval, frmin, frmax, dmax, dmin, scale, t;
  int rows, cols, images;
  struct FITS_Header h;
  char* fits_name;
  char* usage = "[-image N] [-noraw] [-scanmax] [-printmax] [-min f] [-max f] [FITSfile]";

  int doscan = 0;
  int forceplain = 0;
  int forcemin = 0;
  int forcemax = 0;
  int printmax = 0;
  
  pgm_init( &argc, argv );
  
  argn = 1;
  imagenum = 1;
  
  while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
    {
      if ( pm_keymatch( argv[argn], "-image", 2 ) )
	{
	  ++argn;
	  if ( argn == argc || sscanf( argv[argn], "%d", &imagenum ) != 1 )
	    pm_usage( usage );
	}
      else if ( pm_keymatch( argv[argn], "-max", 3 ) )
	{
	  ++argn;
	  forcemax = 1;
	  if ( argn == argc || sscanf( argv[argn], "%lf", &frmax ) != 1 )
	    pm_usage( usage );
	}
      else if ( pm_keymatch( argv[argn], "-min", 3 ) )
	{
	  ++argn;
	  forcemin = 1;
	  if ( argn == argc || sscanf( argv[argn], "%lf", &frmin ) != 1 )
	    pm_usage( usage );
	}
      else if ( pm_keymatch( argv[argn], "-scanmax", 2 ) )
	doscan = 1;
      else if ( pm_keymatch( argv[argn], "-noraw", 2 ) )
	forceplain = 1;
      else if ( pm_keymatch( argv[argn], "-printmax", 2 ) )
	printmax = 1;
      else
	pm_usage( usage );
      ++argn;
    }
  
  if ( argn < argc )
    {
      fits_name = argv[argn];
      ifp = pm_openr( fits_name );
      ++argn;
    }
  else
    ifp = stdin;
  
  if ( argn != argc )
    pm_usage( usage );
  
  read_fits_header( ifp, &h );
  if (forcemin) h.datamin = frmin;
  if (forcemax) h.datamax = frmax;
  
  if ( ! h.simple )
    pm_error( "FITS file is not in simple format, can't read" );
  switch ( h.bitpix )
    {
    case 8:
      fmaxval = 255.0;
      break;
      
    case 16:
      fmaxval = 65535.0;
      break;
      
    case 32:
      fmaxval = 4294967295.0;
      break;
      
    case -32:
      fmaxval = MAXFLOAT;
      break;
      
    case -64:
      fmaxval = MAXDOUBLE;
      break;
      
    default:
      pm_error( "unusual bits per pixel (%d), can't read", h.bitpix );
    }
  if ( h.naxis != 2 && h.naxis != 3 )
    pm_message( "Warning: FITS file has %d axes", h.naxis );
  cols = h.naxis1;
  rows = h.naxis2;
  if ( h.naxis == 2 )
    images = 1;
  else
    images = h.naxis3;
  if ( imagenum > images )
    pm_error( "only %d image%s in this file",
	     images, images > 1 ? "s" : "" );
  maxval = min( fmaxval, PGM_MAXMAXVAL );
  if ( doscan || h.datamin == MAXDOUBLE || h.datamax == MAXDOUBLE )
    {
      /* OK, We have to scale this the hard way. */
      pm_message( "Scanning file for scaling parameters" );
      dmax = -fmaxval;
      dmin = fmaxval;
      for ( image = 1; image <= imagenum; ++image )
	{
	  for ( row = 0; row < rows; ++row)
	    {
	      for ( col = 0; col < cols; ++col)
		{
		  read_val (ifp, h.bitpix, &val);
		  if ( image == imagenum ) {
		    if ( val > dmax ) dmax = val;
		    if ( val < dmin ) dmin = val;
		  }
	        }
	    }
	  pm_close( ifp );
	  if (h.bscale < 0.0) {
	    t = dmax;
	    dmax = dmin;
	    dmin = t;
	  }
	  ifp = pm_openr( fits_name );
	  read_fits_header( ifp, &h );
	  h.datamin = forcemin ? frmin : dmin * h.bscale + h.bzer;
	  h.datamax = forcemax ? frmax : dmax * h.bscale + h.bzer;
	}
    }

  /* If printmax option is true, just print and exit. */
  /* For use in shellscripts.  Ex:                    */
  /* eval `fitstopgm -printmax $filename | \          */
  /*   awk '{min = $1; max = $2}\                     */
  /*         END {print "min=" min; " max=" max}'`    */
  if (printmax) {
    printf( "%lf %lf\n", h.datamin, h.datamax);
    pm_close( ifp );
    pm_close( stdout );
    exit( 0 );
  }

  /* Just in case we have a file with constant value. */
  if (h.datamax != h.datamin)
    scale = maxval / (h.datamax - h.datamin);
  else
    scale = 1.e10;

  grayrow = pgm_allocrow( cols );
  pgm_writepgminit( stdout, cols, rows, maxval, forceplain);
  for ( image = 1; image <= imagenum; ++image )
    {
      if ( image != imagenum )
	pm_message( "skipping image %d of %d", image, images );
      else if ( images > 1 )
	pm_message( "reading image %d of %d", image, images );
      for ( row = 0; row < rows; ++row)
	{
	  for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
	    {
	      read_val (ifp, h.bitpix, &val);
	      t = scale * ( val * h.bscale + h.bzer - h.datamin );
	      if (t < 0)
		*gP = (gray) 0;
	      else if (t > maxval)
		*gP = (gray) maxval;
	      else
		*gP = (gray) t;
	    }
	  if ( image == imagenum )
	    pgm_writepgmrow( stdout, grayrow, cols, maxval, forceplain );
	}
    }
  pm_close( ifp );
  pm_close( stdout );
  
  exit( 0 );
}

/*
 ** This code will deal properly with integers, no matter what the byte order
 ** or integer size of the host machine.  Sign extension is handled manually
 ** to prevent problems with signed/unsigned characters.  Floating point
 ** values will only be read properly when the host architecture is IEEE-754
 ** conformant.  If you need to tweak this code for other machines, you might
 ** want to snag a copy of the FITS documentation from nssdca.gsfc.nasa.gov
 */

static void
  read_val (fp, bitpix, vp)
FILE *fp;
int bitpix;
double *vp;

{
  int i, ich, ival;
  long int lval;
  unsigned char c[8];
  
  switch ( bitpix )
    {
      /* 8 bit FITS integers are unsigned */
    case 8:
      ich = getc( fp );
      if ( ich == EOF )
	pm_error( "EOF / read error" );
      *vp = ich;
      break;
      
    case 16:
      ich = getc( fp );
      if ( ich == EOF )
	pm_error( "EOF / read error" );
      c[0] = ich;
      ich = getc( fp );
      if ( ich == EOF )
	pm_error( "EOF / read error" );
      c[1] = ich;
      if (c[0] & 0x80)
	ival = ~0xFFFF | c[0]<<8 | c[1];
      else
	ival = c[0]<<8 | c[1];
      *vp = ival;
      break;
      
    case 32:
      for (i=0; i<4; i++) {
	ich = getc( fp );
	if ( ich == EOF )
	  pm_error( "EOF / read error" );
	c[i] = ich;
      }
      if (c[0] & 0x80)
	lval = ~0xFFFFFFFF |
	  c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
      else
	lval = c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
      *vp = lval;
      
    case -32:
      for (i=0; i<4; i++) {
	ich = getc( fp );
	if ( ich == EOF )
	  pm_error( "EOF / read error" );
	c[i] = ich;
      }
      *vp = *( (float *) c);
      break;
      
    case -64:
      for (i=0; i<8; i++) {
	ich = getc( fp );
	if ( ich == EOF )
	  pm_error( "EOF / read error" );
	c[i] = ich;
      }
      *vp = *( (double *) c);
      break;
      
    default:
      pm_error( "Strange bitpix in read_value" );
    }
}

static void
  read_fits_header( fp, hP )
FILE* fp;
struct FITS_Header* hP;
{
  char buf[80];
  int seen_end;
  int i;
  char c;
  
  seen_end = 0;
  hP->simple = 0;
  hP->bzer = 0.0;
  hP->bscale = 1.0;
  hP->datamin = MAXDOUBLE;
  hP->datamax = MAXDOUBLE;
  
  while ( ! seen_end )
    for ( i = 0; i < 36; ++i )
      {
	read_card( fp, buf );
	
	if ( sscanf( buf, "SIMPLE = %c", &c ) == 1 )
	  {
	    if ( c == 'T' || c == 't' )
	      hP->simple = 1;
	  }
	else if ( sscanf( buf, "BITPIX = %d", &(hP->bitpix) ) == 1 );
	else if ( sscanf( buf, "NAXIS = %d", &(hP->naxis) ) == 1 );
	else if ( sscanf( buf, "NAXIS1 = %d", &(hP->naxis1) ) == 1 );
	else if ( sscanf( buf, "NAXIS2 = %d", &(hP->naxis2) ) == 1 );
	else if ( sscanf( buf, "NAXIS3 = %d", &(hP->naxis3) ) == 1 );
	else if ( sscanf( buf, "DATAMIN = %lf", &(hP->datamin) ) == 1 );
	else if ( sscanf( buf, "DATAMAX = %lf", &(hP->datamax) ) == 1 );
	else if ( sscanf( buf, "BZERO = %lf", &(hP->bzer) ) == 1 );
	else if ( sscanf( buf, "BSCALE = %lf", &(hP->bscale) ) == 1 );
	else if ( strncmp( buf, "END ", 4 ) == 0 ) seen_end = 1;
      }
}

static void
  read_card( fp, buf )
FILE* fp;
char* buf;
{
  if ( fread( buf, 1, 80, fp ) == 0 )
    pm_error( "error reading header" );
}
-- 
| Daniel Briggs  (dbriggs at nrao.edu)                        |  USPA C-23367
| New Mexico Tech / National Radio Astronomy Observatory   |      DoD #387
| P.O. Box O / Socorro, NM 87801   (505) 835-7391          | 
| Dart:	MC Ot+W H 3 Y L+ W C+ I++ T++ A+ H+ S+ V+ P++/P B+ |