4 ADDITIONAL FEATURES

 4.1 More advanced image access
 4.2 More advanced header access

The IMG library has more capabilities than have been covered in the preceding descriptions, which have been kept as simple as possible. You should read the following if you need to do more than has been covered so far.

4.1 More advanced image access

4.1.1 Getting workspace

Any reasonably complex program sooner or later needs to be able to store image information between processing stages. This program snippet (from an example called shadow.f – see §2) shows you how to create a temporary image for this purpose:

  *  Access the input image.
        CALL IMG_IN( ’IN’, NX, NY, IPIN, ISTAT )
        ...
  *  Get an image-sized piece of workspace.
        CALL IMG_TMP( ’TEMP’, NX, NY, IPTEMP, ISTAT )           [1]
        ...
  *  Free all the images (this deletes the temporary image).
        CALL IMG_FREE( ’*’, ISTAT )                             [2]

The following notes refer to the numbered statements:
(1)
The call to ??x]]IMG_TMP creates a temporary image of the size you specify (in this case the same size as the input image). You will not be prompted for a file name for a temporary image, and no entry is needed for it in the program’s interface file.
(2)
The call to IMG_FREE will delete the temporary image.
4.1.2 Using “images” which are not 2-dimensional

IMG subroutines can access “image” data with between 1 and 3 dimensions. This allows you to handle spectra (1-D) and cubes (3-D) as well as traditional 2-dimensional images. The subroutine calls needed are almost identical to the normal ones (above) that assume 2 dimensions. They differ only in having more or less arguments for the dimension sizes. The following illustrates the possibilities:

        CALL IMG_IN1( ’SPECTRUM’, NX, IP, ISTAT )
        CALL IMG_IN2( ’IMAGE’, NX, NY, IP, ISTAT )
        CALL IMG_IN3( ’CUBE’, NX, NY, NZ, IP, ISTAT )

Note how the number of dimensions is specified by appending a number to the routine name, and how IMG_IN2 is just a synonym for ??x]]IMG_IN (2 dimensions are assumed if you do not say otherwise).

As well as accessing input images, there are equivalent routines for performing most other IMG operations on images with between 1 and 3 dimensions. For instance, the following would create a new 3-dimensional image:

        CALL IMG_NEW3( ’CUBE’, NX, NY, NZ, IP, ISTAT )

When accessing an existing image with (say) 2 dimensions, you will also be able to access an appropriate slice from (say) a 3-dimensional image if you specify this when you are prompted for the image name (see §5.1).

4.1.3 Accessing images using different data types

IMG also allows access to images using data types other than REAL (which is the default). The most useful of these are INTEGER, INTEGER*2, and DOUBLE PRECISION. The subroutine calls needed are almost identical to the normal ones except that you should append a character code to the routine name to indicate the data type you require. The character codes (and their data types) are:




Character code Fortran-77 data type Description



R REAL Single precision
D DOUBLE PRECISION Double precision
I INTEGER Integer



W INTEGER*2 Word
UW INTEGER*2 Unsigned word
B BYTE Byte
UB BYTE Unsigned byte



so among the possibilities are the calls:
        CALL IMG_IND( ’IMAGE’, NX, NY, IP, ISTAT )              [1]
        CALL IMG_INI( ’IMAGE’, NX, NY, IP, ISTAT )              [2]
        CALL IMG_INR( ’IMAGE’, NX, NY, IP, ISTAT )              [3]

The following notes refer to the numbered statements:
(1)
Access an image using DOUBLE PRECISION.
(2)
Access an image using INTEGER.
(3)
Access an image using REAL. Note that this is a synonym for ??x]]IMG_IN, since it also gets a 2-D REAL image.

You should declare the image array in your program to have the corresponding Fortran data type, as in the following example that accesses and modifies an image using INTEGER format:

  
  *  Access an input image, allowing it to be modified.
        CALL IMG_MODI( ’IN’, NX, NY, IP, ISTAT )                [1]
  
  *  Fill the array with zeros.
        CALL DOZERO( %VAL( IP ), NX, NY, ISTAT )
  
  *  Free the image.
        CALL IMG_FREE( ’IN’, ISTAT )
        END
  
        SUBROUTINE DOZERO( IMAGE, NX, NY, ISTAT )
        INCLUDE ’SAE_PAR’
        INTEGER IMAGE( NX, NY )                                 [2]
  
        IF ( ISTAT .NE. SAI__OK ) RETURN
        DO 1 J = 1, NY
           DO 2 I = 1, NX
              IMAGE( I, J ) = 0
   2       CONTINUE
   1    CONTINUE
        END

