From 565a47ec4e037897aaf770aeccc5d67d7ad7b478 Mon Sep 17 00:00:00 2001 From: frog Date: Wed, 5 Mar 2003 14:55:49 +0000 Subject: [PATCH] * src/gdcm.h splitted in gdcmCommon.h, gdcmDict.h, gdcmDictEntry.h, gdcmDictSet.h, gdcmElValSet.h, gdcmElValue.h, gdcmFile.h, gdcmHeader.h * src/gdcm*.cxx only include their corresponding include file (as opposed to gdcm.h) * gdcmPython/gdcm.i changed accordingly * Test/Makefile.am corrected and added NEWS, AUTHORS in order for the autogen.sh generated "make snapshot" command to work. * autogen.sh removed history related references to crea package. --- .cvsignore | 1 + AUTHORS | 0 ChangeLog | 11 + NEWS | 0 autogen.sh | 2 +- gdcmPython/gdcm.i | 19 +- src/gdcm.h | 529 +++--------------------------------------- src/gdcmCommon.h | 27 +++ src/gdcmDict.cxx | 6 +- src/gdcmDict.h | 36 +++ src/gdcmDictEntry.cxx | 6 +- src/gdcmDictEntry.h | 53 +++++ src/gdcmDictSet.cxx | 2 +- src/gdcmDictSet.h | 48 ++++ src/gdcmElValSet.cxx | 2 +- src/gdcmElValSet.h | 39 ++++ src/gdcmElValue.cxx | 2 +- src/gdcmElValue.h | 53 +++++ src/gdcmFile.cxx | 2 +- src/gdcmFile.h | 100 ++++++++ src/gdcmHeader.cxx | 3 +- src/gdcmHeader.h | 169 ++++++++++++++ 22 files changed, 598 insertions(+), 512 deletions(-) create mode 100644 AUTHORS create mode 100644 NEWS create mode 100644 src/gdcmCommon.h create mode 100644 src/gdcmDict.h create mode 100644 src/gdcmDictEntry.h create mode 100644 src/gdcmDictSet.h create mode 100644 src/gdcmElValSet.h create mode 100644 src/gdcmElValue.h create mode 100644 src/gdcmFile.h create mode 100644 src/gdcmHeader.h diff --git a/.cvsignore b/.cvsignore index ed19bc64..0326ae33 100644 --- a/.cvsignore +++ b/.cvsignore @@ -13,3 +13,4 @@ aclocal.m4 build dist MANIFEST +DCMlib*.tar.gz diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/ChangeLog b/ChangeLog index e1d9c8bd..302a1eca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2003-03-05 Eric Boix + * src/gdcm.h splitted in gdcmCommon.h, gdcmDict.h, gdcmDictEntry.h, + gdcmDictSet.h, gdcmElValSet.h, gdcmElValue.h, gdcmFile.h, + gdcmHeader.h + * src/gdcm*.cxx only include their corresponding include file + (as opposed to gdcm.h) + * gdcmPython/gdcm.i changed accordingly + * Test/Makefile.am corrected and added NEWS, AUTHORS in order for + the autogen.sh generated "make snapshot" command to work. + * autogen.sh removed history related references to crea package. + 2003-03-04 Eric Boix * distutilsSwigCPlusPlus.py work around some swig portability bug. diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/autogen.sh b/autogen.sh index 3671df0b..8317d9ac 100755 --- a/autogen.sh +++ b/autogen.sh @@ -4,7 +4,7 @@ srcdir=`dirname $0` test -z "$srcdir" && srcdir=. -PKG_NAME="crea" +PKG_NAME="gdcm" (test -f $srcdir/configure.in \ && test -d $srcdir/src) || { diff --git a/gdcmPython/gdcm.i b/gdcmPython/gdcm.i index 6e00374c..f89537bb 100644 --- a/gdcmPython/gdcm.i +++ b/gdcmPython/gdcm.i @@ -1,6 +1,13 @@ %module gdcm %{ -#include "gdcm.h" +#include "gdcmCommon.h" +#include "gdcmDictEntry.h" +#include "gdcmDict.h" +#include "gdcmDictSet.h" +#include "gdcmElValue.h" +#include "gdcmElValSet.h" +#include "gdcmHeader.h" +#include "gdcmFile.h" // Utility functions on strings for removing leading and trailing spaces void EatLeadingAndTrailingSpaces(string & s) { @@ -77,4 +84,12 @@ typedef unsigned int guint32; $result = NewDict; } -%include gdcm.h +%include "gdcmCommon.h" +%include "gdcmDictEntry.h" +%include "gdcmDict.h" +%include "gdcmDictSet.h" +%include "gdcmElValue.h" +%include "gdcmElValSet.h" +%include "gdcmHeader.h" +%include "gdcmFile.h" + diff --git a/src/gdcm.h b/src/gdcm.h index 735bd98c..c3d5bda2 100644 --- a/src/gdcm.h +++ b/src/gdcm.h @@ -20,7 +20,6 @@ #ifndef GDCM_H #define GDCM_H -#include using namespace std; #include @@ -31,46 +30,29 @@ using namespace std; #include "gdcmException.h" - // The requirement for the hash table (or map) that - // we shall use: - // 1/ First, next, last (iterators) - // 2/ should be sortable (i.e. sorted by TagKey). This - // condition shall be droped since the Win32/VC++ - // implementation doesn't look a sorted one. Pffff.... - // 3/ Make sure we can setup some default size value, - // which should be around 4500 entries which is the - // average dictionary size (said JPR) - // - // En fait, je disais que dans LE Directory Dicom (dans son etat 2001) - // il y a +/- 1600 entrees. - // Une valeur raisonable pour un majorant du nombre d'entrees - // dans une entete DICOM d'une image semble semble etre 300 - // Si on 'decortique' les elements SQ (ce qui ne semble pas etre fait pour le moment) - // on risque en fait de depasser ... un nombre non previsible dans le cas d'une entree SQ - // contenant lui même un tres grand nombre d'entrees ?!?) - // Quant au nombre d'entrees dans un DICOMDIR, c'est encore pire : il n'est limité - // que par la taille d'un CD-ROM (les DVD-ROM ne sont pas encore pris en compte) - // On peut s'attendre a 30 entrees par fichier dicom présent sur le CD-ROM - // REMARQUE : il faudra se pencher sur le pb de la creation du DICOMDIR lorsqu'on voudra - // exporter des images lisibles par les consoles cliniques - // et pas seulement importables dans e-film. - -#ifdef __GNUC__ -#include -#define guint16 uint16_t -#define guint32 uint32_t -#endif - -#ifdef _MSC_VER -typedef unsigned short guint16; -typedef unsigned int guint32; -#endif - -#ifdef _MSC_VER -#define GDCM_EXPORT __declspec( dllexport ) -#else -#define GDCM_EXPORT -#endif +// The requirement for the hash table (or map) that we shall use: +// 1/ First, next, last (iterators) +// 2/ should be sortable (i.e. sorted by TagKey). This +// condition shall be droped since the Win32/VC++ +// implementation doesn't look a sorted one. Pffff.... +// 3/ Make sure we can setup some default size value, +// which should be around 4500 entries which is the +// average dictionary size (said JPR) +// +// JPR CLEAN +// En fait, je disais que dans LE Directory Dicom (dans son etat 2001) +// il y a +/- 1600 entrees. +// Une valeur raisonable pour un majorant du nombre d'entrees +// dans une entete DICOM d'une image semble semble etre 300 +// Si on 'decortique' les elements SQ (ce qui ne semble pas etre fait pour le moment) +// on risque en fait de depasser ... un nombre non previsible dans le cas d'une entree SQ +// contenant lui même un tres grand nombre d'entrees ?!?) +// Quant au nombre d'entrees dans un DICOMDIR, c'est encore pire : il n'est limité +// que par la taille d'un CD-ROM (les DVD-ROM ne sont pas encore pris en compte) +// On peut s'attendre a 30 entrees par fichier dicom présent sur le CD-ROM +// REMARQUE : il faudra se pencher sur le pb de la creation du DICOMDIR lorsqu'on voudra +// exporter des images lisibles par les consoles cliniques +// et pas seulement importables dans e-film. //////////////////////////////////////////////////////////////////////////// // Tag based hash tables. @@ -82,465 +64,16 @@ typedef unsigned int guint32; // the | (pipe symbol) acts as a separator). Refer to // gdcmDictEntry::TranslateToKey for this conversion function. -typedef string TagKey; -typedef string TagName; - -class GDCM_EXPORT gdcmDictEntry { -private: - guint16 group; // e.g. 0x0010 - guint16 element; // e.g. 0x0103 - string vr; // Value Representation i.e. some clue about the nature - // of the data represented e.g. "FD" short for - // "Floating Point Double" - // CLEANME: find the official dicom name for this field ! - string fourth; // Fourth field containing some semantics. - string name; // e.g. "Patient_Name" - TagKey key; // Redundant with (group, element) but we add it - // on efficiency purposes. - // DCMTK has many fields for handling a DictEntry (see below). What are the - // relevant ones for gdcmlib ? - // struct DBI_SimpleEntry { - // Uint16 upperGroup; - // Uint16 upperElement; - // DcmEVR evr; - // const char* tagName; - // int vmMin; - // int vmMax; - // const char* standardVersion; - // DcmDictRangeRestriction groupRestriction; - // DcmDictRangeRestriction elementRestriction; - // }; -public: - gdcmDictEntry(guint16 group, - guint16 element, - string vr = "Unknown", - string fourth = "Unknown", - string name = "Unknown"); - // fabrique une 'clé' par concaténation du numGroupe et du numElement - static TagKey TranslateToKey(guint16 group, guint16 element); - - guint16 GetGroup(void) { return group; }; - guint16 GetElement(void){return element;}; - string GetVR(void) {return vr; }; - void SetVR(string); - void SetKey(string k){ key = k; } - bool IsVrUnknown(void); - string GetFourth(void) {return fourth;}; - string GetName(void) {return name; }; - string GetKey(void) {return key; }; -}; - -//////////////////////////////////////////////////////////////////////////// -// A single DICOM dictionary i.e. a container for a collection of dictionary -// entries. There should be a single public dictionary (THE dictionary of -// the actual DICOM v3) but as many shadow dictionaries as imagers -// combined with all software versions... - -typedef map TagKeyHT; -typedef map TagNameHT; - -class GDCM_EXPORT gdcmDict { - string name; - string filename; - TagKeyHT KeyHt; // Both accesses with a TagKey or with a - TagNameHT NameHt; // TagName are required. -public: - gdcmDict(const char* FileName); // Reads Dict from ascii file - int AddNewEntry (gdcmDictEntry* NewEntry); - int ReplaceEntry(gdcmDictEntry* NewEntry); - int RemoveEntry (TagKey key); - int RemoveEntry (guint16 group, guint16 element); - gdcmDictEntry * GetTagByKey(guint16 group, guint16 element); - gdcmDictEntry * GetTagByName(TagName name); - void Print(ostream&); - void PrintByKey(ostream&); - void PrintByName(ostream&); - TagKeyHT & GetEntries(void) { return KeyHt; } -}; - - -//////////////////////////////////////////////////////////////////////////// -// Container for managing a set of loaded dictionaries. Sharing dictionaries -// should avoid : -// * reloading an allready loaded dictionary, -// * having many in memory representations of the same dictionary. - -typedef string DictKey; -typedef map DictSetHT; - -class GDCM_EXPORT gdcmDictSet { -private: - DictSetHT dicts; - int AppendDict(gdcmDict* NewDict); - void LoadDictFromFile(string filename, DictKey); -private: - static string DictPath; // Directory path to dictionaries - static string BuildDictPath(void); - static gdcmDict* DefaultPubDict; -public: - static list * GetPubDictTagNames(void); - static map >* GetPubDictTagNamesByCategory(void); - static gdcmDict* LoadDefaultPubDict(void); - - gdcmDictSet(void); - // TODO Swig int LoadDictFromFile(string filename); - // QUESTION: the following function might not be thread safe !? Maybe - // we need some mutex here, to avoid concurent creation of - // the same dictionary !?!?! - // TODO Swig int LoadDictFromName(string filename); - // TODO Swig int LoadAllDictFromDirectory(string DirectoryName); - // TODO Swig string* GetAllDictNames(); - // - void Print(ostream&); - gdcmDict* GetDict(DictKey DictName); - gdcmDict* GetDefaultPubDict(void); -}; - -/////////////////////////////////////////////////////////////////////////// -// The dicom header of a Dicom file contains a set of such ELement VALUES -// (when successfuly parsed against a given Dicom dictionary) -class GDCM_EXPORT ElValue { -private: - gdcmDictEntry *entry; - guint32 LgrElem; - bool ImplicitVr; // Even when reading explicit vr files, some - // elements happen to be implicit. Flag them here - // since we can't use the entry->vr without breaking - // the underlying dictionary. -public: - string value; - size_t Offset; // Offset from the begining of file for direct user access - - ElValue(gdcmDictEntry*); - void SetDictEntry(gdcmDictEntry *NewEntry) { entry = NewEntry; }; - bool IsVrUnknown(void) { return entry->IsVrUnknown(); }; - void SetImplicitVr(void) { ImplicitVr = true; }; - bool IsImplicitVr(void) { return ImplicitVr; }; - - guint16 GetGroup(void) { return entry->GetGroup(); }; - guint16 GetElement(void) { return entry->GetElement();}; - string GetKey(void) { return entry->GetKey(); }; - string GetName(void) { return entry->GetName(); }; - string GetVR(void) { return entry->GetVR(); }; - void SetVR(string v) { entry->SetVR(v); }; - void SetLength(guint32 l){ LgrElem = l; }; - guint32 GetLength(void) { return LgrElem; }; - - // Question : SetLength est public - // (sinon, on ne pourrait pas l'appeler dans ElValSet) - // alors que *personne* ne devrait s'en servir ! - // c'est *forcément* la lgr de la string 'value', non? - - void SetValue(string val){ value = val; }; - string GetValue(void) { return value;}; - - void SetOffset(size_t of){ Offset = of; }; - size_t GetOffset(void) { return Offset;}; - // Question : SetOffset est public ... - // Quel utilisateur serait ammené à modifier l'Offset ? -}; - - -//////////////////////////////////////////////////////////////////////////// -// Container for a set of successfully parsed ElValues. -typedef map TagElValueHT; -typedef map TagElValueNameHT; - -class GDCM_EXPORT ElValSet { - TagElValueHT tagHt; // Both accesses with a TagKey or with a - TagElValueNameHT NameHt; // the DictEntry.Name are required. -public: - void Add(ElValue*); - void Print(ostream &); - void PrintByName(ostream &); - int Write(FILE *fp); - ElValue* GetElementByNumber(guint32 group, guint32 element); - ElValue* GetElementByName (string); - string GetElValueByNumber(guint32 group, guint32 element); - string GetElValueByName (string); - - TagElValueHT & GetTagHt(void); - - int SetElValueByNumber(string content, guint32 group, guint32 element); - int SetElValueByName (string content, string TagName); - - int SetElValueLengthByNumber(guint32 l, guint32 group, guint32 element); - int SetElValueLengthByName (guint32 l, string TagName); - -}; - -//////////////////////////////////////////////////////////////////////////// -// The purpous of an instance of gdcmHeader is to act as a container of -// all the elements and their corresponding values (and additionaly the -// corresponding DICOM dictionary entry) of the header of a DICOM file. -// -// The typical usage of instances of class gdcmHeader is to classify a set of -// dicom files according to header information e.g. to create a file hierarchy -// reflecting the Patient/Study/Serie informations, or extracting a given -// SerieId. Accesing the content (image[s] or volume[s]) is beyond the -// functionality of this class and belong to gdmcFile (see below). -// Notes: -// * the various entries of the explicit value representation (VR) shall -// be managed within a dictionary which is shared by all gdcmHeader instances -// * the gdcmHeader::Set*Tag* family members cannot be defined as protected -// (Swig limitations for as Has_a dependency between gdcmFile and gdcmHeader) - -typedef string VRKey; -typedef string VRAtr; -typedef map VRHT; // Value Representation Hash Table - -class GDCM_EXPORT gdcmHeader { - void SkipBytes(guint32); -private: - static VRHT *dicom_vr; - // Dictionaries of data elements: - - gdcmDictSet* Dicts; // global dictionary container - gdcmDict* RefPubDict; // public dictionary - gdcmDict* RefShaDict; // shadow dictionary (optional) - // Parsed element values: - ElValSet PubElVals; // parsed with Public Dictionary - ElValSet ShaElVals; // parsed with Shadow Dictionary - string filename; // refering underlying file - FILE * fp; - guint16 grPixel; - guint16 numPixel; - // Ne faudrait-il pas une indication sur la presence ou non - // du 'groupe des pixels' dans l'entete? - // (voir pb du DICOMDIR) - - // Swap code (little, big, bad-big, bad-little endian): this code is not fixed - // during parsing.FIXME sw should be an enum e.g. - //enum EndianType { - //LittleEndian, - //BadLittleEndian, - //BigEndian, - //BadBigEndian}; - int sw; - - // Only the elements whose size is below this bound will be loaded. - // By default, this upper bound is limited to 1024 (which looks reasonable - // when one considers the definition of the various VR contents). - guint32 MaxSizeLoadElementValue; - - guint16 ReadInt16(void); - guint32 ReadInt32(void); - guint16 SwapShort(guint16); - guint32 SwapLong(guint32); - guint32 FindLengthOB(void); - void Initialise(void); - void CheckSwap(void); - void InitVRDict(void); - void SwitchSwapToBigEndian(void); - void AddAndDefaultElements(void); - void SetMaxSizeLoadElementValue(long); - - gdcmDictEntry * GetDictEntryByKey(guint16, guint16); - gdcmDictEntry * GetDictEntryByName(string name); - - // ElValue related utilities - ElValue * ReadNextElement(void); - ElValue * NewElValueByKey(guint16 group, guint16 element); - ElValue * NewElValueByName(string name); - void FindLength(ElValue *); - void FindVR(ElValue *); - void LoadElementValue(ElValue *); - void LoadElementValueSafe(ElValue *); - void SkipElementValue(ElValue *); - void FixFoundLength(ElValue*, guint32); - bool IsAnInteger(ElValue *); - - bool IsImplicitVRLittleEndianTransferSyntax(void); - bool IsExplicitVRLittleEndianTransferSyntax(void); - bool IsDeflatedExplicitVRLittleEndianTransferSyntax(void); - bool IsExplicitVRBigEndianTransferSyntax(void); - bool IsJPEGBaseLineProcess1TransferSyntax(void); - bool IsJPEGExtendedProcess2_4TransferSyntax(void); - bool IsJPEGExtendedProcess3_5TransferSyntax(void); - bool IsJPEGSpectralSelectionProcess6_8TransferSyntax(void); - -protected: - enum FileType { - Unknown = 0, - TrueDicom, - ExplicitVR, - ImplicitVR, - ACR, - ACR_LIBIDO}; // CLEANME - FileType filetype; - int write(ostream&); - int anonymize(ostream&); // FIXME : anonymize should be a friend ? -public: - void LoadElements(void); - virtual void ParseHeader(bool exception_on_error = false) - throw(gdcmFormatError); - gdcmHeader(const char *filename, bool exception_on_error = false) - throw(gdcmFileError); - virtual ~gdcmHeader(); - - size_t GetPixelOffset(void); - void GetPixels(size_t, void *); - int GetSwapCode(void) { return sw; } - - // TODO Swig int SetPubDict(string filename); - // When some proprietary shadow groups are disclosed, we can set up - // an additional specific dictionary to access extra information. - // TODO Swig int SetShaDict(string filename); - - // Get the element values themselves: - string GetPubElValByName(string TagName); - string GetPubElValByNumber(guint16 group, guint16 element); - - // Getting the element value representation (VR) might be needed by caller - // to convert the string typed content to caller's native type - // (think of C/C++ vs Python). - - string GetPubElValRepByName(string TagName); - string GetPubElValRepByNumber(guint16 group, guint16 element); - - TagElValueHT & GetPubElVal(void) { return PubElVals.GetTagHt(); }; - void PrintPubElVal(ostream & os = cout); - void PrintPubDict(ostream & os = cout); - - // Same thing with the shadow : - // TODO Swig string* GetShaTagNames(); - string GetShaElValByName(string TagName); - string GetShaElValByNumber(guint16 group, guint16 element); - string GetShaElValRepByName(string TagName); - string GetShaElValRepByNumber(guint16 group, guint16 element); - - // Wrappers of the above (public is privileged over shadow) to avoid - // bugging the caller with knowing if ElVal is from the public or shadow - // dictionary. - string GetElValByName(string TagName); - string GetElValByNumber(guint16 group, guint16 element); - string GetElValRepByName(string TagName); - string GetElValRepByNumber(guint16 group, guint16 element); - - int SetPubElValByName(string content, string TagName); - int SetPubElValByNumber(string content, guint16 group, guint16 element); - int SetShaElValByName(string content, string ShadowTagName); - int SetShaElValByNumber(string content, guint16 group, guint16 element); - - ElValSet GetPubElVals() { return(PubElVals); } -}; - -// -// ---------------------------------------------------- gdcmFile -// -// un fichier EST_UNE entete, ou A_UNE entete ? -// -// On dit 'EST_UNE' ... - - -//////////////////////////////////////////////////////////////////////////// -// In addition to Dicom header exploration, this class is designed -// for accessing the image/volume content. One can also use it to -// write Dicom files. -////// QUESTION: this looks still like an open question whether the -////// relationship between a gdcmFile and gdcmHeader is of -////// type IS_A or HAS_A ! - -class GDCM_EXPORT gdcmFile: public gdcmHeader -{ -private: - // QUESTION : - // Data pointe sur quoi? - // sur les Pixels lus? - // --> j'ajoute un champ public : Pixels - // (il faudra que l'utilisateur puisse modifier les pixels ?) - - void* Data; - int Parsed; // weather allready parsed - string OrigFileName; // To avoid file overwrite -public: - // je ne suis pas sur d'avoir compris *où* il serait légitime de ranger ca. - // on pourra tjs le deplacer, et mettre des accesseurs - void * Pixels; - size_t lgrTotale; - - // Constructor dedicated to writing a new DICOMV3 part10 compliant - // file (see SetFileName, SetDcmTag and Write) - // TODO Swig gdcmFile(); - // Opens (in read only and when possible) an existing file and checks - // for DICOM compliance. Returns NULL on failure. - // Note: the in-memory representation of all available tags found in - // the DICOM header is post-poned to first header information access. - // This avoid a double parsing of public part of the header when - // one sets an a posteriori shadow dictionary (efficiency can be - // seen as a side effect). - - gdcmFile(string & filename); - - // For promotion (performs a deepcopy of pointed header object) - // TODO Swig gdcmFile(gdcmHeader* header); - // TODO Swig ~gdcmFile(); - - // On writing purposes. When instance was created through - // gdcmFile(string filename) then the filename argument MUST be different - // from the constructor's one (no overwriting allowed). - // TODO Swig int SetFileName(string filename); - - // Allocates necessary memory, copies the data (image[s]/volume[s]) to - // newly allocated zone and return a pointer to it: - - void * GetImageData(); - - // Returns size (in bytes) of required memory to contain data - // represented in this file. - - size_t GetImageDataSize(); - - // Copies (at most MaxSize bytes) of data to caller's memory space. - // Returns an error code on failure (if MaxSize is not big enough) - - int GetImageDataIntoVector(void* destination, size_t MaxSize ); - - // Question : - // - // GetImageData et GetImageDataIntoVector - // Get et Put pour 2 fonctions qui font presque la meme chose :-( - // - - // Allocates ExpectedSize bytes of memory at this->Data and copies the - // pointed data to it. - - // Question : - // Pourquoi dupliquer les pixels, alors qu'on les a deja en mémoire, - // et que Data (dans le gdcmHeader) est un pointeur ? - - // TODO Swig int SetImageData(void * Data, size_t ExpectedSize); - - // Push to disk. - // A NE PAS OUBLIER : que fait-on en cas de Transfert Syntax (dans l'entete) - // incohérente avec l'ordre des octets en mémoire - // TODO Swig int Write(); - - // Ecrit sur disque les pixels d'UNE image - // Aucun test n'est fait sur l'"Endiannerie" du processeur. - // Ca sera à l'utilisateur d'appeler son Reader correctement - - int WriteRawData (string nomFichier); - int WriteDcm (string nomFichier); -}; - -// -// ---------------------------------------------------- gdcmSerie -// -// une serie EST_UN fichier ???? -// -// +#include "gdcmCommon.h" +#include "gdcmDictEntry.h" +#include "gdcmDict.h" +#include "gdcmDictSet.h" +#include "gdcmElValue.h" +#include "gdcmElValSet.h" +#include "gdcmHeader.h" +#include "gdcmFile.h" //class gdcmSerie : gdcmFile; - -// -// ---------------------------------------------------- gdcmMultiFrame -// -// un fichierMultiFrame EST_UN fichier -// -// - //class gdcmMultiFrame : gdcmFile; - #endif // #ifndef GDCM_H diff --git a/src/gdcmCommon.h b/src/gdcmCommon.h new file mode 100644 index 00000000..ba376a42 --- /dev/null +++ b/src/gdcmCommon.h @@ -0,0 +1,27 @@ +//gdcmCommon.h + +#ifndef GDCMCOMMON_H +#define GDCMCOMMON_H + +#ifdef __GNUC__ +#include +#define guint16 uint16_t +#define guint32 uint32_t +#endif + +#ifdef _MSC_VER +typedef unsigned short guint16; +typedef unsigned int guint32; +#endif + +#ifdef _MSC_VER +#define GDCM_EXPORT __declspec( dllexport ) +#else +#define GDCM_EXPORT +#endif + +#include +typedef string TagKey; +typedef string TagName; + +#endif diff --git a/src/gdcmDict.cxx b/src/gdcmDict.cxx index 2ad026e8..645dcf85 100644 --- a/src/gdcmDict.cxx +++ b/src/gdcmDict.cxx @@ -1,7 +1,7 @@ // gdcmDict.cxx #include -#include "gdcm.h" +#include "gdcmDict.h" #include "gdcmUtil.h" gdcmDict::gdcmDict(const char* FileName) { @@ -131,7 +131,7 @@ int gdcmDict::AddNewEntry(gdcmDictEntry* NewEntry) { key = NewEntry->GetKey(); if(KeyHt.count(key) >= 1) { - printf("gdcmDict::AddNewEntry %s deja present\n", key.c_str()); + dbg.Verbose(1, "gdcmDict::AddNewEntry allready present", key.c_str()); return(0); } else { KeyHt[NewEntry->GetKey()] = NewEntry; @@ -145,7 +145,7 @@ int gdcmDict::RemoveEntry(TagKey key) { KeyHt.erase(key); return (1); } else { - printf("gdcmDict::RemoveEntry %s non trouve\n", key.c_str()); + dbg.Verbose(1, "gdcmDict::RemoveEntry unfound entry", key.c_str()); return (0); } } diff --git a/src/gdcmDict.h b/src/gdcmDict.h new file mode 100644 index 00000000..7fd1b186 --- /dev/null +++ b/src/gdcmDict.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// A single DICOM dictionary i.e. a container for a collection of dictionary +// entries. There should be a single public dictionary (THE dictionary of +// the actual DICOM v3) but as many shadow dictionaries as imagers +// combined with all software versions... + +#ifndef GDCMDICT_H +#define GDCMDICT_H + +#include +#include "gdcmCommon.h" +#include "gdcmDictEntry.h" + +typedef map TagKeyHT; +typedef map TagNameHT; + +class GDCM_EXPORT gdcmDict { + string name; + string filename; + TagKeyHT KeyHt; // Both accesses with a TagKey or with a + TagNameHT NameHt; // TagName are required. +public: + gdcmDict(const char* FileName); // Reads Dict from ascii file + int AddNewEntry (gdcmDictEntry* NewEntry); + int ReplaceEntry(gdcmDictEntry* NewEntry); + int RemoveEntry (TagKey key); + int RemoveEntry (guint16 group, guint16 element); + gdcmDictEntry * GetTagByKey(guint16 group, guint16 element); + gdcmDictEntry * GetTagByName(TagName name); + void Print(ostream&); + void PrintByKey(ostream&); + void PrintByName(ostream&); + TagKeyHT & GetEntries(void) { return KeyHt; } +}; + +#endif diff --git a/src/gdcmDictEntry.cxx b/src/gdcmDictEntry.cxx index 3e9b109e..8ac0d08d 100644 --- a/src/gdcmDictEntry.cxx +++ b/src/gdcmDictEntry.cxx @@ -1,8 +1,10 @@ -// gdcmDict.cxx +// gdcmDictEntry.cxx -#include "gdcm.h" +#include // FIXME For sprintf +#include "gdcmDictEntry.h" #include "gdcmUtil.h" + gdcmDictEntry::gdcmDictEntry(guint16 InGroup, guint16 InElement, string InVr, string InFourth, string InName) { diff --git a/src/gdcmDictEntry.h b/src/gdcmDictEntry.h new file mode 100644 index 00000000..ecbc0290 --- /dev/null +++ b/src/gdcmDictEntry.h @@ -0,0 +1,53 @@ +// gdcmDictEntry.h + +#ifndef GDCMDICTENTRY_H +#define GDCMDICTENTRY_H + +#include "gdcmCommon.h" + +class GDCM_EXPORT gdcmDictEntry { +private: + guint16 group; // e.g. 0x0010 + guint16 element; // e.g. 0x0103 + string vr; // Value Representation i.e. some clue about the nature + // of the data represented e.g. "FD" short for + // "Floating Point Double" + // CLEANME: find the official dicom name for this field ! + string fourth; // Fourth field containing some semantics. + string name; // e.g. "Patient_Name" + TagKey key; // Redundant with (group, element) but we add it + // on efficiency purposes. + // DCMTK has many fields for handling a DictEntry (see below). What are the + // relevant ones for gdcmlib ? + // struct DBI_SimpleEntry { + // Uint16 upperGroup; + // Uint16 upperElement; + // DcmEVR evr; + // const char* tagName; + // int vmMin; + // int vmMax; + // const char* standardVersion; + // DcmDictRangeRestriction groupRestriction; + // DcmDictRangeRestriction elementRestriction; + // }; +public: + gdcmDictEntry(guint16 group, + guint16 element, + string vr = "Unknown", + string fourth = "Unknown", + string name = "Unknown"); + // fabrique une 'clé' par concaténation du numGroupe et du numElement + static TagKey TranslateToKey(guint16 group, guint16 element); + + guint16 GetGroup(void) { return group; }; + guint16 GetElement(void){return element;}; + string GetVR(void) {return vr; }; + void SetVR(string); + void SetKey(string k){ key = k; } + bool IsVrUnknown(void); + string GetFourth(void) {return fourth;}; + string GetName(void) {return name; }; + string GetKey(void) {return key; }; +}; + +#endif diff --git a/src/gdcmDictSet.cxx b/src/gdcmDictSet.cxx index 56482cb3..41a39c3f 100644 --- a/src/gdcmDictSet.cxx +++ b/src/gdcmDictSet.cxx @@ -2,8 +2,8 @@ #include #include // For getenv -#include "gdcm.h" #include "gdcmUtil.h" +#include "gdcmDictSet.h" #define PUB_DICT_NAME "DicomV3Dict" #ifndef PUB_DICT_PATH diff --git a/src/gdcmDictSet.h b/src/gdcmDictSet.h new file mode 100644 index 00000000..0e2aed0b --- /dev/null +++ b/src/gdcmDictSet.h @@ -0,0 +1,48 @@ +// gdcmDictSet.h + +#ifndef GDCMDICTSET_H +#define GDCMDICTSET_H + +#include +#include +#include "gdcmCommon.h" +#include "gdcmDict.h" + +//////////////////////////////////////////////////////////////////////////// +// Container for managing a set of loaded dictionaries. Sharing dictionaries +// should avoid : +// * reloading an allready loaded dictionary, +// * having many in memory representations of the same dictionary. + +typedef string DictKey; +typedef map DictSetHT; + +class GDCM_EXPORT gdcmDictSet { +private: + DictSetHT dicts; + int AppendDict(gdcmDict* NewDict); + void LoadDictFromFile(string filename, DictKey); +private: + static string DictPath; // Directory path to dictionaries + static string BuildDictPath(void); + static gdcmDict* DefaultPubDict; +public: + static list * GetPubDictTagNames(void); + static map >* GetPubDictTagNamesByCategory(void); + static gdcmDict* LoadDefaultPubDict(void); + + gdcmDictSet(void); + // TODO Swig int LoadDictFromFile(string filename); + // QUESTION: the following function might not be thread safe !? Maybe + // we need some mutex here, to avoid concurent creation of + // the same dictionary !?!?! + // TODO Swig int LoadDictFromName(string filename); + // TODO Swig int LoadAllDictFromDirectory(string DirectoryName); + // TODO Swig string* GetAllDictNames(); + // + void Print(ostream&); + gdcmDict* GetDict(DictKey DictName); + gdcmDict* GetDefaultPubDict(void); +}; + +#endif diff --git a/src/gdcmElValSet.cxx b/src/gdcmElValSet.cxx index d716e6af..cd60c764 100644 --- a/src/gdcmElValSet.cxx +++ b/src/gdcmElValSet.cxx @@ -1,7 +1,7 @@ // gdcmElValSet.cxx -#include "gdcm.h" #include "gdcmUtil.h" +#include "gdcmElValSet.h" TagElValueHT & ElValSet::GetTagHt(void) { return tagHt; diff --git a/src/gdcmElValSet.h b/src/gdcmElValSet.h new file mode 100644 index 00000000..e59c9fe3 --- /dev/null +++ b/src/gdcmElValSet.h @@ -0,0 +1,39 @@ +// gdcmElValSet.h + +#ifndef GDCMELVALSET_H +#define GDCMELVALSET_H + +#include // FIXME For FILE on GCC only +#include +#include "gdcmCommon.h" +#include "gdcmElValue.h" + +//////////////////////////////////////////////////////////////////////////// +// Container for a set of successfully parsed ElValues. +typedef map TagElValueHT; +typedef map TagElValueNameHT; + +class GDCM_EXPORT ElValSet { + TagElValueHT tagHt; // Both accesses with a TagKey or with a + TagElValueNameHT NameHt; // the DictEntry.Name are required. +public: + void Add(ElValue*); + void Print(ostream &); + void PrintByName(ostream &); + int Write(FILE *fp); + ElValue* GetElementByNumber(guint32 group, guint32 element); + ElValue* GetElementByName (string); + string GetElValueByNumber(guint32 group, guint32 element); + string GetElValueByName (string); + + TagElValueHT & GetTagHt(void); + + int SetElValueByNumber(string content, guint32 group, guint32 element); + int SetElValueByName (string content, string TagName); + + int SetElValueLengthByNumber(guint32 l, guint32 group, guint32 element); + int SetElValueLengthByName (guint32 l, string TagName); + +}; + +#endif diff --git a/src/gdcmElValue.cxx b/src/gdcmElValue.cxx index a6184af5..ecd680f1 100644 --- a/src/gdcmElValue.cxx +++ b/src/gdcmElValue.cxx @@ -1,6 +1,6 @@ // gdcmElValue.cxx -#include "gdcm.h" +#include "gdcmElValue.h" /** diff --git a/src/gdcmElValue.h b/src/gdcmElValue.h new file mode 100644 index 00000000..b1305bc6 --- /dev/null +++ b/src/gdcmElValue.h @@ -0,0 +1,53 @@ +// gdcmElValue.h + +#ifndef GDCMELVALUE_H +#define GDCMELVALUE_H + +#include "gdcmDictEntry.h" + + +/////////////////////////////////////////////////////////////////////////// +// The dicom header of a Dicom file contains a set of such ELement VALUES +// (when successfuly parsed against a given Dicom dictionary) +class GDCM_EXPORT ElValue { +private: + gdcmDictEntry *entry; + guint32 LgrElem; + bool ImplicitVr; // Even when reading explicit vr files, some + // elements happen to be implicit. Flag them here + // since we can't use the entry->vr without breaking + // the underlying dictionary. +public: + string value; + size_t Offset; // Offset from the begining of file for direct user access + + ElValue(gdcmDictEntry*); + void SetDictEntry(gdcmDictEntry *NewEntry) { entry = NewEntry; }; + bool IsVrUnknown(void) { return entry->IsVrUnknown(); }; + void SetImplicitVr(void) { ImplicitVr = true; }; + bool IsImplicitVr(void) { return ImplicitVr; }; + + guint16 GetGroup(void) { return entry->GetGroup(); }; + guint16 GetElement(void) { return entry->GetElement();}; + string GetKey(void) { return entry->GetKey(); }; + string GetName(void) { return entry->GetName(); }; + string GetVR(void) { return entry->GetVR(); }; + void SetVR(string v) { entry->SetVR(v); }; + void SetLength(guint32 l){ LgrElem = l; }; + guint32 GetLength(void) { return LgrElem; }; + + // Question : SetLength est public + // (sinon, on ne pourrait pas l'appeler dans ElValSet) + // alors que *personne* ne devrait s'en servir ! + // c'est *forcément* la lgr de la string 'value', non? + + void SetValue(string val){ value = val; }; + string GetValue(void) { return value;}; + + void SetOffset(size_t of){ Offset = of; }; + size_t GetOffset(void) { return Offset;}; + // Question : SetOffset est public ... + // Quel utilisateur serait ammené à modifier l'Offset ? +}; + +#endif diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index 77894940..73bc1756 100644 --- a/src/gdcmFile.cxx +++ b/src/gdcmFile.cxx @@ -1,6 +1,6 @@ // gdcmFile.cxx -#include "gdcm.h" +#include "gdcmFile.h" static void _Swap(void* im, int swap, int lgr, int nb); diff --git a/src/gdcmFile.h b/src/gdcmFile.h new file mode 100644 index 00000000..2afd67c7 --- /dev/null +++ b/src/gdcmFile.h @@ -0,0 +1,100 @@ +// gdcmFile.h + +#ifndef GDCMFILE_H +#define GDCMFILE_H + +#include "gdcmCommon.h" +#include "gdcmHeader.h" + +//////////////////////////////////////////////////////////////////////////// +// In addition to Dicom header exploration, this class is designed +// for accessing the image/volume content. One can also use it to +// write Dicom files. +////// QUESTION: this looks still like an open question whether the +////// relationship between a gdcmFile and gdcmHeader is of +////// type IS_A or HAS_A ! + +class GDCM_EXPORT gdcmFile: public gdcmHeader +{ +private: + // QUESTION : + // Data pointe sur quoi? + // sur les Pixels lus? + // --> j'ajoute un champ public : Pixels + // (il faudra que l'utilisateur puisse modifier les pixels ?) + + void* Data; + int Parsed; // weather allready parsed + string OrigFileName; // To avoid file overwrite +public: + // je ne suis pas sur d'avoir compris *où* il serait légitime de ranger ca. + // on pourra tjs le deplacer, et mettre des accesseurs + void * Pixels; + size_t lgrTotale; + + // Constructor dedicated to writing a new DICOMV3 part10 compliant + // file (see SetFileName, SetDcmTag and Write) + // TODO Swig gdcmFile(); + // Opens (in read only and when possible) an existing file and checks + // for DICOM compliance. Returns NULL on failure. + // Note: the in-memory representation of all available tags found in + // the DICOM header is post-poned to first header information access. + // This avoid a double parsing of public part of the header when + // one sets an a posteriori shadow dictionary (efficiency can be + // seen as a side effect). + + gdcmFile(string & filename); + + // For promotion (performs a deepcopy of pointed header object) + // TODO Swig gdcmFile(gdcmHeader* header); + // TODO Swig ~gdcmFile(); + + // On writing purposes. When instance was created through + // gdcmFile(string filename) then the filename argument MUST be different + // from the constructor's one (no overwriting allowed). + // TODO Swig int SetFileName(string filename); + + // Allocates necessary memory, copies the data (image[s]/volume[s]) to + // newly allocated zone and return a pointer to it: + + void * GetImageData(); + + // Returns size (in bytes) of required memory to contain data + // represented in this file. + + size_t GetImageDataSize(); + + // Copies (at most MaxSize bytes) of data to caller's memory space. + // Returns an error code on failure (if MaxSize is not big enough) + + int GetImageDataIntoVector(void* destination, size_t MaxSize ); + + // Question : + // + // GetImageData et GetImageDataIntoVector + // Get et Put pour 2 fonctions qui font presque la meme chose :-( + // + + // Allocates ExpectedSize bytes of memory at this->Data and copies the + // pointed data to it. + + // Question : + // Pourquoi dupliquer les pixels, alors qu'on les a deja en mémoire, + // et que Data (dans le gdcmHeader) est un pointeur ? + + // TODO Swig int SetImageData(void * Data, size_t ExpectedSize); + + // Push to disk. + // A NE PAS OUBLIER : que fait-on en cas de Transfert Syntax (dans l'entete) + // incohérente avec l'ordre des octets en mémoire + // TODO Swig int Write(); + + // Ecrit sur disque les pixels d'UNE image + // Aucun test n'est fait sur l'"Endiannerie" du processeur. + // Ca sera à l'utilisateur d'appeler son Reader correctement + + int WriteRawData (string nomFichier); + int WriteDcm (string nomFichier); +}; + +#endif diff --git a/src/gdcmHeader.cxx b/src/gdcmHeader.cxx index faa95f7b..a01595f8 100644 --- a/src/gdcmHeader.cxx +++ b/src/gdcmHeader.cxx @@ -1,6 +1,5 @@ // gdcmHeader.cxx -#include "gdcm.h" #include #include // For nthos: @@ -10,9 +9,9 @@ #include #endif #include // for isalpha -#include #include #include "gdcmUtil.h" +#include "gdcmHeader.h" #define HEADER_LENGTH_TO_READ 256 // on ne lit plus que le debut #define _MaxSizeLoadElementValue_ 1024 // longueur au dela de laquelle on ne charge plus les valeurs diff --git a/src/gdcmHeader.h b/src/gdcmHeader.h new file mode 100644 index 00000000..0f1c8f32 --- /dev/null +++ b/src/gdcmHeader.h @@ -0,0 +1,169 @@ +// gdcmHeader.h + +#ifndef GDCMHEADER_H +#define GDCMHEADER_H + +#include +#include "gdcmCommon.h" +#include "gdcmException.h" +#include "gdcmDictSet.h" +#include "gdcmElValue.h" +#include "gdcmElValSet.h" + +//////////////////////////////////////////////////////////////////////////// +// The purpous of an instance of gdcmHeader is to act as a container of +// all the elements and their corresponding values (and additionaly the +// corresponding DICOM dictionary entry) of the header of a DICOM file. +// +// The typical usage of instances of class gdcmHeader is to classify a set of +// dicom files according to header information e.g. to create a file hierarchy +// reflecting the Patient/Study/Serie informations, or extracting a given +// SerieId. Accesing the content (image[s] or volume[s]) is beyond the +// functionality of this class and belong to gdmcFile (see below). +// Notes: +// * the various entries of the explicit value representation (VR) shall +// be managed within a dictionary which is shared by all gdcmHeader instances +// * the gdcmHeader::Set*Tag* family members cannot be defined as protected +// (Swig limitations for as Has_a dependency between gdcmFile and gdcmHeader) + +typedef string VRKey; +typedef string VRAtr; +typedef map VRHT; // Value Representation Hash Table + +class GDCM_EXPORT gdcmHeader { + void SkipBytes(guint32); +private: + static VRHT *dicom_vr; + // Dictionaries of data elements: + + gdcmDictSet* Dicts; // global dictionary container + gdcmDict* RefPubDict; // public dictionary + gdcmDict* RefShaDict; // shadow dictionary (optional) + // Parsed element values: + ElValSet PubElVals; // parsed with Public Dictionary + ElValSet ShaElVals; // parsed with Shadow Dictionary + string filename; // refering underlying file + FILE * fp; + guint16 grPixel; + guint16 numPixel; + // Ne faudrait-il pas une indication sur la presence ou non + // du 'groupe des pixels' dans l'entete? + // (voir pb du DICOMDIR) + + // Swap code (little, big, bad-big, bad-little endian): this code is not fixed + // during parsing.FIXME sw should be an enum e.g. + //enum EndianType { + //LittleEndian, + //BadLittleEndian, + //BigEndian, + //BadBigEndian}; + int sw; + + // Only the elements whose size is below this bound will be loaded. + // By default, this upper bound is limited to 1024 (which looks reasonable + // when one considers the definition of the various VR contents). + guint32 MaxSizeLoadElementValue; + + guint16 ReadInt16(void); + guint32 ReadInt32(void); + guint16 SwapShort(guint16); + guint32 SwapLong(guint32); + guint32 FindLengthOB(void); + void Initialise(void); + void CheckSwap(void); + void InitVRDict(void); + void SwitchSwapToBigEndian(void); + void AddAndDefaultElements(void); + void SetMaxSizeLoadElementValue(long); + + gdcmDictEntry * GetDictEntryByKey(guint16, guint16); + gdcmDictEntry * GetDictEntryByName(string name); + + // ElValue related utilities + ElValue * ReadNextElement(void); + ElValue * NewElValueByKey(guint16 group, guint16 element); + ElValue * NewElValueByName(string name); + void FindLength(ElValue *); + void FindVR(ElValue *); + void LoadElementValue(ElValue *); + void LoadElementValueSafe(ElValue *); + void SkipElementValue(ElValue *); + void FixFoundLength(ElValue*, guint32); + bool IsAnInteger(ElValue *); + + bool IsImplicitVRLittleEndianTransferSyntax(void); + bool IsExplicitVRLittleEndianTransferSyntax(void); + bool IsDeflatedExplicitVRLittleEndianTransferSyntax(void); + bool IsExplicitVRBigEndianTransferSyntax(void); + bool IsJPEGBaseLineProcess1TransferSyntax(void); + bool IsJPEGExtendedProcess2_4TransferSyntax(void); + bool IsJPEGExtendedProcess3_5TransferSyntax(void); + bool IsJPEGSpectralSelectionProcess6_8TransferSyntax(void); + +protected: + enum FileType { + Unknown = 0, + TrueDicom, + ExplicitVR, + ImplicitVR, + ACR, + ACR_LIBIDO}; // CLEANME + FileType filetype; + int write(ostream&); + int anonymize(ostream&); // FIXME : anonymize should be a friend ? +public: + void LoadElements(void); + virtual void ParseHeader(bool exception_on_error = false) + throw(gdcmFormatError); + gdcmHeader(const char *filename, bool exception_on_error = false) + throw(gdcmFileError); + virtual ~gdcmHeader(); + + size_t GetPixelOffset(void); + void GetPixels(size_t, void *); + int GetSwapCode(void) { return sw; } + + // TODO Swig int SetPubDict(string filename); + // When some proprietary shadow groups are disclosed, we can set up + // an additional specific dictionary to access extra information. + // TODO Swig int SetShaDict(string filename); + + // Get the element values themselves: + string GetPubElValByName(string TagName); + string GetPubElValByNumber(guint16 group, guint16 element); + + // Getting the element value representation (VR) might be needed by caller + // to convert the string typed content to caller's native type + // (think of C/C++ vs Python). + + string GetPubElValRepByName(string TagName); + string GetPubElValRepByNumber(guint16 group, guint16 element); + + TagElValueHT & GetPubElVal(void) { return PubElVals.GetTagHt(); }; + void PrintPubElVal(ostream & os = cout); + void PrintPubDict(ostream & os = cout); + + // Same thing with the shadow : + // TODO Swig string* GetShaTagNames(); + string GetShaElValByName(string TagName); + string GetShaElValByNumber(guint16 group, guint16 element); + string GetShaElValRepByName(string TagName); + string GetShaElValRepByNumber(guint16 group, guint16 element); + + // Wrappers of the above (public is privileged over shadow) to avoid + // bugging the caller with knowing if ElVal is from the public or shadow + // dictionary. + string GetElValByName(string TagName); + string GetElValByNumber(guint16 group, guint16 element); + string GetElValRepByName(string TagName); + string GetElValRepByNumber(guint16 group, guint16 element); + + int SetPubElValByName(string content, string TagName); + int SetPubElValByNumber(string content, guint16 group, guint16 element); + int SetShaElValByName(string content, string ShadowTagName); + int SetShaElValByNumber(string content, guint16 group, guint16 element); + + ElValSet GetPubElVals() { return(PubElVals); } +}; + +#endif -- 2.45.1