From 9d439450a43135ddd30761ed598dd2bbfcfe26b8 Mon Sep 17 00:00:00 2001 From: jpr Date: Thu, 19 Dec 2002 17:51:42 +0000 Subject: [PATCH] Ajout de quelques Accesseurs pour la Transfert Syntax. IsExplicitBigEndianTransfertSyntax IsJPEGSpectralSelectionTransfertSyntax, etc... (Je ne suis pas sur que ce soit la bonne maniere dee proceder. Il y en a VINGT-CING en tout, J'ai craque au bout de 8 ... Y a-t-il la possibilite de faire chose generalisable ? --- Testing/TestHash.cxx | 16 +-- src/gdcm.h | 45 ++++++-- src/gdcmFile.cxx | 264 +++++++++++++++++++++++++++++++++++++++++++ src/gdcmHeader.cxx | 162 ++++++++++++++++++++++++-- 4 files changed, 463 insertions(+), 24 deletions(-) diff --git a/Testing/TestHash.cxx b/Testing/TestHash.cxx index f8d58bcc..56cbdfcd 100644 --- a/Testing/TestHash.cxx +++ b/Testing/TestHash.cxx @@ -6,19 +6,19 @@ int main() { typedef map dict; - dict current; - dict::iterator im = current.find("00380010"); - current["00100010"] = "Patient Name"; - current["7fe00010"] = "Pixel Data"; - current["50000010"] = "Number of points"; - current["00380010"] = "Admission ID"; + dict tb1; + dict::iterator im = tb1.find("00380010"); + tb1["00100010"] = "Patient Name"; + tb1["7fe00010"] = "Pixel Data"; + tb1["50000010"] = "Number of points"; + tb1["00380010"] = "Admission ID"; cout << "Traversal of dictionary (note the proper ordering on key)." << endl; - for ( dict::iterator im = current.begin(); im != current.end(); ++im ) + for ( dict::iterator im = tb1.begin(); im != tb1.end(); ++im ) cout << " \"" << im->first << "\" = " << im->second << endl; cout << "End of dictionary." << endl; cout << "Find request on key 00380010" << endl; - im = current.find("00380010"); + im = tb1.find("00380010"); cout << " \"" << im->first << "\" = " << im->second << endl; } diff --git a/src/gdcm.h b/src/gdcm.h index c8bf4d04..4bdef369 100644 --- a/src/gdcm.h +++ b/src/gdcm.h @@ -61,7 +61,7 @@ using namespace std; // string type lives in the std namespace on VC++ #ifdef _MSC_VER typedef unsigned short guint16; -typedef unsigned int guint32; +typedef unsigned int guint32; #endif #ifdef _MSC_VER @@ -227,6 +227,12 @@ public: // + ce qu'on est allé chercher dans le Dictionnaire Dicom) // +// QUESTION: +// +// Ne faudrait-il pas trouver un autre nom, qui preterait moins à confusion? +// ElValue n'EST PAS la 'valeur d'un Element', mais la reunion d'infos +// trouvees dans l'Entete" du fichier ET dans le Dictionnaire +// // The dicom header of a Dicom file contains a set of such ELement VALUES // (when successfuly parsed against a given Dicom dictionary) @@ -272,7 +278,7 @@ public: // //////////////////////////////////////////////////////////////////////////// -// Container for a set of succefully parsed ElValues. +// Container for a set of successfully parsed ElValues. typedef map TagElValueHT; typedef map TagElValueNameHT; @@ -285,9 +291,9 @@ public: void Print(ostream &); void PrintByName(ostream &); ElValue* GetElementByNumber(guint32 group, guint32 element); - ElValue* GetElementByName(string); - string GetElValueByNumber(guint32 group, guint32 element); - string GetElValueByName(string); + ElValue* GetElementByName (string); + string GetElValueByNumber(guint32 group, guint32 element); + string GetElValueByName (string); TagElValueHT & GetTagHt(void); }; @@ -338,6 +344,9 @@ private: // outside of the elements: 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, big-bad endian): this code is not fixed // during parsing.FIXME sw should be an enum e.g. @@ -369,7 +378,23 @@ private: void SwitchSwapToBigEndian(void); void FixFoundLength(ElValue*, guint32); bool IsAnInteger(ElValue *); - bool IsBigEndianTransferSyntax(void); + + 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); +// +// Euhhhhhhh +// Il y en a encore DIX-SEPT, comme ça. +// Il faudrait trouver qq chose + rusé ... +// + + + void SetMaxSizeLoadElementValue(long); ElValue * ReadNextElement(void); gdcmDictEntry * IsInDicts(guint32, guint32); @@ -460,7 +485,7 @@ public: ////// relationship between a gdcmFile and gdcmHeader is of ////// type IS_A or HAS_A ! -class GDCM_EXPORT gdcmFile: gdcmHeader +class GDCM_EXPORT gdcmFile: public gdcmHeader { private: void* Data; @@ -491,11 +516,13 @@ public: // Allocates necessary memory, copies the data (image[s]/volume[s]) to // newly allocated zone and return a pointer to it: - // TODO Swig void * GetImageData(); + + void * GetImageData(); // Returns size (in bytes) of required memory to contain data // represented in this file. - // TODO Swig size_t GetImageDataSize(); + + 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) diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index d5a85294..031b2655 100644 --- a/src/gdcmFile.cxx +++ b/src/gdcmFile.cxx @@ -2,8 +2,272 @@ #include "gdcm.h" +static void _Swap(void* im, int swap, int lgr, int nb); + +///////////////////////////////////////////////////////////////// +/** + * \ingroup gdcmFile + * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant + * \file (see SetFileName, SetDcmTag and Write) + * \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). + * + * @param + * + * @return + */ + +///////////////////////////////////////////////////////////////// +/** + * \ingroup gdcmFile + * \brief constructeur + * + * @param + * + * @return + */ + + gdcmFile::gdcmFile(string & filename) :gdcmHeader(filename.c_str()) { +} + + +///////////////////////////////////////////////////////////////// +/** + * \ingroup gdcmFile + * \brief Renvoie la longueur A ALLOUER pour recevoir les pixels de l'image + * \ ou DES images dans le cas d'un multiframe + * \ ATTENTION : il ne s'agit PAS de la lgr du groupe des Pixels + * \ (dans le cas d'images compressees, elle n'a pas de sens). + * + * @param void Rien en entree + * + * @return longueur a allouer + */ + +size_t gdcmFile::GetImageDataSize(void) { + int nbLignes, nbCol, nbFrames, nb; + string str_nbFrames, str_nb; + // Nombre de Lignes + nbLignes=atoi(gdcmHeader::GetPubElValByNumber(0x0028,0x0010).c_str()); + // Nombre de Colonnes + nbCol =atoi(gdcmHeader::GetPubElValByNumber(0x0028,0x0011).c_str()); + + // Nombre de Frames + str_nbFrames=gdcmHeader::GetPubElValByNumber(0x0028,0x0008); + + if (str_nbFrames == "UNFOUND" ) { + nbFrames = 1; + } else { + nbFrames = atoi(str_nbFrames.c_str() ); + } + // Nombre de Bits Alloues pour le stockage d'un Pixel + str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100); + + if (str_nb == "UNFOUND" ) { + nb = 16; + } else { + nb = atoi(str_nb.c_str() ); + } + + size_t lgrTotale = nbFrames*nbLignes*nbCol*(nb/8); + return (lgrTotale); + } + +///////////////////////////////////////////////////////////////// +/** + * \ingroup gdcmFile + * \brief amene en mémoire les Pixels d'une image NON COMPRESSEE + * \Aucun test n'est fait pour le moment sur le caractere compresse ou non de l'image + * + * @param rien + * + * @return Pointeur sur la zone mémoire contenant les Pixels lus + */ + +void * gdcmFile::GetImageData (void) { + + char* Pixels; + int nbLignes, nbCol; + + int nbFrames, nb, nbu, highBit, signe; + string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe; + + unsigned short int mask = 0xffff; + + // Nombre de Lignes + nbLignes=atoi(GetPubElValByNumber(0x0028,0x0010).c_str()); + // Nombre de Colonnes + nbCol =atoi(GetPubElValByNumber(0x0028,0x0011).c_str()); + + // Nombre de Frames + str_nbFrames=GetPubElValByNumber(0x0028,0x0008); + + + if (str_nbFrames == "UNFOUND" ) { + nbFrames = 1; + } else { + nbFrames = atoi(str_nbFrames.c_str() ); + } + + // Nombre de Bits Alloues + str_nb=GetPubElValByNumber(0x0028,0x0100); + + if (str_nb == "UNFOUND" ) { + nb = 16; + } else { + nb = atoi(str_nb.c_str() ); + } + + // Nombre de Bits Utilises + str_nbu=GetPubElValByNumber(0x0028,0x0101); + + if (str_nbu == "UNFOUND" ) { + nbu = nb; + } else { + nbu = atoi(str_nbu.c_str() ); + } + + // Position du Bit de Poids Fort + str_highBit=GetPubElValByNumber(0x0028,0x0102); + + if (str_highBit == "UNFOUND" ) { + highBit = nb - 1; + } else { + highBit = atoi(str_highBit.c_str() ); + } + + // Signe des Pixels + str_signe=GetPubElValByNumber(0x0028,0x0103); + + if (str_signe == "UNFOUND" ) { + signe = 1; + } else { + signe = atoi(str_signe.c_str() ); + } + + // Longueur en Octets des Pixels a lire + size_t lgrTotale = nbFrames*nbLignes*nbCol*(nb/8); + + //Pixels = (char *) g_malloc(lgrTotale); + Pixels = (char *) malloc(lgrTotale); + + GetPixels(lgrTotale, Pixels); + + // On remet les Octets dans le bon ordre si besoin est + if (nb != 8) { + //int sw = GetSwapCode(); + + // _Swap (Pixels, sw, lgrTotale, nb); // A REMETTRE + } + + // On remet les Bits des Octets dans le bon ordre si besoin est + // + // ATTENTION : Jamais confronté a des pixels stockes sur 32 bits + // avec moins de 32 bits utilises + // et dont le bit de poids fort ne serait pas la ou on l'attend ... + // --> ne marchera pas dans ce cas + if (nbu!=nb){ + mask = mask >> (nb-nbu); + int l=(int)lgrTotale/(nb/8); + unsigned short *deb = (unsigned short *)Pixels; + for(int i=0;i> (nbu-highBit-1)) & mask; + deb ++; + } + } + + printf ("on est sorti\n"); + + // VOIR s'il ne faudrait pas l'affecter à un champ du dcmHeader + + return (Pixels); +} + + +// +// Je laisse le code integral, au cas ça puisse etre reutilise ailleurs +// + +static void _Swap(void* im, int swap, int lgr, int nb) { +guint32 s32; +guint16 fort,faible; +int i; + +if(nb == 16) + + switch(swap) { + case 0: + case 12: + case 1234: + break; + + case 21: + case 3412: + case 2143: + case 4321: + + for(i=0;i>8) + | ((((unsigned short int*)im)[i])<<8); + break; + + default: + printf("valeur de SWAP (16 bits) non autorisee : %d\n", swap); + } + +if( nb == 32 ) + + switch (swap) { + case 0: + case 1234: + break; + + case 4321: + for(i=0;i>16; + fort= (fort>>8) | (fort<<8); + faible=(faible>>8) | (faible<<8); + s32=faible; + ((unsigned long int*)im)[i]=(s32<<16)|fort; + } + break; + + case 2143: + for(i=0;i>16; + fort= (fort>>8) | (fort<<8); + faible=(faible>>8) | (faible<<8); + s32=fort; + ((unsigned long int*)im)[i]=(s32<<16)|faible; + } + break; + + case 3412: + for(i=0;i>16; + s32=faible; + ((unsigned long int*)im)[i]=(s32<<16)|fort; + } + break; + + default: + printf("valeur de SWAP (32 bits) non autorisee : %d\n", swap); + } +return; +} + + + diff --git a/src/gdcmHeader.cxx b/src/gdcmHeader.cxx index 3107da2f..dd49a459 100644 --- a/src/gdcmHeader.cxx +++ b/src/gdcmHeader.cxx @@ -243,6 +243,8 @@ void gdcmHeader::GetPixels(size_t lgrTotale, void* Pixels) { fread(Pixels, 1, lgrTotale, fp); } + + /** * \ingroup gdcmHeader * \brief Find the value representation of the current tag. @@ -254,6 +256,11 @@ void gdcmHeader::GetPixels(size_t lgrTotale, void* Pixels) { * effectivement lue * @return longueur retenue pour le champ */ + +// --> +// --> Oops +// --> C'etait la description de quoi, ca? +// --> void gdcmHeader::FindVR( ElValue *ElVal) { if (filetype != ExplicitVR) @@ -334,11 +341,66 @@ void gdcmHeader::FindVR( ElValue *ElVal) { /** * \ingroup gdcmHeader * \brief Determines if the Transfer Syntax was allready encountered - * and if it corresponds to a Big Endian one. + * and if it corresponds to a ImplicitVRLittleEndian one. + * + * @return True when ImplicitVRLittleEndian found. False in all other cases. + */ +bool gdcmHeader::IsImplicitVRLittleEndianTransferSyntax(void) { + ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2" ) + return true; + return false; +} + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was allready encountered + * and if it corresponds to a ExplicitVRLittleEndian one. + * + * @return True when ExplicitVRLittleEndian found. False in all other cases. + */ +bool gdcmHeader::IsExplicitVRLittleEndianTransferSyntax(void) { + ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2.1" ) + return true; + return false; +} + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was allready encountered + * and if it corresponds to a DeflatedExplicitVRLittleEndian one. + * + * @return True when DeflatedExplicitVRLittleEndian found. False in all other cases. + */ +bool gdcmHeader::IsDeflatedExplicitVRLittleEndianTransferSyntax(void) { + ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2.1.99" ) + return true; + return false; +} + + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was allready encountered + * and if it corresponds to a Explicit VR Big Endian one. * * @return True when big endian found. False in all other cases. */ -bool gdcmHeader::IsBigEndianTransferSyntax(void) { +bool gdcmHeader::IsExplicitVRBigEndianTransferSyntax(void) { ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); if ( !Element ) return false; @@ -349,11 +411,92 @@ bool gdcmHeader::IsBigEndianTransferSyntax(void) { return false; } -void gdcmHeader::FixFoundLength(ElValue * ElVal, guint32 FoudLength) { + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was allready encountered + * and if it corresponds to a JPEGBaseLineProcess1 one. + * + * @return True when JPEGBaseLineProcess1found. False in all other cases. + */ +bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) { + ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2.4.50" ) + return true; + return false; +} + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was allready encountered + * and if it corresponds to a JPEGExtendedProcess2-4 one. + * + * @return True when JPEGExtendedProcess2-4 found. False in all other cases. + */ +bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) { + ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2.4.51" ) + return true; + return false; +} + + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was allready encountered + * and if it corresponds to a JPEGExtendeProcess3-5 one. + * + * @return True when JPEGExtendedProcess3-5 found. False in all other cases. + */ +bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) { + ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2.4.52" ) + return true; + return false; +} + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was allready encountered + * and if it corresponds to a JPEGSpectralSelectionProcess6-8 one. + * + * @return True when JPEGSpectralSelectionProcess6-8 found. False in all other cases. + */ +bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) { + ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2.4.53" ) + return true; + return false; +} + +// +// Euhhhhhhh +// Il y en a encore DIX-SEPT, comme ça. +// Il faudrait trouver qq chose + rusé ... +// + + +void gdcmHeader::FixFoundLength(ElValue * ElVal, guint32 FoundLength) { // Heuristic: a final fix. - if ( FoudLength == 0xffffffff) - FoudLength = 0; - ElVal->SetLength(FoudLength); + if ( FoundLength == 0xffffffff) + FoundLength = 0; + ElVal->SetLength(FoundLength); } guint32 gdcmHeader::FindLengthOB(void) { @@ -442,7 +585,7 @@ void gdcmHeader::FindLength(ElValue * ElVal) { // hands on a big endian encoded file: we switch the swap code to // big endian and proceed... if ( (element == 0x000) && (length16 == 0x0400) ) { - if ( ! IsBigEndianTransferSyntax() ) + if ( ! IsExplicitVRBigEndianTransferSyntax() ) throw Error::FileReadError(fp, "gdcmHeader::FindLength"); length16 = 4; SwitchSwapToBigEndian(); @@ -628,6 +771,11 @@ void gdcmHeader::LoadElementValue(ElValue * ElVal) { } // FIXME The exact size should be length if we move to strings or whatever + + // + // QUESTION : y a-t-il une raison pour ne pas utiliser g_malloc ici ? + // + char* NewValue = (char*)malloc(length+1); if( !NewValue) { dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue"); -- 2.48.1