跳轉到內容

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 指標來更正上面的錯誤示例。

型別和宏

[編輯 | 編輯原始碼]

為與 BIC 體積 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 )

ninj 列二維陣列的索引 [i, j] 轉換為單個索引,基於行優先順序。

  IJK( i, j, k, nj, nk )

ninjnk 層三維陣列的索引 [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 )

用引數 stringstr2 的連線替換引數 string

  public  BOOLEAN  is_lower_case(
      char  ch )

如果字元是小寫字母,則返回 TRUE

  public  BOOLEAN  is_upper_case(
      char  ch )

如果字元是大寫字母,則返回 TRUE

  public  char  get_lower_case(
      char   ch )

如果字元是大寫字母,則返回字元的小寫版本,否則返回字元本身。

  public  char  get_upper_case(
      char   ch )

如果字元是小寫字母,則返回字元的大寫版本,否則返回字元本身。

  public  BOOLEAN  string_ends_in(
      STRING   string,
      STRING   ending )

確定字串是否以指定的 ending 結尾。例如,傳遞引數 "rainfall""fall" 會返回 TRUE

  public    STRING   strip_outer_blanks(
      STRING  str )

返回一個字串,它是引數 str,去掉了所有前導和尾隨空格。

  public  void  make_string_upper_case(
      STRING    string )

將字串轉換為全大寫字串。在原地修改字串。

  public  int  find_character(
      STRING  string,
      char    ch )

在給定的 string 中搜索給定的字元,返回找到它的索引,如果未找到,則返回 -1。

  public  BOOLEAN  blank_string(
      STRING   string )

如果字串為空或僅包含空格、製表符和換行符,則返回 true。

通用檔案 I/O

[edit | edit source]

雖然可以使用標準的 UNIX 檔案介面(例如 fprintf),但 BIC Volume IO 庫包含一組例程,用於執行所有檔案操作,並且有可能移植到其他作業系統。還有一些其他的優點,包括自動擴充套件 <function>~ </function> 和環境變數。此外,還提供了壓縮檔案的自動解壓縮功能。

  public  STRING  expand_filename(
      STRING  filename )

在引數 filename 中搜索某些模式,並相應地擴充套件它們,返回擴充套件後的檔名。任何以 ~ 開始,但不包括斜槓 / 的字元序列將被更改為指定的使用者的 home 目錄。任何美元符號 $ 的出現都會導致後面的文字(直到下一個斜槓)被擴充套件為由文字指定的 environment 變數。可以透過在任何 ~ $ 之前放置一個反斜槓 \ 來避免這種擴充套件。在所有以下使用檔名的函式中,都會自動執行檔名擴充套件。

  public  BOOLEAN  file_exists(
      STRING        filename )

如果檔案存在,則返回 TRUE

  public  BOOLEAN  check_clobber_file(
      STRING   filename )
  public  BOOLEAN  check_clobber_file_default_suffix(
      STRING   filename,
      STRING   default_suffix )

