view doc/prog_guide.tex @ 1557:85a970da7e7e

* INDENTATION changes only (merging my and peter neelins code!)
author rotor <rotor>
date Wed, 20 Aug 2003 05:52:55 +0000 (2003-08-20)
parents ad918afde307
children
line wrap: on
line source
%
% Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, 
% Montreal Neurological Institute, McGill University.
% 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.  The author and McGill University
% make no representations about the suitability of this
% software for any purpose.  It is provided "as is" without
% express or implied warranty.
%
\documentclass{article}
\title{MINC Programmer's Guide}
\author{Peter Neelin}
\date{May 14, 1993}
\textwidth 6.0in
\oddsidemargin 0.125in
\textheight 8.5in
\topmargin -0.75in

\begin{document}

\maketitle

\tableofcontents

\clearpage

\section{Introduction}

The MINC file format (Medical Image NetCDF) is based on the NetCDF
file format (Network Common Data Form) distributed by the Unidata
Program Center. NetCDF provides a software interface for storing
named, multi-dimensional variables in files in a machine-independent
way. This interface removes applications from the concerns of
portability and file structure and encourages a self-describing form
of data.

Each NetCDF multi-dimensional variable in a file is described by a
name, by its dimensions and by attributes. For example, an image
stored in a file might be stored as byte data in a variable called
``\verb+image+'', with dimensions ``\verb+x+'' and ``\verb+y+'' (each
of length 256) and with an attribute called ``\verb+long_name+'' which
is a string describing the content of the image. Many variables can be
stored in one file and each variable can have many attributes.
Dimensions exist independently of variables and can subscript more
than one variable.

MINC provides three things on top of the NetCDF interface. It provides
a standard for dimension, variable and attribute names suitable for
medical imaging, it provides some convenience functions to complement
the NetCDF interface (not specific to the MINC conventions) and it
provides convenience functions for using MINC files.

\section{An Introduction to NetCDF}

(For a complete description, see the ``NetCDF User's Guide'').

\subsection{The NetCDF file}

It is useful to look at an example file while considering the NetCDF
interface. Fortunately, the NetCDF package provides utilities (ncdump
and ncgen) for converting the binary NetCDF files to an ascii format
call CDL. A simple NetCDF file, converted to CDL notation by ncdump,
is given below:

\begin{verbatim}
netcdf test {
dimensions:
	ycoord = 3 ;
	xcoord = 4 ;

variables:
	double image(ycoord, xcoord) ;
		image:long_name = "My favorite tiny image" ;
	double xcoord(xcoord) ;

data:

 image =
  1, 2, 3, 4,
  5, 6, 7, 8,
  9, 10, 11, 12 ;

 xcoord = 100, 200, 300, 400 ;
}
\end{verbatim}

The sample file stores a 3 by 4 image of double precision values. The
first thing defined are the dimensions : \verb+xcoord+ and
\verb+ycoord+. Dimensions can represent physical dimensions like x
coordinate, y coordinate etc., or they can represent abstract things
like lookup-table index.  Each dimension has a name and a length and
when joined with other dimensions defines the shape of a variable ---
the variable image is subscripted by \verb+ycoord+ and \verb+xcoord+.
Dimensions can also be used across variables, relating them to each
other. For example, if the file contained another image also
subscripted by \verb+ycoord+ and \verb+xcoord+, we would have the
important information that the two variables were sampled on the same
grid.  Also, coordinate systems can be defined by creating variables
by the same name as the dimension, like \verb+xcoord+ in the example
above, giving the x coordinate of each point in the image.

Variables are the next thing defined in the cdl file. Each variable
has a name, data type and a shape specified by a list of dimensions
(up to a maximum of \verb+MAX_VAR_DIMS+ = 32 dimensions per variable).
The data types are \verb+NC_CHAR+, \verb+NC_BYTE+, \verb+NC_SHORT+,
\verb+NC_INT+, \verb+NC_FLOAT+ and \verb+NC_DOUBLE+. Information
about each variable is stored in attributes.  The attribute
``\verb+long_name+'' gives a character string describing the variable
``\verb+image+''. Attributes are either scalars or vectors of one of
the six types listed above (a character string is a vector of type
\verb+NC_CHAR+).

\subsection{Programming with NetCDF}

Programming with NetCDF can be quite simple. The file listed above was
produced by the following program:

\begin{verbatim}
#include <netcdf.h>

#define THE_NAME "My favorite tiny image"
static double vals[][4]={
   1.0, 2.0, 3.0, 4.0,
   5.0, 6.0, 7.0, 8.0,
   9.0,10.0,11.0,12.0
};
static int ysize=sizeof(vals)/sizeof(vals[0]);
static int xsize=sizeof(vals[0])/sizeof(vals[0][0]);

static double xcoord[]={100.,200.,300.,400.};

main()
{
   int cdf, img, xvar;
   int dim[MAX_VAR_DIMS];
   long count[MAX_VAR_DIMS], start[MAX_VAR_DIMS];

   /* Create the file */
   cdf=nccreate("test.cdf",NC_CLOBBER);

   /* Define the dimensions */
   dim[0]=ncdimdef(cdf, "ycoord", ysize);
   dim[1]=ncdimdef(cdf, "xcoord", xsize);

   /* Define the variables */
   img=ncvardef(cdf, "image", NC_DOUBLE, 2, dim);
   xvar=ncvardef(cdf,"xcoord", NC_DOUBLE, 1, &dim[1]);

   /* Add an attribute */
   ncattput(cdf, img, "long_name", NC_CHAR, strlen(THE_NAME)+1, THE_NAME);

   /* End definition mode */
   ncendef(cdf);

   /* Write the variable values */
   start[0]=start[1]=0;
   count[0]=ysize; count[1]=xsize;
   ncvarput(cdf, img, start, count, vals);
   ncvarput(cdf, xvar, &start[1], &count[1], xcoord);
   ncclose(cdf);
}
\end{verbatim}

The first executable line of the program creates a new NetCDF file. An
open file is either in ``define'' mode or in ``data'' mode. In define
mode, dimensions, variables and attributes can be defined, but data
cannot be written to or read from variables. In data mode, variable
values can be written or read, but no changes to dimensions or
variables can be made and attributes can only be written if they exist
already and will not get larger with the write. Newly created files
are automatically in define mode.

The lines following the call to nccreate define the dimensions and
variables in the file. Notice that the NetCDF file, dimensions and
variables are all identified by an integer returned when they are
created. These id's are subsequently used to refer to each object. The
attribute ``\verb+long_name+'' for the image variable is identified
only by its name.

Once everything is defined, ncendef puts the file into data mode and
values are written. The values to write are defined by a vector of
starting indices and a vector of the number of values to write in each
dimension. This defines a multi-dimensional rectangle within the
variable called a hyperslab. In the C interface, the first element of
the vector refers to the slowest varying index of the variable, so in
this example, the array vals has the \verb+xcoord+ varying fastest. In
the FORTRAN interface, the convention has the first subscript varying
fastest. These conventions follow the language conventions for
multi-dimensional arrays.

\section{The MINC format}

It is possible to build MINC format files using only NetCDF function
calls, however a C include file is provided to facilitate (and help
ensure) adherence to the standard. This file defines useful constants,
standard dimension, variable and attribute names and some attribute
values. It also declares the functions defined in a library of minc
convenience functions, designed to make an application programmer's
life easier.

\subsection{The MINC standard}

Various requirements for file formats have been put forward. One such
list is as follows: A protocol should be: 1) simple, 2) self
describing, 3) maintainable, 4) extensible, 5) N dimensional, and 6)
have a universal data structure. The NetCDF format meets all of these
requirements, suggesting that it is a good place to start. I would,
however, add some more requirements to the list. Implied in the above
list is the requirement that there be a standard for accessing data
(how do I get the patient name) --- this is not provided by NetCDF.
Furthermore, a useful format should come with a software interface
that makes it easy to use, particularly in a development environment.
Finally, a format that stores many associated pieces of information
should also provide some data organization.

