跳轉到內容

MINC/軟體開發/MINC1-程式設計師指南

來自華夏公益教科書


MINC 檔案格式(醫學影像 NetCDF)基於由 Unidata 專案中心釋出的 NetCDF 檔案格式(網路通用資料格式)。NetCDF 提供了一個軟體介面,用於以機器無關的方式將命名、多維變數儲存在檔案中。此介面使應用程式免於擔心可移植性和檔案結構,並鼓勵使用自描述形式的資料。

檔案中每個 NetCDF 多維變數都由名稱、維度和屬性描述。例如,儲存在檔案中的影像可能以位元組資料的形式儲存在一個名為"image"的變數中,具有"x""y"維度(每個長度為 256),以及一個名為"long_name"的屬性,該屬性是一個字串,用於描述影像的內容。一個檔案中可以儲存多個變數,每個變數可以有多個屬性。維度獨立於變數存在,並且可以對多個變數進行下標。

MINC 在 NetCDF 介面之上提供了三件事。它提供了一套適用於醫學成像的維度、變數和屬性名稱標準,還提供了一些補充 NetCDF 介面的便利函式(不特定於 MINC 約定),以及一些用於使用 MINC 檔案的便利函式。

NetCDF 簡介

[編輯 | 編輯原始碼]

(有關完整描述,請參閱NetCDF 使用者指南)。

NetCDF 檔案

[編輯 | 編輯原始碼]

在考慮 NetCDF 介面時,檢視示例檔案很有用。幸運的是,NetCDF 包提供了實用程式(ncdump 和 ncgen)用於將二進位制 NetCDF 檔案轉換為名為 CDL 的 ASCII 格式。下面給出了一個簡單的 NetCDF 檔案,由 ncdump 轉換為 CDL 表示形式。

  netcdf test {
  dimensions:
  	ycoord = 3 ;
  	xcoord = 4 ;
  
  variables:
  	double image(ycoord, xcoord) ;
  		image:long_name = "My favorite tiny image" ;
  	double xcoord(xcoord) ;
  
  data:
  
   image =
    1, 2, 3, 4,
    5, 6, 7, 8,
    9, 10, 11, 12 ;
  
   xcoord = 100, 200, 300, 400 ;
  }

示例檔案儲存了一個 3 x 4 的雙精度值影像。首先定義的是維度:xcoordycoord。維度可以代表物理維度,例如 x 座標、y 座標等,也可以代表抽象事物,例如查詢表索引。每個維度都有一個名稱和一個長度,當與其他維度組合時,它們會定義變數的形狀——變數 image 由ycoordxcoord 下標。

維度也可以在變數之間使用,將它們關聯起來。例如,如果檔案中包含另一個也由ycoordxcoord 下標的影像,我們就會得到重要的資訊,即這兩個變數是在同一個網格上取樣的。此外,可以建立與維度名稱相同的變數來定義座標系,例如上面的示例中的xcoord,它給出了影像中每個點的 x 座標。

變數是在 cdl 檔案中定義的下一項。每個變數都有一個名稱、資料型別和由維度列表(每個變數最多有MAX_VAR_DIMS = 32 個維度)指定的形狀。資料型別為NC_CHAR、NC_BYTE、NC_SHORT、NC_INT、NC_FLOATNC_DOUBLE每個變數的資訊儲存在屬性中。屬性 "long_name" 給出了描述變數 "image" 的字元字串。屬性可以是標量或上面列出的六種型別之一的向量(字元字串是型別為NC_CHAR的向量)。

使用 NetCDF 進行程式設計

[編輯 | 編輯原始碼]

