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)
12 ------------------------------------------------------------------------
16 1) How to Read a DICOM file
17 ===========================
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
34 1-4) Retrictions for Python users
36 2) How to write DICOM file
37 ==========================
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
49 2-4) Retrictions for Python users
53 3-1) How to read a DICIMDIR
54 3-2) How to modifiy a DICOMDIR
55 3-3) How to create a DICOMDIR
57 4) Some 'Command line' utilities
58 ================================
61 4-) exXCoherentFileSet
64 4-) AnonymizeMultiPatient
69 4-) exMoveImagesToSingleSerieUID
72 4-) vtkgdcmSerieViewer2
77 ----------------------------------------------------------------------------
82 If you are not familiar with DICOM files, use :
84 PrintFile filein=yourDicomFile.dcm
86 and have a look at the output.
87 You'll see a lot of self explanatory (?) lines, e.g.
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]
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
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
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
108 [20020524] : the value, printed in a human readable way.
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
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!).
122 You can find also something like :
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]
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 :
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]
218 Probabely, you'll never have to deal with Sequences (hope so!).
220 1) How to Read a DICOM File
221 ===========================
229 1-1-1-1) Deal with the file header
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);
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();
249 Except if you are really aware about it, do *not* use SetLoadMode().
250 LD_ALL is the default.
252 Check if the file is gdcm-readable.
254 if ( !f->IsReadable() )
255 std::cout << "major troubles on [" << f->GetFileName() <<"]"
258 Decide if this is a 'File Of Interest' for you.
259 Check some fields, e.g
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);
267 (or whatever you feel like ...)
269 1-1-1-2) Load the 'pixels' in memory
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.
275 You first have to create a 'File Helper'
277 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
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!)
285 fh->SetKeepOverlays(true); // default is : false
287 void *imageData = fh->GetImageDataRaw();
288 uint32_t dataSize = fh->GetImageDataRawSize();
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
294 std::string pixelType = f->GetPixelType();
296 Possible values are :
298 - "8U" unsigned 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!)
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 (?)
311 Sometimes, you deal with 'colour' images :-(
312 They may be stored, on disc, as :
317 Grey level images + LUT.
319 You'll get an 'RGB Pixels' image in memory if you use:
321 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
322 void *imageData = fh->GetImageData();
323 uint32_t dataSize = fh->GetImageDataSize();
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();
330 Now, you can enjoy your image !
332 1-1-1-3) Get the value of a single Dicom DataElement
334 1-1-1-3-1) as a std::string
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);
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
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-)
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 );
355 1-1-1-4) Get the value(s) within a Dicom Sequence
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.
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
369 // do what you want with the current ROI
370 currentItem = seqEntry->GetNextSQItem(); // Get the next ROI
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.
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)
384 A class gdcm::SerieHelper is designed to help solving this problem.
387 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
388 while (int i=0; i < nbOfFiles; i++) {
389 sh->AddFileName(currentFileName[i]);
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
396 Files are 'splitted' into as many 'Single Serie UID File Set'
397 as Series Instance UID ( 0020|000e );
399 // -------- skip this one, for a first reading ! -----------
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.
405 void SerieHelper::SetUseSeriesDetails(bool s);
406 /// This function will add the following DICOM tag as being part of a
408 /// 0020 0011 Series Number
409 /// 0018 0024 Sequence Name
410 /// 0018 0050 Slice Thickness
412 /// 0028 0011 Columns
414 If it's not enough for you, use :
415 void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
417 std::string SerieHelper::CreateUniqueSeriesIdentifier(gdcm::File *inFile);
418 void SerieHelper::CreateDefaultUniqueSeriesIdentifier();
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);
423 std::string SerieHelper::CreateUserDefinedFileIdentifier(gdcm::File *inFile);
424 and use it as you feel like.
426 // ------------ Resume reading, here !--------------------
428 If you want to 'order' the files within each 'Single Serie UID File Set'
429 (to build a volume, for instance), use :
431 gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
434 sh->OrderFileList(l); // sort the list
435 l = sh->GetNextSingleSerieUIDFileSet();
438 The sorting will be performed on the ImagePositionPatient;
439 if not found, on ImageNumber;
440 if not found, on the File Name.
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();
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)
455 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
456 while (int i=0; i < nbOfFiles; i++) {
457 sh->AddFile(currentFile[i]);
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()
470 User may want to exclude some files.
472 void SerieHelper::AddRestriction(uint16_t group, uint16_t elem,
473 std::string const &value, int op);
476 /// \brief comparison operators
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++) {
489 User wants to deal only with Female patient, any Modality but MR (why not?)
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'
495 sh->SetDropDuplicatePositions(true);
497 Sometimes the previous stuff is *not enough* !
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).
503 User may consider that dealing only with the 'Series Instance UID'
504 is not enough and wishes to 'refine' the image selection :
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
510 gdcm::SerieHelper *s;
511 gdcm::XCoherentFileSetmap xcm;
512 gdcm::FileList *l = s->GetFirstSingleSerieUIDFileSet(); // or what you want
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* >).
520 He may ask for 'splitting' on the Orientation:
521 gdcm::XCoherentFileSetmap xcm = s->SplitOnOrientation(l);
523 He may ask for 'splitting' on the Position:
524 gdcm::XCoherentFileSetmap xcm = s->SplitOnPosition(l);
526 He may ask for 'splitting' on the any DataElement you feel like :
527 gdcm::XCoherentFileSetmap xcm = s->SplitOnTagValue(l, group,elem);
530 He can now work on each 'X Coherent File Set' within the std::map
532 for (gdcm::XCoherentFileSetmap::iterator i = xcm.begin();
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 ...)
541 s->OrderFileList((*i).second); // sort the XCoherent Fileset
544 (have a look at gdcm/Examples/exXCoherentFileSet.cxx for an example)
548 a vtkGdcmReader() method ( derived from vtkReader() ) is available.
553 vtkGdcmReader *reader = vtkGdcmReader::New();
554 reader->SetFileName( yourDicomFilename );
555 reader->SetLoadMode( yourLoadMode ); // See C++ part
556 reader->SetKeepOverlays( true/false ); // See C++ part
558 vtkImageData* ima = reader->GetOutput();
559 int* Size = ima->GetDimensions();
566 If you are 150 % sure of the files you're dealing with, just 'add' the files you
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
575 vtkImageData* ima = reader->GetOutput();
576 int* Size = ima->GetDimensions();
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 !
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)
588 described as : void userSuppliedFunction(uint8_t *im, gdcm::File *f);
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
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 :
601 gdcm::SerieHelper *sh= gdcm::NewSerieHelper();
602 // if user wants *not* to load some parts of the file headers
603 sh->SetLoadMode(yourLoadMode);
605 // if user wants *not* to load some files
606 sh->AddRestriction(group, element, value, operator);
607 sh->AddRestriction( ...
608 sh->SetDirectory(directoryWithImages);
610 // if user *knows* how to order his files
611 sh->SetUserLessThanFunction(userSuppliedComparisonFunction);
613 // if user wants to sort reverse order
614 sh->SetSortOrderToReverse();
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();
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
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'
629 sh->SetDropDuplicatePositions(true);
631 // Sorting the Fileset (*) is mandatory!
632 // ( computing an accurate Series ZSpacing is a side effect ...)
633 sh->OrderFileList(l);
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);
641 // to pass a 'Single SerieUID' Fileset as produced by gdcm::SerieHelper
642 reader->SetCoherentFileList(l);
648 User may also pass an 'X Coherent Fileset', created by one of the following
649 methods : (see 1-1-2 for more details)
651 xcm = sh->SplitOnOrientation(l);
652 xcm = sh->SplitOnPosition(l);
653 xcm = sh->SplitOnTagValue(l, groupelem[0],groupelem[1]);
657 You can see a full example in vtk/vtkgdcmSerieViewer2.cxx
659 vtkgdcmSerieViewer dirname=Dentist mirror
660 vtkgdcmSerieViewer dirname=Dentist reverse
661 vtkgdcmSerieViewer dirname=Dentist reverse upsidedown
664 1-4) Retrictions for Python users
665 ---------------------------------
667 None of the methods receiving a function pointer, or a gdcm::File as a parameter
672 2) How to write DICOM file
673 ==========================
680 In C++, if you have already the pixels in main memory,
681 you just have to process as follow :
683 --> Create an empty gdcm::File
684 gdcm::File *file = gdcm::File::New();
686 std::ostringstream str;
688 // --> Set the mandatory fields
689 // Set the image size
692 file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
695 file->InsertEntryString(str.str(),0x0028,0x0011,"US"); // Columns
698 file->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Nbr of Frames
699 // Set the pixel type
701 str << componentSize; //8, 16, 32
702 file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
704 str << componentUse; // may be 12 or 16 if componentSize =16
705 file->InsertEntryString(str.str(),0x0028,0x0101,"US"); // Bits Stored
707 str << componentSize - 1 ;
708 file->InsertEntryString(str.str(),0x0028,0x0102,"US"); // High Bit
709 // Set the pixel representation // 0/1
712 file->InsertEntryString(str.str(),0x0028,0x0103, "US"); // Pixel Representation
713 // Set the samples per pixel // 1:Grey level, 3:RGB
716 file->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
718 //--> Set Optional fields
719 see further how to deal with optional fields
721 //--> Create a gdcm::FileHelper
722 gdcm::FileHelper *fileH = gdcm::FileHelper::New(file);
724 //--> Tell the FileHelper what you did for creating the image:
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
732 // -2) user modified the pixels of an existing image.
734 // -3) user created a new image, using existing a set of images
735 // (eg MIP, MPR, cartography 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)
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);
750 // depending on what you did before!
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)
758 fileH->SetWriteModeToRaw(); // Probabely you don't want to convert any LUT into RGB pixels ...
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. )
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. )
770 fileH->Write(fileName.str());
772 //This works for a single image (singleframe or multiframe)
774 2-1-1-1) Deal with optional DataElements // TODO : finish it
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.
780 2-1-1-1-1) Add a single Dicom DataElement // TODO : finish it
783 DataEntry * File::InsertEntryString(std::string const &value,
784 uint16_t group, uint16_t elem,
785 VRKey const &vr = GDCM_VRUNKNOWN);
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
791 DataEntry * File:InsertEntryBinArea(uint8_t *binArea, int lgth,
792 uint16_t group, uint16_t elem,
793 VRKey const &vr = GDCM_VRUNKNOWN);
795 2-1-1-1-2) Add a Dicom Sequence // TODO : finish it
796 SeqEntry * File::InsertSeqEntry(uint16_t group, uint16_t elem);
800 /// \todo : write it!
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)
808 // You will probabely want that all the images of your file set belong to the same 'Serie'
814 /// \todo : write it!
819 /// \todo : finish it!
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'
826 // todo : explain how to use it.
827 //vtkMedicalImageProperties
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.
836 vtkSetMacro(GdcmFile, gdcm::File *);
838 void vtkGdcmWriter::SetGdcmFile(gdcm::File *);
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
846 // -2) user modified the pixels of an existing image.
848 // -3) user created a new image, using existing a set of images
849 // (eg MIP, MPR, cartography image)
851 // -4) user modified/added some tags *without processing* the pixels
853 // UNMODIFIED_PIXELS_IMAGE
854 // -Probabely some more to be added
855 //(see gdcmFileHelper.h for more explanations)
857 // User is allowed to use the following methods:
859 void vtkGdcmWriter::SetContentTypeToUserOwnImage()
860 void vtkGdcmWriter::SetContentTypeToFilteredImage()
861 void vtkGdcmWriter::SetContentTypeToUserCreatedImage()
862 vtkGdcmWriter::void SetContentTypeToUnmodifiedPixelsImage()
864 // depending on what he did before (see C++ part)
870 /// \todo : write it!
875 /// \todo : write it!
877 2-4) Retrictions for Python users
878 ---------------------------------
879 /// \todo : write it!
882 3) DICOMDIR /// \todo: finish it!
884 3-1) How to read a DICIMDIR
885 3-2) How to modifiy a DICOMDIR
886 3-3) How to create a DICOMDIR
889 4) Some 'Command line' utilities /// \todo: finish it!
890 ================================
894 4-) exXCoherentFileSet
897 4-) AnonymizeMultiPatient
898 4-) AnonymizeDicomDir
902 4-) exMoveImagesToSingleSerieUID
905 4-) vtkgdcmSerieViewer2
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] } ]
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)
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
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
966 Re write a full gdcm-readable Dicom image
967 (usefull when the file header is not very straight).
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]
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'
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
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
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
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
1026 Allows aware user to patch a gdcm-parsable image header, without
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]
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
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, ...
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
1061 * exXCoherentFileSet :
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]
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
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'