The MINC format attempts to add these things to the NetCDF format.

\subsubsection{MINC variable types}

Medical imaging tends to produce files with a large amount of
ancillary data (patient information, image information, acquisition
information, etc.). To organise this information in a useful fashion,
MINC uses variables to group together related attributes. The variable
itself may or may not contain useful data. For example, the variable
\verb+MIimage+ contains the image data and has attributes relevant to this
data. The variable \verb+MIpatient+ has no relevant variable data, but
serves to group together all attributes describing the patient (name,
birthdate, etc.). This sort of variable is called a group variable.

Variables that correspond to dimensions are called dimension variables
and describe the coordinate system corresponding to the dimension. An
example is MIxspace --- both a dimension and a variable describing the
x coordinate of other variables.

The NetCDF conventions allow for these dimension variables to specify
the coordinate at each point, but there is nothing to describe the
width of the sample at that point. MINC provides the convention of
dimension width variables, e.g. \verb+MIxspace_width+, to give this
information.

Finally, it is possible to have attributes that vary over some of the
dimensions of the variable. For example, if we have a volume of image
data, varying over \verb+MIxspace+, \verb+MIyspace+ and
\verb+MIzspace+, we may want an attribute giving the maximum value of
the each image, varying over \verb+MIzspace+. To achieve this we use a
variable, called a variable attribute, pointed to by an attribute of
the image variable.