檢查檔案是否存在,如果存在,則提示使用者是否可以覆蓋它。如果檔案不存在,或如果檔案存在,使用者對提示輸入 `y',則返回 TRUE。該函式的第二種形式在檔名後面追加一個預設字尾,如果檔名還沒有後綴的話。

  public  void  remove_file(
      STRING  filename )

刪除指定的檔案,這將導致該檔案在所有對它的引用關閉後才會被刪除。

  public  BOOLEAN  filename_extension_matches(
      STRING   filename,
      STRING   extension )

如果檔案以給定的副檔名結尾(前面有一個點),則返回 TRUE。例如,<function>filename_extension_matches( "volume.mnc" , "mnc" )</function> 返回 TRUE。正確處理壓縮檔案,例如,傳遞引數 "volume.mnc.Z""mnc" 返回 TRUE

  public  STRING  remove_directories_from_filename(
      STRING  filename )

從檔名中剝離目錄,返回結果。

  public  STRING  extract_directory(
      STRING    filename )

從檔名中提取目錄,返回結果。

  public  STRING  get_absolute_filename(
      STRING    filename,
      STRING    directory )

給定一個檔名和當前目錄,返回一個絕對檔名(以斜槓 / 開頭的檔名)。

  public  Status  open_file(
      STRING             filename,
      IO_types           io_type,
      File_formats       file_format,
      FILE               **file )
  public  Status  open_file_with_default_suffix(
      STRING             filename,
      STRING             default_suffix,
      IO_types           io_type,
      File_formats       file_format,
      FILE               **file )

函式 open_file() 開啟指定的檔案,其中 io_type 必須是 WRITE_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_FILEWRITE_FILE,輸入或輸出指定二進位制資料。

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

根據引數 io_flagREAD_FILEWRITE_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_FILEWRITE_FILE,輸入或輸出以引號分隔的字串。如果 format 引數為 BINARY_FORMAT,則輸入或輸出以整數開頭表示字串長度的字串。

  public  Status  io_boolean(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      BOOLEAN         *b )
  public  Status  io_short(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      short           *short_int )
  public  Status  io_unsigned_short(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      unsigned short  *unsigned_short )
  public  Status  io_unsigned_char(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      unsigned  char  *c )
  public  Status  io_int(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      int             *i )
  public  Status  io_real(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      Real            *r )
  public  Status  io_float(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      float           *f )
  public  Status  io_double(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      double          *d )

輸入或輸出指定型別的二進位制或 ASCII 值。

  public  Status  io_ints(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      int             n,
      int             *ints[] )

輸入或輸出二進位制或 ASCII 格式的整數陣列。

  public  Status  io_unsigned_chars(
      FILE            *file,
      IO_types        io_flag,
      File_formats    format,
      int             n,
      unsigned char   *unsigned_chars[] )

輸入或輸出二進位制或 ASCII 格式的無符號字元陣列。

記憶體分配

[編輯 | 編輯原始碼]

提供了一組宏,允許輕鬆分配和釋放記憶體,最多可分配 5 維陣列。還執行記憶體分配檢查,以捕獲錯誤,例如釋放未分配的記憶體。此外,記憶體分配會自動跟蹤所有分配的記憶體,以便檢測記憶體洩漏(孤立的記憶體)。

基本記憶體分配

[編輯 | 編輯原始碼]

基本宏如下所示

  ALLOC( ptr, n_items )

分配 n_items 個正確型別元素,並將結果分配給引數 ptr

  FREE( ptr )

釋放參數 ptr 指向的記憶體。

  REALLOC( ptr, n_items )

更改引數 ptr 指向的記憶體大小,使其為 n_items 個元素,在此過程中可能會更改 ptr 的值。

  ALLOC_VAR_SIZED_STRUCT( ptr, element_type, n_elements )

分配一個可變大小的結構,該結構必須具有特定形式。結構的最後一個元素必須是一個大小為 1 的陣列,該陣列將構成結構的可變大小部分。引數 element_type 必須是該最後一個元素的型別,引數 n_elements 是除了結構第一部分的記憶體之外,要為該陣列分配的所需元素數量。一個使用示例是

  {
              struct {  int   a;
                        float b;
                        char  data[1];
                     }                    *ptr;
  
              ALLOC_VAR_SIZED_STRUCT( ptr, char, 10 );
  
              ptr->a = 1;
              ptr->b = 2.5;
              ptr->data[0] = 'a';
              ptr->data[9] = 'i';
          }
  ALLOC2D( ptr, n1, n2 )
      ALLOC3D( ptr, n1, n2, n3 )
      ALLOC4D( ptr, n1, n2, n3, n4 )
      ALLOC5D( ptr, n1, n2, n3, n4, n5 )

分配一個大小為 n1n2 等的 2 到 5 維陣列,並將結果儲存在指定的指標 ptr 中。在二維情況下,這僅透過兩次記憶體分配來完成,一次分配 n1 乘以 n2 個元素用於儲存,另一次分配 n1 個指向第一個記憶體區域的指標。一般來說,每個維度都需要一次記憶體分配。

  FREE2D( ptr )
      FREE3D( ptr )
      FREE4D( ptr )
      FREE5D( ptr )

釋放與多維陣列關聯的記憶體。

  public  void  set_alloc_checking(
      BOOLEAN state )

啟用或停用分配檢查。它通常在程式開始時被呼叫,以提供對分配的雙重檢查。它在每次呼叫之前的分配宏時都會進行額外的分配。如果未呼叫此函式,則可以透過將環境變數 DEBUG_ALLOC 設定為任何內容來開啟分配檢查。

分配檢查檢測三種類型的記憶體程式設計錯誤:釋放未分配的指標,透過檢查從系統 malloc 例程返回的記憶體塊是否與現有記憶體塊重疊,有時檢測到系統記憶體是否已損壞,以及透過記錄所有分配的記憶體並打印出當前分配的記憶體,可以檢測記憶體洩漏。

  public  void  output_alloc_to_file(
      STRING   filename )

如果啟用了記憶體檢查,則可以使用此函式檢測記憶體洩漏。在程式結束時,程式設計師應該釋放所有已知的記憶體,然後呼叫此函式。任何剩餘的已分配記憶體將被列印到檔案中,指示分配記憶體的檔案和行號。

高階陣列分配

[編輯 | 編輯原始碼]

除了前面描述的基本記憶體分配宏之外,還提供了一組用於處理動態更改大小的陣列的有用宏

  SET_ARRAY_SIZE( array, previous_n_elems, new_n_elems,
                      chunk_size )

此宏透過指定先前分配給陣列的元素數量(從零開始)來增加或減少陣列的大小。chunk_size 引數定義分配的記憶體塊的大小。例如,如果 chunk_size 為 100,則此宏僅在大小更改跨越到 100 的不同倍數時才會重新分配陣列,從而避免每次呼叫時都進行記憶體分配。此記憶體分配粒度規範必須始終一致地指定;如果此宏使用給定變數和塊大小被呼叫,則隨後使用相同變數呼叫此宏必須指定相同的塊大小。另請注意,作為 new_n_elems 傳入的數字必須在下次呼叫此宏時作為 previous_n_elems 傳入。

  ADD_ELEMENT_TO_ARRAY( array, n_elems,
                            elem_to_add, chunk_size )

將引數 elem_to_add 新增到陣列的 n_elems 個索引處,然後遞增 n_elems。引數 chunk_size 指定記憶體分配的粒度。

  DELETE_ELEMENT_FROM_ARRAY( array, n_elems, index_to_remove,
                                 chunk_size )

從陣列中刪除 index_to_remove 個元素,減少陣列中的元素數量(n_elems)並減少記憶體分配,如果跨越 chunk_size 的倍數。同樣,chunk_size 必須在涉及給定指標的前面三個宏的所有呼叫中指定為相同。

  ADD_ELEMENT_TO_ARRAY_WITH_SIZE( array, n_alloced, n_elems,
                                      elem_to_add, chunk_size )

向陣列新增一個元素(elem_to_add),遞增 n_elems。如果需要,記憶體將按 chunk_size 中指定的數量增加,並且 n_alloced 變數將按此數量遞增。此用法與 ADD_ELEMENT_TO_ARRAY 的用法不同,因為元素數量(n_elems)可以任意減少,而不會導致記憶體被釋放。

進度報告

[編輯 | 編輯原始碼]

為了提供對特定處理任務進度的簡單監控,可以使用一個進度報告模組。當任務正在進行時,進度報告會在行上列印點,表示任務完成的進度。如果任務將花費很長時間(超過 2 分鐘),進度報告會定期列印當前完成的百分比和估計的剩餘時間。一個使用示例,後面跟著函式描述,如下所示

  {
      int                 i, n_slices;
      progress_struct     progress;
  
      n_slices = 100;
  
      initialize_progress_report( &progress, FALSE,
                  n_slices, "Processing Slices" );
  
      for( i = 0;  i < n_slices;  ++i )
      {
          process_slice( i );
  
          update_progress_report( &progress, i + 1 );
      }
  
      terminate_progress_report( &progress );
  }
  public  void  initialize_progress_report(
      progress_struct   *progress,
      BOOLEAN           one_line_only,
      int               n_steps,
      STRING            title )

初始化進度報告結構,指定處理中將發生的步驟數量,以及要列印的進度報告標題。在進度報告期間,顯示屏將自動從列印一行點的短作業模式切換到定期列印完成百分比和估計剩餘時間的長作業模式。如果 one_line_only 標誌為 TRUE,則此功能將被停用,並且只會顯示一行點。

  public  void  update_progress_report(
      progress_struct   *progress,
      int               current_step )

告訴進度報告模組已完成多少個步驟,並導致更新行上點的顯示或估計剩餘時間。

  public  void  terminate_progress_report(
      progress_struct   *progress )

終止進度報告。

文字輸出

[編輯 | 編輯原始碼]

與使用標準 UNIX 函式 printf 進行例行文字輸出相比,提供了一個外觀相似的模組,該模組允許安裝任意列印輸出函式。例如,這可以用來將輸出映象到日誌檔案,或將文字傳送到 X 視窗。主要變化是使用名為 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位元組型別中,其值為0到255,但有一個實際值範圍,它提供了從體素值到任何任意實數範圍的對映。這樣,實際範圍可以是任何有效的實數區間,並且與特定儲存型別無關。

由於大多數體積將透過從MINC檔案讀取而建立,因此將首先介紹這種方法,然後描述如何從頭開始建立體積。最後,將介紹更高階應用程式的MINC輸入和輸出的較低級別。

體積輸入

[編輯 | 編輯原始碼]
  public  Status  input_volume(
      STRING               filename,
      int                  n_dimensions,
      STRING               dim_names[],
      nc_type              volume_nc_data_type,
      BOOLEAN              volume_signed_flag,
      Real                 volume_voxel_min,
      Real                 volume_voxel_max,
      BOOLEAN              create_volume_flag,
      Volume               *volume,
      minc_input_options   *options )

此例程從MINC檔案讀取一個體積,首先在create_volume_flag指定為TRUE(通常情況)的情況下建立體積。維度數量是體積所需的維度數量。如果此數量小於檔案中的維度數量,則只讀取與該維度數量相對應檔案的第一個部分。如果指定的維度數量小於1,則使用檔案中的維度數量。引數dim_names指定體積在體積變數中儲存的順序。對於儲存體積中的每個維度,都有一個相應的名稱,該名稱是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,範圍為0到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 檔案的唯一方法是使用此函式。

卷操作

[編輯 | 編輯原始碼]

建立和分配卷後,有很多函式用於操作卷。請注意,與每個卷相關聯的是一個有效的體素範圍,該範圍指示實際儲存在卷中的值的範圍,例如,0 到 200 是無符號位元組卷的一種可能的範圍。還有一個第二個範圍,即real範圍,它將有效體素範圍對映到任意實際範圍,例如,有效體素的 0 到 200 可以對映到real範圍內的 -1.5 到 1.5。在處理卷時,通常會對real範圍感興趣。

  public  Real  get_volume_voxel_value(
      Volume   volume,
      int      v0,
      int      v1,
      int      v2,
      int      v3,
      int      v4 )

給定一個卷和一個到五個體素索引(取決於卷的維數),返回相應的體素值。例如,如果卷是三維的,那麼最後兩個引數將被忽略。

  value = convert_voxel_to_value( volume, voxel );

給定一個卷和一個體素值,將它轉換為實際範圍內的值,並返回它。

  voxel = convert_value_to_voxel( volume, value )

給定一個卷和一個實際值,將它轉換為有效體素範圍內的體素值,並返回它。

  public  Real  get_volume_real_value(
      Volume   volume,
      int      v0,
      int      v1,
      int      v2,
      int      v3,
      int      v4 )

給定一個卷和一個到五個體素索引(取決於卷的維數),返回相應的實際值。例如,如果卷是三維的,那麼最後兩個引數將被忽略。

  public  void  set_volume_voxel_value(
      Volume   volume,
      int      v0,
      int      v1,
      int      v2,
      int      v3,
      int      v4,
      Real     voxel )

給定一個卷、一個到五個體素索引和一個voxel值,將此值分配給卷中相應的體素。請注意,不會執行從有效實際範圍到有效體素範圍的轉換,因此使用者可能需要首先使用convert_value_to_voxel函式或使用set_volume_real_value函式。

  public  void  set_volume_real_value(
      Volume   volume,
      int      v0,
      int      v1,
      int      v2,
      int      v3,
      int      v4,
      Real     value )

給定一個卷、一個到五個體素索引和一個value,在轉換為體素值後,將此值分配給卷中相應的體素。

  public  void  delete_volume(
      Volume   volume )

刪除與卷相關聯的所有記憶體。

  public  nc_type  get_volume_nc_data_type(
      Volume       volume,
      BOOLEAN      *signed_flag )

返回卷的儲存型別(例如,NC_SHORT),並傳遞迴一個指示它是有符號還是無符號的資訊。

  public  int  get_volume_n_dimensions(
      Volume   volume )

返回卷的維數。

  public  void  get_volume_sizes(
      Volume   volume,
      int      sizes[] )

將每個維度的尺寸儲存在陣列sizes中。這是每個維度的體素數。陣列sizes必須至少與卷的維數一樣大。

  public  void  get_volume_separations(
      Volume   volume,
      Real     separations[] )

將每個維度的切片間距儲存在陣列separations中。陣列separations必須至少與卷的維數一樣大。

  public  Real  get_volume_voxel_min(
      Volume   volume )
  public  Real  get_volume_voxel_max(
      Volume   volume )
  public  void  get_volume_voxel_range(
      Volume     volume,
      Real       *voxel_min,
      Real       *voxel_max )

前兩個函式返回允許的最小或最大體素值。第三個函式傳遞迴這兩個值。

  public  Real  get_volume_real_min(
      Volume     volume )
  public  Real  get_volume_real_max(
      Volume     volume )
  public  void  get_volume_real_range(
      Volume     volume,
      Real       *min_value,
      Real       *max_value )

前兩個函式返回最小或最大實際值。第三個函式傳遞迴這兩個值。對映到此實際空間將最小voxel值線性對映到最小real值,並將最大voxel值對映到最大real值。

  public  STRING  *get_volume_dimension_names(
      Volume   volume )

返回指向每個維度名稱列表的指標。此記憶體必須由呼叫函式使用以下函式釋放。

  public  void  delete_dimension_names(
      Volume   volume,
      STRING   dimension_names[] )

釋放為維度名稱分配的記憶體。

  public  void  set_rgb_volume_flag(
      Volume   volume,
      BOOLEAN  flag )

設定指示卷是否為紅-綠-藍顏色的卷標誌。只有資料型別為無符號長的卷才能是 rgb 卷。

  public  BOOLEAN  is_an_rgb_volume(
      Volume   volume )

如果卷是紅-綠-藍顏色卷,則返回TRUE,因為可以在卷輸入時建立此卷。

卷座標系

[編輯 | 編輯原始碼]

一個卷有兩個座標系。體素座標系只是卷的 n 維索引座標系。例如,體素座標 (0.0, 0.0, 0.0) 對應於三維卷中第一個體素的中心。體素座標 ( 99.0, 0.0, 0.0 ) 對應於大小為 ( 100, 200, 150 ) 的三維卷的第一個方向中的最後一個體素的中心。第二個座標系是任意的三維座標系,通常稱為世界座標系,通常是 Talairach 座標系。以下函式提供在這兩個座標系之間進行轉換的方法

  public  void  convert_voxel_to_world(
      Volume   volume,
      Real     voxel[],
      Real     *x_world,
      Real     *y_world,
      Real     *z_world )

給定一個卷和一個實值體素索引,傳遞迴相應的世界座標。

  public  void  convert_3D_voxel_to_world(
      Volume   volume,
      Real     voxel1,
      Real     voxel2,
      Real     voxel3,
      Real     *x_world,
      Real     *y_world,
      Real     *z_world )

convert_voxel_to_world相同,只是它僅適用於三維卷。

  public  void  convert_world_to_voxel(
      Volume   volume,
      Real     x_world,
      Real     y_world,
      Real     z_world,
      Real     voxel[] )

將世界座標轉換為體素。為了將這些體素座標用作整數索引(例如,作為GET_VALUE宏的引數),引數voxel的每個分量必須首先四捨五入到最接近的整數。

  public  void  convert_3D_world_to_voxel(
      Volume   volume,
      Real     x_world,
      Real     y_world,
      Real     z_world,
      Real     *voxel1,
      Real     *voxel2,
      Real     *voxel3 )

convert_world_to_voxel相同,只是它僅適用於三維卷。

  public  void  convert_voxel_vector_to_world(
      Volume          volume,
      Real            voxel_vector[],
      Real            *x_world,
      Real            *y_world,
      Real            *z_world )
  public  void  convert_world_vector_to_voxel(
      Volume          volume,
      Real            x_world,
      Real            y_world,
      Real            z_world,
      Real            voxel_vector[] )

這兩個函式在體素空間和世界空間之間轉換向量。這是透過將點 (0,0,0) 轉換為另一個空間來完成的,此外,將向量視為點並將其轉換為另一個空間,並將這兩個結果之間的向量傳遞迴。

  public  void  convert_voxel_normal_vector_to_world(
      Volume          volume,
      Real            voxel1,
      Real            voxel2,
      Real            voxel3,
      Real            *x_world,
      Real            *y_world,
      Real            *z_world )

將被假定為世界座標系中的表面法線的向量轉換。

卷插值

[編輯 | 編輯原始碼]

除了訪問特定體素值的例程之外,還可以使用最近鄰、線性或三次插值對捲進行插值,這些插值在體素空間或世界空間中指定。

  public  int   evaluate_volume(
      Volume         volume,
      Real           voxel[],
      BOOLEAN        interpolating_dimensions[],
      int            degrees_continuity,
      BOOLEAN        use_linear_at_edge,
      Real           outside_value,
      Real           values[],
      Real           **first_deriv,
      Real           ***second_deriv )

在指定的體素處對捲進行插值,其中degrees_continuity的值分別為 -1、0 或 2,表示最近鄰、線性或三次插值。如果use_linear_at_edge為真,則任何三次插值都將降級為卷邊緣附近的線性插值。此選項僅在特殊情況下需要。引數outside_value是卷外部任何點的值。引數interpolating_dimensions指示哪些維度正在進行插值,通常是NULL指標,表示所有維度都在進行插值。插值後的值將放置在 values 陣列中。值的個數等於未插值維度的值的個數,如果所有維度都在進行插值,則為 1。如果導數引數不為空,則生成的導數將放置在相應的位置。first_deriv是一個大小為 values 個數乘以插值維度個數的二維陣列。second_deriv是一個大小為 values 個數乘以插值維度個數乘以插值維度個數的三維陣列。

  public  void   evaluate_volume_in_world(
      Volume         volume,
      Real           x,
      Real           y,
      Real           z,
      int            degrees_continuity,
      BOOLEAN        use_linear_at_edge,
      Real           outside_value,
      Real           values[],
      Real           deriv_x[],
      Real           deriv_y[],
      Real           deriv_z[],
      Real           deriv_xx[],
      Real           deriv_xy[],
      Real           deriv_xz[],
      Real           deriv_yy[],
      Real           deriv_yz[],
      Real           deriv_zz[] )

在指定的世界位置處對捲進行插值,其中degrees_continuity的值分別為 -1、0 或 2,表示最近鄰、線性或三次插值。如果use_linear_at_edge為真,則任何三次插值都將降級為卷邊緣附近的線性插值。此選項僅在特殊情況下需要。引數outside_value是卷外部任何點的值。插值後的值將放置在 values 陣列中。值的個數等於非空間維度的值的個數,如果所有維度都是空間維度,則為 1。如果導數引數不為空,則生成的導數將放置在相應的位置。

  public  void  set_volume_interpolation_tolerance(
      Real   tolerance )

出於速度考慮,如果在體素中心或其附近評估卷,則不執行插值,而是返回體素值。容差定義了此操作發生的距離與體素中心之間的距離,預設值為 0。請注意,如果需要導數並且指定的連續性程度為 0 或更大,則即使在指定的體素容差範圍內,也會執行插值。

從頭開始建立卷

[編輯 | 編輯原始碼]

在某些情況下,希望以與從檔案讀取不同的方式建立卷。以下函式提供從頭開始建立卷或建立與現有卷相似的卷的方法。

  public   Volume   create_volume(
      int         n_dimensions,
      STRING      dimension_names[],
      nc_type     nc_data_type,
      BOOLEAN     signed_flag,
      Real        voxel_min,
      Real        voxel_max )

建立並返回給定型別(例如,NC_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 )

建立卷並設定其大小後,此函式將為體素分配記憶體。請注意,體素值不會被初始化,使用者必須用所需的值填充卷。

與每個卷相關聯的是從voxel空間到world空間的變換。有幾種方法可以定義這種變換。最簡單的方法是直接指定它

  public  void  set_voxel_to_world_transform(
      Volume             volume,
      General_transform  *transform )

將給定變換分配給卷。

  public  General_transform  *get_voxel_to_world_transform(
      Volume   volume )

返回指向卷的體素到世界變換的指標。

  public  void  set_volume_separations(
      Volume   volume,
      Real     separations[] )

設定每個卷維度的體素間距。請注意,這會導致體素到世界變換相應地更新。

  public  void  set_volume_translation(
      Volume  volume,
      Real    voxel[],
      Real    world_space_voxel_maps_to[] )

設定體素到世界變換的平移部分。指定一個體素座標(voxel),以及希望此體素對映到的實際世界位置(world_space_voxel_maps_to)。體素到世界變換將更新以提供此對映。

  public  void  set_volume_direction_cosine(
      Volume   volume,
      int      dimension,
      Real     dir[] )

設定特定體素維度的真實世界軸。例如,如果dimension為 1,而dir為 (0.0, 1.0, 1.0),則體積第二維度的體素將對映到真實世界軸 (0.0, 1.0, 1.0),該軸已歸一化為單位長度,然後按第二體積維度的體積間距進行縮放。

  public  void  get_volume_direction_cosine(
      Volume   volume,
      int      dimension,
      Real     dir[] )

返回特定體素維度的真實世界軸。請注意,dimension必須是體積中的空間維度。

  public  void  set_volume_space_name(
      Volume   volume,
      STRING   name )

使用字串名稱設定體積的座標系型別。這可以是任何字串,但 MINC 提供了一組定義的常量:MI_NATIVEMI_TALAIRACHMI_CALLOSAL

  public  STRING  get_volume_space_name(
      Volume   volume )

返回體積的座標系型別的副本,以字串名稱的形式表示。呼叫函式在完成使用後必須釋放此字串。

複製體積

[編輯 | 編輯原始碼]

建立體積的另一種方法是簡單地從現有體積中複製整個體積定義。

  public  Volume   copy_volume_definition(
      Volume   existing_volume,
      nc_type  nc_data_type,
      BOOLEAN  signed_flag,
      Real     voxel_min,
      Real     voxel_max )
  public  Volume   copy_volume_definition_no_alloc(
      Volume   volume,
      nc_type  nc_data_type,
      BOOLEAN  signed_flag,
      Real     voxel_min,
      Real     voxel_max )

這兩個函式都建立並返回一個新體積,該體積與現有體積具有相同的定義(大小、體素到世界空間變換等)。如果引數nc_data_type不是MI_ORIGINAL_TYPE,則新體積的儲存型別將與原始體積不同,並由nc_data_typesigned_flagvoxel_minvoxel_max 指定。在第一個函式中,體素值已分配但未初始化。在第二個函式中,它們未分配。

  public  Volume   copy_volume(
      Volume   volume )

建立體積的精確副本。

體積快取

[編輯 | 編輯原始碼]

為了有效地處理對虛擬記憶體而言過大的體積,已經實現了一個簡單的快取方案。磁碟檔案按需讀取和寫入,以提供將大型體積完全駐留在記憶體中的外觀。之前描述的所有體積例程都透明地支援此功能,但有一些例程用於配置體積快取。

  public  void  set_n_bytes_cache_threshold(
      int  threshold )

設定體積快取的閾值位元組數。如果建立的體積大於此值(無論顯式建立還是從檔案輸入建立),則該體積將在內部由快取方案表示。如果未呼叫此函式,則預設值為 80 兆位元組,或環境變數VOLUME_CACHE_THRESHOLD的值(如果存在)。閾值為零將導致所有體積被快取。閾值小於零將導致沒有體積被快取。

  typedef  enum  { SLICE_ACCESS, RANDOM_VOLUME_ACCESS }
                 Cache_block_size_hints;
  public  void  set_cache_block_sizes_hint(
      Cache_block_size_hints  hint )
  public  void  set_default_cache_block_sizes(
      int   block_sizes[] )

當在快取的體積中訪問體素時,將根據需要從磁碟讀取或寫入包含該體素的相應塊。這兩個函式控制塊的預設大小,因此控制快速訪問相鄰體素(使用較大的塊大小)和快速訪問任意分佈的體素(使用較小的塊大小)之間的權衡。函式set_cache_block_sizes_hint 表示未來建立的體積的預設塊大小應基於這樣的假設來計算,即應用程式將主要以逐片的方式訪問體積體素(SLICE_ACCESS)或以不可預測的順序訪問體素(RANDOM_VOLUME_ACCESS)。第二個函式set_default_cache_block_sizes 提供了一種替代方案,其中顯式設定了預設塊大小。對任一函式的呼叫都會覆蓋之前對另一個函式的任何呼叫。如果未呼叫這兩個函式中的任何一個,則預設值為每個維度的塊大小為 8 個體素,或環境變數VOLUME_CACHE_BLOCK_SIZE指定的值。這些函式隻影響之後建立的體積。要更改給定體積的值,可以使用以下函式

  public  void  set_volume_cache_block_sizes(
      Volume    volume,
      int       block_sizes[] )

設定給定體積的快取塊大小。由於此函式會導致快取完全重新整理,因此要更改塊大小,可能會在後續體素訪問過程中造成暫時速度損失,這些訪問會導致快取從檔案讀取塊。

  public  void  set_default_max_bytes_in_cache(
      int  n_bytes )

設定每個體積允許在快取中使用的預設最大位元組數。由於有更大的機會在快取中找到特定體素,因此較高的數字可能會提供更快的訪問速度,因此此值應儘可能大,前提是考慮到可用的虛擬記憶體量。如果未呼叫此函式,則預設值為 80 兆位元組,或環境變數VOLUME_CACHE_SIZE的值(如果存在)。設定值為 0 將導致快取的體積在其快取中只有一個塊。此函式隻影響之後建立的體積。要更改特定體積的此值,可以使用以下函式

  public  void  set_volume_cache_size(
      Volume    volume,
      int       max_memory_bytes )

設定特定體積允許在快取中使用的最大位元組數。呼叫此函式將重新整理快取,以便重新分配資料結構以反映新的大小。與呼叫函式set_volume_cache_block_sizes 一樣,訪問畫素時可能會出現暫時的速度損失。

  public  void  set_cache_output_volume_parameters(
      Volume                      volume,
      STRING                      filename,
      nc_type                     file_nc_data_type,
      BOOLEAN                     file_signed_flag,
      Real                        file_voxel_min,
      Real                        file_voxel_max,
      STRING                      original_filename,
      STRING                      history,
      minc_output_options         *options )

當修改快取的體積時,將建立一個臨時檔案來儲存體素值。當刪除體積時,此檔案也將被刪除。當輸出快取的體積時,臨時檔案將被複制到輸出檔案,這會導致同時存在體積的兩個副本,這可能會對磁碟儲存造成不可接受的需求,尤其是對於大型體積而言。為避免這種情況,應用程式可以指定一個檔名來放置體素值,這將覆蓋使用臨時檔案。當刪除體積時,此檔案將被關閉並保留在磁碟上,從而無需應用程式輸出體積。

如果應用程式隨後呼叫output_volume() 將此體積輸出到與由此函式設定的檔名相同的檔案,則輸出請求將被忽略,因為該體積已經存在於此檔案中。基本上,程式設計師應使用與最終呼叫output_volume() 時傳遞的引數相同的引數呼叫set_cache_output_volume_parameters()。如果體積是快取的體積,則輸出檔案將在設定體積體素時立即建立,並且隨後對output_volume() 的呼叫不會重新建立檔案,而只是將快取緩衝區重新整理,使檔案完全更新。如果體積不是快取的體積,則對set_cache_output_volume_parameters() 的呼叫將被忽略,而隨後對output_volume() 的呼叫將按照指定的方式建立檔案。請注意,在呼叫此函式時,重要的是在退出程式之前刪除體積,以便快取的體積可以重新整理其緩衝區並關閉檔案,否則,對體積的最新更改將不會寫入檔案。

體積原始碼示例

[編輯 | 編輯原始碼]

這裡介紹了讀取、寫入和操作體積的示例。

一個完整的程式,用於讀取 MINC 體積,將所有大於 100.0 的值更改為 100.0,然後將結果寫入一個新檔案。

  #include  <volume_io.h>
  /* ------------------------------------------------------------------
  @COPYRIGHT  :
                Copyright 1993,1994,1995 David MacDonald,
                McConnell Brain Imaging Centre,
                Montreal Neurological Institute, McGill University.
                Permission to use, copy, modify, and distribute this
                software and its documentation for any purpose and without
                fee is hereby granted, provided that the above copyright
                notice appear in all copies.  The author and
                McGill University make no representations about the
                suitability of this software for any purpose.  It is
                provided "as is" without express or implied warranty.
  ------------------------------------------------------------------ */
  int  main(
      int   argc,
      char  *argv[] )
  {
      int        v1, v2, v3, sizes[MAX_DIMENSIONS];
      Real       value;
      Volume     volume;
      /*--- input the volume */
      if( input_volume( "volume.mnc", 3, NULL, MI_ORIGINAL_TYPE, FALSE,
              0.0, 0.0, TRUE, &volume,
              (minc_input_options *) NULL ) != OK )
          return( 1 );
      get_volume_sizes( volume, sizes );
      /*--- change all values over 100 to 100 */
      for( v1 = 0;  v1 < sizes[0];  ++v1 ) {
          for( v2 = 0;  v2 < sizes[1];  ++v2 ) {
              for( v3 = 0;  v3 < sizes[2];  ++v3 ) {
                  value = get_volume_real_value( volume, v1, v2, v3,
                                                 0, 0 );
                  if( value > 100.0 ) {
                      set_volume_real_value( volume, v1, v2, v3,
                                             0, 0, 100.0 );
                  }
              }
          }
      }
      /*--- output the modified volume */
      if( output_modified_volume( "output.mnc", MI_ORIGINAL_TYPE,
               FALSE, 0.0, 0.0, volume, "volume.mnc",
               "Modified by clamping to 100",
               (minc_output_options *) NULL ) != OK )
          return( 1 );
      return( 0 );
  }

多體積輸入

[編輯 | 編輯原始碼]

之前描述的input_volume 函式旨在為大多數應用程式提供一個簡單的單函式介面,用於讀取體積。但是,應用程式有時可能不想一次讀取整個體積,或者可能需要將輸入檔案分解成多個體積,例如,3D 體積的切片。可以使用以下函式來實現這一點。

  public  Minc_file  initialize_minc_input(
      STRING               filename,
      Volume               volume,
      minc_input_options   *options )

此函式開啟一個 MINC 檔案以輸入到指定的體積。檔案中的維度數量必須大於或等於體積中的維度數量,並且體積中的每個維度名稱都必須在檔案中具有匹配的維度名稱。將返回一個檔案控制代碼,用於以下例程中。

  public  Minc_file  initialize_minc_input_from_minc_id(
      int                  minc_id,
      Volume               volume,
      minc_input_options   *options )

此函式執行與initialize_minc_input 相同的任務,只是它接受一個先前開啟的 MINC 檔案作為引數,而不是檔名。

  public  int  get_minc_file_id(
      Minc_file  file )

返回檔案的 MINC ID,該控制代碼可用於對檔案執行 MINC 函式。

  public  int  get_n_input_volumes(
      Minc_file  file )

確定開啟的 MINC 檔案中的體積總數。例如,如果檔案包含大小為 100 x 200 x 300 的 x、y、z 資料,並且該檔案已使用二維 x-z 體積開啟,則返回的體積數將為 200,即 y 切片的數量。

  public  BOOLEAN  input_more_minc_file(
      Minc_file   file,
      Real        *fraction_done )

將 MINC 檔案的一部分讀取到initialize_minc_input 中指定的體積。必須呼叫此函式,直到它返回 FALSE,此時體積已完全讀入。要讀取檔案中下一個體積(如果有),必須呼叫以下函式。

  public  BOOLEAN  advance_input_volume(
      Minc_file   file )

將輸入檔案推進到下一個體積。

  public  BOOLEAN  reset_input_volume(
      Minc_file   file )

將輸入重置為檔案中的第一個體積的開頭。

  public  Status  close_minc_input(
      Minc_file   file )

關閉 MINC 檔案。

多體積輸出

[編輯 | 編輯原始碼]

與多體積輸入類似,還有一些例程用於透過子體積將輸出提供給檔案。

  public  Minc_file  initialize_minc_output(
      STRING                 filename,
      int                    n_dimensions,
      STRING                 dim_names[],
      int                    sizes[],
      nc_type                file_nc_data_type,
      BOOLEAN                file_signed_flag,
      Real                   file_voxel_min,
      Real                   file_voxel_max,
      General_transform      *voxel_to_world_transform,
      Volume                 volume,
      minc_output_options    *options )

開啟一個 MINC 檔案以供建立。引數指定每個維度的數量和名稱,以及每個維度的尺寸。檔案的型別由四個引數控制:file_nc_data_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,則返回的數量是變換的數量,否則為 1。

  public  General_transform  *get_nth_general_transform(
      General_transform   *transform,
      int                 n )

如果變換的型別為CONCATENATED_TRANSFORM,則返回指向第n個變換的指標,其中n大於等於零且小於級聯變換中的變換數量。

使用一般變換

[編輯 | 編輯原始碼]
  public  void  general_transform_point(
      General_transform   *transform,
      Real                x,
      Real                y,
      Real                z,
      Real                *x_transformed,
      Real                *y_transformed,
      Real                *z_transformed )

透過一般變換變換三維點,並將結果傳遞迴最後三個引數。

  public  void  general_inverse_transform_point(
      General_transform   *transform,
      Real                x,
      Real                y,
      Real                z,
      Real                *x_transformed,
      Real                *y_transformed,
      Real                *z_transformed )

透過一般變換的逆變換變換三維點,並將結果傳遞迴最後三個引數。

  public  void  copy_general_transform(
      General_transform   *transform,
      General_transform   *copy )

建立一般變換的副本,根據需要在結構中分配記憶體。

  public  void  create_inverse_general_transform(
      General_transform   *transform,
      General_transform   *inverse )

建立一個新的一般變換,它是給定變換的逆變換。

  public  void  invert_general_transform(
      General_transform   *transform )

更改變換使其成為其逆變換。在同一個變換上呼叫它兩次等同於根本不呼叫該函式。

讀取和寫入一般變換

[編輯 | 編輯原始碼]

一般變換儲存在以麥康奈爾腦成像中心設計的 ASCII 格式的檔案中,通常具有 .xfm 的檔名副檔名。輸入和輸出函式是

  public  Status  output_transform_file(
      STRING              filename,
      STRING              comments,
      General_transform   *transform )
  public  Status  output_transform(
      FILE                *file,
      STRING              filename,
      int                 *volume_count_ptr,
      STRING              comments,
      General_transform   *transform )

這兩個函式將一般變換寫入檔案,以適當的格式。comments 行是一個任意字串,它儲存在檔案中以供文件目的。comments 中的換行符會正確地導致多行註釋,並在每行開頭插入註釋字元。第一種形式開啟檔案,使用預設副檔名 .xfm,寫入變換,然後關閉檔案。函式的第二種形式假定檔案已經開啟,稍後將關閉。由於變換可能包含指向定義變換的 MINC 檔案的指標,因此必須將檔案的名稱傳遞給此函式,以用於建立 MINC 檔案的名稱。volume_count_ptr 必須指向一個已初始化的整數。每次建立變換的輔助 MINC 檔案時,該整數都會用於建立唯一的檔名,然後 volume_count_ptr 會遞增。這兩個函式都返回 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 體積 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 );
  }
華夏公益教科書