使用 NetCDF 進行程式設計非常簡單。上面的檔案是由以下程式生成的

  #include <netcdf.h>
  
  #define THE_NAME "My favorite tiny image"
  static double vals[][4]={
     1.0, 2.0, 3.0, 4.0,
     5.0, 6.0, 7.0, 8.0,
     9.0,10.0,11.0,12.0
  };
  static int ysize=sizeof(vals)/sizeof(vals[0]);
  static int xsize=sizeof(vals[0])/sizeof(vals[0][0]);
  
  static double xcoord[]={100.,200.,300.,400.};
  
  main()
  {
     int cdf, img, xvar;
     int dim[MAX_VAR_DIMS];
     long count[MAX_VAR_DIMS], start[MAX_VAR_DIMS];
  
     /* Create the file */
     cdf=nccreate("test.cdf",NC_CLOBBER);
  
     /* Define the dimensions */
     dim[0]=ncdimdef(cdf, "ycoord", ysize);
     dim[1]=ncdimdef(cdf, "xcoord", xsize);
  
     /* Define the variables */
     img=ncvardef(cdf, "image", NC_DOUBLE, 2, dim);
     xvar=ncvardef(cdf,"xcoord", NC_DOUBLE, 1, &amp;dim[1]);
  
     /* Add an attribute */
     ncattput(cdf, img, "long_name", NC_CHAR, strlen(THE_NAME)+1, THE_NAME);
  
     /* End definition mode */
     ncendef(cdf);
  
     /* Write the variable values */
     start[0]=start[1]=0;
     count[0]=ysize; count[1]=xsize;
     ncvarput(cdf, img, start, count, vals);
     ncvarput(cdf, xvar, &amp;start[1], &amp;count[1], xcoord);
     ncclose(cdf);
  }

程式的第一行可執行語句建立了一個新的 NetCDF 檔案。開啟的檔案要麼處於“定義”模式,要麼處於“資料”模式。在定義模式下,可以定義維度、變數和屬性,但不能寫入或讀取變數資料。在資料模式下,可以寫入或讀取變數值,但不能對維度或變數進行更改,並且只有在屬性已經存在的情況下才能寫入屬性,並且不會隨著寫入而變得更大。新建立的檔案會自動處於定義模式。

呼叫 nccreate 後的行定義了檔案中的維度和變數。請注意,NetCDF 檔案、維度和變數都由建立它們時返回的整數標識。這些 ID 隨後用於引用每個物件。影像變數的屬性 "long_name" 僅由其名稱標識。

一旦所有內容都定義好,ncendef 就會將檔案置於資料模式,然後寫入值。要寫入的值由一個起始索引向量和一個每個維度中要寫入的值數量向量定義。這在變數中定義了一個稱為超片的、多維的矩形。在 C 介面中,向量的第一個元素引用變數變化最慢的索引,因此在此示例中,陣列 vals 的xcoord 變化最快。在 FORTRAN 介面中,約定是第一個下標變化最快。這些約定遵循語言對多維陣列的約定。

MINC 格式

[編輯 | 編輯原始碼]

可以使用 NetCDF 函式呼叫來構建 MINC 格式檔案,但提供了一個 C 包含檔案以促進(並幫助確保)遵守標準。此檔案定義了有用的常量、標準維度、變數和屬性名稱以及一些屬性值。它還聲明瞭在 MINC 便利函式庫中定義的函式,這些函式旨在簡化應用程式程式設計師的工作。

MINC 標準

[編輯 | 編輯原始碼]

人們提出了各種關於檔案格式的要求。其中一個列表如下:協議應該:1)簡單,2)自描述,3)可維護,4)可擴充套件,5)N維,以及 6)擁有通用資料結構。NetCDF 格式滿足所有這些要求,表明它是一個良好的起點。然而,我會在列表中新增一些額外的要求。以上列表中隱含著需要一個用於訪問資料的標準(如何獲取患者姓名)--- 這並非 NetCDF 提供的。此外,一個有用的格式應該附帶一個軟體介面,使其易於使用,特別是在開發環境中。最後,一個儲存許多關聯資訊片段的格式也應該提供一些資料組織。

MINC 格式試圖將這些內容新增到 NetCDF 格式中。

MINC 變數型別

[edit | edit source]

醫學影像通常會生成包含大量輔助資料的檔案(患者資訊、影像資訊、採集資訊等)。為了以有用的方式組織這些資訊,MINC 使用變數將相關的屬性分組在一起。變數本身可能包含或不包含有用的資料。例如,變數MIimage 包含影像資料,並具有與該資料相關的屬性。變數MIpatient 沒有相關的變數資料,但用於將描述患者的所有屬性(姓名、出生日期等)分組在一起。這種型別的變數稱為分組變數。

與維度相對應的變數稱為維度變數,用於描述與維度相對應的座標系。例如 MIxspace --- 既是維度,也是描述其他變數的 x 座標的變數。

NetCDF 約定允許這些維度變數指定每個點的座標,但沒有描述該點處樣本的寬度。MINC 提供了維度寬度變數的約定,例如MIxspace_width,用於提供此資訊。

