X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=Doc%2FWebsite%2FHowToUseGdcm.html;h=de92c7be36f788fc14617c2a15b78fd041af468c;hb=a401c209a120a44b355e53180a5a9a8d1a3cdc4c;hp=5a4f05a013638e1b93c6cc2fe79422587bf31b1e;hpb=6e55dbca8ed17c48949d8ce226d3aaee099a02ff;p=gdcm.git diff --git a/Doc/Website/HowToUseGdcm.html b/Doc/Website/HowToUseGdcm.html index 5a4f05a0..de92c7be 100755 --- a/Doc/Website/HowToUseGdcm.html +++ b/Doc/Website/HowToUseGdcm.html @@ -4,39 +4,82 @@ This is a vi-able/notepad-able document to (try to) explain in a few words 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 @@ -49,6 +92,8 @@ D 0018|602c [FD] [Physical Delta X][0] 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 @@ -106,7 +151,70 @@ S 0018|6011 [SQ] [Sequence of Ultrasound Regions] 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 @@ -118,18 +226,29 @@ Probabely, you'll never have to deal with Sequences (hope so!). 1-1-1) A single file -------------------- +1-1-1-1) Deal with the file header + The first step is to load 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(). + 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. @@ -148,11 +267,24 @@ Check some fields, e.g (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(); @@ -170,18 +302,20 @@ Possible values are : - "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: @@ -189,6 +323,54 @@ 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 ----------------- @@ -201,7 +383,7 @@ of images, laying in a Directories tree-like structure, you don't know anything 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++) { @@ -214,7 +396,36 @@ You can also pass a 'root directory', and ask or not for recursive parsing. 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 : @@ -238,8 +449,9 @@ If you want to 'order' the files within each 'Single Serie UID File Set' 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++) { @@ -276,8 +488,6 @@ e.g. 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. @@ -288,7 +498,7 @@ He wants to drop the 'duplicate images' 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' @@ -306,16 +516,16 @@ gdcm::FileList *l = s->GetFirstSingleSerieUIDFileSet(); // or what you want 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). 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 @@ -338,17 +548,17 @@ for (gdcm::XCoherentFileSetmap::iterator i = xcm.begin(); -------------- 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 @@ -360,10 +570,12 @@ feel like: 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. @@ -371,7 +583,7 @@ feel like: 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); @@ -443,7 +655,7 @@ methods : (see 1-1-2 for more details) //---------------- -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 @@ -461,36 +673,11 @@ is wrapable by swig. 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 : @@ -500,7 +687,7 @@ 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 @@ -510,7 +697,7 @@ you just have to process as follow : 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 @@ -530,58 +717,154 @@ you just have to process as follow : 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 ----------------- @@ -592,5 +875,208 @@ vtkSetMacro(GdcmFile, gdcm::File *); -------------- /// \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 + + Displays the header of any kind of ACR-NEMA/PAPYRUS/DICOM File + usage: PrintFile {filein=inputFileName|dirin=inputDirectoryName}[level=n] + [forceload=listOfElementsToForceLoad] + [4DLoc= ][dict= privateDirectory] + [ { [noshadowseq] | [noshadow][noseq] } ] + [debug] [warning] + level = 0,1,2 : depending on the amount of details user wants to see + 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) + + [ { [noshadowseq] | [noshadow][noseq] } ] [debug] [usage] + level = 0,1,2 : depending on the amount of details user wants to see + 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 + + + * 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 + + Reads and rewrites 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' a full gdcm-readable Dicom image (compressed Pixels are expanded) + (usefull when the file is not very straight). + usage: ReWrite filein=inputFileName fileout=anonymizedFileName + [mode=write mode] [rgb] + [ { [noshadowseq] | [noshadow][noseq] } ] [debug] [usage] + write mode = a (ACR), x (Explicit VR Dicom), r (RAW : only pixels) + rgb : user wants to transform LUT (if any) into RGB + 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 + + + * 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 + + }}}