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 :
233 gdcm::File *f = new gdcm::File();
234 f->SetLoadMode(LD_NOSEQ); | depending on what
235 f->SetLoadMode(LD_NOSHADOW); | you want *not*
236 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW);| to load from the
237 f->SetLoadMode(LD_NOSHADOWSEQ); | target file
238 f->SetFileName(fileName);
242 The 'long' Data Element (>4096 char) are not loaded by default
243 You may modify this default length :
244 f->SetMaxSizeLoadEntry(int yourOwnMaxSize);
245 Or ask to force the loading of given Data Elements, whatever their size is:
246 f->AddForceLoadElement (uint16_t group, uint16_t elem);
247 before calling gdcm::File::Load();
250 Except if you are really aware about it, do *not* use SetLoadMode().
251 LD_ALL is the default.
253 Check if the file is gdcm-readable.
255 if ( !f->IsReadable() )
256 std::cout << "major troubles on [" << f->GetFileName() <<"]"
259 Decide if this is a 'File Of Interest' for you.
260 Check some fields, e.g
262 std::string StudyDate = f->GetEntryString(0x0008,0x0020);
263 std::string PatientName = f->GetEntryString(0x0010,0x0010);
264 std::string PatientID = f->GetEntryString(0x0010,0x0020);
265 std::string PatientSex = f->GetEntryString(0x0010,0x0040);
266 std::string Modality = f->GetEntryString(0x0008,0x0060);
268 (or whatever you feel like ...)
270 1-1-1-2) Load the 'pixels' in memory
272 Next step is to load the pixels in memory.
273 Uncompression (JPEG lossless, JPEG lossy, JPEG 2000, RLE, ...)
274 will be automatically performed if necessary.
276 You first have to create a 'File Helper'
278 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
280 In some images,(where BitsAllocated=16, BitsUsed=8),
281 the 'unused bits' are actually ... used for storing 'overlays'.
282 (e.g. : Patient / Aquisition / Institution informations, or drawings ...)
283 It's up to you to decide whether you want or not to load the overlays.
284 (Probabely, if you want to post-process the images, you dont' want!)
286 fh->SetKeepOverlays(true); // default is : false
288 void *imageData = fh->GetImageDataRaw();
289 uint32_t dataSize = fh->GetImageDataRawSize();
291 Generally, you work on 'Grey level' images (as opposed to RGB images).
292 Depending on the Pixel size (8/16/32 bits) and the Pixel Type (signed/unsigned),
293 you cast the imageData
295 std::string pixelType = f->GetPixelType();
297 Possible values are :
299 - "8U" unsigned 8 bit,
301 - "16U" unsigned 16 bit,
302 - "16S" signed 16 bit,
303 - "32U" unsigned 32 bit,
304 - "32S" signed 32 bit,
305 - (NEITHER 'float' NOR 'double' pixels in DICOM!)
307 int dimX = f->GetXSize();
308 int dimY = f->GetYSize();
309 int dimZ = f->GetZSize(); // meaningfull only for 'Volumes' or 'multiframe files'
310 int dimT = f->GetTSize(); // meaningfull only for 4D objects (?)
312 Sometimes, you deal with 'colour' images :-(
313 They may be stored, on disc, as :
318 Grey level images + LUT.
320 You'll get an 'RGB Pixels' image in memory if you use:
322 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
323 void *imageData = fh->GetImageData();
324 uint32_t dataSize = fh->GetImageDataSize();
326 If you don't want to convert a "Grey level images + LUT" into "RGB Pixel image" use
327 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
328 void *imageData = fh->GetImageDataRaw();
329 uint32_t dataSize = fh->GetImageDataRawSize();
331 Now, you can enjoy your image !
333 1-1-1-3) Get the value of a single Dicom DataElement
335 1-1-1-3-1) as a std::string
337 - some DataEntries are 'human readable' (those whose VR is AE, DA, DS, PN, SH, TM)
338 Get their value using group number-element number :
339 std::string patientName = f->GetEntryString(0x0010,0x0010);
341 - Some DataEntries are stored with their own binary representation, but maybe you feel like
342 to get them in a 'human readable' form (those whose VR is FL, FD, SL, SS, UL, US)
343 Get their value using group number-element number :
344 std::string rowNumber =f->GetEntryString(0x0028, 0x0010);// nb of Rows
346 (of course, the very often used DataEntries have their own accessors :
347 e.g. GetXsize, GetYSize, GetSpacing, GetImageOrientationPatient, GetImagePositionPatient,
348 GetRescaleSlope, GetRescaleIntercept, GetNumberOfScalarComponents, etc -see gdcmFile.h-)
351 1-1-1-3-3) as a void* pointer
352 - Some DataEntries are stored with their own binary representation, and you want to get them 'as they are'.
353 You will have to cast them, according to the knowledge you have about them.
354 LutRedData = (uint8_t*)f->GetEntryBinArea( 0x0028, 0x1201 );
356 1-1-1-4) Get the value(s) within a Dicom Sequence
358 Actually, a 'Dicom Sequence' is composed of a list a 'Sequence Items',
359 each Sequence Item is a set of DataElement (that can be Dicom Sequences, recursively).
360 You have to get the Sequence element, to get its number of Sequence items, to iterate on each one.
363 SeqEntry *seqEntry = f->GetSeqEntry(0x3006,0x0020); //Structure Set ROI sequence
364 unsigned int n = seqEntry->GetNumberOfSQItems(); // useless : just to see !
365 currentItem = seqEntry->GetFirstSQItem(); // Get the first ROI
366 while (currentItem != NULL) {
367 std::string roiName = currentItem->GetEntryString(0x3006,0x0026); //ROI name
368 std::string roiDescr = currentItem->GetEntryString(0x3006,0x0028); //ROI description
370 // do what you want with the current ROI
371 currentItem = seqEntry->GetNextSQItem(); // Get the next ROI
378 If you are 150 % sure of the files you're dealing with, just read the files you
379 feel like, and concatenate the pixels.
381 Sometimes you are not sure at all (say : you were given a CDROM with an amount
382 of images, laying in a Directories tree-like structure, you don't know anything
383 about the Patients, and so on)
385 A class gdcm::SerieHelper is designed to help solving this problem.
388 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
389 while (int i=0; i < nbOfFiles; i++) {
390 sh->AddFileName(currentFileName[i]);
393 You can also pass a 'root directory', and ask or not for recursive parsing.
394 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
395 sh->SetDirectory(yourRootDirectoryName, true); // true : recursive parsing
397 Files are 'splitted' into as many 'Single Serie UID File Set'
398 as Series Instance UID ( 0020|000e );
400 // -------- skip this one, for a first reading ! -----------
402 Sometimes, the Serie UID is not enough to disseminate properly the images.
403 We may want to disseminate into multiple sub series when needed.
406 void SerieHelper::SetUseSeriesDetails(bool s);
407 /// This function will add the following DICOM tag as being part of a
409 /// 0020 0011 Series Number
410 /// 0018 0024 Sequence Name
411 /// 0018 0050 Slice Thickness
413 /// 0028 0011 Columns
415 If it's not enough for you, use :
416 void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
418 std::string SerieHelper::CreateUniqueSeriesIdentifier(gdcm::File *inFile);
419 void SerieHelper::CreateDefaultUniqueSeriesIdentifier();
421 You may also create a "tokenizable' File Identifier of your own, using :
422 void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
424 std::string SerieHelper::CreateUserDefinedFileIdentifier(gdcm::File *inFile);
425 and use it as you feel like.
427 // ------------ Resume reading, here !--------------------
429 If you want to 'order' the files within each 'Single Serie UID File Set'
430 (to build a volume, for instance), use :
432 gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
435 sh->OrderFileList(l); // sort the list
436 l = sh->GetNextSingleSerieUIDFileSet();
439 The sorting will be performed on the ImagePositionPatient;
440 if not found, on ImageNumber;
441 if not found, on the File Name.
443 Aware user is allowed to pass his own comparison function
444 (if he knows, for instance, the files must be sorted on 'Trigger Time')
445 He will use the method
446 void SerieHelper::SetUserLessThanFunction( bool(*) userFunc((File *,File *) );
447 He may ask for a reverse sorting :
448 sh->SetSortOrderToReverse();
449 or back to Direct Order
450 sh->SetSortOrderToDirect();
452 If, for any reason of his own, user already get the file headers,
453 he may add the gdcm::File (instead of the file name) to the SerieHelper.
454 (Sorry, not available in Python)
456 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
457 while (int i=0; i < nbOfFiles; i++) {
458 sh->AddFile(currentFile[i]);
460 * \warning : this method should be used by aware users only!
461 * User is supposed to know the files he want to deal with
462 * and consider them they belong to the same Set
463 * (even if their Serie UID is different)
464 * user will probabely OrderFileList() this list (actually, ordering
465 * user choosen gdm::File is the sole interest of this method)
466 * Moreover, using vtkGdcmReader::SetCoherentFileList() will avoid
467 * vtkGdcmReader parsing twice the same files.
468 * *no* coherence check is performed, but those specified
469 * by SerieHelper::AddRestriction()
471 User may want to exclude some files.
473 void SerieHelper::AddRestriction(uint16_t group, uint16_t elem,
474 std::string const &value, int op);
477 /// \brief comparison operators
485 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
486 sh->AddRestriction(0x0010,0x0040,"F",GDCM_EQUAL); // Patient's Sex
487 sh->AddRestriction(0x0008,0x0060,"MR",GDCM_DIFFERENT); // Modality
488 while (int i=0; i < nbOfFiles; i++) {
490 User wants to deal only with Female patient, any Modality but MR (why not?)
492 Maybe user knows there are several images with the same position
493 and *no dicom field* may discriminates them.
494 He wants to drop the 'duplicate images'
496 sh->SetDropDuplicatePositions(true);
498 Sometimes the previous stuff is *not enough* !
500 Within a SingleSerieUIDFileSet, you can have have various orientations,
501 or various positions, at various times. (not only various positions, at a single
502 time, for a single orientation).
504 User may consider that dealing only with the 'Series Instance UID'
505 is not enough and wishes to 'refine' the image selection :
507 Suppose he has a Single Serie UID File Set (gdcm::FileList).
508 He may ask to split it into several 'X Coherent File Sets' (X stands for
511 gdcm::SerieHelper *s;
512 gdcm::XCoherentFileSetmap xcm;
513 gdcm::FileList *l = s->GetFirstSingleSerieUIDFileSet(); // or what you want
516 The following methods must be called by user, depending on
517 what *he* wants to do, at application time.
518 - *he* only knows what his Series contain ! -
519 They return a std::map of Filesets (actually : a std::map of std::vector<gdcm::File* >).
521 He may ask for 'splitting' on the Orientation:
522 gdcm::XCoherentFileSetmap xcm = s->SplitOnOrientation(l);
524 He may ask for 'splitting' on the Position:
525 gdcm::XCoherentFileSetmap xcm = s->SplitOnPosition(l);
527 He may ask for 'splitting' on the any DataElement you feel like :
528 gdcm::XCoherentFileSetmap xcm = s->SplitOnTagValue(l, group,elem);
531 He can now work on each 'X Coherent File Set' within the std::map
533 for (gdcm::XCoherentFileSetmap::iterator i = xcm.begin();
538 // ask for 'ordering' according to the 'Image Position Patient'
539 // Sorting the Fileset (*) is mandatory!
540 // ( computing an accurate Series ZSpacing -whenever possible- is a side effect ...)
542 s->OrderFileList((*i).second); // sort the XCoherent Fileset
545 (have a look at gdcm/Examples/exXCoherentFileSet.cxx for an example)
549 a vtkGdcmReader() method ( derived from vtkReader() ) is available.
554 vtkGdcmReader *reader = vtkGdcmReader::New();
555 reader->SetFileName( yourDicomFilename );
556 reader->SetLoadMode( yourLoadMode ); // See C++ part
557 reader->SetKeepOverlays( true/false ); // See C++ part
559 vtkImageData* ima = reader->GetOutput();
560 int* Size = ima->GetDimensions();
567 If you are 150 % sure of the files you're dealing with, just 'add' the files you
570 vtkGdcmReader *reader = vtkGdcmReader::New();
571 for(int i=1; i< yourNumberOfFiles; i++)
572 reader->AddFileName( yourTableOfFileNames[i] );
573 reader->SetLoadMode( yourLoadMode ); // See C++ part
574 reader->SetKeepOverlays( true/false ); // See C++ part
576 vtkImageData* ima = reader->GetOutput();
577 int* Size = ima->GetDimensions();
581 Warning : The first file is assumed to be the reference file.
582 All the inconsistent files (different sizes, pixel types, etc) are discarted
583 and replaced by a black image !
585 User is allowed to pass a Pointer to a function of his own
586 to allow modification of pixel order (i.e. : Mirror, UpsideDown, )
587 to gdcm::FileHeleper, using SetUserFunction(userSuppliedFunction)
589 described as : void userSuppliedFunction(uint8_t *im, gdcm::File *f);
591 NB : the "uint8_t *" type of first param is just for prototyping.
592 User will Cast it according what he found with f->GetPixelType()
593 See vtkgdcmSerieViewer for an example
596 Many users expect from vtkGdcmReader it 'orders' the images
597 (Actually, that's the job of gdcm::SerieHelper ...)
598 When user knows the files with same Serie UID have same sizes,
599 same 'pixel' type, same color convention, ...
600 the right way to proceed is as follow :
602 gdcm::SerieHelper *sh= new gdcm::SerieHelper();
603 // if user wants *not* to load some parts of the file headers
604 sh->SetLoadMode(yourLoadMode);
606 // if user wants *not* to load some files
607 sh->AddRestriction(group, element, value, operator);
608 sh->AddRestriction( ...
609 sh->SetDirectory(directoryWithImages);
611 // if user *knows* how to order his files
612 sh->SetUserLessThanFunction(userSuppliedComparisonFunction);
614 // if user wants to sort reverse order
615 sh->SetSortOrderToReverse();
617 // here, we suppose only the first 'Single SerieUID' Fileset is of interest
618 // Just iterate using sh->NextSingleSerieUIDFileSet()
619 // if you want to get all of them
620 gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
622 // if user is doesn't trust too much the files with same Serie UID
623 if ( !sh->IsCoherent(l) )
624 return; // not same sizes, or not same 'pixel type' -> stop
626 // Maybe user knows there are several images with the same position
627 // and *no dicom field* may discriminates them.
628 // He wants to drop the 'duplicate images'
630 sh->SetDropDuplicatePositions(true);
632 // Sorting the Fileset (*) is mandatory!
633 // ( computing an accurate Series ZSpacing is a side effect ...)
634 sh->OrderFileList(l);
636 vtkGdcmReader *reader = vtkGdcmReader::New();
637 // if user wants to modify pixel order (Mirror, TopDown, ...)
638 // he has to supply the function that does the job
639 // (a *very* simple example is given in vtkgdcmSerieViewer.cxx)
640 reader->SetUserFunction (userSuppliedFunction);
642 // to pass a 'Single SerieUID' Fileset as produced by gdcm::SerieHelper
643 reader->SetCoherentFileList(l);
649 User may also pass an 'X Coherent Fileset', created by one of the following
650 methods : (see 1-1-2 for more details)
652 xcm = sh->SplitOnOrientation(l);
653 xcm = sh->SplitOnPosition(l);
654 xcm = sh->SplitOnTagValue(l, groupelem[0],groupelem[1]);
658 You can see a full example in vtk/vtkgdcmSerieViewer2.cxx
660 vtkgdcmSerieViewer dirname=Dentist mirror
661 vtkgdcmSerieViewer dirname=Dentist reverse
662 vtkgdcmSerieViewer dirname=Dentist reverse upsidedown
665 1-4) Retrictions for Python users
666 ---------------------------------
668 None of the methods receiving a function pointer, or a gdcm::File as a parameter
673 2) How to write DICOM file
674 ==========================
681 In C++, if you have already the pixels in main memory,
682 you just have to process as follow :
684 --> Create an empty gdcm::File
685 gdcm::File *file = gdcm::File::New();
687 std::ostringstream str;
689 // --> Set the mandatory fields
690 // Set the image size
693 file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
696 file->InsertEntryString(str.str(),0x0028,0x0011,"US"); // Columns
699 file->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Nbr of Frames
700 // Set the pixel type
702 str << componentSize; //8, 16, 32
703 file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
705 str << componentUse; // may be 12 or 16 if componentSize =16
706 file->InsertEntryString(str.str(),0x0028,0x0101,"US"); // Bits Stored
708 str << componentSize - 1 ;
709 file->InsertEntryString(str.str(),0x0028,0x0102,"US"); // High Bit
710 // Set the pixel representation // 0/1
713 file->InsertEntryString(str.str(),0x0028,0x0103, "US"); // Pixel Representation
714 // Set the samples per pixel // 1:Grey level, 3:RGB
717 file->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
719 //--> Set Optional fields
720 see further how to deal with optional fields
722 //--> Create a gdcm::FileHelper
723 gdcm::FileHelper *fileH = gdcm::FileHelper::New(file);
725 //--> Tell the FileHelper what you did for creating the image:
727 // gdcm cannot guess how user built his image
728 // (and therefore cannot be clever about some Dicom fields)
729 // It's up to the user to tell gdcm what he did.
730 // -1) user created ex nihilo his own image and wants to write it
733 // -2) user modified the pixels of an existing image.
735 // -3) user created a new image, using existing a set of images
736 // (eg MIP, MPR, cartography image)
738 // -4) user modified/added some tags *without processing* the pixels
739 // (anonymization, ...)
740 // UNMODIFIED_PIXELS_IMAGE
741 // -Probabely some more to be added
742 //(see gdcmFileHelper.h for more explanations)
746 fileH->SetContentType(GDCM_NAME_SPACE::USER_OWN_IMAGE);
747 fileH->SetContentType(GDCM_NAME_SPACE::FILTERED_IMAGE);
748 fileH->SetContentType(GDCM_NAME_SPACE::CREATED_IMAGE);
749 fileH->SetContentType(GDCM_NAME_SPACE::UNMODIFIED_PIXELS_IMAGE);
751 // depending on what you did before!
753 //--> Set the compression type :
754 fileH->SetWriteTypeToJPEG(); // lossless compression
755 fileH->SetWriteTypeToJPEG2000(); // lossless compression
756 fileH->SetWriteTypeToDcmExplVR(); // Explicit Value Representation (no compression)
757 fileH->SetWriteTypeToDcmImplVR(); // Implicit Value Representation (no compression)
759 fileH->SetWriteModeToRaw(); // Probabely you don't want to convert any LUT into RGB pixels ...
761 //--> Set the Image Data
762 fileH->SetImageData((unsigned char *)imageData,size);
763 // ( Casting as 'unsigned char *' is just to avoid warnings.
764 // It doesn't change the values. )
766 fileH->SetUserData((unsigned char *)imageData,size); // performs compression, when required
767 // ( Casting as 'unsigned char *' is just to avoid warnings.
768 // It doesn't change the values. )
771 fileH->Write(fileName.str());
773 //This works for a single image (singleframe or multiframe)
775 2-1-1-1) Deal with optional DataElements // TODO : finish it
777 Any Data Element may be added (it's up to the user to understand what he is doing!)
778 The supplied methods 'InsertXxx' will create the DataElement or replace it if it already exists.
779 Have a look at gdcm/Dict/dicomV3.dic to see what are the various DICOM fields, with their VR.
781 2-1-1-1-1) Add a single Dicom DataElement // TODO : finish it
784 DataEntry * File::InsertEntryString(std::string const &value,
785 uint16_t group, uint16_t elem,
786 VRKey const &vr = GDCM_VRUNKNOWN);
788 // (e.g. : patient name, patient ID, ... , or what you want,
789 // using their Dicom identifier, and 'VR'
790 file->InsertEntryString("MyOwnPatient" ,0x0010,0x0010,"PN"); // 0010 0010 : Patient's Name
792 DataEntry * File:InsertEntryBinArea(uint8_t *binArea, int lgth,
793 uint16_t group, uint16_t elem,
794 VRKey const &vr = GDCM_VRUNKNOWN);
796 2-1-1-1-2) Add a Dicom Sequence // TODO : finish it
797 SeqEntry * File::InsertSeqEntry(uint16_t group, uint16_t elem);
801 /// \todo : write it!
804 // If you deal with a Serie of images, it up to you to tell gdcm, for each image,
805 // what are the values of
806 // 0020 0032 DS 3 Image Position (Patient)
807 // 0020 0037 DS 6 Image Orientation (Patient)
809 // You will probabely want that all the images of your file set belong to the same 'Serie'
815 /// \todo : write it!
820 /// \todo : finish it!
822 // User of the CVS version of VTK 5 may set some 'Medical Image Properties'
823 // Only the predefined DataElements are available :
824 // PatientName, PatientID, PatientAge, PatientSex, PatientBirthDate, StudyID
825 // It's reasonably enough for any 'decent use'
827 // todo : explain how to use it.
828 //vtkMedicalImageProperties
830 // Aware user is allowed to pass his own gdcm::File *,
831 // so he may set *any Dicom field* he wants.
832 // (including his own Shadow Elements, or any gdcm::SeqEntry)
833 // gdcm::FileHelper::CheckMandatoryElements() will check inconsistencies,
834 // as far as it knows how.
835 // Sorry, not yet available under Python.
837 vtkSetMacro(GdcmFile, gdcm::File *);
839 void vtkGdcmWriter::SetGdcmFile(gdcm::File *);
841 // gdcm cannot guess how user built his image
842 // (and therefore cannot be clever about some Dicom fields)
843 // It's up to the user to tell gdcm what he did.
844 // -1) user created ex nihilo his own image and wants to write it
847 // -2) user modified the pixels of an existing image.
849 // -3) user created a new image, using existing a set of images
850 // (eg MIP, MPR, cartography image)
852 // -4) user modified/added some tags *without processing* the pixels
854 // UNMODIFIED_PIXELS_IMAGE
855 // -Probabely some more to be added
856 //(see gdcmFileHelper.h for more explanations)
858 // User is allowed to use the following methods:
860 void vtkGdcmWriter::SetContentTypeToUserOwnImage()
861 void vtkGdcmWriter::SetContentTypeToFilteredImage()
862 void vtkGdcmWriter::SetContentTypeToUserCreatedImage()
863 vtkGdcmWriter::void SetContentTypeToUnmodifiedPixelsImage()
865 // depending on what he did before (see C++ part)
871 /// \todo : write it!
876 /// \todo : write it!
878 2-4) Retrictions for Python users
879 ---------------------------------
880 /// \todo : write it!
883 3) DICOMDIR /// \todo: finish it!
885 3-1) How to read a DICIMDIR
886 3-2) How to modifiy a DICOMDIR
887 3-3) How to create a DICOMDIR
890 4) Some 'Command line' utilities /// \todo: finish it!
891 ================================
895 4-) exXCoherentFileSet
898 4-) AnonymizeMultiPatient
899 4-) AnonymizeDicomDir
903 4-) exMoveImagesToSingleSerieUID
906 4-) vtkgdcmSerieViewer2
914 Displays the header of any kind of ACR-NEMA/PAPYRUS/DICOM File
915 usage: PrintFile {filein=inputFileName|dirin=inputDirectoryName}[level=n]
916 [forceload=listOfElementsToForceLoad]
917 [4DLoc= ][dict= privateDirectory]
918 [ { [noshadowseq] | [noshadow][noseq] } ]
920 level = 0,1,2 : depending on the amount of details user wants to see
921 4DLoc: group-elem(in hexa, no space) of the DataEntry holdind 4thDim
922 listOfElementsToForceLoad : group-elem,g2-e2,... (in hexa, no space)
923 of Elements to load whatever their length
924 privateDirectory : source file full path name of Shadow Group elems
925 noshadowseq: user doesn't want to load Private Sequences
926 noshadow : user doesn't want to load Private groups (odd number)
927 noseq : user doesn't want to load Sequences
928 debug : user wants to run the program in 'debug mode'
929 warning : user wants to be warned about any oddity in the File
930 showlut :user wants to display the Palette Color (as an int array)
932 [ { [noshadowseq] | [noshadow][noseq] } ] [debug] [usage]
933 level = 0,1,2 : depending on the amount of details user wants to see
934 noshadowseq: user doesn't want to load Private Sequences
935 noshadow : user doesn't want to load Private groups (odd number)
936 noseq : user doesn't want to load Sequences
937 debug : user wants to run the program in 'debug mode'
938 usage : user wants to display usage
943 Anonymizes a full gdcm-readable Dicom image
944 Warning : probably segfaults if pixels are not gdcm readable.
945 Use AnonymizeNoLoad instead.
946 usage: Anonymize filein=inputFileName fileout=anonymizedFileName [debug][usage]
947 debug : user wants to run the program in 'debug mode'
948 usage : user wants to display usage
954 Anonymizes a gdcm-readable Dicom image even if pixels aren't gdcm readable
955 Warning : the image is overwritten;
956 to preserve its integrity, use a copy.
957 usage: AnonymizeNoLoad {filein=inputFileName|dirin=inputDirectoryName}
958 [rubout=listOfPrivateElementsToRubOut]
959 [ { [noshadowseq] | [noshadow][noseq] } ] [debug]
960 inputFileName : Name of the (single) file user wants to anonymize
961 inputDirectoryName : user wants to anonymize *all* the files
962 within the (single Patient!) directory
963 listOfElementsToRubOut : group1-elem1,g2-e2,... (in hexa)
964 of extra Elements to rub out
965 noshadowseq: user doesn't want to load Private Sequences
966 noshadow : user doesn't want to load Private groups (odd number)
967 noseq : user doesn't want to load Sequences
968 debug : user wants to run the program in 'debug mode'
969 usage : user wants to display usage
974 Reads and rewrites a full gdcm-readable Dicom image
975 (usefull when the file header is not very straight).
977 usage: ReWrite filein=inputFileName fileout=outputFileName
978 [keepoverlays] [mode=write mode] [monochrome1]
979 [noshadow] [noseq][debug]
980 --> The following line to 'rubout' a burnt-in Patient name
981 [rubout=xBegin,xEnd,yBegin,yEnd [ruboutvalue=n (<255)] ]
982 --> The 2 following lines, to extract a sub image within some frames
983 [ROI=xBegin,xEnd,yBegin,yEnd]
984 [firstframe=beg] [lastframe=end]
986 mode = a (ACR), x (Explicit VR Dicom), r (RAW : only pixels)
987 j (jpeg lossless), 2 (jpeg2000)
988 keepoverlays : user wants to keep ACR-NEMA-like overlays
989 monochrome1 = user wants MONOCHROME1 photom. interp. (0=white)
990 noshadowseq: user doesn't want to load Private Sequences
991 noshadow : user doesn't want to load Private groups (odd number)
992 noseq : user doesn't want to load Sequences
993 rgb : user wants to transform LUT (if any) to RGB pixels
994 warning : developper wants to run the program in 'warning mode'
995 debug : developper wants to run the program in 'debug mode' a full gdcm-readable Dicom image (compressed Pixels are expanded)
996 (usefull when the file is not very straight).
997 usage: ReWrite filein=inputFileName fileout=anonymizedFileName
998 [mode=write mode] [rgb]
999 [ { [noshadowseq] | [noshadow][noseq] } ] [debug] [usage]
1000 write mode = a (ACR), x (Explicit VR Dicom), r (RAW : only pixels)
1001 rgb : user wants to transform LUT (if any) into RGB
1002 noshadowseq: user doesn't want to load Private Sequences
1003 noshadow : user doesn't want to load Private groups (odd number)
1004 noseq : user doesn't want to load Sequences
1005 debug : user wants to run the program in 'debug mode'
1006 usage : user wants to display usage
1011 Displays the tree-like structure of a DICOMDIR File
1012 usage: PrintDicomDir filein=fileName [detail=n] [level=n] [debug] [usage]
1013 detail = 1 : Patients, 2 : Studies, 3 : Series, 4 : Images
1015 level = 0,1,2 : depending on user (what he wants to see, when detail=5)
1016 debug : user wants to run the program in 'debug mode'
1017 usage : user wants to display usage
1022 Explores recursively the given directory, makes the relevant DICOMDIR
1023 and writes it as 'NewDICOMDIR'
1024 usage: MakeDicomDir dirname=rootDirectoryName
1025 [ { [noshadowseq] | [noshadow][noseq] } ] [debug] [usage]
1026 noshadowseq: user doesn't want to load Private Sequence
1027 noshadow : user doesn't want to load Private groups (odd number)
1028 noseq : user doesn't want to load Sequences
1029 debug : user wants to run the program in 'debug mode'
1030 usage : user wants to display usage
1035 Anonymizes a gdcm-readable DICOMDIR even when some 'Objects'
1036 are not yet taken into account
1037 Warning : the DICOMDIR is overwritten;
1038 to preserve its integrity, use a copy.
1039 usage: AnonymizeDicomDir filein=dicomDirName [debug] [usage] [usage]
1040 debug : user wants to run the program in 'debug mode'
1041 usage : user wants to display usage
1046 Allows aware user to patch a gdcm-parsable image header, without
1048 Warning : the image(s) is/are overwritten
1049 to preserve image(s) integrity, use a copy.
1050 WARNING : *NO CHECK* is performed on the new values.
1051 Use only if you are sure the original values are wrong
1052 *and* your values are right...
1053 usage: PatchHeader {filein=inputFileName|dirin=inputDirectoryName}
1054 [ { [size=] | [rows=][columns=] } ] [planes=]
1055 [bitsallocated=] [bitsstored=]
1056 [highbit=] [samplesperpixel=]
1057 [pixelrepresentation=] [samplesperpixel=]
1058 [ { [noshadowseq] | [noshadow][noseq] } ] [debug]
1060 inputFileName : Name of the (single) file user wants to modify
1061 inputDirectoryName : user wants to modify *all* the files
1062 within the directory
1063 newsize : new size, to overwrite old (wrong) one
1065 rows : new Rows number, to overwrite old (wrong) one
1066 columns : new Columns number, to overwrite old (wrong) one
1067 planes : new Planes number, ...
1068 bitsallocated : new Bits Allocated number, ...
1069 bitsstored : new Bits Stored number, ...
1070 highbit : new High Bit number, ...
1071 samplesperpixel : new Samples Per Pixel, ...
1072 pixelrepresentation : new Pixel Representation, ...
1074 noshadowseq: user doesn't want to load Private Sequences
1075 noshadow : user doesn't want to load Private groups (odd number)
1076 noseq : user doesn't want to load Sequences
1077 debug : user wants to run the program in 'debug mode'
1078 usage : user wants to display usage