The following notes refer to the numbered statements:
(1)
An INTEGER input image is accessed for modification
(2)
In the subroutine DOZERO the image is declared as an adjustable dimension INTEGER array.

All IMG subroutines that access images have variants that allow you to use different data types. There are also versions that allow these to be mixed with different numbers of data dimensions. Some possibilities are:

        CALL IMG_IN1D( ’SPECTRUM’, NX, IP, ISTAT )              [1]
        CALL IMG_MOD2I( ’IMAGE’, NX, NY, IP, ISTAT )            [2]
        CALL IMG_NEW3W( ’CUBE’, NX, NY, NZ, IP, ISTAT )         [3]
        CALL IMG_IN2R( ’IMAGE’, NX, NY, IP, ISTAT )             [4]

The following notes refer to the numbered statements:
(1)
This call accesses a 1-D image for reading using DOUBLE PRECISION.
(2)
This call accesses a 2-D image for modification using the INTEGER data type.
(3)
This call creates a new 3-D data cube with the word (Fortran INTEGER*2) data type.
(4)
This call is a synonym for ??x]]IMG_IN, since it gets a 2-D REAL image.

If your image isn’t stored with the type that you ask for, then it will be converted from the stored type (if possible). If you modify the data it will be re-converted to the storage type when the image is freed. New images are stored using the data type you ask for.

The data types unsigned word (UW), signed byte (B) and unsigned byte (UB) are less commonly used. Indeed, the unsigned data types have no equivalents in Fortran and should be manipulated using the PRIMDAT library (SUN/39).

4.1.4 Accessing multiple images

IMG can access more than one image at a time using a single subroutine call. An example of this is:

  
  *  Declare pointers.
        INTEGER IP( 3 )
  
  *  Access images.
        CALL IMG_IN( ’BIAS,FLAT,RAW’, NX, NY, IP, ISTAT )       [1]
  
  *  Create a new output image by copying the RAW input image.
        CALL IMG_OUT( ’RAW’, ’PROC’, IPPROC, ISTAT )            [2]
  
  *  Debias and flatfield data.
        CALL DOPROC( %VAL( IP( 1 ) ), %VAL( IP( 2 ) ),          [3]
       :             %VAL( IP( 3 ) ), NX, NY, %VAL( IPPROC ),
       :             ISTAT )
  
  *  Free all the images.
        CALL IMG_FREE( ’*’, ISTAT )
        END
  
        SUBROUTINE DOPROC( BIAS, FLAT, RAW, NX, NY, PROC, ISTAT )
        INCLUDE ’SAE_PAR’
        REAL BIAS( NX, NY ), FLAT( NX, NY ), RAW( NX, NY ),     [4]
       :     PROC( NX, NY )
  
        IF ( ISTAT .NE. SAI__OK ) RETURN
        DO 1 J = 1, NY
           DO 2 I = 1, NX
              PROC( I, J ) = ( RAW( I, J ) - BIAS( I, J ) ) / FLAT( I, J )
   2       CONTINUE
   1    CONTINUE
        END

The following notes refer to the numbered statements:
(1)
This call accesses three images ’BIAS’, ’FLAT’ and ’RAW’. Prompts for the actual image files will be made for each of these names and pointers to the image data will returned in IP, which should now be an array of size 3.
(2)
The image ’RAW’ is copied to a new file. This also copies any header information, so is usually preferred to using ??x]]IMG_NEW.
(3)
The individual pointers are now passed to a subroutine so that the ’BIAS’, ’FLAT’ and ’RAW’ images together with the copy of ’RAW’ can be accessed.
(4)
The image arrays are declared as normal.

There are two advantages to accessing multiple images in one call in this way:

(1)
All the images are made available to the program as if they were the same size, regardless of their actual sizes (the largest region that is common to all the images is selected).
(2)
All the images are made available with the same data type (REAL in this case).