Thus MINC introduces a number of types of variables: group variables,
dimension variables, dimension width variables and variable
attributes.

\subsubsection{Data organization}

MINC attempts to provide some level of data organization through a
hierarchy of group variables. As mentioned above, attributes are
grouped according to type in group variables. Each group variable can
have an \verb+MIparent+ and an \verb+MIchildren+ attribute --- the
former specifying the name of another variable that is above this one
in the hierarchy, the latter specifying a newline-separated list of
variables that come below this one in the hierarchy. At the root of
the hierarchy is the \verb+MIrootvariable+ variable, with no parent.
Although it is not necessary to make use of this structure, it can
provide a mechanism for ordering large amounts of information.

\subsubsection{MINC dimension, variable and attribute names}

The NetCDF format says nothing about variable and dimension naming
conventions and little about attribute names. It does provide a few
standards, such as the attribute ``\verb+long_name+'' for describing a
variable, which have been adopted by the MINC standard. MINC defines a
set of standard names for commonly used entities and the include file
defines constants specifying these names. These are described at
length in the MINC reference manual. The most interesting of these is
\verb+MIimage+, the name of the variable used for storing the actual image
data in the file.

\subsubsection{Image dimensions}

The MINC standard gives some special status to the concept of an
image. There is nothing inherent in NetCDF that suggests any special
status for particular dimensions, but it can be convenient to place
limitations on what can vary over which dimensions in an imaging
context. For example, the requirement that the variables that specify
how to rescale images (see later section on pixel values) not vary
with image dimensions means that we can treat the image as a simple
unit. In the simplest case, the image dimensions are simply the two
fastest varying dimensions of the \verb+MIimage+ variable.

It can also be helpful to allow for vector fields --- images or image
volumes that have a vector of values at each point. A simple example
of a vector field is an RGB image. At each point in space, there are
three values: red, green and blue. The dimension
\verb+MIvector_dimension+ is used for the components of the vector and
it should be the fastest varying dimension in the \verb+MIimage+ variable. If
it is present, then the three fastest varying dimensions of \verb+MIimage+
are the image dimensions.

\subsubsection{MINC coordinate system}

The MINC standard defines how spatial coordinates should be oriented
relative to patients. Files are free to have data stored in the
desired direction, but positive world coordinates are given a definite
meaning in the medical imaging context. The standard is that the
positive x axis points from the patient's left to right, the positive
y axis points from posterior to anterior and the positive z axis
points from inferior to superior.

