轉載http://blog.sina.com.cn/s/blog_4bce5f4b01019ix5.html
DICOM 文件內容在 Part 3 DICOM IOD 里定義。CT, MR, CR, DR, US, NM, PET, XA 等各有自己的內容定義,由共同的專有的部分 (IE 和 Modules) 組成。
DICOMDIR 由 Part 10 定義。是一種 mini 可變長度數據庫文件結構。
過去我手下一個學計算機的非常聰明的印度人問我:圖像文件格式不是有 BMP, GIF, TIF 和 JPEG 等等嗎,為什么還要定義 DICOM 呢? 我向他解釋了好幾次最后還是放棄了。 任何圖像文件格式無非是由兩個部分組成:存參數的 header 和圖點數據 (pixel data)。 BMP, JPEG, TIFF 之類的格式的 header 只描述圖像的基本參數:如幾行、幾烈、每點用了幾位、有沒有壓縮、調色板等等。Header 往往是固定長度的。 而醫療影像還要許多其它參數,如病人基本資料、檢驗基本資料、系列資料、位置資料等等。而且每種 Modality 和每種 image 所需要的內容不一樣。因此,一般的圖像格式不能用。
DICOM 的 4 個內容層次:
1. Patient (病人)
2. Study (檢驗)
3. Series (系列)
4. Image (圖像)
盡管頭幾層的內容在很多圖像里是相同的,它們在每個圖像文件里都要有。 每一層叫一個 Information Entity,或 IE (從 relational database schema 設計引用而來)。每一層又細分成 Module。每個 Module 里面的最小單元叫做一個 attribute 或 element。
現在舉個例子:
CR 圖像
DICOM Part 3, A.2.3, Table A.2-1
1. Patient IE: a. Patient Module (參考 C.7.1.1)
2. Study IE: a. Study Module (參考 C.7.2.1) b. Patient Study Module (參考 C.7.2.2)
3. Series IE: a. General Series (參考 C.7.3.1) b. CR Series (參考 C.8.1..1) c. General Equipment (參考 C.7.5.1)
4. Image IE: a. Genrral Image (C.7.6.1) b. Image Pixel (C.7.6.3) c. Contrast/bolus (C.7.6.4) d. CR Image (C.8.1.2)
...
i. SOP Common (C.12.1)
將這些 modules (tables) 里的所有 elements 都找出來就做成了一個 CR 圖像的架構。 要注意的是這些 module 有些是一定要的 (modatory) 有些是用戶選用的 (user)。 到了每個 module 里 attribute/element 表又有分五類 Type 1, 1C, 2, 2C 和 3。 Type 1 是一定要的,2 也是一定要的但是內容可以是空的。Type 3 則可要可不要。 所以濃縮一下,一個 CR 圖像里的元素 (elements) 也不是太多。
把這些表格展開后,這些 elements 組成一個 dataset。 那么寫到一個文件里或通過網路傳送又是個什么格式呢?這就要看 Part 5。
一個元素 (element) 的結構是:
1. group tag: 16-bit
2. element tag: 16-bit
3. length (or VR/length): 32-bit
4. data (bytes of length)
對應每一個用到的 element DICOM 標准 Part 6 都定義了一個 group tag 和 element tag。
比如說: patient name: 0x0010, 0x0010 patient ID: 0x0010, 0x0020 ...
VR 說的是 element 格式,比如說 patinet name 的 VR 是 PN。格式是 last_name^first_name^middle_name^prefix^surfix。那么我的英文名字就是: Wang^JB^^Dr.^
往外寫時要做幾個事情:
1. 要把所有元素按 group tage 和 element tag 理一遍 (sort)。從小排到大。
2. 如果是寫 DICOM 介質的 DICOM file 還先寫 128 bytes preamble (一般是空白),加 "DICM", 加 group 2 Meta header。(講到 Part 10 時再細說)
3. 如果 dataset 里面含有 Sequence elements, sequence 里面每一個 Item 又是一個 dataset。
作業: 有了這些知識,你就可以開始寫一個小小的 BMP 到 DICOM 的轉化程序。
關鍵資料:
Modality (0008, 0060): SC
Photometric Interpretation (0028, 0004): RGB
SOP Class UID: 1.2.840.10008.1.5.4.1.1.7
最簡單的辦法是寫一個 structure,然后一個
array. typedef struct DicomElem(short int group_tag, short int element_tag, char VR[4], int length, char data[128]) DicomElem;
DicomElem CRDataSet [] = ( ( 0x0008, 0x0005, "CS", 10, "ISO_IR 100"), ( 0x0008, 0x0008, "CS", 16, "ORIGINAL\\PRIMARY"), ... (0x0010, 0x0010, "PN", 16, "My^Test^Image^^ "),
(0x0010, 0x0020, "SH", 6, "123456"),
...
(0,0,"",0,"")
);
void WriteCDImage(FILE *fp)
{
DicomElem elem = CRDataSet[0];
unsigned long int lComboTag;
int nCols, nRows;
unsigned char *pPixelData unsigned long int lPixelLength;
pPixelData = LoadBMPImgeData("MyImage.bmp", nCols, nRows, lPixelLength);
while(CRDataSet.group_tag)
{
lComboTag = (CRDataSet.group_tag << 16) | CRDataSet.element_tag;
switch(lComboTag)
{
case 0x00280010: *((short int *)CRDataSet.data) = nCols;
break;
case 0x00280011: *((short int *)CRDataSet.data) = nRows;
break;
...
}
// Write group and element tag
fwrite(&lComboTag, 1, sizeof(long), fp);
// Write VR
fwrite(CRDataSet.VR, 1, 2, fp);
if (lComboTag != 0x7fe00010)
{
fwrite(CRDataSet.length, 1, sizeof(short), fp);
fwrite(CRDataSet.data, 1, CRDataSet.length, fp);
}
else
{
fwrite("\0\0", 1, 2, fp);
// Two blank bytes after VR
fwrite(&lPixelLength, 1, sizeof(long), fp);
// Length
fwrite(pPixelData, 1, lPixelLength, fp);
}
i++;
}
}
unsigned char *LoadBMPImgeData(char *fileName, int &nCols, int &nRows, unsigned long &lPixelLength) ( .... )
細節自己去寫。
更上一層樓: DICOM 文件讀寫最難的是兩個東西:
o DICOM Sequence
o DICOM Pixel Data
剛剛讓大家做的作業里沒有 Sequence,pixel data 也很簡單。 Sequence 在 C 里的類比是一個 structure 的 array。 是結構套結構。 所以讀起來難。更討厭的是 Sequence 還可以不定義長度, 即長度是 -1。要靠你自己去找 (FFFE, E0DD) 來決定 Sequence 是否結束。 Array 里面的每個 structure 單元就是 DICOM Sequence 里的一個 Item。Item 的開頭是一個特定 element (FFFE, E000)。 如果 Item 的長度是 -1, 要靠找到 (FFFE, E00D) 來決定 Item 的結束。 Pixel Data (7fe0, 0010) 是一個特殊的 DICOM element。總是在所有元素的最后面。它個 Sequence 有兩個相似的地方: o 長度區總是 32-位 (即 explicit VR 的情況下要在 VR 區后面填兩個 bytes,然后再加 4-bytes 的長度。 o 如圖像是壓縮的,每幅圖用一個 item 來存。 第一個 item 是個 offset table。每幅圖的 offset 是一個 dword (4 bytes),第一幅圖的 offset 是 0。
DICOM 介質與 DICOMDIR:
DICOM 圖像通過 Store SCU/SCP 來傳的時候最小的組是 (0008, xxxx),一般頭一個 element 是 (0008, 0000) 或 (0008, 0005)。 而存在介質上 (如光盤,硬盤和 MOD),則要加兩套東西:
1. Preamble 和 DICOM signature: 128 bytes (一般是空的) 后面跟 DICM。
2. Meta header: group (0002, xxxx) 的十幾個 elements. 看 DICOM Part 10。 DICOMDIR 是一個可變長度 迷你 database 文件。由 group (0002, xxxx) 和 group (0004, xxxx) 為主題。描述的是一個 4 層的樹狀結構 (tree structure)。 1. Patient 2. Study
3. Series 4. Image 除了用 DICOM 慣用的 Sequene 外還用了 offset 來做 linked list。所以讀寫起來比較麻煩。請看 DICOM Part 10。
DICOM 的中文支持: DICOM 本身可以支持任何文字。應用程序則可能無法破譯。 DICOM 圖像用中文是可以通過下列方法實現: (0008,0005)設 "GB2312" 或 “Big5"。 所有文字用雙字節 GB 或 Big 5 碼。 大部分的 DICOM 控件用起來都會有問題。國標用的是 8-bit bytes 而非 7-bit printable ASCII。 ANSI 字串函數如 sprintf, sscanf 會將最高一個 bit 削掉。要改寫成支持 wide character 的才行。