]> Creatis software - gdcm.git/blob - src/gdcmElValSet.cxx
*ENH gdcmHeader constructor has one more parameter (default value : false)
[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    std::cout << "------------- using tagHt ----------------------------" << std::endl;
72    
73    for (TagElValueHT::iterator tag = tagHt.begin();
74            tag != tagHt.end();
75            ++tag){
76       g = tag->second->GetGroup();
77       e = tag->second->GetElement();
78       v = tag->second->GetValue();
79       o = tag->second->GetOffset();
80       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
81                  
82       os << tag->first << ": ";
83       os << " lgr : " << tag->second->GetLength();
84       os << ", Offset : " << o;
85       os << " x(" << std::hex << o << std::dec << ") ";
86       os << "\t[" << tag->second->GetVR()    << "]";
87       os << "\t[" << tag->second->GetName()  << "]";       
88       os << "\t[" << d2 << "]";
89       
90       // Display the UID value (instead of displaying the rough code)  
91       if (g == 0x0002) {  // Some more to be displayed ?
92          if ( (e == 0x0010) || (e == 0x0002) )     
93             os << "  ==>\t[" << ts->GetValue(v) << "]";   
94       } else {
95          if (g == 0x0008) {
96             if ( (e == 0x0016) || (e == 0x1150)  )         
97                os << "  ==>\t[" << ts->GetValue(v) << "]"; 
98          }
99       }              
100       os << std::endl;
101    }
102    
103    std::cout << "------------ using listElem -----------------" << std::endl;
104       
105   guint32 lgth;
106   char greltag[9];  //group element tag
107    
108   for (std::list<gdcmElValue*>::iterator i = listElem.begin();  
109            i != listElem.end();
110            ++i){
111       g = (*i)->GetGroup();
112       e = (*i)->GetElement();
113       sprintf(greltag,"%04x|%04x",g,e);      
114       v = (*i)->GetValue();
115       o = (*i)->GetOffset();
116       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
117       os << greltag << ": lgth : ";
118       lgth = (*i)->GetReadLength();
119       if ( lgth == 0xffffffff) 
120          os << std::hex << lgth << std::dec ;
121       else
122          os << lgth;
123       os << ", Offset : " << o;
124       os << " x(" << std::hex << o << std::dec << ") ";
125       os << "\t[" << (*i)->GetVR()    << "]";
126       os << "\t[" << (*i)->GetName()  << "]";       
127       os << "\t[" << d2 << "]";
128       
129       // Display the UID value (instead of displaying the rough code)  
130       if (g == 0x0002) {  // Any more to be displayed ?
131          if ( (e == 0x0010) || (e == 0x0002) )     
132             os << "  ==>\t[" << ts->GetValue(v) << "]";   
133       } else {
134          if (g == 0x0008) {
135             if ( (e == 0x0016) || (e == 0x1150)  )         
136                os << "  ==>\t[" << ts->GetValue(v) << "]"; 
137          }
138       }              
139       os << std::endl;
140    }       
141
142
143 /**
144  * \ingroup gdcmElValSet
145  * \brief   
146  */
147 void gdcmElValSet::PrintByName(std::ostream & os) {
148    for (TagElValueNameHT::iterator tag = NameHt.begin();
149           tag != NameHt.end();
150           ++tag){
151       os << tag->first << ": ";
152       os << "[" << tag->second->GetValue() << "]";
153       os << "[" << tag->second->GetKey()   << "]";
154       os << "[" << tag->second->GetVR()    << "]" << std::endl;
155    }
156 }
157
158 /**
159  * \ingroup gdcmElValSet
160  * \brief   
161  * @param   group 
162  * @param   element 
163  * @return  
164  */
165 gdcmElValue* gdcmElValSet::GetElementByNumber(guint16 group, guint16 element) {
166    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
167    if ( ! tagHt.count(key))
168       return (gdcmElValue*)0;
169    return tagHt.find(key)->second;
170 }
171
172 /**
173  * \ingroup gdcmElValSet
174  * \brief   
175  * @return  
176  */
177 gdcmElValue* gdcmElValSet::GetElementByName(std::string TagName) {
178    if ( ! NameHt.count(TagName))
179       return (gdcmElValue*)0;
180    return NameHt.find(TagName)->second;
181 }
182
183 /**
184  * \ingroup gdcmElValSet
185  * \brief   
186  * @param   group 
187  * @param   element 
188  * @return  
189  */
190 std::string gdcmElValSet::GetElValueByNumber(guint16 group, guint16 element) {
191    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
192    if ( ! tagHt.count(key))
193       return GDCM_UNFOUND;
194    return tagHt.find(key)->second->GetValue();
195 }
196
197 /**
198  * \ingroup gdcmElValSet
199  * \brief   
200  * @return  
201  */
202 std::string gdcmElValSet::GetElValueByName(std::string TagName) {
203    if ( ! NameHt.count(TagName))
204       return GDCM_UNFOUND;
205    return NameHt.find(TagName)->second->GetValue();
206 }
207
208 /**
209  * \ingroup gdcmElValSet
210  * \brief   
211  * @param   content
212  * @param   group 
213  * @param   element 
214  * @return  
215  */
216 int gdcmElValSet::SetElValueByNumber(std::string content,
217                                      guint16 group, guint16 element) {
218    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
219    if ( ! tagHt.count(key))
220       return 0;
221    int l = content.length();
222    if(l%2) {  // Odd length are padded with a space (020H).
223       l++;
224       content = content + '\0';
225    }
226    tagHt[key]->SetValue(content);
227
228    std::string vr = tagHt[key]->GetVR();
229    guint32 lgr;
230
231    if( (vr == "US") || (vr == "SS") ) 
232       lgr = 2;
233    else if( (vr == "UL") || (vr == "SL") )
234       lgr = 4;
235    else
236       lgr = l;     
237    tagHt[key]->SetLength(lgr); 
238    return 1;
239 }
240
241 /**
242  * \ingroup gdcmElValSet
243  * \brief   
244  * @param   content
245  * @param   TagName
246  * @return  
247  */
248 int gdcmElValSet::SetElValueByName(std::string content, std::string TagName) {
249    if ( ! NameHt.count(TagName))
250       return 0;
251    int l = content.length();
252    if(l%2) {  // Odd length are padded with a space (020H).
253       l++;
254       // Well. I know that '/0' is NOT a space
255       // but it doesn't work with a space. 
256       // Use hexedit and see 0002|0010 value (Transfer Syntax UID)
257       content = content + '\0';
258    }
259    NameHt[TagName]->SetValue(content);
260
261    std::string vr = NameHt[TagName]->GetVR();
262    guint32 lgr;
263
264    if( (vr == "US") || (vr == "SS") ) 
265       lgr = 2;
266    else if( (vr == "UL") || (vr == "SL") )
267       lgr = 4;     
268    else 
269       lgr = content.length();
270            
271 // TODO : WARNING: le cas de l'element des pixels (7fe0,0010) n'est pas traite
272 // par SetElValueByName
273 // il faudra utiliser SetElValueByNumber
274            
275    NameHt[TagName]->SetLength(lgr);
276    return 1;            
277 }
278
279 /**
280  * \ingroup gdcmElValSet
281  * \brief   Generate a free TagKey i.e. a TagKey that is not present
282  *          in the TagHt dictionary.
283  * @param   group The generated tag must belong to this group.  
284  * @return  The element of tag with given group which is fee.
285  */
286 guint32 gdcmElValSet::GenerateFreeTagKeyInGroup(guint16 group) {
287    for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
288       TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
289       if (tagHt.count(key) == 0)
290          return elem;
291    }
292    return UINT32_MAX;
293 }
294
295 /**
296  * \ingroup gdcmElValSet
297  * \brief   
298  * @param   area
299  * @param   group 
300  * @param   element 
301  * @return  
302  */
303 int gdcmElValSet::SetVoidAreaByNumber(void * area,
304                                       guint16 group, guint16 element) {
305    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
306    if ( ! tagHt.count(key))
307       return 0;
308    tagHt[key]->SetVoidArea(area);        
309    return 1 ;           
310 }
311
312 /**
313  * \ingroup gdcmElValSet
314  * \brief   
315  * @param   length
316  * @param   group 
317  * @param   element 
318  * @return  int acts as a boolean
319  */
320 int gdcmElValSet::SetElValueLengthByNumber(guint32 length,
321                                            guint16 group, guint16 element) {
322    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
323    if ( ! tagHt.count(key))
324       return 0;
325    if (length%2) length++; // length must be even
326    tagHt[key]->SetLength(length);        
327    return 1 ;           
328 }
329 /**
330  * \ingroup gdcmElValSet
331  * \brief   
332  * @param   length
333  * @param   TagName
334  * @return  
335  */
336 int gdcmElValSet::SetElValueLengthByName(guint32 length, std::string TagName) {
337    if ( ! NameHt.count(TagName))
338       return 0;
339    if (length%2) length++; // length must be even
340    NameHt.find(TagName)->second->SetLength(length);      
341    return 1 ;           
342 }
343
344 /**
345  * \ingroup gdcmElValSet
346  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
347  * @param   SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
348  * @param   type
349  */
350 void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) {
351    guint16 gr, el;
352    std::string vr;
353    
354    gdcmElValue *elem;
355    char trash[10];
356    std::string str_trash;
357    
358    GroupKey key;
359    GroupHT groupHt;  // to hold the length of each group
360    TagKey tk;
361    // remember :
362    // typedef std::map<GroupKey, int> GroupHT;
363    
364    gdcmElValue *elemZ;
365   
366    // for each Tag in the DCM Header
367    
368    for (TagElValueHT::iterator tag2 = tagHt.begin(); 
369         tag2 != tagHt.end();
370         ++tag2){
371
372       elem  = tag2->second;
373       gr = elem->GetGroup();
374       el = elem->GetElement();
375       vr = elem->GetVR(); 
376                  
377       sprintf(trash, "%04x", gr);
378       key = trash;              // generate 'group tag'
379       
380       // if the caller decided not to take SEQUENCEs into account 
381       // e.g : he wants to write an ACR-NEMA File 
382                 
383       if (SkipSequence && vr == "SQ") continue;
384       
385          // Still unsolved problem :
386          // we cannot find the 'Sequence Delimitation Item'
387          // since it's at the end of the Hash Table
388          // (fffe,e0dd) 
389              
390          // pas SEQUENCE en ACR-NEMA
391          // WARNING : 
392          // --> la descente a l'interieur' des SQ 
393          // devrait etre faite avec une liste chainee, pas avec une HTable...
394             
395       if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
396          if (el == 0x0000) {          // the first elem is 0x0000
397             groupHt[key] = 0;         // initialize group length 
398          } else {
399             groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
400          } 
401       } else {   // any elem but the first    
402          if (type == ExplicitVR) {
403             if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
404                groupHt[key] +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
405             }
406          }
407          groupHt[key] += 2 + 2 + 4 + elem->GetLength(); 
408       } 
409    }
410
411    unsigned short int gr_bid;
412   
413    for (GroupHT::iterator g = groupHt.begin(); // for each group we found
414         g != groupHt.end();
415         ++g){ 
416       // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
417       //        warning: unsigned int format, different type arg
418       sscanf(g->first.c_str(),"%x",&gr_bid);
419       tk = g->first + "|0000";                  // generate the element full tag
420                      
421       if ( tagHt.count(tk) == 0) {              // if element 0x0000 not found
422          gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");       
423          elemZ = new gdcmElValue(tagZ);
424          elemZ->SetLength(4);
425          Add(elemZ);                            // create it
426       } else {
427          elemZ=GetElementByNumber(gr_bid, 0x0000);
428       }     
429       sprintf(trash ,"%d",g->second);
430       str_trash=trash;
431       elemZ->SetValue(str_trash);
432    }   
433 }
434
435 /**
436  * \ingroup gdcmElValSet
437  * \brief   writes on disc according to the requested format
438  * \        (ACR-NEMA, DICOM, RAW) the image
439  * \ warning does NOT add the missing elements in the header :
440  * \         it's up to the user doing it !
441  * \         (function CheckHeaderCoherence to be written)
442  * @param   type type of the File to be written 
443  *          (ACR-NEMA, DICOM, RAW)
444  * @param   _fp already open file pointer
445  * @return  
446  */
447 void gdcmElValSet::WriteElements(FileType type, FILE * _fp) {
448    guint16 gr, el;
449    guint32 lgr;
450    const char * val;
451    std::string vr;
452    guint32 val_uint32;
453    guint16 val_uint16;
454    
455    std::vector<std::string> tokens;
456    
457    // TODO : use listElem to iterate, not TagHt!
458    //        pb : gdcmElValSet.Add does NOT update listElem
459    //        find a trick in STL to do it, at low cost !
460
461    void *ptr;
462
463    // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian 
464    // restent a tester les echecs en ecriture (apres chaque fwrite)
465
466    for (TagElValueHT::iterator tag2=tagHt.begin();
467         tag2 != tagHt.end();
468         ++tag2){
469
470       gr =  tag2->second->GetGroup();
471       el =  tag2->second->GetElement();
472       lgr = tag2->second->GetLength();
473       val = tag2->second->GetValue().c_str();
474       vr =  tag2->second->GetVR();
475       
476      // std::cout << "Tag "<< std::hex << gr << " " << el << std::endl;
477
478       if ( type == ACR ) { 
479          if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
480          if (gr %2)         continue; // ignore shadow groups
481          if (vr == "SQ" )   continue; // ignore Sequences
482          if (gr == 0xfffe ) continue; // ignore delimiters
483       } 
484
485       fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
486       fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp);  //element
487
488       if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
489          // EXPLICIT VR
490          guint16 z=0, shortLgr;
491          fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
492
493          if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
494             fwrite ( &z,  (size_t)2 ,(size_t)1 ,_fp);
495             fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
496
497          } else {
498             shortLgr=lgr;
499             fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
500          }
501       } else { // IMPLICIT VR
502          fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
503       }
504
505       if (vr == "US" || vr == "SS") {
506          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
507          Tokenize (tag2->second->GetValue(), tokens, "\\");
508          for (unsigned int i=0; i<tokens.size();i++) {
509             val_uint16 = atoi(tokens[i].c_str());
510             ptr = &val_uint16;
511             fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
512          }
513          tokens.clear();
514          continue;
515       }
516       if (vr == "UL" || vr == "SL") {
517          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
518          Tokenize (tag2->second->GetValue(), tokens, "\\");
519          for (unsigned int i=0; i<tokens.size();i++) {
520             val_uint32 = atoi(tokens[i].c_str());
521             ptr = &val_uint32;
522             fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
523          }
524          tokens.clear();
525          continue;
526       }     
527       // Pixels are never loaded in the element !
528       if ((gr == 0x7fe0) && (el == 0x0010) ) break;
529
530       fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
531    }
532 }
533
534 /**
535  * \ingroup gdcmElValSet
536  * \brief   
537  * @param   _fp
538  * @param   type
539  * @return  
540  */
541 int gdcmElValSet::Write(FILE * _fp, FileType type) {
542
543         // Question :
544         // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
545         // (FileType est un champ de gdcmHeader ...)
546         // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
547         // no way 
548         // a moins de se livrer a un tres complique ajout des champs manquants.
549         // faire un CheckAndCorrectHeader (?)
550
551    if ( (type == ImplicitVR) || (type == ExplicitVR) )
552       UpdateGroupLength(false,type);
553    if ( type == ACR)
554       UpdateGroupLength(true,ACR);
555
556    WriteElements(type, _fp);
557    return(1);
558 }