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