This means that corresponding values in each of the images can be directly inter-compared by your program, even if the images themselves have different sizes or data types.

4.1.5 Creating multiple images

IMG can also create more than one image using a single subroutine call. Some possibilities are:

        CALL IMG_NEW( ’MODEL1,MODEL2’, 1024, 1024, IP, ISTAT )  [1]
        CALL IMG_OUT( ’TEMPLATE’, ’OUT1,OUT2’, IP, ISTAT )      [2]
        CALL IMG_TMP( ’T1,T2,T3,T4’, NX, NY, IP, ISTAT )        [3]

The following notes refer to the numbered statements:
(1)
The call to ??x]]IMG_NEW creates two new images with size 1024 × 1024. The variable IP should be declared as an INTEGER array of size 2.
(2)
The image associated with parameter ’TEMPLATE’ is copied to two new images ’OUT1’ and ’OUT2’. Again, the pointers are returned in an array IP(2).
(3)
Four temporary images of the same size and type are created.
4.1.6 Handling “bad” data

In practice, astronomical images will often contain values that are unreliable (or unknown) and should be ignored. For example, you might not be able to compute a result for every element of an image array (say where a divide by zero would occur), or your detector system may not generate sensible values everywhere. These missing data are usually flagged by setting them to a unique number that allows them to be recognised, and this is known as the “bad” data value.2

Unfortunately, unless you have complete control over your data, you cannot usually ignore the possibility that an image may contain some of these “bad” values. If you did, and processed the numbers as if they were valid data, you would at best get the wrong answer. More probably, your program would crash.

To prevent you being baffled by this pitfall, IMG will normally check whether your input images contain any bad values and will issue a warning if any are found. Then at least you know why your program crashed!

You can deal with bad data values in one of two ways. The simplest is to use a suitable program to detect them and replace them with a sensible alternative value (for example, the KAPPA - SUN/95 - program NOMAGIC will do this). Alternatively, you can modify your IMG program to take proper account of them.

If you decide to modify your program, you’ll probably want to inhibit the checks that IMG makes. This is done by appending an exclamation mark (!) to the parameter name of each affected image. So, for instance:

        CALL IMG_IN( ’BIAS!,FLAT!,RAW!’, NX, NY, IP, ISTAT )

inhibits bad data value checking for all the input images.

The following example is a version of the mean.f program (see §2.1) that shows the sort of changes needed to deal with bad data:

        SUBROUTINE MEAN( ISTAT )
  
  *  Access an input image. Inhibit checks for bad pixels.
        CALL IMG_IN( ’IN!’, NX, NY, IP, ISTAT )                 [1]
  
  *  Derive the mean and write it out.
        CALL DOSTAT( %VAL( IP ), NX, NY, ISTAT )
  
  *  Free the input image.
        CALL IMG_FREE( ’IN’, ISTAT )
        END
  
        SUBROUTINE DOSTAT( IMAGE, NX, NY, ISTAT )
        INCLUDE ’SAE_PAR’
        INCLUDE ’PRM_PAR’                                       [2]
        REAL IMAGE( NX, NY )
  
        IF ( ISTAT .NE. SAI__OK ) RETURN
  
  *  Initialise the sum and loop over all elements of the image, checking
  *  that every data value is not bad.
        SUM = 0.0
        N = 0
        DO 1 J = 1, NY
           DO 2 I = 1, NX
              IF ( IMAGE( I, J ) .NE. VAL__BADR ) THEN          [3]
                 SUM = SUM + IMAGE( I, J )
                 N = N + 1
              END IF
   2       CONTINUE
   1    CONTINUE
  
  *  Write out the mean value.
        IF ( N .GT. 0 ) THEN
           WRITE( *, * ) ’Mean of ’, N, ’ values = ’, SUM / REAL( N )
        ELSE
           WRITE( *, * ) ’Error: all data values are bad’
        END IF
        END

