]> Creatis software - gdcm.git/blob - Doc/Website/HowToUseGdcm.html
5a4f05a013638e1b93c6cc2fe79422587bf31b1e
[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, and add it to the gdcm site as soon as 
8 there is enough 'content' within it)
9
10 HTH
11 Jean-Pierre Roux
12
13 ------------------------------------------------------------------------
14 0) Intro
15 1) How to Read a DICOM file
16 1-1) using raw C++
17 1-1-1) A single file
18 1-1-2) A File Set
19 1-2) using VTK
20 1-2-1) A single file
21 1-2-2) A File Set
22 1-3) using ITK
23 1-3-1) A single file
24 1-3-2) A File Set
25 1-4) Retrictions for Python users
26
27 2) How to write DICOM file
28 2-1) using raw C++
29 2-1-1) A single file
30 2-1-2) A File Set
31 2-2) using VTK
32 2-3) using ITK
33
34 ----------------------------------------------------------------------------
35
36 0) Intro
37 --------
38
39 If you are not familiar with DICOM files , use :
40
41 PrintFile filein=yourDicomFile.dcm
42
43 and have a look at the output.
44 You'll see a lot of self explanatory (?) lines, e.g.
45
46 D 0008|0021 [DA]   [Series Date]     [20020524]
47 D 0008|0060 [CS]   [Modality]        [US]
48 D 0018|602c [FD]   [Physical Delta X][0]
49 D 0020|0013 [IS]   [Instance Number] [5 ]
50 D 0028|0010 [US]   [Rows]            [480]
51 D 0011|0010 [  ]   [gdcm::Unknown]   [DLX_PATNT_01]
52
53 0008|0021 : 'Group Number'|'Element Number' -> The Element identifier
54 Have a look at gdcm/Dicts/dicomV3.dic for the whole list of Elements
55  
56 DA        : The 'Value Representation' : DA for Date, US for Unsigned Short, ...
57 Have a look at gdcm/Dicts/dicomVR.dic for the set of possible values
58
59 [Series Date] : The 'official' English name of the Element
60 (When *you* have to deal with a given Element, its meaning should be clear for
61 *you*)
62
63 [20020524] : the value, printed in a human readable way.
64
65 If something like :
66 D 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
67                                  ===> [gdcm::Binary data loaded;length = 113784]
68 is displayed, it means that it's a 'long' binary area, gdcm (I) decided not to
69 show.
70
71 D 0011|0010 [  ]   [gdcm::Unknown]   [DLX_PATNT_01]
72 is a 'Private (or Shadow) Element', depending on the manufacturer.
73 It's *not* known within the 'official' Dicom Dictionnary.
74 Except if someone told you, you cannot guess the meaning of such an element.
75 Probabely, you'll never have to deal with Shadow Elements (hope so!).
76
77 You can find also something like : 
78
79 S 0018|6011 [SQ]                       [Sequence of Ultrasound Regions]
80    |  --- SQItem number 0
81    | D fffe|e000 [UL]                               [Item]
82    | D 0018|6018 [UL]             [Region Location Min X0] [32]
83    | D 0018|601a [UL]             [Region Location Min Y0] [24]
84    | D 0018|601c [UL]             [Region Location Max X1] [335]
85    | D 0018|601e [UL]             [Region Location Max Y1] [415]
86    | D 0018|602c [FD]                   [Physical Delta X] [0.0382653]
87    | D 0018|602e [FD]                   [Physical Delta Y] [0.0382653]
88    |  --- SQItem number 1
89    | D fffe|e000 [UL]                               [Item]
90    | D 0018|6018 [UL]             [Region Location Min X0] [336]
91    | D 0018|601a [UL]             [Region Location Min Y0] [24]
92    | D 0018|601c [UL]             [Region Location Max X1] [639]
93    | D 0018|601e [UL]             [Region Location Max Y1] [415]
94    | D 0018|602c [FD]                   [Physical Delta X] [0.0382653]
95    | D 0018|602e [FD]                   [Physical Delta Y] [0.0382653]
96    |  --- SQItem number 2
97    | D fffe|e000 [UL]                               [Item]
98    | D 0018|6018 [UL]             [Region Location Min X0] [32]
99    | D 0018|601a [UL]             [Region Location Min Y0] [40]
100    | D 0018|601c [UL]             [Region Location Max X1] [63]
101    | D 0018|601e [UL]             [Region Location Max Y1] [103]
102    | D 0018|6024 [US]         [Physical Units X Direction] [0]
103    | D 0018|6026 [US]         [Physical Units Y Direction] [0]
104    | D 0018|602c [FD]                   [Physical Delta X] [0]
105    | D 0018|602e [FD]                   [Physical Delta Y] [0]
106
107 0018|6011 is a 'Sequence' (SQ), composed of various Sequence Items(SQItem)
108 Each SQItem is a set of Elements (an Element may be a DataElement (D) or a
109 Sequence (S), recursively.
110 Probabely, you'll never have to deal with Sequences (hope so!).
111
112 1) How to Read a DICOM File
113 ===========================
114
115 1-1) using raw C++
116 ------------------
117
118 1-1-1) A single file
119 --------------------
120
121 The first step is to load the file header :
122
123            gdcm::File *f = new gdcm::File();
124                   f->SetLoadMode(NO_SEQ);            | depending on what
125                   f->SetLoadMode(NO_SHADOW);         | you want *not* 
126                   f->SetLoadMode(NO_SEQ | NO_SHADOW);| to load from the
127                   f->SetLoadMode(NO_SHADOWSEQ);      | target file
128             f->SetFileName(fileName);
129             f->Load( );
130     
131 Except if someone told you -he knows the file headers are bugged-, 
132 do *not* use SetLoadMode().
133
134 Check if the file is gdcm-readable.
135
136            if ( !f->IsReadable() )
137              std::cout << "major troubles on [" << f->GetFileName() <<"]"
138                        << std::endl;
139
140 Decide if this is a 'File Of Interest' for you.
141 Check some fields, e.g
142
143            std::string StudyDate           = f->GetEntryString(0x0008,0x0020);
144            std::string PatientName         = f->GetEntryString(0x0010,0x0010);
145            std::string PatientID           = f->GetEntryString(0x0010,0x0020);
146            std::string PatientSex          = f->GetEntryString(0x0010,0x0040);
147            std::string Modality            = f->GetEntryString(0x0008,0x0060);
148
149 (or whatever you feel like ...)
150
151 Next step is to load the pixels in memory.
152 Uncompression (JPEG lossless, JPEG lossy, JPEG 2000, RLE, ...) 
153 is automatically performed if necessary.
154
155            gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
156            void *imageData = fh->GetImageDataRaw();
157            uint32_t dataSize = fh->GetImageDataRawSize();
158
159 Generally, you work on 'Grey level' images (as opposed to RGB images).
160 Depending on the Pixel size (8/16/32 bits) and the Pixel Type (signed/unsigned),
161 you cast the imageData
162
163           std::string pixelType = f->GetPixelType();
164   
165 Possible values are : 
166
167 - "8U"  unsigned  8 bit,
168 - "8S"    signed  8 bit,
169 - "16U" unsigned 16 bit,
170 - "16S"   signed 16 bit,
171 - "32U" unsigned 32 bit,
172 - "32S"   signed 32 bit,
173
174    
175            int dimX = f->GetXcurrentFileName[i]Size();
176            int dimY = f->GetYSize();
177            int dimZ = f->GetZSize();
178            int dimT = f->GetTSize();
179
180 Now, you can enjoy your image !
181
182 Sometimes, you deal with 'colour' images :-(
183 They may be stored, on disc, as RGB pixels, RGB planes, YBR pixels, YBR planes
184 Grey level images + LUT.
185
186 You'll get an 'RGB Pixels' image in memory if you use: 
187
188            gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
189            void *imageData = fh->GetImageData();
190            uint32_t dataSize = fh->GetImageDataSize();
191
192
193 1-1-2) A File Set
194 -----------------
195
196 If you are 150 % sure of the files you're dealing with, just read the files you
197 feel like, and concatenate the pixels.
198
199 Sometimes you are not sure at all (say : you were given a CDROM with an amount
200 of images, laying in a Directories tree-like structure, you don't know anything
201 about the Patients, and so on)
202
203 A class gdcm::SerieHelper is designed to help solving this problem.
204 Use it as follows.
205
206     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
207     while (int i=0; i < nbOfFiles; i++) {
208        sh->AddFileName(currentFileName[i]);
209     }
210     
211 You can also pass a 'root directory', and ask or not for recursive parsing.
212     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
213     sh->SetDirectory(yourRootDirectoryName, true); // true : recursive parsing
214
215 Files are 'splitted' into as many 'Single Serie UID File Set' 
216   as Series Instance UID ( 0020|000e );
217   
218 If you want to 'order' the files within each 'Single Serie UID File Set'
219 (to build a volume, for instance), use :
220   
221    gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
222    while (l)
223    { 
224       sh->OrderFileList(l);  // sort the list
225       l = sh->GetNextSingleSerieUIDFileSet();
226    } 
227     
228   The sorting will be performed on the ImagePositionPatient;
229   if not found, on ImageNumber;
230   if not found, on the File Name.
231   
232   Aware user is allowed to pass his own comparison function 
233   (if he knows, for instance, the files must be sorted on 'Trigger Time')
234   He will use the method
235   void SerieHelper::SetUserLessThanFunction( bool(*) userFunc((File *,File *) ); 
236   He may ask for a reverse sorting : 
237   sh->SetSortOrderToReverse();
238   or back to Direct Order 
239   sh->SetSortOrderToDirect();
240   
241   If, for any reason of is own, user already get the file headers,
242   he may add the gdcm::File (instead of the file name) to the SerieHelper.
243
244     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
245     while (int i=0; i < nbOfFiles; i++) {
246        sh->AddFile(currentFile[i]);
247     }                           
248  * \warning : this method should be used by aware users only!
249  *            User is supposed to know the files he want to deal with
250  *           and consider them they belong to the same Set
251  *           (even if their Serie UID is different)
252  *           user will probabely OrderFileList() this list (actually, ordering
253  *           user choosen gdm::File is the sole interest of this method)
254  *           Moreover, using vtkGdcmReader::SetCoherentFileList() will avoid
255  *           vtkGdcmReader parsing twice the same files.
256  *           *no* coherence check is performed, but those specified
257  *           by SerieHelper::AddRestriction()
258  
259    User may want to exclude some files.
260    He will use
261     void SerieHelper::AddRestriction(uint16_t group, uint16_t elem,
262                                  std::string const &value, int op);
263 op belongs to :
264
265 /// \brief comparison operators
266    GDCM_EQUAL ,
267    GDCM_DIFFERENT,
268    GDCM_GREATER,
269    GDCM_GREATEROREQUAL,
270    GDCM_LESS,
271    GDCM_LESSOREQUAL
272 e.g. 
273     gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
274     sh->AddRestriction(0x0010,0x0040,"F",GDCM_EQUAL);      // Patient's Sex
275     sh->AddRestriction(0x0008,0x0060,"MR",GDCM_DIFFERENT); // Modality 
276     while (int i=0; i < nbOfFiles; i++) { 
277     ...
278 User wants to deal only with Female patient, any Modality but MR (why not?)
279
280
281    
282 Maybe user knows there are several images with the same position
283 and *no dicom field* may discriminates them.
284 He wants to drop the 'duplicate images'
285    
286      sh->SetDropDuplicatePositions(true);
287
288 Sometimes the previous stuff is *not enough* !
289
290 Within a SingleSerieUIDFileSet, you can have have various orientations,
291 or various positions, at various times. (not only various position , at a single
292 time, for a single orientation).
293
294 User may consider that dealing only with the 'Series Instance UID' 
295 is not enough and wishes to 'refine' the image selection :
296
297 Suppose he has a Single Serie UID File Set (gdcm::FileList).
298 He may ask to split it into several 'X Coherent File Sets' (X stands for
299 'Extra').
300
301 gdcm::SerieHelper *s;
302 gdcm::XCoherentFileSetmap xcm;
303 gdcm::FileList *l = s->GetFirstSingleSerieUIDFileSet(); // or what you want
304
305
306  The following methods must be called by user, depending on 
307  what *he* wants to do, at application time.
308   - *he* only  knows what his Series contain ! - 
309  They return a std::map of Filesets (actually : std::vector of gdcm::File*).
310  
311 He may ask for 'splitting' on the Orientation:
312            xcm = s->SplitOnOrientation(l);
313    
314 He may ask for 'splitting' on the Position:
315            xcm = s->SplitOnPosition(l);
316    
317 He may ask for 'splitting' on the any DataElement you feel like :   
318            xcm = s->SplitOnTagValue(l, groupelem[0],groupelem[1]);
319
320  
321 He can now work on each 'X Coherent File Set' within the std::map
322
323 for (gdcm::XCoherentFileSetmap::iterator i = xcm.begin();
324                                          i != xcm.end();
325                                        ++i)
326 {
327
328    // ask for 'ordering' according to the 'Image Position Patient'
329    // Sorting the Fileset (*) is mandatory!
330    // ( computing an accurate Series ZSpacing -whenever possible- is a side effect ...)
331      
332    s->OrderFileList((*i).second);  // sort the XCoherent Fileset
333 }  
334
335 (have a look at gdcm/Examples/exXCoherentFileSet.cxx for an example)
336
337 1-2) using VTK
338 --------------
339 a vtkGdcmReader() method ( derived from vtkReader() ) is available.
340
341
342
343 1-2-1) A single file
344 --------------------
345
346    vtkGdcmReader *reader = vtkGdcmReader::New();
347    reader->SetFileName( yourDicomFilename );      
348    reader->SetLoadMode( yourLoadMode); // See C++ part 
349    reader->Update();
350    vtkImageData* ima = reader->GetOutput();
351    int* Size = ima->GetDimensions();   
352  // -> Enjoy it.     
353
354 1-2-2) A File Set
355 -----------------
356
357 If you are 150 % sure of the files you're dealing with, just 'add' the files you
358 feel like:
359
360    vtkGdcmReader *reader = vtkGdcmReader::New();
361    for(int i=1; i< yourNumberOfFiles; i++)
362          reader->AddFileName( yourTableOfFileNames[i] );     
363    reader->SetLoadMode( yourLoadMode); // See C++ part 
364    reader->Update();
365    vtkImageData* ima = reader->GetOutput();
366    int* Size = ima->GetDimensions();
367  // -> Enjoy it.
368  
369  Warning : The first file is assumed to be the reference file.
370  All the inconsistent files (different sizes, pixel types, etc) are discarted
371  and replaced by a black image ! 
372  
373       User is allowed to pass a Pointer to a function of his own
374       to allow modification of pixel order (i.e. : Mirror, TopDown, )
375       to gdcm::FileHeleper, using SetUserFunction(userSuppliedFunction)
376
377       described as : void userSuppliedFunction(uint8_t *im, gdcm::File *f);
378
379       NB : the "uint8_t *" type of first param is just for prototyping.
380         User will Cast it according what he found with f->GetPixelType()
381         See vtkgdcmSerieViewer for an example
382  
383
384 Many users expect from vtkGdcmReader it 'orders' the images 
385 (Actually, that's the job of gdcm::SerieHelper ...)
386 When user knows the files with same Serie UID have same sizes, 
387 same 'pixel' type, same color convention, ... 
388 the right way to proceed is as follow :
389
390         gdcm::SerieHelper *sh= new gdcm::SerieHelper();
391    //      if user wants *not* to load some parts of the file headers
392         sh->SetLoadMode(yourLoadMode);
393
394    //      if user wants *not* to load some files
395         sh->AddRestriction(group, element, value, operator);
396         sh->AddRestriction( ...
397         sh->SetDirectory(directoryWithImages);
398
399    //      if user *knows* how to order his files
400         sh->SetUserLessThanFunction(userSuppliedComparisonFunction);
401    //      or/and
402    //      if user wants to sort reverse order
403         sh->SetSortOrderToReverse();
404    
405    //      here, we suppose only the first 'Single SerieUID' Fileset is of interest
406    //      Just iterate using sh->NextSingleSerieUIDFileSet()
407    //      if you want to get all of them
408         gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
409
410    //      if user is doesn't trust too much the files with same Serie UID
411         if ( !sh->IsCoherent(l) )
412            return; // not same sizes, or not same 'pixel type' -> stop
413    
414    // Maybe user knows there are several images with the same position
415    // and *no dicom field* may discriminates them.
416    // He wants to drop the 'duplicate images'
417    
418         sh->SetDropDuplicatePositions(true); 
419
420    // Sorting the Fileset (*) is mandatory!
421    // ( computing an accurate Series ZSpacing is a side effect ...)  
422         sh->OrderFileList(l);
423
424         vtkGdcmReader *reader = vtkGdcmReader::New();
425    //      if user wants to modify pixel order (Mirror, TopDown, ...)
426    //      he has to supply the function that does the job
427    //      (a *very* simple example is given in vtkgdcmSerieViewer.cxx)
428         reader->SetUserFunction (userSuppliedFunction);
429
430    //      to pass a 'Single SerieUID' Fileset as produced by gdcm::SerieHelper
431         reader->SetCoherentFileList(l);
432         reader->Update();
433
434
435 //-----------------
436 (*)
437 User may also pass an 'X Coherent Fileset', created by one of the following
438 methods : (see 1-1-2 for more details)
439  
440            xcm = sh->SplitOnOrientation(l); 
441            xcm = sh->SplitOnPosition(l);   
442            xcm = sh->SplitOnTagValue(l, groupelem[0],groupelem[1]);
443 //----------------
444
445
446 You can see a full example in vtk/vtkgdcmSerieViewer.cxx
447 e.g.
448 vtkgdcmSerieViewer dirname=Dentist mirror
449 vtkgdcmSerieViewer dirname=Dentist reverse
450 vtkgdcmSerieViewer dirname=Dentist reverse upsidedown
451
452
453 1-4) Retrictions for Python users
454 ---------------------------------
455
456 None of the methods receiving a function pointer, or a gdcm::File as a parameter
457 is wrapable by swig.
458 :-(
459
460
461 2) How to write DICOM file
462 ==========================
463
464    // gdcm cannot guess how user built his image 
465    //  (and therefore cannot be clever about some Dicom fields)
466    // It's up to the user to tell gdcm what he did.
467    // -1) user created ex nihilo his own image and wants to write it 
468    //     as a Dicom image.
469    // USER_OWN_IMAGE
470    // -2) user modified the pixels of an existing image.
471    // FILTERED_IMAGE
472    // -3) user created a new image, using existing a set of images 
473    //    (eg MIP, MPR, cartography image)
474    //  CREATED_IMAGE
475    // -4) user modified/added some tags *without processing* the pixels 
476    //     (anonymization..
477    //  UNMODIFIED_PIXELS_IMAGE
478    // -Probabely some more to be added
479    //(see gdcmFileHelper.h for more explanations)
480    
481    // User is allowed to use the following methods:
482    
483    void SetContentTypeToUserOwnImage()         
484                     {SetContentType(VTK_GDCM_WRITE_TYPE_USER_OWN_IMAGE);}
485    void SetContentTypeToFilteredImage()        
486                     {SetContentType(VTK_GDCM_WRITE_TYPE_FILTERED_IMAGE);}
487    void SetContentTypeToUserCreatedImage()     
488                     {SetContentType(VTK_GDCM_WRITE_TYPE_CREATED_IMAGE);}
489    void SetContentTypeToUnmodifiedPixelsImage()
490                     {SetContentType(VTK_GDCM_WRITE_TYPE_UNMODIFIED_PIXELS_IMAGE);}
491
492 2-1) using raw C++
493 ------------------
494 In C++, if you have already the pixels in main memory, 
495 you just have to process as follow :
496
497 --> Create an empty gdcm::File
498         gdcm::File *file = gdcm::File::New();
499
500         std::ostringstream str;
501
502 // --> Set the mandatory fields
503 // Set the image size
504         str.str("");
505         str << sizeY;
506         file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
507         str.str("");
508         str << sizeX;
509         file->InsertEntryString(str.str(),0x0028,0x0011,"US"); // Columns
510         str.str("");
511         str << sizeZ;
512         file->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Nbr of Frames
513           // Set the pixel type
514         str.str("");
515         str << componentSize; //8, 16, 32
516         file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
517         str.str("");
518         str << componentUse; // may be 12 or 16 if componentSize =16
519         file->InsertEntryString(str.str(),0x0028,0x0101,"US"); // Bits Stored
520         str.str("");
521         str << componentSize - 1 ;
522         file->InsertEntryString(str.str(),0x0028,0x0102,"US"); // High Bit
523   // Set the pixel representation // 0/1
524         str.str("");
525         str << img.sign;
526         file->InsertEntryString(str.str(),0x0028,0x0103, "US"); // Pixel Representation
527   // Set the samples per pixel // 1:Grey level, 3:RGB
528         str.str("");
529         str << components;
530         file->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
531
532 //--> Set Optional fields
533 //(patient name, patient ID, modality, or what you want
534 //Have look at gdcm/Dict/dicomV3.dic to see what are the various DICOM fields)
535
536 //--> Create a gdcm::FileHelper
537
538        gdcm::FileHelper *fileH = gdcm::FileHelper::New(file);
539        fileH->SetImageData((unsigned char *)imageData,size);
540        
541 //casting as 'unsigned char *' is just to avoid warnings.
542 // It doesn't change the values.
543
544       fileH->SetWriteModeToRaw();
545       fileH->SetWriteTypeToDcmExplVR();
546       fileH->Write(fileName.str());
547
548 //This works for a single image (singleframe or multiframe)
549
550 // If you deal with a Serie of images, it up to you to tell gdcm, for each image,
551 // what are the values of
552 // 0020 0032 DS 3 Image Position (Patient)
553 // 0020 0037 DS 6 Image Orientation (Patient) 
554
555 2-1-1) A single file
556 --------------------
557 /// \todo : write it!
558
559 2-1-2) A File Set
560 -----------------
561 /// \todo : write it!
562
563
564
565 2-2) using VTK
566 --------------
567
568 2-2-1) A single file
569 --------------------
570
571 // User of the CVS version of VTK 5 may set some 'Medical Image Properties'
572 // Only the predefined DataElements are available :
573 // PatientName, PatientID, PatientAge, PatientSex, PatientBirthDate, StudyID
574 // It's reasonablt enough for any 'decent use'
575 vtkMedicalImageProperties
576
577    // Aware user is allowed to pass his own gdcm::File *, 
578    //  so he may set *any Dicom field* he wants.
579    // (including his own Shadow Eleents, or any gdcm::SeqEntry)
580    // gdcm::FileHelper::CheckMandatoryElements() will check inconsistencies,
581    // as far as it knows how.
582    // Sorry, not yet available under Python.
583 vtkSetMacro(GdcmFile, gdcm::File *);
584
585
586 2-2-2) A File Set
587 -----------------
588 /// \todo : write it!
589
590
591 2-3) using ITK
592 --------------
593 /// \todo : write it!
594
595 }}}
596 </pre>