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 value of 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
51 3) Some 'Command line' utilities
52 ================================
55 3-) exXCoherentFileSet
58 3-) AnonymizeMultiPatient
63 3-) exMoveImagesToSingleSerieUID
66 3-) vtkgdcmSerieViewer2
71 ----------------------------------------------------------------------------
76 If you are not familiar with DICOM files, use :
78 PrintFile filein=yourDicomFile.dcm
80 and have a look at the output.
81 You'll see a lot of self explanatory (?) lines, e.g.
83 D 0008|0021 [DA] [Series Date] [20020524]
84 D 0008|0060 [CS] [Modality] [US]
85 D 0018|602c [FD] [Physical Delta X][0]
86 D 0020|0013 [IS] [Instance Number] [5 ]
87 D 0028|0010 [US] [Rows] [480]
88 D 0011|0010 [ ] [gdcm::Unknown] [DLX_PATNT_01]
89 D 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
90 ===> [gdcm::Binary data loaded;length = 113784]
92 0008|0021 : 'Group Number'|'Element Number' -> The Element identifier
93 Have a look at gdcm/Dicts/dicomV3.dic for the whole list of Elements
95 DA : The 'Value Representation' : DA for Date, US for Unsigned Short, ...
96 Have a look at gdcm/Dicts/dicomVR.dic for the set of possible values
98 [Series Date] : The 'official' English name of the Element
99 (When *you* have to deal with a given Element, its meaning should be clear for
102 [20020524] : the value, printed in a human readable way.
105 D 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
106 ===> [gdcm::Binary data loaded;length = 113784]
107 is displayed, it means that it's a 'long' binary area, gdcm (I) decided not to
110 D 0011|0010 [ ] [gdcm::Unknown] [DLX_PATNT_01]
111 is a 'Private (or Shadow) Element', depending on the manufacturer.
112 It's *not* known within the 'official' Dicom Dictionnary.
113 Except if someone told you, you cannot guess the meaning of such an element.
114 Probabely, you'll never have to deal with Shadow Elements (hope so!).
116 You can find also something like :
118 S 0018|6011 [SQ] [Sequence of Ultrasound Regions]
119 | --- SQItem number 0
120 | D fffe|e000 [UL] [Item]
121 | D 0018|6018 [UL] [Region Location Min X0] [32]
122 | D 0018|601a [UL] [Region Location Min Y0] [24]
123 | D 0018|601c [UL] [Region Location Max X1] [335]
124 | D 0018|601e [UL] [Region Location Max Y1] [415]
125 | D 0018|602c [FD] [Physical Delta X] [0.0382653]
126 | D 0018|602e [FD] [Physical Delta Y] [0.0382653]
127 | --- SQItem number 1
128 | D fffe|e000 [UL] [Item]
129 | D 0018|6018 [UL] [Region Location Min X0] [336]
130 | D 0018|601a [UL] [Region Location Min Y0] [24]
131 | D 0018|601c [UL] [Region Location Max X1] [639]
132 | D 0018|601e [UL] [Region Location Max Y1] [415]
133 | D 0018|602c [FD] [Physical Delta X] [0.0382653]
134 | D 0018|602e [FD] [Physical Delta Y] [0.0382653]
135 | --- SQItem number 2
136 | D fffe|e000 [UL] [Item]
137 | D 0018|6018 [UL] [Region Location Min X0] [32]
138 | D 0018|601a [UL] [Region Location Min Y0] [40]
139 | D 0018|601c [UL] [Region Location Max X1] [63]
140 | D 0018|601e [UL] [Region Location Max Y1] [103]
141 | D 0018|6024 [US] [Physical Units X Direction] [0]
142 | D 0018|6026 [US] [Physical Units Y Direction] [0]
143 | D 0018|602c [FD] [Physical Delta X] [0]
144 | D 0018|602e [FD] [Physical Delta Y] [0]
146 0018|6011 is a 'Sequence' (SQ), composed of various Sequence Items(SQItem)
147 Each SQItem is a set of Elements (an Element may be a DataElement (D) or a
148 Sequence (S), recursively, within any level of embedding :
151 | --- SQItem number 0
152 | D fffe|e000 [UL] [Item ]
153 | D 0008|0000 [UL] [Group Length] [12]
154 | D 0008|0001 [UL] [Length to End (RET) ] [28776]
155 | D 0029|0000 [UL] [Group Length] [28764]
156 | D 0029|002a [LO] [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
157 | D 0029|2a02 [LO] [] [PERFUSION_MR_T2STAR_GAMMA_VARIATE_ANALYSER]
158 | S 0029|2a06 [SQ] []
159 | | --- SQItem number 0
160 | | D fffe|e000 [UL] [Item ]
161 | | D 0008|0000 [UL] [Group Length] [12]
162 | | D 0008|0001 [UL] [Length to End (RET) ] [20]
163 | | D fffe|0000 [UL] [Group Length]
164 | | --- SQItem number 1
165 | | D fffe|e000 [UL] [Item ]
166 | | D 0008|0000 [UL] [Group Length] [12]
167 | | D 0008|0001 [UL] [Length to End (RET) ] [194]
168 | | D 0029|0000 [UL] [Group Length] [182]
169 | | D 0029|002a [LO] [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
170 | | S 0029|2a07 [SQ] []
171 | | | --- SQItem number 0
172 | | | D fffe|e000 [UL] [Item ]
173 | | | D 0008|0000 [UL] [Group Length] [12]
174 | | | D 0008|0001 [UL] [Length to End (RET) ] [100]
175 | | | D 0029|0000 [UL] [Group Length] [88]
176 | | | D 0029|002a [LO] [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
177 | | | D 0029|2a0a [US] [] [1]
178 | | | D 0029|2a0b [US] [] [2]
179 | | | D 0029|2a0c [US] [] [2]
180 | | | D 0029|2a0d [US] [] [1]
181 | | | D 0029|2a10 [US] [] [1]
182 | | | S 0029|2a2e [SQ] []
183 | | | | --- SQItem number 0
184 | | | | D fffe|e000 [UL] [Item ]
185 | | | | D 0008|0000 [UL] [Group Length] [12]
186 | | | | D 0008|0001 [UL] [Length to End (RET) ] [2266]
187 | | | | D 0029|0000 [UL] [Group Length] [2254]
188 | | | | D 0029|0025 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;1 ]
189 | | | | D 0029|0027 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;3 ]
190 | | | | D 0029|0028 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;4 ]
191 | | | | D 0029|2500 [SL] [] [43]
192 | | | | D 0029|256b [FD] [] [0.5]
193 | | | | D 0029|2700 [LO] [] [L1]
194 | | | | D 0029|276a [FL] [] [0.35]
195 | | | | S 0029|27c0 [SQ] []
196 | | | | | --- SQItem number 0
197 | | | | | D fffe|e000 [UL] [Item ]
198 | | | | | D 0008|0000 [UL] [Group Length] [12]
199 | | | | | D 0008|0001 [UL] [Length to End (RET) ] [110]
200 | | | | | D 0029|0000 [UL] [Group Length] [98]
201 | | | | | D 0029|0027 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;3 ]
202 | | | | | D 0029|27b0 [SL] [] [0]
203 | | | | | D 0029|27b1 [FL] [] [0]
204 | | | | | D 0029|27b2 [FL] [] [0]
205 | | | | | D 0029|27b4 [FL] [] [0]
206 | | | | | D 0029|27b9 [FL] [] [1]
207 | S 0029|2a14 [SQ] []
208 | | --- SQItem number 0
209 | | D fffe|e000 [UL] [Item ]
210 | | D 0008|0000 [UL] [Group Length] [12]
212 Probabely, you'll never have to deal with Sequences (hope so!).
214 1) How to Read a DICOM File
215 ===========================
223 1-1-1-1) Deal with the file header
225 The first step is to load the file header :
227 gdcm::File *f = new gdcm::File();
228 f->SetLoadMode(LD_NOSEQ); | depending on what
229 f->SetLoadMode(LD_NOSHADOW); | you want *not*
230 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW);| to load from the
231 f->SetLoadMode(LD_NOSHADOWSEQ); | target file
232 f->SetFileName(fileName);
236 The 'long' Data Element (>4096 char) are not loaded by default
237 You may modify this default length :
238 f->SetMaxSizeLoadEntry(int yourOwnMaxSize);
239 Or ask to force the loading of given Data Elements, whatever their size is:
240 f->AddForceLoadElement (uint16_t group, uint16_t elem);
241 before calling gdcm::File::Load();
244 Except if you are really aware about it, do *not* use SetLoadMode().
245 LD_ALL is the default.
247 Check if the file is gdcm-readable.
249 if ( !f->IsReadable() )
250 std::cout << "major troubles on [" << f->GetFileName() <<"]"
253 Decide if this is a 'File Of Interest' for you.
254 Check some fields, e.g
256 std::string StudyDate = f->GetEntryString(0x0008,0x0020);
257 std::string PatientName = f->GetEntryString(0x0010,0x0010);
258 std::string PatientID = f->GetEntryString(0x0010,0x0020);
259 std::string PatientSex = f->GetEntryString(0x0010,0x0040);
260 std::string Modality = f->GetEntryString(0x0008,0x0060);
262 (or whatever you feel like ...)
264 1-1-1-2) Load the 'pixels' in memory
266 Next step is to load the pixels in memory.
267 Uncompression (JPEG lossless, JPEG lossy, JPEG 2000, RLE, ...)
268 will be automatically performed if necessary.
270 You first have to create a 'File Helper'
272 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
274 In some images,(where BitsAllocated=16, BitsUsed=8),
275 the 'unused bits' are actually ... used for storing 'overlays'.
276 (e.g. : Patient / Aquisition / Institution informations, or drawings ...)
277 It's up to you to decide whether you want or not to load the overlays.
278 (Probabely, if you want to post-process the images, you dont' want!)
280 fh->SetKeepOverlays(true); // default is : false
282 void *imageData = fh->GetImageDataRaw();
283 uint32_t dataSize = fh->GetImageDataRawSize();
285 Generally, you work on 'Grey level' images (as opposed to RGB images).
286 Depending on the Pixel size (8/16/32 bits) and the Pixel Type (signed/unsigned),
287 you cast the imageData
289 std::string pixelType = f->GetPixelType();
291 Possible values are :
293 - "8U" unsigned 8 bit,
295 - "16U" unsigned 16 bit,
296 - "16S" signed 16 bit,
297 - "32U" unsigned 32 bit,
298 - "32S" signed 32 bit,
299 - (NEITHER 'float' NOR 'double' pixels in DICOM!)
301 int dimX = f->GetXSize();
302 int dimY = f->GetYSize();
303 int dimZ = f->GetZSize(); // meaningfull only for 'Volumes' or 'multiframe files'
304 int dimT = f->GetTSize(); // meaningfull only for 4D objects (?)
306 Sometimes, you deal with 'colour' images :-(
307 They may be stored, on disc, as :
312 Grey level images + LUT.
314 You'll get an 'RGB Pixels' image in memory if you use:
316 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
317 void *imageData = fh->GetImageData();
318 uint32_t dataSize = fh->GetImageDataSize();
320 If you don't want to convert a "Grey level images + LUT" into "RGB Pixel image" use
321 gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
322 void *imageData = fh->GetImageDataRaw();
323 uint32_t dataSize = fh->GetImageDataRawSize();
325 Now, you can enjoy your image !
327 1-1-1-3) Get the value of a single Dicom DataElement
329 1-1-1-3-1) as a std::string
331 - some DataEntries are 'human readable' (those whose VR is AE, DA, DS, PN, SH, TM)
332 Get their value using group number-element number :
333 std::string patientName = f->GetEntryString(0x0010,0x0010);
335 - Some DataEntries are stored with their own binary representation, but maybe you feel like
336 to get them in a 'human readable' form (those whose VR is FL, FD, SL, SS, UL, US)
337 Get their value using group number-element number :
338 std::string rowNumber =f->GetEntryString(0x0028, 0x0010);// nb of Rows
340 (of course, the very often used DataEntries have their own accessors :
341 e.g. GetXsize, GetYSize, GetSpacing, GetImageOrientationPatient, GetImagePositionPatient,
342 GetRescaleSlope, GetRescaleIntercept, GetNumberOfScalarComponents, etc -see gdcmFile.h-)
345 1-1-1-3-3) as a void* pointer
346 - Some DataEntries are stored with their own binary representation, and you want to get them 'as they are'.
347 You will have to cast them, according to the knowledge you have about them.
348 LutRedData = (uint8_t*)f->GetEntryBinArea( 0x0028, 0x1201 );
350 1-1-1-4) Get the value(s) of a Dicom Sequence
352 Actually, a 'Dicom Sequence' is composed of a list a 'Sequence Items',
353 each Sequence Item is a set of DataElement (that can be a Sequence Element, recursively).
354 You have to get the Sequence element, to get its number of Sequence items, to iterate on each one.
357 SeqEntry *seqEntry = f->GetSeqEntry(0x3006,0x0020); //Structure Set ROI sequence
358 unsigned int n = seqEntry->GetNumberOfSQItems(); // useless : just to see !
359 currentItem = seqEntry->GetFirstSQItem(); // Get the first ROI
360 while (currentItem != NULL) {
361 std::string roiName = currentItem->GetEntryString(0x3006,0x0026); //ROI name
362 std::string roiDescr = currentItem->GetEntryString(0x3006,0x0028); //ROI description
364 // do what you want with the current ROI
365 currentItem = seqEntry->GetNextSQItem(); // Get the next ROI
372 If you are 150 % sure of the files you're dealing with, just read the files you
373 feel like, and concatenate the pixels.
375 Sometimes you are not sure at all (say : you were given a CDROM with an amount
376 of images, laying in a Directories tree-like structure, you don't know anything
377 about the Patients, and so on)
379 A class gdcm::SerieHelper is designed to help solving this problem.
382 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
383 while (int i=0; i < nbOfFiles; i++) {
384 sh->AddFileName(currentFileName[i]);
387 You can also pass a 'root directory', and ask or not for recursive parsing.
388 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
389 sh->SetDirectory(yourRootDirectoryName, true); // true : recursive parsing
391 Files are 'splitted' into as many 'Single Serie UID File Set'
392 as Series Instance UID ( 0020|000e );
394 // -------- skip this one, for a first reading ! -----------
396 Sometimes, the Serie UID is not enough to disseminate properly the images.
397 We may want to disseminate into multiple sub series when needed.
400 void SerieHelper::SetUseSeriesDetails(bool s);
401 /// This function will add the following DICOM tag as being part of a
403 /// 0020 0011 Series Number
404 /// 0018 0024 Sequence Name
405 /// 0018 0050 Slice Thickness
407 /// 0028 0011 Columns
409 If it's not enough for you, use :
410 void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
412 std::string SerieHelper::CreateUniqueSeriesIdentifier(gdcm::File *inFile);
413 void SerieHelper::CreateDefaultUniqueSeriesIdentifier();
415 You may also create a "tokenizable' File Identifier of your own, using :
416 void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
418 std::string SerieHelper::CreateUserDefinedFileIdentifier(gdcm::File *inFile);
419 and use it as you feel like.
421 // ------------ Resume reading, here !--------------------
423 If you want to 'order' the files within each 'Single Serie UID File Set'
424 (to build a volume, for instance), use :
426 gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
429 sh->OrderFileList(l); // sort the list
430 l = sh->GetNextSingleSerieUIDFileSet();
433 The sorting will be performed on the ImagePositionPatient;
434 if not found, on ImageNumber;
435 if not found, on the File Name.
437 Aware user is allowed to pass his own comparison function
438 (if he knows, for instance, the files must be sorted on 'Trigger Time')
439 He will use the method
440 void SerieHelper::SetUserLessThanFunction( bool(*) userFunc((File *,File *) );
441 He may ask for a reverse sorting :
442 sh->SetSortOrderToReverse();
443 or back to Direct Order
444 sh->SetSortOrderToDirect();
446 If, for any reason of his own, user already get the file headers,
447 he may add the gdcm::File (instead of the file name) to the SerieHelper.
449 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
450 while (int i=0; i < nbOfFiles; i++) {
451 sh->AddFile(currentFile[i]);
453 * \warning : this method should be used by aware users only!
454 * User is supposed to know the files he want to deal with
455 * and consider them they belong to the same Set
456 * (even if their Serie UID is different)
457 * user will probabely OrderFileList() this list (actually, ordering
458 * user choosen gdm::File is the sole interest of this method)
459 * Moreover, using vtkGdcmReader::SetCoherentFileList() will avoid
460 * vtkGdcmReader parsing twice the same files.
461 * *no* coherence check is performed, but those specified
462 * by SerieHelper::AddRestriction()
464 User may want to exclude some files.
466 void SerieHelper::AddRestriction(uint16_t group, uint16_t elem,
467 std::string const &value, int op);
470 /// \brief comparison operators
478 gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
479 sh->AddRestriction(0x0010,0x0040,"F",GDCM_EQUAL); // Patient's Sex
480 sh->AddRestriction(0x0008,0x0060,"MR",GDCM_DIFFERENT); // Modality
481 while (int i=0; i < nbOfFiles; i++) {
483 User wants to deal only with Female patient, any Modality but MR (why not?)
485 Maybe user knows there are several images with the same position
486 and *no dicom field* may discriminates them.
487 He wants to drop the 'duplicate images'
489 sh->SetDropDuplicatePositions(true);
491 Sometimes the previous stuff is *not enough* !
493 Within a SingleSerieUIDFileSet, you can have have various orientations,
494 or various positions, at various times. (not only various position , at a single
495 time, for a single orientation).
497 User may consider that dealing only with the 'Series Instance UID'
498 is not enough and wishes to 'refine' the image selection :
500 Suppose he has a Single Serie UID File Set (gdcm::FileList).
501 He may ask to split it into several 'X Coherent File Sets' (X stands for
504 gdcm::SerieHelper *s;
505 gdcm::XCoherentFileSetmap xcm;
506 gdcm::FileList *l = s->GetFirstSingleSerieUIDFileSet(); // or what you want
509 The following methods must be called by user, depending on
510 what *he* wants to do, at application time.
511 - *he* only knows what his Series contain ! -
512 They return a std::map of Filesets (actually : a std::map of std::vector<gdcm::File* >).
514 He may ask for 'splitting' on the Orientation:
515 gdcm::XCoherentFileSetmap xcm = s->SplitOnOrientation(l);
517 He may ask for 'splitting' on the Position:
518 gdcm::XCoherentFileSetmap xcm = s->SplitOnPosition(l);
520 He may ask for 'splitting' on the any DataElement you feel like :
521 gdcm::XCoherentFileSetmap xcm = s->SplitOnTagValue(l, group,elem);
524 He can now work on each 'X Coherent File Set' within the std::map
526 for (gdcm::XCoherentFileSetmap::iterator i = xcm.begin();
531 // ask for 'ordering' according to the 'Image Position Patient'
532 // Sorting the Fileset (*) is mandatory!
533 // ( computing an accurate Series ZSpacing -whenever possible- is a side effect ...)
535 s->OrderFileList((*i).second); // sort the XCoherent Fileset
538 (have a look at gdcm/Examples/exXCoherentFileSet.cxx for an example)
542 a vtkGdcmReader() method ( derived from vtkReader() ) is available.
547 vtkGdcmReader *reader = vtkGdcmReader::New();
548 reader->SetFileName( yourDicomFilename );
549 reader->SetLoadMode( yourLoadMode); // See C++ part
550 reader->SetKeepOverlays( true/false); // See C++ part
552 vtkImageData* ima = reader->GetOutput();
553 int* Size = ima->GetDimensions();
560 If you are 150 % sure of the files you're dealing with, just 'add' the files you
563 vtkGdcmReader *reader = vtkGdcmReader::New();
564 for(int i=1; i< yourNumberOfFiles; i++)
565 reader->AddFileName( yourTableOfFileNames[i] );
566 reader->SetLoadMode( yourLoadMode); // See C++ part
567 reader->SetKeepOverlays( true/false); // See C++ part
569 vtkImageData* ima = reader->GetOutput();
570 int* Size = ima->GetDimensions();
574 Warning : The first file is assumed to be the reference file.
575 All the inconsistent files (different sizes, pixel types, etc) are discarted
576 and replaced by a black image !
578 User is allowed to pass a Pointer to a function of his own
579 to allow modification of pixel order (i.e. : Mirror, UpsideDown, )
580 to gdcm::FileHeleper, using SetUserFunction(userSuppliedFunction)
582 described as : void userSuppliedFunction(uint8_t *im, gdcm::File *f);
584 NB : the "uint8_t *" type of first param is just for prototyping.
585 User will Cast it according what he found with f->GetPixelType()
586 See vtkgdcmSerieViewer for an example
589 Many users expect from vtkGdcmReader it 'orders' the images
590 (Actually, that's the job of gdcm::SerieHelper ...)
591 When user knows the files with same Serie UID have same sizes,
592 same 'pixel' type, same color convention, ...
593 the right way to proceed is as follow :
595 gdcm::SerieHelper *sh= new gdcm::SerieHelper();
596 // if user wants *not* to load some parts of the file headers
597 sh->SetLoadMode(yourLoadMode);
599 // if user wants *not* to load some files
600 sh->AddRestriction(group, element, value, operator);
601 sh->AddRestriction( ...
602 sh->SetDirectory(directoryWithImages);
604 // if user *knows* how to order his files
605 sh->SetUserLessThanFunction(userSuppliedComparisonFunction);
607 // if user wants to sort reverse order
608 sh->SetSortOrderToReverse();
610 // here, we suppose only the first 'Single SerieUID' Fileset is of interest
611 // Just iterate using sh->NextSingleSerieUIDFileSet()
612 // if you want to get all of them
613 gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
615 // if user is doesn't trust too much the files with same Serie UID
616 if ( !sh->IsCoherent(l) )
617 return; // not same sizes, or not same 'pixel type' -> stop
619 // Maybe user knows there are several images with the same position
620 // and *no dicom field* may discriminates them.
621 // He wants to drop the 'duplicate images'
623 sh->SetDropDuplicatePositions(true);
625 // Sorting the Fileset (*) is mandatory!
626 // ( computing an accurate Series ZSpacing is a side effect ...)
627 sh->OrderFileList(l);
629 vtkGdcmReader *reader = vtkGdcmReader::New();
630 // if user wants to modify pixel order (Mirror, TopDown, ...)
631 // he has to supply the function that does the job
632 // (a *very* simple example is given in vtkgdcmSerieViewer.cxx)
633 reader->SetUserFunction (userSuppliedFunction);
635 // to pass a 'Single SerieUID' Fileset as produced by gdcm::SerieHelper
636 reader->SetCoherentFileList(l);
642 User may also pass an 'X Coherent Fileset', created by one of the following
643 methods : (see 1-1-2 for more details)
645 xcm = sh->SplitOnOrientation(l);
646 xcm = sh->SplitOnPosition(l);
647 xcm = sh->SplitOnTagValue(l, groupelem[0],groupelem[1]);
651 You can see a full example in vtk/vtkgdcmSerieViewer2.cxx
653 vtkgdcmSerieViewer dirname=Dentist mirror
654 vtkgdcmSerieViewer dirname=Dentist reverse
655 vtkgdcmSerieViewer dirname=Dentist reverse upsidedown
658 1-4) Retrictions for Python users
659 ---------------------------------
661 None of the methods receiving a function pointer, or a gdcm::File as a parameter
666 2) How to write DICOM file
667 ==========================
674 In C++, if you have already the pixels in main memory,
675 you just have to process as follow :
677 --> Create an empty gdcm::File
678 gdcm::File *file = gdcm::File::New();
680 std::ostringstream str;
682 // --> Set the mandatory fields
683 // Set the image size
686 file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
689 file->InsertEntryString(str.str(),0x0028,0x0011,"US"); // Columns
692 file->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Nbr of Frames
693 // Set the pixel type
695 str << componentSize; //8, 16, 32
696 file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
698 str << componentUse; // may be 12 or 16 if componentSize =16
699 file->InsertEntryString(str.str(),0x0028,0x0101,"US"); // Bits Stored
701 str << componentSize - 1 ;
702 file->InsertEntryString(str.str(),0x0028,0x0102,"US"); // High Bit
703 // Set the pixel representation // 0/1
706 file->InsertEntryString(str.str(),0x0028,0x0103, "US"); // Pixel Representation
707 // Set the samples per pixel // 1:Grey level, 3:RGB
710 file->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
712 //--> Set Optional fields
713 see further how to deal with optional fields
715 //--> Create a gdcm::FileHelper
716 gdcm::FileHelper *fileH = gdcm::FileHelper::New(file);
718 //--> Tell the FileHelper what you did for creating the image:
720 // gdcm cannot guess how user built his image
721 // (and therefore cannot be clever about some Dicom fields)
722 // It's up to the user to tell gdcm what he did.
723 // -1) user created ex nihilo his own image and wants to write it
726 // -2) user modified the pixels of an existing image.
728 // -3) user created a new image, using existing a set of images
729 // (eg MIP, MPR, cartography image)
731 // -4) user modified/added some tags *without processing* the pixels
732 // (anonymization, ...)
733 // UNMODIFIED_PIXELS_IMAGE
734 // -Probabely some more to be added
735 //(see gdcmFileHelper.h for more explanations)
739 fileH->SetContentType(GDCM_NAME_SPACE::USER_OWN_IMAGE);
740 fileH->SetContentType(GDCM_NAME_SPACE::FILTERED_IMAGE);
741 fileH->SetContentType(GDCM_NAME_SPACE::CREATED_IMAGE);
742 fileH->SetContentType(GDCM_NAME_SPACE::UNMODIFIED_PIXELS_IMAGE);
744 // depending on what you did before!
746 //--> Set the compression type :
747 fileH->SetWriteTypeToJPEG(); // lossless compression
748 fileH->SetWriteTypeToJPEG2000(); // lossless compression
749 fileH->SetWriteTypeToDcmExplVR(); // Explicit Value Representation (no compression)
750 fileH->SetWriteTypeToDcmImplVR(); // Implicit Value Representation (no compression)
752 fileH->SetWriteModeToRaw(); // Probabely you don't want to convert any LUT into RGB pixels ...
754 //--> Set the Image Data
755 fileH->SetImageData((unsigned char *)imageData,size);
756 // ( Casting as 'unsigned char *' is just to avoid warnings.
757 // It doesn't change the values. )
759 fileH->SetUserData((unsigned char *)imageData,size); // performs compression, when required
760 // ( Casting as 'unsigned char *' is just to avoid warnings.
761 // It doesn't change the values. )
764 fileH->Write(fileName.str());
766 //This works for a single image (singleframe or multiframe)
768 2-1-1-1) Deal with optional DataElements // TODO : finish it
770 Any Data Element may be added (it's up to the user to understand what he is doing!)
771 The supplied methods 'InsertXxx' will create the DataElement or replace it if it already exists.
772 Have a look at gdcm/Dict/dicomV3.dic to see what are the various DICOM fields, with their VR.
774 2-1-1-1-1) Add a single Dicom DataElement // TODO : finish it
777 DataEntry * File::InsertEntryString(std::string const &value,
778 uint16_t group, uint16_t elem,
779 VRKey const &vr = GDCM_VRUNKNOWN);
781 // (e.g. : patient name, patient ID, ... , or what you want,
782 // using their Dicom identifier, and 'VR'
783 file->InsertEntryString("MyOwnPatient" ,0x0010,0x0010,"PN"); // 0010 0010 : Patient's Name
785 DataEntry * File:InsertEntryBinArea(uint8_t *binArea, int lgth,
786 uint16_t group, uint16_t elem,
787 VRKey const &vr = GDCM_VRUNKNOWN);
789 2-1-1-1-2) Add a Dicom Sequence // TODO : finish it
790 SeqEntry * File::InsertSeqEntry(uint16_t group, uint16_t elem);
794 /// \todo : write it!
797 // If you deal with a Serie of images, it up to you to tell gdcm, for each image,
798 // what are the values of
799 // 0020 0032 DS 3 Image Position (Patient)
800 // 0020 0037 DS 6 Image Orientation (Patient)
802 // You will probabely want that all the images of your file set belong to the same 'Serie'
808 /// \todo : write it!
813 /// \todo : finish it!
815 // User of the CVS version of VTK 5 may set some 'Medical Image Properties'
816 // Only the predefined DataElements are available :
817 // PatientName, PatientID, PatientAge, PatientSex, PatientBirthDate, StudyID
818 // It's reasonably enough for any 'decent use'
820 // todo : explain how to use it.
821 //vtkMedicalImageProperties
823 // Aware user is allowed to pass his own gdcm::File *,
824 // so he may set *any Dicom field* he wants.
825 // (including his own Shadow Elements, or any gdcm::SeqEntry)
826 // gdcm::FileHelper::CheckMandatoryElements() will check inconsistencies,
827 // as far as it knows how.
828 // Sorry, not yet available under Python.
830 vtkSetMacro(GdcmFile, gdcm::File *);
832 void vtkGdcmWriter::SetGdcmFile(gdcm::File *);
834 // gdcm cannot guess how user built his image
835 // (and therefore cannot be clever about some Dicom fields)
836 // It's up to the user to tell gdcm what he did.
837 // -1) user created ex nihilo his own image and wants to write it
840 // -2) user modified the pixels of an existing image.
842 // -3) user created a new image, using existing a set of images
843 // (eg MIP, MPR, cartography image)
845 // -4) user modified/added some tags *without processing* the pixels
847 // UNMODIFIED_PIXELS_IMAGE
848 // -Probabely some more to be added
849 //(see gdcmFileHelper.h for more explanations)
851 // User is allowed to use the following methods:
853 void vtkGdcmWriter::SetContentTypeToUserOwnImage()
854 void vtkGdcmWriter::SetContentTypeToFilteredImage()
855 void vtkGdcmWriter::SetContentTypeToUserCreatedImage()
856 vtkGdcmWriter::void SetContentTypeToUnmodifiedPixelsImage()
858 // depending on what he did before (see C++ part)
864 /// \todo : write it!
869 /// \todo : write it!
871 2-4) Retrictions for Python users
872 ---------------------------------
873 /// \todo : write it!
875 3) Some 'Command line' utilities /// \todo: finish it!
876 ================================
880 3-) exXCoherentFileSet
883 3-) AnonymizeMultiPatient
884 3-) AnonymizeDicomDir
888 3-) exMoveImagesToSingleSerieUID
891 3-) vtkgdcmSerieViewer2