]> Creatis software - gdcm.git/blob - src/gdcmElValSet.cxx
91045a055093f03dfd8fddcedd534fef3ca0dfb2
[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    int l = content.length();
171    if(l%2) {  // Odd length are padded with a space (020H).
172       l++;
173       content = content + '\0';
174    }
175    tagHt[key]->SetValue(content);
176
177    std::string vr = tagHt[key]->GetVR();
178    guint32 lgr;
179
180    if( (vr == "US") || (vr == "SS") ) 
181       lgr = 2;
182    else if( (vr == "UL") || (vr == "SL") )
183       lgr = 4;
184    else
185       lgr = l;     
186    tagHt[key]->SetLength(lgr); 
187
188         
189
190    return 1;
191 }
192
193 /**
194  * \ingroup gdcmElValSet
195  * \brief   
196  * @param   content
197  * @param   TagName
198  * @return  
199  */
200 int gdcmElValSet::SetElValueByName(std::string content, std::string TagName) {
201    if ( ! NameHt.count(TagName))
202       return 0;
203    int l = content.length();
204    if(l%2) {  // Odd length are padded with a space (020H).
205       l++;
206       // Well. I know that '/0' is NOT a space
207       // but it doesn't work with a space. 
208       // Use hexedit and see 0002|0010 value (Transfer Syntax UID)
209       content = content + '\0';
210    }
211    NameHt[TagName]->SetValue(content);
212
213    std::string vr = NameHt[TagName]->GetVR();
214    guint32 lgr;
215
216    if( (vr == "US") || (vr == "SS") ) 
217       lgr = 2;
218    else if( (vr == "UL") || (vr == "SL") )
219       lgr = 4;     
220    else 
221       lgr = content.length();
222            
223 // TODO : WARNING: le cas de l'element des pixels (7fe0,0010) n'est pas traite
224 // par SetElValueByName
225 // il faudra utiliser SetElValueByNumber
226            
227    NameHt[TagName]->SetLength(lgr);
228    return 1;            
229 }
230
231 /**
232  * \ingroup gdcmElValSet
233  * \brief   Generate a free TagKey i.e. a TagKey that is not present
234  *          in the TagHt dictionary.
235  * @param   group The generated tag must belong to this group.  
236  * @return  The element of tag with given group which is fee.
237  */
238 guint32 gdcmElValSet::GenerateFreeTagKeyInGroup(guint16 group) {
239    for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
240       TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
241       if (tagHt.count(key) == 0)
242          return elem;
243    }
244    return UINT32_MAX;
245 }
246
247 /**
248  * \ingroup gdcmElValSet
249  * \brief   
250  * @param   area
251  * @param   group 
252  * @param   element 
253  * @return  
254  */
255 int gdcmElValSet::SetVoidAreaByNumber(void * area,
256                                       guint16 group, guint16 element) {
257    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
258    if ( ! tagHt.count(key))
259       return 0;
260    tagHt[key]->SetVoidArea(area);        
261    return 1 ;           
262 }
263
264 /**
265  * \ingroup gdcmElValSet
266  * \brief   
267  * @param   length
268  * @param   group 
269  * @param   element 
270  * @return  int acts as a boolean
271  */
272 int gdcmElValSet::SetElValueLengthByNumber(guint32 length,
273                                            guint16 group, guint16 element) {
274    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
275    if ( ! tagHt.count(key))
276       return 0;
277    if (length%2) length++; // length must be even
278    tagHt[key]->SetLength(length);        
279    return 1 ;           
280 }
281 /**
282  * \ingroup gdcmElValSet
283  * \brief   
284  * @param   length
285  * @param   TagName
286  * @return  
287  */
288 int gdcmElValSet::SetElValueLengthByName(guint32 length, std::string TagName) {
289    if ( ! NameHt.count(TagName))
290       return 0;
291    if (length%2) length++; // length must be even
292    NameHt.find(TagName)->second->SetLength(length);      
293    return 1 ;           
294 }
295
296 /**
297  * \ingroup gdcmElValSet
298  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
299  * @param   SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
300  * @param   type
301  */
302 void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) {
303    guint16 gr, el;
304    std::string vr;
305    
306    gdcmElValue *elem;
307    char trash[10];
308    std::string str_trash;
309    
310    GroupKey key;
311    GroupHT groupHt;  // to hold the length of each group
312    TagKey tk;
313    // remember :
314    // typedef std::map<GroupKey, int> GroupHT;
315    
316    gdcmElValue *elemZ;
317   
318    // for each Tag in the DCM Header
319    
320    for (TagElValueHT::iterator tag2 = tagHt.begin(); 
321         tag2 != tagHt.end();
322         ++tag2){
323
324       elem  = tag2->second;
325       gr = elem->GetGroup();
326       el = elem->GetElement();
327       vr = elem->GetVR(); 
328                  
329       sprintf(trash, "%04x", gr);
330       key = trash;              // generate 'group tag'
331       
332       // if the caller decided not to take SEQUENCEs into account 
333       // e.g : he wants to write an ACR-NEMA File 
334                 
335       if (SkipSequence && vr == "SQ") continue;
336       
337          // Still unsolved problem :
338          // we cannot find the 'Sequence Delimitation Item'
339          // since it's at the end of the Hash Table
340          // (fffe,e0dd) 
341              
342          // pas SEQUENCE en ACR-NEMA
343          // WARNING : 
344          // --> la descente a l'interieur' des SQ 
345          // devrait etre faite avec une liste chainee, pas avec une HTable...
346             
347       if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
348          if (el == 0x0000) {          // the first elem is 0x0000
349             groupHt[key] = 0;         // initialize group length 
350          } else {
351             groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
352          } 
353       } else {   // any elem but the first    
354          if (type == ExplicitVR) {
355             if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
356                groupHt[key] +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
357             }
358          }
359          groupHt[key] += 2 + 2 + 4 + elem->GetLength(); 
360       } 
361    }
362
363    unsigned short int gr_bid;
364   
365    for (GroupHT::iterator g = groupHt.begin(); // for each group we found
366         g != groupHt.end();
367         ++g){ 
368       // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
369       //        warning: unsigned int format, different type arg
370       sscanf(g->first.c_str(),"%x",&gr_bid);
371       tk = g->first + "|0000";                  // generate the element full tag
372                      
373       if ( tagHt.count(tk) == 0) {              // if element 0x0000 not found
374          gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");       
375          elemZ = new gdcmElValue(tagZ);
376          elemZ->SetLength(4);
377          Add(elemZ);                            // create it
378       } else {
379          elemZ=GetElementByNumber(gr_bid, 0x0000);
380       }     
381       sprintf(trash ,"%d",g->second);
382       str_trash=trash;
383       elemZ->SetValue(str_trash);
384    }   
385 }
386
387 /**
388  * \ingroup gdcmElValSet
389  * \brief   
390  * @param   type
391  * @param   _fp 
392  * @return  
393  */
394 void gdcmElValSet::WriteElements(FileType type, FILE * _fp) {
395    guint16 gr, el;
396    guint32 lgr;
397    const char * val;
398    std::string vr;
399    guint32 val_uint32;
400    guint16 val_uint16;
401    
402    std::vector<std::string> tokens;
403
404    void *ptr;
405
406    // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian 
407    // restent a tester les echecs en ecriture (apres chaque fwrite)
408
409    for (TagElValueHT::iterator tag2=tagHt.begin();
410         tag2 != tagHt.end();
411         ++tag2){
412
413       gr =  tag2->second->GetGroup();
414       el =  tag2->second->GetElement();
415       lgr = tag2->second->GetLength();
416       val = tag2->second->GetValue().c_str();
417       vr =  tag2->second->GetVR();
418       
419      // std::cout << "Tag "<< std::hex << gr << " " << el << std::endl;
420
421       if ( type == ACR ) { 
422          if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
423          if (gr %2)         continue; // ignore shadow groups
424          if (vr == "SQ" )   continue; // ignore Sequences
425          if (gr == 0xfffe ) continue; // ignore delimiters
426       } 
427
428       fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
429       fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp);  //element
430
431       if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
432          // EXPLICIT VR
433          guint16 z=0, shortLgr;
434          fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
435
436          if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
437             fwrite ( &z,  (size_t)2 ,(size_t)1 ,_fp);
438             fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
439
440          } else {
441             shortLgr=lgr;
442             fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
443          }
444       } else { // IMPLICIT VR
445          fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
446       }
447
448       if (vr == "US" || vr == "SS") {
449          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
450          Tokenize (tag2->second->GetValue(), tokens, "\\");
451          for (unsigned int i=0; i<tokens.size();i++) {
452             val_uint16 = atoi(tokens[i].c_str());
453             ptr = &val_uint16;
454             fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
455          }
456          tokens.clear();
457          continue;
458       }
459       if (vr == "UL" || vr == "SL") {
460          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
461          Tokenize (tag2->second->GetValue(), tokens, "\\");
462          for (unsigned int i=0; i<tokens.size();i++) {
463             val_uint32 = atoi(tokens[i].c_str());
464             ptr = &val_uint32;
465             fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
466          }
467          tokens.clear();
468          continue;
469       }     
470       // Pixels are never loaded in the element !
471       if ((gr == 0x7fe0) && (el == 0x0010) ) break;
472
473       fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
474    }
475 }
476
477 /**
478  * \ingroup gdcmElValSet
479  * \brief   
480  * @param   _fp
481  * @param   type
482  * @return  
483  */
484 int gdcmElValSet::Write(FILE * _fp, FileType type) {
485
486         // Question :
487         // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
488         // (FileType est un champ de gdcmHeader ...)
489         // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
490         // no way 
491         // a moins de se livrer a un tres complique ajout des champs manquants.
492         // faire un CheckAndCorrectHeader (?)
493
494    if ( (type == ImplicitVR) || (type == ExplicitVR) )
495       UpdateGroupLength(false,type);
496    if ( type == ACR)
497       UpdateGroupLength(true,ACR);
498
499    WriteElements(type, _fp);
500    return(1);
501 }