]> Creatis software - gdcm.git/blob - Doc/Website/HowToUseGdcm.html
Fix mistypings
[gdcm.git] / Doc / Website / HowToUseGdcm.html
1 <pre>
2 {{{
3 This is a vi-able/notepad-able document to (try to) explain in a few words 
4 how to use efficently gdcm.
5 -->Feel free to send/add your comments/suggestions/modifications.
6 -->Don't try to 'beautify' this page.
7 (I plane to rewrite it in html, as soon as there is enough 'content' within it)
8
9 HTH
10 Jean-Pierre Roux
11
12 ------------------------------------------------------------------------
13
14 0) Intro
15 ========
16 1) How to Read a DICOM file
17 ===========================
18 1-1) using raw C++
19 1-1-1) A single file
20 1-1-1-1) Deal with the file header
21 1-1-1-2) Load the 'pixels' in memory
22 1-1-1-3) Get the value of a single Dicom DataElement 
23 1-1-1-4) Get the values within a Dicom Sequence  
24 1-1-2) A File Set
25
26 1-2) using VTK
27 1-2-1) A single file
28 1-2-2) A File Set
29
30 1-3) using ITK
31 1-3-1) A single file
32 1-3-2) A File Set
33
34 1-4) Retrictions for Python users
35
36 2) How to write DICOM file
37 ==========================
38 2-1) using raw C++
39 2-1-1) A single file
40 2-1-1-1) Deal with optional DataElements
41 2-1-1-1-1) Add a single Dicom DataElement // TODO
42 2-1-1-1-2) Add a Dicom Sequence           // TODO
43 2-1-2) A File Set
44
45 2-2) using VTK
46
47 2-3) using ITK
48
49 2-4) Retrictions for Python users
50
51 3) DICOMDIR
52 ===========
53 3-1) How to read a DICIMDIR
54 3-2) How to modifiy a DICOMDIR
55 3-3) How to create a DICOMDIR
56
57 4) Some 'Command line' utilities
58 ================================
59 4-) PrintFile
60 4-) exSerieHelper
61 4-) exXCoherentFileSet
62
63 4-) AnonymizeNoLoad
64 4-) AnonymizeMultiPatient
65 4-) AnonymizeDicomDir
66
67 4-) ReWrite
68 4-) RawToDicom
69 4-) exMoveImagesToSingleSerieUID
70
71 4-) vtkgdcmViewer2
72 4-) vtkgdcmSerieViewer2
73
74 4-) PrintDicomDir
75 4-) MakeDicomDir
76
77 ----------------------------------------------------------------------------
78
79 0) Intro
80 ========
81
82 If you are not familiar with DICOM files, use :
83
84 PrintFile filein=yourDicomFile.dcm
85
86 and have a look at the output.
87 You'll see a lot of self explanatory (?) lines, e.g.
88
89 D 0008|0021 [DA]   [Series Date]     [20020524]
90 D 0008|0060 [CS]   [Modality]        [US]
91 D 0018|602c [FD]   [Physical Delta X][0]
92 D 0020|0013 [IS]   [Instance Number] [5 ]
93 D 0028|0010 [US]   [Rows]            [480]
94 D 0011|0010 [  ]   [gdcm::Unknown]   [DLX_PATNT_01]
95 D 0028|1222 [OW]   [Segmented Green Palette Color Lookup Table Data]
96                                  ===> [gdcm::Binary data loaded;length = 113784]
97
98 0008|0021 : 'Group Number'|'Element Number' -> The Element identifier
99 Have a look at gdcm/Dicts/dicomV3.dic for the whole list of Elements
100  
101 DA        : The 'Value Representation' : DA for Date, US for Unsigned Short, ...
102 Have a look at gdcm/Dicts/dicomVR.dic for the set of possible values
103
104 [Series Date] : The 'official' English name of the Element
105 (When *you* have to deal with a given Element, its meaning should be clear for
106 *you*)
107
108 [20020524] : the value, printed in a human readable way.
109
110 If something like :
111 D 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
112                                  ===> [gdcm::Binary data loaded;length = 113784]
113 is displayed, it means that it's a 'long' binary area, gdcm (I) decided not to
114 show.
115
116 D 0011|0010 [  ]   [gdcm::Unknown]   [DLX_PATNT_01]
117 is a 'Private (or Shadow) Element', depending on the manufacturer.
118 It's *not* known within the 'official' Dicom Dictionnary.
119 Except if someone told you, you cannot guess the meaning of such an element.
120 Probabely, you'll never have to deal with Shadow Elements (hope so!).
121
122 You can find also something like : 
123
124 S 0018|6011 [SQ]                       [Sequence of Ultrasound Regions]
125    |  --- SQItem number 0
126    | D fffe|e000 [UL]                               [Item]
127    | D 0018|6018 [UL]             [Region Location Min X0] [32]
128    | D 0018|601a [UL]             [Region Location Min Y0] [24]
129    | D 0018|601c [UL]             [Region Location Max X1] [335]
130    | D 0018|601e [UL]             [Region Location Max Y1] [415]
131    | D 0018|602c [FD]                   [Physical Delta X] [0.0382653]
132    | D 0018|602e [FD]                   [Physical Delta Y] [0.0382653]
133    |  --- SQItem number 1
134    | D fffe|e000 [UL]                               [Item]
135    | D 0018|6018 [UL]             [Region Location Min X0] [336]
136    | D 0018|601a [UL]             [Region Location Min Y0] [24]
137    | D 0018|601c [UL]             [Region Location Max X1] [639]
138    | D 0018|601e [UL]             [Region Location Max Y1] [415]
139    | D 0018|602c [FD]                   [Physical Delta X] [0.0382653]
140    | D 0018|602e [FD]                   [Physical Delta Y] [0.0382653]
141    |  --- SQItem number 2
142    | D fffe|e000 [UL]                               [Item]
143    | D 0018|6018 [UL]             [Region Location Min X0] [32]
144    | D 0018|601a [UL]             [Region Location Min Y0] [40]
145    | D 0018|601c [UL]             [Region Location Max X1] [63]
146    | D 0018|601e [UL]             [Region Location Max Y1] [103]
147    | D 0018|6024 [US]         [Physical Units X Direction] [0]
148    | D 0018|6026 [US]         [Physical Units Y Direction] [0]
149    | D 0018|602c [FD]                   [Physical Delta X] [0]
150    | D 0018|602e [FD]                   [Physical Delta Y] [0]
151
152 0018|6011 is a 'Sequence' (SQ), composed of various Sequence Items(SQItem)
153 Each SQItem is a set of Elements (an Element may be a DataElement (D) or a
154 Sequence (S), recursively, within any level of embedding :
155
156 S 0029|263d [SQ]                                                                   []
157    |  --- SQItem number 0
158    | D fffe|e000 [UL]                                                              [Item ]
159    | D 0008|0000 [UL]                                                       [Group Length] [12]
160    | D 0008|0001 [UL]                                               [Length to End (RET) ] [28776]
161    | D 0029|0000 [UL]                                                       [Group Length] [28764]
162    | D 0029|002a [LO]                                                    [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
163    | D 0029|2a02 [LO]                                                                   [] [PERFUSION_MR_T2STAR_GAMMA_VARIATE_ANALYSER]
164    | S 0029|2a06 [SQ]                                                                   []
165    |    |  --- SQItem number 0
166    |    | D fffe|e000 [UL]                                                              [Item ]
167    |    | D 0008|0000 [UL]                                                       [Group Length] [12]
168    |    | D 0008|0001 [UL]                                               [Length to End (RET) ] [20]
169    |    | D fffe|0000 [UL]                                                       [Group Length]
170    |    |  --- SQItem number 1
171    |    | D fffe|e000 [UL]                                                              [Item ]
172    |    | D 0008|0000 [UL]                                                       [Group Length] [12]
173    |    | D 0008|0001 [UL]                                               [Length to End (RET) ] [194]
174    |    | D 0029|0000 [UL]                                                       [Group Length] [182]
175    |    | D 0029|002a [LO]                                                    [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
176    |    | S 0029|2a07 [SQ]                                                                   []
177    |    |    |  --- SQItem number 0
178    |    |    | D fffe|e000 [UL]                                                              [Item ]
179    |    |    | D 0008|0000 [UL]                                                       [Group Length] [12]
180    |    |    | D 0008|0001 [UL]                                               [Length to End (RET) ] [100]
181    |    |    | D 0029|0000 [UL]                                                       [Group Length] [88]
182    |    |    | D 0029|002a [LO]                                                    [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
183    |    |    | D 0029|2a0a [US]                                                                   [] [1]
184    |    |    | D 0029|2a0b [US]                                                                   [] [2]
185    |    |    | D 0029|2a0c [US]                                                                   [] [2]
186    |    |    | D 0029|2a0d [US]                                                                   [] [1]
187    |    |    | D 0029|2a10 [US]                                                                   [] [1]
188    |    |    | S 0029|2a2e [SQ]                                                                   []
189    |    |    |    |  --- SQItem number 0
190    |    |    |    | D fffe|e000 [UL]                                                              [Item ]
191    |    |    |    | D 0008|0000 [UL]                                                       [Group Length] [12]
192    |    |    |    | D 0008|0001 [UL]                                               [Length to End (RET) ] [2266]
193    |    |    |    | D 0029|0000 [UL]                                                       [Group Length] [2254]
194    |    |    |    | D 0029|0025 [LO]                                                    [Private Creator] [SPI-P-Private_ICS Release 1;1 ]
195    |    |    |    | D 0029|0027 [LO]                                                    [Private Creator] [SPI-P-Private_ICS Release 1;3 ]
196    |    |    |    | D 0029|0028 [LO]                                                    [Private Creator] [SPI-P-Private_ICS Release 1;4 ]
197    |    |    |    | D 0029|2500 [SL]                                                                   [] [43]
198    |    |    |    | D 0029|256b [FD]                                                                   [] [0.5]
199    |    |    |    | D 0029|2700 [LO]                                                                   [] [L1]
200    |    |    |    | D 0029|276a [FL]                                                                   [] [0.35]
201    |    |    |    | S 0029|27c0 [SQ]                                                                   []
202    |    |    |    |    |  --- SQItem number 0
203    |    |    |    |    | D fffe|e000 [UL]                                                              [Item ]
204    |    |    |    |    | D 0008|0000 [UL]                                                       [Group Length] [12]
205    |    |    |    |    | D 0008|0001 [UL]                                               [Length to End (RET) ] [110]
206    |    |    |    |    | D 0029|0000 [UL]                                                       [Group Length] [98]
207    |    |    |    |    | D 0029|0027 [LO]                                                    [Private Creator] [SPI-P-Private_ICS Release 1;3 ]
208    |    |    |    |    | D 0029|27b0 [SL]                                                                   [] [0]
209    |    |    |    |    | D 0029|27b1 [FL]                                                                   [] [0]
210    |    |    |    |    | D 0029|27b2 [FL]                                                                   [] [0]
211    |    |    |    |    | D 0029|27b4 [FL]                                                                   [] [0]
212    |    |    |    |    | D 0029|27b9 [FL]                                                                   [] [1]   
213    | S 0029|2a14 [SQ]                                                                   []
214    |    |  --- SQItem number 0
215    |    | D fffe|e000 [UL]                                                              [Item ]
216    |    | D 0008|0000 [UL]                                                       [Group Length] [12]
217    
218 Probabely, you'll never have to deal with Sequences (hope so!).
219
220 1) How to Read a DICOM File
221 ===========================
222
223 1-1) using raw C++
224 ------------------
225
226 1-1-1) A single file
227 --------------------
228
229 1-1-1-1) Deal with the file header
230
231 The first step is to load the file header :
232            gdcm::File *f = gdcm::File::New();
233                   f->SetLoadMode(LD_NOSEQ);              | depending on what
234                   f->SetLoadMode(LD_NOSHADOW);           | you want *not* 
235                   f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW);| to load from the
236                   f->SetLoadMode(LD_NOSHADOWSEQ);        | target file
237            f->SetFileName(fileName);
238            f->Load( );
239
240 Note :
241 The 'long' Data Element (>4096 char) are not loaded by default
242 You may modify this default length :
243            f->SetMaxSizeLoadEntry(int yourOwnMaxSize);
244 Or ask to force the loading of given Data Elements, whatever their size is:  
245             f->AddForceLoadElement (uint16_t group, uint16_t elem);
246 before calling gdcm::File::Load();
247
248 Note :
249 Except if you are really aware about it, do *not* use SetLoadMode().
250 LD_ALL is the default.
251
252 Check if the file is gdcm-readable.
253
254            if ( !f->IsReadable() )
255              std::cout << "major troubles on [" << f->GetFileName() <<"]"
256                        << std::endl;
257
258 Decide if this is a 'File Of Interest' for you.
259 Check some fields, e.g
260
261            std::string StudyDate           = f->GetEntryString(0x0008,0x0020);
262            std::string PatientName         = f->GetEntryString(0x0010,0x0010);
263            std::string PatientID           = f->GetEntryString(0x0010,0x0020);
264            std::string PatientSex          = f->GetEntryString(0x0010,0x0040);
265            std::string Modality            = f->GetEntryString(0x0008,0x0060);
266
267 (or whatever you feel like ...)
268
269 1-1-1-2) Load the 'pixels' in memory
270
271 Next step is to load the pixels in memory.
272 Uncompression (JPEG lossless, JPEG lossy, JPEG 2000, RLE, ...) 
273 will be automatically performed if necessary.
274
275 You first have to create a 'File Helper'
276
277            gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
278
279 In some images,(where BitsAllocated=16, BitsUsed=8), 
280 the 'unused bits' are actually ... used for storing 'overlays'.
281 (e.g. : Patient / Aquisition / Institution informations, or drawings ...)
282 It's up to you to decide whether you want or not to load the overlays.
283 (Probabely, if you want to post-process the images, you dont' want!)
284
285            fh->SetKeepOverlays(true);  // default is : false
286
287            void *imageData = fh->GetImageDataRaw();
288            uint32_t dataSize = fh->GetImageDataRawSize();
289
290 Generally, you work on 'Grey level' images (as opposed to RGB images).
291 Depending on the Pixel size (8/16/32 bits) and the Pixel Type (signed/unsigned),
292 you cast the imageData
293
294           std::string pixelType = f->GetPixelType();
295   
296 Possible values are : 
297
298 - "8U"  unsigned  8 bit,
299 - "8S"    signed  8 bit,
300 - "16U" unsigned 16 bit,
301 - "16S"   signed 16 bit,
302 - "32U" unsigned 32 bit,
303 - "32S"   signed 32 bit,
304 - (NEITHER 'float' NOR 'double' pixels in DICOM!)
305    
306            int dimX = f->GetXSize();
307            int dimY = f->GetYSize();
308            int dimZ = f->GetZSize(); // meaningfull only for 'Volumes' or 'multiframe files'
309            int dimT = f->GetTSize(); // meaningfull only for 4D objects (?)
310
311 Sometimes, you deal with 'colour' images :-(
312 They may be stored, on disc, as :
313      RGB pixels, 
314      RGB planes, 
315      YBR pixels, 
316      YBR planes
317      Grey level images + LUT.
318
319 You'll get an 'RGB Pixels' image in memory if you use: 
320
321            gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
322            void *imageData = fh->GetImageData();
323            uint32_t dataSize = fh->GetImageDataSize();
324
325 If you don't want to convert a "Grey level images + LUT" into "RGB Pixel image" use
326            gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
327            void *imageData = fh->GetImageDataRaw();
328            uint32_t dataSize = fh->GetImageDataRawSize();
329    
330 Now, you can enjoy your image !   
331
332 1-1-1-3) Get the value of a single Dicom DataElement 
333
334 1-1-1-3-1) as a std::string
335
336 - some DataEntries are 'human readable' (those whose VR is AE, DA, DS, PN, SH, TM)
337   Get their value using group number-element number :
338   std::string patientName = f->GetEntryString(0x0010,0x0010);
339   
340 - Some DataEntries are stored with their own binary representation, but maybe you feel like 
341   to get them in a 'human readable' form (those whose VR is FL, FD, SL, SS, UL, US)
342   Get their value using group number-element number : 
343   std::string rowNumber =f->GetEntryString(0x0028, 0x0010);// nb of Rows
344   
345   (of course, the very often used DataEntries have their own accessors :
346   e.g. GetXsize, GetYSize, GetSpacing, GetImageOrientationPatient, GetImagePositionPatient,
347        GetRescaleSlope, GetRescaleIntercept, GetNumberOfScalarComponents, etc -see gdcmFile.h-)
348   
349
350 1-1-1-3-3) as a void* pointer
351 - Some DataEntries are stored with their own binary representation, and you want to get them 'as they are'.
352   You will have to cast them, according to the knowledge you have about them.
353   LutRedData = (uint8_t*)f->GetEntryBinArea( 0x0028, 0x1201 ); 
354    
355 1-1-1-4) Get the value(s) within a Dicom Sequence           
356
357 Actually, a 'Dicom Sequence' is composed of a list a 'Sequence Items',
358 each Sequence Item is a set of DataElement (that can be Dicom Sequences, recursively).
359 You have to get the Sequence element, to get its number of Sequence items, to iterate on each one.
360 e.g.:
361
362 SeqEntry *seqEntry = f->GetSeqEntry(0x3006,0x0020); //Structure Set ROI sequence
363 unsigned int n = seqEntry->GetNumberOfSQItems();    // useless : just to see !
364 currentItem = seqEntry->GetFirstSQItem(); // Get the first ROI
365 while (currentItem != NULL) {
366    std::string roiName = currentItem->GetEntryString(0x3006,0x0026);  //ROI name
367    std::string roiDescr = currentItem->GetEntryString(0x3006,0x0028); //ROI description
368    ...
369    // do what you want with the current ROI
370    currentItem = seqEntry->GetNextSQItem(); // Get the next ROI
371 }
372
373
374 1-1-2) A File Set
375 -----------------
376
377 If you are 150 % sure of the files you're dealing with, just read the files you
378 feel like, and concatenate the pixels.
379
380 Sometimes you are not sure at all (say : you were given a CDROM with an amount
381 of images, laying in a Directories tree-like structure, you don't know anything
382 about the Patients, and so on)
383
384 A class gdcm::SerieHelper is designed to help solving this problem.
385 Use it as follows :
386
387     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
388     while (int i=0; i < nbOfFiles; i++) {
389        sh->AddFileName(currentFileName[i]);
390     }
391     
392 You can also pass a 'root directory', and ask or not for recursive parsing.
393     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
394     sh->SetDirectory(yourRootDirectoryName, true); // true : recursive parsing
395
396 Files are 'splitted' into as many 'Single Serie UID File Set' 
397   as Series Instance UID ( 0020|000e );
398
399 // -------- skip this one, for a first reading ! -----------
400
401 Sometimes, the Serie UID is not enough to disseminate properly the images.
402 We may want to disseminate into multiple sub series when needed.
403
404 Use :
405 void SerieHelper::SetUseSeriesDetails(bool s);
406    /// This function will add the following DICOM tag as being part of a
407    /// 'fake' uid. :
408    /// 0020 0011 Series Number
409    /// 0018 0024 Sequence Name
410    /// 0018 0050 Slice Thickness
411    /// 0028 0010 Rows
412    /// 0028 0011 Columns 
413
414 If it's not enough for you, use :    
415 void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
416
417 std::string SerieHelper::CreateUniqueSeriesIdentifier(gdcm::File *inFile);
418 void SerieHelper::CreateDefaultUniqueSeriesIdentifier();
419
420 You may also create a "tokenizable' File Identifier of your own, using :
421 void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
422 and
423 std::string SerieHelper::CreateUserDefinedFileIdentifier(gdcm::File *inFile);
424 and use it as you feel like.
425
426 // ------------ Resume reading, here !--------------------
427  
428 If you want to 'order' the files within each 'Single Serie UID File Set'
429 (to build a volume, for instance), use :
430   
431    gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
432    while (l)
433    { 
434       sh->OrderFileList(l);  // sort the list
435       l = sh->GetNextSingleSerieUIDFileSet();
436    } 
437     
438   The sorting will be performed on the ImagePositionPatient;
439   if not found, on ImageNumber;
440   if not found, on the File Name.
441   
442   Aware user is allowed to pass his own comparison function 
443   (if he knows, for instance, the files must be sorted on 'Trigger Time')
444   He will use the method
445   void SerieHelper::SetUserLessThanFunction( bool(*) userFunc((File *,File *) ); 
446   He may ask for a reverse sorting : 
447   sh->SetSortOrderToReverse();
448   or back to Direct Order 
449   sh->SetSortOrderToDirect();
450   
451   If, for any reason of his own, user already get the file headers,
452   he may add the gdcm::File (instead of the file name) to the SerieHelper.
453   (Sorry, not available in Python)
454
455     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
456     while (int i=0; i < nbOfFiles; i++) {
457        sh->AddFile(currentFile[i]);
458     }                           
459  * \warning : this method should be used by aware users only!
460  *            User is supposed to know the files he want to deal with
461  *           and consider them they belong to the same Set
462  *           (even if their Serie UID is different)
463  *           user will probabely OrderFileList() this list (actually, ordering
464  *           user choosen gdm::File is the sole interest of this method)
465  *           Moreover, using vtkGdcmReader::SetCoherentFileList() will avoid
466  *           vtkGdcmReader parsing twice the same files.
467  *           *no* coherence check is performed, but those specified
468  *           by SerieHelper::AddRestriction()
469  
470    User may want to exclude some files.
471    He will use
472     void SerieHelper::AddRestriction(uint16_t group, uint16_t elem,
473                                  std::string const &value, int op);
474 op belongs to :
475
476 /// \brief comparison operators
477    GDCM_EQUAL ,
478    GDCM_DIFFERENT,
479    GDCM_GREATER,
480    GDCM_GREATEROREQUAL,
481    GDCM_LESS,
482    GDCM_LESSOREQUAL
483 e.g. 
484     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
485     sh->AddRestriction(0x0010,0x0040,"F",GDCM_EQUAL);      // Patient's Sex
486     sh->AddRestriction(0x0008,0x0060,"MR",GDCM_DIFFERENT); // Modality 
487     while (int i=0; i < nbOfFiles; i++) { 
488     ...
489 User wants to deal only with Female patient, any Modality but MR (why not?)
490    
491 Maybe user knows there are several images with the same position
492 and *no dicom field* may discriminates them.
493 He wants to drop the 'duplicate images'
494    
495      sh->SetDropDuplicatePositions(true);
496
497 Sometimes the previous stuff is *not enough* !
498
499 Within a SingleSerieUIDFileSet, you can have have various orientations,
500 or various positions, at various times. (not only various positions, at a single
501 time, for a single orientation).
502
503 User may consider that dealing only with the 'Series Instance UID' 
504 is not enough and wishes to 'refine' the image selection :
505
506 Suppose he has a Single Serie UID File Set (gdcm::FileList).
507 He may ask to split it into several 'X Coherent File Sets' (X stands for
508 'Extra').
509
510 gdcm::SerieHelper *s;
511 gdcm::XCoherentFileSetmap xcm;
512 gdcm::FileList *l = s->GetFirstSingleSerieUIDFileSet(); // or what you want
513
514
515  The following methods must be called by user, depending on 
516  what *he* wants to do, at application time.
517   - *he* only  knows what his Series contain ! - 
518  They return a std::map of Filesets (actually : a std::map of std::vector<gdcm::File* >).
519  
520 He may ask for 'splitting' on the Orientation:
521            gdcm::XCoherentFileSetmap xcm = s->SplitOnOrientation(l);
522    
523 He may ask for 'splitting' on the Position:
524            gdcm::XCoherentFileSetmap xcm = s->SplitOnPosition(l);
525    
526 He may ask for 'splitting' on the any DataElement you feel like :   
527            gdcm::XCoherentFileSetmap xcm = s->SplitOnTagValue(l, group,elem);
528
529  
530 He can now work on each 'X Coherent File Set' within the std::map
531
532 for (gdcm::XCoherentFileSetmap::iterator i = xcm.begin();
533                                          i != xcm.end();
534                                        ++i)
535 {
536
537    // ask for 'ordering' according to the 'Image Position Patient'
538    // Sorting the Fileset (*) is mandatory!
539    // ( computing an accurate Series ZSpacing -whenever possible- is a side effect ...)
540      
541    s->OrderFileList((*i).second);  // sort the XCoherent Fileset
542 }  
543
544 (have a look at gdcm/Examples/exXCoherentFileSet.cxx for an example)
545
546 1-2) using VTK
547 --------------
548 a vtkGdcmReader() method ( derived from vtkReader() ) is available.
549
550 1-2-1) A single file
551 --------------------
552
553    vtkGdcmReader *reader = vtkGdcmReader::New();
554    reader->SetFileName( yourDicomFilename );      
555    reader->SetLoadMode( yourLoadMode );   // See C++ part 
556    reader->SetKeepOverlays( true/false ); // See C++ part    
557    reader->Update();
558    vtkImageData* ima = reader->GetOutput();
559    int* Size = ima->GetDimensions(); 
560      
561  // -> Enjoy it.     
562
563 1-2-2) A File Set
564 -----------------
565
566 If you are 150 % sure of the files you're dealing with, just 'add' the files you
567 feel like:
568
569    vtkGdcmReader *reader = vtkGdcmReader::New();
570    for(int i=1; i< yourNumberOfFiles; i++)
571          reader->AddFileName( yourTableOfFileNames[i] );     
572    reader->SetLoadMode( yourLoadMode );   // See C++ part 
573    reader->SetKeepOverlays( true/false ); // See C++ part     
574    reader->Update();
575    vtkImageData* ima = reader->GetOutput();
576    int* Size = ima->GetDimensions();
577    
578  // -> Enjoy it.
579  
580  Warning : The first file is assumed to be the reference file.
581  All the inconsistent files (different sizes, pixel types, etc) are discarted
582  and replaced by a black image ! 
583  
584       User is allowed to pass a Pointer to a function of his own
585       to allow modification of pixel order (i.e. : Mirror, UpsideDown, )
586       to gdcm::FileHeleper, using SetUserFunction(userSuppliedFunction)
587
588       described as : void userSuppliedFunction(uint8_t *im, gdcm::File *f);
589
590       NB : the "uint8_t *" type of first param is just for prototyping.
591         User will Cast it according what he found with f->GetPixelType()
592         See vtkgdcmSerieViewer for an example
593  
594
595 Many users expect from vtkGdcmReader it 'orders' the images 
596 (Actually, that's the job of gdcm::SerieHelper ...)
597 When user knows the files with same Serie UID have same sizes, 
598 same 'pixel' type, same color convention, ... 
599 the right way to proceed is as follow :
600
601         gdcm::SerieHelper *sh= gdcm::NewSerieHelper();
602    //      if user wants *not* to load some parts of the file headers
603         sh->SetLoadMode(yourLoadMode);
604
605    //      if user wants *not* to load some files
606         sh->AddRestriction(group, element, value, operator);
607         sh->AddRestriction( ...
608         sh->SetDirectory(directoryWithImages);
609
610    //      if user *knows* how to order his files
611         sh->SetUserLessThanFunction(userSuppliedComparisonFunction);
612    //      or/and
613    //      if user wants to sort reverse order
614         sh->SetSortOrderToReverse();
615    
616    //      here, we suppose only the first 'Single SerieUID' Fileset is of interest
617    //      Just iterate using sh->NextSingleSerieUIDFileSet()
618    //      if you want to get all of them
619         gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
620
621    //      if user is doesn't trust too much the files with same Serie UID
622         if ( !sh->IsCoherent(l) )
623            return; // not same sizes, or not same 'pixel type' -> stop
624    
625    // Maybe user knows there are several images with the same position
626    // and *no dicom field* may discriminates them.
627    // He wants to drop the 'duplicate images'
628    
629         sh->SetDropDuplicatePositions(true); 
630
631    // Sorting the Fileset (*) is mandatory!
632    // ( computing an accurate Series ZSpacing is a side effect ...)  
633         sh->OrderFileList(l);
634
635         vtkGdcmReader *reader = vtkGdcmReader::New();
636    //      if user wants to modify pixel order (Mirror, TopDown, ...)
637    //      he has to supply the function that does the job
638    //      (a *very* simple example is given in vtkgdcmSerieViewer.cxx)
639         reader->SetUserFunction (userSuppliedFunction);
640
641    //      to pass a 'Single SerieUID' Fileset as produced by gdcm::SerieHelper
642         reader->SetCoherentFileList(l);
643         reader->Update();
644
645
646 //-----------------
647 (*)
648 User may also pass an 'X Coherent Fileset', created by one of the following
649 methods : (see 1-1-2 for more details)
650  
651            xcm = sh->SplitOnOrientation(l); 
652            xcm = sh->SplitOnPosition(l);   
653            xcm = sh->SplitOnTagValue(l, groupelem[0],groupelem[1]);
654 //----------------
655
656
657 You can see a full example in vtk/vtkgdcmSerieViewer2.cxx
658 e.g.
659 vtkgdcmSerieViewer dirname=Dentist mirror
660 vtkgdcmSerieViewer dirname=Dentist reverse
661 vtkgdcmSerieViewer dirname=Dentist reverse upsidedown
662
663
664 1-4) Retrictions for Python users
665 ---------------------------------
666
667 None of the methods receiving a function pointer, or a gdcm::File as a parameter
668 is wrapable by swig.
669 :-(
670
671
672 2) How to write DICOM file
673 ==========================
674
675 2-1) using raw C++
676 ------------------
677
678 2-1-1) A single file
679 --------------------
680 In C++, if you have already the pixels in main memory, 
681 you just have to process as follow :
682
683 --> Create an empty gdcm::File
684         gdcm::File *file = gdcm::File::New();
685
686         std::ostringstream str;
687
688 // --> Set the mandatory fields
689   // Set the image size
690         str.str("");
691         str << sizeY;
692         file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
693         str.str("");
694         str << sizeX;
695         file->InsertEntryString(str.str(),0x0028,0x0011,"US"); // Columns
696         str.str("");
697         str << sizeZ;
698         file->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Nbr of Frames
699   // Set the pixel type
700         str.str("");
701         str << componentSize; //8, 16, 32
702         file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
703         str.str("");
704         str << componentUse; // may be 12 or 16 if componentSize =16
705         file->InsertEntryString(str.str(),0x0028,0x0101,"US"); // Bits Stored
706         str.str("");
707         str << componentSize - 1 ;
708         file->InsertEntryString(str.str(),0x0028,0x0102,"US"); // High Bit
709   // Set the pixel representation // 0/1
710         str.str("");
711         str << img.sign;
712         file->InsertEntryString(str.str(),0x0028,0x0103, "US"); // Pixel Representation
713   // Set the samples per pixel // 1:Grey level, 3:RGB
714         str.str("");
715         str << components;
716         file->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
717
718 //--> Set Optional fields
719       see further how to deal with optional fields
720
721 //--> Create a gdcm::FileHelper
722        gdcm::FileHelper *fileH = gdcm::FileHelper::New(file);
723
724 //--> Tell the FileHelper what you did for creating the image:
725
726       // gdcm cannot guess how user built his image 
727       //  (and therefore cannot be clever about some Dicom fields)
728       // It's up to the user to tell gdcm what he did.
729       // -1) user created ex nihilo his own image and wants to write it 
730       //     as a Dicom image.
731       // USER_OWN_IMAGE
732       // -2) user modified the pixels of an existing image.
733       // FILTERED_IMAGE
734       // -3) user created a new image, using existing a set of images 
735       //    (eg MIP, MPR, cartography image)
736       //  CREATED_IMAGE
737       // -4) user modified/added some tags *without processing* the pixels 
738       //     (anonymization, ...)
739       //  UNMODIFIED_PIXELS_IMAGE
740       // -Probabely some more to be added
741       //(see gdcmFileHelper.h for more explanations)
742
743       // Use :
744       
745       fileH->SetContentType(GDCM_NAME_SPACE::USER_OWN_IMAGE);
746       fileH->SetContentType(GDCM_NAME_SPACE::FILTERED_IMAGE);
747       fileH->SetContentType(GDCM_NAME_SPACE::CREATED_IMAGE);
748       fileH->SetContentType(GDCM_NAME_SPACE::UNMODIFIED_PIXELS_IMAGE);
749       
750       // depending on what you did before!
751
752 //--> Set the compression type : 
753       fileH->SetWriteTypeToJPEG();      // lossless compression        
754       fileH->SetWriteTypeToJPEG2000();  // lossless compression 
755       fileH->SetWriteTypeToDcmExplVR(); // Explicit Value Representation (no compression)
756       fileH->SetWriteTypeToDcmImplVR(); // Implicit Value Representation (no compression)
757       
758       fileH->SetWriteModeToRaw();       // Probabely you don't want to convert any LUT into RGB pixels ...
759
760 //--> Set the Image Data
761        fileH->SetImageData((unsigned char *)imageData,size);
762       // ( Casting as 'unsigned char *' is just to avoid warnings.
763       // It doesn't change the values. )
764       // or
765        fileH->SetUserData((unsigned char *)imageData,size); // performs compression, when required
766       // ( Casting as 'unsigned char *' is just to avoid warnings.
767       // It doesn't change the values. )
768                   
769 //-> Write !      
770       fileH->Write(fileName.str());
771
772 //This works for a single image (singleframe or multiframe)
773
774 2-1-1-1) Deal with optional DataElements          // TODO : finish it
775
776     Any Data Element may be added (it's up to the user to understand what he is doing!)
777     The supplied methods 'InsertXxx' will create the DataElement or replace it if it already exists.  
778     Have a look at gdcm/Dict/dicomV3.dic to see what are the various DICOM fields, with their VR.
779     
780 2-1-1-1-1) Add a single Dicom DataElement          // TODO : finish it
781
782    use :
783    DataEntry * File::InsertEntryString(std::string const &value,
784                                    uint16_t group, uint16_t elem,
785                                    VRKey const &vr = GDCM_VRUNKNOWN);
786   
787    // (e.g. : patient name, patient ID, ... , or what you want,
788    //  using their Dicom identifier, and 'VR'
789       file->InsertEntryString("MyOwnPatient" ,0x0010,0x0010,"PN"); // 0010 0010 : Patient's Name
790    
791     DataEntry * File:InsertEntryBinArea(uint8_t *binArea, int lgth,
792                                     uint16_t group, uint16_t elem,
793                                     VRKey const &vr = GDCM_VRUNKNOWN);  
794    
795 2-1-1-1-2) Add a Dicom Sequence           // TODO : finish it
796    SeqEntry * File::InsertSeqEntry(uint16_t group, uint16_t elem);
797    
798 2-1-2) A File Set
799 -----------------
800 /// \todo : write it!
801
802
803 // If you deal with a Serie of images, it up to you to tell gdcm, for each image,
804 // what are the values of
805 // 0020 0032 DS 3 Image Position (Patient)
806 // 0020 0037 DS 6 Image Orientation (Patient)
807
808 // You will probabely want that all the images of your file set belong to the same 'Serie'
809
810
811 2-2) using VTK
812 --------------
813
814 /// \todo : write it!
815
816 2-2-1) A single file
817 --------------------
818
819 /// \todo : finish it!
820
821 // User of the CVS version of VTK 5 may set some 'Medical Image Properties'
822 // Only the predefined DataElements are available :
823 // PatientName, PatientID, PatientAge, PatientSex, PatientBirthDate, StudyID
824 // It's reasonably enough for any 'decent use'
825 //
826 // todo : explain how to use it.
827 //vtkMedicalImageProperties
828
829    // Aware user is allowed to pass his own gdcm::File *, 
830    //  so he may set *any Dicom field* he wants.
831    // (including his own Shadow Elements, or any gdcm::SeqEntry)
832    // gdcm::FileHelper::CheckMandatoryElements() will check inconsistencies,
833    // as far as it knows how.
834    // Sorry, not yet available under Python.
835    
836      vtkSetMacro(GdcmFile, gdcm::File *);
837    
838 void vtkGdcmWriter::SetGdcmFile(gdcm::File *);
839
840    // gdcm cannot guess how user built his image 
841    //  (and therefore cannot be clever about some Dicom fields)
842    // It's up to the user to tell gdcm what he did.
843    // -1) user created ex nihilo his own image and wants to write it 
844    //     as a Dicom image.
845    // USER_OWN_IMAGE
846    // -2) user modified the pixels of an existing image.
847    // FILTERED_IMAGE
848    // -3) user created a new image, using existing a set of images 
849    //    (eg MIP, MPR, cartography image)
850    //  CREATED_IMAGE
851    // -4) user modified/added some tags *without processing* the pixels 
852    //     (anonymization..
853    //  UNMODIFIED_PIXELS_IMAGE
854    // -Probabely some more to be added
855    //(see gdcmFileHelper.h for more explanations)
856    
857    // User is allowed to use the following methods:
858    
859 void vtkGdcmWriter::SetContentTypeToUserOwnImage()                           
860 void vtkGdcmWriter::SetContentTypeToFilteredImage()                            
861 void vtkGdcmWriter::SetContentTypeToUserCreatedImage()                        
862 vtkGdcmWriter::void SetContentTypeToUnmodifiedPixelsImage()
863    
864   // depending on what he did before (see C++ part)
865   
866                       
867
868 2-2-2) A File Set
869 -----------------
870 /// \todo : write it!
871
872
873 2-3) using ITK
874 --------------
875 /// \todo : write it!
876
877 2-4) Retrictions for Python users
878 ---------------------------------
879 /// \todo : write it!
880
881
882 3) DICOMDIR /// \todo: finish it!
883 ===========
884 3-1) How to read a DICIMDIR
885 3-2) How to modifiy a DICOMDIR
886 3-3) How to create a DICOMDIR
887
888
889 4) Some 'Command line' utilities  /// \todo: finish it!
890 ================================
891
892 4-) PrintFile
893 4-) exSerieHelper
894 4-) exXCoherentFileSet
895
896 4-) AnonymizeNoLoad
897 4-) AnonymizeMultiPatient
898 4-) AnonymizeDicomDir
899 4-) PatchHeader
900 4-) ReWrite
901 4-) RawToDicom
902 4-) exMoveImagesToSingleSerieUID
903
904 4-) vtkgdcmViewer2
905 4-) vtkgdcmSerieViewer2
906
907 4-) PrintDicomDir
908 4-) MakeDicomDir
909
910
911     *   PrintFile
912     
913      Display the header of a ACR-NEMA/PAPYRUS/DICOM File                      
914      usage: PrintFile {filein=inputFileName|dirin=inputDirectoryName}[level=n]
915                        [forceload=listOfElementsToForceLoad] [rec] [noex] 
916                        [4DLoc= ][dict= privateDirectory]                  
917                        [ { [noshadowseq] | [noshadow][noseq] } ]          
918                        [debug] [warning]                                  
919       level = 0,1,2 : depending on the amount of details user wants to see
920       rec : user wants to parse recursively the directory                 
921       noex : user doen't want extra 'user friendly' info                  
922       4DLoc: group-elem(in hexa, no space) of the DataEntry holdind 4thDim
923       listOfElementsToForceLoad : group-elem,g2-e2,... (in hexa, no space)
924                                 of Elements to load whatever their length 
925       privateDirectory : source file full path name of Shadow Group elems 
926       noshadowseq: user doesn't want to load Private Sequences            
927       noshadow   : user doesn't want to load Private groups (odd number)  
928       noseq      : user doesn't want to load Sequences                    
929       debug      : user wants to run the program in 'debug mode'          
930       warning    : user wants to be warned about any oddity in the File   
931       showlut :user wants to display the Palette Color (as an int array)  
932
933     * Anonymize
934
935          Anonymizes a full gdcm-readable Dicom image
936                 Warning : probably segfaults if pixels are not gdcm readable.
937                           Use AnonymizeNoLoad instead.
938          usage: Anonymize filein=inputFileName fileout=anonymizedFileName [debug][usage]
939                 debug    : user wants to run the program in 'debug mode'
940                 usage    : user wants to display usage
941         
942
943     * AnonymizeNoLoad
944
945        
946          Anonymizes a gdcm-readable Dicom image even if pixels aren't gdcm readable
947                Warning : the image is overwritten;
948                          to preserve its integrity, use a copy.
949          usage: AnonymizeNoLoad {filein=inputFileName|dirin=inputDirectoryName}
950                                 [rubout=listOfPrivateElementsToRubOut]
951                                 [ { [noshadowseq] | [noshadow][noseq] } ] [debug]
952                 inputFileName : Name of the (single) file user wants to anonymize
953                 inputDirectoryName : user wants to anonymize *all* the files
954                                      within the (single Patient!) directory
955                 listOfElementsToRubOut : group1-elem1,g2-e2,... (in hexa)
956                                          of extra Elements to rub out
957                 noshadowseq: user doesn't want to load Private Sequences
958                 noshadow   : user doesn't want to load Private groups (odd number)
959                 noseq      : user doesn't want to load Sequences
960                 debug      : user wants to run the program in 'debug mode'
961                 usage      : user wants to display usage
962          
963
964     * ReWrite
965
966  Re write a full gdcm-readable Dicom image                              
967      (usefull when the file header is not very straight).               
968                                                                         
969  usage: ReWrite filein=inputFileName fileout=outputFileName             
970        [keepoverlays] [mode=write mode] [monochrome1]                   
971        [noshadow] [noseq][debug]                                        
972   --> The following line to 'rubout' a burnt-in Patient name            
973        [rubout=xBegin,xEnd,yBegin,yEnd [ruboutvalue=n (<255)] ]         
974   --> The 2 following lines, to extract a sub image within some frames  
975        [ROI=xBegin,xEnd,yBegin,yEnd]                                    
976        [firstframe=beg] [lastframe=end]                                 
977                                                                         
978         mode = a (ACR), x (Explicit VR Dicom), r (RAW : only pixels)    
979                j (jpeg lossless), 2 (jpeg2000)                          
980         keepoverlays : user wants to keep ACR-NEMA-like overlays        
981         monochrome1 = user wants MONOCHROME1 photom. interp. (0=white)  
982         noshadowseq: user doesn't want to load Private Sequences        
983         noshadow : user doesn't want to load Private groups (odd number)
984         noseq    : user doesn't want to load Sequences                  
985         rgb      : user wants to transform LUT (if any) to RGB pixels   
986         warning  : developper wants to run the program in 'warning mode'
987         debug    : developper wants to run the program in 'debug mode'          
988
989     * PrintDicomDir
990
991          Displays the tree-like structure of a DICOMDIR File
992          usage: PrintDicomDir filein=fileName [detail=n] [level=n] [debug] [usage]
993                 detail = 1 : Patients, 2 : Studies, 3 : Series, 4 : Images
994                          5 : Full Content
995                 level = 0,1,2 : depending on user (what he wants to see, when detail=5)
996                 debug    : user wants to run the program in 'debug mode'
997                 usage    : user wants to display usage
998          
999
1000     * MakeDicomDir
1001
1002          Explores recursively the given directory, makes the relevant DICOMDIR
1003                 and writes it as 'NewDICOMDIR'
1004          usage: MakeDicomDir dirname=rootDirectoryName 
1005                             [ { [noshadowseq] | [noshadow][noseq] } ] [debug] [usage]
1006                 noshadowseq: user doesn't want to load Private Sequence
1007                 noshadow   : user doesn't want to load Private groups (odd number)
1008                 noseq      : user doesn't want to load Sequences
1009                 debug      : user wants to run the program in 'debug mode'
1010                 usage      : user wants to display usage
1011          
1012
1013     * AnonymizeDicomDir
1014
1015          Anonymizes a gdcm-readable DICOMDIR even when some 'Objects'
1016                 are not yet taken into account
1017                 Warning : the DICOMDIR is overwritten; 
1018                           to preserve its integrity, use a copy.
1019          usage: AnonymizeDicomDir filein=dicomDirName [debug] [usage] [usage]
1020                 debug    : user wants to run the program in 'debug mode'
1021                 usage    : user wants to display usage
1022          
1023
1024     * PatchHeader
1025
1026           Allows aware user to patch a gdcm-parsable image header, without 
1027                loading image.
1028                Warning : the image(s) is/are overwritten
1029                          to preserve image(s) integrity, use a copy.
1030                WARNING : *NO CHECK* is performed on the new values.
1031                          Use only if you are sure the original values are wrong
1032                          *and* your values are right...
1033          usage: PatchHeader {filein=inputFileName|dirin=inputDirectoryName}
1034                      [ { [size=] | [rows=][columns=] } ] [planes=]
1035                      [bitsallocated=] [bitsstored=]
1036                      [highbit=] [samplesperpixel=]
1037                      [pixelrepresentation=] [samplesperpixel=]
1038                      [ { [noshadowseq] | [noshadow][noseq] } ] [debug]
1039                                                                                  
1040              inputFileName : Name of the (single) file user wants to modify
1041              inputDirectoryName : user wants to modify *all* the files
1042                                   within the directory
1043              newsize         : new size, to overwrite old (wrong) one
1044                 or
1045              rows            : new Rows number,    to overwrite old (wrong) one
1046              columns         : new Columns number, to overwrite old (wrong) one
1047              planes          : new Planes number,  ...
1048              bitsallocated   : new Bits Allocated number,  ...
1049              bitsstored      : new Bits Stored number,  ...
1050              highbit         : new High Bit number,  ...
1051              samplesperpixel : new Samples Per Pixel, ...
1052              pixelrepresentation : new Pixel Representation, ...
1053                                                                                  
1054              noshadowseq: user doesn't want to load Private Sequences
1055              noshadow   : user doesn't want to load Private groups (odd number)
1056              noseq      : user doesn't want to load Sequences
1057              debug      : user wants to run the program in 'debug mode'
1058              usage      : user wants to display usage
1059          
1060
1061     * exXCoherentFileSet :
1062                                                  
1063 Shows the various 'XCoherent' Filesets within a directory                 
1064 Optionaly copies the images in a Directories tree                          
1065 usage: exXCoherentFileSet {dirin=inputDirectoryName}                      
1066                            dirout=outputDirectoryName                     
1067                        { tag=group-elem | pos | ori } [sort] [write]      
1068                        [ { [noshadowseq] | [noshadow][noseq] } ] [debug]  
1069                                                                           
1070        dirin : user wants to analyze *all* the files                      
1071                             within the directory                          
1072        write : user wants to create directories                           
1073        dirout : will be created if doesn't exist                          
1074        pos  : user wants to split each Single SerieUID Fileset on the     
1075                          'Image Position '                                
1076        ori  : user wants to split each Single SerieUID Fileset on the     
1077                          'Image Orientation '                             
1078        tag : group-elem    (in hexa, no space)                            
1079                        the user wants to split on                         
1080        sort :  user wants FileHelper to sort the images                   
1081                Warning : will probabely crah if sort has no meaning       
1082                 (not only look at image names)                            
1083        noshadowseq: user doesn't want to load Private Sequences           
1084        noshadow   : user doesn't want to load Private groups (odd number) 
1085        noseq      : user doesn't want to load Sequences                   
1086        verbose    : user wants to run the program in 'verbose mode'       
1087        debug      : developper wants to run the program in 'debug mode'  
1088
1089 }}}
1090 </pre>