最後,可以擁有在變數的某些維度上變化的屬性。例如,如果我們有一個影像資料的體積,在MIxspaceMIyspaceMIzspace 上變化,我們可能想要一個屬性來提供每個影像的最大值,在MIzspace 上變化。為了實現這一點,我們使用一個變數,稱為變數屬性,由影像變數的屬性指向。

因此,MINC 引入了幾種型別的變數:分組變數、維度變數、維度寬度變數和變數屬性。

資料組織

[edit | edit source]

MINC 試圖透過分組變數的層次結構來提供一定程度的資料組織。如上所述,屬性根據型別在分組變數中進行分組。每個分組變數都可以具有一個MIparent 和一個MIchildren 屬性 --- 前者指定層次結構中位於該變數之上的另一個變數的名稱,後者指定層次結構中位於該變數之下的變數的換行符分隔列表。層次結構的根是MIrootvariable 變數,沒有父級。雖然不需要使用這種結構,但它可以提供一種機制來組織大量資訊。

MINC 維度變數和屬性名稱

[edit | edit source]

NetCDF 格式對變數和維度命名約定沒有說明,對屬性名稱也沒有說明太多。它確實提供了一些標準,例如用於描述變數的屬性“long_name”,這些標準已被 MINC 標準採用。MINC 定義了一組用於常用實體的標準名稱,包含檔案定義了指定這些名稱的常量。這些在 MINC 參考手冊中有詳細說明。其中最有趣的是MIimage,這是用於在檔案中儲存實際影像資料的變數的名稱。

影像維度

[edit | edit source]

MINC 標準賦予了影像概念一些特殊的地位。NetCDF 本身並沒有任何內容表明特定維度具有任何特殊地位,但在成像上下文中,對哪些維度可以變化以及如何變化進行限制可能很方便。例如,要求指定如何重新縮放影像的變數(參見後面關於畫素值的章節)不隨影像維度變化,這意味著我們可以將影像視為一個簡單的單元。在最簡單的情況下,影像維度只是MIimage 變數的兩個變化最快的維度。

允許向量場 --- 在每個點上具有向量值的影像或影像體積 --- 也很有幫助。RGB 影像是一個向量場的簡單示例。在空間中的每個點,都有三個值:紅色、綠色和藍色。維度MIvector_dimension 用於向量的分量,它應該是MIimage 變數中變化最快的維度。如果它存在,那麼MIimage 的三個變化最快的維度就是影像維度。

MINC 座標系

[edit | edit source]

MINC 標準定義了空間座標如何相對於患者進行定向。檔案可以自由地以所需的方向儲存資料,但在醫學影像上下文中,正的世界座標具有明確的含義。標準是正 x 軸從患者的左指向右,正 y 軸從後指向前,正 z 軸從下指向上。

元素索引到世界座標的轉換是使用維度變數屬性MIdirection_cosinesMIstepMIstart 完成的。如果方向餘弦為c=(c_x, c_y, c_z),則沿軸的相鄰元素之間的向量為 step x c。如果 start(i) 和c(i) 是維度 i 的MIstartMIdirection_cosines 屬性(MIxspaceMIyspaceMIzspace 之一),則影像變數的第一個元素位於世界座標 \sum_i start(i) c(i) 處。

如果方向餘弦不存在,則假設它們對於MIxspace 為 (1,0,0),對於MIyspace 為 (0,1,0),對於MIzspace 為 (0,0,1)。方向餘弦是單位向量,應該歸一化。同樣,step 屬性應該攜帶有關軸翻轉(負或正)的資訊,而不是方向餘弦。

畫素值和真實值

[edit | edit source]

在醫學影像中,畫素值通常儲存為位元組或短整型,但通常與每個畫素也關聯一個真實值。該真實值是透過與每個影像或影像體積相關的比例因子和偏移獲得的。MINC 標準指示瞭如何解釋畫素值。

MIimage 變數中的影像資料可以儲存為位元組、短整型、整型(32 位)、浮點型或雙精度型。NetCDF 約定使用屬性MIvalid_rangeMIvalid_maxMIvalid_min 來指示變數中可以找到的值範圍。例如,對於短整型值,我們可能有一個有效的範圍為 0 到 32000。為了將這些整數轉換為真實值,我們可以使用比例因子和偏移。但是,如果資料轉換為範圍為 23 到 228 的位元組,則這些值必須更改。

