]> Creatis software - gdcm.git/blob - Example/exExtractCSA.cxx
Add some explanations
[gdcm.git] / Example / exExtractCSA.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: exExtractCSA.cxx,v $
5   Language:  C++
6   Date:      $Date: 2007/10/03 09:14:55 $
7   Version:   $Revision: 1.8 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 /*
20  * http://www.enac.northwestern.edu/~tew/archives/2003/02/25/incomplete-dicom-headers/
21  * http://www.nmr.mgh.harvard.edu/~rudolph/software/vox2ras/download/vox2ras_rsolve.m
22  * http://www.mail-archive.com/freesurfer@nmr.mgh.harvard.edu/msg03409.html
23  * http://www.mmrrcc.upenn.edu/CAMRIS/cfn/dicomhdr.html
24  */
25
26 // See this one :
27 // http://www.mmrrcc.upenn.edu/CAMRIS/cfn/dicomhdr.html
28
29 #include <iostream>
30 #include <fstream>
31 #include <stdint.h>
32 #include <assert.h>
33 #include <vector>
34 #include <map>
35
36 #include "gdcmFile.h"
37 #include "gdcmFileHelper.h"
38 #include "gdcmCommon.h"
39 #include "gdcmDebug.h"
40 #include "gdcmDocEntry.h"
41 #include "gdcmDataEntry.h"
42 #include "gdcmSeqEntry.h"
43 #include "gdcmSQItem.h"
44 #include "gdcmArgMgr.h"
45
46
47 // --------------------------------------------------------
48
49 typedef struct {
50    uint32_t  Item_xx[4];
51    int Len;
52    std::string Value;
53 } CSA_item;
54
55 typedef std::vector<CSA_item *> ItemVector;
56
57 typedef struct {
58    std::string Name;
59    int VM;
60    std::string VR;
61    int Syngodt;
62    int Nitems;
63    ItemVector Items_set;   
64 } CSA_entry;
65
66 typedef std::map<std::string, CSA_entry *> CSA_content;
67
68 // --------------------------------------------------------
69
70
71 struct equ
72
73   uint32_t syngodt;
74   char vr[2+1];
75 };
76
77 // Looks like there is mapping in between syngodt and vr...
78 //  O <=> UN
79 //  3 <=> DS
80 //  4 <=> FD
81 //  5 <=> FL
82 //  6 <=> IS
83 //  9 <=> UL
84 // 10 <=> US
85 // 16 <=> CS
86 // 19 <=> LO
87 // 20 <=> LT
88 // 22 <=> SH
89 // 25 <=> UI
90 static equ mapping[] = {
91   {  0 , "UN" },
92   {  3 , "DS" },
93   {  4 , "FD" },
94   {  5 , "FL" },
95   {  6 , "IS" },
96   {  7 , "SL" },
97   {  8 , "SS" },
98   {  9 , "UL" },
99   { 10 , "US" },
100   { 16 , "CS" },
101   { 19 , "LO" },
102   { 20 , "LT" },
103   { 22 , "SH" },
104   { 23 , "ST" },
105   { 25 , "UI" },
106   { 27 , "UT" }
107 };
108
109 bool check_mapping(uint32_t syngodt, const char *vr)
110 {
111   static const unsigned int max = sizeof(mapping) / sizeof(equ);
112   unsigned int s = 0;
113   const equ *p = mapping;
114   assert( syngodt <= mapping[max-1].syngodt );
115   while(p->syngodt < syngodt )
116   {
117     //std::cout << "mapping:" << p->vr << std::endl;
118     ++p;
119   }
120   assert( p->syngodt == syngodt ); // or else need to update mapping
121   const char* lvr = p->vr;
122   int check = strcmp(vr, lvr) == 0;
123   assert( check );
124   return true;
125 }
126
127
128      ///\to  fix the Destructor!
129 void  DeleteCSA_content (CSA_content &myMap) {
130    for ( CSA_content::const_iterator it = myMap.begin();
131                                     it != myMap.end();
132                                   ++it)
133    { 
134      ItemVector item_v = (*it).second->Items_set;
135      for ( ItemVector::const_iterator it2  = item_v.begin();
136                                       it2 != item_v.end();
137                                     ++it2)
138      {
139        // delete (*it2); 
140      }
141      //delete (*it).second;   
142    } 
143 }
144
145 void PrintCSA_content(CSA_content &myMap) {
146    int item_no;
147    for ( CSA_content::const_iterator it = myMap.begin();
148                                     it != myMap.end();
149                                   ++it)
150    { 
151      std::cout << "[" << (*it).second->Name << "] : VR=[" << (*it).second->VR 
152                << "] vm = " << (*it).second->VM << std::endl;
153      item_no = 0;
154      ItemVector item_v = (*it).second->Items_set;
155      for ( ItemVector::const_iterator it2 = item_v.begin();
156                                       it2 != item_v.end();
157                                     ++it2)
158      {
159        std::cout << "      --- item no : " << item_no << std::endl;
160        std::cout << "      Item_xxx : " << (*it2)->Item_xx[0] << " " 
161                  << (*it2)->Item_xx[1] 
162                  << " " << (*it2)->Item_xx[2] << " " << (*it2)->Item_xx[3] 
163                  << std::endl;
164     
165        std::cout << "      Len = " << (*it2)->Len ;
166        std::cout << "      Value = [" << (*it2)->Value << "]" << std::endl;
167    
168        item_no++;
169      } 
170    }
171 }
172
173 int main(int argc, char *argv[])
174 {
175    START_USAGE(usage)
176    "\n exExtractCSA :\n                                                       ",
177    "Extracts and displays the CSA tag(s) of gdcm-parsable Dicom file          ",
178    "                                                                          ",
179    "usage: exExtractCSA {filein=inputFileName|dirin=inputDirectoryName}       ",
180    "                   tmp=temporaryWorkFileName                              ",
181    "                       [extract=listOfElementsToExtract]                  ",
182    "                       [ { [noshadowseq] | [noshadow][noseq] } ] [debug]  ",
183    "       inputFileName : Name of the (single) file user wants to anonymize  ",
184    "       listOfElementsExtract : group-elem,g2-e2,... (in hexa, no space)   ",
185    "                                of Elements to extract                    ",
186    "                              default : 0029-1010,0029-1020               ",
187    "       noshadowseq: user doesn't want to load Private Sequences           ",
188    "       noshadow   : user doesn't want to load Private groups (odd number) ",
189    "       noseq      : user doesn't want to load Sequences                   ",
190    "       verbose    : developper wants to run the program in 'verbose mode' ",
191    "       debug      : developper wants to run the program in 'debug mode'   ",
192    FINISH_USAGE
193
194    // ----- Initialize Arguments Manager ------
195   
196    GDCM_NAME_SPACE::ArgMgr *am = new GDCM_NAME_SPACE::ArgMgr(argc, argv);
197   
198    if (am->ArgMgrDefined("usage") || argc == 1) 
199    {
200       am->ArgMgrUsage(usage); // Display 'usage'
201       delete am;
202       return 1;
203    }
204
205    if (am->ArgMgrDefined("debug"))
206       GDCM_NAME_SPACE::Debug::DebugOn();
207
208    bool verbose = am->ArgMgrDefined("verbose");
209    
210    const char *fileName = am->ArgMgrGetString("filein");
211
212    int loadMode = GDCM_NAME_SPACE::LD_ALL;
213    if ( am->ArgMgrDefined("noshadowseq") )
214       loadMode |= GDCM_NAME_SPACE::LD_NOSHADOWSEQ;
215    else 
216    {
217       if ( am->ArgMgrDefined("noshadow") )
218          loadMode |= GDCM_NAME_SPACE::LD_NOSHADOW;
219       if ( am->ArgMgrDefined("noseq") )
220          loadMode |= GDCM_NAME_SPACE::LD_NOSEQ;
221    }
222    
223    const char *tempWorkFile = am->ArgMgrGetString("tmp");
224
225    int extractNb;
226    uint16_t *elemsToExtract;
227    if (am->ArgMgrDefined("extract")) 
228    {
229       am->ArgMgrGetXInt16Enum("extract", &extractNb);
230       std::cout << "extractNb=" << extractNb << std::endl;
231       if (extractNb =! 0)
232          for (int k=0;k<extractNb; k++)
233             std::cout << std::hex << elemsToExtract[2*k] << "|" << elemsToExtract[2*k+1] <<std::endl;
234    }
235    else 
236    {
237      elemsToExtract = new  uint16_t[4];
238      elemsToExtract[0] = 0x0029;
239      elemsToExtract[1] = 0x1010;
240      elemsToExtract[2] = 0x0029;  
241      elemsToExtract[3] = 0x1020;
242      extractNb=2;
243    }     
244
245    /* if unused Param we give up */
246    if ( am->ArgMgrPrintUnusedLabels() )
247    {
248       am->ArgMgrUsage(usage);
249       delete am;
250       return 1;
251    }  
252    delete am;  // ------ we don't need Arguments Manager any longer ------
253
254 // ============================================================
255 //   Read the input image.
256 // ============================================================ 
257   
258    GDCM_NAME_SPACE::File *f = GDCM_NAME_SPACE::File::New( );
259
260    //f->SetLoadMode(GDCM_NAME_SPACE::LD_NOSEQ | GDCM_NAME_SPACE::LD_NOSHADOW);
261    f->SetFileName( fileName );
262    f->SetMaxSizeLoadEntry(0xffff);
263    bool res = f->Load();  
264
265    if( GDCM_NAME_SPACE::Debug::GetDebugFlag())
266    {
267       std::cout << "---------------------------------------------" << std::endl;
268       f->Print();
269       std::cout << "---------------------------------------------" << std::endl;
270    }
271    if (!res) {
272        std::cerr << "Sorry, " << fileName << " not a gdcm-readable "
273            << "DICOM / ACR File"
274            << std::endl;
275       f->Delete();
276       return 1;
277    }
278    std::cout << " ... is readable " << std::endl;
279    
280    
281 // --------------------------------------------------------
282 CSA_content myMap;
283 CSA_entry  *myEntry; 
284 CSA_item   *myItem;
285 // --------------------------------------------------------
286
287 // For each tag user wants to extract :
288
289 for (int tag_no=0; tag_no<extractNb; tag_no++) {
290
291    uint16_t group = elemsToExtract[2*tag_no];
292    uint16_t elem = elemsToExtract[2*tag_no+1];
293    
294    if (verbose)
295       std::cout << "Let's try tag : " << std::hex << group << "|" << elem << std::endl;
296       
297    std::string dicom_tag_value = f->GetEntryString(group, elem);
298    if (dicom_tag_value == GDCM_NAME_SPACE::GDCM_UNFOUND)
299    {
300      GDCM_NAME_SPACE::DictEntry *dictEntry = f->GetPubDict()->GetEntry( group, elem);
301      if (dictEntry != NULL)
302         std::cerr << "Image doesn't contain any tag: " << dictEntry->GetName() 
303                   << std::endl;
304      else
305         std::cerr << "Dicom Dictionary doesn't contain any tag: " 
306           << std::hex << group << "|" << elem << std::endl; 
307      f->Delete();
308      return 1;
309    }
310
311    GDCM_NAME_SPACE::DocEntry *dicom_tag_doc = f->GetDocEntry(group, elem);
312    GDCM_NAME_SPACE::DataEntry *dicom_tag = dynamic_cast<GDCM_NAME_SPACE::DataEntry *>(dicom_tag_doc);
313    if( !dicom_tag )
314    {
315       std::cerr << "Sorry DataEntry only please" << std::endl;
316       f->Delete();
317       return 1;
318    }
319
320    // Write out the data as a file:
321    std::ofstream o(tempWorkFile);
322    if( !o )
323    {
324       std::cerr << "Problem opening file: [" << tempWorkFile << "]" 
325                 << std::endl;
326       f->Delete();
327       return 1;
328    }
329    o.write((char*)dicom_tag->GetBinArea(), dicom_tag->GetLength());
330    o.close();
331    
332    
333 //#define OLDFORMAT
334
335 std::ifstream is( tempWorkFile );
336 #ifndef OLDFORMAT
337 char dummy[4+1];
338 dummy[4] = 0;
339 is.read(dummy, 4);
340 if (verbose)
341  std::cout << dummy << std::endl;
342 if( strcmp( dummy, "SV10" )  ) 
343 {
344    std::cerr << "Either not a SV10 header or filled with 0..." << std::endl;
345    return 1;
346 }
347 // wotsit ?
348 is.read(dummy, 4);
349 if( strcmp( dummy, "\4\3\2\1" )  ) 
350 {
351   std::cerr << "Either not a SV10 header or filled with 0..." << std::endl;
352   return 1;
353 }
354 if (verbose)
355    std::cout << (int)dummy[0] << (int)dummy[1] << (int)dummy[2] 
356              << (int)dummy[3]<< std::endl;
357 #endif
358 uint32_t n;
359 is.read((char*)&n, sizeof(n));
360 if (verbose)
361    std::cout << "number of entries " <<n << std::endl;
362 uint32_t unused;
363 is.read((char*)&unused, sizeof(unused));
364 if (verbose)
365    std::cout << "unused " << unused<< std::endl;
366 assert( unused == 77 ); // 'M' character...
367
368 for(uint32_t i = 0; i < n; ++i)
369 {
370 if (verbose)
371      std::cout << "============================================== " << i <<
372      std::endl;
373   char name[64+1];
374   name[64] = 0; // security
375   //std::cout << "Pos 0x" << std::hex << is.tellg() << std::dec << std::endl;
376   is.read(name, 64);
377 if (verbose)
378      std::cout << "Name=[" << name << "]" << std::endl;
379   uint32_t vm;
380   is.read((char*)&vm, sizeof(vm));
381 if (verbose)
382      std::cout << "vm=" << vm <<  std::endl;
383   char vr[4];
384   is.read(vr, 4);
385 #ifndef OLDFORMAT
386      assert( vr[2] == vr[3] && vr[2] == 0 );
387 #endif
388 if (verbose)
389      std::cout << "vr=[" << vr << "]" <<std::endl;
390   uint32_t syngodt;
391   is.read((char*)&syngodt, sizeof(syngodt));
392   check_mapping(syngodt, vr);
393
394 if (verbose)
395      std::cout << "syngodt=" << syngodt << std::endl;
396   uint32_t nitems;
397   is.read((char*)&nitems, sizeof(nitems));
398 if (verbose)
399      std::cout << "nitems=" << nitems<< std::endl;
400   uint32_t xx;
401   is.read((char*)&xx, sizeof(xx));
402   //std::cout << "xx=" << xx<< std::endl;
403   assert( xx == 77 || xx == 205 );
404
405   myEntry = new CSA_entry;
406   myEntry->Name    = name;
407   myEntry->VM      = vm;
408   myEntry->VR      = vr;
409   myEntry->Syngodt = syngodt;
410   myEntry->Nitems  = nitems;
411
412   for( uint32_t j = 0; j < nitems; ++j)
413   {
414 if (verbose)
415        std::cout << "-------------------------------------------- " << j
416                  << std::endl;
417     uint32_t item_xx[4];
418     is.read((char*)&item_xx, 4*sizeof(uint32_t));
419 if (verbose)
420        std::cout << std::dec 
421                  << "item_xx=" << item_xx[0] << " " << item_xx[1] << " " 
422                  << item_xx[2] << " " << item_xx[3] << std::endl;
423     //std::cout << "0x" << std::hex << is.tellg() << std::dec << std::endl;
424     assert( item_xx[2] == 77 || item_xx[2] == 205 );
425     uint32_t len = item_xx[1]; // 2nd element
426 if (verbose)
427        std::cout << "len=" << len << std::endl;
428     assert( item_xx[0] == item_xx[1] && item_xx[1] == item_xx[3] );
429     char *val = new char[len+1];
430     val[len] = 0; // security
431     is.read(val,len);
432     // WARNING vr does not means anything AFAIK, 
433     // simply print the value as if it was IS/DS or LO (ASCII)
434 if (verbose)
435      std::cout << "val=[" << val << "]" << std::endl;
436
437     char dummy[4];
438     uint32_t dummy_len = (4 - len % 4) % 4;
439     is.read(dummy, dummy_len );
440
441
442     for(uint32_t d= 0; d < dummy_len; ++d)
443     {
444      // I think dummy should always be 0
445       if( dummy[d] )
446       {
447 if (verbose)
448            std::cout << "dummy=" << (int)dummy[d] << std::endl;
449       }
450     }
451
452      myItem = new CSA_item;
453      for (int i2=0; i2<4; i2++)
454         myItem->Item_xx[i2] = item_xx[i2];
455      myItem->Len = len;
456      myItem->Value = val;
457      
458      myEntry->Items_set.push_back(myItem);
459      delete[] val;     
460
461    }
462    myMap[name] = myEntry;
463   }
464
465 std::cout << "================================================================"
466           << std::endl
467           << "=== Extract :" << std::hex << group << " " << elem  << "========"
468           << std::endl
469           << "================================================================"
470           << std::endl;
471
472   PrintCSA_content (myMap);
473   DeleteCSA_content (myMap);
474 }
475
476 f->Delete();   
477 return 0;
478 }