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