如果我們指定一個影像最大值和最小值,MIvalid_maxMIvalid_min 應該透過適當的比例因子和偏移對映到該值,那麼我們就可以轉換型別和有效範圍,而無需更改真實最大值和最小值。為了允許影像中的最大動態範圍,我們使用變數MIimagemaxMIimagemin 來儲存真實最大值和最小值 --- 這些值可以在MIimage 的任何非影像維度上變化。

通用便利函式

[edit | edit source]

MINC 提供了許多便利函式,這些函式與醫學影像無關,但使 NetCDF 檔案的使用更加容易。NetCDF 格式的一個缺點是資料可以以任何形式(位元組、短整型、整型、浮點型、雙精度型)出現,並且呼叫程式必須處理一般情況。與其限制這一點,MINC 提供了用於轉換型別的函式。

第一組便利函式用於型別轉換。

  • miattget - 讀取屬性向量,指定所需的數字型別和要讀取的最大值數量。
  • miattget1 - 讀取一個指定型別的屬性值。
  • miattgetstr - 讀取指定最大長度的字元屬性。
  • miattputdbl - 寫入一個雙精度屬性。
  • miattputstr - 寫入一個字串屬性。
  • mivarget - 獲取指定型別的超立方體值。
  • mivarget1 - 獲取一個指定型別的單個值。
  • mivarput - 放置一個指定型別的超立方體值。
  • mivarput1 - 放置指定型別的一個單一值。

接下來我們有一些處理座標向量的函式。

  • miset_coords - 將座標向量設定為一個單一值。
  • mitranslate_coords - 將一個變數的座標轉換為另一個變數的索引向量。

最後,有一些函式可以將變數作為一組屬性進行處理,使修改檔案更容易,同時保留輔助資訊。

  • micopy_all_atts - 將一個變數的所有屬性複製到另一個變數(可能跨檔案)。
  • micopy_var_def - 將一個變數定義(包括屬性)從一個檔案複製到另一個檔案。
  • micopy_var_vals - 將一個變數的值從一個變數複製到另一個變數(可能跨檔案)。
  • micopy_all_var_defs - 將所有變數定義從一個檔案複製到另一個檔案,排除一個變數列表。
  • micopy_all_var_vals - 將所有變數值從一個檔案複製到另一個檔案,排除一個變數列表。

MINC 特定的便利函式

[編輯 | 編輯原始碼]

提供了一些例程來處理一些 minc 結構。miattput_pointermiattget_pointer 放置/獲取指向變數屬性的指標。miadd_child 透過處理兩個變數的 MIparentMIchildren 屬性來幫助維護變數的層次結構。最後,micreate_std_variablemicreate_group_variable 建立一些標準變數並填寫一些預設屬性。

影像轉換變數

[編輯 | 編輯原始碼]

前面提到的檔案格式要求之一是軟體介面,使其易於使用。使用靈活格式的最大困難在於應用程式必須處理許多可能性。在影像方面,這意味著各種資料型別和比例因子,以及不同大小的影像。MINC 的影像轉換變數函式試圖為程式設計師消除這種複雜性。

影像轉換變數 (icv) 本質上是對程式希望影像在型別、比例和維度上呈現方式的規範。當透過 icv 讀取 MINC 影像時,無論資料如何在檔案中儲存,它都會被轉換為呼叫程式的標準格式。

轉換分為兩類:型別和範圍轉換改變影像值的 資料型別(和符號),並選擇性地對其進行縮放以進行適當的規範化。維度轉換允許程式指定影像維度的尺寸和影像軸的方向(MIxspace 座標應該增加還是減少?患者的左側應該出現在影像的左側還是右側?)。

ICV 例程

[編輯 | 編輯原始碼]

透過 icv 訪問檔案是一個簡單的過程。使用 miicv_create 建立 icv,使用 miicv_set 例程設定屬性(如所需的 資料型別),使用 miicv_attach 將 icv 附加到 NetCDF 變數,並使用 miicv_getmiicv_put 訪問資料。可以使用 miicv_detach 將 icv 從 NetCDF 變數中分離,並可以使用 miicv_free 釋放它。

