DICOMDIR结构分析与实现
无论是在DICOM文件还是DICOM通信中,其信息都是由许多data element(数据单元)的集合所表示,每个data element表示一个属性,如病人姓名、图像类型等等。这些data element按照Tag值从小到大依次连接,类似于数据结构的链表或者数组(SQ类型有另外的编码方式,以后会讲到),请看下图,一个data element包含四个字段Tag,VR,ValueLength,Value Field.
DICOMDIR 是一个可变长度 迷你 database 文件。由 group (0002, xxxx) 和 group (0004, xxxx) 为主题。描述的是一个 4 层的树状结构 (tree structure)。
1. Patient
2. Study
3. Series
4. Image
将data element的数据结构编码为字节流时受以下几个因素影响:
1.传输语法: Implicit/Explicit VR, BIG/LITTLE Endian
2.VR
当采用implicit VR时,其编码如下,这个时候是没有VR字段的,它采用data dictionary默认的VR.
DICOMDir结构的delphi实现:
1 /////////////////////////////////////////////////////////////////////////////// 2 // 3 // Filename: uDICOMDIR.pas 4 // 5 // Summary: 6 // DicomStation Source With Delphi7 7 // 8 // Modification History: 9 // Date By Summary 10 // -------- -------- --------------------------------------------- 11 // 07/24/2011 hegb Original Creation 12 /////////////////////////////////////////////////////////////////////////////// 13 unit uDICOMDIR; 14 15 interface 16 17 uses 18 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 19 Dialogs, StdCtrls; 20 21 type 22 //Instance Class 23 TImageItem = class(TObject) 24 NextDirRecordOffset: LongWord; //(0004,1400) 25 RecordInUseFlag: Word; //(0004,1410) 26 LowerLevelDirOffset: LongWord; //(0004,1420) 27 DirRecordType: string; //(0004,1430) 28 ReferenceFileID: string; //(0004,1500) 29 ReferenceTransSyntaxUID: string; //(0004,1512) 30 InstanceNumber: string; //(0020,0013) 31 public 32 constructor Create; 33 destructor Destroy; override; 34 end; 35 36 //Series Class 37 TSeriesItem = class(TObject) 38 NextDirRecordOffset: LongWord; //(0004,1400) 39 RecordInUseFlag: Word; //(0004,1410) 40 LowerLevelDirOffset: LongWord; //(0004,1420) 41 DirRecordType: string; //(0004,1430) 42 Modality: string; //(0008,0060) 43 SeriesInstanceUID: string; //(0020,000E) 44 SeriesNumber: string; //(0020,0011) 45 private 46 ImageList: TList; //Image for this Series 47 public 48 procedure AddImage(pImage: TImageItem); 49 50 constructor Create; 51 destructor Destroy; override; 52 end; 53 54 //Study Class 55 TStudyItem = class(TObject) 56 NextDirRecordOffset: LongWord; //(0004,1400) 57 RecordInUseFlag: Word; //(0004,1410) 58 LowerLevelDirOffset: LongWord; //(0004,1420) 59 DirRecordType: string; //(0004,1430) 60 StudyDate: string; //(0008,0020) 61 StudyTime: string; //(0008,0030) 62 AccessionNumber: string; //(0008, 0005) /////////(0008,0050) ===== SH 63 StudyInstanceUID: string; //(0020,000D) 64 StudyID: string; //(0020,0010) 65 private 66 SeriesList: TList; //Series for this Study 67 public 68 procedure AddSeries(pSeries: TSeriesItem); 69 70 constructor Create; 71 destructor Destroy; override; 72 end; 73 74 //Patient Structure 75 TPatientItem = class(TObject) 76 NextDirRecordOffset: LongWord; //(0004,1400) 77 RecordInUseFlag: Word; //(0004,1410) 78 LowerLevelDirOffset: LongWord; //(0004,1420) 79 DirRecordType: string; //(0004,1430) 80 PatientName: string; //(0010,0010) 81 PatientID: string; //(0010,0020) 82 private 83 StudyList: TList; //Study for this Patient 84 public 85 procedure AddStudy(pStudy: TStudyItem); 86 87 constructor Create; 88 destructor Destroy; override; 89 end; 90 91 //DicomDir Class 92 TDicomDir = class(TObject) 93 GroupLength: LongWord; //(0002,0000) 94 FileMetaVersion: string[2]; //(0002,0001) ====OB==== 95 MediaSOPClassUID: string; //(0002,0002) 96 MediaSOPInstanceUID: string; //(0002,0003) 97 TransferSyntaxUID: string; //(0002,0010) 98 ImplementClassUID: string; //(0002,0012) 99 ImplementVersionName: string; //(0002,0013) 100 FilesetID: string; //(0004,1130) 101 RootDirFistRecord: LongWord; //(0004,1200) 102 RootDirLastRecord: LongWord; //(0004,1202) 103 FileSetConsFlag: Word; //(0004,1212) 104 DirRecordSequence: LongWord; //(0004,1220) ====SQ==== 105 SOPClassUID: string; //(0008, 0016) 106 private 107 PatientList: TList; //Patient for DICOMDIR 108 public 109 procedure AddPatient(pPatient: TPatientItem); 110 procedure ClearPatient; 111 112 function WriteDicomDir(FilePath: string): Boolean; 113 constructor Create; 114 destructor Destroy; override; 115 end; 116 117 function GetEvenStr(const str: string): string; 118 119 var 120 g_pDICOMDIR: TDicomDir; 121 122 implementation 123 124 const 125 GroupTagLen = 2; //Group Number -- 2bytes 126 ElementTagLen = 2; //Element Number -- 2bytes 127 // VR2 = 2; //Explicit VR时,如果VR = OB,OW,OF,SQ,UT,UN时,VR占用2字节 128 // VR4 = 4; //Explicit VR时,如果VR <> OB,OW,OF,SQ,UT,UN时,VR占用4字节 129 // VR_Len2 = 2; //Explicit VR时,如果VR = OB,OW,OF,SQ,UT,UN时,Value Length占用2字节 130 // VR_Len4 = 4; //Explicit VR时,如果VR <> OB,OW,OF,SQ,UT,UN时,Value Length占用4字节 131 132 var 133 DICOM_signature: array[0..131] of byte = 134 ( 135 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 136 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 137 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 138 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 139 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 140 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 141 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 142 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 0, 0, 0 , 0, 143 68, 73, 67 , 77 144 ); 145 146 VR_Reserved: array[0..1] of Byte = (0, 0); 147 148 ItemBegin_Group: Word = $FFFE; 149 ItemBegin_Element: Word = $E000; 150 ItemEnd_Group: Word = $FFFE; 151 ItemEnd_Element: Word = $E00D; 152 ItemEnd_Sequence: Word = $E0DD; 153 154 ItemBegin_Value: LongWord = $FFFFFFFF; 155 ItemEnd_Value: LongWord = $00000000; 156 157 iOffSet: LongWord = 0; 158 159 procedure WriteDicomDir; 160 begin 161 end; 162 163 function SwapLong(Value: Cardinal): Cardinal; 164 asm 165 BSWAP EAX 166 end; 167 168 function GetEvenStr(const str: string): string; 169 begin 170 if Length(str) mod 2 <> 0 then 171 Result := str + Char(0) 172 else 173 Result := str; 174 end;