一、基礎知識:
1、數據集和元素結構
一個數據集由多個數據元素組成
數據元素可以嵌套為多級(分組)
一個數據元素分:標記、值類型、長度、和值四部分。
傳輸語法為隱式VR的,沒有值類型。
一個標記分:組和組元素,如(0010,0010)
顯示VR數據結構:
tag | vr | value length | value filed |
標記 | 值類型 | 長度 | 值 |
隱式VR數據結構:
tag | value length | value filed |
標記 | 長度 | 值 |
數據意義:0002組存儲通訊描述;0008組檢查特征; 0010組病人信息組; 0028 圖像信息;
2、數據元素長度定義:
顯示VR,值類型為 OB,OW,OF,OD,SQ,UC,UR,UT,UN
顯示VR,其它類型
隱式VRml數據長度:
二、DCM文件結構
用二進制查看文件,可以明顯看到文件內容結構:
有了上面的簡單知識,下面就可以了解源碼是怎么解析DCM文件的。
三、源碼內容:
clearcnavas讀取文件類ClearCanvas.Dicom.IO.DicomStreamReader
實現函數:public DicomReadStatus Read(DicomTag stopAtTag, DicomReadOptions options)
從第132個字節開始讀取內容----》128+4
先讀取MetaInfo信息,這個信息不受傳輸語法影響,,直接按ExplicitVrLittleEndian語法來解析就行,讀取結束到0x0002FFFF
MetaInfo信息讀取完后,可以確定DCM的傳輸語法,,后面信息就根據DCM文件的傳輸語法做解析。。
讀取4個字節,得到VR的TAG值。
如果參數e為0x0000,則為標識為Group Length,VR類型為UL,(參照DICOM3.0標准,第3單和第6單);
其它值則先標識為未知,待后面解析。。。。。
這三個TAG是沒有VR值的。。。
本人電腦單步調試太慢,,先寫到這里,,,未完待續
下面寫一個自己的簡單小例子,實現簡單的顯示VR,DCM文件的讀寫。。
1 static void Main(string[] args) 2 { 3 string filename = @"E:\work\dcm\1.2.840.10008.1.2.4.50.dcm"; 4 5 BinaryReader dcmfile = new BinaryReader(File.OpenRead(filename)); 6 dcmfile.BaseStream.Seek(132, 0); 7 while (true) 8 { 9 var g1 = dcmfile.ReadUInt16(); 10 var g2 = dcmfile.ReadUInt16(); 11 12 13 string vr = Encoding.Default.GetString(dcmfile.ReadBytes(2)); 14 int nRead = Read2Byte(vr) ? 2 : 6; 15 byte[] bRead = dcmfile.ReadBytes(nRead); 16 int nLen = 0; 17 foreach (byte b in bRead) 18 { 19 nLen += b; 20 } 21 22 bRead = dcmfile.ReadBytes(nLen); 23 string val = getVF(vr, bRead); 24 Console.WriteLine(string.Format("({0} ,{1}) {2} {3} {4}", 25 g1.ToString("X").PadLeft(4, '0'), 26 g2.ToString("X").PadLeft(4, '0'), 27 vr, nLen, val)); 28 29 if (g1 == 0x7FE0 && g2 == 0x0010) 30 break; 31 } 32 33 34 Console.ReadKey(); 35 } 36 37 38 private static bool Read2Byte(string vrNmae) 39 { 40 switch (vrNmae) 41 { 42 case "OB": 43 case "OW": 44 case "OF": 45 case "OD": 46 case "SQ": 47 case "UC": 48 case "UT": 49 case "UN": 50 return false; 51 default: 52 break; 53 } 54 return true; 55 } 56 57 private static string getVF(string VR, byte[] VF) 58 { 59 string VFStr = string.Empty; 60 switch (VR) 61 { 62 case "SS": 63 VFStr = BitConverter.ToInt16(VF, 0).ToString(); 64 break; 65 case "US": 66 VFStr = BitConverter.ToUInt16(VF, 0).ToString(); 67 68 break; 69 case "SL": 70 VFStr = BitConverter.ToInt32(VF, 0).ToString(); 71 72 break; 73 case "UL": 74 VFStr = BitConverter.ToUInt32(VF, 0).ToString(); 75 break; 76 case "AT": 77 VFStr = BitConverter.ToUInt16(VF, 0).ToString(); 78 79 break; 80 case "FL": 81 VFStr = BitConverter.ToSingle(VF, 0).ToString(); 82 83 break; 84 case "FD": 85 VFStr = BitConverter.ToDouble(VF, 0).ToString(); 86 87 break; 88 case "OB": 89 VFStr = BitConverter.ToString(VF, 0); 90 break; 91 case "OW": 92 VFStr = BitConverter.ToString(VF, 0); 93 break; 94 case "SQ": 95 VFStr = BitConverter.ToString(VF, 0); 96 break; 97 case "OF": 98 VFStr = BitConverter.ToString(VF, 0); 99 break; 100 case "UT": 101 VFStr = BitConverter.ToString(VF, 0); 102 break; 103 case "UN": 104 VFStr = Encoding.Default.GetString(VF); 105 break; 106 default: 107 VFStr = Encoding.Default.GetString(VF); 108 break; 109 } 110 return VFStr; 111 } 112 }
結果對比::
得到的數據,一模一樣。。。
對於圖像解析部分,不太建議用clearCanvas,c#對圖像操作不如C++。。還是建議用DCMTK來處理圖像壓縮和解壓縮。。
具體實現方法,后面再寫。。