Icv 屬性是字串、整數、長整數或雙精度數。例如,MI_ICV_SIGN(變數值的符號)是一個字串,而 MI_ICV_IMAGE_MAX(影像最大值)是一個雙精度值。提供了四個函式——miicv_setint、miicv_setlong、miicv_setdblmiicv_setstr——來簡化屬性值的設定。程式可以使用 miicv_inqint、miicv_inqlong、miicv_inqdblmiicv_inqstr 查詢屬性值。

型別和範圍轉換

[編輯 | 編輯原始碼]

透過指定 MI_ICV_TYPEMI_ICV_SIGN 屬性的值來轉換畫素值的型別和符號(它們預設為 NC_SHORTMI_SIGNED)。還可以將值轉換為有效範圍並進行規範化。透過將 MI_ICV_DO_RANGE 設定為 TRUE(預設值)來啟用這些轉換。

如果 MI_ICV_DO_NORMFALSE(預設值),則僅進行有效範圍的轉換。這意味著,如果輸入檔案具有 0 到 4095 範圍內的短整型,則可以將其轉換為 64 到 248 範圍內的位元組(例如)。實際影像的最大值和最小值(MIimagemaxMIimagemin)將被忽略。有效範圍由 MI_ICV_VALID_MAXMI_ICV_VALID_MIN 屬性指定,它們預設為型別和符號的合法範圍。

我們可能希望縮放值,以便它們規範化為 MIimage 變數中的所有值,或者規範化為某個使用者定義的範圍。要進行規範化,請將 MI_ICV_DO_NORM 設定為 TRUE。將 MI_ICV_USER_NORM 設定為 FALSE(預設值)會導致規範化為變數的實際最大值和最小值(MIimagemax 的最大值和 MIimagemin 的最小值)。如果 MI_ICV_USER_NORM 為 true,則使用 MI_ICV_IMAGE_MAXMI_ICV_IMAGE_MIN 的值(預設為 1.0 和 0.0)。

MI_ICV_TYPE 或檔案型別為浮點數時,從實數到實數的轉換始終使用實際影像的最大值和最小值資訊進行。如果內部型別為整型且 MI_ICV_DO_NORMFALSE,則進行重新縮放,以便切片最大值對映到內部值的有效範圍。

請注意,在轉換為整型時,值將四捨五入到最接近的整數,並限制在資料型別的合法範圍內。

上述轉換很簡單,但使用浮點數增加了複雜性,因為一般情況下,我們不想重新縮放這些值以獲得實際值。下面更詳細地描述了各種可能性。

畫素值轉換的詳細資訊

[編輯 | 編輯原始碼]

思考重新縮放的最簡單方法是透過四個範圍(最大值-最小值對)。在檔案變數中,值具有有效範圍 var_vrange,這些值對應於實際值 var_imgrange。使用者/應用程式希望將實際值 usr_imgrange 轉換為有用的有效範圍 usr_vrange。從 var_vrangevar_imgrangeusr_imgrangeusr_vrange,我們可以確定用於轉換畫素值的比例和偏移量:輸入值按 var_vrangevar_imgrange 的比例縮放為實際值,然後再次按 usr_imgrangeusr_vrange 的比例縮放為使用者值。

如果兩個 vrange 變數都沒有指定,則它們預設為整型型別的最大可能範圍。對於浮點型別,usr_vrange 設定為等於 usr_imgrange,因此不進行實際值的轉換。

如果沒有進行規範化,則對於整型,var_imgrangeusr_imgrange 設定為 [0,1](縮放到 [0,1] 並再次縮放)。在規範化時,usr_imgrange 設定為變數的整個範圍(如果找不到則為 [0,1])或使用者請求的範圍。如果變數值為浮點數,則 var_imgrange 設定為 var_vrange(不縮放到實際值),否則 var_imgrange 為每個影像讀取(同樣,如果找不到,則為 [0,1])。

這對於讀取和寫入影像意味著什麼將在下面討論。

使用畫素轉換讀取

[編輯 | 編輯原始碼]

當讀取到內部浮點數時,規範化沒有影響。當讀取沒有規範化的整數時,每個影像都被縮放到整個範圍。使用規範化,它們被縮放到指定的範圍,並且可以比較切片。

