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