【題外話】
其實早就想開始重新寫博客,但總是有各種各樣的理由“說服”自己偷懶,這次在一家小公司實習,要做Dicom文件的解析,覺得有必要記錄一些東西,也是本博第一篇文章了。
【文章索引】
個人感覺,要想解析一個文件,雖然可能會有各種各樣的類庫去做,但還是很有必要去了解下最起碼文件的結構,這樣在使用類庫的時候思路也會更清晰一些。
總體來說,DICOM的文件格式還是很清晰的,簡單看其實可以分為三部分:
- 第一部分是文件開始固定的128字節為DICOM文件的引言(Preamble),不過由於可以不寫,所以基本上所有的DICOM文件前128字節均為置0;
- 第二部分為跟在之后的4字節文件標識,即“DICM”四個字節,如果不是則表示不是DICOM文件;
- 第三部分為數據集(DataSet),數據集中包含若干個數據元素(DataElement),數據集中包含了文件的元數據(File Meta Data Element)、患者及檢查的數據、私有數據(Private Data Element)以及圖像、覆蓋層等數據(Encoding of Pixel, Overlay and Waveform Data)。
對於每一個數據元素,其又包含如下圖所示的四部分:
- 數據元素標識 Tag(2字節UInt16分組號和2字節UInt16元素號);
- 數據表現類型 Value Representation(2個單字節Char,有些情況之后會預留兩字節的置0,如上圖的0x20 0x00 0x01 0x00);
- 數據長度 Value Length(2字節UInt16,有些情況是4字節UInt32,如上圖的0x20 0x00 0x01 0x00);
- 數據內容 Value Field(長度為Value Length,如果VL=0xFFFFFFFF,則需要一直讀到截止符)。
數據內容的存儲與表現格式與VR是關聯的(參見標准第五部分第24頁),但比較惡心的是,VR不是一定存在,也就是可能有隱式的情況(需要根據元素標識進行判斷),此外VR的屬性還可能是UN(Unknown)等等等等。當然除非你要自己寫解析,否則了解到這就可以了。
.NET平台下其實有好多好用的DICOM解析庫,之前有一個叫mDCM的類庫(https://github.com/rcd/mdcm),能讀取大部分DICOM文件(手頭有幾張色深16位的片子解析有偏差,還有幾張測試軟件的片子無法讀取,再就是無法支持32位色片子等等),作者也已經對類庫停止了更新。不過好在之后又有了fo-dicom(https://github.com/rcd/fo-dicom),用起來感覺跟mDCM是一樣的(代碼很多也是一樣的),而且上述的問題都解決了,起碼我手頭幾個醫院的片子都能正常解析。
要獲取的fo-dicom的話,需要去上述提供的github鏈接上下載代碼,可以直接點擊上方工具欄的ZIP打包下載所有代碼,然后在本地打開編譯一下。需要說明的是,項目是基於.NET 4.0的,由於這個項目包含了C++的代碼,所以如果用VS2012打開的話需要更新為VC++2012編譯。其實如果不需要Jpeg解析,只需要編譯Dicom項目即可。
fo-dicom對DICOM文件格式進行了高度封裝,首先一個DICOM文件是一個DicomFile類(using Dicom),你可以這樣從文件中創建,比如這樣:
DicomFile dcmFile = DicomFile.Open(filePath);
或者這樣從流中創建,比如這樣:
DicomFile dcmFile = DicomFile.Open(stream);
如果要獲取DICOM文件中的數據元素(Data Element),需要從DICOM的數據中獲取,與mDCM不同的是,fo-dicom提供了泛型的方法,而不是針對每個類型提供了一個方法。同時,與mDCM一樣的是,fo-dicom對所有的數據元素標識(Tag)都進行了封裝,你無需考慮Tag到底是多少,而只需要給出你需要什么就可以了,DicomTag類下提供了幾千個封裝好的靜態類可供選擇,比如這樣:
DicomDataset dcmDataSet = dcmFile.Dataset;
String patientName = dcmDataSet.Get<String>(DicomTag.PatientName);
如果要獲取圖像信息的話需要創建DicomImage類(using Dicom.Imaging),比如這樣:
DicomImage dcmImage = new DicomImage(dcmDataSet);//可以增加第二個參數來指定獲取第幾幀 Image image = dcmImage.RenderImage();
fo-dicom另一點比mDCM好的地方在於,其將窗位(WindowCenter)和窗寬(WindowWidth)也放到了DicomImage類中,可以直接修改DicomImage類中的這兩個參數來獲取和設置窗位和窗寬,調整完直接RenderImage即可,而無需調整DataSet中的參數再重新創建DicomImage類(不但簡化了操作而且提交了效率),比如這樣:
dcmImage.WindowCenter = 300; dcmImage.WindowWidth = 1500;
1、在某個WindowCenter和WindowWidth程序提示數組越界,程序崩潰。
修改Dicom/Imaging/LUT/OutputLUT.cs中的索引器(public int this[int value],如下圖),將注釋掉的四行屏蔽注釋即可(判斷value < 0 和 > 255的部分)。不知道作者的因為調試程序還是其他原因,為什么將這四行注釋掉,mDCM的這個文件也是如此,實際測試中對於某些WindowCenter和WindowWidth,在這個地方可能會導致數組越界。
2、調整Overlays層的繪制顏色。
修改Dicom/Imaging/DicomImage.cs中的常量OverylayColor;
3、在輸出圖片時直接輸出旋轉后的影像。
修改Dicom/Imaging/DicomImage.cs中RenderImage方法,在創建完ImageGraphic的對象graphic之后執行graphic.Rotate(angle)方法,其中angle如果大於零表示向右旋轉的度數,如果小於零表示向左的度數,所以你可以在DicomImage中創建個屬性設置好旋轉的度數或者直接在RenderImage方法傳入旋轉的度數。例如改成下圖這樣:
1、Dicom格式文件解析器:http://www.cnblogs.com/assassinx/archive/2013/01/09/dicomViewer.html
2、《DICOM標准醫學圖像文件解析及工具軟件的研制》:http://wenku.baidu.com/view/7511df2c2af90242a895e524.html
3、The DICOM Standard:http://medical.nema.org/standard.html