當輸入檔案缺少 MIimagemax/MIimageminvar_imgrange 資訊)或 MIvalid_range 時,例程會嘗試提供合理的預設值,但仍然會發生奇怪的事情。最大的問題是,如果預設值不正確(整型值的整個範圍,浮點數的 [0,1]),則缺少 MIvalid_range。在將浮點數轉換為整型時,如果值超出了 [0,1] 範圍,將會發生溢位。

使用畫素轉換寫入

[編輯 | 編輯原始碼]

轉換例程可用於寫入值。這對於資料壓縮很有用——例如,將內部浮點數轉換為檔案中的位元組值,或將內部短整型轉換為位元組。在使用規範化進行此操作時(例如,將位元組重新縮放到切片最大值),重要的是在寫入切片之前在 MIimagemaxMIimagemin 中寫入切片最大值和最小值。

另一個問題是,MIvalid_rangeMIvalid_maxMIvalid_min 應該正確寫入(尤其是當預設值不正確時)。在寫入浮點數時,MIvalid_range 應該設定為變數中所有值的整個範圍。在這種情況下,屬性不必在寫入變數之前正確設定,但如果它存在,則值應該合理(最大值大於最小值,並且值不太可能導致溢位)。如果例程 micreate_std_variableNC_FILL 模式下使用(預設值),則這些值將被自動設定。

示例 讀取值

[編輯 | 編輯原始碼]