The following notes refer to the numbered statements:
(1)
An input image is accessed without performing any checks for the presence of bad data.
(2)
The file ‘PRM_PAR’ is included. This defines Fortran parameters for the values used to flag bad data (it should be used by all programs that handle bad data). The parameter names are type-dependent and have the form VAL__BAD[x], where [x] is replaced by the character code for the data type you’re processing. These codes are the same as those used when accessing images of different data types – see §4.1.3. So, for instance, with REAL data you would use VAL__BADR, while with INTEGER data you would use VAL__BADI, etc. Before compiling a program that includes this file you should execute the command prm_dev, which creates a soft link to the include file.
(3)
Since every input value may now potentially be bad, each one must be checked before use. As the data type being processed is REAL, the bad data value we test against is VAL__BADR.

If your program accesses more than one input image, you must be careful to check each of them. For instance if you were adding two images together you’d need to do something like the following:

        DO 1 I = 1, NX
           IF ( A( I ) .NE. VAL__BADI .AND. B( I ) .NE. VAL__BADI ) THEN
              C( I ) = A( I ) + B( I )
           ELSE
              C( I ) = VAL__BADI
           END IF
   1    CONTINUE

Where A and B are the “images” to be added and C is the image to store the result (in this case the images are really 1-D INTEGER spectra). Note how a bad value is assigned to the output image if we are unable to calculate a result.

If you need to know more about how to handle bad data you should consult SUN/33.

4.2 More advanced header access

4.2.1 Accessing header items using different data types

Header items can come in more than one data type, so extended HDR subroutines (as for IMG) are provided to access them. If the item isn’t of the type you want, then a format conversion will be attempted. The way you specify the type you require is similar to that used for IMG routines – you just append the necessary character code to the routine name:




Character code Fortran-77 data type Description



C CHARACTER Character string
D DOUBLE PRECISION Double precision
I INTEGER Integer
L LOGICAL Logical
R REAL Single precision



Since all header item values can be converted to a character representation, this is the safest method to access an item of unknown type (which is why it is the method used by the ordinary ??HDR_IN subroutine). Some of the possible calls are:
       CALL HDR_INR( ’IN’, ’ ’, ’BSCALE’, 1, BSCALE, ISTAT )    [1]
       CALL HDR_INI( ’IN’, ’ ’, ’BINNED’, 1, IBFACT, ISTAT )    [2]
       CALL HDR_OUTL( ’OUT’, ’ ’, ’CHECKED’,                    [3]
       :              ’Data checked for C/Rs’, .TRUE., ISTAT )

The following notes refer to the numbered statements:
(1)
This example reads in a REAL value BSCALE.
(2)
This example reads in an INTEGER value BINNED.
(3)
This example writes out a LOGICAL value CHECKED with the comment ’Data checked for C/Rs’.
4.2.2 Using header items from different sources

So far, header items have been assumed to originate only from one source (FITS). In reality, an image may have more than one set of header information, and these are distinguished by giving them different names. FITS headers are normally used for storing information that may be widely distributed, perhaps to people using different data reduction systems, whereas more private collections of header information may be created by using a name of your own choice. Private header information has the advantage that it is unlikely to be trampled on by other software (that believes it knows what the headers mean – rightly or wrongly) and is typically used for information needed only within a particular software package.3

Using header items from other sources is achieved simply by replacing ’ ’ (which is a synonym for ’FITS’) with the appropriate name. Storing information in a named source solely used by your programs is encouraged. This is better than trusting other programs not to modify values that you have set. ’MYSOURCE’ is used in the following examples:

  *  Write a new header item.
        CALL HDR_OUT( ’OUT’, ’MYSOURCE’, ITEM, ’ ’, VALUE, ISTAT )
        ...
  *  Read back the value.
        CALL HDR_IN( ’IN’, ’MYSOURCE’, ITEM, 1, VALUE, ISTAT )

4.2.3 Accessing header items by index

There are two HDR routines that allow you to access header items using an index number. Header item index numbers run from 1 through to the maximum number of items available. These facilities can be used to list all the header items, or to test for the existence of items with particular names.

