From 283cb16bbde7f9a68f240d9a28b018a3f05c746b Mon Sep 17 00:00:00 2001 From: jpr Date: Thu, 7 Jun 2007 16:13:14 +0000 Subject: [PATCH] Add an example on how to extract CSA informations. Thx to Mathieu Malaterre for supplying root code. A lot of work remains to do to make gdcm methods (class ?) --- Example/exExtractCSA.cxx | 455 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100755 Example/exExtractCSA.cxx diff --git a/Example/exExtractCSA.cxx b/Example/exExtractCSA.cxx new file mode 100755 index 00000000..35c1a8a3 --- /dev/null +++ b/Example/exExtractCSA.cxx @@ -0,0 +1,455 @@ +/*========================================================================= + + Program: gdcm + Module: $RCSfile: exExtractCSA.cxx,v $ + Language: C++ + Date: $Date: 2007/06/07 16:13:14 $ + Version: $Revision: 1.1 $ + + Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de + l'Image). All rights reserved. See Doc/License.txt or + http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +/* + * http://www.enac.northwestern.edu/~tew/archives/2003/02/25/incomplete-dicom-headers/ + * http://www.nmr.mgh.harvard.edu/~rudolph/software/vox2ras/download/vox2ras_rsolve.m + * http://www.mail-archive.com/freesurfer@nmr.mgh.harvard.edu/msg03409.html + * http://www.mmrrcc.upenn.edu/CAMRIS/cfn/dicomhdr.html + */ + +// See this one : +// http://www.mmrrcc.upenn.edu/CAMRIS/cfn/dicomhdr.html + +#include +#include +#include +#include +#include +#include + +#include "gdcmFile.h" +#include "gdcmFileHelper.h" +#include "gdcmCommon.h" +#include "gdcmDebug.h" +#include "gdcmDocEntry.h" +#include "gdcmDataEntry.h" +#include "gdcmSeqEntry.h" +#include "gdcmSQItem.h" +#include "gdcmArgMgr.h" + +// Looks like there is mapping in between syngodt and vr... +// O <=> UN +// 3 <=> DS +// 4 <=> FD +// 5 <=> FL +// 6 <=> IS +// 9 <=> UL +// 10 <=> US +// 16 <=> CS +// 19 <=> LO +// 20 <=> LT +// 22 <=> SH +// 25 <=> UI + +// -------------------------------------------------------- + +typedef struct { + uint32_t Item_xx[4]; + int Len; + std::string Value; +} CSA_item; + +typedef std::vector ItemVector; + +typedef struct { + std::string Name; + int VM; + std::string VR; + int Syngodt; + int Nitems; + ItemVector Items_set; +} CSA_entry; + +typedef std::map CSA_content; + +// -------------------------------------------------------- + + +struct equ +{ + uint32_t syngodt; + char vr[2+1]; +}; + +static equ mapping[] = { + { 0 , "UN" }, + { 3 , "DS" }, + { 4 , "FD" }, + { 5 , "FL" }, + { 6 , "IS" }, + { 9 , "UL" }, + { 10 , "US" }, + { 16 , "CS" }, + { 19 , "LO" }, + { 20 , "LT" }, + { 22 , "SH" }, + { 23 , "ST" }, + { 25 , "UI" }, +}; + +bool check_mapping(uint32_t syngodt, const char *vr) +{ + static const unsigned int max = sizeof(mapping) / sizeof(equ); + unsigned int s = 0; + const equ *p = mapping; + assert( syngodt <= mapping[max-1].syngodt ); + while(p->syngodt < syngodt ) + { + //std::cout << "mapping:" << p->vr << std::endl; + ++p; + } + assert( p->syngodt == syngodt ); // or else need to update mapping + const char* lvr = p->vr; + int check = strcmp(vr, lvr) == 0; + assert( check ); + return true; +} + + + ///\to fix the Desctructor! +void DeleteCSA_content (CSA_content &myMap) { + for ( CSA_content::const_iterator it = myMap.begin(); + it != myMap.end(); + ++it) + { + ItemVector item_v = (*it).second->Items_set; + for ( ItemVector::const_iterator it2 = item_v.begin(); + it2 != item_v.end(); + ++it2) + { + // delete (*it2); + } + //delete (*it).second; + } +} + +void PrintCSA_content(CSA_content &myMap) { + int item_no; + for ( CSA_content::const_iterator it = myMap.begin(); + it != myMap.end(); + ++it) + { + std::cout << "[" << (*it).second->Name << "] : VR=[" << (*it).second->VR + << "] vm = " << (*it).second->VM << std::endl; + item_no = 0; + ItemVector item_v = (*it).second->Items_set; + for ( ItemVector::const_iterator it2 = item_v.begin(); + it2 != item_v.end(); + ++it2) + { + std::cout << " --- item no : " << item_no << std::endl; + std::cout << " Item_xxx : " << (*it2)->Item_xx[0] << " " + << (*it2)->Item_xx[1] + << " " << (*it2)->Item_xx[2] << " " << (*it2)->Item_xx[3] + << std::endl; + + std::cout << " Len = " << (*it2)->Len ; + std::cout << " Value = [" << (*it2)->Value << "]" << std::endl; + + item_no++; + } + } +} + +int main(int argc, char *argv[]) +{ + START_USAGE(usage) + "\n exExtractCSA :\n ", + "Extracts and displays the CSA tag(s) of gdcm-parsable Dicom file ", + " ", + "usage: exExtractCSA {filein=inputFileName|dirin=inputDirectoryName} ", + " tmp=temporaryWorkFileName ", + " [extract=listOfElementsToExtract] ", + " [ { [noshadowseq] | [noshadow][noseq] } ] [debug] ", + " inputFileName : Name of the (single) file user wants to anonymize ", + " listOfElementsExtract : group-elem,g2-e2,... (in hexa, no space) ", + " of Elements to extract ", + " default : 0029-1210,0029-1220 ", + " 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 : developper wants to run the program in 'verbose mode' ", + " debug : developper wants to run the program in 'debug mode' ", + FINISH_USAGE + + // ----- Initialize Arguments Manager ------ + + GDCM_NAME_SPACE::ArgMgr *am = new GDCM_NAME_SPACE::ArgMgr(argc, argv); + + if (am->ArgMgrDefined("usage") || argc == 1) + { + am->ArgMgrUsage(usage); // Display 'usage' + delete am; + return 0; + } + + if (am->ArgMgrDefined("debug")) + GDCM_NAME_SPACE::Debug::DebugOn(); + + bool verbose = am->ArgMgrDefined("verbose"); + + const char *fileName = am->ArgMgrGetString("filein"); + + int loadMode = GDCM_NAME_SPACE::LD_ALL; + if ( am->ArgMgrDefined("noshadowseq") ) + loadMode |= GDCM_NAME_SPACE::LD_NOSHADOWSEQ; + else + { + if ( am->ArgMgrDefined("noshadow") ) + loadMode |= GDCM_NAME_SPACE::LD_NOSHADOW; + if ( am->ArgMgrDefined("noseq") ) + loadMode |= GDCM_NAME_SPACE::LD_NOSEQ; + } + + const char *tempWorkFile = am->ArgMgrGetString("tmp"); + + int extractNb; + uint16_t *elemsToExtract; + if (am->ArgMgrDefined("extract")) + am->ArgMgrGetXInt16Enum("extract", &extractNb); + else + { + elemsToExtract = new uint16_t[4]; + elemsToExtract[0] = 0x0029; + elemsToExtract[1] = 0x1210; + elemsToExtract[2] = 0x0029; + elemsToExtract[3] = 0x1220; + extractNb=2; + } + + /* if unused Param we give up */ + if ( am->ArgMgrPrintUnusedLabels() ) + { + am->ArgMgrUsage(usage); + delete am; + return 0; + } + delete am; // ------ we don't need Arguments Manager any longer ------ + +// ============================================================ +// Read the input image. +// ============================================================ + + gdcm::File *f = gdcm::File::New( ); + + //f->SetLoadMode(gdcm::LD_NOSEQ | gdcm::LD_NOSHADOW); + f->SetFileName( fileName ); + f->SetMaxSizeLoadEntry(0xffff); + bool res = f->Load(); + + if( gdcm::Debug::GetDebugFlag()) + { + std::cout << "---------------------------------------------" << std::endl; + f->Print(); + std::cout << "---------------------------------------------" << std::endl; + } + if (!res) { + std::cerr << "Sorry, " << fileName << " not a gdcm-readable " + << "DICOM / ACR File" + << std::endl; + f->Delete(); + return 1; + } + std::cout << " ... is readable " << std::endl; + + +// -------------------------------------------------------- +CSA_content myMap; +CSA_entry *myEntry; +CSA_item *myItem; +// -------------------------------------------------------- + +// For each tag user wants to extract : + +for (int tag_no=0; tag_noGetEntryString(group, elem); + if (dicom_tag_value == gdcm::GDCM_UNFOUND) + { + gdcm::DictEntry *dictEntry = f->GetPubDict()->GetEntry( group, elem); + std::cerr << "Image doesn't contain any tag: " << dictEntry->GetName() + << std::endl; + f->Delete(); + return 1; + } + + gdcm::DocEntry *dicom_tag_doc = f->GetDocEntry(group, elem); + gdcm::DataEntry *dicom_tag = dynamic_cast(dicom_tag_doc); + if( !dicom_tag ) + { + std::cerr << "Sorry DataEntry only please" << std::endl; + f->Delete(); + return 1; + } + + // Write out the data as a file: + std::ofstream o(tempWorkFile); + if( !o ) + { + std::cerr << "Problem opening file: [" << tempWorkFile << "]" + << std::endl; + f->Delete(); + return 1; + } + o.write((char*)dicom_tag->GetBinArea(), dicom_tag->GetLength()); + o.close(); + + +std::ifstream is( tempWorkFile ); +char dummy[4+1]; +dummy[4] = 0; +is.read(dummy, 4); +if (verbose) + std::cout << dummy << std::endl; +if( strcmp( dummy, "SV10" ) ) +{ + std::cerr << "Either not a SV10 header or filled with 0..." << std::endl; + return 1; +} +// wotsit ? +is.read(dummy, 4); +if( strcmp( dummy, "\4\3\2\1" ) ) +{ + std::cerr << "Either not a SV10 header or filled with 0..." << std::endl; + return 1; +} +if (verbose) + std::cout << (int)dummy[0] << (int)dummy[1] << (int)dummy[2] + << (int)dummy[3]<< std::endl; +uint32_t n; +is.read((char*)&n, sizeof(n)); +if (verbose) + std::cout << "number of entries " <Delete(); +return 0; +} -- 2.48.1