]> Creatis software - gdcm.git/blob - src/gdcmHeaderEntrySet.cxx
* vtk/vtkGdcmReader.cxx : bug fix : before, with python only, the program
[gdcm.git] / src / gdcmHeaderEntrySet.cxx
1 // gdcmHeaderEntrySet.cxx
2 //-----------------------------------------------------------------------------
3 #include "gdcmUtil.h"
4 #include "gdcmHeaderEntrySet.h"
5 #include "gdcmTS.h"
6 #ifdef GDCM_NO_ANSI_STRING_STREAM
7 #  include <strstream>
8 #  define  ostringstream ostrstream
9 # else
10 #  include <sstream>
11 #endif
12
13 #include <iomanip> //la bibli qui va bien
14
15 //-----------------------------------------------------------------------------
16 // Constructor / Destructor
17 /**
18  * \ingroup gdcmHeaderEntrySet
19  * \brief  Destructor 
20  */
21 gdcmHeaderEntrySet::~gdcmHeaderEntrySet() {
22    for (TagHeaderEntryHT::iterator tag = tagHT.begin(); tag != tagHT.end(); ++tag) {
23       gdcmHeaderEntry* EntryToDelete = tag->second;
24       if ( EntryToDelete )
25          delete EntryToDelete;
26       tag->second=NULL;
27    }
28
29    tagHT.clear();
30 }
31
32 //-----------------------------------------------------------------------------
33 // Print
34 /**
35  * \ingroup gdcmHeaderEntrySet
36  * \brief prints the Dicom Elements of the gdcmHeader
37  *        using both H table and Chained List
38  * @param   os The output stream to be written to.  
39  */
40 void gdcmHeaderEntrySet::Print(std::ostream & os) {
41
42    size_t o;
43    unsigned short int g, e;
44    TSKey v;
45    std::string d2;
46    gdcmTS * ts = gdcmGlobal::GetTS();
47
48    std::ostringstream s;
49    
50    // Tag HT
51    s << "------------- using tagHT ---------------------" << std::endl; 
52    for (TagHeaderEntryHT::iterator tag = tagHT.begin();
53            tag != tagHT.end();
54            ++tag){
55       g = tag->second->GetGroup();
56       e = tag->second->GetElement();
57       v = tag->second->GetValue();
58       o = tag->second->GetOffset();
59       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
60
61       s << tag->first << ": ";
62       s << " lgr : " <<tag->second->GetLength();
63       s << ",\t Offset : " << o;
64       s << " x(" << std::hex << o << std::dec << ") ";
65       s << "\t[" << tag->second->GetVR()    << "]";
66       s << "\t[" << tag->second->GetName()  << "]";       
67       s << "\t[" << d2 << "]";
68
69       // Display the UID value (instead of displaying the rough code)  
70       if (g == 0x0002) {  // Some more to be displayed ?
71          if ( (e == 0x0010) || (e == 0x0002) )     
72             s << "  ==>\t[" << ts->GetValue(v) << "]";   
73       } else {
74          if (g == 0x0008) {
75             if ( (e == 0x0016) || (e == 0x1150)  )         
76                s << "  ==>\t[" << ts->GetValue(v) << "]"; 
77          }
78       }              
79       s << std::endl;
80    }
81
82    // List element
83    guint32 lgth;
84    char greltag[10];  //group element tag
85  
86    s << "------------ using listEntries ----------------" << std::endl; 
87
88    char st[11];
89    for (ListTag::iterator i = listEntries.begin();  
90            i != listEntries.end();
91            ++i){
92       g = (*i)->GetGroup();
93       e = (*i)->GetElement();
94       v = (*i)->GetValue();
95       o = (*i)->GetOffset();
96       sprintf(greltag,"%04x|%04x",g,e);           
97       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
98       s << greltag << " lg : ";
99       lgth = (*i)->GetReadLength(); 
100       sprintf(st,"x(%x)",lgth);
101       s.setf(std::ios::left);
102       s << std::setw(10-strlen(st)) << " ";  
103       s << st << " ";
104       s.setf(std::ios::left);
105       s << std::setw(8) << lgth; 
106       s << " Off.: ";
107       sprintf(st,"x(%x)",o); 
108       s << std::setw(10-strlen(st)) << " ";       
109       s << st << " ";
110       s << std::setw(8) << o; 
111       s << "[" << (*i)->GetVR()  << "] ";
112       s.setf(std::ios::left);
113       s << std::setw(66-(*i)->GetName().length()) << " ";
114       s << "[" << (*i)->GetName()<< "] ";       
115       s << "[" << d2 << "]";
116        // Display the UID value (instead of displaying the rough code)  
117       if (g == 0x0002) {  // Any more to be displayed ?
118          if ( (e == 0x0010) || (e == 0x0002) )     
119             s << "  ==>\t[" << ts->GetValue(v) << "]";   
120       } else {
121          if (g == 0x0008) {
122             if ( (e == 0x0016) || (e == 0x1150)  )         
123                s << "  ==>\t[" << ts->GetValue(v) << "]"; 
124          }
125       }                        
126       s << std::endl;
127    } 
128
129    os<<s.str();
130
131
132 //-----------------------------------------------------------------------------
133 // Public
134 /**
135  * \ingroup gdcmHeaderEntrySet
136  * \brief  add a new Dicom Element pointer to 
137  *         the H Table and to the chained List
138  * @param   newHeaderEntry
139  */
140 void gdcmHeaderEntrySet::Add(gdcmHeaderEntry * newHeaderEntry) {
141
142 //      tagHT [newHeaderEntry->GetKey()]  = newHeaderEntry;
143
144    tagHT.insert( PairHT( newHeaderEntry->GetKey(),newHeaderEntry) );
145         
146 // WARNING : push_bash in listEntries ONLY during ParseHeader
147 // TODO : something to allow further Elements addition 
148 // position to be taken care of !       
149    listEntries.push_back(newHeaderEntry); 
150 }
151
152 /**
153  * \ingroup gdcmHeaderEntrySet
154  * \brief  retrieves a Dicom Element (the first one) using (group, element)
155  * \ warning (group, element) IS NOT an identifier inside the Dicom Header
156  *           if you think it's NOT UNIQUE, check the count number
157  *           and use iterators to retrieve ALL the Dicoms Elements within
158  *           a given couple (group, element)
159  * @param   group Group number of the searched Dicom Element 
160  * @param   element Element number of the searched Dicom Element 
161  * @return  
162  */
163 gdcmHeaderEntry* gdcmHeaderEntrySet::GetHeaderEntryByNumber(guint16 group, guint16 element) {
164    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
165    if ( ! tagHT.count(key))
166       return NULL;
167    return tagHT.find(key)->second;
168 }
169
170 /**
171  * \ingroup gdcmHeaderEntrySet
172  * \brief  Gets the value (string) of the target Dicom Element
173  * @param   group Group  number of the searched Dicom Element 
174  * @param   element Element number of the searched Dicom Element 
175  * @return  
176  */
177 std::string gdcmHeaderEntrySet::GetEntryByNumber(guint16 group, guint16 element) {
178    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
179    if ( ! tagHT.count(key))
180       return GDCM_UNFOUND;
181    return tagHT.find(key)->second->GetValue();
182 }
183
184 /**
185  * \ingroup gdcmHeaderEntrySet
186  * \brief  Sets the value (string) of the target Dicom Element
187  * @param   content string value of the Dicom Element
188  * @param   group Group number of the searched Dicom Element 
189  * @param   element Element number of the searched Dicom Element 
190  * @return  
191  */
192 bool gdcmHeaderEntrySet::SetEntryByNumber(std::string content,
193                                           guint16 group, guint16 element) {
194    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
195    if ( ! tagHT.count(key))
196       return false;
197    int l = content.length();
198    if(l%2) {  // Odd length are padded with a space (020H).
199       l++;
200       content = content + '\0';
201    }
202       
203    //tagHT[key]->SetValue(content);   
204    gdcmHeaderEntry * a;
205    IterHT p;
206    TagHeaderEntryHT::iterator p2;
207    // DO NOT remove the following lines : they explain the stuff   
208    //p= tagHT.equal_range(key); // get a pair of iterators first-last synonym
209    //p2=p.first;                // iterator on the first synonym 
210    //a=p2->second;              // H Table target column (2-nd col)
211     
212    // or, easier :
213    a = ((tagHT.equal_range(key)).first)->second; 
214        
215    a-> SetValue(content); 
216    
217    //std::string vr = tagHT[key]->GetVR();
218    std::string vr = a->GetVR();
219    
220    guint32 lgr;
221    if( (vr == "US") || (vr == "SS") ) 
222       lgr = 2;
223    else if( (vr == "UL") || (vr == "SL") )
224       lgr = 4;
225    else
226       lgr = l;     
227    //tagHT[key]->SetLength(lgr);
228    a->SetLength(lgr);   
229    return true;
230 }
231
232 /**
233  * \ingroup gdcmHeaderEntrySet
234  * \brief   Sets the value length of the Dicom Element
235  * \warning : use with caution !
236  * @param   length
237  * @param   group Group number of the searched Dicom Element 
238  * @param   element Element number of the searched Dicom Element 
239  * @return  boolean
240  */
241 bool gdcmHeaderEntrySet::SetEntryLengthByNumber(guint32 length,
242                                                 guint16 group, guint16 element) {
243    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
244    if ( ! tagHT.count(key))
245       return false;
246    if (length%2) length++; // length must be even
247    //tagHT[key]->SetLength(length);
248    ( ((tagHT.equal_range(key)).first)->second )->SetLength(length);      
249          
250    return true ;                
251 }
252 /**
253  * \ingroup gdcmHeaderEntrySet
254  * \brief   Sets a 'non string' value to a given Dicom Element
255  * @param   area
256  * @param   group Group number of the searched Dicom Element 
257  * @param   element Element number of the searched Dicom Element 
258  * @return  
259  */
260 bool gdcmHeaderEntrySet::SetVoidAreaByNumber(void * area,
261                                       guint16 group, guint16 element) {
262    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
263    if ( ! tagHT.count(key))
264       return false;
265    //tagHT[key]->SetVoidArea(area);
266    ( ((tagHT.equal_range(key)).first)->second )->SetVoidArea(area);      
267    return true ;                
268 }
269
270 /**
271  * \ingroup gdcmHeaderEntrySet
272  * \brief   Generate a free TagKey i.e. a TagKey that is not present
273  *          in the TagHt dictionary.
274  * @param   group The generated tag must belong to this group.  
275  * @return  The element of tag with given group which is fee.
276  */
277 guint32 gdcmHeaderEntrySet::GenerateFreeTagKeyInGroup(guint16 group) {
278    for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
279       TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
280       if (tagHT.count(key) == 0)
281          return elem;
282    }
283    return UINT32_MAX;
284 }
285
286 /**
287  * \ingroup gdcmHeaderEntrySet
288  * \brief   Checks if a given Dicom Element exists
289  * \        within the H table
290  * @param   group Group   number of the searched Dicom Element 
291  * @param   element  Element number of the searched Dicom Element 
292  * @return  number of occurences
293  */
294 int gdcmHeaderEntrySet::CheckIfExistByNumber(guint16 group, guint16 element ) {
295         std::string key = gdcmDictEntry::TranslateToKey(group, element );
296         return (tagHT.count(key));
297 }
298
299 // ==============
300 // TODO to be re-written using the chained list instead of the H table
301 //      so we can remove the GroupHT from the gdcmHeader
302 // =============
303 /**
304  * \ingroup gdcmHeaderEntrySet
305  * \brief   
306  * @param   _fp already open file pointer
307  * @param   type type of the File to be written 
308  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
309  * @return  always "True" ?!
310  */
311 bool gdcmHeaderEntrySet::Write(FILE * _fp, FileType type) {
312
313         // Question :
314         // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
315         // (FileType est un champ de gdcmHeader ...)
316         // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
317         // no way 
318         // a moins de se livrer a un tres complique ajout des champs manquants.
319         // faire un CheckAndCorrectHeader (?)
320
321    if ( (type == ImplicitVR) || (type == ExplicitVR) )
322       UpdateGroupLength(false,type);
323    if ( type == ACR)
324       UpdateGroupLength(true,ACR);
325
326    WriteEntries(type, _fp);
327    return(true);
328 }
329
330 //-----------------------------------------------------------------------------
331 // Protected
332
333 //-----------------------------------------------------------------------------
334 // Private
335 /**
336  * \ingroup gdcmHeaderEntrySet
337  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
338  * \warning : to be re-written using the chained list instead of the H table.
339  * \todo : to be re-written using the chained list instead of the H table
340  * @param   SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
341  * @param   type Type of the File (ExplicitVR,ImplicitVR, ACR, ...) 
342  */
343 void gdcmHeaderEntrySet::UpdateGroupLength(bool SkipSequence, FileType type) {
344    guint16 gr, el;
345    std::string vr;
346    
347    gdcmHeaderEntry *elem;
348    char trash[10];
349    std::string str_trash;
350    
351    GroupKey key;
352    GroupHT groupHt;  // to hold the length of each group
353    TagKey tk;
354    // remember :
355    // typedef std::map<GroupKey, int> GroupHT;
356    
357    gdcmHeaderEntry *elemZ;
358   
359    // for each Tag in the DCM Header
360    
361    for (TagHeaderEntryHT::iterator tag2 = tagHT.begin(); 
362         tag2 != tagHT.end();
363         ++tag2){
364
365       elem  = tag2->second;
366       gr = elem->GetGroup();
367       el = elem->GetElement();
368       vr = elem->GetVR(); 
369                  
370       sprintf(trash, "%04x", gr);
371       key = trash;              // generate 'group tag'
372       
373       // if the caller decided not to take SEQUENCEs into account 
374       // e.g : he wants to write an ACR-NEMA File 
375                 
376       if (SkipSequence && vr == "SQ") continue;
377       
378          // Still unsolved problem :
379          // we cannot find the 'Sequence Delimitation Item'
380          // since it's at the end of the Hash Table
381          // (fffe,e0dd) 
382              
383          // pas SEQUENCE en ACR-NEMA
384          // WARNING : 
385          // --> la descente a l'interieur' des SQ 
386          // devrait etre faite avec une liste chainee, pas avec une HTable...
387             
388       if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
389          if (el == 0x0000) {          // the first elem is 0x0000
390             groupHt[key] = 0;         // initialize group length 
391          } else {
392             groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
393          } 
394       } else {   // any elem but the first    
395          if (type == ExplicitVR) {
396             if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
397                groupHt[key] +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
398             }
399          }
400          groupHt[key] += 2 + 2 + 4 + elem->GetLength(); 
401       } 
402    }
403
404    unsigned short int gr_bid;
405   
406    for (GroupHT::iterator g = groupHt.begin(); // for each group we found
407         g != groupHt.end();
408         ++g){ 
409       // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
410       //        warning: unsigned int format, different type arg
411       sscanf(g->first.c_str(),"%x",&gr_bid);
412       tk = g->first + "|0000";                  // generate the element full tag
413                      
414       if ( tagHT.count(tk) == 0) {              // if element 0x0000 not found
415          gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");       
416          elemZ = new gdcmHeaderEntry(tagZ);
417          elemZ->SetLength(4);
418          Add(elemZ);                            // create it
419       } else {
420          elemZ=GetHeaderEntryByNumber(gr_bid, 0x0000);
421       }     
422       sprintf(trash ,"%d",g->second);
423       str_trash=trash;
424       elemZ->SetValue(str_trash);
425    }   
426 }
427
428 /**
429  * \ingroup gdcmHeaderEntrySet
430  * \brief   writes on disc according to the requested format
431  * \        (ACR-NEMA, ExplicitVR, ImplicitVR) the image
432  * \ warning does NOT add the missing elements in the header :
433  * \         it's up to the user doing it !
434  * \         (function CheckHeaderCoherence to be written)
435  * @param   type type of the File to be written 
436  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
437  * @param   _fp already open file pointer
438  * @return  
439  */
440 void gdcmHeaderEntrySet::WriteEntries(FileType type, FILE * _fp) {
441    guint16 gr, el;
442    guint32 lgr;
443    const char * val;
444    std::string vr;
445    guint32 val_uint32;
446    guint16 val_uint16;
447    
448    std::vector<std::string> tokens;
449    
450    // TODO : use listEntries to iterate, not TagHt!
451    //        pb : gdcmHeaderEntrySet.Add does NOT update listEntries
452    //        find a trick in STL to do it, at low cost !
453
454    void *ptr;
455
456    // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian 
457    // restent a tester les echecs en ecriture (apres chaque fwrite)
458
459    for (TagHeaderEntryHT::iterator tag2=tagHT.begin();
460         tag2 != tagHT.end();
461         ++tag2){
462
463       gr =  tag2->second->GetGroup();
464       el =  tag2->second->GetElement();
465       lgr = tag2->second->GetLength();
466       val = tag2->second->GetValue().c_str();
467       vr =  tag2->second->GetVR();
468       
469       if ( type == ACR ) { 
470          if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
471          if (gr %2)         continue; // ignore shadow groups
472          if (vr == "SQ" )   continue; // ignore Sequences
473                    // TODO : find a trick to *skip* the SeQuences !
474                    // Not only ignore the SQ element
475          if (gr == 0xfffe ) continue; // ignore delimiters
476       } 
477
478       fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
479       fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp);  //element
480
481       if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
482          // EXPLICIT VR
483          guint16 z=0, shortLgr;
484          fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
485
486          if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
487             fwrite ( &z,  (size_t)2 ,(size_t)1 ,_fp);
488             fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
489
490          } else {
491             shortLgr=lgr;
492             fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
493          }
494       } else { // IMPLICIT VR
495          fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
496       }
497
498       if (vr == "US" || vr == "SS") {
499          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
500          Tokenize (tag2->second->GetValue(), tokens, "\\");
501          for (unsigned int i=0; i<tokens.size();i++) {
502             val_uint16 = atoi(tokens[i].c_str());
503             ptr = &val_uint16;
504             fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
505          }
506          tokens.clear();
507          continue;
508       }
509       if (vr == "UL" || vr == "SL") {
510          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
511          Tokenize (tag2->second->GetValue(), tokens, "\\");
512          for (unsigned int i=0; i<tokens.size();i++) {
513             val_uint32 = atoi(tokens[i].c_str());
514             ptr = &val_uint32;
515             fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
516          }
517          tokens.clear();
518          continue;
519       }     
520       // Pixels are never loaded in the element !
521       if ((gr == 0x7fe0) && (el == 0x0010) ) break;
522
523       fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
524    }
525 }
526
527 //-----------------------------------------------------------------------------