MINC/软件开发/MINC1-volumeio-程序员参考
本文档描述了蒙特利尔神经病学研究所麦康奈尔大脑成像中心 (BIC) 可用的例程库。它是由 David MacDonald 作为医学影像软件测试平台的一部分开发的,源代码和相当多的输入来自该中心的 Peter Neelin、Louis Collins 和其他人。
该库,被称为 BIC 体积 IO 库,包含一组用于读取和写入医学影像数据的体积,以及对许多编程任务有用的通用支持例程。大脑成像中心的图像以一种称为 *MINC* 的格式存储在磁盘上,它代表 *医学图像网络 CDF*。有关此格式和用于读取和写入 MINC 文件的 MINC 例程库的更多信息,请参见相关文档。
BIC 体积 IO 库建立在 MINC 库之上,为大多数一般操作提供对 MINC 文件的轻松访问,而无需学习太多 MINC 库的细节,MINC 库是一个用于处理大多数可想而知的情况的综合系统。BIC 体积 IO 库提供了用于内部存储体积的结构和例程,用于访问和修改体积。此外,它还提供了例程来操作标签点和变换,并在大脑成像中心的标准格式下对这些对象执行输入和输出。
本文档描述了 BIC 体积 IO 库的查找位置、提供的功能以及如何将其集成到用户的程序中。该库是用 C 源代码编写的,旨在与 C 源代码链接。它调用了另外两个库:MINC
(BIC 文件格式库)和 netcdf
(一个可移植文件格式管理器)。
用于链接的库是 libvolume_io.a
,它位于 /usr/local/lib
中,相关的包含文件位于 /usr/local/include
下的 volume_io
目录中。这两个目录通常都在编译器搜索路径中。调用体积 IO 函数的源文件必须在顶部包含以下行,以包含相关的类型定义和原型
#include <volume_io/volume_io.h>
为了与 BIC 体积 IO 库链接,必须指定相关的库
cc test_volume_io.o -o test_volume_io \
-lvolume_io -lminc -lnetcdf -lm -lmalloc
这两个库 minc
和 netcdf
通常位于 /usr/local/lib
中,该目录自动在搜索路径中。-lm
选项包含数学库,该库有时由 BIC 体积 IO 库调用。-lmalloc
库提供的内存分配比默认的更快、更健壮。
为了使用此库,必须习惯某些风格上的细微差别,其中一些是特定于此软件作者的,而另一些则是随着软件历史的演变而演变的。因此,请务必注意以下与风格相关的注意事项,恕不道歉。
已经采用了全局变量通常很糟糕的理念。在许多情况下,在函数中使用全局变量会隐藏函数的行为,因此,最好在参数列表中指定所有与函数相关的 信息,而不是依赖任何全局变量。其次,给定函数修改全局变量会阻止在多线程环境中使用此函数(两个进程可能同时尝试修改全局变量)。由于采用了反全局变量的理念,BIC 体积 IO 库的用户通常必须为函数指定许多参数。这似乎是一个很小的代价,作为回报,我们能够在参数列表中看到控制每个函数行为的所有因素。
此库的实现者喜欢输入大量文本,并且通常不喜欢缩写。因此,BIC 体积 IO 库中出现的函数和变量名称可能非常冗长,几乎到了完全语法正确的句子的程度。通常,您永远不会看到名称为 rem_garb()
的函数。相反,函数更可能具有诸如 remove_garbage()
的名称,并且在许多情况下,甚至 take_garbage_to_curb()
。因此,BIC 体积 IO 库的用户将不得不键入比平时更长的函数和变量名称,但希望能够更清楚地了解相关的功能。
在整个库中定义了许多对象,作为不同大小的结构体。由于按值传递结构体(将整个结构体复制到函数中)的效率低下,结构体通常通过引用传递给函数。因此,总是有可能将无效指针传递给期望指向结构体的指针的函数。以下示例说明了使用此类函数的正确和错误方法。给定一个结构体和一个初始化它的库函数,
使用此函数的正确方法
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;
}
使用此函数的错误方法
int main()
{
big_struct *s;
initialize( s ); /* WRONG */
}
因为变量 s
是一个未初始化的指针。正确的方法是定义一个结构体变量,而不是指向结构体的指针,并将指向结构体的指针传递过去
int main()
{
big_struct s;
initialize( &s );
}
或者,可以通过在调用 initialize
之前分配 s
指针来更正上面的错误示例。
为与 BIC 体积 IO 库一起使用定义了几种类型和宏。库中的所有函数声明都以 public
或 private
开头,这指示函数是否可以从其所在的外部文件访问。库用户只对以 public
开头的函数感兴趣。它们定义如下
#define public
#define private static
定义了用于逻辑值的类型
typedef int BOOLEAN
#define FALSE 0
#define TRUE 1
#define OFF FALSE
#define ON TRUE
其他定义的有用类型包括
typedef double Real;
typedef enum
{ OK, ERROR, INTERNAL_ERROR, END_OF_FILE, QUIT }
Status;
typedef char Smallest_int;
一些对一般编程有用的宏包括
N_DIMENSIONS
等于 3 的常量,代表现实世界中的维度数量。
X
等于 0 的常量,用作各种 XYZ 结构体中的索引。
Y
等于 1 的常量,用作各种 XYZ 结构体中的索引。
Z
等于 2 的常量,用作各种 XYZ 结构体中的索引。
SIZEOF_STATIC_ARRAY( array )
返回静态分配数组中的元素数量,例如,
{
int size;
int array[] = { 1, 2, 3 };
size = SIZEOF_STATIC_ARRAY( array ); /* == 3 */
}
ROUND( x )
返回最接近于 x
的整数。如果在两个整数之间,则返回较大的一个。对于负数、零和正数,都能正确运行。
IS_INT( x )
如果实数参数恰好是整数,则返回 TRUE
。
FRACTION( x )
返回参数的小数部分。
FLOOR( x )
返回小于或等于参数的最大整数。
CEILING( x )
返回大于或等于参数的最小整数。
ABS( x )
返回整数或实数的绝对值。
MAX( x, y )
返回两个整数或实数中的最大值。
MAX3( x, y, z )
返回三个整数或实数中的最大值。
MIN( x, y )
返回两个整数或实数中的最小值。
MIN3( x, y, z )
返回三个整数或实数中的最小值。
INTERPOLATE( alpha, a, b )
返回 a
和 b
之间的插值,其中 alpha
的范围为零到一。
PI
返回 Π 的值。
DEG_TO_RAD
返回每度弧度数,用于将角度(以度为单位)乘以该值以得到弧度。
RAD_TO_DEG
返回每弧度度数,用于将角度(以弧度为单位)乘以该值以得到度数。
IJ( i, j, nj )
将 ni
行 nj
列二维数组的索引 [i, j]
转换为单个索引,基于行优先顺序。
IJK( i, j, k, nj, nk )
将 ni
行 nj
列 nk
层三维数组的索引 [i, j, k]
转换为单个索引,基于行优先顺序。
for_less( i, start, end )
执行一个 for 循环,其中 i
从 start
开始,递增直到大于或等于 end
。
等效于 for( i = start; i < end; ++i )
。
for_inclusive( i, start, end )
执行一个 for 循环,其中 i
从 start
开始,递增直到大于 end
。
等效于 for( i = start; i <= end; ++i )
。
GLUE(x,y)
特殊的 C 源代码宏,用于将两个不同的标识符粘合在一起,例如 GLUE(colour,_name)
会得到 colour_name
。
GLUE3(x,y,z)
特殊的 C 源代码宏,用于将三个不同的标识符粘合在一起,例如 GLUE(a,b,c)
会得到 abc
。
编程工具
[edit | edit source]提供了一组函数,用于通用、可移植的编程。这些函数涵盖了字符串、时间、文件 I/O 等基本领域。BIC Volume IO 库的许多部分都引用了编程工具,建议 BIC Volume IO 库的用户尽可能使用这些函数。以下是所有编程工具的列表,按相关领域分组,并按字母顺序排列。
字符串
[edit | edit source]提供了一些简单的字符串操作技术。字符串是任意长的以 NULL
结尾的字符数组,根据需要进行分配和删除。类型 STRING
定义为
typedef char *STRING;
基本的字符串创建和删除例程是
public STRING alloc_string(
int length )
public STRING create_string(
STRING initial )
函数 alloc_string
为指定长度的字符串分配存储空间(通过分配 length+1
字节),但不为字符串分配任何值。函数 create_string
创建一个已分配的字符串,其值为 initial
。如果 initial
是一个 NULL
指针,则创建一个空字符串(``)。
public void delete_string(
STRING string )
可以使用此函数删除与字符串关联的存储空间。
public int string_length(
STRING string )
返回字符串的长度。
public BOOLEAN equal_strings(
STRING str1,
STRING str2 )
如果两个字符串完全相等,则返回 TRUE
。
public void replace_string(
STRING *string,
STRING new_string )
一个方便的函数,它删除 string
参数,并将其重新分配给 new_string
。它不会复制 new_string
的值,而只是将 string
设置为指针 new_string
。
public void concat_char_to_string(
STRING *string,
char ch )
将一个字符连接到字符串的末尾。
public STRING concat_strings(
STRING str1,
STRING str2 )
创建一个字符串,它是两个参数的连接,并返回该字符串,而不会改变字符串参数。
public void concat_to_string(
STRING *string,
STRING str2 )
用参数 string
和 str2
的连接替换参数 string
。
public BOOLEAN is_lower_case(
char ch )
如果字符是小写字母,则返回 TRUE
。
public BOOLEAN is_upper_case(
char ch )
如果字符是大写字母,则返回 TRUE
。
public char get_lower_case(
char ch )
如果字符是大写字母,则返回字符的小写版本,否则返回字符本身。
public char get_upper_case(
char ch )
如果字符是小写字母,则返回字符的大写版本,否则返回字符本身。
public BOOLEAN string_ends_in(
STRING string,
STRING ending )
确定字符串是否以指定的 ending
结尾。例如,传递参数 "rainfall"
和 "fall"
会返回 TRUE
。
public STRING strip_outer_blanks(
STRING str )
返回一个字符串,它是参数 str
,去掉了所有前导和尾随空格。
public void make_string_upper_case(
STRING string )
将字符串转换为全大写字符串。在原地修改字符串。
public int find_character(
STRING string,
char ch )
在给定的 string
中搜索给定的字符,返回找到它的索引,如果未找到,则返回 -1。
public BOOLEAN blank_string(
STRING string )
如果字符串为空或仅包含空格、制表符和换行符,则返回 true。
通用文件 I/O
[edit | edit source]虽然可以使用标准的 UNIX 文件接口(例如 fprintf
),但 BIC Volume IO 库包含一组例程,用于执行所有文件操作,并且有可能移植到其他操作系统。还有一些其他的优点,包括自动扩展 <function>~ </function> 和环境变量。此外,还提供了压缩文件的自动解压缩功能。
public STRING expand_filename(
STRING filename )
在参数 filename
中搜索某些模式,并相应地扩展它们,返回扩展后的文件名。任何以 ~
开始,但不包括斜杠 /
的字符序列将被更改为指定的用户的 home 目录。任何美元符号 $
的出现都会导致后面的文本(直到下一个斜杠)被扩展为由文本指定的 environment 变量。可以通过在任何 ~
或 $
之前放置一个反斜杠 \ 来避免这种扩展。在所有以下使用文件名的函数中,都会自动执行文件名扩展。
public BOOLEAN file_exists(
STRING filename )
如果文件存在,则返回 TRUE
。
public BOOLEAN check_clobber_file(
STRING filename )
public BOOLEAN check_clobber_file_default_suffix(
STRING filename,
STRING default_suffix )
检查文件是否存在,如果存在,则提示用户是否可以覆盖它。如果文件不存在,或如果文件存在,用户对提示输入 `y'
,则返回 TRUE。该函数的第二种形式在文件名后面追加一个默认后缀,如果文件名还没有后缀的话。
public void remove_file(
STRING filename )
删除指定的文件,这将导致该文件在所有对它的引用关闭后才会被删除。
public BOOLEAN filename_extension_matches(
STRING filename,
STRING extension )
如果文件以给定的扩展名结尾(前面有一个点),则返回 TRUE
。例如,<function>filename_extension_matches( "volume.mnc" , "mnc" )</function> 返回 TRUE
。正确处理压缩文件,例如,传递参数 "volume.mnc.Z"
和 "mnc"
返回 TRUE
。
public STRING remove_directories_from_filename(
STRING filename )
从文件名中剥离目录,返回结果。
public STRING extract_directory(
STRING filename )
从文件名中提取目录,返回结果。
public STRING get_absolute_filename(
STRING filename,
STRING directory )
给定一个文件名和当前目录,返回一个绝对文件名(以斜杠 /
开头的文件名)。
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 )
函数 open_file()
打开指定的文件,其中 io_type
必须是 WRITE_FILE
或 READ_FILE
之一,file_format
必须是 ASCII_FORMAT
或 BINARY_FORMAT
之一。如果成功,则将文件指针传递到最后一个参数中,并返回状态 OK
。否则,将返回一个空指针,并返回状态 ERROR
。自动执行文件名扩展。第二个函数执行与 open_file
相同的任务,此外还自动添加指定的后缀扩展名,如果需要的话。在输入时,如果指定的文件不存在,并且文件没有扩展名,则查找带有默认扩展名的指定文件。
public Status close_file(
FILE *file )
关闭文件,如果成功则返回 OK
。
public Status set_file_position(
FILE *file,
long byte_position )
将文件指针定位到文件中的指定位置,其中 0 对应于文件开头,如果成功则返回 OK
。
public Status flush_file(
FILE *file )
刷新文件缓冲区中的任何待定输出,如果成功则返回 OK
。
public Status input_character(
FILE *file,
char *ch )
从文件中输入一个字符,并将其作为第二个参数传递回来。如果字符是文件结尾字符,则返回状态 ERROR
,否则返回 OK
。
public Status unget_character(
FILE *file,
char ch )
将字符放回输入队列。
public Status input_nonwhite_character(
FILE *file,
char *ch )
输入下一个非空白字符,即下一个不是空格、制表符或换行符的字符。
public Status skip_input_until(
FILE *file,
char search_char )
从文件中输入字符,直到找到指定的搜索字符。
public Status input_string(
FILE *file,
STRING *str,
char termination_char )
从文件中输入一个字符串。从文件中读取字符串,直到遇到终止字符或换行符。如果字符串以换行符结尾,并且终止字符不是换行符,则从文件中获取的下一个字符将是换行符。否则,从文件中获取的下一个字符将是终止字符之后的字符。
public Status input_quoted_string(
FILE *file,
STRING *str )
从文件中输入一个字符串,以引号分隔,返回 OK
或 ERROR
。成功读取一个字符串后,从文件中获取的下一个字符将是结束引号之后的字符。
public Status input_possibly_quoted_string(
FILE *file,
STRING *str )
从文件中输入一个字符串,以引号或空白符分隔,返回 OK
或 ERROR
。成功读取一个字符串后,从文件中获取的下一个字符将是结束引号或最后一个非空白字符之后的字符。
public Status input_line(
FILE *file,
STRING line )
将文件中的字符输入到参数 line
中,直到遇到下一个换行符。如果找到了换行符,则从文件中获取的下一个字符将是换行符之后的字符。换行符不会存储在字符串中。
public Status input_boolean(
FILE *file,
BOOLEAN *b )
输入下一个非空白字符。如果它是 ``f
或 ``F
,则传递值 FALSE
回来。如果它是 ``t
或 ``T
,则传递值 TRUE
回来。否则返回 ERROR
。
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 )
从 ascii 文件中输入指定类型的值。
public Status input_newline(
FILE *file )
从文件读取字符并丢弃字符,直到并包括下一个换行符。如果找到换行符,则返回 OK
;如果先到达文件末尾,则返回 ERROR
。
public Status input_binary_data(
FILE *file,
void *data,
size_t element_size,
int n )
从文件读取二进制格式的数据数组。该数组包含 n
个元素,每个元素的大小为 element_size
。返回 OK
或 ERROR
。
public Status output_character(
FILE *file,
char ch )
将一个字符输出到文件,返回 OK
或 ERROR
。
public Status output_string(
FILE *file,
STRING str )
将指定字符串输出到文件,返回 OK
或 ERROR
。
public Status output_quoted_string(
FILE *file,
STRING str )
输出一个引号,指定字符串,以及一个结束引号。
public Status output_newline(
FILE *file )
输出一个换行符,返回 OK
或 ERROR
。
public Status output_boolean(
FILE *file,
BOOLEAN b )
如果参数为 TRUE
,则输出一个空格和字母“T
”;否则,输出一个空格和字母“F
”。
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 )
输出一个空格,然后将指定值输出到 ASCII 文件。
public Status output_binary_data(
FILE *file,
void *data,
size_t element_size,
int n )
将数据数组输出到文件,以二进制格式。该数组包含 n
个元素,每个元素的大小为 element_size
。返回 OK
或 ERROR
。
public Status io_binary_data(
FILE *file,
IO_types io_flag,
void *data,
size_t element_size,
int n )
根据参数 io_flag
为 READ_FILE
或 WRITE_FILE
,输入或输出指定二进制数据。
public Status io_newline(
FILE *file,
IO_types io_flag,
File_formats format )
根据参数 io_flag
为 READ_FILE
或 WRITE_FILE
,输入或输出 ASCII 换行符。如果 format
参数为 BINARY_FORMAT
,则该函数什么也不做。
public Status io_quoted_string(
FILE *file,
IO_types io_flag,
File_formats format,
STRING str )
如果 format
参数为 ASCII_FORMAT
,则根据参数 io_flag
为 READ_FILE
或 WRITE_FILE
,输入或输出以引号分隔的字符串。如果 format
参数为 BINARY_FORMAT
,则输入或输出以整数开头表示字符串长度的字符串。
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 )
输入或输出指定类型的二进制或 ASCII 值。
public Status io_ints(
FILE *file,
IO_types io_flag,
File_formats format,
int n,
int *ints[] )
输入或输出二进制或 ASCII 格式的整数数组。
public Status io_unsigned_chars(
FILE *file,
IO_types io_flag,
File_formats format,
int n,
unsigned char *unsigned_chars[] )
输入或输出二进制或 ASCII 格式的无符号字符数组。
提供了一组宏,允许轻松分配和释放内存,最多可分配 5 维数组。还执行内存分配检查,以捕获错误,例如释放未分配的内存。此外,内存分配会自动跟踪所有分配的内存,以便检测内存泄漏(孤立的内存)。
基本宏如下所示
ALLOC( ptr, n_items )
分配 n_items
个正确类型元素,并将结果分配给参数 ptr
。
FREE( ptr )
释放参数 ptr
指向的内存。
REALLOC( ptr, n_items )
更改参数 ptr
指向的内存大小,使其为 n_items
个元素,在此过程中可能会更改 ptr
的值。
ALLOC_VAR_SIZED_STRUCT( ptr, element_type, n_elements )
分配一个可变大小的结构,该结构必须具有特定形式。结构的最后一个元素必须是一个大小为 1 的数组,该数组将构成结构的可变大小部分。参数 element_type
必须是该最后一个元素的类型,参数 n_elements
是除了结构第一部分的内存之外,要为该数组分配的所需元素数量。一个使用示例是
{
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 )
分配一个大小为 n1
到 n2
等的 2 到 5 维数组,并将结果存储在指定的指针 ptr
中。在二维情况下,这仅通过两次内存分配来完成,一次分配 n1
乘以 n2
个元素用于存储,另一次分配 n1
个指向第一个内存区域的指针。一般来说,每个维度都需要一次内存分配。
FREE2D( ptr )
FREE3D( ptr )
FREE4D( ptr )
FREE5D( ptr )
释放与多维数组关联的内存。
public void set_alloc_checking(
BOOLEAN state )
启用或禁用分配检查。它通常在程序开始时被调用,以提供对分配的双重检查。它在每次调用之前的分配宏时都会进行额外的分配。如果未调用此函数,则可以通过将环境变量 DEBUG_ALLOC
设置为任何内容来打开分配检查。
分配检查检测三种类型的内存编程错误:释放未分配的指针,通过检查从系统 malloc 例程返回的内存块是否与现有内存块重叠,有时检测到系统内存是否已损坏,以及通过记录所有分配的内存并打印出当前分配的内存,可以检测内存泄漏。
public void output_alloc_to_file(
STRING filename )
如果启用了内存检查,则可以使用此函数检测内存泄漏。在程序结束时,程序员应该释放所有已知的内存,然后调用此函数。任何剩余的已分配内存将被打印到文件中,指示分配内存的文件和行号。
除了前面描述的基本内存分配宏之外,还提供了一组用于处理动态更改大小的数组的有用宏
SET_ARRAY_SIZE( array, previous_n_elems, new_n_elems,
chunk_size )
此宏通过指定先前分配给数组的元素数量(从零开始)来增加或减少数组的大小。chunk_size
参数定义分配的内存块的大小。例如,如果 chunk_size
为 100,则此宏仅在大小更改跨越到 100 的不同倍数时才会重新分配数组,从而避免每次调用时都进行内存分配。此内存分配粒度规范必须始终一致地指定;如果此宏使用给定变量和块大小被调用,则随后使用相同变量调用此宏必须指定相同的块大小。另请注意,作为 new_n_elems
传入的数字必须在下次调用此宏时作为 previous_n_elems
传入。
ADD_ELEMENT_TO_ARRAY( array, n_elems,
elem_to_add, chunk_size )
将参数 elem_to_add
添加到数组的 n_elems
个索引处,然后递增 n_elems
。参数 chunk_size
指定内存分配的粒度。
DELETE_ELEMENT_FROM_ARRAY( array, n_elems, index_to_remove,
chunk_size )
从数组中删除 index_to_remove
个元素,减少数组中的元素数量(n_elems
)并减少内存分配,如果跨越 chunk_size
的倍数。同样,chunk_size
必须在涉及给定指针的前面三个宏的所有调用中指定为相同。
ADD_ELEMENT_TO_ARRAY_WITH_SIZE( array, n_alloced, n_elems,
elem_to_add, chunk_size )
向数组添加一个元素(elem_to_add
),递增 n_elems
。如果需要,内存将按 chunk_size
中指定的数量增加,并且 n_alloced
变量将按此数量递增。此用法与 ADD_ELEMENT_TO_ARRAY
的用法不同,因为元素数量(n_elems
)可以任意减少,而不会导致内存被释放。
为了提供对特定处理任务进度的简单监控,可以使用一个进度报告模块。当任务正在进行时,进度报告会在行上打印点,表示任务完成的进度。如果任务将花费很长时间(超过 2 分钟),进度报告会定期打印当前完成的百分比和估计的剩余时间。一个使用示例,后面跟着函数描述,如下所示
{
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 )
初始化进度报告结构,指定处理中将发生的步骤数量,以及要打印的进度报告标题。在进度报告期间,显示屏将自动从打印一行点的短作业模式切换到定期打印完成百分比和估计剩余时间的长作业模式。如果 one_line_only
标志为 TRUE
,则此功能将被禁用,并且只会显示一行点。
public void update_progress_report(
progress_struct *progress,
int current_step )
告诉进度报告模块已完成多少个步骤,并导致更新行上点的显示或估计剩余时间。
public void terminate_progress_report(
progress_struct *progress )
终止进度报告。
与使用标准 UNIX 函数 printf
进行例行文本输出相比,提供了一个外观相似的模块,该模块允许安装任意打印输出函数。例如,这可以用来将输出镜像到日志文件,或将文本发送到 X 窗口。主要变化是使用名为 print
或 print_error
的函数,该函数与标准 printf
函数具有完全相同的参数。
public void print( STRING format, ... )
public void print_error( STRING format, ... )
接受与 printf()
相同的参数,但允许为最终输出阶段安装用户输出函数。
public void set_print_function(
void (*function) ( STRING ) )
public void set_print_error_function(
void (*function) ( STRING )
设置输出函数,print()
或 print_error
调用中的所有文本将被发送到该函数。默认情况下,没有打印函数,输出被发送到 printf()。
public void push_print_function()
public void push_print_error_function()
临时将打印或错误打印函数设置为标准输出,并允许用户设置另一个打印函数,该函数将在相应的 pop
函数被调用时消失。
public void pop_print_function()
public void pop_print_error_function()
恢复先前的用户打印或错误打印函数。
提供了一些与时间、日期和 CPU 时间相关的基本实用程序。
public Real current_cpu_seconds( void )
返回当前进程到目前为止使用的 CPU 秒数。
public Real current_realtime_seconds( void )
返回当前进程运行的秒数。
public STRING get_clock_time()
返回表示当前时钟时间的字符串(小时和分钟),调用者负责删除该字符串。
public STRING get_date()
返回表示当前日期的字符串,调用者负责删除该字符串。
public STRING format_time(
STRING format,
Real seconds )
接受以秒为单位的时间和具有格式组件的格式字符串,例如“%g %s
”,并将时间以毫秒、秒、分钟、天等适当单位打印到字符串中。字符串被返回,调用程序负责删除该字符串。
public void print_time(
STRING format,
Real seconds )
与 format_time
相同,但使用结果调用 print()
。
public void sleep_program(
Real seconds )
将程序挂起指定的秒数。请注意,在大多数系统上,这只会执行到某个特定时间增量的最接近倍数。在 Silicon Graphics 系统上,这将是到最接近的百分之一秒。
在这个软件开发的实验室中,处理的任务涉及到多维数据体,例如由磁共振和PET扫描仪产生的数据。因此,提供了一套广泛的例程来表示体积,以及以MINC格式读取和写入体积。
体积的基本类型是Volume
,它实际上是指向已分配结构的指针,该结构包含有关体积类型、维度数量、体素值等的所有信息。为了使用体积结构,必须首先创建体积,然后设置体积的大小,然后分配体素值的大数组。或者,可以通过调用相应的函数来读取MINC文件并创建体积,从而自动创建体积。
一个体积与一定数量的维度相关联,这些维度必须在1到5之间,但通常为3。体积被认为是任何一种类型的多维数组,包括所有大小的整数和实数类型。即使一个体积可能存储在例如1字节类型中,其值为0到255,但有一个实际值范围,它提供了从体素值到任何任意实数范围的映射。这样,实际范围可以是任何有效的实数区间,并且与特定存储类型无关。
由于大多数体积将通过从MINC文件读取而创建,因此将首先介绍这种方法,然后描述如何从头开始创建体积。最后,将介绍更高级应用程序的MINC输入和输出的较低级别。
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 )
此例程从MINC文件读取一个体积,首先在create_volume_flag
指定为TRUE
(通常情况)的情况下创建体积。维度数量是体积所需的维度数量。如果此数量小于文件中的维度数量,则只读取与该维度数量相对应文件的第一个部分。如果指定的维度数量小于1,则使用文件中的维度数量。参数dim_names
指定体积在体积变量中存储的顺序。对于存储体积中的每个维度,都有一个相应的名称,该名称是MIxspace
、MIyspace
、MIzspace
、ANY_SPATIAL_DIMENSION
或空字符串之一。这些与文件中的对应维度相匹配,并且体积数组的维度排序在输入时重新排序。因此,如果用户希望以X-Z-Y顺序表示体积,则作为dim_names
数组传递的值应为三个字符串MIxspace
、MIzspace
和MIyspace
。此排序选择很重要,因为它定义了随后对体素索引的任何引用的顺序。
四个参数volume_nc_data_type
到volume_voxel_max
可用于指定体积变量中所需的存储类型,这些类型会从文件中的存储类型自动转换。volume_nc_data_type
是MI_ORIGINAL_TYPE
、NC_BYTE
、NC_SHORT
、NC_LONG
、NC_FLOAT
或NC_DOUBLE
之一。对于整数类型,如果需要有符号类型,则volume_signed_flag
为TRUE
,否则为FALSE
。volume_voxel_min
和volume_voxel_max
指定有效体素值的范围,并且通常设置为相等以指示使用类型的完整范围,例如对于无符号NC_BYTE
,范围为0到255.0。如果传递MI_ORIGINAL_TYPE
,则使用文件中的类型、符号和体素范围。
如果create_volume_flag
为TRUE
(通常情况),则会自动创建体积。否则,假定体积已经存在,并且只有在当前大小与文件导致的新大小不同时才会重新创建,从而减少读取多个文件时的内存分配量。
options
参数指定输入过程的一些特殊选项,通常只传递一个NULL
指针,表示应使用默认选项。目前,可能的选项是
public void set_default_minc_input_options(
minc_input_options *options )
将默认选项填充到options
结构中,该结构随后将传递给input_volume()
。
public void set_minc_input_promote_invalid_to_min_flag(
minc_input_options *options,
BOOLEAN flag )
默认情况下,任何超出有效体素值范围的体素值都会提升到最小有效体素值。如果将此flag
设置为FALSE
,则会禁用此提升。
public void set_minc_input_vector_to_scalar_flag(
minc_input_options *options,
BOOLEAN flag )
默认情况下,任何包含类型为向量的维度的体积(例如RGB颜色体积)都会通过对每个向量的分量进行平均来转换为标量体积。可以通过将flag
传递为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] )
默认情况下,任何包含类型为向量的维度的体积(例如RGB颜色体积)都会通过对每个向量的分量进行平均来转换为标量体积。如果将此选项设置为true,则任何具有set_minc_input_colour_dimension_size
(默认值为3)所指定的组件数量的向量体积将使用set_minc_input_colour_indices
(默认值为0、1、2)所指定的索引转换为颜色体积。
public void set_minc_input_user_real_range(
minc_input_options *options,
double minimum,
double maximum )
默认情况下,读取到整数类型体积中的文件将进行缩放,以便实际范围是输入文件中数据的完整范围。用户可以通过使用适当的实际最小值和最大值调用此函数来定义另一个范围。对于浮点类型体积,设置范围将更改体积的实际范围,但不会更改体积中的实际值。如果最小值大于或等于最大值,则恢复默认行为。
除了使用input_volume()
函数一次性输入体积外,还有另一种方法可以允许在体积输入与其他处理任务之间穿插。以下是input_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 )
这将初始化一个文件名,以便增量输入体积。此函数的参数与input_volume()
函数的参数相同,此外还添加了input_info
结构,该结构被初始化以供在剩余函数中使用。如果需要,将创建体积,但不分配,并且不会读取体积的任何部分。
public void delete_volume_input(
volume_input_struct *input_info )
关闭文件并终止体积的输入。通过调用start_volume_input()
函数,然后调用delete_volume_input()
函数,可以创建一个体积,该体积具有有关文件体积的所有信息,但未分配体素。
public void cancel_volume_input(
Volume volume,
volume_input_struct *input_info )
关闭文件,终止体积的输入,并删除体积。只需调用delete_volume_input()
,然后调用delete_volume()
。
public BOOLEAN input_more_of_volume(
Volume volume,
volume_input_struct *input_info,
Real *fraction_done )
输入体积的一小部分,计算结果既有效率,又不会太大,以至于不能在调用此函数时穿插其他处理的调用。如果还有更多工作要做,则返回TRUE
,即应再次调用此函数。已完成的部分作为0到1之间的数字传递回来。
public Minc_file get_volume_input_minc_file(
volume_input_struct *volume_input )
将体积输入中的Minc_file
结构传递回来,以允许在需要此类型例程中使用此结构。
体积输出可以通过两种例程之一完成,具体取决于体积是否被视为另一个体积的修改版本或独立体积。
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 )
将指定的体积输出到指定的文件名。如果参数file_nc_data_type
为MI_ORIGINAL_TYPE
,则体积将以体积变量中的相同类型存储在MINC文件中。否则,四个参数file_nc_data_type
、file_signed_flag
、file_voxel_min
和file_voxel_max
指定在文件中存储体积的类型和有效体素范围。如果history
参数非空,则它表示体积的描述,并将放置在MINC体积中。如果options
参数为NULL
,则将使用默认选项。否则,可以通过此参数设置一些特定输出选项,以及以下函数
public void set_default_minc_output_options(
minc_output_options *options )
将options
结构设置为默认值。然后,用户可以覆盖默认值并将结构传递给output_volume()
函数。目前,只有一个输出选项
public void delete_minc_output_options(
minc_output_options *options )
删除options
数据。
public void set_minc_output_dimensions_order(
minc_output_options *options,
int n_dimensions,
STRING dimension_names[] )
定义文件的输出顺序。每个维度名称字符串必须在体积中具有匹配的维度名称,这定义了输出文件中维度的顺序。例如,可能以<function>(x, y, z)</function>顺序输入体积。要以(z, y, x)
顺序输出它,请将dimension_names
设置为三个字符串MIzspace
、MIyspace
和MIxspace
。
public void set_minc_output_real_range(
minc_output_options *options,
Real real_min,
Real real_max )
定义文件中将出现的图像范围。默认情况下,不指定任何内容,并且output_volume
使用卷的实际最小值和最大值。若要设置卷的实际范围,请参阅set_volume_real_range()
的相关文档。
如果卷是当前存储在文件中的另一个卷的修改,那么使用以下函数输出卷更为合适
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 )
此函数与其他卷输出方法(output_volume()
)之间的唯一区别在于,此函数将辅助数据从原始文件(original_filename
)复制到新文件。此辅助数据包括患者姓名和其他扫描数据等项目,并且不会读入卷,因此将它正确传递到新的 MINC 文件的唯一方法是使用此函数。
创建和分配卷后,有很多函数用于操作卷。请注意,与每个卷相关联的是一个有效的体素范围,该范围指示实际存储在卷中的值的范围,例如,0 到 200 是无符号字节卷的一种可能的范围。还有一个第二个范围,即real
范围,它将有效体素范围映射到任意实际范围,例如,有效体素的 0 到 200 可以映射到real
范围内的 -1.5 到 1.5。在处理卷时,通常会对real
范围感兴趣。
public Real get_volume_voxel_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4 )
给定一个卷和一个到五个体素索引(取决于卷的维数),返回相应的体素值。例如,如果卷是三维的,那么最后两个参数将被忽略。
value = convert_voxel_to_value( volume, voxel );
给定一个卷和一个体素值,将它转换为实际范围内的值,并返回它。
voxel = convert_value_to_voxel( volume, value )
给定一个卷和一个实际值,将它转换为有效体素范围内的体素值,并返回它。
public Real get_volume_real_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4 )
给定一个卷和一个到五个体素索引(取决于卷的维数),返回相应的实际值。例如,如果卷是三维的,那么最后两个参数将被忽略。
public void set_volume_voxel_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4,
Real voxel )
给定一个卷、一个到五个体素索引和一个voxel
值,将此值分配给卷中相应的体素。请注意,不会执行从有效实际范围到有效体素范围的转换,因此用户可能需要首先使用convert_value_to_voxel
函数或使用set_volume_real_value
函数。
public void set_volume_real_value(
Volume volume,
int v0,
int v1,
int v2,
int v3,
int v4,
Real value )
给定一个卷、一个到五个体素索引和一个value
,在转换为体素值后,将此值分配给卷中相应的体素。
public void delete_volume(
Volume volume )
删除与卷相关联的所有内存。
public nc_type get_volume_nc_data_type(
Volume volume,
BOOLEAN *signed_flag )
返回卷的存储类型(例如,NC_SHORT
),并传递回一个指示它是有符号还是无符号的信息。
public int get_volume_n_dimensions(
Volume volume )
返回卷的维数。
public void get_volume_sizes(
Volume volume,
int sizes[] )
将每个维度的尺寸存储在数组sizes
中。这是每个维度的体素数。数组sizes
必须至少与卷的维数一样大。
public void get_volume_separations(
Volume volume,
Real separations[] )
将每个维度的切片间距存储在数组separations
中。数组separations
必须至少与卷的维数一样大。
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 )
前两个函数返回允许的最小或最大体素值。第三个函数传递回这两个值。
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 )
前两个函数返回最小或最大实际值。第三个函数传递回这两个值。映射到此实际空间将最小voxel
值线性映射到最小real
值,并将最大voxel
值映射到最大real
值。
public STRING *get_volume_dimension_names(
Volume volume )
返回指向每个维度名称列表的指针。此内存必须由调用函数使用以下函数释放。
public void delete_dimension_names(
Volume volume,
STRING dimension_names[] )
释放为维度名称分配的内存。
public void set_rgb_volume_flag(
Volume volume,
BOOLEAN flag )
设置指示卷是否为红-绿-蓝颜色的卷标志。只有数据类型为无符号长的卷才能是 rgb 卷。
public BOOLEAN is_an_rgb_volume(
Volume volume )
如果卷是红-绿-蓝颜色卷,则返回TRUE
,因为可以在卷输入时创建此卷。
一个卷有两个坐标系。体素坐标系只是卷的 n 维索引坐标系。例如,体素坐标 (0.0, 0.0, 0.0) 对应于三维卷中第一个体素的中心。体素坐标 ( 99.0, 0.0, 0.0 ) 对应于大小为 ( 100, 200, 150 ) 的三维卷的第一个方向中的最后一个体素的中心。第二个坐标系是任意的三维坐标系,通常称为世界坐标系,通常是 Talairach 坐标系。以下函数提供在这两个坐标系之间进行转换的方法
public void convert_voxel_to_world(
Volume volume,
Real voxel[],
Real *x_world,
Real *y_world,
Real *z_world )
给定一个卷和一个实值体素索引,传递回相应的世界坐标。
public void convert_3D_voxel_to_world(
Volume volume,
Real voxel1,
Real voxel2,
Real voxel3,
Real *x_world,
Real *y_world,
Real *z_world )
与convert_voxel_to_world
相同,只是它仅适用于三维卷。
public void convert_world_to_voxel(
Volume volume,
Real x_world,
Real y_world,
Real z_world,
Real voxel[] )
将世界坐标转换为体素。为了将这些体素坐标用作整数索引(例如,作为GET_VALUE
宏的参数),参数voxel
的每个分量必须首先四舍五入到最接近的整数。
public void convert_3D_world_to_voxel(
Volume volume,
Real x_world,
Real y_world,
Real z_world,
Real *voxel1,
Real *voxel2,
Real *voxel3 )
与convert_world_to_voxel
相同,只是它仅适用于三维卷。
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[] )
这两个函数在体素空间和世界空间之间转换向量。这是通过将点 (0,0,0) 转换为另一个空间来完成的,此外,将向量视为点并将其转换为另一个空间,并将这两个结果之间的向量传递回。
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 )
将被假定为世界坐标系中的表面法线的向量转换。
除了访问特定体素值的例程之外,还可以使用最近邻、线性或三次插值对卷进行插值,这些插值在体素空间或世界空间中指定。
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 )
在指定的体素处对卷进行插值,其中degrees_continuity
的值分别为 -1、0 或 2,表示最近邻、线性或三次插值。如果use_linear_at_edge
为真,则任何三次插值都将降级为卷边缘附近的线性插值。此选项仅在特殊情况下需要。参数outside_value
是卷外部任何点的值。参数interpolating_dimensions
指示哪些维度正在进行插值,通常是NULL
指针,表示所有维度都在进行插值。插值后的值将放置在 values 数组中。值的个数等于未插值维度的值的个数,如果所有维度都在进行插值,则为 1。如果导数参数不为空,则生成的导数将放置在相应的位置。first_deriv
是一个大小为 values 个数乘以插值维度个数的二维数组。second_deriv
是一个大小为 values 个数乘以插值维度个数乘以插值维度个数的三维数组。
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[] )
在指定的世界位置处对卷进行插值,其中degrees_continuity
的值分别为 -1、0 或 2,表示最近邻、线性或三次插值。如果use_linear_at_edge
为真,则任何三次插值都将降级为卷边缘附近的线性插值。此选项仅在特殊情况下需要。参数outside_value
是卷外部任何点的值。插值后的值将放置在 values 数组中。值的个数等于非空间维度的值的个数,如果所有维度都是空间维度,则为 1。如果导数参数不为空,则生成的导数将放置在相应的位置。
public void set_volume_interpolation_tolerance(
Real tolerance )
出于速度考虑,如果在体素中心或其附近评估卷,则不执行插值,而是返回体素值。容差定义了此操作发生的距离与体素中心之间的距离,默认值为 0。请注意,如果需要导数并且指定的连续性程度为 0 或更大,则即使在指定的体素容差范围内,也会执行插值。
在某些情况下,希望以与从文件读取不同的方式创建卷。以下函数提供从头开始创建卷或创建与现有卷相似的卷的方法。
public Volume create_volume(
int n_dimensions,
STRING dimension_names[],
nc_type nc_data_type,
BOOLEAN signed_flag,
Real voxel_min,
Real voxel_max )
创建并返回给定类型(例如,NC_BYTE
,signed_flag
等于FALSE
)和给定有效体素范围的卷。dimension_names
用于描述卷的每个维度,目前仅在将卷写入文件时使用。
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 )
创建卷后,可以使用这些函数随后更改有效体素范围或有效实际范围。
public void set_volume_sizes(
Volume volume,
int sizes[] )
设置卷的尺寸,即每个维度的体素数。请注意,这必须在调用函数alloc_volume_data
分配体素之前完成。
public void alloc_volume_data(
Volume volume )
创建卷并设置其大小后,此函数将为体素分配内存。请注意,体素值不会被初始化,用户必须用所需的值填充卷。
与每个卷相关联的是从voxel
空间到world
空间的变换。有几种方法可以定义这种变换。最简单的方法是直接指定它
public void set_voxel_to_world_transform(
Volume volume,
General_transform *transform )
将给定变换分配给卷。
public General_transform *get_voxel_to_world_transform(
Volume volume )
返回指向卷的体素到世界变换的指针。
public void set_volume_separations(
Volume volume,
Real separations[] )
设置每个卷维度的体素间距。请注意,这会导致体素到世界变换相应地更新。
public void set_volume_translation(
Volume volume,
Real voxel[],
Real world_space_voxel_maps_to[] )
设置体素到世界变换的平移部分。指定一个体素坐标(voxel
),以及希望此体素映射到的实际世界位置(world_space_voxel_maps_to
)。体素到世界变换将更新以提供此映射。
public void set_volume_direction_cosine(
Volume volume,
int dimension,
Real dir[] )
设置特定体素维度的真实世界轴。例如,如果dimension
为 1,而dir
为 (0.0, 1.0, 1.0),则体积第二维度的体素将映射到真实世界轴 (0.0, 1.0, 1.0),该轴已归一化为单位长度,然后按第二体积维度的体积间距进行缩放。
public void get_volume_direction_cosine(
Volume volume,
int dimension,
Real dir[] )
返回特定体素维度的真实世界轴。请注意,dimension
必须是体积中的空间维度。
public void set_volume_space_name(
Volume volume,
STRING name )
使用字符串名称设置体积的坐标系类型。这可以是任何字符串,但 MINC 提供了一组定义的常量:MI_NATIVE
、MI_TALAIRACH
和 MI_CALLOSAL
。
public STRING get_volume_space_name(
Volume 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 )
这两个函数都创建并返回一个新体积,该体积与现有体积具有相同的定义(大小、体素到世界空间变换等)。如果参数nc_data_type
不是MI_ORIGINAL_TYPE
,则新体积的存储类型将与原始体积不同,并由nc_data_type
、signed_flag
、voxel_min
和 voxel_max
指定。在第一个函数中,体素值已分配但未初始化。在第二个函数中,它们未分配。
public Volume copy_volume(
Volume volume )
创建体积的精确副本。
为了有效地处理对虚拟内存而言过大的体积,已经实现了一个简单的缓存方案。磁盘文件按需读取和写入,以提供将大型体积完全驻留在内存中的外观。之前描述的所有体积例程都透明地支持此功能,但有一些例程用于配置体积缓存。
public void set_n_bytes_cache_threshold(
int threshold )
设置体积缓存的阈值字节数。如果创建的体积大于此值(无论显式创建还是从文件输入创建),则该体积将在内部由缓存方案表示。如果未调用此函数,则默认值为 80 兆字节,或环境变量VOLUME_CACHE_THRESHOLD
的值(如果存在)。阈值为零将导致所有体积被缓存。阈值小于零将导致没有体积被缓存。
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[] )
当在缓存的体积中访问体素时,将根据需要从磁盘读取或写入包含该体素的相应块。这两个函数控制块的默认大小,因此控制快速访问相邻体素(使用较大的块大小)和快速访问任意分布的体素(使用较小的块大小)之间的权衡。函数set_cache_block_sizes_hint
表示未来创建的体积的默认块大小应基于这样的假设来计算,即应用程序将主要以逐片的方式访问体积体素(SLICE_ACCESS
)或以不可预测的顺序访问体素(RANDOM_VOLUME_ACCESS
)。第二个函数set_default_cache_block_sizes
提供了一种替代方案,其中显式设置了默认块大小。对任一函数的调用都会覆盖之前对另一个函数的任何调用。如果未调用这两个函数中的任何一个,则默认值为每个维度的块大小为 8 个体素,或环境变量VOLUME_CACHE_BLOCK_SIZE
指定的值。这些函数只影响之后创建的体积。要更改给定体积的值,可以使用以下函数
public void set_volume_cache_block_sizes(
Volume volume,
int block_sizes[] )
设置给定体积的缓存块大小。由于此函数会导致缓存完全刷新,因此要更改块大小,可能会在后续体素访问过程中造成暂时速度损失,这些访问会导致缓存从文件读取块。
public void set_default_max_bytes_in_cache(
int n_bytes )
设置每个体积允许在缓存中使用的默认最大字节数。由于有更大的机会在缓存中找到特定体素,因此较高的数字可能会提供更快的访问速度,因此此值应尽可能大,前提是考虑到可用的虚拟内存量。如果未调用此函数,则默认值为 80 兆字节,或环境变量VOLUME_CACHE_SIZE
的值(如果存在)。设置值为 0 将导致缓存的体积在其缓存中只有一个块。此函数只影响之后创建的体积。要更改特定体积的此值,可以使用以下函数
public void set_volume_cache_size(
Volume volume,
int max_memory_bytes )
设置特定体积允许在缓存中使用的最大字节数。调用此函数将刷新缓存,以便重新分配数据结构以反映新的大小。与调用函数set_volume_cache_block_sizes
一样,访问像素时可能会出现暂时的速度损失。
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 )
当修改缓存的体积时,将创建一个临时文件来保存体素值。当删除体积时,此文件也将被删除。当输出缓存的体积时,临时文件将被复制到输出文件,这会导致同时存在体积的两个副本,这可能会对磁盘存储造成不可接受的需求,尤其是对于大型体积而言。为避免这种情况,应用程序可以指定一个文件名来放置体素值,这将覆盖使用临时文件。当删除体积时,此文件将被关闭并保留在磁盘上,从而无需应用程序输出体积。
如果应用程序随后调用output_volume()
将此体积输出到与由此函数设置的文件名相同的文件,则输出请求将被忽略,因为该体积已经存在于此文件中。基本上,程序员应使用与最终调用output_volume()
时传递的参数相同的参数调用set_cache_output_volume_parameters()
。如果体积是缓存的体积,则输出文件将在设置体积体素时立即创建,并且随后对output_volume()
的调用不会重新创建文件,而只是将缓存缓冲区刷新,使文件完全更新。如果体积不是缓存的体积,则对set_cache_output_volume_parameters()
的调用将被忽略,而随后对output_volume()
的调用将按照指定的方式创建文件。请注意,在调用此函数时,重要的是在退出程序之前删除体积,以便缓存的体积可以刷新其缓冲区并关闭文件,否则,对体积的最新更改将不会写入文件。
这里介绍了读取、写入和操作体积的示例。
一个完整的程序,用于读取 MINC 体积,将所有大于 100.0 的值更改为 100.0,然后将结果写入一个新文件。
#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 );
}
之前描述的input_volume
函数旨在为大多数应用程序提供一个简单的单函数接口,用于读取体积。但是,应用程序有时可能不想一次读取整个体积,或者可能需要将输入文件分解成多个体积,例如,3D 体积的切片。可以使用以下函数来实现这一点。
public Minc_file initialize_minc_input(
STRING filename,
Volume volume,
minc_input_options *options )
此函数打开一个 MINC 文件以输入到指定的体积。文件中的维度数量必须大于或等于体积中的维度数量,并且体积中的每个维度名称都必须在文件中具有匹配的维度名称。将返回一个文件句柄,用于以下例程中。
public Minc_file initialize_minc_input_from_minc_id(
int minc_id,
Volume volume,
minc_input_options *options )
此函数执行与initialize_minc_input
相同的任务,只是它接受一个先前打开的 MINC 文件作为参数,而不是文件名。
public int get_minc_file_id(
Minc_file file )
返回文件的 MINC ID,该句柄可用于对文件执行 MINC 函数。
public int get_n_input_volumes(
Minc_file file )
确定打开的 MINC 文件中的体积总数。例如,如果文件包含大小为 100 x 200 x 300 的 x、y、z 数据,并且该文件已使用二维 x-z 体积打开,则返回的体积数将为 200,即 y 切片的数量。
public BOOLEAN input_more_minc_file(
Minc_file file,
Real *fraction_done )
将 MINC 文件的一部分读取到initialize_minc_input
中指定的体积。必须调用此函数,直到它返回 FALSE,此时体积已完全读入。要读取文件中下一个体积(如果有),必须调用以下函数。
public BOOLEAN advance_input_volume(
Minc_file file )
将输入文件推进到下一个体积。
public BOOLEAN reset_input_volume(
Minc_file file )
将输入重置为文件中的第一个体积的开头。
public Status close_minc_input(
Minc_file file )
关闭 MINC 文件。
与多体积输入类似,还有一些例程用于通过子体积将输出提供给文件。
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 )
打开一个 MINC 文件以供创建。参数指定每个维度的数量和名称,以及每个维度的尺寸。文件的类型由四个参数控制:file_nc_data_type
、file_signed_flag
、file_voxel_min
和 file_voxel_max
。到世界坐标的变换由voxel_to_world_transform
参数指定。volume
附加到文件以供输出。体积的维度不能超过文件中的维度,并且每个维度的名称必须与文件中的一个维度匹配。options
与output_volume
函数中指定的一致。请注意,与output_volume
函数不同,如果未指定图像最小值和最大值,则输出文件将为每个图像具有单独的图像最小值和最大值。
public int get_minc_file_id(
Minc_file file )
返回文件的 MINC ID,该句柄可用于对文件执行 MINC 函数。
public Status copy_auxiliary_data_from_minc_file(
Minc_file file,
STRING filename,
STRING history_string )
将filename
参数中指定的 MINC 文件中的辅助信息复制到打开的 MINC file
中。
public Status output_minc_volume(
Minc_file file )
将附加的体积输出到当前文件位置的 MINC 文件。文件位置将根据体积的大小前进。
public Status output_volume_to_minc_file_position(
Minc_file file,
Volume volume,
int volume_count[],
long file_start[] )
更通用的例程,用于将指定的体积输出到指定位置的 MINC 文件。由于此例程不会更新文件位置,因此它不会干扰使用函数output_minc_volume
进行的正常体积输出。由volume_count
定义的体积超立方体将从给定的文件位置开始写入文件。
public Status close_minc_output(
Minc_file file )
关闭 MINC 文件。
标签点是三维点集,用于标记感兴趣的位置。通常,这些点来自手动定位多个体积中的对应位置,以提供这些体积之间的映射。标签点存储在麦康奈尔脑成像中心设计的 ASCII 格式中,文件名约定以.tag
结尾。一个标签文件可以包含单个标签点集或两个标签点集,具体取决于文件是否对应一个或两个体积。每个标签点包含三个坐标:x、y 和 z。每一组一个或两个标签点还存储着一些额外的可选信息。一个标签点集可能包含也可能不包含一组三个值:一个实数值权重、一个整型结构 ID 和一个整型患者 ID。一个标签点可能包含也可能不包含一个标签字符串。本文档描述了读取和写入此格式文件的函数。
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[] )
这两个函数从文件中读取一组标签点。第一个形式假设文件已经打开,并将由调用函数关闭,而第二个形式打开并关闭由文件名指定的,扩展名为.tag
的文件。每个集合中的体积数(每个集合中标签的数量,目前为一个或两个)在n_volumes
参数中返回。标签点数在n_tag_points
参数中返回。每个集合中第一个标签点的三维坐标在tags_volume1
参数中返回。如果体积数为两个,则每个集合中的第二个标签点在tags_volume2
参数中返回。最后四个参数返回与每个标签点集相关的辅助信息。如果调用程序对这四个数据中的任何一个都不感兴趣,则可以传入一个NULL
指针,文件中的值将不会被返回。
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[] )
完成标签点列表的处理后,可以通过调用此函数释放相关的内存。
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 )
这两个例程提供了一种更节省内存的方法来输入标签点。打开文件后,调用第一个例程来初始化标签的输入。接下来,重复调用第二个例程,直到它返回 FALSE,每次读取一个标签。
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[] )
这两个函数将标签点列表写入标签点文件。第一个形式假设文件已经打开,并将在稍后由调用函数关闭,而第二个形式打开并关闭文件,扩展名为.tag
。comments
参数是任何任意字符串,用于记录此文件的内容。体积数(n_volumes
)必须为一个或两个。标签点数由n_tag_points
参数指定。一组或两组标签点的坐标分别在tags_volume1
和tags_volume2
参数中。如果weights
、structure_ids
或patient_ids
三个参数中的任何一个被指定为NULL
,则这三部分信息都不会写入文件。同样,如果labels
参数为NULL
,则不会写入任何标签。
public STRING get_default_tag_file_suffix()
返回一个指向字符串的指针,该字符串包含标签点文件的默认后缀,目前为``tag
。此指针不应该被释放,其内容也不应该被修改。
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 )
这两个例程提供了一种更节省内存的方法来输出标签点。打开文件后,调用第一个例程来初始化标签的输出。接下来,重复调用第二个例程来每次输出一个标签点。第三个例程被调用以指示标签文件的结束。
以下是一个读取标签体积、删除所有 x 坐标为负的标签、忽略每个集合中的第二个标签点(如果存在)并将结果写入新文件的示例。
#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 );
}
在处理诸如跨模态和跨受试者配准等任务时,不同坐标系之间的变换概念就出现了。提供了一个模块来处理线性(仿射)和非线性变换,并在标准化的脑成像中心格式中提供输入和输出,通常用于扩展名为.xfm
的文件名。
为了支持这些功能,提供了两种结构类型。第一个(Transform
)是一个四乘四的线性(仿射)变换,它可以促进刚性变换,包括缩放、旋转、平移和剪切。第二个是更高层次的变换(General_transform
),它表示线性变换、非线性变换(薄板样条)、平滑插值的网格变换、用户定义的变换或这些变换的级联。
线性变换函数都处理类型为Transform
的对象。
#define Transform_elem( transform, i, j )
用于设置或检索变换的第i
行和第j
列的值,其中<function>0<=i, j<=3</function>
public void make_identity_transform(
Transform *transform )
创建一个四乘四的单位变换。
public void transform_point(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
通过给定的变换变换三维点,并返回三个变换后的坐标。
public void transform_vector(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
通过给定的变换变换三维向量,并返回三个变换后的坐标。变换点和变换向量之间的唯一区别在于,变换向量不涉及变换的平移部分。
public void inverse_transform_point(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
假设变换是正交变换(没有剪切分量),则通过变换的逆变换变换点。
public void inverse_transform_vector(
Transform *transform,
Real x,
Real y,
Real z,
Real *x_trans,
Real *y_trans,
Real *z_trans )
假设变换是正交变换(没有剪切分量),则通过变换的逆变换变换向量。
public void concat_transforms(
Transform *result,
Transform *t1,
Transform *t2 )
将变换t2
乘以变换t1
,并将乘积存储在result
中。通过result
变换一个点等效于通过t1
变换该点,然后通过t2
变换结果。
一般变换可以表示线性变换、薄板样条变换、网格变换和用户定义的变换,以及这些变换的级联。所有处理一般变换的函数都涉及类型为General_transform
的对象。
public void create_linear_transform(
General_transform *transform,
Transform *linear_transform )
创建一个由单个线性变换组成的一般变换,该线性变换由linear_transform
指定。如果传入一个NULL
作为linear_transform
参数,则创建一个单位变换。
public void create_thin_plate_transform(
General_transform *transform,
int n_dimensions,
int n_points,
float **points,
float **displacements )
创建一个由薄板样条组成的一般变换,它提供了多维空间的平滑非线性映射。points
参数是一个大小为n_points
乘以n_dimensions
的数组,表示一组点。displacements
是一个(n_points
+ n_dimensions
+ 1)乘以n_dimensions
的数组,它是由get_nonlinear_warp()
函数创建的,该函数不包含在这个库中。
public void create_grid_transform(
General_transform *transform,
Volume displacement_volume )
创建一个由网格变换组成的一般变换,它提供了多维空间的平滑非线性映射。displacment_volume
参数是一个四维体积,表示 x、y 和 z 方向的位移。体积的三个维度必须对应于 x、y 和 z 空间维度,第四个维度必须恰好有三个分量,即世界空间中每个方向的位移。维度可以以任何顺序排列。
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 )
通过复制用户数据(从user_data
开始的size_user_data
字节的数据)来创建一个用户定义的变换。还需要两个函数指针,用来指定变换点和逆变换点的方法。这些函数的类型为User_transform_function
,上面已指定。
public void delete_general_transform(
General_transform *transform )
释放一般变换结构中存储的内存。
public Transform_types get_transform_type(
General_transform *transform )
返回一般变换类型,可以是LINEAR
、THIN_PLATE_SPLINE
、USER_TRANSFORM
或CONCATENATED_TRANSFORM
之一。
public Transform *get_linear_transform_ptr(
General_transform *transform )
如果一般变换的类型为LINEAR
,则返回指向线性变换(类型为Transform
)的指针,以便与前面描述的特定于线性变换的例程一起使用。否则,打印错误消息。
public void concat_general_transforms(
General_transform *first,
General_transform *second,
General_transform *result )
级联两个一般变换。如果这两个变换都是类型LINEAR
,则结果也是这种类型,是两个矩阵的乘积。否则,结果变换只是两个变换的级联。
public int get_n_concated_transforms(
General_transform *transform )
返回给定变换中级联变换的数量。如果变换的类型为CONCATENATED_TRANSFORM
,则返回的数量是变换的数量,否则为 1。
public General_transform *get_nth_general_transform(
General_transform *transform,
int n )
如果变换的类型为CONCATENATED_TRANSFORM
,则返回指向第n
个变换的指针,其中n
大于等于零且小于级联变换中的变换数量。
public void general_transform_point(
General_transform *transform,
Real x,
Real y,
Real z,
Real *x_transformed,
Real *y_transformed,
Real *z_transformed )
通过一般变换变换三维点,并将结果传递回最后三个参数。
public void general_inverse_transform_point(
General_transform *transform,
Real x,
Real y,
Real z,
Real *x_transformed,
Real *y_transformed,
Real *z_transformed )
通过一般变换的逆变换变换三维点,并将结果传递回最后三个参数。
public void copy_general_transform(
General_transform *transform,
General_transform *copy )
创建一般变换的副本,根据需要在结构中分配内存。
public void create_inverse_general_transform(
General_transform *transform,
General_transform *inverse )
创建一个新的一般变换,它是给定变换的逆变换。
public void invert_general_transform(
General_transform *transform )
更改变换使其成为其逆变换。在同一个变换上调用它两次等同于根本不调用该函数。
一般变换存储在以麦康奈尔脑成像中心设计的 ASCII 格式的文件中,通常具有 .xfm
的文件名扩展名。输入和输出函数是
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 )
这两个函数将一般变换写入文件,以适当的格式。comments
行是一个任意字符串,它存储在文件中以供文档目的。comments
中的换行符会正确地导致多行注释,并在每行开头插入注释字符。第一种形式打开文件,使用默认扩展名 .xfm
,写入变换,然后关闭文件。函数的第二种形式假定文件已经打开,稍后将关闭。由于变换可能包含指向定义变换的 MINC 文件的指针,因此必须将文件的名称传递给此函数,以用于创建 MINC 文件的名称。volume_count_ptr 必须指向一个已初始化的整数。每次创建变换的辅助 MINC 文件时,该整数都会用于创建唯一的文件名,然后 volume_count_ptr 会递增。这两个函数都返回 ERROR
或 OK
。
public Status input_transform(
FILE *file,
STRING filename,
General_transform *transform )
public Status input_transform_file(
STRING filename,
General_transform *transform )
从文件输入一般变换。第一种形式假定文件已经打开以供输入,并且稍后将关闭。第二种形式打开文件以供输入,使用默认扩展名 .xfm
,读取变换,然后关闭文件。这两个函数都返回 ERROR
或 OK
。
public STRING get_default_transform_file_suffix()
返回指向字符串的指针,该字符串包含变换文件的默认后缀,目前为 ``xfm
。此指针不应释放或修改其内容。
这是一个示例,它试图说明一个典型的处理任务,包括使用体积、标签点和变换。这是一个完整的程序,当与 BIC 体积 IO 库链接时,它会读取 MINC 格式的两个体积,从文件读取一组标签点,以及从文件读取变换。假定标签点位于第一个体积的世界空间中,并且假定变换会将第一个体积的世界空间中的点变换到第二个体积的世界空间中。该程序使用输入的变换变换每个标签点(大概是从第一个体积的世界空间变换到第二个体积的世界空间),然后将结果变换到第二个体积的体素空间。如果体素坐标在第二个体积内,则打印相应体素的值。
#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 );
}