]> Creatis software - gdcm.git/blob - src/gdcm.h
* src/gdcmHeader.cxx added a post header parsing AddAndDefaultElements
[gdcm.git] / src / gdcm.h
1 // gdcm.h
2
3 // gdcmlib Intro:  
4 // * gdcmlib is a library dedicated to reading and writing dicom files.
5 // * LGPL for the license
6 // * lightweigth as opposed to CTN or DCMTK which come bundled which try
7 //   to implement the full DICOM standard (networking...). gdcmlib concentrates
8 //   on reading and writing
9 // * Formats: this lib should be able to read ACR-NEMA v1 and v2, Dicom v3 (as
10 //   stated in part10). [cf dcmtk/dcmdata/docs/datadict.txt]
11 // * Targeted plateforms: Un*xes and Win32/VC++6.0
12 //
13 //
14 // TODO
15 // The declarations commented out and starting with "TODO Swig" needed
16 // to be temporarily removed for swig to proceed correctly (in fact
17 // problems appears at loading of _gdcm.[so/dll]). So, simply uncomment
18 // the declaration once you provided the definition of the method...
19
20 #include <string>
21 #ifdef _MSC_VER
22 using namespace std;  // string type lives in the std namespace on VC++
23 #endif
24
25 #include <iostream>
26 #include <stddef.h>   // For size_t
27 #include <stdio.h>    // FIXME For FILE on GCC only
28 #include <list>
29 #include <map>
30
31                       // The requirement for the hash table (or map) that
32                       // we shall use:
33                       // 1/ First, next, last (iterators)
34                       // 2/ should be sortable (i.e. sorted by TagKey). This
35                       //    condition shall be droped since the Win32/VC++
36                       //    implementation doesn't look a sorted one. Pffff....
37                       // 3/ Make sure we can setup some default size value,
38                       //    which should be around 4500 entries which is the
39                       //    average dictionary size (said JPR)
40                       //
41                       // En fait, je disais que dans LE Directory Dicom (dans son etat 2002)
42                       // il y a 1600 entrees.
43                       // Une valeur raisonable pour un  majorant du nombre d'entrees
44                       // dans une entete DICOM d'une image semble semble etre 300
45                       // Si on 'decortique' les elements SQ (ce qui ne semble pas etre fait pour le moment)
46                       // on risque en fait de depasser ... un nombre non previsible dans le cas d'une entree SQ
47                       // contenant lui même un tres grand nombre d'entrees ?!?)
48                       // Quant au nombre d'entrees dans un DICOMDIR, c'est encore pire : il n'est limité
49                       // que par la taille d'un CD-ROM (les DVD-ROM ne sont pas encore pris en compte)
50                       // On peut s'attendre a 30 entrees par fichier dicom présent sur le CD-ROM
51                       // REMARQUE : il faudra se pencher sur le pb de la creation du DICOMDIR lorsqu'on voudra 
52                       // exporter des images lisibles par les consoles cliniques 
53                       // et pas seulement importables dans e-film. 
54
55 #ifdef __GNUC__
56 #include <stdint.h>
57 #define guint16 uint16_t
58 #define guint32 uint32_t
59 #endif
60
61 #ifdef _MSC_VER 
62 typedef  unsigned short guint16;
63 typedef  unsigned int   guint32;
64 #endif
65
66 #ifdef _MSC_VER
67 #define GDCM_EXPORT __declspec( dllexport )
68 #else
69 #define GDCM_EXPORT
70 #endif
71
72 ////////////////////////////////////////////////////////////////////////////
73 // Tag based hash tables.
74 // We shall use as keys the strings (as the C++ type) obtained by
75 // concatenating the group value and the element value (both of type
76 // unsigned 16 bit integers in Dicom) expressed in hexadecimal.
77 // Example: consider the tag given as (group, element) = (0x0010, 0x0010).
78 // Then the corresponding TagKey shall be the string 0010|0010 (where
79 // the | (pipe symbol) acts as a separator). Refer to 
80 // gdcmDictEntry::TranslateToKey for this conversion function.
81
82 typedef string TagKey;
83 typedef string TagName;
84
85 class GDCM_EXPORT gdcmDictEntry {
86 private:
87         guint16 group;    // e.g. 0x0010
88         guint16 element;  // e.g. 0x0103
89         string  vr;       // Value Representation i.e. some clue about the nature
90                           // of the data represented e.g. "FD" short for
91                           // "Floating Point Double"
92         // CLEANME: find the official dicom name for this field !
93         string  fourth;   // Fourth field containing some semantics.
94         string  name;     // e.g. "Patient_Name"
95         TagKey  key;      // Redundant with (group, element) but we add it
96                           // on efficiency purposes.
97         // DCMTK has many fields for handling a DictEntry (see below). What are the
98         // relevant ones for gdcmlib ?
99         //      struct DBI_SimpleEntry {
100         //         Uint16 upperGroup;
101         //         Uint16 upperElement;
102         //         DcmEVR evr;
103         //         const char* tagName;
104         //         int vmMin;
105         //         int vmMax;
106         //         const char* standardVersion;
107         //         DcmDictRangeRestriction groupRestriction;
108         //         DcmDictRangeRestriction elementRestriction;
109         //       };
110 public:
111         gdcmDictEntry(guint16 group, 
112                       guint16 element,
113                       string vr     = "Unknown",
114                       string fourth = "Unknown",
115                       string name   = "Unknown");
116         // fabrique une 'clé' par concaténation du numGroupe et du numElement
117         static TagKey TranslateToKey(guint16 group, guint16 element);
118         
119         guint16 GetGroup(void)  { return group; };
120         guint16 GetElement(void){return element;};
121         string  GetVR(void)     {return vr;     };
122         void    SetVR(string);
123         void    SetKey(string k){ key = k;     }
124         bool    IsVrUnknown(void);
125         string  GetFourth(void) {return fourth;};
126         string  GetName(void)   {return name;  };
127         string  GetKey(void)    {return key;   };
128 };
129   
130 ////////////////////////////////////////////////////////////////////////////
131 // A single DICOM dictionary i.e. a container for a collection of dictionary
132 // entries. There should be a single public dictionary (THE dictionary of
133 // the actual DICOM v3) but as many shadow dictionaries as imagers 
134 // combined with all software versions...
135
136 typedef map<TagKey,  gdcmDictEntry*> TagKeyHT;
137 typedef map<TagName, gdcmDictEntry*> TagNameHT;
138
139 class GDCM_EXPORT gdcmDict {
140         string name;
141         string filename;
142         TagKeyHT  KeyHt;                // Both accesses with a TagKey or with a
143         TagNameHT NameHt;               // TagName are required.
144 public:
145         gdcmDict(const char* FileName);   // Reads Dict from ascii file
146         int AddNewEntry (gdcmDictEntry* NewEntry);
147         int ReplaceEntry(gdcmDictEntry* NewEntry);
148         int RemoveEntry (TagKey key);
149         int RemoveEntry (guint16 group, guint16 element);
150         gdcmDictEntry * GetTagByKey(guint16 group, guint16 element);
151         gdcmDictEntry * GetTagByName(TagName name);
152         void Print(ostream&);
153         void PrintByKey(ostream&);
154         void PrintByName(ostream&);
155         TagKeyHT & GetEntries(void) { return KeyHt; }
156 };
157
158
159 ////////////////////////////////////////////////////////////////////////////
160 // Container for managing a set of loaded dictionaries. Sharing dictionaries
161 // should avoid :
162 // * reloading an allready loaded dictionary,
163 // * having many in memory representations of the same dictionary.
164
165 typedef string DictKey;
166 typedef map<DictKey, gdcmDict*> DictSetHT;
167
168 class GDCM_EXPORT gdcmDictSet {
169 private:
170         string DictPath;      // Directory path to dictionaries
171         DictSetHT dicts;
172         int AppendDict(gdcmDict* NewDict);
173         int LoadDictFromFile(string filename, DictKey);
174         void SetDictPath(void);
175 public:
176         gdcmDictSet(void);    // loads THE DICOM v3 dictionary
177         // TODO Swig int LoadDictFromFile(string filename);
178    // QUESTION: the following function might not be thread safe !? Maybe
179    //           we need some mutex here, to avoid concurent creation of
180    //           the same dictionary !?!?!
181         // TODO Swig int LoadDictFromName(string filename);
182         // TODO Swig int LoadAllDictFromDirectory(string DirectoryName);
183         // TODO Swig string* GetAllDictNames();
184         //
185         // Question : ne faudra-t-il pas mettre LE dictionnaire DICOM dans un Directory
186         // et les eventuels 'dictionnaires prives' dans un autre?
187         // (pour eviter a un utilisateur mal dégourdi de tout saccager ?)
188         //
189         int LoadDicomV3Dict(void);
190         void Print(ostream&);
191         gdcmDict* GetDict(DictKey DictName);
192         gdcmDict* GetDefaultPublicDict(void);
193 };
194
195 ///////////////////////////////////////////////////////////////////////////
196 // The dicom header of a Dicom file contains a set of such ELement VALUES
197 // (when successfuly parsed against a given Dicom dictionary)
198 class GDCM_EXPORT ElValue {
199 private:
200         gdcmDictEntry *entry;
201         guint32 LgrElem;
202         bool ImplicitVr;       // Even when reading explicit vr files, some
203                                // elements happen to be implicit. Flag them here
204                                // since we can't use the entry->vr without breaking
205                                // the underlying dictionary.
206 public:
207         string  value;
208         size_t Offset;     // Offset from the begining of file for direct user access
209         
210         ElValue(gdcmDictEntry*);
211         void SetDictEntry(gdcmDictEntry *NewEntry) { entry = NewEntry; };
212         bool   IsVrUnknown(void) { return entry->IsVrUnknown(); };
213         void SetImplicitVr(void) { ImplicitVr = true; };
214         bool  IsImplicitVr(void) { return ImplicitVr; };
215         
216         guint16 GetGroup(void)   { return entry->GetGroup();  };
217         guint16 GetElement(void) { return entry->GetElement();};
218         string  GetKey(void)     { return entry->GetKey();    };
219         string  GetName(void)    { return entry->GetName();   };
220         string  GetVR(void)      { return entry->GetVR();     };
221         void    SetVR(string v)  { entry->SetVR(v);           }; 
222         void SetLength(guint32 l){ LgrElem = l;               };
223         guint32 GetLength(void)  { return LgrElem;            };
224         
225         // Question : SetLength est public 
226         // (sinon, on ne pourrait pas l'appeler dans ElValSet)
227         // alors que *personne* ne devrait s'en servir !
228         // c'est *forcément* la lgr de la string 'value', non?
229
230         void SetValue(string val){ value = val; };
231         string  GetValue(void)   { return value;};
232
233         void SetOffset(size_t of){ Offset = of; };
234         size_t  GetOffset(void)  { return Offset;};
235         // Question : SetOffset est public ...
236         // Quel utilisateur serait ammené à modifier l'Offset ?
237 };
238
239
240 ////////////////////////////////////////////////////////////////////////////
241 // Container for a set of successfully parsed ElValues.
242 typedef map<TagKey, ElValue*> TagElValueHT;
243 typedef map<string, ElValue*> TagElValueNameHT;
244
245 class GDCM_EXPORT ElValSet {
246         TagElValueHT tagHt;             // Both accesses with a TagKey or with a
247         TagElValueNameHT NameHt;        // the DictEntry.Name are required.
248 public: 
249         void Add(ElValue*);             
250         void Print(ostream &);
251         void PrintByName(ostream &);
252         int  Write(FILE *fp);
253         ElValue* GetElementByNumber(guint32 group, guint32 element);
254         ElValue* GetElementByName  (string);
255         string   GetElValueByNumber(guint32 group, guint32 element);
256         string   GetElValueByName  (string);
257         
258         TagElValueHT & GetTagHt(void);  
259         
260         int SetElValueByNumber(string content, guint32 group, guint32 element);
261         int SetElValueByName(string content, string TagName);
262 };
263
264 ////////////////////////////////////////////////////////////////////////////
265 // The purpous of an instance of gdcmHeader is to act as a container of
266 // all the elements and their corresponding values (and additionaly the
267 // corresponding DICOM dictionary entry) of the header of a DICOM file.
268 //
269 // The typical usage of instances of class gdcmHeader is to classify a set of
270 // dicom files according to header information e.g. to create a file hierarchy
271 // reflecting the Patient/Study/Serie informations, or extracting a given
272 // SerieId. Accesing the content (image[s] or volume[s]) is beyond the
273 // functionality of this class and belong to gdmcFile (see below).
274 // Notes:
275 // * the various entries of the explicit value representation (VR) shall
276 //   be managed within a dictionary which is shared by all gdcmHeader instances
277 // * the gdcmHeader::Set*Tag* family members cannot be defined as protected
278 //   (Swig limitations for as Has_a dependency between gdcmFile and gdcmHeader)
279
280 typedef string VRKey;
281 typedef string VRAtr;
282 typedef map<VRKey, VRAtr> VRHT;    // Value Representation Hash Table
283
284 class GDCM_EXPORT gdcmHeader {
285         void SkipBytes(guint32);
286 private:
287         static VRHT *dicom_vr;
288         // Dictionaries of data elements:
289
290         static gdcmDictSet* Dicts;  // global dictionary container
291         gdcmDict* RefPubDict;       // public dictionary
292         gdcmDict* RefShaDict;       // shadow dictionary (optional)
293         // Parsed element values:
294         ElValSet PubElVals;         // parsed with Public Dictionary
295         ElValSet ShaElVals;         // parsed with Shadow Dictionary
296         string filename;            // refering underlying file
297         FILE * fp;
298         guint16 grPixel;
299         guint16 numPixel;
300         // Ne faudrait-il pas une indication sur la presence ou non
301         // du 'groupe des pixels' dans l'entete?
302         // (voir pb du DICOMDIR)
303         
304         // Swap code (little, big, bad-big, bad-little endian): this code is not fixed
305         // during parsing.FIXME sw should be an enum e.g.
306         //enum EndianType {
307                 //LittleEndian, 
308                 //BadLittleEndian,
309                 //BigEndian, 
310                 //BadBigEndian};
311         int sw;
312
313         // Only the elements whose size is below this bound will be loaded.
314         // By default, this upper bound is limited to 1024 (which looks reasonable
315         // when one considers the definition of the various VR contents).
316         guint32 MaxSizeLoadElementValue;
317
318         guint16 ReadInt16(void);
319         guint32 ReadInt32(void);
320         guint16 SwapShort(guint16);
321         guint32 SwapLong(guint32);
322         guint32 FindLengthOB(void);
323         void Initialise(void);
324         void CheckSwap(void);
325         void InitVRDict(void);
326         void SwitchSwapToBigEndian(void);
327         void AddAndDefaultElements(void);
328         void SetMaxSizeLoadElementValue(long);
329
330         gdcmDictEntry * GetDictEntryByKey(guint16, guint16);
331         gdcmDictEntry * GetDictEntryByName(string name);
332
333         // ElValue related utilities
334         ElValue * ReadNextElement(void);
335         ElValue * NewElValueByKey(guint16 group, guint16 element);
336         ElValue * NewElValueByName(string name);
337         void FindLength(ElValue *);
338         void FindVR(ElValue *);
339         void LoadElementValue(ElValue *);
340         void LoadElementValueSafe(ElValue *);
341         void SkipElementValue(ElValue *);
342         void FixFoundLength(ElValue*, guint32);
343         bool IsAnInteger(ElValue *);
344         
345         bool IsImplicitVRLittleEndianTransferSyntax(void);
346         bool IsExplicitVRLittleEndianTransferSyntax(void);
347         bool IsDeflatedExplicitVRLittleEndianTransferSyntax(void);
348         bool IsExplicitVRBigEndianTransferSyntax(void);
349         bool IsJPEGBaseLineProcess1TransferSyntax(void);
350         bool IsJPEGExtendedProcess2_4TransferSyntax(void);      
351         bool IsJPEGExtendedProcess3_5TransferSyntax(void);
352         bool IsJPEGSpectralSelectionProcess6_8TransferSyntax(void);     
353                 
354 protected:
355         enum FileType {
356                 Unknown = 0,
357                 TrueDicom,
358                 ExplicitVR,
359                 ImplicitVR,
360                 ACR,
361                 ACR_LIBIDO};  // CLEANME
362         FileType filetype;
363         int write(ostream&);   
364         int anonymize(ostream&);  // FIXME : anonymize should be a friend ?
365 public:
366         void LoadElements(void);
367         virtual void ParseHeader(void);
368         gdcmHeader(const char* filename);
369         virtual ~gdcmHeader();
370         
371         size_t GetPixelOffset(void);
372         void   GetPixels(size_t, void *);
373         int    GetSwapCode(void) { return sw; }
374
375         // TODO Swig int SetPubDict(string filename);
376         // When some proprietary shadow groups are disclosed, we can set up
377         // an additional specific dictionary to access extra information.
378         // TODO Swig int SetShaDict(string filename);
379
380         // Retrieve all potentially available tag [tag = (group, element)] names
381         // from the standard (or public) dictionary. Typical usage : enable the
382         // user of a GUI based interface to select his favorite fields for sorting
383         // or selecting.
384         list<string> * GetPubTagNames(void);
385         map<string, list<string> > * GetPubTagNamesByCategory(void);
386         // Get the element values themselves:
387         
388         string GetPubElValByName(string TagName);
389         string GetPubElValByNumber(guint16 group, guint16 element);
390
391         // Getting the element value representation (VR) might be needed by caller
392         // to convert the string typed content to caller's native type 
393         // (think of C/C++ vs Python).
394
395         string GetPubElValRepByName(string TagName);
396         string GetPubElValRepByNumber(guint16 group, guint16 element);
397
398         TagElValueHT & GetPubElVal(void) { return PubElVals.GetTagHt(); };
399         void   PrintPubElVal(ostream & os = cout);
400         void   PrintPubDict(ostream &);
401           
402         // Same thing with the shadow :
403         // TODO Swig string* GetShaTagNames(); 
404         string GetShaElValByName(string TagName);
405         string GetShaElValByNumber(guint16 group, guint16 element);
406         string GetShaElValRepByName(string TagName);
407         string GetShaElValRepByNumber(guint16 group, guint16 element);
408
409         // Wrappers of the above (public is privileged over shadow) to avoid
410         // bugging the caller with knowing if ElVal is from the public or shadow
411         // dictionary.
412         string GetElValByName(string TagName);
413         string GetElValByNumber(guint16 group, guint16 element);
414         string GetElValRepByName(string TagName);
415         string GetElValRepByNumber(guint16 group, guint16 element);
416
417         int SetPubElValByName(string content, string TagName);
418         int SetPubElValByNumber(string content, guint16 group, guint16 element);
419         int SetShaElValByName(string content, string ShadowTagName);
420         int SetShaElValByNumber(string content, guint16 group, guint16 element);
421
422         ElValSet GetPubElVals() { return(PubElVals); }
423 };
424
425 //
426 // ---------------------------------------------------- gdcmFile
427 //
428 //      un fichier EST_UNE entete, ou A_UNE entete ?
429 // 
430 //      On dit 'EST_UNE' ...
431
432
433 ////////////////////////////////////////////////////////////////////////////
434 // In addition to Dicom header exploration, this class is designed
435 // for accessing the image/volume content. One can also use it to
436 // write Dicom files.
437 ////// QUESTION: this looks still like an open question whether the
438 //////           relationship between a gdcmFile and gdcmHeader is of
439 //////           type IS_A or HAS_A !
440
441 class GDCM_EXPORT gdcmFile: public gdcmHeader
442 {
443 private:
444         // QUESTION :
445         // Data pointe sur quoi?
446         // sur les Pixels lus?
447         // --> j'ajoute un champ public : Pixels
448         // (il faudra que l'utilisateur puisse modifier les pixels ?)
449         
450         void* Data;
451         int Parsed;          // weather allready parsed
452         string OrigFileName; // To avoid file overwrite
453 public:
454         // je ne suis pas sur d'avoir compris *où* il serait légitime de ranger ca.
455         // on pourra tjs le deplacer, et mettre des accesseurs
456         void * Pixels;
457         size_t lgrTotale;
458         
459         // Constructor dedicated to writing a new DICOMV3 part10 compliant
460         // file (see SetFileName, SetDcmTag and Write)
461         // TODO Swig gdcmFile();
462         // Opens (in read only and when possible) an existing file and checks
463         // for DICOM compliance. Returns NULL on failure.
464         // Note: the in-memory representation of all available tags found in
465         //    the DICOM header is post-poned to first header information access.
466         //    This avoid a double parsing of public part of the header when
467         //    one sets an a posteriori shadow dictionary (efficiency can be
468         //    seen as a side effect).
469         
470         gdcmFile(string & filename);
471         
472         // For promotion (performs a deepcopy of pointed header object)
473         // TODO Swig gdcmFile(gdcmHeader* header);
474         // TODO Swig ~gdcmFile();
475
476         // On writing purposes. When instance was created through
477         // gdcmFile(string filename) then the filename argument MUST be different
478         // from the constructor's one (no overwriting allowed).
479         // TODO Swig int SetFileName(string filename);
480
481         // Allocates necessary memory, copies the data (image[s]/volume[s]) to
482         // newly allocated zone and return a pointer to it:
483         
484          void * GetImageData();
485         
486         // Returns size (in bytes) of required memory to contain data
487         // represented in this file.
488         
489         size_t GetImageDataSize();
490         
491         // Copies (at most MaxSize bytes) of data to caller's memory space.
492         // Returns an error code on failure (if MaxSize is not big enough)
493         
494         int PutImageDataHere(void* destination, size_t MaxSize );
495         
496         // Question :
497         //
498         //      GetImageData et PutImageDataHere
499         // Get et Put pour 2 fonctions qui font presque la meme chose :-(
500         //
501         
502         // Allocates ExpectedSize bytes of memory at this->Data and copies the
503         // pointed data to it.
504         
505         // Question :
506         // Pourquoi dupliquer les pixels, alors qu'on les a deja en mémoire,
507         // et que Data (dans le gdcmHeader) est un pointeur ?
508         
509         // TODO Swig int SetImageData(void * Data, size_t ExpectedSize);
510         
511         // Push to disk.
512         // A NE PAS OUBLIER : que fait-on en cas de Transfert Syntax (dans l'entete)
513         // incohérente avec l'ordre des octets en mémoire  
514         // TODO Swig int Write();
515         
516         // Ecrit sur disque les pixels d'UNE image
517         // Aucun test n'est fait sur l'"Endiannerie" du processeur.
518         // Ca sera à l'utilisateur d'appeler son Reader correctement
519                 
520         int WriteRawData (string nomFichier);
521         int WriteDcm (string nomFichier);
522 };
523
524 //
525 // ---------------------------------------------------- gdcmSerie
526 //
527 //      une serie EST_UN fichier ????
528 //
529 //
530
531 //class gdcmSerie : gdcmFile;
532
533 //
534 // ---------------------------------------------------- gdcmMultiFrame
535 //
536 //      un fichierMultiFrame EST_UN fichier 
537 //
538 //
539
540 //class gdcmMultiFrame : gdcmFile;
541
542