在沒有規範化的情況下讀取影像

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
  (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
  (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
  (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
  
  /* Open the file, attach the image variable */
  cdfid=ncopen(filename, NC_NOWRITE);
  
  /* Attach image variable */
  img=ncvarid(cdfid, MIimage);
  (void) miicv_attach(icv, cdfid, img);
  
  /* Get the data - we assume that coord and count are set properly */
  (void) miicv_get(icv, coord, count, image);
  
  /* Close the file and free the icv */
  (void) ncclose(cdfid);
  (void) miicv_free(icv);

使用規範化讀取影像

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
  (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
  (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
  (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
  (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
  (void) miicv_setint(icv, MI_ICV_USER_NORM, TRUE);
  (void) miicv_setdbl(icv, MI_ICV_IMAGE_MAX, 1.83);
  (void) miicv_setdbl(icv, MI_ICV_IMAGE_MIN, -0.57);
  ...

讀取浮點數影像

  /* Create the icv. We don't have to set MI_ICV_USER_NORM to TRUE,
     but doing so ensures that the conversion is done properly
     without looking at file values (the defaults for
     MI_ICV_IMAGE_MAX and MI_ICV_IMAGE_MIN are 1 and 0) */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT);
  (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
  (void) miicv_setint(icv, MI_ICV_USER_NORM, TRUE);
  ...

示例 寫入值

[編輯 | 編輯原始碼]

從浮點數寫入位元組值

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT);
  (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
  
  /* Create the file */
  cdf=nccreate(filename, NC_CLOBBER);
  
  /* Define the dimensions */
  dim[0]=ncdimdef(cdf, MIyspace, ysize);
  dim[1]=ncdimdef(cdf, MIxspace, xsize);
  
  /* Define the variables */
  img=micreate_std_variable(cdf, MIimage, NC_BYTE, 2, dim);
  (void) miattputstr(cdf, img, MIsigntype, MI_UNSIGNED);
  vrange[0]=0; vrange[1]=200;
  (void) ncattput(cdf, img, MIvalid_range, NC_DOUBLE, 2, vrange);
  max=micreate_std_variable(cdf, MIimagemax, NC_DOUBLE, 0, NULL);
  min=micreate_std_variable(cdf, MIimagemin, NC_DOUBLE, 0, NULL);
  
  /* End definition mode */
  ncendef(cdf);
  
  /* Attach image variable */
  (void) miicv_attach(icv, cdf, img);
  
  /* Write the image max and min */
  ncvarput1(cdf, max, NULL, &image_maximum);
  ncvarput1(cdf, min, NULL, &image_minimum);
  
  /* Write the image */
  start[0]=start[1]=0;
  count[0]=ysize; count[1]=xsize;
  miicv_put(icv, start, count, vals);
  
  /* Close the file and free the icv */
  (void) ncclose(cdf);
  (void) miicv_free(icv);

如果我們正在寫入浮點影像,唯一的區別(除了將NC_BYTE更改為NC_FLOAT之外)是,我們將在檔案末尾用浮點值的完整範圍重寫MIvalid_range

維度轉換

[編輯 | 編輯原始碼]

任意維影像的一個問題是,軟體需要處理一般情況。如果事先知道所有影像都具有特定大小(例如 256 x 256)和特定方向(例如,第一個畫素位於患者的前面右側),則編寫應用程式軟體會更容易。

透過將 icv 屬性 MI_ICV_DO_DIM_CONV 設定為 TRUE,可以自動完成這些轉換。空間軸的方向由屬性 MI_ICV_XDIM_DIRMI_ICV_YDIM_DIRMI_ICV_ZDIM_DIR 確定。這些影響任何是 MI?spaceMI?frequency 的影像維度,其中 ? 對應於 xyz。這些屬性可以具有值 MI_ICV_POSITIVEMI_ICV_NEGATIVEMI_ICV_ANYDIR。最後一個將防止維度翻轉。前兩個將在必要時翻轉維度,以便維度變數的屬性 MIstep 具有正確的符號。

這兩個影像維度被稱為維度 A 和 B。維度 A 是這兩個維度中變化最快的維度。設定屬性 MI_ICV_ADIM_SIZEMI_ICV_BDIM_SIZE 指定影像維度的期望大小。調整維度大小,以便檔案影像完全適合呼叫程式的影像,並且在影像中居中。大小 MI_ICV_ANYSIZE 允許其中一個維度具有可變大小。如果屬性 MI_ICV_KEEP_ASPECT 設定為 TRUE,則兩個維度將按相同量重新縮放。可以查詢與屬性 MIstepMIstart 相對應的新的步長和起點(其中畫素位置 = ipixel*step+start,ipixel 從零開始計數)。屬性 MI_ICV_?DIM_STEPMI_ICV_?DIM_START? = AB)會自動設定,可以查詢但不能設定。

雖然允許向量影像,但許多應用程式寧願只處理標量影像(每個點一個強度值)。將 MI_ICV_DO_SCALAR 設定為 TRUE(預設值)將導致向量影像透過對分量求平均值轉換為標量影像。(因此,RGB 影像以這種簡單的方式自動轉換為灰度影像)。

對於程式來說,對三個(或者更多)維度(而不僅僅是兩個標準影像維度)執行維度轉換有時可能很有用。為了對超出通常兩個的維度執行維度翻轉和/或調整大小,可以將屬性 MI_ICV_NUM_IMGDIMS 設定為 1 到 MI_MAX_IMGDIMS 之間的整數值。要設定維度的尺寸,請設定屬性 MI_ICV_DIM_SIZE(類似於 MI_ICV_ADIM_SIZE)。要指定要設定的維度,請將維度新增到屬性中(新增零對應於變化最快的維度——對“A”維度新增零,對“B”維度新增一,等等)。可以透過屬性 MI_ICV_DIM_STEPMI_ICV_DIM_START 查詢體素間距和位置(類似於 MI_ICV_ADIM_STEPMI_ICV_ADIM_START),同樣將維度號新增到屬性中。

帶維度轉換的讀取示例

[編輯 | 編輯原始碼]

讀取一個 256 x 256 的影像,第一個畫素位於患者的下方、後面、左側,作為 0 到 32000 之間的短整數值

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
  (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
  (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
  (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
  (void) miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE);
  (void) miicv_setint(icv, MI_ICV_ADIM_SIZE, 256);
  (void) miicv_setint(icv, MI_ICV_BDIM_SIZE, 256);
  (void) miicv_setint(icv, MI_ICV_KEEP_ASPECT, TRUE);
  (void) miicv_setint(icv, MI_ICV_XDIM_DIR, MI_POSITIVE);
  (void) miicv_setint(icv, MI_ICV_YDIM_DIR, MI_POSITIVE);
  (void) miicv_setint(icv, MI_ICV_ZDIM_DIR, MI_POSITIVE);
  
  /* Open the file, attach the image variable */
  cdfid=ncopen(filename, NC_NOWRITE);
  
  /* Attach image variable */
  img=ncvarid(cdfid, MIimage);
  (void) miicv_attach(icv, cdfid, img);
  
  /* Get the data - we assume that coord and count are set properly */
  (void) miicv_get(icv, coord, count, image);
  
  /* Close the file and free the icv */
  (void) ncclose(cdfid);
  (void) miicv_free(icv);
華夏公益教科書