From: jpr Date: Wed, 23 Jul 2003 08:43:03 +0000 (+0000) Subject: - Color problems taken in to account X-Git-Tag: Version0.3.1~202 X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=b158878c883def8179e3eccc27fcea5bb6035a4c;p=gdcm.git - Color problems taken in to account - 12 Bits Jpeg Lossy taken into account - (RLE LossLess functions not yet made) --- diff --git a/src/Makefile.am b/src/Makefile.am index ade41d50..c60798f5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,8 +8,10 @@ CXXFLAGS = @CXXFLAGS@ -DPUB_DICT_PATH=\"$(datadir)/gdcm/\" \ -D__STDC_LIMIT_MACROS # Refer to stdint.h libgdcm_la_LIBADD = \ + $(top_builddir)/src/jpeg/libijg12/libgdcmijpeg12.la \ $(top_builddir)/src/jpeg/libijg8/libgdcmijpeg8.la + libgdcm_la_SOURCES= \ gdcmException.cxx \ gdcmUtil.cxx \ @@ -20,10 +22,13 @@ libgdcm_la_SOURCES= \ gdcmDictSet.cxx \ gdcmElValSet.cxx \ gdcmHeaderIdo.cxx \ - gdcmFile.cxx \ - gdcmVR.cxx \ - gdcmTS.cxx \ - gdcmJpeg.cxx \ + gdcmFile.cxx \ + gdcmVR.cxx \ + gdcmTS.cxx \ + gdcmJpeg.cxx \ + gdcmJpeg12.cxx \ + gdcmJpeg2000.cxx \ + gdcmRLE.cxx \ gdcmJpegIdo.cxx libgdcmincludedir = $(includedir) diff --git a/src/gdcmElValSet.cxx b/src/gdcmElValSet.cxx index 044ee8eb..58302b3d 100644 --- a/src/gdcmElValSet.cxx +++ b/src/gdcmElValSet.cxx @@ -14,7 +14,7 @@ gdcmElValSet::~gdcmElValSet() { } tagHt.clear(); // Since Add() adds symetrical in both tagHt and NameHt we can - // assume all the pointed gdcmElValues are allready cleaned-up when + // assume all the pointed gdcmElValues are already cleaned-up when // we cleaned tagHt. NameHt.clear(); } @@ -34,7 +34,6 @@ void gdcmElValSet::Add(gdcmElValue * newElValue) { NameHt[newElValue->GetName()] = newElValue; } - /** * \ingroup gdcmElValSet * \brief Checks if a given Dicom element exists @@ -68,24 +67,28 @@ void gdcmElValSet::Print(ostream & os) { e = tag->second->GetElement(); v = tag->second->GetValue(); o = tag->second->GetOffset(); - d = _CreateCleanString(v); // TODO : trouver qq chose moins goret + d = _CreateCleanString(v); // replace non printable characters by '.' d2=d; os << tag->first << ": "; - //os << "[" << v << "]"; - os << "[" << d2 << "]"; - os << "[" << tag->second->GetName() << "]"; - os << "[" << tag->second->GetVR() << "]"; - - if ( (g == 0x0002) && (e == 0x0010) ) { - os << " [" << ts->GetValue(v) << "]"; - } - - // liberer 'd' ici ? - os << " lgr : " << tag->second->GetLength(); os << ", Offset : " << o; os << " x(" << hex << o << dec << ") "; + os << "\t[" << tag->second->GetVR() << "]"; + os << "\t[" << tag->second->GetName() << "]"; + os << "\t[" << d2 << "]"; + + // Display the UID value (instead of displaying the rough code) + if (g == 0x0002) { // Some more to be displayed ? + if ( (e == 0x0010) || (e == 0x0002) ) + os << " ==>\t[" << ts->GetValue(v) << "]"; + } else { + if (g == 0x0008) { + if ( (e == 0x0016) || (e == 0x1150) ) + os << " ==>\t[" << ts->GetValue(v) << "]"; + } + } + free(d); os << endl; } } @@ -230,11 +233,28 @@ guint32 gdcmElValSet::GenerateFreeTagKeyInGroup(guint16 group) { /** * \ingroup gdcmElValSet * \brief - * @param length + * @param area * @param group * @param element * @return */ +int gdcmElValSet::SetVoidAreaByNumber(void * area, + guint16 group, guint16 element) { + TagKey key = gdcmDictEntry::TranslateToKey(group, element); + if ( ! tagHt.count(key)) + return 0; + tagHt[key]->SetVoidArea(area); + return 1 ; +} + +/** + * \ingroup gdcmElValSet + * \brief + * @param length + * @param group + * @param element + * @return int acts as a boolean + */ int gdcmElValSet::SetElValueLengthByNumber(guint32 length, guint16 group, guint16 element) { TagKey key = gdcmDictEntry::TranslateToKey(group, element); @@ -243,8 +263,6 @@ int gdcmElValSet::SetElValueLengthByNumber(guint32 length, tagHt[key]->SetLength(length); return 1 ; } - - /** * \ingroup gdcmElValSet * \brief @@ -262,7 +280,7 @@ int gdcmElValSet::SetElValueLengthByName(guint32 length, string TagName) { /** * \ingroup gdcmElValSet * \brief Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader - * @param SkipSequence + * @param SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files) * @param type */ void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) { @@ -300,13 +318,16 @@ void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) { if (SkipSequence && vr == "SQ") continue; + // Still unsolved problem : + // we cannot find the 'Sequence Delimitation Item' + // since it's at the end of the Hash Table + // (fffe,e0dd) + // pas SEQUENCE en ACR-NEMA - // WARNING : pb CERTAIN - // si on est descendu 'a l'interieur' des SQ - // + // WARNING : // --> la descente a l'interieur' des SQ - // devra etre faite avec une liste chainee, pas avec une HTable... - + // devrait etre faite avec une liste chainee, pas avec une HTable... + if ( groupHt.count(key) == 0) { // we just read the first elem of a given group if (el == 0x0000) { // the first elem is 0x0000 groupHt[key] = 0; // initialize group length @@ -322,6 +343,7 @@ void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) { groupHt[key] += 2 + 2 + 4 + elem->GetLength(); } } + unsigned short int gr_bid; for (GroupHT::iterator g = groupHt.begin(); // for each group we found @@ -350,7 +372,7 @@ void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) { * \ingroup gdcmElValSet * \brief * @param type - * @param _fp + * @param _fp * @return */ void gdcmElValSet::WriteElements(FileType type, FILE * _fp) { @@ -377,18 +399,21 @@ void gdcmElValSet::WriteElements(FileType type, FILE * _fp) { lgr = tag2->second->GetLength(); val = tag2->second->GetValue().c_str(); vr = tag2->second->GetVR(); + + // cout << "Tag "<< hex << gr << " " << el << "\n"; if ( type == ACR ) { - if (gr < 0x0008) continue; - // if (gr %2) continue; // pour voir - if (vr == "SQ" ) continue; + if (gr < 0x0008) continue; // ignore pure DICOM V3 groups + if (gr %2) continue; // ignore shadow groups + if (vr == "SQ" ) continue; // ignore Sequences + if (gr == 0xfffe ) continue; // ignore delimiters } fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp); //group fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp); //element if ( (type == ExplicitVR) && (gr <= 0x0002) ) { - // On est en EXPLICIT VR + // EXPLICIT VR guint16 z=0, shortLgr; fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp); @@ -400,35 +425,36 @@ void gdcmElValSet::WriteElements(FileType type, FILE * _fp) { shortLgr=lgr; fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp); } - } else { + } else { // IMPLICIT VR fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp); } - tokens.erase(tokens.begin(),tokens.end()); - Tokenize (tag2->second->GetValue(), tokens, "\\"); - if (vr == "US" || vr == "SS") { + tokens.erase(tokens.begin(),tokens.end()); // clean any previous value + Tokenize (tag2->second->GetValue(), tokens, "\\"); for (unsigned int i=0; isecond->GetValue(), tokens, "\\"); for (unsigned int i=0; iGetGroup(); }; - guint16 GetElement(void) { return entry->GetElement();}; - std::string GetKey(void) { return entry->GetKey(); }; - std::string GetName(void) { return entry->GetName(); }; - std::string GetVR(void) { return entry->GetVR(); }; - std::string GetValue(void) { return value; }; - size_t GetOffset(void) { return Offset; }; - guint32 GetLength(void) { return LgrElem; }; + guint16 GetGroup(void) { return entry->GetGroup(); }; + guint16 GetElement(void) { return entry->GetElement();}; + std::string GetKey(void) { return entry->GetKey(); }; + std::string GetName(void) { return entry->GetName(); }; + std::string GetVR(void) { return entry->GetVR(); }; + std::string GetValue(void) { return value; }; + void * GetVoidArea(void) { return voidArea; }; + size_t GetOffset(void) { return Offset; }; + guint32 GetLength(void) { return LgrElem; }; void SetVR(std::string v) { entry->SetVR(v); }; void SetLength(guint32 l) { LgrElem = l; }; - void SetValue(std::string val){ value = val; }; + void SetValue(std::string val){ value = val; }; + void SetVoidArea(void * area) { voidArea = area; }; }; diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index 3431c0f8..2721d9ad 100644 --- a/src/gdcmFile.cxx +++ b/src/gdcmFile.cxx @@ -2,9 +2,11 @@ #include "gdcmFile.h" #include "gdcmUtil.h" -#include "iddcmjpeg.h" +#include "iddcmjpeg.h" // for the 'LibIDO' Jpeg LossLess using namespace std; +#define str2num(str, typeNum) *((typeNum *)(str)) + ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile @@ -56,7 +58,18 @@ void gdcmFile::SetPixelDataSizeFromHeader(void) { nb = atoi(str_nb.c_str() ); if (nb == 12) nb =16; } - lgrTotale = GetXSize() * GetYSize() * GetZSize() * (nb/8)* GetSamplesPerPixel();; + lgrTotale = GetXSize() * GetYSize() * GetZSize() * (nb/8)* GetSamplesPerPixel(); + + string str_PhotometricInterpretation = gdcmHeader::GetPubElValByNumber(0x0028,0x0004); + if ( str_PhotometricInterpretation == "PALETTE COLOR " + || str_PhotometricInterpretation == "YBR_FULL") { // --> some more to be added !! + lgrTotale*=3; + } + + // remaining to check : + // str_PhotometricInterpretation == "YBR_FULL" + // str_PhotometricInterpretation == "YBR_FULL_422" (no LUT, no Palette) + // -->and some more !! } ///////////////////////////////////////////////////////////////// @@ -71,6 +84,122 @@ size_t gdcmFile::GetImageDataSize(void) { } +///////////////////////////////////////////////////////////////// +/** + * \ingroup gdcmFile + * \brief Parse pixel data from disk and *prints* the result + * \ For multi-fragment Jpeg files checking purpose *only* + * \ Allows to 'see' if the file *does* conform + * \ (some of them do not) + * \ with Dicom Part 3, Annex A (PS 3.5-2003, page 58) + * + */ +bool gdcmFile::ParsePixelData(void) { + if ( !OpenFile()) + return false; + + if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) { + CloseFile(); + return false; + } + + if ( !IsDicomV3() || + IsImplicitVRLittleEndianTransferSyntax() || + IsExplicitVRLittleEndianTransferSyntax() || + IsExplicitVRBigEndianTransferSyntax() || + IsDeflatedExplicitVRLittleEndianTransferSyntax() ) { + + printf ("gdcmFile::ParsePixelData : non JPEG File\n"); + return 0; + } + + int nb; + string str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100); + if (str_nb == "gdcm::Unfound" ) { + nb = 16; + } else { + nb = atoi(str_nb.c_str() ); + if (nb == 12) nb =16; + } + int nBytes= nb/8; + + //int taille = GetXSize() * GetYSize() * GetZSize() * GetSamplesPerPixel(); + int taille = GetXSize() * GetYSize() * GetSamplesPerPixel(); + + printf ("Checking the Dicom-Jpeg/RLE Pixels\n"); + + // ------------------------------- for Parsing : Position on begining of Jpeg Pixels + guint16 ItemTagGr,ItemTagEl; + int ln; + long ftellRes; + char * destination = NULL; + ftellRes=ftell(fp); + fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Basic Offset Table Item Tag Gr + fread(&ItemTagEl,2,1,fp); // Reading (e000) : Basic Offset Table Item Tag El + if(GetSwapCode()) { + ItemTagGr=SwapShort(ItemTagGr); + ItemTagEl=SwapShort(ItemTagEl); + } + printf ("at %x : ItemTag (should be fffe,e000): %04x,%04x\n", + ftellRes,ItemTagGr,ItemTagEl ); + ftellRes=ftell(fp); + fread(&ln,4,1,fp); + if(GetSwapCode()) + ln=SwapLong(ln); // Basic Offset Table Item Lentgh + printf("at %x : Basic Offset Table Item Lentgh (??) %d x(%08x)\n", + ftellRes,ln,ln); + if (ln != 0) { + // What is it used for ?? + char * BasicOffsetTableItemValue= (char *)malloc(ln+1); + fread(BasicOffsetTableItemValue,ln,1,fp); + guint32 a; + for (int i=0;iDataImg; - int taille = GetXSize() * GetYSize() * GetZSize() * GetSamplesPerPixel(); + + //int taille = GetXSize() * GetYSize() * GetZSize() * GetSamplesPerPixel(); + int taille = GetXSize() * GetYSize() * GetSamplesPerPixel(); + + // ------------------------------- JPEG LossLess : call to Jpeg Libido + + if (IsJPEGLossless() && GetZSize() == 1) { + + int ln; // Position on begining of Jpeg Pixels + fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : Basic Offset Table Item + fread(&ln,4,1,fp); + if(GetSwapCode()) + ln=SwapLong(ln); // Item length + fseek(fp,ln,SEEK_CUR); // skipping Basic Offset Table ('ln' bytes) + fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : First fragment Item Tag + fread(&ln,4,1,fp); // First fragment length (just to know) + if(GetSwapCode()) + ln=SwapLong(ln); + + ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one. + // (We use the LibIDO one :-( + if(jpg == NULL) { + CloseFile(); + return false; + } + int * dataJpg = jpg->DataImg; + switch (nBytes) { case 1: { unsigned short *dest = (unsigned short *)destination; for (int i=0; i - // TODO : eplucher icelui pour traiter *egalement* bits=16 - - int nBS; - if ((nBS = GetBitsStored()) != 12) { - printf("Sorry, Bits Stored = %d not yet taken into account\n",nBS); - return false; + + long fragmentBegining; // for ftell, fseek + + bool a=0, b=0; + + a = gdcmHeader::IsRLELossLessTransferSyntax(); + if (!a) + bool b = gdcmHeader::IsJPEG2000(); + + bool res; + guint16 ItemTagGr,ItemTagEl; + int ln; // Position on begining of Jpeg Pixels + + fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr + fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El + if(GetSwapCode()) { + ItemTagGr=SwapShort(ItemTagGr); + ItemTagEl=SwapShort(ItemTagEl); } + fread(&ln,4,1,fp); + if(GetSwapCode()) + ln=SwapLong(ln); // Basic Offset Table Item length + + if (ln != 0) { + // What is it used for ?!? + char *BasicOffsetTableItemValue = (char *)malloc(ln+1); + fread(BasicOffsetTableItemValue,ln,1,fp); + } + + // first Fragment initialisation + fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr + fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El + if(GetSwapCode()) { + ItemTagGr=SwapShort(ItemTagGr); + ItemTagEl=SwapShort(ItemTagEl); + } + + // parsing fragments until Sequence Delim. Tag found + //unsigned short *dest = (unsigned short *)destination; + + while ( ( ItemTagGr == 0xfffe) && (ItemTagEl != 0xe0dd) ) { + fread(&ln,4,1,fp); + if(GetSwapCode()) + ln=SwapLong(ln); // Fragment Item length - bool res = (bool)gdcm_read_JPEG_file (destination); + // FIXME : multi fragments + fragmentBegining=ftell(fp); + + if (a) + res = (bool)gdcm_read_RLE_file (destination); // Reading Fragment pixels + else if (b) + res = (bool)gdcm_read_JPEG2000_file (destination); // Reading Fragment pixels + + else if (IsJPEGLossless()) { // ------------- call to LibIDO Jpeg for each Frame/fragment + + // Warning : Works only if there is one fragment per frame + // (Or a single fragment for the multiframe file) + ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one. + // (We use the LibIDO one :-( + if(jpg == NULL) { + CloseFile(); + return false; + } + int * dataJpg = jpg->DataImg; + unsigned short *dest = (unsigned short *)destination; + switch (nBytes) { + case 1: + { + for (int i=0; i not-for-rats function + * + * \warning WARNING doit-etre etre publique ? * TODO : y aurait il un inconvenient à fusionner ces 2 fonctions * - * @param ImageDataSize TODO JPR - * + * @param ImageDataSize new Pixel Area Size + * warning : nothing else is checked */ void gdcmFile::SetImageDataSize(size_t ImageDataSize) { - string content1; - char car[20]; - - // suppose que le ElValue (0x7fe0, 0x0010) existe ... - + char car[20]; + // Assumes ElValue (0x7fe0, 0x0010) exists ... sprintf(car,"%d",ImageDataSize); gdcmElValue*a = GetElValueByNumber(0x7fe0, 0x0010); @@ -457,70 +748,62 @@ void gdcmFile::SetImageDataSize(size_t ImageDataSize) { * \brief 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 - * (Equivalent a IdImaWriteRawFile) FIXME JPR - * - * @param nomFichier TODO JPR + * (Equivalent a IdImaWriteRawFile) * - * @return TODO JPR + * @param fileName + * @return */ -int gdcmFile::WriteRawData (string nomFichier) { - +int gdcmFile::WriteRawData (string fileName) { FILE * fp1; - fp1 = fopen(nomFichier.c_str(),"wb"); + fp1 = fopen(fileName.c_str(),"wb"); if (fp1 == NULL) { - printf("Echec ouverture (ecriture) Fichier [%s] \n",nomFichier.c_str()); + printf("Echec ouverture (ecriture) Fichier [%s] \n",fileName.c_str()); return (0); - } - + } fwrite (PixelData,lgrTotale, 1, fp1); fclose (fp1); return(1); } - - ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile * \brief Ecrit sur disque UNE image Dicom * Aucun test n'est fait sur l'"Endiannerie" du processeur. * Ca fonctionnera correctement (?) sur processeur Intel - * (Equivalent a IdDcmWrite) FIXME JPR - * - * @param nomFichier TODO JPR + * (Equivalent a IdDcmWrite) * - * @return TODO JPR + * @param fileName + * @return int acts as a boolean */ -int gdcmFile::WriteDcmImplVR (string nomFichier) { - return WriteBase(nomFichier, ImplicitVR); +int gdcmFile::WriteDcmImplVR (string fileName) { + return WriteBase(fileName, ImplicitVR); } ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile - * - * @param nomFichier TODO JPR - * - * @return TODO JPR + * \brief + * @param fileName + * @return int acts as a boolean */ -int gdcmFile::WriteDcmImplVR (const char* nomFichier) { - return WriteDcmImplVR (string (nomFichier)); +int gdcmFile::WriteDcmImplVR (const char* fileName) { + return WriteDcmImplVR (string (fileName)); } ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile - * - * @param nomFichier TODO JPR - * - * @return TODO JPR + * \brief + * @param fileName + * @return int acts as a boolean */ -int gdcmFile::WriteDcmExplVR (string nomFichier) { - return WriteBase(nomFichier, ExplicitVR); +int gdcmFile::WriteDcmExplVR (string fileName) { + return WriteBase(fileName, ExplicitVR); } ///////////////////////////////////////////////////////////////// @@ -535,23 +818,22 @@ int gdcmFile::WriteDcmExplVR (string nomFichier) { * Ca fonctionnera correctement (?) sur processeur Intel * (Equivalent a IdDcmWrite) * - * @param nomFichier TODO JPR - * - * @return TODO JPR + * @param fileName + * @return int acts as a boolean */ -int gdcmFile::WriteAcr (string nomFichier) { - return WriteBase(nomFichier, ACR); +int gdcmFile::WriteAcr (string fileName) { + return WriteBase(fileName, ACR); } ///////////////////////////////////////////////////////////////// /** * \ingroup gdcmFile * - * @param FileName TODO JPR - * @param type TODO JPR + * @param FileName + * @param type * - * @return TODO JPR + * @return int acts as a boolean */ int gdcmFile::WriteBase (string FileName, FileType type) { @@ -564,7 +846,7 @@ int gdcmFile::WriteBase (string FileName, FileType type) { if ( (type == ImplicitVR) || (type == ExplicitVR) ) { char * filePreamble; - // Ecriture Dicom File Preamble + // writing Dicom File Preamble filePreamble=(char*)calloc(128,1); fwrite(filePreamble,128,1,fp1); fwrite("DICM",4,1,fp1); diff --git a/src/gdcmFile.h b/src/gdcmFile.h index e9bf7081..f33eca6e 100644 --- a/src/gdcmFile.h +++ b/src/gdcmFile.h @@ -19,9 +19,14 @@ private: int Parsed; // weather already parsed std::string OrigFileName; // To avoid file overwrite void SwapZone(void* im, int swap, int lgr, int nb); + bool ReadPixelData(void * destination); - int gdcm_read_JPEG_file (void * image_buffer); + int gdcm_read_JPEG_file (void * image_buffer); // For JPEG 8 Bits + int gdcm_read_JPEG_file12 (void * image_buffer); // For JPEG 12 Bits + int gdcm_read_JPEG2000_file (void * image_buffer); // For JPEG 2000 (TODO) + int gdcm_read_RLE_file (void * image_buffer); // For Run Length Encoding (TODO) + protected: int WriteBase(std::string FileName, FileType type); @@ -62,11 +67,13 @@ public: // Aucun test n'est fait sur l'"Endiannerie" du processeur. // Ca sera à l'utilisateur d'appeler son Reader correctement - int WriteRawData (std::string nomFichier); - int WriteDcmImplVR(std::string nomFichier); - int WriteDcmImplVR(const char * nomFichier); - int WriteDcmExplVR(std::string nomFichier); - int WriteAcr (std::string nomFichier); + int WriteRawData (std::string fileName); + int WriteDcmImplVR(std::string fileName); + int WriteDcmImplVR(const char * fileName); + int WriteDcmExplVR(std::string fileName); + int WriteAcr (std::string fileName); + + bool ParsePixelData(void); }; #endif diff --git a/src/gdcmHeader.cxx b/src/gdcmHeader.cxx index b92ba737..46838845 100644 --- a/src/gdcmHeader.cxx +++ b/src/gdcmHeader.cxx @@ -1,5 +1,5 @@ -// $Header: /cvs/public/gdcm/src/Attic/gdcmHeader.cxx,v 1.78 2003/07/03 14:38:16 jpr Exp $ +// $Header: /cvs/public/gdcm/src/Attic/gdcmHeader.cxx,v 1.79 2003/07/23 08:43:03 jpr Exp $ #include #include @@ -14,6 +14,7 @@ #include "gdcmUtil.h" #include "gdcmHeader.h" using namespace std; +#include "gdcmTS.h" // TODO : remove DEBUG @@ -22,8 +23,8 @@ using namespace std; // Refer to gdcmHeader::CheckSwap() #define HEADER_LENGTH_TO_READ 256 // Refer to gdcmHeader::SetMaxSizeLoadElementValue() -#define _MaxSizeLoadElementValue_ 1024 - +//#define _MaxSizeLoadElementValue_ 1024 +#define _MaxSizeLoadElementValue_ 4096 /** * \ingroup gdcmHeader * \brief @@ -85,7 +86,7 @@ gdcmHeader::gdcmHeader(bool exception_on_error) { /** * \ingroup gdcmHeader * \brief - * @return + * @return TRUE if the close was successfull */ bool gdcmHeader::CloseFile(void) { int closed = fclose(fp); @@ -137,6 +138,7 @@ gdcmHeader::~gdcmHeader (void) { // CRV Curve // OLY Overlays // PXL Pixels +// DL Delimiters // /** @@ -152,7 +154,7 @@ void gdcmHeader::CheckSwap() // 0x00000004. Finding the swap code in then straigthforward. Trouble // occurs when we can't find such group... guint32 s; - guint32 x=4; // x : pour ntohs + guint32 x=4; // x : for ntohs bool net2host; // true when HostByteOrder is the same as NetworkByteOrder int lgrLue; @@ -545,6 +547,45 @@ bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) { return false; } +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was already encountered + * and if it corresponds to a RLE Lossless one. + * + * @return True when RLE Lossless found. False in all + * other cases. + */ +bool gdcmHeader::IsRLELossLessTransferSyntax(void) { + gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( Transfer == "1.2.840.10008.1.2.5" ) + return true; + return false; +} + +/** + * \ingroup gdcmHeader + * \brief Determines if the Transfer Syntax was already encountered + * and if it corresponds to a JPEG200 one.0 + * + * @return True when JPEG2000 (Lossly or LossLess) found. False in all + * other cases. + */ +bool gdcmHeader::IsJPEG2000(void) { + gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010); + if ( !Element ) + return false; + LoadElementValueSafe(Element); + string Transfer = Element->GetValue(); + if ( (Transfer == "1.2.840.10008.1.2.4.90") + || (Transfer == "1.2.840.10008.1.2.4.91") ) + return true; + return false; +} + /** * \ingroup gdcmHeader * \brief Predicate for dicom version 3 file. @@ -782,7 +823,7 @@ guint16 gdcmHeader::SwapShort(guint16 a) { /** * \ingroup gdcmHeader * \brief - * + * @param ElVal * @return */ void gdcmHeader::SkipElementValue(gdcmElValue * ElVal) { @@ -792,7 +833,7 @@ guint16 gdcmHeader::SwapShort(guint16 a) { /** * \ingroup gdcmHeader * \brief - * + * @param NewSize * @return */ void gdcmHeader::SetMaxSizeLoadElementValue(long NewSize) { @@ -834,6 +875,11 @@ void gdcmHeader::LoadElementValue(gdcmElValue * ElVal) { // Heuristic : a sequence "contains" a set of tags (called items). It looks // like the last tag of a sequence (the one that terminates the sequence) // has a group of 0xfffe (with a dummy length). + // Well ... + // Actually (fffe e000) tells us an Element is beginning + // (fffe e00d) tells us an Element just ended + // (fffe e0dd) tells us the current SEQuence just ended + if( group == 0xfffe ) SkipLoad = true; @@ -935,9 +981,10 @@ void gdcmHeader::LoadElementValueSafe(gdcmElValue * ElVal) { /** * \ingroup gdcmHeader - * \brief + * \brief Reads a supposed to be 16 Bits integer + * \ (swaps it depending on processor endianity) * - * @return + * @return integer acts as a boolean */ guint16 gdcmHeader::ReadInt16(void) { guint16 g; @@ -959,7 +1006,8 @@ guint16 gdcmHeader::ReadInt16(void) { /** * \ingroup gdcmHeader - * \brief + * \brief Reads a supposed to be 32 Bits integer + * \ (swaps it depending on processor endianity) * * @return */ @@ -1027,6 +1075,7 @@ gdcmElValue* gdcmHeader::NewElValueByNumber(guint16 Group, guint16 Elem) { * @param Value * @param Group * @param Elem + * \return integer acts as a boolean */ int gdcmHeader::ReplaceOrCreateByNumber(string Value, guint16 Group, guint16 Elem ) { @@ -1046,10 +1095,12 @@ int gdcmHeader::ReplaceOrCreateByNumber(string Value, guint16 Group, guint16 Ele /** * \ingroup gdcmHeader - * \brief TODO - * @param Value + * \brief Modify or (Creates if not found) an element + * @param Value new value * @param Group - * @param Elem + * @param Elem + * \return integer acts as a boolean + * */ int gdcmHeader::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem ) { @@ -1060,18 +1111,37 @@ int gdcmHeader::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem return(1); } + /** * \ingroup gdcmHeader - * \brief TODO + * \brief Set a new value if the invoked element exists + * @param Value + * @param Group + * @param Elem + * \return integer acts as a boolean + */ +int gdcmHeader::ReplaceIfExistByNumber(char* Value, guint16 Group, guint16 Elem ) { + + gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem); + string v = Value; + PubElValSet.SetElValueByNumber(v, Group, Elem); + return 1; +} + + +/** + * \ingroup gdcmHeader + * \brief Checks if a given ElValue (group,number) + * \ exists in the Public ElValSet * @param Group - * @param Elem + * @param Elem + * @return integer acts as a boolean */ int gdcmHeader::CheckIfExistByNumber(guint16 Group, guint16 Elem ) { return (PubElValSet.CheckIfExistByNumber(Group, Elem)); } - - + /** * \ingroup gdcmHeader * \brief Build a new Element Value from all the low level arguments. @@ -1616,6 +1686,19 @@ void gdcmHeader::LoadElements(void) { for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag) { LoadElementValue(tag->second); } + // Load 'non string' values + rewind(fp); + string PhotometricInterpretation = GetPubElValByNumber(0x0028,0x0004); + if( PhotometricInterpretation == "PALETTE COLOR " ){ + LoadElementVoidArea(0x0028,0x1200); // gray LUT + LoadElementVoidArea(0x0028,0x1201); // R LUT + LoadElementVoidArea(0x0028,0x1202); // G LUT + LoadElementVoidArea(0x0028,0x1203); // B LUT + + LoadElementVoidArea(0x0028,0x1221); // Segmented Red Palette Color LUT Data + LoadElementVoidArea(0x0028,0x1222); // Segmented Green Palette Color LUT Data + LoadElementVoidArea(0x0028,0x1223); // Segmented Blue Palette Color LUT Data + } } /** @@ -1645,9 +1728,80 @@ int gdcmHeader::Write(FILE * fp, FileType type) { return PubElValSet.Write(fp, type); } +// +// ------------------------ 'non string' elements related functions +// + +/** + * \ingroup gdcmHeader + * \brief Loads (from disk) the element content + * when a string is not suitable + */ +void * gdcmHeader::LoadElementVoidArea(guint16 Group, guint16 Elem) { + gdcmElValue * Element= PubElValSet.GetElementByNumber(Group, Elem); + if ( !Element ) + return NULL; + size_t o =(size_t)Element->GetOffset(); + fseek(fp, o, SEEK_SET); + int l=Element->GetLength(); + void * a = malloc(l); + if(!a) { + cout << "Big Broblem (LoadElementVoidArea, malloc) " + << hex << Group << " " << Elem << "\n"; + return NULL; + } + int res = PubElValSet.SetVoidAreaByNumber(a, Group, Elem); + // TODO check the result + size_t l2 = fread(a, 1, l ,fp); + if(l != l2) { + cout << "Big Broblem (LoadElementVoidArea, fread) " + << hex << Group << " " << Elem << "\n"; + free(a); + return NULL; + } +} + +/** + * \ingroup gdcmHeader + * \brief Gets (from Header) the offset of a 'non string' element value + * \ (LoadElementValue has already be executed) + * @param Group + * @param Elem + * @return File Offset of the Element Value + */ + size_t gdcmHeader::GetPubElValOffsetByNumber(guint16 Group, guint16 Elem) { + gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem); + if (!elValue) { + dbg.Verbose(1, "gdcmHeader::GetElValueByNumber", + "failed to Locate gdcmElValue"); + return (size_t)0; + } + return elValue->GetOffset(); +} + +/** + * \ingroup gdcmHeader +* \brief Gets (from Header) a 'non string' element value + * \ (LoadElementValue has already be executed) + * @param Group + * @param Elem + * @return Pointer to the 'non string' area + + */ + void * gdcmHeader::GetPubElValVoidAreaByNumber(guint16 Group, guint16 Elem) { + gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem); + if (!elValue) { + dbg.Verbose(1, "gdcmHeader::GetElValueByNumber", + "failed to Locate gdcmElValue"); + return (NULL); + } + return elValue->GetVoidArea(); +} + + // // ============================================================================= -// Accesors with euristics +// Heuristics based accessors //============================================================================== // @@ -1742,15 +1896,13 @@ int gdcmHeader::GetSamplesPerPixel(void) { return atoi(StrSize.c_str()); } - -/* ================ COMMENT OUT after unfreeze -** +/** * \ingroup gdcmHeader * \brief Retrieve the Planar Configuration for RGB images * (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane) * * @return The encountered Planar Configuration, 0 by default. - * + */ int gdcmHeader::GetPlanarConfiguration(void) { string StrSize = GetPubElValByNumber(0x0028,0x0006); if (StrSize == "gdcm::Unfound") @@ -1758,8 +1910,6 @@ int gdcmHeader::GetPlanarConfiguration(void) { return atoi(StrSize.c_str()); } - ======================================= */ - /** * \ingroup gdcmHeader * \brief Return the size (in bytes) of a single pixel of data. @@ -2010,4 +2160,199 @@ float gdcmHeader::GetZImagePosition(void) { } +/** + * \ingroup gdcmHeader + * \brief gets the info from 0002,0010 : Transfert Syntax + * \ else 1. + * @return Transfert Syntax Name (as oposite to Transfert Syntax UID) + */ +string gdcmHeader::GetTransferSyntaxName(void) { + string TransfertSyntax = GetPubElValByNumber(0x0002,0x0010); + if (TransfertSyntax == "gdcm::Unfound") { + dbg.Verbose(0, "gdcmHeader::GetTransferSyntaxName: unfound Transfert Syntax (0002,0010)"); + return "Uncompressed ACR-NEMA"; + } + // we do it only when we need it + gdcmTS * ts = gdcmGlobal::GetTS(); + string tsName=ts->GetValue(TransfertSyntax); + //delete ts; // Seg Fault when deleted ?! + return tsName; +} + +// -------------------------------- Lookup Table related functions ------------ +/** + * \ingroup gdcmHeader + * \brief gets the info from 0028,1101 : Lookup Table Desc-Red + * \ else 0 + * @return Lookup Table Length + * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] + */ + +int gdcmHeader::GetLUTLength(void) { + vector tokens; + int LutLength; + //int LutDepth; + //int LutNbits; + // Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue + string LutDescriptionR = GetPubElValByNumber(0x0028,0x1101); + if (LutDescriptionR == "gdcm::Unfound") + return 0; + string LutDescriptionG = GetPubElValByNumber(0x0028,0x1102); + if (LutDescriptionG == "gdcm::Unfound") + return 0; + string LutDescriptionB = GetPubElValByNumber(0x0028,0x1103); + if (LutDescriptionB == "gdcm::Unfound") + return 0; + if( (LutDescriptionR != LutDescriptionG) || (LutDescriptionR != LutDescriptionB) ) { + dbg.Verbose(0, "gdcmHeader::GetLUTLength: The CLUT R,G,B are not equal"); + return 0; + } + cout << "Lut Description " << LutDescriptionR <<"\n"; + tokens.erase(tokens.begin(),tokens.end()); // clean any previous value + Tokenize (LutDescriptionR, tokens, "\\"); + LutLength=atoi(tokens[0].c_str()); + //LutDepth=atoi(tokens[1].c_str()); + //LutNbits=atoi(tokens[2].c_str()); + tokens.clear(); + return LutLength; +} + +/** + * \ingroup gdcmHeader + * \brief gets the info from 0028,1101 : Lookup Table Desc-Red + * \ else 0 + * @return Lookup Table nBit + * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] + */ + +int gdcmHeader::GetLUTNbits(void) { + vector tokens; + //int LutLength; + //int LutDepth; + int LutNbits; + // Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue + // Consistency already checked in GetLUTLength + string LutDescription = GetPubElValByNumber(0x0028,0x1101); + if (LutDescription == "gdcm::Unfound") + return 0; + tokens.erase(tokens.begin(),tokens.end()); // clean any previous value + Tokenize (LutDescription, tokens, "\\"); + //LutLength=atoi(tokens[0].c_str()); + //LutDepth=atoi(tokens[1].c_str()); + LutNbits=atoi(tokens[2].c_str()); + tokens.clear(); + return LutNbits; +} + + +/** + * \ingroup gdcmHeader + * \brief gets the info from 0028,1201 : Lookup Table Red + * \ else 0 + * @return Lookup Table Red + * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] + */ +void * gdcmHeader::GetLUTRed(void) { + return GetPubElValVoidAreaByNumber(0x0028,0x1201); +} + +/** + * \ingroup gdcmHeader + * \brief gets the info from 0028,1202 : Lookup Table Green + * \ else 0 + * @return Lookup Table Red + * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] + */ + void * gdcmHeader::GetLUTGreen(void) { + return GetPubElValVoidAreaByNumber(0x0028,0x1202); +} + +/** + * \ingroup gdcmHeader + * \brief gets the info from 0028,1202 : Lookup Table Blue + * \ else 0 + * @return Lookup Table Blue + * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] + */ +void * gdcmHeader::GetLUTBlue(void) { + return GetPubElValVoidAreaByNumber(0x0028,0x1203); +} + +/** + * \ingroup gdcmHeader + * \brief + * @return Lookup Table RGB + * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] + * \ and (0028,1201),(0028,1202),(0028,1202) are found + * \warning : hazardous ! Use better GetPubElValVoidAreaByNumber + */ +void * gdcmHeader::GetLUTRGB(void) { +// Not so easy : see +// http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables +// and OT-PAL-8-face.dcm + + if (GetPubElValByNumber(0x0028,0x0004) == "gdcm::Unfound") { + dbg.Verbose(0, "gdcmHeader::GetLUTRGB: unfound Photometric Interpretation"); + return NULL; + } + void * LutR,*LutG,*LutB; + int l; + + // Maybe, some day we get an image + // that respects the definition ... + // Let's consider no ones does. + + l= GetLUTLength(); + if(l==0) + return (NULL); + int nBits=GetLUTNbits(); + // a virer quand on aura trouve UNE image + // qui correspond VRAIMENT à la definition ! + cout << "l " << l << " nBits " << nBits; + + l= l/(nBits/8); + + LutR =GetPubElValVoidAreaByNumber(0x0028,0x1201); + LutG =GetPubElValVoidAreaByNumber(0x0028,0x1202); + LutB =GetPubElValVoidAreaByNumber(0x0028,0x1203); + + // Warning : Any value for nBits as to be considered as 8 + // Any value for Length as to be considered as 256 + // That's DICOM ... + + // Just wait before removing the following code + /* + if (nBits == 16) { + guint16 * LUTRGB, *rgb; + LUTRGB = rgb = (guint16 *) malloc(3*l*sizeof( guint16)); + guint16 * r = (guint16 *)LutR; + guint16 * g = (guint16 *)LutG; + guint16 * b = (guint16 *)LutB; + for(int i=0;i #include "gdcmFile.h" +// for jpeglib defined BITS_IN_JSAMPLE +#include "jpeg/libijg8/jBitsInJsample.h" +// FIXME : find something else when both +// libJpeg8 and libJpeg12 will be active + #define DEBUG 0 +/* +DICOM provides a mechanism for supporting the use of JPEG Image Compression +through the Encapsulated Format (see PS 3.3 of the DICOM Standard). +Annex A defines a number of Transfer Syntaxes which reference +the JPEG Standard and provide a number of lossless (bit preserving) +and lossy compression schemes. +In order to facilitate interoperability of implementations conforming +to the DICOM Standard which elect to use one or more +of the Transfer Syntaxes for JPEG Image Compression, the following policy is specified: + + Any implementation which conforms to the DICOM Standard and has elected + to support any one of the Transfer Syntaxes for lossless JPEG Image Compression, + shall support the following lossless compression: + The subset (first-order horizontal prediction [Selection Value 1) of JPEG Process 14 + (DPCM, non-hierarchical with Huffman coding) (see Annex F of the DICOM Standard). + + Any implementation which conforms to the DICOM Standard and has elected + to support any one of the Transfer Syntaxes for 8-bit lossy JPEG Image Compression, + shall support the JPEG Baseline Compression (coding Process 1). + + Any implementation which conforms to the DICOM Standard and has elected + to support any one of the Transfer Syntaxes for 12-bit lossy JPEG Image Compression, + shall support the JPEG Compression Process 4. + +Note: The DICOM conformance statement shall differentiate between implementations +that can simply receive JPEG encoded images and those that can receive and process +JPEG encoded images (see PS 3.2 of the DICOM Standard). + +The use of the DICOM Encapsulated Format to support JPEG Compressed Pixel Data +implies that the Data Elements which are related to the Native Format Pixel Data encoding +(e.g. Bits Allocated, Bits Stored, High Bit, Pixel Representation, Rows, Columns, etc.) +shall contain values which are consistent with the characteristics +of the uncompressed pixel data from which the compressed Data Stream was derived. +The Pixel Data characteristics included in the JPEG Interchange Format +shall be used to decode the compressed data stream. + +Run Length Encoding Compression + +DICOM provides a mechanism for supporting the use of Run Length Encoding (RLE) +Compression which is a byte oriented lossless compression scheme through +the encapsulated Format (see PS 3.3 of this Standard). +Annex G of the DICOM Standard defines RLE Compression and its Transfer Syntax. + +Note: The RLE Compression algorithm described in Annex G +of the DICOM Standard is the compression used in +the TIFF 6.0 specification known as the "PackBits" scheme. + +The use of the DICOM Encapsulated Format to support RLE Compressed Pixel Data +implies that the Data Elements which are related to the Native Format Pixel Data encoding ( +e.g. Bits Allocated, Bits Stored, High Bit, Pixel Representation, Rows, Columns, etc.) +shall contain values which are consistent with the characteristics +of the uncompressed pixel data from which the compressed data is derived +*/ + /* * is used for the optional error recovery mechanism shown in * the second part of the example. @@ -121,6 +180,7 @@ char *pimage; JSAMPARRAY buffer; /* Output row buffer */ // rappel : + // ------ // typedef unsigned char JSAMPLE; // typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ // typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ @@ -221,6 +281,7 @@ if (DEBUG) { * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ + /* JSAMPLEs per row in output buffer */ row_stride = cinfo.output_width * cinfo.output_components; @@ -259,10 +320,15 @@ if (DEBUG) { //(void) jpeg_read_scanlines(&cinfo, pimage, 1); - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - memcpy( pimage, buffer[0],row_stride*2 ); // FIXME : *2 car 16 bits?!? - - pimage+=row_stride*2; // FIXME : *2 car 16 bits?!? + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + + if ( BITS_IN_JSAMPLE == 8) { + memcpy( pimage, buffer[0],row_stride); + pimage+=row_stride; + } else { + memcpy( pimage, buffer[0],row_stride*2 ); // FIXME : *2 car 16 bits?!? + pimage+=row_stride*2; // FIXME : *2 car 16 bits?!? + } } /* Step 7: Finish decompression */ @@ -323,3 +389,8 @@ if (DEBUG) printf("Entree Step 8\n"); */ + + + + + diff --git a/src/jpeg/Makefile.am b/src/jpeg/Makefile.am index f9ee3b82..7537bd08 100644 --- a/src/jpeg/Makefile.am +++ b/src/jpeg/Makefile.am @@ -1 +1,2 @@ -SUBDIRS = libijg8 +SUBDIRS = libijg12 \ + libijg8