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