how to use efficently gdcm.
-->Feel free to send/add your comments/suggestions/modifications.
-->Don't try to 'beautify' this page.
-(I plane to rewrite it, and add it to the gdcm site as soon as
-there is enough 'content' within it)
+(I plane to rewrite it in html, as soon as there is enough 'content' within it)
HTH
Jean-Pierre Roux
------------------------------------------------------------------------
+
0) Intro
+========
1) How to Read a DICOM file
+===========================
1-1) using raw C++
1-1-1) A single file
+1-1-1-1) Deal with the file header
+1-1-1-2) Load the 'pixels' in memory
+1-1-1-3) Get the value of a single Dicom DataElement
+1-1-1-4) Get the values within a Dicom Sequence
1-1-2) A File Set
+
1-2) using VTK
1-2-1) A single file
1-2-2) A File Set
+
1-3) using ITK
1-3-1) A single file
1-3-2) A File Set
+
1-4) Retrictions for Python users
2) How to write DICOM file
+==========================
2-1) using raw C++
2-1-1) A single file
+2-1-1-1) Deal with optional DataElements
+2-1-1-1-1) Add a single Dicom DataElement // TODO
+2-1-1-1-2) Add a Dicom Sequence // TODO
2-1-2) A File Set
+
2-2) using VTK
+
2-3) using ITK
+2-4) Retrictions for Python users
+
+3) DICOMDIR
+===========
+3-1) How to read a DICIMDIR
+3-2) How to modifiy a DICOMDIR
+3-3) How to create a DICOMDIR
+
+4) Some 'Command line' utilities
+================================
+4-) PrintFile
+4-) exSerieHelper
+4-) exXCoherentFileSet
+
+4-) AnonymizeNoLoad
+4-) AnonymizeMultiPatient
+4-) AnonymizeDicomDir
+
+4-) ReWrite
+4-) RawToDicom
+4-) exMoveImagesToSingleSerieUID
+
+4-) vtkgdcmViewer2
+4-) vtkgdcmSerieViewer2
+
+4-) PrintDicomDir
+4-) MakeDicomDir
+
----------------------------------------------------------------------------
0) Intro
---------
+========
-If you are not familiar with DICOM files , use :
+If you are not familiar with DICOM files, use :
PrintFile filein=yourDicomFile.dcm
D 0020|0013 [IS] [Instance Number] [5 ]
D 0028|0010 [US] [Rows] [480]
D 0011|0010 [ ] [gdcm::Unknown] [DLX_PATNT_01]
+D 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
+ ===> [gdcm::Binary data loaded;length = 113784]
0008|0021 : 'Group Number'|'Element Number' -> The Element identifier
Have a look at gdcm/Dicts/dicomV3.dic for the whole list of Elements
0018|6011 is a 'Sequence' (SQ), composed of various Sequence Items(SQItem)
Each SQItem is a set of Elements (an Element may be a DataElement (D) or a
-Sequence (S), recursively.
+Sequence (S), recursively, within any level of embedding :
+
+S 0029|263d [SQ] []
+ | --- SQItem number 0
+ | D fffe|e000 [UL] [Item ]
+ | D 0008|0000 [UL] [Group Length] [12]
+ | D 0008|0001 [UL] [Length to End (RET) ] [28776]
+ | D 0029|0000 [UL] [Group Length] [28764]
+ | D 0029|002a [LO] [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
+ | D 0029|2a02 [LO] [] [PERFUSION_MR_T2STAR_GAMMA_VARIATE_ANALYSER]
+ | S 0029|2a06 [SQ] []
+ | | --- SQItem number 0
+ | | D fffe|e000 [UL] [Item ]
+ | | D 0008|0000 [UL] [Group Length] [12]
+ | | D 0008|0001 [UL] [Length to End (RET) ] [20]
+ | | D fffe|0000 [UL] [Group Length]
+ | | --- SQItem number 1
+ | | D fffe|e000 [UL] [Item ]
+ | | D 0008|0000 [UL] [Group Length] [12]
+ | | D 0008|0001 [UL] [Length to End (RET) ] [194]
+ | | D 0029|0000 [UL] [Group Length] [182]
+ | | D 0029|002a [LO] [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
+ | | S 0029|2a07 [SQ] []
+ | | | --- SQItem number 0
+ | | | D fffe|e000 [UL] [Item ]
+ | | | D 0008|0000 [UL] [Group Length] [12]
+ | | | D 0008|0001 [UL] [Length to End (RET) ] [100]
+ | | | D 0029|0000 [UL] [Group Length] [88]
+ | | | D 0029|002a [LO] [Private Creator] [SPI-P-Private_ICS Release 1;6 ]
+ | | | D 0029|2a0a [US] [] [1]
+ | | | D 0029|2a0b [US] [] [2]
+ | | | D 0029|2a0c [US] [] [2]
+ | | | D 0029|2a0d [US] [] [1]
+ | | | D 0029|2a10 [US] [] [1]
+ | | | S 0029|2a2e [SQ] []
+ | | | | --- SQItem number 0
+ | | | | D fffe|e000 [UL] [Item ]
+ | | | | D 0008|0000 [UL] [Group Length] [12]
+ | | | | D 0008|0001 [UL] [Length to End (RET) ] [2266]
+ | | | | D 0029|0000 [UL] [Group Length] [2254]
+ | | | | D 0029|0025 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;1 ]
+ | | | | D 0029|0027 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;3 ]
+ | | | | D 0029|0028 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;4 ]
+ | | | | D 0029|2500 [SL] [] [43]
+ | | | | D 0029|256b [FD] [] [0.5]
+ | | | | D 0029|2700 [LO] [] [L1]
+ | | | | D 0029|276a [FL] [] [0.35]
+ | | | | S 0029|27c0 [SQ] []
+ | | | | | --- SQItem number 0
+ | | | | | D fffe|e000 [UL] [Item ]
+ | | | | | D 0008|0000 [UL] [Group Length] [12]
+ | | | | | D 0008|0001 [UL] [Length to End (RET) ] [110]
+ | | | | | D 0029|0000 [UL] [Group Length] [98]
+ | | | | | D 0029|0027 [LO] [Private Creator] [SPI-P-Private_ICS Release 1;3 ]
+ | | | | | D 0029|27b0 [SL] [] [0]
+ | | | | | D 0029|27b1 [FL] [] [0]
+ | | | | | D 0029|27b2 [FL] [] [0]
+ | | | | | D 0029|27b4 [FL] [] [0]
+ | | | | | D 0029|27b9 [FL] [] [1]
+ | S 0029|2a14 [SQ] []
+ | | --- SQItem number 0
+ | | D fffe|e000 [UL] [Item ]
+ | | D 0008|0000 [UL] [Group Length] [12]
+
Probabely, you'll never have to deal with Sequences (hope so!).
1) How to Read a DICOM File
1-1-1) A single file
--------------------
-The first step is to load the file header :
+1-1-1-1) Deal with the file header
- gdcm::File *f = new gdcm::File();
- f->SetLoadMode(NO_SEQ); | depending on what
- f->SetLoadMode(NO_SHADOW); | you want *not*
- f->SetLoadMode(NO_SEQ | NO_SHADOW);| to load from the
- f->SetLoadMode(NO_SHADOWSEQ); | target file
- f->SetFileName(fileName);
- f->Load( );
-
-Except if someone told you -he knows the file headers are bugged-,
-do *not* use SetLoadMode().
+The first step is to load the file header :
+ gdcm::File *f = gdcm::File::New();
+ f->SetLoadMode(LD_NOSEQ); | depending on what
+ f->SetLoadMode(LD_NOSHADOW); | you want *not*
+ f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW);| to load from the
+ f->SetLoadMode(LD_NOSHADOWSEQ); | target file
+ f->SetFileName(fileName);
+ f->Load( );
+
+Note :
+The 'long' Data Element (>4096 char) are not loaded by default
+You may modify this default length :
+ f->SetMaxSizeLoadEntry(int yourOwnMaxSize);
+Or ask to force the loading of given Data Elements, whatever their size is:
+ f->AddForceLoadElement (uint16_t group, uint16_t elem);
+before calling gdcm::File::Load();
+
+Note :
+Except if you are really aware about it, do *not* use SetLoadMode().
+LD_ALL is the default.
Check if the file is gdcm-readable.
(or whatever you feel like ...)
+1-1-1-2) Load the 'pixels' in memory
+
Next step is to load the pixels in memory.
Uncompression (JPEG lossless, JPEG lossy, JPEG 2000, RLE, ...)
-is automatically performed if necessary.
+will be automatically performed if necessary.
+
+You first have to create a 'File Helper'
gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
+
+In some images,(where BitsAllocated=16, BitsUsed=8),
+the 'unused bits' are actually ... used for storing 'overlays'.
+(e.g. : Patient / Aquisition / Institution informations, or drawings ...)
+It's up to you to decide whether you want or not to load the overlays.
+(Probabely, if you want to post-process the images, you dont' want!)
+
+ fh->SetKeepOverlays(true); // default is : false
+
void *imageData = fh->GetImageDataRaw();
uint32_t dataSize = fh->GetImageDataRawSize();
- "16S" signed 16 bit,
- "32U" unsigned 32 bit,
- "32S" signed 32 bit,
-
+- (NEITHER 'float' NOR 'double' pixels in DICOM!)
- int dimX = f->GetXcurrentFileName[i]Size();
+ int dimX = f->GetXSize();
int dimY = f->GetYSize();
- int dimZ = f->GetZSize();
- int dimT = f->GetTSize();
-
-Now, you can enjoy your image !
+ int dimZ = f->GetZSize(); // meaningfull only for 'Volumes' or 'multiframe files'
+ int dimT = f->GetTSize(); // meaningfull only for 4D objects (?)
Sometimes, you deal with 'colour' images :-(
-They may be stored, on disc, as RGB pixels, RGB planes, YBR pixels, YBR planes
-Grey level images + LUT.
+They may be stored, on disc, as :
+ RGB pixels,
+ RGB planes,
+ YBR pixels,
+ YBR planes
+ Grey level images + LUT.
You'll get an 'RGB Pixels' image in memory if you use:
void *imageData = fh->GetImageData();
uint32_t dataSize = fh->GetImageDataSize();
+If you don't want to convert a "Grey level images + LUT" into "RGB Pixel image" use
+ gdcm::FileHelper *fh = gdcm::FileHelper::New(f);
+ void *imageData = fh->GetImageDataRaw();
+ uint32_t dataSize = fh->GetImageDataRawSize();
+
+Now, you can enjoy your image !
+
+1-1-1-3) Get the value of a single Dicom DataElement
+
+1-1-1-3-1) as a std::string
+
+- some DataEntries are 'human readable' (those whose VR is AE, DA, DS, PN, SH, TM)
+ Get their value using group number-element number :
+ std::string patientName = f->GetEntryString(0x0010,0x0010);
+
+- Some DataEntries are stored with their own binary representation, but maybe you feel like
+ to get them in a 'human readable' form (those whose VR is FL, FD, SL, SS, UL, US)
+ Get their value using group number-element number :
+ std::string rowNumber =f->GetEntryString(0x0028, 0x0010);// nb of Rows
+
+ (of course, the very often used DataEntries have their own accessors :
+ e.g. GetXsize, GetYSize, GetSpacing, GetImageOrientationPatient, GetImagePositionPatient,
+ GetRescaleSlope, GetRescaleIntercept, GetNumberOfScalarComponents, etc -see gdcmFile.h-)
+
+
+1-1-1-3-3) as a void* pointer
+- Some DataEntries are stored with their own binary representation, and you want to get them 'as they are'.
+ You will have to cast them, according to the knowledge you have about them.
+ LutRedData = (uint8_t*)f->GetEntryBinArea( 0x0028, 0x1201 );
+
+1-1-1-4) Get the value(s) within a Dicom Sequence
+
+Actually, a 'Dicom Sequence' is composed of a list a 'Sequence Items',
+each Sequence Item is a set of DataElement (that can be Dicom Sequences, recursively).
+You have to get the Sequence element, to get its number of Sequence items, to iterate on each one.
+e.g.:
+
+SeqEntry *seqEntry = f->GetSeqEntry(0x3006,0x0020); //Structure Set ROI sequence
+unsigned int n = seqEntry->GetNumberOfSQItems(); // useless : just to see !
+currentItem = seqEntry->GetFirstSQItem(); // Get the first ROI
+while (currentItem != NULL) {
+ std::string roiName = currentItem->GetEntryString(0x3006,0x0026); //ROI name
+ std::string roiDescr = currentItem->GetEntryString(0x3006,0x0028); //ROI description
+ ...
+ // do what you want with the current ROI
+ currentItem = seqEntry->GetNextSQItem(); // Get the next ROI
+}
+
1-1-2) A File Set
-----------------
about the Patients, and so on)
A class gdcm::SerieHelper is designed to help solving this problem.
-Use it as follows.
+Use it as follows :
gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
- while (int i=0; i<nbOfFiles; i++) {
+ while (int i=0; i < nbOfFiles; i++) {
sh->AddFileName(currentFileName[i]);
}
Files are 'splitted' into as many 'Single Serie UID File Set'
as Series Instance UID ( 0020|000e );
-
+
+// -------- skip this one, for a first reading ! -----------
+
+Sometimes, the Serie UID is not enough to disseminate properly the images.
+We may want to disseminate into multiple sub series when needed.
+
+Use :
+void SerieHelper::SetUseSeriesDetails(bool s);
+ /// This function will add the following DICOM tag as being part of a
+ /// 'fake' uid. :
+ /// 0020 0011 Series Number
+ /// 0018 0024 Sequence Name
+ /// 0018 0050 Slice Thickness
+ /// 0028 0010 Rows
+ /// 0028 0011 Columns
+
+If it's not enough for you, use :
+void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
+
+std::string SerieHelper::CreateUniqueSeriesIdentifier(gdcm::File *inFile);
+void SerieHelper::CreateDefaultUniqueSeriesIdentifier();
+
+You may also create a "tokenizable' File Identifier of your own, using :
+void SerieHelper::AddSeriesDetail(uint16_t group, uint16_t elem, bool convert);
+and
+std::string SerieHelper::CreateUserDefinedFileIdentifier(gdcm::File *inFile);
+and use it as you feel like.
+
+// ------------ Resume reading, here !--------------------
+
If you want to 'order' the files within each 'Single Serie UID File Set'
(to build a volume, for instance), use :
or back to Direct Order
sh->SetSortOrderToDirect();
- If, for any reason of is own, user already get the file headers,
+ If, for any reason of his own, user already get the file headers,
he may add the gdcm::File (instead of the file name) to the SerieHelper.
+ (Sorry, not available in Python)
gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
- while (int i=0; i<nbOfFiles; i++) {
+ while (int i=0; i < nbOfFiles; i++) {
sh->AddFile(currentFile[i]);
}
* \warning : this method should be used by aware users only!
std::string const &value, int op);
op belongs to :
-/// \brief comparaison operators
+/// \brief comparison operators
GDCM_EQUAL ,
GDCM_DIFFERENT,
GDCM_GREATER,
gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
sh->AddRestriction(0x0010,0x0040,"F",GDCM_EQUAL); // Patient's Sex
sh->AddRestriction(0x0008,0x0060,"MR",GDCM_DIFFERENT); // Modality
- while (int i=0; i<nbOfFiles; i++) {
+ while (int i=0; i < nbOfFiles; i++) {
...
User wants to deal only with Female patient, any Modality but MR (why not?)
+
+Maybe user knows there are several images with the same position
+and *no dicom field* may discriminates them.
+He wants to drop the 'duplicate images'
+
+ sh->SetDropDuplicatePositions(true);
Sometimes the previous stuff is *not enough* !
Within a SingleSerieUIDFileSet, you can have have various orientations,
-or various positions, at various times. (not only various position , at a single
+or various positions, at various times. (not only various positions, at a single
time, for a single orientation).
User may consider that dealing only with the 'Series Instance UID'
The following methods must be called by user, depending on
what *he* wants to do, at application time.
- *he* only knows what his Series contain ! -
- They return a std::map of Filesets (actually : std::vector of gdcm::File*).
+ They return a std::map of Filesets (actually : a std::map of std::vector<gdcm::File* >).
He may ask for 'splitting' on the Orientation:
- xcm = s->SplitOnOrientation(l);
+ gdcm::XCoherentFileSetmap xcm = s->SplitOnOrientation(l);
He may ask for 'splitting' on the Position:
- xcm = s->SplitOnPosition(l);
+ gdcm::XCoherentFileSetmap xcm = s->SplitOnPosition(l);
He may ask for 'splitting' on the any DataElement you feel like :
- xcm = s->SplitOnTagValue(l, groupelem[0],groupelem[1]);
+ gdcm::XCoherentFileSetmap xcm = s->SplitOnTagValue(l, group,elem);
He can now work on each 'X Coherent File Set' within the std::map
{
// ask for 'ordering' according to the 'Image Position Patient'
+ // Sorting the Fileset (*) is mandatory!
+ // ( computing an accurate Series ZSpacing -whenever possible- is a side effect ...)
+
s->OrderFileList((*i).second); // sort the XCoherent Fileset
}
-(have a look at gdcm/Examples/exXCoherentFileSet.cxx for an exmaple)
+(have a look at gdcm/Examples/exXCoherentFileSet.cxx for an example)
1-2) using VTK
--------------
a vtkGdcmReader() method ( derived from vtkReader() ) is available.
-
-
1-2-1) A single file
--------------------
vtkGdcmReader *reader = vtkGdcmReader::New();
reader->SetFileName( yourDicomFilename );
- reader->SetLoadMode( yourLoadMode); // See C++ part
+ reader->SetLoadMode( yourLoadMode ); // See C++ part
+ reader->SetKeepOverlays( true/false ); // See C++ part
reader->Update();
vtkImageData* ima = reader->GetOutput();
- int* Size = ima->GetDimensions();
+ int* Size = ima->GetDimensions();
+
// -> Enjoy it.
1-2-2) A File Set
vtkGdcmReader *reader = vtkGdcmReader::New();
for(int i=1; i< yourNumberOfFiles; i++)
reader->AddFileName( yourTableOfFileNames[i] );
- reader->SetLoadMode( yourLoadMode); // See C++ part
+ reader->SetLoadMode( yourLoadMode ); // See C++ part
+ reader->SetKeepOverlays( true/false ); // See C++ part
reader->Update();
vtkImageData* ima = reader->GetOutput();
int* Size = ima->GetDimensions();
+
// -> Enjoy it.
Warning : The first file is assumed to be the reference file.
and replaced by a black image !
User is allowed to pass a Pointer to a function of his own
- to allow modification of pixel order (i.e. : Mirror, TopDown, )
+ to allow modification of pixel order (i.e. : Mirror, UpsideDown, )
to gdcm::FileHeleper, using SetUserFunction(userSuppliedFunction)
described as : void userSuppliedFunction(uint8_t *im, gdcm::File *f);
same 'pixel' type, same color convention, ...
the right way to proceed is as follow :
- gdcm::SerieHelper *sh= new gdcm::SerieHelper();
+ gdcm::SerieHelper *sh= gdcm::NewSerieHelper();
// if user wants *not* to load some parts of the file headers
sh->SetLoadMode(yourLoadMode);
// if user is doesn't trust too much the files with same Serie UID
if ( !sh->IsCoherent(l) )
return; // not same sizes, or not same 'pixel type' -> stop
+
+ // Maybe user knows there are several images with the same position
+ // and *no dicom field* may discriminates them.
+ // He wants to drop the 'duplicate images'
+
+ sh->SetDropDuplicatePositions(true);
- sh->OrderFileList(l); // sort the list (*)
+ // Sorting the Fileset (*) is mandatory!
+ // ( computing an accurate Series ZSpacing is a side effect ...)
+ sh->OrderFileList(l);
vtkGdcmReader *reader = vtkGdcmReader::New();
// if user wants to modify pixel order (Mirror, TopDown, ...)
xcm = sh->SplitOnTagValue(l, groupelem[0],groupelem[1]);
//----------------
-You can see a full example in vtk/vtkgdcmSerieViewer.cxx
+
+You can see a full example in vtk/vtkgdcmSerieViewer2.cxx
e.g.
vtkgdcmSerieViewer dirname=Dentist mirror
vtkgdcmSerieViewer dirname=Dentist reverse
2) How to write DICOM file
==========================
- // gdcm cannot guess how user built his image
- // (and therefore cannot be clever about some Dicom fields)
- // It's up to the user to tell gdcm what he did.
- // -1) user created ex nihilo his own image and wants to write it
- // as a Dicom image.
- // USER_OWN_IMAGE
- // -2) user modified the pixels of an existing image.
- // FILTERED_IMAGE
- // -3) user created a new image, using existing a set of images
- // (eg MIP, MPR, cartography image)
- // CREATED_IMAGE
- // -4) user modified/added some tags *without processing* the pixels
- // (anonymization..
- // UNMODIFIED_PIXELS_IMAGE
- // -Probabely some more to be added
- //(see gdcmFileHelper.h for more explanations)
-
- // User is allowed to use the following methods:
-
- void SetContentTypeToUserOwnImage()
- {SetContentType(VTK_GDCM_WRITE_TYPE_USER_OWN_IMAGE);}
- void SetContentTypeToFilteredImage()
- {SetContentType(VTK_GDCM_WRITE_TYPE_FILTERED_IMAGE);}
- void SetContentTypeToUserCreatedImage()
- {SetContentType(VTK_GDCM_WRITE_TYPE_CREATED_IMAGE);}
- void SetContentTypeToUnmodifiedPixelsImage()
- {SetContentType(VTK_GDCM_WRITE_TYPE_UNMODIFIED_PIXELS_IMAGE);}
-
2-1) using raw C++
------------------
+
+2-1-1) A single file
+--------------------
In C++, if you have already the pixels in main memory,
you just have to process as follow :
std::ostringstream str;
// --> Set the mandatory fields
-// Set the image size
+ // Set the image size
str.str("");
str << sizeY;
file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
str.str("");
str << sizeZ;
file->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Nbr of Frames
- // Set the pixel type
+ // Set the pixel type
str.str("");
str << componentSize; //8, 16, 32
file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
file->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
//--> Set Optional fields
-//(patient name, patient ID, modality, or what you want
-//Have look at gdcm/Dict/dicomV3.dic to see what are the various DICOM fields)
+ see further how to deal with optional fields
//--> Create a gdcm::FileHelper
-
gdcm::FileHelper *fileH = gdcm::FileHelper::New(file);
- fileH->SetImageData((unsigned char *)imageData,size);
-
-//casting as 'unsigned char *' is just to avoid warnings.
-// It doesn't change the values.
- fileH->SetWriteModeToRaw();
- fileH->SetWriteTypeToDcmExplVR();
+//--> Tell the FileHelper what you did for creating the image:
+
+ // gdcm cannot guess how user built his image
+ // (and therefore cannot be clever about some Dicom fields)
+ // It's up to the user to tell gdcm what he did.
+ // -1) user created ex nihilo his own image and wants to write it
+ // as a Dicom image.
+ // USER_OWN_IMAGE
+ // -2) user modified the pixels of an existing image.
+ // FILTERED_IMAGE
+ // -3) user created a new image, using existing a set of images
+ // (eg MIP, MPR, cartography image)
+ // CREATED_IMAGE
+ // -4) user modified/added some tags *without processing* the pixels
+ // (anonymization, ...)
+ // UNMODIFIED_PIXELS_IMAGE
+ // -Probabely some more to be added
+ //(see gdcmFileHelper.h for more explanations)
+
+ // Use :
+
+ fileH->SetContentType(GDCM_NAME_SPACE::USER_OWN_IMAGE);
+ fileH->SetContentType(GDCM_NAME_SPACE::FILTERED_IMAGE);
+ fileH->SetContentType(GDCM_NAME_SPACE::CREATED_IMAGE);
+ fileH->SetContentType(GDCM_NAME_SPACE::UNMODIFIED_PIXELS_IMAGE);
+
+ // depending on what you did before!
+
+//--> Set the compression type :
+ fileH->SetWriteTypeToJPEG(); // lossless compression
+ fileH->SetWriteTypeToJPEG2000(); // lossless compression
+ fileH->SetWriteTypeToDcmExplVR(); // Explicit Value Representation (no compression)
+ fileH->SetWriteTypeToDcmImplVR(); // Implicit Value Representation (no compression)
+
+ fileH->SetWriteModeToRaw(); // Probabely you don't want to convert any LUT into RGB pixels ...
+
+//--> Set the Image Data
+ fileH->SetImageData((unsigned char *)imageData,size);
+ // ( Casting as 'unsigned char *' is just to avoid warnings.
+ // It doesn't change the values. )
+ // or
+ fileH->SetUserData((unsigned char *)imageData,size); // performs compression, when required
+ // ( Casting as 'unsigned char *' is just to avoid warnings.
+ // It doesn't change the values. )
+
+//-> Write !
fileH->Write(fileName.str());
//This works for a single image (singleframe or multiframe)
-// If you deal with a Serie of images, it up to you to tell gdcm, for each image,
-// what are the values of
-// 0020 0032 DS 3 Image Position (Patient)
-// 0020 0037 DS 6 Image Orientation (Patient)
+2-1-1-1) Deal with optional DataElements // TODO : finish it
-2-1-1) A single file
---------------------
-/// \todo : write it!
+ Any Data Element may be added (it's up to the user to understand what he is doing!)
+ The supplied methods 'InsertXxx' will create the DataElement or replace it if it already exists.
+ Have a look at gdcm/Dict/dicomV3.dic to see what are the various DICOM fields, with their VR.
+
+2-1-1-1-1) Add a single Dicom DataElement // TODO : finish it
+ use :
+ DataEntry * File::InsertEntryString(std::string const &value,
+ uint16_t group, uint16_t elem,
+ VRKey const &vr = GDCM_VRUNKNOWN);
+
+ // (e.g. : patient name, patient ID, ... , or what you want,
+ // using their Dicom identifier, and 'VR'
+ file->InsertEntryString("MyOwnPatient" ,0x0010,0x0010,"PN"); // 0010 0010 : Patient's Name
+
+ DataEntry * File:InsertEntryBinArea(uint8_t *binArea, int lgth,
+ uint16_t group, uint16_t elem,
+ VRKey const &vr = GDCM_VRUNKNOWN);
+
+2-1-1-1-2) Add a Dicom Sequence // TODO : finish it
+ SeqEntry * File::InsertSeqEntry(uint16_t group, uint16_t elem);
+
2-1-2) A File Set
-----------------
/// \todo : write it!
+// If you deal with a Serie of images, it up to you to tell gdcm, for each image,
+// what are the values of
+// 0020 0032 DS 3 Image Position (Patient)
+// 0020 0037 DS 6 Image Orientation (Patient)
+
+// You will probabely want that all the images of your file set belong to the same 'Serie'
+
2-2) using VTK
--------------
+/// \todo : write it!
+
2-2-1) A single file
--------------------
+/// \todo : finish it!
+
// User of the CVS version of VTK 5 may set some 'Medical Image Properties'
// Only the predefined DataElements are available :
// PatientName, PatientID, PatientAge, PatientSex, PatientBirthDate, StudyID
-// It's reasonablt enough for any 'decent use'
-vtkMedicalImageProperties
+// It's reasonably enough for any 'decent use'
+//
+// todo : explain how to use it.
+//vtkMedicalImageProperties
// Aware user is allowed to pass his own gdcm::File *,
// so he may set *any Dicom field* he wants.
- // (including his own Shadow Eleents, or any gdcm::SeqEntry)
+ // (including his own Shadow Elements, or any gdcm::SeqEntry)
// gdcm::FileHelper::CheckMandatoryElements() will check inconsistencies,
// as far as it knows how.
// Sorry, not yet available under Python.
-vtkSetMacro(GdcmFile, gdcm::File *);
+
+ vtkSetMacro(GdcmFile, gdcm::File *);
+
+void vtkGdcmWriter::SetGdcmFile(gdcm::File *);
+ // gdcm cannot guess how user built his image
+ // (and therefore cannot be clever about some Dicom fields)
+ // It's up to the user to tell gdcm what he did.
+ // -1) user created ex nihilo his own image and wants to write it
+ // as a Dicom image.
+ // USER_OWN_IMAGE
+ // -2) user modified the pixels of an existing image.
+ // FILTERED_IMAGE
+ // -3) user created a new image, using existing a set of images
+ // (eg MIP, MPR, cartography image)
+ // CREATED_IMAGE
+ // -4) user modified/added some tags *without processing* the pixels
+ // (anonymization..
+ // UNMODIFIED_PIXELS_IMAGE
+ // -Probabely some more to be added
+ //(see gdcmFileHelper.h for more explanations)
+
+ // User is allowed to use the following methods:
+
+void vtkGdcmWriter::SetContentTypeToUserOwnImage()
+void vtkGdcmWriter::SetContentTypeToFilteredImage()
+void vtkGdcmWriter::SetContentTypeToUserCreatedImage()
+vtkGdcmWriter::void SetContentTypeToUnmodifiedPixelsImage()
+
+ // depending on what he did before (see C++ part)
+
+
2-2-2) A File Set
-----------------
--------------
/// \todo : write it!
+2-4) Retrictions for Python users
+---------------------------------
+/// \todo : write it!
+
+
+3) DICOMDIR /// \todo: finish it!
+===========
+3-1) How to read a DICIMDIR
+3-2) How to modifiy a DICOMDIR
+3-3) How to create a DICOMDIR
+
+
+4) Some 'Command line' utilities /// \todo: finish it!
+================================
+
+4-) PrintFile
+4-) exSerieHelper
+4-) exXCoherentFileSet
+
+4-) AnonymizeNoLoad
+4-) AnonymizeMultiPatient
+4-) AnonymizeDicomDir
+4-) PatchHeader
+4-) ReWrite
+4-) RawToDicom
+4-) exMoveImagesToSingleSerieUID
+
+4-) vtkgdcmViewer2
+4-) vtkgdcmSerieViewer2
+
+4-) PrintDicomDir
+4-) MakeDicomDir
+
+
+ * PrintFile
+
+ Display the header of a ACR-NEMA/PAPYRUS/DICOM File
+ usage: PrintFile {filein=inputFileName|dirin=inputDirectoryName}[level=n]
+ [forceload=listOfElementsToForceLoad] [rec] [noex]
+ [4DLoc= ][dict= privateDirectory]
+ [ { [noshadowseq] | [noshadow][noseq] } ]
+ [debug] [warning]
+ level = 0,1,2 : depending on the amount of details user wants to see
+ rec : user wants to parse recursively the directory
+ noex : user doen't want extra 'user friendly' info
+ 4DLoc: group-elem(in hexa, no space) of the DataEntry holdind 4thDim
+ listOfElementsToForceLoad : group-elem,g2-e2,... (in hexa, no space)
+ of Elements to load whatever their length
+ privateDirectory : source file full path name of Shadow Group elems
+ noshadowseq: user doesn't want to load Private Sequences
+ noshadow : user doesn't want to load Private groups (odd number)
+ noseq : user doesn't want to load Sequences
+ debug : user wants to run the program in 'debug mode'
+ warning : user wants to be warned about any oddity in the File
+ showlut :user wants to display the Palette Color (as an int array)
+
+ * Anonymize
+
+ Anonymizes a full gdcm-readable Dicom image
+ Warning : probably segfaults if pixels are not gdcm readable.
+ Use AnonymizeNoLoad instead.
+ usage: Anonymize filein=inputFileName fileout=anonymizedFileName [debug][usage]
+ debug : user wants to run the program in 'debug mode'
+ usage : user wants to display usage
+
+
+ * AnonymizeNoLoad
+
+
+ Anonymizes a gdcm-readable Dicom image even if pixels aren't gdcm readable
+ Warning : the image is overwritten;
+ to preserve its integrity, use a copy.
+ usage: AnonymizeNoLoad {filein=inputFileName|dirin=inputDirectoryName}
+ [rubout=listOfPrivateElementsToRubOut]
+ [ { [noshadowseq] | [noshadow][noseq] } ] [debug]
+ inputFileName : Name of the (single) file user wants to anonymize
+ inputDirectoryName : user wants to anonymize *all* the files
+ within the (single Patient!) directory
+ listOfElementsToRubOut : group1-elem1,g2-e2,... (in hexa)
+ of extra Elements to rub out
+ noshadowseq: user doesn't want to load Private Sequences
+ noshadow : user doesn't want to load Private groups (odd number)
+ noseq : user doesn't want to load Sequences
+ debug : user wants to run the program in 'debug mode'
+ usage : user wants to display usage
+
+
+ * ReWrite
+
+ Re write a full gdcm-readable Dicom image
+ (usefull when the file header is not very straight).
+
+ usage: ReWrite filein=inputFileName fileout=outputFileName
+ [keepoverlays] [mode=write mode] [monochrome1]
+ [noshadow] [noseq][debug]
+ --> The following line to 'rubout' a burnt-in Patient name
+ [rubout=xBegin,xEnd,yBegin,yEnd [ruboutvalue=n (<255)] ]
+ --> The 2 following lines, to extract a sub image within some frames
+ [ROI=xBegin,xEnd,yBegin,yEnd]
+ [firstframe=beg] [lastframe=end]
+
+ mode = a (ACR), x (Explicit VR Dicom), r (RAW : only pixels)
+ j (jpeg lossless), 2 (jpeg2000)
+ keepoverlays : user wants to keep ACR-NEMA-like overlays
+ monochrome1 = user wants MONOCHROME1 photom. interp. (0=white)
+ noshadowseq: user doesn't want to load Private Sequences
+ noshadow : user doesn't want to load Private groups (odd number)
+ noseq : user doesn't want to load Sequences
+ rgb : user wants to transform LUT (if any) to RGB pixels
+ warning : developper wants to run the program in 'warning mode'
+ debug : developper wants to run the program in 'debug mode'
+
+ * PrintDicomDir
+
+ Displays the tree-like structure of a DICOMDIR File
+ usage: PrintDicomDir filein=fileName [detail=n] [level=n] [debug] [usage]
+ detail = 1 : Patients, 2 : Studies, 3 : Series, 4 : Images
+ 5 : Full Content
+ level = 0,1,2 : depending on user (what he wants to see, when detail=5)
+ debug : user wants to run the program in 'debug mode'
+ usage : user wants to display usage
+
+
+ * MakeDicomDir
+
+ Explores recursively the given directory, makes the relevant DICOMDIR
+ and writes it as 'NewDICOMDIR'
+ usage: MakeDicomDir dirname=rootDirectoryName
+ [ { [noshadowseq] | [noshadow][noseq] } ] [debug] [usage]
+ noshadowseq: user doesn't want to load Private Sequence
+ noshadow : user doesn't want to load Private groups (odd number)
+ noseq : user doesn't want to load Sequences
+ debug : user wants to run the program in 'debug mode'
+ usage : user wants to display usage
+
+
+ * AnonymizeDicomDir
+
+ Anonymizes a gdcm-readable DICOMDIR even when some 'Objects'
+ are not yet taken into account
+ Warning : the DICOMDIR is overwritten;
+ to preserve its integrity, use a copy.
+ usage: AnonymizeDicomDir filein=dicomDirName [debug] [usage] [usage]
+ debug : user wants to run the program in 'debug mode'
+ usage : user wants to display usage
+
+
+ * PatchHeader
+
+ Allows aware user to patch a gdcm-parsable image header, without
+ loading image.
+ Warning : the image(s) is/are overwritten
+ to preserve image(s) integrity, use a copy.
+ WARNING : *NO CHECK* is performed on the new values.
+ Use only if you are sure the original values are wrong
+ *and* your values are right...
+ usage: PatchHeader {filein=inputFileName|dirin=inputDirectoryName}
+ [ { [size=] | [rows=][columns=] } ] [planes=]
+ [bitsallocated=] [bitsstored=]
+ [highbit=] [samplesperpixel=]
+ [pixelrepresentation=] [samplesperpixel=]
+ [ { [noshadowseq] | [noshadow][noseq] } ] [debug]
+
+ inputFileName : Name of the (single) file user wants to modify
+ inputDirectoryName : user wants to modify *all* the files
+ within the directory
+ newsize : new size, to overwrite old (wrong) one
+ or
+ rows : new Rows number, to overwrite old (wrong) one
+ columns : new Columns number, to overwrite old (wrong) one
+ planes : new Planes number, ...
+ bitsallocated : new Bits Allocated number, ...
+ bitsstored : new Bits Stored number, ...
+ highbit : new High Bit number, ...
+ samplesperpixel : new Samples Per Pixel, ...
+ pixelrepresentation : new Pixel Representation, ...
+
+ noshadowseq: user doesn't want to load Private Sequences
+ noshadow : user doesn't want to load Private groups (odd number)
+ noseq : user doesn't want to load Sequences
+ debug : user wants to run the program in 'debug mode'
+ usage : user wants to display usage
+
+
+ * exXCoherentFileSet :
+
+Shows the various 'XCoherent' Filesets within a directory
+Optionaly copies the images in a Directories tree
+usage: exXCoherentFileSet {dirin=inputDirectoryName}
+ dirout=outputDirectoryName
+ { tag=group-elem | pos | ori } [sort] [write]
+ [ { [noshadowseq] | [noshadow][noseq] } ] [debug]
+
+ dirin : user wants to analyze *all* the files
+ within the directory
+ write : user wants to create directories
+ dirout : will be created if doesn't exist
+ pos : user wants to split each Single SerieUID Fileset on the
+ 'Image Position '
+ ori : user wants to split each Single SerieUID Fileset on the
+ 'Image Orientation '
+ tag : group-elem (in hexa, no space)
+ the user wants to split on
+ sort : user wants FileHelper to sort the images
+ Warning : will probabely crah if sort has no meaning
+ (not only look at image names)
+ noshadowseq: user doesn't want to load Private Sequences
+ noshadow : user doesn't want to load Private groups (odd number)
+ noseq : user doesn't want to load Sequences
+ verbose : user wants to run the program in 'verbose mode'
+ debug : developper wants to run the program in 'debug mode'
+
}}}
</pre>