]> Creatis software - gdcm.git/blob - src/gdcmElValSet.cxx
*FIX : gdcmHeader::LoadElements is now based
[gdcm.git] / src / gdcmElValSet.cxx
1 // gdcmElValSet.cxx
2
3 #include "gdcmUtil.h"
4 #include "gdcmElValSet.h"
5 #include "gdcmTS.h"
6 #include <sstream>
7
8 gdcmElValSet::~gdcmElValSet() {
9    for (TagElValueHT::iterator tag = tagHt.begin(); tag != tagHt.end(); ++tag) {
10       gdcmElValue* EntryToDelete = tag->second;
11       if ( EntryToDelete )
12          delete EntryToDelete;
13    }
14    tagHt.clear();
15    // Since Add() adds symetrical in both tagHt and NameHt we can
16    // assume all the pointed gdcmElValues are already cleaned-up when
17    // we cleaned tagHt.
18    NameHt.clear();
19 }
20
21 TagElValueHT & gdcmElValSet::GetTagHt(void) {
22         return tagHt;
23 }
24
25
26 ListTag & gdcmElValSet::GetListElem(void) {
27         return listElem;
28 }
29
30 /**
31  * \ingroup gdcmElValSet
32  * \brief   
33  * @param   newElValue
34  * @return  
35  */
36 void gdcmElValSet::Add(gdcmElValue * newElValue) {
37         tagHt [newElValue->GetKey()]  = newElValue;
38         NameHt[newElValue->GetName()] = newElValue;
39         
40 // WARNING : push_bash in listElem ONLY during ParseHeader
41 // TODO : something to allow further Elements addition 
42 // position to be taken care of !       
43         listElem.push_back(newElValue); 
44 }
45
46 /**
47  * \ingroup gdcmElValSet
48  * \brief   Checks if a given Dicom element exists
49  * \        within a ElValSet
50  * @param   Group
51  * @param   Elem
52  * @return  
53  */
54 int gdcmElValSet::CheckIfExistByNumber(guint16 Group, guint16 Elem ) {
55         std::string key = TranslateToKey(Group, Elem );
56         return (tagHt.count(key));
57 }
58
59 /**
60  * \ingroup gdcmElValSet
61  * \brief   
62  */
63 void gdcmElValSet::Print(std::ostream & os) {
64
65    size_t o;
66    short int g, e;
67    TSKey v;
68    std::string d2;
69    gdcmTS * ts = gdcmGlobal::GetTS();
70    
71    for (TagElValueHT::iterator tag = tagHt.begin();
72            tag != tagHt.end();
73            ++tag){
74       g = tag->second->GetGroup();
75       e = tag->second->GetElement();
76       v = tag->second->GetValue();
77       o = tag->second->GetOffset();
78       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
79                  
80       os << tag->first << ": ";
81       os << " lgr : " << tag->second->GetLength();
82       os << ", Offset : " << o;
83       os << " x(" << std::hex << o << std::dec << ") ";
84       os << "\t[" << tag->second->GetVR()    << "]";
85       os << "\t[" << tag->second->GetName()  << "]";       
86       os << "\t[" << d2 << "]";
87       
88       // Display the UID value (instead of displaying the rough code)  
89       if (g == 0x0002) {  // Some more to be displayed ?
90          if ( (e == 0x0010) || (e == 0x0002) )     
91             os << "  ==>\t[" << ts->GetValue(v) << "]";   
92       } else {
93          if (g == 0x0008) {
94             if ( (e == 0x0016) || (e == 0x1150)  )         
95                os << "  ==>\t[" << ts->GetValue(v) << "]"; 
96          }
97       }              
98       os << std::endl;
99    }
100    
101    std::cout << "----------------------------------------------" << std::endl;
102    
103    //for (ListTag::iterator i = listElem.begin();
104    
105   char tag[9];
106    
107   for (std::list<gdcmElValue*>::iterator i = listElem.begin();  
108            i != listElem.end();
109            ++i){
110       sprintf(tag,"%04x|%04x",(*i)->GetGroup(),(*i)->GetElement());
111       g = (*i)->GetGroup();
112       e = (*i)->GetElement();
113       v = (*i)->GetValue();
114       o = (*i)->GetOffset();
115       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
116       //os << std::hex <<g << "|" << e << std::dec << ": ";
117       os << tag << " : ";
118       os << " lgr : " << (*i)->GetLength();
119       os << ", Offset : " << o;
120       os << " x(" << std::hex << o << std::dec << ") ";
121       os << "\t[" << (*i)->GetVR()    << "]";
122       os << "\t[" << (*i)->GetName()  << "]";       
123       os << "\t[" << d2 << "]";
124       
125       // Display the UID value (instead of displaying the rough code)  
126       if (g == 0x0002) {  // Any more to be displayed ?
127          if ( (e == 0x0010) || (e == 0x0002) )     
128             os << "  ==>\t[" << ts->GetValue(v) << "]";   
129       } else {
130          if (g == 0x0008) {
131             if ( (e == 0x0016) || (e == 0x1150)  )         
132                os << "  ==>\t[" << ts->GetValue(v) << "]"; 
133          }
134       }              
135       os << std::endl;
136
137    }
138            
139
140
141 /**
142  * \ingroup gdcmElValSet
143  * \brief   
144  */
145 void gdcmElValSet::PrintByName(std::ostream & os) {
146    for (TagElValueNameHT::iterator tag = NameHt.begin();
147           tag != NameHt.end();
148           ++tag){
149       os << tag->first << ": ";
150       os << "[" << tag->second->GetValue() << "]";
151       os << "[" << tag->second->GetKey()   << "]";
152       os << "[" << tag->second->GetVR()    << "]" << std::endl;
153    }
154 }
155
156 /**
157  * \ingroup gdcmElValSet
158  * \brief   
159  * @param   group 
160  * @param   element 
161  * @return  
162  */
163 gdcmElValue* gdcmElValSet::GetElementByNumber(guint16 group, guint16 element) {
164    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
165    if ( ! tagHt.count(key))
166       return (gdcmElValue*)0;
167    return tagHt.find(key)->second;
168 }
169
170 /**
171  * \ingroup gdcmElValSet
172  * \brief   
173  * @return  
174  */
175 gdcmElValue* gdcmElValSet::GetElementByName(std::string TagName) {
176    if ( ! NameHt.count(TagName))
177       return (gdcmElValue*)0;
178    return NameHt.find(TagName)->second;
179 }
180
181 /**
182  * \ingroup gdcmElValSet
183  * \brief   
184  * @param   group 
185  * @param   element 
186  * @return  
187  */
188 std::string gdcmElValSet::GetElValueByNumber(guint16 group, guint16 element) {
189    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
190    if ( ! tagHt.count(key))
191       return GDCM_UNFOUND;
192    return tagHt.find(key)->second->GetValue();
193 }
194
195 /**
196  * \ingroup gdcmElValSet
197  * \brief   
198  * @return  
199  */
200 std::string gdcmElValSet::GetElValueByName(std::string TagName) {
201    if ( ! NameHt.count(TagName))
202       return GDCM_UNFOUND;
203    return NameHt.find(TagName)->second->GetValue();
204 }
205
206 /**
207  * \ingroup gdcmElValSet
208  * \brief   
209  * @param   content
210  * @param   group 
211  * @param   element 
212  * @return  
213  */
214 int gdcmElValSet::SetElValueByNumber(std::string content,
215                                      guint16 group, guint16 element) {
216    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
217    if ( ! tagHt.count(key))
218       return 0;
219    int l = content.length();
220    if(l%2) {  // Odd length are padded with a space (020H).
221       l++;
222       content = content + '\0';
223    }
224    tagHt[key]->SetValue(content);
225
226    std::string vr = tagHt[key]->GetVR();
227    guint32 lgr;
228
229    if( (vr == "US") || (vr == "SS") ) 
230       lgr = 2;
231    else if( (vr == "UL") || (vr == "SL") )
232       lgr = 4;
233    else
234       lgr = l;     
235    tagHt[key]->SetLength(lgr); 
236
237         
238
239    return 1;
240 }
241
242 /**
243  * \ingroup gdcmElValSet
244  * \brief   
245  * @param   content
246  * @param   TagName
247  * @return  
248  */
249 int gdcmElValSet::SetElValueByName(std::string content, std::string TagName) {
250    if ( ! NameHt.count(TagName))
251       return 0;
252    int l = content.length();
253    if(l%2) {  // Odd length are padded with a space (020H).
254       l++;
255       // Well. I know that '/0' is NOT a space
256       // but it doesn't work with a space. 
257       // Use hexedit and see 0002|0010 value (Transfer Syntax UID)
258       content = content + '\0';
259    }
260    NameHt[TagName]->SetValue(content);
261
262    std::string vr = NameHt[TagName]->GetVR();
263    guint32 lgr;
264
265    if( (vr == "US") || (vr == "SS") ) 
266       lgr = 2;
267    else if( (vr == "UL") || (vr == "SL") )
268       lgr = 4;     
269    else 
270       lgr = content.length();
271            
272 // TODO : WARNING: le cas de l'element des pixels (7fe0,0010) n'est pas traite
273 // par SetElValueByName
274 // il faudra utiliser SetElValueByNumber
275            
276    NameHt[TagName]->SetLength(lgr);
277    return 1;            
278 }
279
280 /**
281  * \ingroup gdcmElValSet
282  * \brief   Generate a free TagKey i.e. a TagKey that is not present
283  *          in the TagHt dictionary.
284  * @param   group The generated tag must belong to this group.  
285  * @return  The element of tag with given group which is fee.
286  */
287 guint32 gdcmElValSet::GenerateFreeTagKeyInGroup(guint16 group) {
288    for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
289       TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
290       if (tagHt.count(key) == 0)
291          return elem;
292    }
293    return UINT32_MAX;
294 }
295
296 /**
297  * \ingroup gdcmElValSet
298  * \brief   
299  * @param   area
300  * @param   group 
301  * @param   element 
302  * @return  
303  */
304 int gdcmElValSet::SetVoidAreaByNumber(void * area,
305                                       guint16 group, guint16 element) {
306    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
307    if ( ! tagHt.count(key))
308       return 0;
309    tagHt[key]->SetVoidArea(area);        
310    return 1 ;           
311 }
312
313 /**
314  * \ingroup gdcmElValSet
315  * \brief   
316  * @param   length
317  * @param   group 
318  * @param   element 
319  * @return  int acts as a boolean
320  */
321 int gdcmElValSet::SetElValueLengthByNumber(guint32 length,
322                                            guint16 group, guint16 element) {
323    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
324    if ( ! tagHt.count(key))
325       return 0;
326    if (length%2) length++; // length must be even
327    tagHt[key]->SetLength(length);        
328    return 1 ;           
329 }
330 /**
331  * \ingroup gdcmElValSet
332  * \brief   
333  * @param   length
334  * @param   TagName
335  * @return  
336  */
337 int gdcmElValSet::SetElValueLengthByName(guint32 length, std::string TagName) {
338    if ( ! NameHt.count(TagName))
339       return 0;
340    if (length%2) length++; // length must be even
341    NameHt.find(TagName)->second->SetLength(length);      
342    return 1 ;           
343 }
344
345 /**
346  * \ingroup gdcmElValSet
347  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
348  * @param   SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
349  * @param   type
350  */
351 void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) {
352    guint16 gr, el;
353    std::string vr;
354    
355    gdcmElValue *elem;
356    char trash[10];
357    std::string str_trash;
358    
359    GroupKey key;
360    GroupHT groupHt;  // to hold the length of each group
361    TagKey tk;
362    // remember :
363    // typedef std::map<GroupKey, int> GroupHT;
364    
365    gdcmElValue *elemZ;
366   
367    // for each Tag in the DCM Header
368    
369    for (TagElValueHT::iterator tag2 = tagHt.begin(); 
370         tag2 != tagHt.end();
371         ++tag2){
372
373       elem  = tag2->second;
374       gr = elem->GetGroup();
375       el = elem->GetElement();
376       vr = elem->GetVR(); 
377                  
378       sprintf(trash, "%04x", gr);
379       key = trash;              // generate 'group tag'
380       
381       // if the caller decided not to take SEQUENCEs into account 
382       // e.g : he wants to write an ACR-NEMA File 
383                 
384       if (SkipSequence && vr == "SQ") continue;
385       
386          // Still unsolved problem :
387          // we cannot find the 'Sequence Delimitation Item'
388          // since it's at the end of the Hash Table
389          // (fffe,e0dd) 
390              
391          // pas SEQUENCE en ACR-NEMA
392          // WARNING : 
393          // --> la descente a l'interieur' des SQ 
394          // devrait etre faite avec une liste chainee, pas avec une HTable...
395             
396       if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
397          if (el == 0x0000) {          // the first elem is 0x0000
398             groupHt[key] = 0;         // initialize group length 
399          } else {
400             groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
401          } 
402       } else {   // any elem but the first    
403          if (type == ExplicitVR) {
404             if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
405                groupHt[key] +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
406             }
407          }
408          groupHt[key] += 2 + 2 + 4 + elem->GetLength(); 
409       } 
410    }
411
412    unsigned short int gr_bid;
413   
414    for (GroupHT::iterator g = groupHt.begin(); // for each group we found
415         g != groupHt.end();
416         ++g){ 
417       // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
418       //        warning: unsigned int format, different type arg
419       sscanf(g->first.c_str(),"%x",&gr_bid);
420       tk = g->first + "|0000";                  // generate the element full tag
421                      
422       if ( tagHt.count(tk) == 0) {              // if element 0x0000 not found
423          gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");       
424          elemZ = new gdcmElValue(tagZ);
425          elemZ->SetLength(4);
426          Add(elemZ);                            // create it
427       } else {
428          elemZ=GetElementByNumber(gr_bid, 0x0000);
429       }     
430       sprintf(trash ,"%d",g->second);
431       str_trash=trash;
432       elemZ->SetValue(str_trash);
433    }   
434 }
435
436 /**
437  * \ingroup gdcmElValSet
438  * \brief   
439  * @param   type
440  * @param   _fp 
441  * @return  
442  */
443 void gdcmElValSet::WriteElements(FileType type, FILE * _fp) {
444    guint16 gr, el;
445    guint32 lgr;
446    const char * val;
447    std::string vr;
448    guint32 val_uint32;
449    guint16 val_uint16;
450    
451    std::vector<std::string> tokens;
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 (TagElValueHT::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      // std::cout << "Tag "<< std::hex << gr << " " << el << std::endl;
469
470       if ( type == ACR ) { 
471          if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
472          if (gr %2)         continue; // ignore shadow groups
473          if (vr == "SQ" )   continue; // ignore Sequences
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 /**
527  * \ingroup gdcmElValSet
528  * \brief   
529  * @param   _fp
530  * @param   type
531  * @return  
532  */
533 int gdcmElValSet::Write(FILE * _fp, FileType type) {
534
535         // Question :
536         // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
537         // (FileType est un champ de gdcmHeader ...)
538         // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
539         // no way 
540         // a moins de se livrer a un tres complique ajout des champs manquants.
541         // faire un CheckAndCorrectHeader (?)
542
543    if ( (type == ImplicitVR) || (type == ExplicitVR) )
544       UpdateGroupLength(false,type);
545    if ( type == ACR)
546       UpdateGroupLength(true,ACR);
547
548    WriteElements(type, _fp);
549    return(1);
550 }