跳转到内容

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

这两个库 mincnetcdf 通常位于 /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 库一起使用定义了多种类型和宏。库中的所有函数声明都以publicprivate开头,这表明该函数是否可以从其所在的外部文件访问。库的用户只对那些以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 )

返回ab之间的插值,其中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循环,其中istart开始,递增直到它大于或等于end

等同于for( i = start; i < end; ++i )

  for_inclusive( i, start, end )

执行一个for循环,其中istart开始,递增直到它大于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替换为参数stringstr2的连接。

  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_FILEREAD_FILE之一,而file_format必须是ASCII_FORMATBINARY_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 )

从文件中输入一个字符串,以引号分隔,返回OKERROR。在成功读取字符串后,文件中的下一个可用字符将是紧接在结束引号之后的字符。

  public  Status  input_possibly_quoted_string(
      FILE            *file,
      STRING          *str )

从文件中输入一个字符串,以引号或空格分隔,返回OKERROR。在成功读取字符串后,文件中的下一个可用字符将是紧接在结束引号或最后一个非空格字符之后的字符。

  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。返回 OKERROR

  public  Status  output_character(
      FILE   *file,
      char   ch )

向文件输出一个字符,返回 OKERROR

  public  Status  output_string(
      FILE    *file,
      STRING  str )

将指定的字符串输出到文件,返回 OKERROR

  public  Status  output_quoted_string(
      FILE            *file,
      STRING          str )

输出一个引号、指定的字符串和一个闭合引号。

  public  Status  output_newline(
      FILE            *file )

输出一个换行符,返回 OKERROR

  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。返回 OKERROR

  public  Status  io_binary_data(
      FILE            *file,
      IO_types        io_flag,
      void            *data,
      size_t          element_size,
      int             n )

根据参数 io_flagREAD_FILE 还是 WRITE_FILE,输入或输出指定的二进制数据。

  public  Status  io_newline(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format )

根据参数 io_flagREAD_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_flagREAD_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 窗口。主要变化是使用名为 printprint_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指定体积在体积变量中存储的顺序。对于存储的体积中的每个维,都有一个对应的名称,它是MIxspaceMIyspaceMIzspaceANY_SPATIAL_DIMENSION或空字符串之一。这些与文件中的对应维匹配,体积数组的维顺序在输入时重新排序。因此,如果用户希望以 X-Z-Y 顺序表示体积,则作为dim_names数组传递的值应该是三个字符串MIxspaceMIzspaceMIyspace。这种排序的选择很重要,因为它定义了任何后续对体素索引引用的顺序。

四个参数volume_nc_data_typevolume_voxel_max可用于指定体积变量中所需的存储类型,自动从文件中的存储类型转换。volume_nc_data_typeMI_ORIGINAL_TYPENC_BYTENC_SHORTNC_LONGNC_FLOATNC_DOUBLE之一。对于整数类型,如果需要有符号类型,则volume_signed_flagTRUE,否则为FALSEvolume_voxel_minvolume_voxel_max指定有效体素值的范围,通常设置为相等以指示使用类型的整个范围,例如对于无符号NC_BYTE为零到 255.0。如果传递MI_ORIGINAL_TYPE,则使用文件中的类型、符号和体素范围。

如果create_volume_flagTRUE,即通常情况,则体积将自动创建。否则,假设体积已经存在,并且只有当它当前的大小与文件导致的新大小不同时才重新创建,从而在读取多个文件时减少内存分配量。

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_typeMI_ORIGINAL_TYPE,则体积将以与体积变量中相同的类型存储在 MINC 文件中。否则,四个参数file_nc_data_typefile_signed_flagfile_voxel_minfile_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设置为三个字符串MIzspaceMIyspaceMIxspace

  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_BYTEsigned_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_NATIVEMI_TALAIRACHMI_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_typesigned_flagvoxel_minvoxel_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_typefile_signed_flagfile_voxel_minfile_voxel_max。转换到世界坐标由 voxel_to_world_transform 参数指定。volume 附加到文件用于输出。体积的维度不能多于文件,并且每个维度的名称必须与文件中的一个维度匹配。optionsoutput_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[] )

这两个函数将标签点列表写入标签点文件。第一种形式假设文件已打开,并将由调用函数稍后关闭,而第二种形式打开并关闭文件,默认扩展名为 .tagcomments 参数是任何任意字符串,用于记录此文件的内容。卷数 (n_volumes) 必须为一或二。标签点数由 n_tag_points 参数指定。一组或两组标签点的坐标位于 tags_volume1tags_volume2 参数中。如果任何三个参数 weightsstructure_idspatient_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 )

返回一般变换类型,为 LINEARTHIN_PLATE_SPLINEUSER_TRANSFORMCONCATENATED_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 会递增。两个函数都返回ERROROK

  public  Status  input_transform(
      FILE                *file,
      STRING              filename,
      General_transform   *transform )
  public  Status  input_transform_file(
      STRING              filename,
      General_transform   *transform )

从文件中输入通用变换。第一种形式假设文件已打开以供输入,并将稍后关闭。第二种形式打开文件以供输入,默认扩展名为.xfm,读取变换,然后关闭文件。两个函数都返回ERROROK

  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 );
  }
华夏公益教科书