The following program snippet shows how to list all the header items associated with an image. It uses the HDR_NUMB subroutine to query the number of items. The complete example is called hdrlist.f – see §2.

  *  See how many header items are available.
        CALL HDR_NUMB( ’IN’, ’ ’, ’*’, N, ISTAT )            [1]
        DO 1 I = 1, N
  
  *  Get the name of the I’th header item.
           CALL HDR_NAME( ’IN’, ’ ’, I, ITEM, ISTAT )        [2]
  
  *  Get its value.
           CALL HDR_IN( ’IN’, ’ ’, ITEM, 1, VALUE, ISTAT )   [3]
  
  *  And write it out.
           WRITE( *, ’( 1X, 3A )’ ) ITEM ,’ = ’, VALUE       [4]
   1    CONTINUE

The following notes refer to the numbered statements:
(1)
The subroutine HDR_NUMB counts the number of header items or the number of occurrences of an item. The ’*’ argument indicates that all the header items are to be counted. If an item name was given the number of occurrences of that item would be returned (zero if none exist).
(2)
Item names may be queried by using an index number. This provides a method of getting all the names when the exact contents of a header item source are not known.
(3)
??HDR_IN returns the value of the header item as a character string.
(4)
The name and value of the header item are written out.
4.2.4 Special behaviour of FITS headers

A complication that we have glossed over so far is the fact that certain FITS header items can occur more than once within an image. Strictly, this is limited to the keywords COMMENT, HISTORY and ’ ’ (blank) which are often used to construct multi-line descriptions of the contents or history of a FITS file. To handle this, various of the HDR subroutines are “FITS occurrence aware”. For instance, the fourth argument of the ??HDR_IN subroutine specifies the occurrence of the item to be read, and ??HDR_OUT will append (rather than overwriting) items with these special names.

FITS items can also have a comment associated with them (to be read by the recipient when a FITS file is sent to another astronomical institution). The comment string is given by the fourth argument of the HDR_OUT subroutine, but this facility isn’t available for non-FITS header sources where this argument will simply be ignored.

4.2.5 Hierarchical header items

Header items can be stored in hierarchical structures.4 What this actually means is that a header item can be used as a “container” to store other items (which are known as components). Hierarchical items are distinguished by using a period sign in the name to separate their various components. So, for instance, if you had a series of values that described a useful region on an image you might write these out using something like:

  *  Store the useful region of this image.
        CALL HDR_OUTI( ’OUT’, ’MYSOURCE’, ’USEFUL.MINX’, ’ ’,IXLOW, ISTAT )
        CALL HDR_OUTI( ’OUT’, ’MYSOURCE’, ’USEFUL.MAXX’, ’ ’,IXHIGH, ISTAT )
        CALL HDR_OUTI( ’OUT’, ’MYSOURCE’, ’USEFUL.MINY’, ’ ’,IYLOW, ISTAT )
        CALL HDR_OUTI( ’OUT’, ’MYSOURCE’, ’USEFUL.MAXY’, ’ ’,IYHIGH, ISTAT )

The number of sub-components and the level of hierarchy isn’t restricted, so more elaborate structures are possible (but not necessarily desirable).

You can examine the header items in an image by using the HDSTRACE program (SUN/102). If your image file is called image then you would use the command:

  %  hdstrace image.more.mysource

to see the contents of the source ’MYSOURCE’. To list the contents of a FITS source the most convenient method is to use the KAPPA program FITSLIST (SUN/95).

If you have any hierarchical FITS headers (these were once used extensively by UK observatories), then they should look something like:

          ING DETHEAD = ’CCD-RCA2’       /Type of detector head

when examined by FITSLIST. To access this item using IMG you should call it ’ING.DETHEAD’. The value of this item is shown being read by the example program hdrread.f (see §2) next:
  % hdrread
  ITEM - Header item > ING.DETHEAD
  IN - Input image > image
  The header item ’ING.DETHEAD’ has a value of CCD-RCA2.
  %

Hierarchical items are processed as normal by the HDR indexing routines4.2.3). Each component item counts only once and no significance is attached to the fact it may be part of a hierarchy. When retrieving an indexed header item, its name is returned in full, including all the necessary periods.

2It may also be known as the “invalid”, “flagged” or even “magic” value, but the meaning is the same.

3The Starlink application packages CCDPACK (SUN/139) and POLPACK (SUN/223) make extensive use of these facilities.

4Warning: you should not write hierarchical FITS header items as this violates the FITS standard (although data from outside sources may sometimes contain them).