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)
13 ------------------------------------------------------------------------
15 1) How to Read a DICOM file
25 1-4) Retrictions for Python users
27 2) How to write DICOM file
34 ----------------------------------------------------------------------------
39 If you are not familiar with DICOM files , use :
41 PrintFile filein=yourDicomFile.dcm
43 and have a look at the output.
44 You'll see a lot of self explanatory (?) lines, e.g.
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]
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
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
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
63 [20020524] : the value, printed in a human readable way.
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
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!).
77 You can find also something like :
79 S 0018|6011 [SQ] [Sequence of Ultrasound Regions]
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]
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]
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]
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!).
112 1) How to Read a DICOM File
113 ===========================
121 The first step is to load the file header :
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);
131 Except if someone told you -he knows the file headers are bugged-,
132 do *not* use SetLoadMode().
134 Check if the file is gdcm-readable.
136 if ( !f->IsReadable() )
137 std::cout << "major troubles on [" << f->GetFileName() <<"]"
140 Decide if this is a 'File Of Interest' for you.
141 Check some fields, e.g
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);
149 (or whatever you feel like ...)
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.
155 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
156 void *imageData = fh->GetImageDataRaw();
157 uint32_t dataSize = fh->GetImageDataRawSize();
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
163 std::string pixelType = f->GetPixelType();
165 Possible values are :
167 - "8U" unsigned 8 bit,
169 - "16U" unsigned 16 bit,
170 - "16S" signed 16 bit,
171 - "32U" unsigned 32 bit,
172 - "32S" signed 32 bit,
175 int dimX = f->GetXcurrentFileName[i]Size();
176 int dimY = f->GetYSize();
177 int dimZ = f->GetZSize();
178 int dimT = f->GetTSize();
180 Now, you can enjoy your image !
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.
186 You'll get an 'RGB Pixels' image in memory if you use:
188 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
189 void *imageData = fh->GetImageData();
190 uint32_t dataSize = fh->GetImageDataSize();
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.
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)
203 A class gdcm::SerieHelper is designed to help solving this problem.
206 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
207 while (int i=0; i < nbOfFiles; i++) {
208 sh->AddFileName(currentFileName[i]);
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
215 Files are 'splitted' into as many 'Single Serie UID File Set'
216 as Series Instance UID ( 0020|000e );
218 If you want to 'order' the files within each 'Single Serie UID File Set'
219 (to build a volume, for instance), use :
221 gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
224 sh->OrderFileList(l); // sort the list
225 l = sh->GetNextSingleSerieUIDFileSet();
228 The sorting will be performed on the ImagePositionPatient;
229 if not found, on ImageNumber;
230 if not found, on the File Name.
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();
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.
244 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
245 while (int i=0; i < nbOfFiles; i++) {
246 sh->AddFile(currentFile[i]);
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()
259 User may want to exclude some files.
261 void SerieHelper::AddRestriction(uint16_t group, uint16_t elem,
262 std::string const &value, int op);
265 /// \brief comparison operators
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++) {
278 User wants to deal only with Female patient, any Modality but MR (why not?)
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'
286 sh->SetDropDuplicatePositions(true);
288 Sometimes the previous stuff is *not enough* !
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).
294 User may consider that dealing only with the 'Series Instance UID'
295 is not enough and wishes to 'refine' the image selection :
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
301 gdcm::SerieHelper *s;
302 gdcm::XCoherentFileSetmap xcm;
303 gdcm::FileList *l = s->GetFirstSingleSerieUIDFileSet(); // or what you want
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*).
311 He may ask for 'splitting' on the Orientation:
312 xcm = s->SplitOnOrientation(l);
314 He may ask for 'splitting' on the Position:
315 xcm = s->SplitOnPosition(l);
317 He may ask for 'splitting' on the any DataElement you feel like :
318 xcm = s->SplitOnTagValue(l, groupelem[0],groupelem[1]);
321 He can now work on each 'X Coherent File Set' within the std::map
323 for (gdcm::XCoherentFileSetmap::iterator i = xcm.begin();
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 ...)
332 s->OrderFileList((*i).second); // sort the XCoherent Fileset
335 (have a look at gdcm/Examples/exXCoherentFileSet.cxx for an example)
339 a vtkGdcmReader() method ( derived from vtkReader() ) is available.
346 vtkGdcmReader *reader = vtkGdcmReader::New();
347 reader->SetFileName( yourDicomFilename );
348 reader->SetLoadMode( yourLoadMode); // See C++ part
350 vtkImageData* ima = reader->GetOutput();
351 int* Size = ima->GetDimensions();
357 If you are 150 % sure of the files you're dealing with, just 'add' the files you
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
365 vtkImageData* ima = reader->GetOutput();
366 int* Size = ima->GetDimensions();
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 !
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)
377 described as : void userSuppliedFunction(uint8_t *im, gdcm::File *f);
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
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 :
390 gdcm::SerieHelper *sh= new gdcm::SerieHelper();
391 // if user wants *not* to load some parts of the file headers
392 sh->SetLoadMode(yourLoadMode);
394 // if user wants *not* to load some files
395 sh->AddRestriction(group, element, value, operator);
396 sh->AddRestriction( ...
397 sh->SetDirectory(directoryWithImages);
399 // if user *knows* how to order his files
400 sh->SetUserLessThanFunction(userSuppliedComparisonFunction);
402 // if user wants to sort reverse order
403 sh->SetSortOrderToReverse();
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();
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
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'
418 sh->SetDropDuplicatePositions(true);
420 // Sorting the Fileset (*) is mandatory!
421 // ( computing an accurate Series ZSpacing is a side effect ...)
422 sh->OrderFileList(l);
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);
430 // to pass a 'Single SerieUID' Fileset as produced by gdcm::SerieHelper
431 reader->SetCoherentFileList(l);
437 User may also pass an 'X Coherent Fileset', created by one of the following
438 methods : (see 1-1-2 for more details)
440 xcm = sh->SplitOnOrientation(l);
441 xcm = sh->SplitOnPosition(l);
442 xcm = sh->SplitOnTagValue(l, groupelem[0],groupelem[1]);
446 You can see a full example in vtk/vtkgdcmSerieViewer.cxx
448 vtkgdcmSerieViewer dirname=Dentist mirror
449 vtkgdcmSerieViewer dirname=Dentist reverse
450 vtkgdcmSerieViewer dirname=Dentist reverse upsidedown
453 1-4) Retrictions for Python users
454 ---------------------------------
456 None of the methods receiving a function pointer, or a gdcm::File as a parameter
461 2) How to write DICOM file
462 ==========================
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
470 // -2) user modified the pixels of an existing image.
472 // -3) user created a new image, using existing a set of images
473 // (eg MIP, MPR, cartography image)
475 // -4) user modified/added some tags *without processing* the pixels
477 // UNMODIFIED_PIXELS_IMAGE
478 // -Probabely some more to be added
479 //(see gdcmFileHelper.h for more explanations)
481 // User is allowed to use the following methods:
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);}
494 In C++, if you have already the pixels in main memory,
495 you just have to process as follow :
497 --> Create an empty gdcm::File
498 gdcm::File *file = gdcm::File::New();
500 std::ostringstream str;
502 // --> Set the mandatory fields
503 // Set the image size
506 file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
509 file->InsertEntryString(str.str(),0x0028,0x0011,"US"); // Columns
512 file->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Nbr of Frames
513 // Set the pixel type
515 str << componentSize; //8, 16, 32
516 file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
518 str << componentUse; // may be 12 or 16 if componentSize =16
519 file->InsertEntryString(str.str(),0x0028,0x0101,"US"); // Bits Stored
521 str << componentSize - 1 ;
522 file->InsertEntryString(str.str(),0x0028,0x0102,"US"); // High Bit
523 // Set the pixel representation // 0/1
526 file->InsertEntryString(str.str(),0x0028,0x0103, "US"); // Pixel Representation
527 // Set the samples per pixel // 1:Grey level, 3:RGB
530 file->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
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)
536 //--> Create a gdcm::FileHelper
538 gdcm::FileHelper *fileH = gdcm::FileHelper::New(file);
539 fileH->SetImageData((unsigned char *)imageData,size);
541 //casting as 'unsigned char *' is just to avoid warnings.
542 // It doesn't change the values.
544 fileH->SetWriteModeToRaw();
545 fileH->SetWriteTypeToDcmExplVR();
546 fileH->Write(fileName.str());
548 //This works for a single image (singleframe or multiframe)
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)
557 /// \todo : write it!
561 /// \todo : write it!
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
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 *);
588 /// \todo : write it!
593 /// \todo : write it!