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