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