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
指针来更正。
类型和宏
[edit | edit source]为与 BIC Volume 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
替换为参数string
和str2
的连接。
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 目录。任何美元符号$
的出现,都会导致接下来的文本(直到下一个斜杠)扩展到由文本指定的環境變數。通过在任何~
或$
之前放置一个反斜杠 \,可以避免这种扩展。在以下所有接受文件名的函数中,都会自动执行文件名扩展。
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 格式表示的无符号字符数组。
内存分配
[edit | edit source]提供了一组宏,允许轻松分配和释放内存,最多支持 5 维数组。还会执行内存分配检查,以捕获错误,例如释放未分配的内存。此外,内存分配会自动跟踪所有分配的内存,因此可以检测内存泄漏(孤立内存)。
基本内存分配
[edit | edit source]基本宏如下所示
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
中。在二维情况下,这仅通过 2 次内存分配来实现,一次是为存储分配 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 )
如果启用了内存检查,则可以使用此函数来检测内存泄漏。在程序结束时,程序员应该释放所有已知的内存,然后调用此函数。任何剩余的已分配内存都会被打印到一个文件中,指示分配内存的文件和行号。
更高级别的数组分配
[edit | edit source]除了前面介绍的基本内存分配宏之外,还提供了一组用于处理动态更改大小的数组的有用宏
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
'th 索引处,然后递增 n_elems
。参数 chunk_size
指定内存分配的粒度。
DELETE_ELEMENT_FROM_ARRAY( array, n_elems, index_to_remove,
chunk_size )
从数组中删除 index_to_remove
'th 元素,减少数组中的元素数(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
变量将增加此值。此使用的不同之处在于,元素数 (n_elems
) 可以任意减少,而不会导致内存被释放。
进度报告
[edit | edit source]为了简化对特定处理任务的进度的监控,提供了一个进度报告模块。在任务执行过程中,进度报告会在行中打印点,指示任务完成的进度。如果任务将花费很长时间(超过 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 )
终止进度报告。
文本输出
[edit | edit source]与使用标准的 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 字节类型中,其值从零到 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
为零到 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 文件的唯一方法是使用此函数。
创建和分配卷后,有很多函数可用于操作卷。请注意,与每个卷关联的是一个有效的体素范围,该范围指示实际存储在卷中的值范围,例如,零到 200 是无符号字节卷的一个可能的范围。还有一个第二个范围,即real
范围,它将有效体素范围映射到任意实数范围,例如,有效体素的零到 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 )
给定一个卷和一个实数值体素索引,传递回相应的 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[] )
将 world 坐标转换为体素。为了将这些体素坐标用作整数索引,例如,作为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 )
转换假定为表面法线的向量到 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
是一个二维数组,大小为值的个数乘以插值维度的个数。second_deriv
是一个三维数组,大小为值的个数乘以插值维度的个数乘以插值维度的个数。
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 )
创建体积并设置其大小后,此函数将分配体素的内存。请注意,体素值不会被初始化,用户必须用所需的值填充体积。
每个体积都与从 体素
空间到 世界
空间的转换相关联。有几种方法可以定义这种转换。最简单的方法是直接指定它。
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[] )
设置体素到世界转换的平移部分。指定一个体素坐标 (体素
),以及希望此体素映射到的真实世界位置 (world_space_voxel_maps_to
)。体素到世界转换将更新以提供此映射。
public void set_volume_direction_cosine(
Volume volume,
int dimension,
Real dir[] )
设置特定体素维度的真实世界轴。例如,如果 维度
为 1,而 方向
为 (0.0, 1.0, 1.0),那么体积的第二个维度上的体素将映射到真实世界轴 (0.0, 1.0, 1.0) 并归一化为单位长度,然后按体积的第二个维度上的间距进行缩放。
public void get_volume_direction_cosine(
Volume volume,
int dimension,
Real dir[] )
返回特定体素维度的真实世界轴。请注意,维度
必须是体积中的空间维度。
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 )
返回体积的坐标系类型的副本,用字符串名称表示。调用函数在完成操作后必须释放此字符串。
复制体积
[edit | edit source]创建体积的另一种方法是从现有体积中简单地复制整个体积定义。
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 )
创建体积的精确副本。
体积缓存
[edit | edit source]为了有效地处理太大而无法放入虚拟内存的体积,我们实现了一个简单的缓存方案。按需读写磁盘文件以提供大容量完全驻留在内存中的外观。前面描述的所有体积例程都透明地支持此功能,但也有一些例程用于配置体积缓存。
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()
将按指定创建文件。请注意,当调用此函数时,重要的是在退出程序之前删除体积,以便缓存的体积刷新其缓冲区并关闭文件,否则,对体积的最新更改将不会写入文件。
体积源代码示例
[edit | edit source]这里提供了一些读取、写入和操作体积的示例。
一个完整的程序,用于读取 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 );
}
多体积输入
[edit | edit source]前面描述的 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
,则返回的数量是变换的数量,否则为一。
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 Volume 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 );
}