Subject: FITS -> GIF or JPEG or TIFF converter

From: rousself at univ-rennes1.fr ( Frank ROUSSEL )
Date: 19 Aug 1993 15:49:23 GMT

Does such a converter exist for Unix ?
Thanks
---------------------------------------------------------------------------------
   _______       _______       ________   | Firstname: Frank
  /______/|     /______/\     /_______/|  | Lastname : ROUSSEL
 / ______|/    /  ____ \/|    |__   __|/  | E-mail: rousself at univ-rennes1.fr
/ /|           | |____| |/       | ||     | Telephone: + 33 99 83 26 10
| ||           |  __  __/        | ||     | 
| |\______     | || \ \\       __| ||__   | Address: 175, rue Belle Epine
\ \______/|    | ||  \ \\     /__| |/_/|  | CityStateZip: 35510
 \_______|/    |_|/   \_\|    |_______|/  | Cityname: CESSON SEVIGNE
    Centre de Ressources Informatiques    | Country: FRANCE
---------------------------------------------------------------------------------
---------- Science without conscience is only soul's ruin (Rabelais) ------------
---------------------------------------------------------------------------------
- Signed: The responsible of ASTROGOF project at Rennes' University of France   -
- who contributes to the development of CRI-CICB Gopher's server by maintaining -
- an astronomic anonymous ftp server 'ftp.univ-rennes1.fr' in /pub/Images/ASTRO -
---------------------------------------------------------------------------------

---

From: pmurphy at cv3.CV.NRAO.EDU (Pat Murphy)
Date: Fri, 20 Aug 1993 15:30:59 GMT

In article <2507e4$etv at news.univ-rennes1.fr>, rousself at univ-rennes1.fr (
  Frank ROUSSEL ) writes: 

FR> Does such a converter exist for Unix ?

Sort of.  Here's an excerpt:

orangutan_pmurphy<524> man fitstopgm

     fitstopgm(1)            (20 September 89)            fitstopgm(1)

     NAME
          fitstopgm - convert a FITS file into a portable graymap

     SYNOPSIS
          fitstopgm [-image N] [FITSfile]

     DESCRIPTION
          Reads a FITS file as input.  Produces a portable graymap as
          output.  The results may need to be flipped top for bottom;
          if so, just pipe the output through pnmflip -tb.

     OPTIONS
          The -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.

You could do something like:

	fitstopgm <file> | pnmflip | pgmtoppm | ppmtogif

The pbmplus package is available at many fine archive sites, the nearest
to you is probably cnam.cnam.fr (pub/Archives/comp.sources.misc/pbmplus/)
according to xarchie.
				- Pat
--
==========================================================================
| Patrick P. Murphy, Ph.D.                Scientific Programming Analyst |
| National Radio Astronomy Observatory    Net:       pmurphy at nrao.edu    |
| 520 Edgemont Road                       Phone:     (804) 296-0372      |
| Charlottesville, VA 22903-2475          VoiceMail: (804) 980-5889      |
|      "I don't believe in the no-win scenario"  --- James T. Kirk       |
==========================================================================

---

From: dbriggs at Mr-Hyde.aoc.nrao.edu (Daniel Briggs)
Date: Fri, 20 Aug 93 21:01:34 GMT

In article <PMURPHY.93Aug20103059 at orangutan.cv.nrao.edu>
  pmurphy at nrao.edu (Pat Murphy) writes:
>Sort of....

I hacked on the pbmplus program fitstopgm, and added a few more options.
(Some generally useful, and one or several that are kind of obscure.)
Most important to the folks on this group is that it can handle floating
point FITS on machines whose native architecture support IEEE-754.
I *think* I sent it off to Jef for incorporation into the next release, but
I honestly can't remember.  Certainly I never heard back from him.

Anyway, here's what I did.  Feel free to mess with it some more.  If we
want to have a decent FITS->generic image translator widely available, *we*
are going to have to write it.  It's no fair to ask Jef to do so, when he
doesn't use FITS routinely.

-----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 B-14993
| New Mexico Tech / National Radio Astronomy Observatory |      DoD #387
| P.O. Box O / Socorro, NM 87801   (505) 835-7391        | 
Support the League for Programming Freedom   (info from lpf at uunet.uu.net)

---

From: drtr at mail.ast.cam.ac.uk (David Robinson)
Date: Sat, 21 Aug 1993 15:34:14 GMT

12345678901234567890123456789012345678901234567890123456789012345678901234567890
In article <2507e4$etv at news.univ-rennes1.fr>, rousself at univ-rennes1.fr
( Frank ROUSSEL ) writes:
|> Does such a converter exist for Unix ?
|> Thanks

The xv package (current version 3.00a, archie search for xv-3.00a.tar.Z)
supports many formats, including GIF, PBM, JPEG, TIFF and PostScript. The
current version does not support FITS by default. However, I have patched it to
support reading and writing of FITS files. The patches supports all BITPIX
values, and do *not* assume that the host has IEEE floating point. It supports
three dimensional (NAXIS=3) FITS files by treating then as containing
NAXIS3 sets of two-dimensional images.

The patches can be anonymously ftp'd from ftp-hst.ast.cam.ac.uk by fetching the
file pub/software/xv-patches/FITS-v2.tar.Z.

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