The conversion of element index to world coordinates is done using the
dimension variable attributes \verb+MIdirection_cosines+,
\verb+MIstep+ and \verb+MIstart+. If the direction cosines are 
${\bf c}=(c_x, c_y, c_z)$, then the vector between adjacent elements
along an axis is $step\times {\bf c}$. If $start(i)$ and 
${\bf c}(i)$ are the \verb+MIstart+ and
\verb+MIdirection_cosines+ attributes for dimension $i$ (one of
\verb+MIxspace+, \verb+MIyspace+ and \verb+MIzspace+),
then the first element of the image variable is at world coordinate
$ \sum_i start(i) {\bf c}(i) $.

If the direction cosines are not present, then they are
assumed to be $(1,0,0)$ for \verb+MIxspace+, $(0,1,0)$ for
\verb+MIyspace+ and $(0,0,1)$ for \verb+MIzspace+. Direction cosines are
unit vectors and should be normalized. As well, the step attribute
should carry the information about axis flipping (negative or
positive) rather than the direction cosine.

\subsubsection{Pixel values and real values}

In medical imaging, pixel values are frequently stored as bytes or
shorts, but there is a generally a real value associated with each
pixel as well. This real value is obtained by a scale factor and
offset associated with each image or image volume. The MINC standard
indicates how pixel values should be interpreted. 

Image data in the \verb+MIimage+ variable can be stored as bytes, shorts,
ints (32-bit), floats or doubles. NetCDF conventions use the attributes
\verb+MIvalid_range+ or \verb+MIvalid_max+ and \verb+MIvalid_min+ to
indicate the range of values that can be found in the variable. For
short values, for example, we might have a valid range of 0 to 32000.
To convert these integers to real values, we could use a scale and
offset. However, these values would have to change if the data where
converted to bytes in the range 23 to 228. If we specify an image
maximum and minimum to which \verb+MIvalid_max+ and \verb+MIvalid_min+
should be mapped by an appropriate scale and offset, then we can
convert type and valid range without having to change the real maximum
and minimum.  To allow the maximum dynamic range in an image, we use
the variables \verb+MIimagemax+ and \verb+MIimagemin+ to store the
real maximum and minimum --- these can vary over any of the non-image
dimensions of \verb+MIimage+.

\subsection{General convenience functions}

MINC provides a number of convenience functions that have nothing to
do with medical imaging, but that make the use of NetCDF files a
little easier. One of the drawbacks of the NetCDF format is that data
can come in any form (byte, short, int, float, double) and the
calling program must handle the general case. Rather than restrict
this, MINC provides functions to convert types.

The first set of convenience functions are for type conversion. A list
follows : 
\begin{itemize}
   \item {\bf \verb+miattget+} - reads an attribute vector, specifying
      the numeric type desired and the maximum number of values to read.
   \item {\bf \verb+miattget1+} - reads one attribute value of the
      specified type. 
   \item {\bf \verb+miattgetstr+} - read a character attribute of a specified
      maximum length.
   \item {\bf \verb+miattputdbl+} - write a double precision attribute.
   \item {\bf \verb+miattputstr+} - write a string attribute.
   \item {\bf \verb+mivarget+} - get a hyperslab of values of the
      specified type. 
   \item {\bf \verb+mivarget1+} - get a single value of the specified type.
   \item {\bf \verb+mivarput+} - put a hyperslab of values of the
      specified type. 
   \item {\bf \verb+mivarput1+} - put a single value of the specified type.
\end{itemize}

Next we have some functions for handling coordinate vectors :
\begin{itemize}
   \item {\bf \verb+miset_coords+} - set a vector of coordinates to a
      single value. 
   \item {\bf \verb+mitranslate_coords+} - translate the coordinates for one
      variable to a vector for subscripting another variable.
\end{itemize}

Finally, there are functions for dealing with variables as groups of
attributes, making it easier to modify a file while keeping ancillary
information :
\begin{itemize}
   \item {\bf \verb+micopy_all_atts+} - copy all of the attributes of
      one variable to another (possibly across files).
   \item {\bf \verb+micopy_var_def+} - copy a variable definition (including
      attributes) from one file to another.
   \item {\bf \verb+micopy_var_vals+} - copy a variable's values from
      one variable to another (possibly across files).
   \item {\bf \verb+micopy_all_var_defs+} - copy all variable
      definitions from one file to another, excluding a list of variables.
   \item {\bf \verb+micopy_all_var_vals+} - copy all variable values
      from one file to another, excluding a list of variables.
