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