MINC/SoftwareDevelopment/MINC1-volumeio-programmers-reference
This document describes a library of routines available at the McConnell Brain Imaging Centre (BIC) at the Montreal Neurological Institute. It was developed as part of a medical imaging software testbed by David MacDonald, with source code and considerable input from Peter Neelin, Louis Collins, and others at the Centre.
The library, which is referred to as the BIC Volume IO Library, comprises a set of functions for reading and writing volumes of medical imaging data, as well as some general support routines useful to many programming tasks. Images at the Brain Imaging Centre are stored on disk in a format called MINC, which stands for Medical Image Net CDF. More information is available in related documentation specifically concerning this format and the MINC library of routines used to read and write MINC files.
The BIC Volume IO Library is built on top of the MINC library to provide easy access to MINC files for most general operations, without having to learn too much of the details of the MINC library, which is a comprehensive system for handling most conceivable cases. The BIC Volume IO Library provides a structure for internal storage of volumes and routines to access and modify volumes. In addition, it provides routines to manipulate tag points and transformations and to perform input and output on these objects in the standardized formats of the Brain Imaging Centre.
This document describes where to find the BIC Volume IO Library, what
functionality is provided, and how to integrate it into a user's programs. The
library is written in C source code, and is designed to be linked with C
source. It makes calls to two other libraries: MINC
(the
BIC file format library) and netcdf
(a portable file
format manager).
Compiling and Linking
editThe library for linking is libvolume_io.a
, which is in
/usr/local/lib
, and the related include files are in the
directory, /usr/local/include
under
volume_io
. Both directories are usually in the compiler
search path. Source files making calls to Volume IO functions must have the
following line at the top, to include the relevant type definitions and
prototypes:
#include <volume_io/volume_io.h>
In order to link with BIC Volume IO Library, the relevant libraries must be specified:
cc test_volume_io.o -o test_volume_io \
-lvolume_io -lminc -lnetcdf -lm -lmalloc
The two libraries, minc
and netcdf
are usually in /usr/local/lib
, which is automatically in
the search path. The -lm
option includes the math
library, which is sometimes called by the BIC Volume IO Library. The
-lmalloc
library provides faster and more robust memory
allocation than the default.
Style
editIn order to use this library, one must become accustomed to certain subtleties of style, some of which are peculiar to the author of this software and some of which have evolved in ways dependent on history of the software. Therefore, it is important to note the following style-related issues, for which no apologies are forthcoming.
Global Variables
editThe philosophy that global variables are generally a bad idea has been adopted. In many cases, using global variables within functions hides the behaviour of a function, and therefore, it is preferable to specify all relevant information to a function in the argument list, rather than relying on any global variables. Secondly, modification of a global variable by a given function precludes the use of this function in a multi-threaded environment (two processes may attempt to modify the global variable at the same time). As a result of adopting the anti-global variable philosophy, the user of the BIC Volume IO Library often must specify many arguments to a function. This seems a small price to pay, in return for the ability to see all factors controlling the behaviour of each function in the argument list.
Identifier Names
editThe implementer of this library has an affinity to typing large amounts of
text, and generally dislikes abbreviations. As a result, the function and
variable names occurring in the BIC Volume IO Library may be quite
verbose, almost to the point of being complete grammatically correct
sentences. In general, one will never see functions with names such as
rem_garb()
. Instead, functions are more likely to
have names such as remove_garbage()
, and, in many
cases, even take_garbage_to_curb()
. As a result, the
user of the BIC Volume IO Library will have to type longer-than-usual
function and variable names, but hopefully will have a clearer insight into
the associated functionality.
Structure Arguments
editThere are many objects defined throughout the library as structures of varying and possibly large sizes. Due to the inefficiency of passing structures by value (copying the whole structure to a function), structures are generally passed to functions by reference. Because of this, there is always a potential to pass an invalid pointer to a function which expects a pointer to a structure. The following examples illustrate the right and wrong ways to use such a function. Given a structure and a library function which initializes it,
The correct method of using this function
typedef struct
{
int a, b, c, d;
} big_struct;
void initialize( struct big_struct *s )
{
s->a = 1; s->b = 2; s->c = 3; s->d = 4;
}
The incorrect method of using this function
int main()
{
big_struct *s;
initialize( s ); /* WRONG */
}
because the variable s
is an uninitialized pointer.
The correct method is to define a structure variable, not a pointer to a
structure, and pass a pointer to the structure:
int main()
{
big_struct s;
initialize( &s );
}
Alternately, the incorrect example above could have been corrected by
allocating the s
pointer before calling
initialize
.
Types and Macros
editThere are several types and macros defined for use with the BIC Volume IO
Library. All function declarations in the library are preceded with either the
word public
or private
, which
indicates whether the function is accessible from outside the file in which it
resides. Users of the library will only be interested in those functions
preceded by public
. They are defined as follows:
#define public
#define private static
A type for logical values is defined:
typedef int BOOLEAN
#define FALSE 0
#define TRUE 1
#define OFF FALSE
#define ON TRUE
Other useful types defined include:
typedef double Real;
typedef enum
{ OK, ERROR, INTERNAL_ERROR, END_OF_FILE, QUIT }
Status;
typedef char Smallest_int;
Some macros useful for general programming include:
N_DIMENSIONS
A constant equal to 3, the number of dimensions in the real world.
X
A constant equal to 0, used as an index into various XYZ structures.
Y
A constant equal to 1, used as an index into various XYZ structures.
Z
A constant equal to 2, used as an index into various XYZ structures.
SIZEOF_STATIC_ARRAY( array )
returns the number of elements in a statically allocated array, for example,
{
int size;
int array[] = { 1, 2, 3 };
size = SIZEOF_STATIC_ARRAY( array ); /* == 3 */
}
ROUND( x )
returns the nearest integer to the x
. If halfway in
between two integers, returns the higher of the two. Works correctly
for negative, zero, and positive numbers.
IS_INT( x )
returns TRUE
if the real argument is exactly an integer.
FRACTION( x )
returns the fractional part of the argument.
FLOOR( x )
returns the largest integer less than or equal to the argument.
CEILING( x )
returns the smallest integer greater than or equal to the argument.
ABS( x )
returns the absolute value of an integer or real.
MAX( x, y )
returns the maximum of two integers or reals.
MAX3( x, y, z )
returns the maximum of three integers or reals.
MIN( x, y )
returns the minimum of two integers or reals.
MIN3( x, y, z )
returns the minimum of three integers or reals.
INTERPOLATE( alpha, a, b )
returns the interpolation between a
and b
,
where alpha
is in the range zero to one.
PI
returns the value of Π
DEG_TO_RAD
returns the number of radians per degrees, used to multiply an angle in degrees to result in radians.
RAD_TO_DEG
returns the number of degrees per radian, used to multiply an angle in radians to result in degrees.
IJ( i, j, nj )
converts the indices [i, j]
of a 2-D ni
by
nj
array into a single index, based on row-major order.
IJK( i, j, k, nj, nk )
converts the indices [i, j, k]
of a 3-D ni
by
nj
by nk
array into a single index, based on
row-major order.
for_less( i, start, end )
performs a for loop where i
starts at start
and increments until it is greater than or equal to end
.
Equivalent to for( i = start; i < end; ++i )
.
for_inclusive( i, start, end )
performs a for loop where i
starts at start
and increments until it is greater than end
.
Equivalent to for( i = start; i <= end; ++i )
.
GLUE(x,y)
Special C source macro to stick two different identifiers together, i.e.,
GLUE(colour,_name)
results in colour_name
.
GLUE3(x,y,z)
Special C source macro to stick three different identifiers together, i.e.,
GLUE(a,b,c)
results in abc
.
Programming Utilities
editA set of functions which are useful for general purpose, portable programming is provided. These include such basic areas as strings, time, file IO, etc. Many parts of the BIC Volume IO Library refer to the programming utilities, and it is advisable that users of the BIC Volume IO Library try to use these functions whenever convenient. The following is a list of all the programming utilities, grouped into related areas, and ordered alphabetically.
Strings
editSome simple string manipulation techniques are provided. Strings are arbitrarily
long NULL
-terminated character arrays, which are allocated and
deleted as needed. The type STRING
is defined as:
typedef char *STRING;
The basic string creation and deletion routines are:
public STRING alloc_string(
int length )
public STRING create_string(
STRING initial )
The function alloc_string
allocates storage for a string of the
desired length (by allocating length+1
bytes), without assigning
any value to the string. The function create_string
creates an
allocated string with the value equal to initial
. If
initial
is a NULL
pointer an empty string
(``) is created.
public void delete_string(
STRING string )
The storage associated with a string may be deleted with this function.
public int string_length(
STRING string )
Returns the length of the string.
public BOOLEAN equal_strings(
STRING str1,
STRING str2 )
Returns TRUE
if the two strings are exactly equal.
public void replace_string(
STRING *string,
STRING new_string )
A convenience function which deletes the string
argument, and
reassigns it to the new_string
. It does not copy the value of
new_string
, but merely sets the string
to
the pointer new_string
.
public void concat_char_to_string(
STRING *string,
char ch )
Concatenates a character to the end of the string.
public STRING concat_strings(
STRING str1,
STRING str2 )
Creates and returns a string which is the concatenation of the two arguments, without changing the string arguments.
public void concat_to_string(
STRING *string,
STRING str2 )
Replaces the argument string
with the concatenation of the
arguments string
and str2
.
public BOOLEAN is_lower_case(
char ch )
Returns TRUE
if the character is a lower case letter.
public BOOLEAN is_upper_case(
char ch )
Returns TRUE
if the character is an upper case letter.
public char get_lower_case(
char ch )
If the character is upper case, returns the lower case version of the character, otherwise, returns the character itself.
public char get_upper_case(
char ch )
If the character is lower case, returns the upper case version of the character, otherwise, returns the character itself.
public BOOLEAN string_ends_in(
STRING string,
STRING ending )
Determines if the string ends in the specified ending
. For
instance, passing the arguments, "rainfall"
and
"fall"
returns TRUE
.
public STRING strip_outer_blanks(
STRING str )
Returns a string which is the argument str
without any leading
or trailing blanks.
public void make_string_upper_case(
STRING string )
Converts the string to an all upper case string. Modifies the string in place.
public int find_character(
STRING string,
char ch )
Searches for the given character in the given string
,
returning the index where it was found, or -1 if it was not found.
public BOOLEAN blank_string(
STRING string )
Returns true if the string is empty or consists of only space, tab, and newline characters.
General File IO
editAlthough one may use the standard UNIX file interface (fprintf
,
for example), the BIC Volume IO Library contains a set of routines for doing all
file operations, with the potential to be portable to other operating systems. There
are some other minor advantages, including the automatic expansion of <function>~
</function> and environment variables. Also, automatic decompression of compressed
files is provided.
public STRING expand_filename(
STRING filename )
Searches the argument filename
for certain patterns, and expands
them appropriately, returning the resulting expanded filename. Any sequence of
characters starting with a ~
up to but not including a slash,
/
, will be changed to the specified user's home directory. Any
occurrence of a dollar sign, $
, will result in the following
text, up to the next slash, being expanded to the environment variable specified by
the text. This expansion can be avoided by placing a backslash, \ before any
~
or $
. In all the following functions
which take filenames, automatic expansion of filenames is performed.
public BOOLEAN file_exists(
STRING filename )
Returns TRUE
if the file exists.
public BOOLEAN check_clobber_file(
STRING filename )
public BOOLEAN check_clobber_file_default_suffix(
STRING filename,
STRING default_suffix )
Checks to see if the file exists, and if so, prompts the user as to
whether it may be overwritten. Returns TRUE if either the file
does not exist, or if it does and the user answers `y'
to the
prompt. The second form of the file appends a default suffix to the
filename, if it does not yet have a suffix.
public void remove_file(
STRING filename )
Removes the specified file, which results in the file being removed only after all references to it are closed.
public BOOLEAN filename_extension_matches(
STRING filename,
STRING extension )
Returns TRUE
if the file ends in the given extension, preceded
by a period. e.g., <function>filename_extension_matches( "volume.mnc" , "mnc"
)</function> returns TRUE
. Correctly handles compressed files,
e.g., passing the arguments, "volume.mnc.Z"
and
"mnc"
returns TRUE
.
public STRING remove_directories_from_filename(
STRING filename )
Strips the directories from the filename, returning the result.
public STRING extract_directory(
STRING filename )
Extracts the directory from the filename, returning the result.
public STRING get_absolute_filename(
STRING filename,
STRING directory )
Given a filename and a current directory, returns an absolute filename (one starting
with a slash, /
.
public Status open_file(
STRING filename,
IO_types io_type,
File_formats file_format,
FILE **file )
public Status open_file_with_default_suffix(
STRING filename,
STRING default_suffix,
IO_types io_type,
File_formats file_format,
FILE **file )
The function open_file()
opens the specified file, where
io_type
must be one of WRITE_FILE
or
READ_FILE
, and file_format
must be one of
ASCII_FORMAT
or BINARY_FORMAT
. If
successful, the file pointer is passed back in the last argument and a status of
OK
is returned. Otherwise, a null pointer is passed back, and a
status of ERROR
is returned. Filename expansion is
automatically performed. The second function performs the same task as
open_file
with the addition that it automatically adds the
specified suffix extension, if needed. On input, if the specified file does not
exist and the file does not have an extension, then it looks for the specified file
with the default extension.
public Status close_file(
FILE *file )
Closes the file, returning OK
if successful.
public Status set_file_position(
FILE *file,
long byte_position )
Seeks to the specified position in the file, where 0 corresponds to the beginning of
the file, returning OK
if successful.
public Status flush_file(
FILE *file )
Flushes the file buffer of any pending output, returning OK
if
successful.
public Status input_character(
FILE *file,
char *ch )
Inputs a character from the file and passes it back as the second argument. If the
character is the end of file character, returns a status of
ERROR
, otherwise, OK
.
public Status unget_character(
FILE *file,
char ch )
Places the character back on the input queue.
public Status input_nonwhite_character(
FILE *file,
char *ch )
Inputs the next non-white character, i.e. the next character that is neither a blank, a tab, or a newline character.
public Status skip_input_until(
FILE *file,
char search_char )
Inputs characters from the file until the specified search character is found.
public Status input_string(
FILE *file,
STRING *str,
char termination_char )
Inputs a string from the file. The file is read into the string until either the termination character or a newline character is found. If the string ends at a newline character and the termination character is not a newline, the next character available from the file will be the newline character. Otherwise, the next character available is the one just after the termination character found.
public Status input_quoted_string(
FILE *file,
STRING *str )
Inputs a string from a file, delimited by quotation marks, returning
OK
or ERROR
. After successful reading of
a string, the next character available from the file is the one just after the
ending quotation mark.
public Status input_possibly_quoted_string(
FILE *file,
STRING *str )
Inputs a string from a file, delimited by quotation marks or white space, returning
OK
or ERROR
. After successful reading of
a string, the next character available from the file is the one just after the
ending quotation mark or last non-white space character.
public Status input_line(
FILE *file,
STRING line )
Inputs characters from the file into the argument line
, up
until the next newline character. If a newline was found, the next available
character will be the one after the newline. The newline character is not stored in
the string.
public Status input_boolean(
FILE *file,
BOOLEAN *b )
Inputs the next non-white character. If it is a ``f
or
``F
, then the value FALSE
is passed
back. If it is a ``t
or ``T
, then the
value TRUE
is passed back. Otherwise
ERROR
is returned.
public Status input_short(
FILE *file,
short *s )
public Status input_unsigned_short(
FILE *file,
unsigned short *s )
public Status input_int(
FILE *file,
int *i )
public Status input_real(
FILE *file,
Real *r )
public Status input_float(
FILE *file,
float *f )
public Status input_double(
FILE *file,
double *d )
Inputs a value of the specified type from an ascii file.
public Status input_newline(
FILE *file )
Inputs and discards characters from the file up to and including the next newline
character. Returns OK
if a newline was found, or
ERROR
if the end of file was reached first.
public Status input_binary_data(
FILE *file,
void *data,
size_t element_size,
int n )
Inputs an array of data from a file, in binary format. The array contains
n
elements, each of size element_size
.
Returns either OK
or ERROR
.
public Status output_character(
FILE *file,
char ch )
Outputs a character to the file, returning OK
or
ERROR
.
public Status output_string(
FILE *file,
STRING str )
Outputs the specified string to the file, returning OK
or
ERROR
.
public Status output_quoted_string(
FILE *file,
STRING str )
Outputs a quotation mark, the specified string, and a closing quotation mark.
public Status output_newline(
FILE *file )
Outputs a newline character, returning OK
or
ERROR
.
public Status output_boolean(
FILE *file,
BOOLEAN b )
If the argument is TRUE
, then a space and the letter
``T
is output, otherwise, a space and the letter
``F
is output.
public Status output_short(
FILE *file,
short s )
public Status output_unsigned_short(
FILE *file,
unsigned short s )
public Status output_int(
FILE *file,
int i )
public Status output_real(
FILE *file,
Real r )
public Status output_float(
FILE *file,
float f )
public Status output_double(
FILE *file,
double d )
Outputs a space and then the specified value to an ascii file.
public Status output_binary_data(
FILE *file,
void *data,
size_t element_size,
int n )
Outputs an array of data to a file, in binary format. The array contains
n
elements, each of size element_size
.
Returns either OK
or ERROR
.
public Status io_binary_data(
FILE *file,
IO_types io_flag,
void *data,
size_t element_size,
int n )
Inputs or outputs the specified binary data, depending on whether the argument
io_flag
is READ_FILE
or
WRITE_FILE
.
public Status io_newline(
FILE *file,
IO_types io_flag,
File_formats format )
Inputs or outputs an ascii newline character, depending on whether the argument
io_flag
is READ_FILE
or
WRITE_FILE
. If the format
argument is
BINARY_FORMAT
, this function does nothing.
public Status io_quoted_string(
FILE *file,
IO_types io_flag,
File_formats format,
STRING str )
If the format
argument is ASCII_FORMAT
,
inputs or outputs a quotation mark delimited string, depending on whether the
argument io_flag
is READ_FILE
or
WRITE_FILE
. IF the format
argument is
BINARY_FORMAT
, inputs or outputs a string preceded by an
integer indicating the length of the string.
public Status io_boolean(
FILE *file,
IO_types io_flag,
File_formats format,
BOOLEAN *b )
public Status io_short(
FILE *file,
IO_types io_flag,
File_formats format,
short *short_int )
public Status io_unsigned_short(
FILE *file,
IO_types io_flag,
File_formats format,
unsigned short *unsigned_short )
public Status io_unsigned_char(
FILE *file,
IO_types io_flag,
File_formats format,
unsigned char *c )
public Status io_int(
FILE *file,
IO_types io_flag,
File_formats format,
int *i )
public Status io_real(
FILE *file,
IO_types io_flag,
File_formats format,
Real *r )
public Status io_float(
FILE *file,
IO_types io_flag,
File_formats format,
float *f )
public Status io_double(
FILE *file,
IO_types io_flag,
File_formats format,
double *d )
Inputs or outputs a binary or ascii value of the specified type.
public Status io_ints(
FILE *file,
IO_types io_flag,
File_formats format,
int n,
int *ints[] )
Inputs or outputs an array of integers in binary or ascii format.
public Status io_unsigned_chars(
FILE *file,
IO_types io_flag,
File_formats format,
int n,
unsigned char *unsigned_chars[] )
Inputs or outputs an array of unsigned characters in binary or ascii format.
Memory Allocation
editA set of macros is provided to allow easy allocation and deallocation of memory, with up to 5 dimensional arrays. Memory allocation checking is also performed, to catch errors such as freeing memory that was not allocated. Also, the memory allocation automatically tracks all memory allocated, so that detection of memory leaks (orphaned memory) is possible.
Basic Memory Allocation
editThe basic macros are as follows:
ALLOC( ptr, n_items )
Allocates n_items
elements of the correct type,
assigning the result to the argument ptr
.
FREE( ptr )
Frees the memory pointed to by the argument ptr
.
REALLOC( ptr, n_items )
Changes the size of the memory pointed to by ptr
to be of
size n_items
elements, possibly changing the value of
ptr
in the process.
ALLOC_VAR_SIZED_STRUCT( ptr, element_type, n_elements )
Allocates a variable sized structure, which must be of a specific form. The last
element of the structure must be an array of size 1, and this array will
constitute the variable-sized part of the structure. The argument
element_type
must be the type of this last element, and the
argument n_elements
is the desired number of elements to
allocate for this array, in addition to the memory for the first part of the
structure. An example of usage is:
{
struct { int a;
float b;
char data[1];
} *ptr;
ALLOC_VAR_SIZED_STRUCT( ptr, char, 10 );
ptr->a = 1;
ptr->b = 2.5;
ptr->data[0] = 'a';
ptr->data[9] = 'i';
}
ALLOC2D( ptr, n1, n2 )
ALLOC3D( ptr, n1, n2, n3 )
ALLOC4D( ptr, n1, n2, n3, n4 )
ALLOC5D( ptr, n1, n2, n3, n4, n5 )
Allocates a 2 to 5 dimensional array of size n1
by
n2
, etc. and stores the result in the specified pointer,
ptr
. In the 2 dimensional case, this is accomplished with
only 2 memory allocations, one to allocate n1
times
n2
elements for the storage, and the second to allocate
n1
pointers into the first memory area. In general, there is
one memory allocation for each dimension required.
FREE2D( ptr )
FREE3D( ptr )
FREE4D( ptr )
FREE5D( ptr )
Frees the memory associated with the multi-dimensional array.
public void set_alloc_checking(
BOOLEAN state )
Enables or disables allocation checking. It is usually called at the beginning
of a program to provide double-checking of allocation. It incurs an extra
allocation every time one of the previous allocation macros is invoked. If this
function is not called, allocation checking can be turned on by setting the
environment variable DEBUG_ALLOC
to anything.
Allocation checking detects three types of memory programming errors: freeing a pointer that was not allocated, by checking that memory chunks returned from the system malloc routine do not overlap existing ones, sometimes detects when the system memory has become corrupted, and by recording all memory allocated and printing out what is currently allocated, can detect memory leaks.
public void output_alloc_to_file(
STRING filename )
If memory checking was enabled, this function can be used to detect memory leaks. At the end of a program, the programmer should free up all memory that is known, then call this function. Any remaining allocated memory is printed out to a file, indicating the file and line number where the memory was allocated.
Higher Level Array Allocation
editIn addition to the basic memory allocation macros described previously, a set of useful macros for dealing with arrays of dynamically changing size are provided:
SET_ARRAY_SIZE( array, previous_n_elems, new_n_elems,
chunk_size )
This macro increases or decreases the size of an array, by specifying the number
of elements previously allocated to the array (starts out at zero). The
chunk_size
argument defines the size of the memory chunks
which are allocated. For instance, if chunk_size
is 100,
then this macro will only reallocate the array if the size change crosses to a
different multiple of 100, thus avoiding memory allocation every time it is
called. This specification of the granularity of the memory allocation must be
consistently specified; if this macro is called with a given variable and chunk
size, then subsequent calls to this macro with the same variable must specify the
same chunk size. Note also that the number passed in as
new_n_elems
must be passed in as
previous_n_elems
on the next call to this macro.
ADD_ELEMENT_TO_ARRAY( array, n_elems,
elem_to_add, chunk_size )
Adds the argument elem_to_add
to the array at the
n_elems
'th index, then increments
n_elems
. The argument chunk_size
specifies the granularity of memory allocation.
DELETE_ELEMENT_FROM_ARRAY( array, n_elems, index_to_remove,
chunk_size )
Deletes the index_to_remove
'th element from the array,
decreasing the number of elements in the array (n_elems
) and
decreasing the memory allocation, if crossing a multiple of
chunk_size
. Again, chunk_size
must be
specified the same for all invocations of the previous three macros involving a
given pointer.
ADD_ELEMENT_TO_ARRAY_WITH_SIZE( array, n_alloced, n_elems,
elem_to_add, chunk_size )
Adds an element (elem_to_add
) to the array, incrementing
n_elems
. If necessary, the memory is increased by the amount
specified in chunk_size
and the
n_alloced
variable is incremented by this amount. The usage
of this differs from the use of ADD_ELEMENT_TO_ARRAY
in that
the number of elements (n_elems
) can be decreased
arbitrarily, without causing memory to be deallocated.
Progress Reports
editIn order to provide simple monitoring of the progress of a particular processing task, a progress reporting module is available. While a task is in progress, the progress report prints dots across the line indicating how close to finished the task is. If the task is going to take very long, (greater than 2 minutes), the progress report periodically prints the current percentage done and the estimated time remaining. An example of usage, followed by function descriptions, is presented:
{
int i, n_slices;
progress_struct progress;
n_slices = 100;
initialize_progress_report( &progress, FALSE,
n_slices, "Processing Slices" );
for( i = 0; i < n_slices; ++i )
{
process_slice( i );
update_progress_report( &progress, i + 1 );
}
terminate_progress_report( &progress );
}
public void initialize_progress_report(
progress_struct *progress,
BOOLEAN one_line_only,
int n_steps,
STRING title )
Initializes a progress report struct, specifying the number of steps that will occur
in the processing, and the title to print out for the progress report. During
progress report, the display will automatically switch from the short-job mode of
printing a single row of dots across a line to the long-job mode of periodically
printing the percentage done and estimated time remaining. If the
one_line_only
flag is TRUE
, this is
disabled and only a single row of dots will be displayed.
public void update_progress_report(
progress_struct *progress,
int current_step )
Tells the progress reporting module how many steps have been done, and causes update of the display of dots on the line or estimated time remaining.
public void terminate_progress_report(
progress_struct *progress )
Terminates the progress report.
Text Output
editRather than using the standard UNIX function printf
for routine
text output, a module which is similar in appearance is provided, which allows for
installing arbitrary printing output functions. This can be used, for instance, to
mirror output to a log file for instance, or to send text to an X window. The
primary change is to use a function called print
or
print_error
with exactly the same arguments as for the standard
printf
function.
public void print( STRING format, ... )
public void print_error( STRING format, ... )
Takes the same arguments as printf()
, but allows
installing of a user output function for the final stage of output.
public void set_print_function(
void (*function) ( STRING ) )
public void set_print_error_function(
void (*function) ( STRING )
Sets the output function, where all text from calls to print()
or print_error
will be sent. By default, there is no print
function, and output is sent to printf().
public void push_print_function()
public void push_print_error_function()
Temporarily sets the print or error printing function to go to
standard out, and allows the user to set another print function, which
will disappear when the corresponding pop
function is called.
public void pop_print_function()
public void pop_print_error_function()
Restores the previous user print or error printing function.
Time
editSome basic utilities relating to time, date, and CPU time are provided.
public Real current_cpu_seconds( void )
Returns the number of CPU seconds used by the current process so far.
public Real current_realtime_seconds( void )
Returns the number of seconds that the current process has been running.
public STRING get_clock_time()
Returns a string representing the current clock time (hours and minutes), for which the caller is responsible for deleting.
public STRING get_date()
Returns a string representing the current date, for which the caller is responsible for deleting.
public STRING format_time(
STRING format,
Real seconds )
Takes a time in seconds and a format string which has format components, e.g. ``%g %s, and prints the time into a string in appropriate units of milliseconds, seconds, minutes, days, etc. The string is returned and the calling program is responsible for deleting the string.
public void print_time(
STRING format,
Real seconds )
Same as format_time
, but calls print()
with
the result.
public void sleep_program(
Real seconds )
Suspends the program for the specified number of seconds. Note that on most systems, this will only be performed to the nearest multiple of some particular time increment. On Silicon Graphics Systems this will be to the nearest hundredth of a second.
Volumes
editProcessing tasks within the lab where this software was developed deal with multi-dimensional volumes of data such as created by Magnetic Resonance and PET scanners. Therefore, an extensive set of routines is provided to represent volumes, and to read and write volumes in the MINC format.
The basic type of a volume is Volume
, which is actually a pointer
to an allocated structure which contains all the information about the type of volume,
number of dimensions, voxel values, etc. In order to use a volume structure, the
volume must first be created, then the size of the volume set, then the large array of
voxel values is allocated. Alternately, a volume may be automatically created by
calling the appropriate function to read a MINC file and create a volume.
A volume has an associated number of dimensions, which must be in the range from one to five, but typically is three. The volume is thought of as a multi-dimensional array of any of a variety of types, including all sizes of integer and real types. Even though a volume may be stored in say, a 1 byte type, with values from zero to 255, there is a real value range which provides mapping a mapping from voxel values to any arbitrary real range. In this way, the real range may be any valid real interval and is independent of the particular storage type.
Since most volumes will be created by reading from a MINC file, this method will be presented first, followed by a description of how to create a volume from scratch. Finally a lower level of MINC input and output for more advanced applications will be presented.
Volume Input
edit public Status input_volume(
STRING filename,
int n_dimensions,
STRING dim_names[],
nc_type volume_nc_data_type,
BOOLEAN volume_signed_flag,
Real volume_voxel_min,
Real volume_voxel_max,
BOOLEAN create_volume_flag,
Volume *volume,
minc_input_options *options )
This routine reads a volume from a MINC file, first creating the volume if the
create_volume_flag
is specified as TRUE
(the usual case). The number of dimensions is the desired number of dimensions for
the volume. If this is less than the number of dimensions in the file, then only
the first part of the file, corresponding to this number of dimensions, is read. If
the number of dimensions specified is less than 1, then the number of dimensions in
the file is used. The argument dim_names
specifies in what
order the volume is to be stored in the volume variable. For each dimension in the
stored volume, there is a corresponding name, which is one of
MIxspace
, MIyspace
,
MIzspace
, ANY_SPATIAL_DIMENSION
, or an
empty string. These are matched up with corresponding dimensions in the file and
the dimension ordering of the volume array is reordered on input. So, if the user
wishes to represent the volume in X-Z-Y order, then the value passed as the
dim_names
array should be the three strings,
MIxspace
, MIzspace
, and
MIyspace
. This choice of ordering is important, as it defines
the order of any subsequent reference to voxel indices.
The four arguments, volume_nc_data_type
through
volume_voxel_max
can be used to specified the desired storage
type within the volume variable, automatically converted from the storage type in
the file. The volume_nc_data_type
is one of
MI_ORIGINAL_TYPE
, NC_BYTE
,
NC_SHORT
, NC_LONG
,
NC_FLOAT
, or NC_DOUBLE
. For the integer
types, the volume_signed_flag
is TRUE
if a
signed type is desired, otherwise, it is FALSE
. The
volume_voxel_min
and volume_voxel_max
specify the range of valid voxel values, and are usually set equal to indicate to
use the full range of the type, e.g. zero to 255.0 for unsigned
NC_BYTE
. If MI_ORIGINAL_TYPE
is passed,
then the type, sign, and voxel range in the file are used.
If the create_volume_flag
is TRUE
, the
usual case, then the volume is automatically created. Otherwise, it is assumed that
the volume already exists and will only be recreated if its current size is
different from the new size resulting from the file, thus reducing the amount of
memory allocation when reading multiple files.
The options
argument specifies some special options to the
input process, and is usually passed just a NULL
pointer,
indicating that the default options should be used. Currently the possible options
are:
public void set_default_minc_input_options(
minc_input_options *options )
Fills in the default options into an options
structure
which will subsequently be passed to input_volume()
.
public void set_minc_input_promote_invalid_to_min_flag(
minc_input_options *options,
BOOLEAN flag )
By default, any voxel value which is outside the valid range of voxel values is
promoted to the minimum valid voxel value. If this flag
is set
to FALSE
, this promotion is disabled.
public void set_minc_input_vector_to_scalar_flag(
minc_input_options *options,
BOOLEAN flag )
By default, any volume which contains a dimension which is of
type vector, for instance, an RGB colour volume, is converted to a
scalar volume by averaging the components of each vector. This can be
disabled by passing the flag
as FALSE
.
public void set_minc_input_vector_to_colour_flag(
minc_input_options *options,
BOOLEAN flag )
public void set_minc_input_colour_dimension_size(
minc_input_options *options,
int size )
public void set_minc_input_colour_indices(
minc_input_options *options,
int indices[4] )
By default, any volume which contains a dimension which is of
type vector, for instance, an RGB colour volume, is converted to a
scalar volume by averaging the components of each vector. If this
option is set to true, any vector volume with the number of components
specified by set_minc_input_colour_dimension_size
(default 3) is converted to a colour volume, using the
indices specified by set_minc_input_colour_indices
(default 0, 1, 2).
public void set_minc_input_user_real_range(
minc_input_options *options,
double minimum,
double maximum )
By default, files read into an integer-type volume will be scaled so that the real range is the full range of the data in the input file. The user can define another range by calling this function with an appropriate real minimum and maximum. For float-type Volumes, setting the range will change the real range of the volume, but not change the actual values in the volume. If minimum is greater than or equal to maximum, then the default behaviour is restored.
Alternative Volume Input Methods
editRather than using the input_volume()
function to input a volume
in one shot, there is another method which will allow interspersing volume input
with other processing tasks. The following is the set of routines which the
input_volume()
function calls in order to input a volume.
public Status start_volume_input(
STRING filename,
int n_dimensions,
STRING dim_names[],
nc_type volume_nc_data_type,
BOOLEAN volume_signed_flag,
Real volume_voxel_min,
Real volume_voxel_max,
BOOLEAN create_volume_flag,
Volume *volume,
minc_input_options *options,
volume_input_struct *input_info )
This initializes a filename for inputting the volume incrementally. The arguments
to this are identical to those for the input_volume()
function,
with the addition of the input_info
structure which is
initialized for use in the remaining functions. The volume is created if necessary,
but not allocated, and no part of the volume is read in.
public void delete_volume_input(
volume_input_struct *input_info )
Closes the file and terminates the input of the volume. By calling the
start_volume_input()
function followed by the
delete_volume_input()
function, a volume can be created that
has all the information about the file volume, without the voxels being allocated.
public void cancel_volume_input(
Volume volume,
volume_input_struct *input_info )
Closes the file, terminates the input of the volume, and deletes the volume. Simply
calls delete_volume_input()
followed by
delete_volume()
.
public BOOLEAN input_more_of_volume(
Volume volume,
volume_input_struct *input_info,
Real *fraction_done )
Inputs a small chunk of the volume, calculated to be efficient, yet not be so large
that one cannot usably intersperse calls to do other processing with calls to this
function. Returns TRUE
if there is more to do, i.e., this
function should be called again. The fraction done is passed back as a number
between 0 and 1.
public Minc_file get_volume_input_minc_file(
volume_input_struct *volume_input )
The Minc_file
structure from the volume input is passed back,
to allow use of this in routines that require this type.
Volume Output
editVolume output is accomplished by one of two routines, depending on whether or not the volume is treated as a modified version of another volume or is an independent volume on its own.
public Status output_volume(
STRING filename,
nc_type file_nc_data_type,
BOOLEAN file_signed_flag,
Real file_voxel_min,
Real file_voxel_max,
Volume volume,
STRING history,
minc_output_options *options )
Outputs the specified volume to the specified filename. If the argument
file_nc_data_type
is MI_ORIGINAL_TYPE
then
the volume is stored in the MINC file in the same type as in the volume variable.
Otherwise, the four arguments, file_nc_data_type
,
file_signed_flag
, file_voxel_min
, and
file_voxel_max
, specify the type and valid voxel range to store
the volume in the file. If the history
argument is non-null,
then it represents a description of the volume, and will be placed in the MINC
volume. If the options
argument is NULL
,
then the default options will be used. Otherwise, some specific output options can
be set through this parameter, and the following functions:
public void set_default_minc_output_options(
minc_output_options *options )
Sets the options
structure to the default values. The user can
then override the default values and pass the structure to the
output_volume()
function. Currently, there is only one output
option:
public void delete_minc_output_options(
minc_output_options *options )
Deletes the options
data.
public void set_minc_output_dimensions_order(
minc_output_options *options,
int n_dimensions,
STRING dimension_names[] )
Defines the output order of the file. Each dimension name string must have a
matching dimension name in the volume, which defines the order of the dimensions in
the output file. For instance, one may have input the volume in <function>(x, y,
z)</function> order. To get it output in (z, y, x)
order, set
dimension_names
to the three strings
MIzspace
, MIyspace
, and
MIxspace
.
public void set_minc_output_real_range(
minc_output_options *options,
Real real_min,
Real real_max )
Defines the image range that will appear in the file. By default, none is
specified, and output_volume
uses the real minimum and maximum
of the volume. To set the real range of the volume, see the relevant documentation
for set_volume_real_range()
.
If the volume is a modification of another volume currently stored in a file, then it is more appropriate to use the following function to output the volume:
public Status output_modified_volume(
STRING filename,
nc_type file_nc_data_type,
BOOLEAN file_signed_flag,
Real file_voxel_min,
Real file_voxel_max,
Volume volume,
STRING original_filename,
STRING history,
minc_output_options *options )
The only difference between this function and the other method of volume output
(output_volume()
), is that this function copies auxiliary data
from the original file (original_filename
) to the new file.
This auxiliary data includes such items as patient name and other scanning data, and
is not read into a volume, so the only way to correctly pass it along to a new MINC
file is to use this function.
Volume Manipulation
editOnce a volume has been created and allocated, there are many functions for
manipulating the volume. Note that associated with each volume is a valid voxel
range indicating the range of values actually stored in the volume, for instance,
zero to 200 is one possible range for an unsigned byte volume. There is a second
range, the real
range, which is the mapping of the valid voxel
range to an arbitrary real range, for instance, the zero to 200 of valid voxels
could map to -1.5 to 1.5 in the real
range. When dealing with
volumes, one is generally interested in the real
range.
public Real get_volume_voxel_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4 )
Given a volume and from one to five voxel indices, depending on the volume dimensions, returns the corresponding voxel value. For instance, if the volume is three dimensional, then the final two arguments are ignored.
value = convert_voxel_to_value( volume, voxel );
Given a volume and a voxel value, converts this to a value in the real range, and returns it.
voxel = convert_value_to_voxel( volume, value )
Given a volume and a real value, converts this to a voxel value in the valid voxel range, and returns it.
public Real get_volume_real_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4 )
Given a volume and from one to five voxel indices, depending on the volume dimensions, returns the corresponding real value. For instance, if the volume is three dimensional, then the final two arguments are ignored.
public void set_volume_voxel_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4,
Real voxel )
Given a volume, one to five voxel indices, and a voxel
value,
assigns this value to the corresponding voxel in the volume. Note that no conversion
from the valid real range to the valid voxel range is performed, so the user may
need to use the convert_value_to_voxel
function first or use
the set_volume_real_value
function.
public void set_volume_real_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4,
Real value )
Given a volume, one to five voxel indices, and a value
, assigns
this value to the corresponding voxel in the volume, after converting to a voxel
value.
public void delete_volume(
Volume volume )
Deletes all the memory associated with an volume.
public nc_type get_volume_nc_data_type(
Volume volume,
BOOLEAN *signed_flag )
Returns the storage type of the volume (for instance,
NC_SHORT
), and passes back an indication of whether it is
signed or unsigned.
public int get_volume_n_dimensions(
Volume volume )
Returns the number of dimensions of the volume.
public void get_volume_sizes(
Volume volume,
int sizes[] )
Stores the size of each dimension in the array sizes
. This is
the number of voxels in each dimension. The array sizes
must
be at least as large as the number of dimensions of the volume.
public void get_volume_separations(
Volume volume,
Real separations[] )
Stores the slice separation for each dimension in the array
separations
. The array separations
must
be at least as large as the number of dimensions of the volume.
public Real get_volume_voxel_min(
Volume volume )
public Real get_volume_voxel_max(
Volume volume )
public void get_volume_voxel_range(
Volume volume,
Real *voxel_min,
Real *voxel_max )
The first two functions return the minimum or maximum allowable voxel value. The third function passes back both values.
public Real get_volume_real_min(
Volume volume )
public Real get_volume_real_max(
Volume volume )
public void get_volume_real_range(
Volume volume,
Real *min_value,
Real *max_value )
The first two functions return the minimum or maximum real value. The third
function passes back both values. The mapping to this real space linearly maps the
minimum voxel
value to the minimum real
value and the maximum voxel
value to the maximum
real
value.
public STRING *get_volume_dimension_names(
Volume volume )
Returns a pointer to the list of names of each dimension. This memory must be freed by the calling function using the following function.
public void delete_dimension_names(
Volume volume,
STRING dimension_names[] )
Frees the memory allocated for the dimension names.
public void set_rgb_volume_flag(
Volume volume,
BOOLEAN flag )
Sets the volume flag indicating whether the volume is of type red-green-blue colour. Only volumes whose data type is unsigned long may be rgb volumes.
public BOOLEAN is_an_rgb_volume(
Volume volume )
Returns TRUE
if the volume is a red-green-blue colour volume,
as can be created on volume input.
Volume Coordinate Systems
editA volume has two coordinate systems. The voxel coordinate system is simply the n-dimensional indexing coordinate system for a volume. A voxel coordinate of (0.0, 0.0, 0.0), for instance, corresponds to the centre of the first voxel in a three dimensional volume. A voxel coordinate of ( 99.0, 0.0, 0.0 ) corresponds to the centre of the last voxel in the first direction of a three dimensional volume of size ( 100, 200, 150 ). The second coordinate system is an arbitrary three dimensional coordinate system, usually referred to as the world coordinate system, often the Talairach coordinate system. The following functions provide conversion to and from these two coordinate systems:
public void convert_voxel_to_world(
Volume volume,
Real voxel[],
Real *x_world,
Real *y_world,
Real *z_world )
Given a volume and a real valued voxel index, passes back the corresponding world coordinate.
public void convert_3D_voxel_to_world(
Volume volume,
Real voxel1,
Real voxel2,
Real voxel3,
Real *x_world,
Real *y_world,
Real *z_world )
Same as convert_voxel_to_world
except it applies only to
three dimensional volumes.
public void convert_world_to_voxel(
Volume volume,
Real x_world,
Real y_world,
Real z_world,
Real voxel[] )
Converts a world coordinate into a voxel. In order to use these voxel
coordinates as integer indices, for instance, as arguments to the
GET_VALUE
macros, each component of the argument
voxel
must first be rounded to the nearest integer.
public void convert_3D_world_to_voxel(
Volume volume,
Real x_world,
Real y_world,
Real z_world,
Real *voxel1,
Real *voxel2,
Real *voxel3 )
Same as convert_world_to_voxel
except it applies only to
three dimensional volumes.
public void convert_voxel_vector_to_world(
Volume volume,
Real voxel_vector[],
Real *x_world,
Real *y_world,
Real *z_world )
public void convert_world_vector_to_voxel(
Volume volume,
Real x_world,
Real y_world,
Real z_world,
Real voxel_vector[] )
These two functions convert vectors between the voxel and world spaces. This is done by transforming the point (0,0,0) to the other space, in addition to treating the vector as a point and transforming it to the other space, and passing back the vector between the two results.
public void convert_voxel_normal_vector_to_world(
Volume volume,
Real voxel1,
Real voxel2,
Real voxel3,
Real *x_world,
Real *y_world,
Real *z_world )
Converts a vector which is assumed to be a surface normal to the world coordinate system.
Volume Interpolation
editIn addition to the routines for accessing particular voxel values, the volume can be interpolated using nearest neighbour, linear, or cubic interpolation, specified in either voxel or world space.
public int evaluate_volume(
Volume volume,
Real voxel[],
BOOLEAN interpolating_dimensions[],
int degrees_continuity,
BOOLEAN use_linear_at_edge,
Real outside_value,
Real values[],
Real **first_deriv,
Real ***second_deriv )
Interpolates a volume at the specified voxel, where
degrees_continuity
has values -1, 0, or 2, for nearest
neighbour, linear, or cubic interpolation. If
use_linear_at_edge
is true, then any cubic interpolation
will be downgraded to linear interpolation near the edge of the volume. This
option is only needed in special cases. The parameter
outside_value
is the value for any point outside the volume.
The argument interpolating_dimensions
indicates which
dimensions are being interpolated, typically a NULL
pointer
indicating that all dimensions are being interpolated. The interpolated values
are placed in the values array. The number of values is equal to the number of
values in the non-interpolated dimensions, which is one, if all dimensions are
being interpolated. If the derivative arguments are non-null, then the resulting
derivatives are placed in the appropriate places. The
first_deriv
is a two dimensional array of size number of
values by number of interpolating dimensions. The
second_deriv
is a three dimensional array of size number of
values by number of interpolating dimensions by number of interpolating
dimensions.
public void evaluate_volume_in_world(
Volume volume,
Real x,
Real y,
Real z,
int degrees_continuity,
BOOLEAN use_linear_at_edge,
Real outside_value,
Real values[],
Real deriv_x[],
Real deriv_y[],
Real deriv_z[],
Real deriv_xx[],
Real deriv_xy[],
Real deriv_xz[],
Real deriv_yy[],
Real deriv_yz[],
Real deriv_zz[] )
Interpolates a volume at the specified world position, where
degrees_continuity
has values -1, 0, or 2, for nearest
neighbour, linear, or cubic interpolation. If
use_linear_at_edge
is true, then any cubic interpolation
will be downgraded to linear interpolation near the edge of the volume. This
option is only needed in special cases. The parameter
outside_value
is the value for any point outside the volume.
The interpolated values are placed in the values array. The number of values is
equal to the number of values in the non-spatial dimensions, which is one if all
dimensions are spatial. If the derivative arguments are non-null, then the
resulting derivatives are placed in the appropriate places.
public void set_volume_interpolation_tolerance(
Real tolerance )
For speed considerations, if a volume is evaluated at or near a voxel centre, then no interpolation is performed, and the voxel value is returned. The tolerance defines how close to the voxel centre this occurs, and defaults to 0. Note that if derivatives are desired and the degree of continuity specified is 0 or greater, then interpolation will be performed, even when within the specified tolerance of the voxel.
Volume Creation from Scratch
editIn some circumstances, it is desirable to create volumes in ways other than reading from a file. The following functions provide methods to create a volume from scratch or to create a volume which is similar to an existing volume.
public Volume create_volume(
int n_dimensions,
STRING dimension_names[],
nc_type nc_data_type,
BOOLEAN signed_flag,
Real voxel_min,
Real voxel_max )
Creates and returns a volume of the given type (for instance,
NC_BYTE
, signed_flag
equal
FALSE
), and given valid voxel range. The
dimension_names
is used to describe each dimension of the
volume and is currently only used when writing the volume to a file.
public void set_volume_voxel_range(
Volume volume,
Real voxel_min,
Real voxel_max )
public void set_volume_real_range(
Volume volume,
Real real_min,
Real real_max )
After creation of a volume, the valid voxel range or valid real range can subsequently be changed by using these functions.
public void set_volume_sizes(
Volume volume,
int sizes[] )
Sets the sizes of the volume, the number of voxels in each dimension. Note that
this must be done before calling the function alloc_volume_data
to allocate the voxels.
public void alloc_volume_data(
Volume volume )
After the volume has been created, and its size has been set, then this function allocates the memory for the voxels. Note that the voxel values are not initialized, and the user must fill the volume with desired values.
Associated with each volume is a transformation from voxel
space to world
space. There are several ways to define this
transformation. The simplest is just to specify it directly:
public void set_voxel_to_world_transform(
Volume volume,
General_transform *transform )
Assigns the given transformation to the volume.
public General_transform *get_voxel_to_world_transform(
Volume volume )
Returns a pointer to the voxel to world transform of the volume.
public void set_volume_separations(
Volume volume,
Real separations[] )
Sets the inter-voxel separations in each of the volume dimensions. Note that this will cause the voxel-to-world transformation to be updated accordingly.
public void set_volume_translation(
Volume volume,
Real voxel[],
Real world_space_voxel_maps_to[] )
Sets the translation portion of the voxel-to-world transformation. A voxel
coordinate is specified (voxel
), as well as a real world
position to which it is desired that this voxel map
(world_space_voxel_maps_to
). The voxel-to-world transformation
is updated to provide this mapping.
public void set_volume_direction_cosine(
Volume volume,
int dimension,
Real dir[] )
Sets the real world axis for a specific voxel dimension. For instance if
dimension
is 1, and dir
is (0.0, 1.0,
1.0), then voxels along the second dimension of the volume will map to the real
world axis (0.0, 1.0, 1.0) normalized to unit length, then scaled by the volume
separation for the second volume dimension.
public void get_volume_direction_cosine(
Volume volume,
int dimension,
Real dir[] )
Passes back the real world axis for a specific voxel dimension. Note that
dimension
must be a spatial dimension in the volume.
public void set_volume_space_name(
Volume volume,
STRING name )
Sets the coordinate system type of the volume in terms of a string name. This can
be any string, but a set of defined constants is given by MINC:
MI_NATIVE
, MI_TALAIRACH
, and
MI_CALLOSAL
.
public STRING get_volume_space_name(
Volume volume )
Returns a copy of the coordinate system type of the volume in terms of a string name. The calling function must free this string when finished with it.
Copying Volumes
editAnother method of creating a volume is to simply copy the entire volume definition from an existing volume:
public Volume copy_volume_definition(
Volume existing_volume,
nc_type nc_data_type,
BOOLEAN signed_flag,
Real voxel_min,
Real voxel_max )
public Volume copy_volume_definition_no_alloc(
Volume volume,
nc_type nc_data_type,
BOOLEAN signed_flag,
Real voxel_min,
Real voxel_max )
Both functions creates and returns a new volume which has the same definition
(sizes, voxel-to-world space transform, etc.), as an existing volume. If the
argument nc_data_type
is not
MI_ORIGINAL_TYPE
, then the storage type of the new volume
differs from the original and is specified by nc_data_type
,
signed_flag
, voxel_min
, and
voxel_max
. In the first function, the voxel values are
allocated but not initialized. In the second function, they are not allocated.
public Volume copy_volume(
Volume volume )
Creates an exact copy of a volume.
Volume Caching
editIn order to efficiently handle volumes that are too large for virtual memory, a simple caching scheme has been implemented. Disk files are read and written on demand to provide the appearance of having large volumes fully resident in memory. All the volume routines described previously support this feature transparently, but there are a few routines for configuration of the volume caching.
public void set_n_bytes_cache_threshold(
int threshold )
Sets the threshold number of bytes for volume caching. If a volume larger than
this is created, either explicitly or by input from a file, then that volume will
be internally represented by the caching scheme. If this function is not called,
the default is either 80 megabytes, or the value of the environment variable
VOLUME_CACHE_THRESHOLD
, if present. A threshold of zero
will cause all volumes to be cached. A threshold less than zero will result in
no volumes being cached.
typedef enum { SLICE_ACCESS, RANDOM_VOLUME_ACCESS }
Cache_block_size_hints;
public void set_cache_block_sizes_hint(
Cache_block_size_hints hint )
public void set_default_cache_block_sizes(
int block_sizes[] )
When a voxel is accessed in a cached volume, the corresponding block containing
the voxel is read from or written to the disk as necessary. These two functions
control the default size of the blocks and therefore the tradeoff between fast
access of adjacent voxels (using large block sizes) and fast access of
arbitrarily distributed voxels (using small block sizes). The function
set_cache_block_sizes_hint
indicates that the default block
sizes for volumes created in the future should be computed based on the
assumption that the application will be accessing volume voxels mostly in a
slice-by-slice manner (SLICE_ACCESS
) or in an unpredictable
order (RANDOM_VOLUME_ACCESS
). The second function,
set_default_cache_block_sizes
, provides an alternative where
the default block sizes are explicitly set. A call to either function overrides
any previous call to the other function. If neither of these functions is called,
the default value is a block size of 8 voxels per dimension, or the value
specified by the environment variable
VOLUME_CACHE_BLOCK_SIZE
. These functions only affect
volumes created afterwards. In order to change the value for a given volume, the
following function may be used:
public void set_volume_cache_block_sizes(
Volume volume,
int block_sizes[] )
Sets the size of the cache block for the given volume. Because this function causes the cache to be completely flushed, in order to change the block size, it may incur a temporary speed penalty during subsequent voxel accesses which cause the cache to read blocks from a file
public void set_default_max_bytes_in_cache(
int n_bytes )
Sets the default maximum number of bytes allowed in the cache for each volume. A
higher number may provide faster access, due to a greater chance of finding a
particular voxel within the cache, so this value should be as large as
reasonable, given the amount of virtual memory available. If this function is
not called, the default value is 80 megabytes, or the value of the environment
variable VOLUME_CACHE_SIZE
, if present. Setting a value of
0 will result in cached volumes having exactly one block in their cache. This
function only affects volumes created subsequently. In order to change this
value for a specific volume, the following function may be used:
public void set_volume_cache_size(
Volume volume,
int max_memory_bytes )
Sets the maximum number of bytes allowed in the cache for a particular volume.
Calling this function flushes the cache, in order to reallocate the data
structures to account for the new size. As in calling the function
set_volume_cache_block_sizes
, there may be a temporary speed
loss in accessing pixels.
public void set_cache_output_volume_parameters(
Volume volume,
STRING filename,
nc_type file_nc_data_type,
BOOLEAN file_signed_flag,
Real file_voxel_min,
Real file_voxel_max,
STRING original_filename,
STRING history,
minc_output_options *options )
When a cached volume is modified, a temporary file is created to contain the voxel values. When the volume is deleted this file is also deleted. When a cached volume is output, the temporary file is copied to the output file, which results in two copies of the volume existing at once, which may incur unacceptable demands on disk storage, especially for large volumes. To avoid this, an application can specify a filename where to place the voxel values, which overrides the use of a temporary file. When the volume is deleted, this file is closed and remains on the disk, obviating the need for the application to output the volume.
If the application later calls output_volume()
to output
this volume to a file of the same name as that set by this function, the request
to output will be ignored, because the volume already exists in this file.
Basically, the programmer should call
set_cache_output_volume_parameters()
with the same
parameters that will be passed to the eventual call to
output_volume()
. If the volume is a cached volume, then the
output file will be created as soon as a volume voxel is set, and the later call
to output_volume()
will not recreate the file, but simply
flush the cache buffer to make the file fully up to date. If the volume is not a
cached volume, the call to
set_cache_output_volume_parameters()
will be ignored, and
the later call to output_volume()
will create the file as
specified. Note that when calling this function, it is important to delete the
volume before exiting the program, so that cached volumes will flush their
buffer and close the file, Otherwise, the most recent changes to the volume will
not be written to the file.
Volume Source Code Example
editAn examples of reading, writing, and manipulating volumes is presented here.
a complete program to read a MINC volume, change all values over 100.0 to 100.0, then write out the result in a new file.
#include <volume_io.h>
/* ------------------------------------------------------------------
@COPYRIGHT :
Copyright 1993,1994,1995 David MacDonald,
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.
------------------------------------------------------------------ */
int main(
int argc,
char *argv[] )
{
int v1, v2, v3, sizes[MAX_DIMENSIONS];
Real value;
Volume volume;
/*--- input the volume */
if( input_volume( "volume.mnc", 3, NULL, MI_ORIGINAL_TYPE, FALSE,
0.0, 0.0, TRUE, &volume,
(minc_input_options *) NULL ) != OK )
return( 1 );
get_volume_sizes( volume, sizes );
/*--- change all values over 100 to 100 */
for( v1 = 0; v1 < sizes[0]; ++v1 ) {
for( v2 = 0; v2 < sizes[1]; ++v2 ) {
for( v3 = 0; v3 < sizes[2]; ++v3 ) {
value = get_volume_real_value( volume, v1, v2, v3,
0, 0 );
if( value > 100.0 ) {
set_volume_real_value( volume, v1, v2, v3,
0, 0, 100.0 );
}
}
}
}
/*--- output the modified volume */
if( output_modified_volume( "output.mnc", MI_ORIGINAL_TYPE,
FALSE, 0.0, 0.0, volume, "volume.mnc",
"Modified by clamping to 100",
(minc_output_options *) NULL ) != OK )
return( 1 );
return( 0 );
}
Multiple Volume Input
editThe input_volume
function described previously is intended to
be a simple single-function interface for reading volumes for most
applications. Occasionally, however, an application may not want to
read the whole volume at once, or may need to break the input file
into several volumes, for instance, the slices of a 3D volume. This can
be facilitated by the following functions:
public Minc_file initialize_minc_input(
STRING filename,
Volume volume,
minc_input_options *options )
This functions opens a MINC file for input to the specified volume. The number of dimensions in the file must be greater than or equal to the number of dimensions in the volume, and each dimension name in the volume must have a matching dimension name in the file. A file handle is returned, for use in the following routines.
public Minc_file initialize_minc_input_from_minc_id(
int minc_id,
Volume volume,
minc_input_options *options )
This performs the same task as initialize_minc_input
, except it
takes a previously opened MINC file as an argument, rather than a filename.
public int get_minc_file_id(
Minc_file file )
Returns the MINC id of the file, a handle which can be used to perform MINC functions on the file.
public int get_n_input_volumes(
Minc_file file )
Determines the total number of volumes in the opened MINC file. For instance, if the file contains x, y, z data of size 100 by 200 by 300 and the file was opened with a two dimensional x-z volume, then the number of volumes returned will be 200, the number of y slices.
public BOOLEAN input_more_minc_file(
Minc_file file,
Real *fraction_done )
Reads part of the MINC file to the volume specified in
initialize_minc_input
. This function must be called until it
returns FALSE, at which point the volume has been fully read in. In order to read
the next volume in the file, if any, the following function must be called.
public BOOLEAN advance_input_volume(
Minc_file file )
Advances the input file to the next volume.
public BOOLEAN reset_input_volume(
Minc_file file )
Resets the input to the beginning of the first volume in the file.
public Status close_minc_input(
Minc_file file )
Closes the MINC file.
Multiple Volume Output
editSimilarly to the multiple volume input, there are routines for providing output to a file by subvolumes.
public Minc_file initialize_minc_output(
STRING filename,
int n_dimensions,
STRING dim_names[],
int sizes[],
nc_type file_nc_data_type,
BOOLEAN file_signed_flag,
Real file_voxel_min,
Real file_voxel_max,
General_transform *voxel_to_world_transform,
Volume volume,
minc_output_options *options )
Opens a MINC file for creation. The arguments specify the number of dimensions and
the names of each dimension, as well as the sizes in each dimensions. The type of
the file is controlled by the four parameters,
file_nc_data_type
, file_signed_flag
,
file_voxel_min
, and file_voxel_max
. The
transformation to world coordinates is specified by the
voxel_to_world_transform
argument. The
volume
is attached to file for output. The volume must have no
more dimensions than the file and the names of each dimension must match with one of
the dimensions in the file. The options
are as specified for
the output_volume
function. Note that, unlike the
output_volume
function, if the image minimum and maximum are
not specified, then the output file will have a separate image minimum and maximum
for each image.
public int get_minc_file_id(
Minc_file file )
Returns the MINC id of the file, a handle which can be used to perform MINC functions on the file.
public Status copy_auxiliary_data_from_minc_file(
Minc_file file,
STRING filename,
STRING history_string )
Copies the auxiliary information from the MINC file specified in the
filename
argument to the open MINC file
.
public Status output_minc_volume(
Minc_file file )
Outputs the attached volume to the MINC file in the current file position. The file position is advanced by the size of the volume.
public Status output_volume_to_minc_file_position(
Minc_file file,
Volume volume,
int volume_count[],
long file_start[] )
More general routine to output the specified volume to the MINC file in the
specified position. Since this routine does not update the file position, it does
not interfere with normal volume output using the function
output_minc_volume
. The volume hyperslab defined by
volume_count
is written to the file starting at the given file
position.
public Status close_minc_output(
Minc_file file )
Closes the MINC file.
Tag Points
editTag points are sets of three dimensional points which are used to mark positions of
interest. Generally these result from manually locating corresponding positions in
multiple volumes to provide a mapping among the volumes. Tag points are stored in an
ascii format devised at the McConnell Brain Imaging Centre, with a convention of
filenames ending in .tag
. A tag file can contain either sets of
single tag points or two tag points, depending on whether the file corresponds to one
or two volumes. Each tag point consists of three coordinates, x, y, and z. Each set
of one or two tag points has additional optional information stored with it. A tag
point set may or may not include a set of three values: a real valued weight, an
integer structure id, and an integer patient id. A tag point may or may not include a
label string. Functions to read and write files of this format are described:
public Status input_tag_points(
FILE *file,
int *n_volumes,
int *n_tag_points,
Real ***tags_volume1,
Real ***tags_volume2,
Real **weights,
int **structure_ids,
int **patient_ids,
STRING *labels[] )
public Status input_tag_file(
STRING filename,
int *n_volumes,
int *n_tag_points,
Real ***tags_volume1,
Real ***tags_volume2,
Real **weights,
int **structure_ids,
int **patient_ids,
STRING *labels[] )
These two functions read a set of tag points from a file. The first form assumes the
file is already open and will be closed by the calling function, while the second form
opens and closes the file specified by a filename, with a default extension of
.tag
. The number of volumes (number of tags in a set, currently
either one or two) is passed back in the n_volumes
argument. The
number of tag points is passed back in the n_tag_points
argument.
The three dimensional coordinates of the first tag point in each set is passed back in
the argument tags_volume1
. If the number of volumes is two, then
the second tag point in each set is passed back in the argument
tags_volume2
. The final four arguments pass back the auxiliary
information associated with each tag point set. If the calling program is not
interested in any one of the four data, then it can pass in a NULL
pointer and the values in the file will not be passed back.
public void free_tag_points(
int n_volumes,
int n_tag_points,
Real **tags_volume1,
Real **tags_volume2,
Real weights[],
int structure_ids[],
int patient_ids[],
STRING labels[] )
When finished with a list of tag points, the associated memory may be freed by calling this function.
public Status initialize_tag_file_input(
FILE *file,
int *n_volumes )
public BOOLEAN input_one_tag(
FILE *file,
int n_volumes,
Real tag_volume1[],
Real tag_volume2[],
Real *weight,
int *structure_id,
int *patient_id,
STRING *label,
Status *status )
These two routines provide a more memory efficient method to input tag points. After opening a file, the first routine is called to initialize the input of tags. The next routine is repeatedly called until it returns FALSE, reading one tag at a time.
public Status output_tag_points(
FILE *file,
STRING comments,
int n_volumes,
int n_tag_points,
Real **tags_volume1,
Real **tags_volume2,
Real weights[],
int structure_ids[],
int patient_ids[],
STRING labels[] )
public Status output_tag_file(
STRING filename,
STRING comments,
int n_volumes,
int n_tag_points,
Real **tags_volume1,
Real **tags_volume2,
Real weights[],
int structure_ids[],
int patient_ids[],
STRING labels[] )
These two functions write a list of tag points to a tag point file. The first form
assumes that the file has already been opened and will be closed later by the calling
function, while the second form opens and closes the file, with the default extension
of .tag
. The comments
argument is any
arbitrary string documenting the contents of this file. The number of volumes
(n_volumes
) must be either one or two. The number of tag points
is specified by the argument n_tag_points
. The positions of the
sets of one or two tag points are in the arguments tags_volume1
and tags_volume2
. If any of the three arguments,
weights
, structure_ids
, or
patient_ids
, are specified as NULL
, then none
of these three pieces or information is written to the file. Similarly, if the
labels
argument is NULL
, then no labels are
written to the file.
public STRING get_default_tag_file_suffix()
Returns a pointer to a string consisting of the default suffix for tag point files,
currently ``tag
. This pointer should not be freed or its
contents modified.
public Status initialize_tag_file_output(
FILE *file,
STRING comments,
int n_volumes )
public Status output_one_tag(
FILE *file,
int n_volumes,
Real tag_volume1[],
Real tag_volume2[],
Real *weight,
int *structure_id,
int *patient_id,
STRING label )
public void terminate_tag_file_output(
FILE *file )
These two routines provide a more memory efficient method to output tag points. After opening a file, the first routine is called to initialize the output of tags. The next routine is repeatedly called to output a single tag point each time. The third routine is called to indicate the end of tag files.
Tag Points Source Code Example
editThe following is an example which reads a tag volume, removes all tags which have negative x positions, ignores the second tag point in each set, if present, and writes the result to a new file.
#include <volume_io.h>
int main(
int argc,
char *argv[] )
{
int i, n_volumes, n_tag_points, *structure_ids, *patient_ids;
Real **tags1, **tags2, *weights;
STRING *labels;
int new_n_tag_points, *new_structure_ids, *new_patient_ids;
Real **new_tags1, *new_weights;
STRING *new_labels;
/*--- input the tag file */
if( input_tag_file( "input_tags.tag", &n_volumes, &n_tag_points,
&tags1, &tags2, &weights, &structure_ids,
&patient_ids, &labels ) != OK )
return( 1 );
/*--- create a new tag point list of only those tag points
whose x coordinate is nonnegative */
new_n_tag_points = 0;
for_less( i, 0, n_tag_points )
{
if( tags1[i][0] >= 0.0 )
{
/*--- increase the memory allocation of the tag points */
SET_ARRAY_SIZE( new_tags1, new_n_tag_points,
new_n_tag_points+1, 10 );
ALLOC( new_tags1[new_n_tag_points], 3 );
SET_ARRAY_SIZE( new_weights, new_n_tag_points,
new_n_tag_points+1, 10 );
SET_ARRAY_SIZE( new_structure_ids, new_n_tag_points,
new_n_tag_points+1, 10 );
SET_ARRAY_SIZE( new_patient_ids, new_n_tag_points,
new_n_tag_points+1, 10 );
SET_ARRAY_SIZE( new_labels, new_n_tag_points,
new_n_tag_points+1, 10 );
ALLOC( new_labels[new_n_tag_points], strlen(labels[i])+1 );
/*--- copy from the input tags to the new tags */
new_tags1[new_n_tag_points][0] = tags1[i][0];
new_tags1[new_n_tag_points][1] = tags1[i][1];
new_tags1[new_n_tag_points][2] = tags1[i][2];
new_weights[new_n_tag_points] = weights[i];
new_structure_ids[new_n_tag_points] = structure_ids[i];
new_patient_ids[new_n_tag_points] = patient_ids[i];
(void) strcpy( new_labels[new_n_tag_points], labels[i] );
/*--- increment the number of new tags */
++new_n_tag_points;
}
}
/*--- output the new tags, the subset of the input tags */
if( output_tag_file( "output.tag", "Removed negative X's",
1, new_n_tag_points, new_tags1, NULL,
new_weights, new_structure_ids,
new_patient_ids, new_labels ) != OK )
return( 1 );
return( 0 );
}
Transforms
editIn dealing with such tasks as inter- and intra-subject registration both within and
across imaging modalities, the concept of transformations between different coordinate
systems arises. A module is provided to handle both linear (affine) and non-linear
transformations, and to provide input and output in a standardized Brain Imaging Centre
format, usually to filenames with the extension .xfm
.
To support these functions, two structure types are provided. The first
(Transform
) is a four by four linear (affine) transform, which
facilitates rigid transformations, consisting of scaling, rotation, translation, and
shearing. The second is a higher level transform
(General_transform
), which represents either a linear transform, a
non-linear transform (thin-plate spline), a smoothly interpolated grid transform, a
user definable transform, or a concatenation of two or more of these.
Linear Transforms
editThe linear transform functions all deal with objects whose type is
Transform
.
#define Transform_elem( transform, i, j )
Is used to set or retrieve the value of the i
'th row and
j
'th column of the transform, where <function>0<=i,
j<=3</function>
public void make_identity_transform(
Transform *transform )
Creates a four by four identity transform.
public void transform_point(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
Transforms a three dimensional point by the given transform, passing back the three transformed coordinates.
public void transform_vector(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
Transforms a three dimensional vector by the given transform, passing back the three transformed coordinates. The only difference between transforming a point and a vector is that transforming a vector does not involve the translational component of the transform.
public void inverse_transform_point(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
Assuming the transform is an orthogonal transform (no shear components), the point is transformed by the inverse of the transform.
public void inverse_transform_vector(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
Assuming the transform is an orthogonal transform (no shear components), the vector is transformed by the inverse of the transform.
public void concat_transforms(
Transform *result,
Transform *t1,
Transform *t2 )
Multiplies the transform t2
by the transform
t1
, storing the product in result
.
Transforming a point by result
is equivalent to transforming
the point by t1
, then transforming the result by
t2
.
General Transforms
editGeneral transforms can represent linear transforms, thin-plate spline transforms,
grid transforms, and user defined transforms, and concatenations of these. All
functions dealing with general transforms involve objects of type
General_transform
.
public void create_linear_transform(
General_transform *transform,
Transform *linear_transform )
Creates a general transform consisting of a single linear transform, specified by
linear_transform
. If a NULL
is passed as
the argument linear_transform
, then an identity transform is
created.
public void create_thin_plate_transform(
General_transform *transform,
int n_dimensions,
int n_points,
float **points,
float **displacements )
Creates a general transform consisting of a thin plate spline, which provides a
smooth non-linear mapping of a multidimensional space. The
points
argument is an array of size
n_points
by n_dimensions
, representing a
set of points. The displacements
is a
(n_points
+ n_dimensions
+ 1) by
n_dimensions
array, which is created by the function
get_nonlinear_warp()
, which is not included in this library.
public void create_grid_transform(
General_transform *transform,
Volume displacement_volume )
Creates a general transform consisting of a grid transform, which provides a smooth
non-linear mapping of a multidimensional space. The
displacment_volume
argument is a four dimensional volume
representing the displacements for the x, y, and z directions. Three of the
dimensions of the volume must correspond to the x, y, and z spatial dimensions, and
the fourth must have exactly three components, the displacements in each direction
in world space. The dimensions may be in any order.
typedef void (*User_transform_function)( void *user_data,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
public void create_user_transform(
General_transform *transform,
void *user_data,
size_t size_user_data,
User_transform_function transform_function,
User_transform_function inverse_transform_function )
Creates a user defined transformation, by copying the user data
(size_user_data
bytes of data starting at
user_data
). Two function pointers are also required, to
specify the method of transforming points and inversely transforming points. These
functions are of the type User_transform_function
, which is
specified above.
public void delete_general_transform(
General_transform *transform )
Frees up the memory stored in the general transform structure.
public Transform_types get_transform_type(
General_transform *transform )
Returns the general transform type, one of LINEAR
,
THIN_PLATE_SPLINE
, USER_TRANSFORM
, or
CONCATENATED_TRANSFORM
.
public Transform *get_linear_transform_ptr(
General_transform *transform )
If the general transform is of type LINEAR
, then returns a
pointer to the linear transform (of type Transform
), for use
with the routines specific to linear transforms, described earlier. Otherwise prints
an error message.
public void concat_general_transforms(
General_transform *first,
General_transform *second,
General_transform *result )
Concatenates two general transforms. If both transforms are of type
LINEAR
, then the result is also of this type, being the matrix
product of the two. Otherwise, the resulting transform is simply the concatenation
of the two transforms.
public int get_n_concated_transforms(
General_transform *transform )
Returns the number of concatenated transforms in the given transform. If the type
of the transform is CONCATENATED_TRANSFORM
, then the number
returned is the number of transforms, otherwise it is one.
public General_transform *get_nth_general_transform(
General_transform *transform,
int n )
If the transform is of type CONCATENATED_TRANSFORM
, then a
pointer to the n
'th transform is returned, where
n
is greater than or equal to zero and less than the number of
transforms in the concatenated transform.
Using General Transforms
edit public void general_transform_point(
General_transform *transform,
Real x,
Real y,
Real z,
Real *x_transformed,
Real *y_transformed,
Real *z_transformed )
Transforms a three dimensional point by a general transform, passing back the result in the last three arguments.
public void general_inverse_transform_point(
General_transform *transform,
Real x,
Real y,
Real z,
Real *x_transformed,
Real *y_transformed,
Real *z_transformed )
Transforms a three dimensional point by the inverse of the general transform, passing back the result in the last three arguments.
public void copy_general_transform(
General_transform *transform,
General_transform *copy )
Creates a copy of the general transform, allocating memory within the structure as required.
public void create_inverse_general_transform(
General_transform *transform,
General_transform *inverse )
Creates a new general transform that is the inverse of the given one.
public void invert_general_transform(
General_transform *transform )
Changes the transform to be its inverse. Calling it twice on the same transform is equivalent to not calling the function at all.
Reading and Writing General Transforms
editGeneral transforms are stored in files in an ascii format devised at the McConnell
Brain Imaging Centre, and usually have a filename extension of
.xfm
. The input and output functions are:
public Status output_transform_file(
STRING filename,
STRING comments,
General_transform *transform )
public Status output_transform(
FILE *file,
STRING filename,
int *volume_count_ptr,
STRING comments,
General_transform *transform )
These two functions write the general transform to a file, in the appropriate
format. The comments
line is an arbitrary string which is
stored in the file for documentation purposes. Newline characters in the
comments
correctly result in a multi-line comment with a
comment character inserted at the beginning of each line. The first form opens the
file, with a default extension of .xfm
, writes the transform,
then closes the file. The second form of the function assumes the file is already
open and will later be closed. Because the transform may contain pointers to MINC
files that define the transform, the name of the file must be passed in to this
function, to be used to create the name of the MINC files. The volume_count_ptr
must point to an integer that has been initialized. Each time an auxiliary MINC
file of the transform is created, the integer is used to create a unique filename,
and then the volume_count_ptr is incremented. Both functions return
ERROR
or OK
.
public Status input_transform(
FILE *file,
STRING filename,
General_transform *transform )
public Status input_transform_file(
STRING filename,
General_transform *transform )
Inputs a general transform from a file. The first form assumes the file has already
been opened for input, and will later be closed. The second form opens the file for
input, with a default extension of .xfm
, reads the transform,
then closes the file. Both functions return ERROR
or
OK
.
public STRING get_default_transform_file_suffix()
Returns a pointer to a string consisting of the default suffix for transform files,
currently ``xfm
. This pointer should not be freed or its
contents modified.
Final Source Code Example
editThis is an example which attempts to illustrate a typical processing task incorporating use of volumes, tag points, and transformations. This is a full program which, when linked to the BIC Volume IO Library, reads two volumes in MINC format, a set of tag points from file, and a transformation from file. The tag points are assumed to be in the world space of the first volume, and the transformation is assumed to transform points in the world space of the first volume to the world space of the second volume. The program transforms each tag point by the transformation input (presumably transforming from the world space of the first volume to that of the second volume), then transforms the result to the voxel space of the second volume. If the voxel coordinate is within the second volume, then the value of the corresponding voxel is printed.
#include <volume_io.h>
/* ------------------------------------------------------------------
@COPYRIGHT :
Copyright 1993,1994,1995 David MacDonald,
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.
------------------------------------------------------------------ */
int main()
{
int v1, v2, v3, sizes[MAX_DIMENSIONS];
Real x_world2, y_world2, z_world2;
Real voxel2[MAX_DIMENSIONS];
Real voxel_value;
Volume volume1, volume2;
int i, n_volumes, n_tag_points;
int *structure_ids, *patient_ids;
Real **tags1, **tags2, *weights;
STRING *labels;
General_transform transform;
/*--- input the two volumes */
if( input_volume( "volume1.mnc", 3, NULL, MI_ORIGINAL_TYPE, FALSE,
0.0, 0.0, TRUE, &volume1,
(minc_input_options *) NULL ) != OK )
return( 1 );
if( input_volume( "volume2.mnc", 3, NULL, MI_ORIGINAL_TYPE, FALSE,
0.0, 0.0, TRUE, &volume2,
(minc_input_options *) NULL ) != OK )
return( 1 );
/*--- input the tag points */
if( input_tag_file( "tags_volume1.tag", &n_volumes, &n_tag_points,
&tags1, &tags2, &weights, &structure_ids,
&patient_ids, &labels ) != OK )
return( 1 );
/*--- input the general transform */
if( input_transform_file( "vol1_to_vol2.xfm", &transform ) != OK )
return( 1 );
/*--- convert each tag point */
get_volume_sizes( volume2, sizes );
for_less( i, 0, n_tag_points )
{
/*--- transform the tag points from volume 1 to volume 2
world space */
general_transform_point( &transform,
tags1[i][X], tags1[i][Y], tags1[i][Z],
&x_world2, &y_world2, &z_world2 );
/*--- transform from volume 2 world space to
volume 2 voxel space */
convert_world_to_voxel( volume2, x_world2, y_world2, z_world2,
voxel2 );
/*--- convert voxel coordinates to voxel indices */
v1 = ROUND( voxel2[0] );
v2 = ROUND( voxel2[1] );
v3 = ROUND( voxel2[2] );
/*--- check if voxel indices inside volume */
if( v1 >= 0 && v1 < sizes[0] &&
v2 >= 0 && v2 < sizes[1] &&
v3 >= 0 && v3 < sizes[2] )
{
voxel_value = get_volume_real_value( volume2, v1, v2, v3,
0, 0 );
print( "The value for tag point %d (%s) is: %g\n",
i, labels[i], voxel_value );
}
else
print( "The tag point %d (%s) is outside.\n" );
}
/*--- free up memory */
delete_volume( volume1 );
delete_volume( volume2 );
free_tag_points( n_volumes, n_tag_points, tags1, tags2,
weights, structure_ids, patient_ids, labels );
delete_general_transform( &transform );
return( 0 );
}