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