\end{itemize}

\subsection{MINC specific convenience functions}

A few routines are provided to deal with some of the minc structures.
\verb+miattput_pointer+ and \verb+miattget_pointer+ put/get a pointer to a
variable attribute. \verb+miadd_child+ helps maintain the hierarchy of
variables by handling the \verb+MIparent+ and \verb+MIchildren+
attributes of two variables. Finally \verb+micreate_std_variable+ and
\verb+micreate_group_variable+ create some of the standard variables
and fill in a few of the default attributes.

\subsection{Image conversion variables}

One of the requirements for file formats mentioned earlier was
a software interface to make the interface easy to use. The biggest
difficulty in using a flexible format is that the application must
handle many possibilities. Where images are concerned, this means
various data types and scale factors, and images of differing sizes.
The image conversion variable functions of MINC attempt to remove this
complication for the programmer.

An image conversion variable (icv) is essentially a specification of what
the program wants images to look like, in type, scale and dimension.
When an MINC image is read through an icv, it is converted for the
calling program to a standard format regardless of how data is stored
in the file.

There are two categories of conversion: Type and range conversions
change the datatype (and sign) of image values and optionally scale
them for proper normalization. Dimension conversions allow programs to
specify image dimension size and image axis orientation (should
\verb+MIxspace+ coordinates be increasing or decreasing? should the
patient's left side appear on the left or right of the image?).

\subsubsection{ICV routines}

Accessing a file through an icv is a straight-forward process.
Create the icv with \verb+miicv_create+, set properties
(like desired datatype) with the \verb+miicv_set+ routines, attach the
icv to a NetCDF 
variable with \verb+miicv_attach+ and access the data with
\verb+miicv_get+ or \verb+miicv_put+. The icv can be detached from a
NetCDF variable with \verb+miicv_detach+ and can be freed with
\verb+miicv_free+.

Icv properties are strings, integers, long integers or doubles. For
example, \verb+MI_ICV_SIGN+ (the sign of variable values) is a string,
while \verb+MI_ICV_IMAGE_MAX+ (image maximum) is a double precision
value.  Four functions --- \verb+miicv_setint+, \verb+miicv_setlong+,
\verb+miicv_setdbl+ and \verb+miicv_setstr+ ---
are provided to simplify the setting of property values. Programs can
inquire about property values with \verb+miicv_inqint+,
\verb+miicv_inqlong+, \verb+miicv_inqdbl+ and \verb+miicv_inqstr+.

\subsubsection{Type and range conversion}

Pixel values are converted for type and sign by specifying values for
the properties \verb+MI_ICV_TYPE+ and \verb+MI_ICV_SIGN+ (they default
to \verb+NC_SHORT+ and \verb+MI_SIGNED+). Values can also be converted
for valid range and for normalization. These conversions are enabled
by setting \verb+MI_ICV_DO_RANGE+ to \verb+TRUE+ (the default).

If \verb+MI_ICV_DO_NORM+ is \verb+FALSE+ (the default) then only
conversions for valid range are made. This means that if the input
file has shorts in the range 0 to 4095, then they can be converted to
bytes in the range 64 to 248 (for example). The real image maximum and
minimum (\verb+MIimagemax+ and \verb+MIimagemin+) are ignored. The
valid range is specified by the properties \verb+MI_ICV_VALID_MAX+ and
\verb+MI_ICV_VALID_MIN+, which default to the legal range for the type
and sign.

We may want to scale values so that they are normalized either to all
values in the \verb+MIimage+ variable or to some user-defined range.
To do normalization, set \verb+MI_ICV_DO_NORM+ to \verb+TRUE+. Setting
\verb+MI_ICV_USER_NORM+ to \verb+FALSE+ (the default) causes normalization to
the real maximum and minimum of the variable (the maximum of
\verb+MIimagemax+ and the minimum of \verb+MIimagemin+). If
\verb+MI_ICV_USER_NORM+ is true then the values of \verb+MI_ICV_IMAGE_MAX+
and \verb+MI_ICV_IMAGE_MIN+ are used (defaulting to 1.0 and 0.0).

When either \verb+MI_ICV_TYPE+ or the file type is floating-point,
then the conversion to and from real values is always done using the
real image maximum and minimum information. If the internal type is
integer and \verb+MI_ICV_DO_NORM+ is \verb+FALSE+, then the rescaling
is done so that the slice maximum maps to the valid range of the
internal values.

Note that when converting to integer types, values are rounded to the
nearest integer and limited to be within the legal range for the data
type.

The above transformations are simple enough, but the use of
floating-point values adds to the complexity, since in general we do
not want to rescale these values to get the real values. The various
possibilities are described in greater detail below.

\subsubsection{The details of pixel value conversion}

The easiest way to think about the rescaling is through four ranges
(maximum-minimum pairs). In the file variable, values have a
valid range \verb+var_vrange+ and these correspond to real values
\verb+var_imgrange+. The user/application wants to convert real values
\verb+usr_imgrange+ to a useful valid range \verb+usr_vrange+. From
\verb+var_vrange+, \verb+var_imgrange+, \verb+usr_imgrange+ and
\verb+usr_vrange+, we can determine a scale and offset for converting
pixel values: Input values are scaled to real values by
\verb+var_vrange+ to \verb+var_imgrange+ and then scaled again to user
values by \verb+usr_imgrange+ to \verb+usr_vrange+.

If either of the \verb+vrange+ variables are not specified, they default to
maximum possible range for integer types. For floating
point types, \verb+usr_vrange+ is set equal to \verb+usr_imgrange+
so that no conversion of real values is done. 

If normalization is not being done, then for integer types
\verb+var_imgrange+ and \verb+usr_imgrange+ are set to [0,1] (scale
down to [0,1] and scale up again).  When normalizibng,
\verb+usr_imgrange+ is set to either the full range of the variable
([0,1] if not found) or the user's requested range. If the variable
values are floating point, then \verb+var_imgrange+ is set to
\verb+var_vrange+ (no scaling to real values), otherwise
\verb+var_imgrange+ is read for each image (again, [0,1] if not
found).

What this means for reading and writing images is discussed below.

\subsubsection{Reading with pixel conversion}

When reading into internal floating point values, normalization has no
effect. When reading integers without normalization, each image is
scaled to full range. With normalization they are scaled to the
specified range and slices can be compared.

When the input file is missing either
\verb+MIimagemax+/\verb+MIimagemin+ (\verb+var_imgrange+ information)
or \verb+MIvalid_range+, the routines try to provide sensible
defaults, but funny things can still happen. The biggest problem is
the absence of \verb+MIvalid_range+ if the defaults are not correct
(full range for integer values and [0,1] for floating point). When
converting floating point values to an integer type, there will be
overflows if values are outside the range [0,1].

\subsubsection{Writing with pixel conversion}

The conversion routines can be used for writing values. This can be
useful for data compression --- e.g. converting internal floats to
byte values in the file, or converting internal shorts to bytes. When
doing this with normalization (to rescale bytes to the slice maximum,
for example) it is important to write the slice maximum and minimum in
\verb+MIimagemax+ and \verb+MIimagemin+ before writing the slice.

The other concern is that \verb+MIvalid_range+ or \verb+MIvalid_max+
and \verb+MIvalid_min+ be written properly (especially if the defaults
are not correct). When writing floating point values,
\verb+MIvalid_range+ should be set to the full range of values in the
variable. In this case, the attribute does not have to be set
correctly before writing the variable, but if it exists, the values
should be reasonable (maximum greater than minimum and values not
likely to cause overflow). These will be set automatically if the
routine \verb+micreate_std_variable+ is used with
\verb+NC_FILL+ mode on (the default).

\subsubsection{Example: Reading values}

Read an image without normalization:
\begin{verbatim}
   /* Create the icv */
   icv=miicv_create();
   (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
   (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
   (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
   (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);

   /* Open the file, attach the image variable */
   cdfid=ncopen(filename, NC_NOWRITE);

   /* Attach image variable */
   img=ncvarid(cdfid, MIimage);
   (void) miicv_attach(icv, cdfid, img);

   /* Get the data - we assume that coord and count are set properly */
   (void) miicv_get(icv, coord, count, image);

   /* Close the file and free the icv */
   (void) ncclose(cdfid);
   (void) miicv_free(icv);
\end{verbatim}

Read an integer image with normalization:
\begin{verbatim}
   /* Create the icv */
   icv=miicv_create();
   (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
   (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
   (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
   (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
   (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
   (void) miicv_setint(icv, MI_ICV_USER_NORM, TRUE);
   (void) miicv_setdbl(icv, MI_ICV_IMAGE_MAX, 1.83);
   (void) miicv_setdbl(icv, MI_ICV_IMAGE_MIN, -0.57);
   ...
\end{verbatim}

Read a floating point image :
\begin{verbatim}
   /* Create the icv. We don't have to set MI_ICV_USER_NORM to TRUE,
      but doing so ensures that the conversion is done properly
      without looking at file values (the defaults for
      MI_ICV_IMAGE_MAX and MI_ICV_IMAGE_MIN are 1 and 0) */
   icv=miicv_create();
   (void) miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT);
   (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
   (void) miicv_setint(icv, MI_ICV_USER_NORM, TRUE);
   ...
\end{verbatim}

\subsubsection{Example: Writing values}

Writing from floating point to byte values :

\begin{verbatim}
   /* Create the icv */
   icv=miicv_create();
   (void) miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT);
   (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);

   /* Create the file */
   cdf=nccreate(filename, NC_CLOBBER);

   /* Define the dimensions */
   dim[0]=ncdimdef(cdf, MIyspace, ysize);
   dim[1]=ncdimdef(cdf, MIxspace, xsize);

   /* Define the variables */
   img=micreate_std_variable(cdf, MIimage, NC_BYTE, 2, dim);
   (void) miattputstr(cdf, img, MIsigntype, MI_UNSIGNED);
   vrange[0]=0; vrange[1]=200;
   (void) ncattput(cdf, img, MIvalid_range, NC_DOUBLE, 2, vrange);
   max=micreate_std_variable(cdf, MIimagemax, NC_DOUBLE, 0, NULL);
   min=micreate_std_variable(cdf, MIimagemin, NC_DOUBLE, 0, NULL);

   /* End definition mode */
   ncendef(cdf);

   /* Attach image variable */
   (void) miicv_attach(icv, cdf, img);

   /* Write the image max and min */
   ncvarput1(cdf, max, NULL, &image_maximum);
   ncvarput1(cdf, min, NULL, &image_minimum);

   /* Write the image */
   start[0]=start[1]=0;
   count[0]=ysize; count[1]=xsize;
   miicv_put(icv, start, count, vals);

   /* Close the file and free the icv */
   (void) ncclose(cdf);
   (void) miicv_free(icv);
\end{verbatim}

If we were writing a floating point image, the only difference (apart
from changing \verb+NC_BYTE+ to \verb+NC_FLOAT+) would be that we
would rewrite \verb+MIvalid_range+ at the end of the file with the
full range of floating point values.

\subsubsection{Dimension conversion}

One of the problems of arbitrary dimensioned images is that it becomes
necessary for software to handle the general case. It is easier to write
application software if it is known in advance that all images will
have a specific size (e.g. $256\times 256$) and a specific orientation
(e.g. the first pixel is at the patient's anterior, right side). 

By setting the icv property \verb+MI_ICV_DO_DIM_CONV+ to \verb+TRUE+
these conversions can be done automatically. The orientation of
spatial axes is determined by the properties \verb+MI_ICV_XDIM_DIR+,
\verb+MI_ICV_YDIM_DIR+ and \verb+MI_ICV_ZDIM_DIR+. These affect any
image dimensions that are \verb+MI?space+ or \verb+MI?frequency+ where
\verb+?+ corresponds to \verb+x+, \verb+y+ or \verb+z+. These
properties can have values \verb+MI_ICV_POSITIVE+,
\verb+MI_ICV_NEGATIVE+ or \verb+MI_ICV_ANYDIR+. The last of these will
prevent flipping of the dimension. The first two will flip the
dimension if necessary so that the attribute \verb+MIstep+ of the
dimension variable will have the correct sign.

The two image dimensions are referred to as dimensions A and B.
Dimension A is the fastest varying dimension of the two. Setting
properties \verb+MI_ICV_ADIM_SIZE+ and \verb+MI_ICV_BDIM_SIZE+ specify
the desired size for the image dimension. Dimensions are resized so
that the file image will fit entirely in the calling program's image,
and is centred in the image. The size \verb+MI_ICV_ANYSIZE+ allows
one of the dimensions to have a variable size. If property
\verb+MI_ICV_KEEP_ASPECT+ is set to \verb+TRUE+, then the two
dimensions are rescaled by the same amount. It is possible to inquire
about the new step and start, corresponding to attributes
\verb+MIstep+ and \verb+MIstart+ (where pixel position = ipixel*step +
start, with ipixel counting from zero). The properties
\verb+MI_ICV_?DIM_STEP+ and \verb+MI_ICV_?DIM_START+ (\verb+?+ =
\verb+A+ or \verb+B+) are set automatically and can be inquired but
not set.

Although vector images are allowed, many applications would rather
only deal with scalar images (one intensity value at each point).
Setting \verb+MI_ICV_DO_SCALAR+ to \verb+TRUE+ (the default) will
cause vector images to be converted to scalar images by averaging the
components.  (Thus, RGB images are automatically converted to
gray-scale images in this simple way).

It can sometimes be useful for a program to perform dimension
conversions on three (or perhaps more) dimensions, not just the two
standard image dimensions. To perform dimension flipping and/or
resizing on dimensions beyond the usual two, the property
\verb+MI_ICV_NUM_IMGDIMS+ can be set to an integer value between
one and \verb+MI_MAX_IMGDIMS+. To set the size of a dimension,
set the property \verb+MI_ICV_DIM_SIZE+ (analogous to
\verb+MI_ICV_ADIM_SIZE+). To specify the dimension to be set, add
the dimension to the property (adding zero corresponds to the fastest
varying dimension --- add zero for the ``A'' dimension, one for the
``B'' dimension, etc.). Voxel separation and
location can be inquired about through the properties
\verb+MI_ICV_DIM_STEP+ and \verb+MI_ICV_DIM_START+ (analogous to
\verb+MI_ICV_ADIM_STEP+ and \verb+MI_ICV_ADIM_START+), again
adding the dimension number to the property.

\subsubsection{Example: Reading with dimension conversion}

Reading a $256 \times 256$ image with the first pixel at the patient's
inferior, posterior, left side as short values between 0 and 32000:
\begin{verbatim}
   /* Create the icv */
   icv=miicv_create();
   (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
   (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
   (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
   (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
   (void) miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE);
   (void) miicv_setint(icv, MI_ICV_ADIM_SIZE, 256);
   (void) miicv_setint(icv, MI_ICV_BDIM_SIZE, 256);
   (void) miicv_setint(icv, MI_ICV_KEEP_ASPECT, TRUE);
   (void) miicv_setint(icv, MI_ICV_XDIM_DIR, MI_POSITIVE);
   (void) miicv_setint(icv, MI_ICV_YDIM_DIR, MI_POSITIVE);
   (void) miicv_setint(icv, MI_ICV_ZDIM_DIR, MI_POSITIVE);

   /* Open the file, attach the image variable */
   cdfid=ncopen(filename, NC_NOWRITE);

   /* Attach image variable */
   img=ncvarid(cdfid, MIimage);
   (void) miicv_attach(icv, cdfid, img);

   /* Get the data - we assume that coord and count are set properly */
   (void) miicv_get(icv, coord, count, image);

   /* Close the file and free the icv */
   (void) ncclose(cdfid);
   (void) miicv_free(icv);
\end{verbatim}


\end{document}