X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;ds=sidebyside;f=src%2FgdcmHeader.cxx;h=faa95f7bccd4fcffd175453a3da4f9426066f316;hb=e0202ea1c0b395871073c56f06fbcd789d2056de;hp=6eeebe416a421f5327dd32076ca87adf9581ada7;hpb=ec2e18e4b67fae08879a7428560e1c021ed11d6d;p=gdcm.git diff --git a/src/gdcmHeader.cxx b/src/gdcmHeader.cxx index 6eeebe41..faa95f7b 100644 --- a/src/gdcmHeader.cxx +++ b/src/gdcmHeader.cxx @@ -2,6 +2,7 @@ #include "gdcm.h" #include +#include // For nthos: #ifdef _MSC_VER #include @@ -16,17 +17,6 @@ #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 -namespace Error { - struct FileReadError { - FileReadError(FILE* fp, const char* Mesg) { - if (feof(fp)) - dbg.Verbose(1, "EOF encountered :", Mesg); - if (ferror(fp)) - dbg.Verbose(1, "Error on reading :", Mesg); - } - }; -} - //FIXME: this looks dirty to me... #define str2num(str, typeNum) *((typeNum *)(str)) @@ -38,24 +28,33 @@ namespace Error { // je suis preneur VRHT * gdcmHeader::dicom_vr = (VRHT*)0; -gdcmDictSet* gdcmHeader::Dicts = new gdcmDictSet(); void gdcmHeader::Initialise(void) { if (!gdcmHeader::dicom_vr) InitVRDict(); - RefPubDict = gdcmHeader::Dicts->GetDefaultPublicDict(); + Dicts = new gdcmDictSet(); + RefPubDict = Dicts->GetDefaultPubDict(); RefShaDict = (gdcmDict*)0; } -gdcmHeader::gdcmHeader (const char* InFilename) { - SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_); - filename = InFilename; - Initialise(); - fp=fopen(InFilename,"rw"); - dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename); - ParseHeader(); + +gdcmHeader::gdcmHeader(const char *InFilename, bool exception_on_error) + throw(gdcmFileError) { + SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_); + filename = InFilename; + Initialise(); + fp=fopen(InFilename,"rb"); + if(exception_on_error) { + if(!fp) + throw gdcmFileError("gdcmHeader::gdcmHeader(const char *, bool)"); + } + else + dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename); + ParseHeader(); + AddAndDefaultElements(); } + gdcmHeader::~gdcmHeader (void) { fclose(fp); return; @@ -256,20 +255,7 @@ void gdcmHeader::GetPixels(size_t lgrTotale, void* _Pixels) { /** * \ingroup gdcmHeader * \brief Find the value representation of the current tag. - * - * @param sw code swap - * @param skippedLength pointeur sur nombre d'octets que l'on a saute qd - * la lecture est finie - * @param longueurLue pointeur sur longueur (en nombre d'octets) - * effectivement lue - * @return longueur retenue pour le champ */ - -// --> -// --> Oops -// --> C'etait la description de quoi, ca? -// --> - void gdcmHeader::FindVR( ElValue *ElVal) { if (filetype != ExplicitVR) return; @@ -521,18 +507,22 @@ guint32 gdcmHeader::FindLengthOB(void) { while ( ! FoundSequenceDelimiter) { g = ReadInt16(); n = ReadInt16(); + if (errno == 1) + return 0; TotalLength += 4; // We even have to decount the group and element if ( g != 0xfffe ) { dbg.Verbose(1, "gdcmHeader::FindLengthOB: ", "wrong group for an item sequence."); - throw Error::FileReadError(fp, "gdcmHeader::FindLengthOB"); + errno = 1; + return 0; } if ( n == 0xe0dd ) FoundSequenceDelimiter = true; else if ( n != 0xe000) { dbg.Verbose(1, "gdcmHeader::FindLengthOB: ", "wrong element for an item sequence."); - throw Error::FileReadError(fp, "gdcmHeader::FindLengthOB"); + errno = 1; + return 0; } ItemLength = ReadInt32(); TotalLength += ItemLength + 4; // We add 4 bytes since we just read @@ -595,15 +585,18 @@ 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 ( ! IsExplicitVRBigEndianTransferSyntax() ) - throw Error::FileReadError(fp, "gdcmHeader::FindLength"); + if ( ! IsExplicitVRBigEndianTransferSyntax() ) { + dbg.Verbose(0, "gdcmHeader::FindLength", "not explicit VR"); + errno = 1; + return; + } length16 = 4; SwitchSwapToBigEndian(); // Restore the unproperly loaded values i.e. the group, the element // and the dictionary entry depending on them. guint16 CorrectGroup = SwapShort(ElVal->GetGroup()); guint16 CorrectElem = SwapShort(ElVal->GetElement()); - gdcmDictEntry * NewTag = IsInDicts(CorrectGroup, CorrectElem); + gdcmDictEntry * NewTag = GetDictEntryByKey(CorrectGroup, CorrectElem); if (!NewTag) { // This correct tag is not in the dictionary. Create a new one. NewTag = new gdcmDictEntry(CorrectGroup, CorrectElem); @@ -695,12 +688,9 @@ void gdcmHeader::SetMaxSizeLoadElementValue(long NewSize) { /** * \ingroup gdcmHeader - * \brief Loads the element if it's size is not to big. - * @param ElVal Element whose value shall be loaded. - * @param MaxSize Size treshold above which the element value is not - * loaded in memory. The element value is allways loaded - * when MaxSize is equal to UINT32_MAX. - * @return + * \brief Loads the element content if it's length is not bigger + * than the value specified with + * gdcmHeader::SetMaxSizeLoadElementValue() */ void gdcmHeader::LoadElementValue(ElValue * ElVal) { size_t item_read; @@ -749,17 +739,14 @@ void gdcmHeader::LoadElementValue(ElValue * ElVal) { return; } - // Values bigger than specified are not loaded. - // - // En fait, c'est les elements dont la longueur est superieure - // a celle fixee qui ne sont pas charges - // + // The elements whose length is bigger than the specified upper bound + // are not loaded. Instead we leave a short notice of the offset of + // the element content and it's length. if (length > MaxSizeLoadElementValue) { ostringstream s; s << "gdcm::NotLoaded."; s << " Address:" << (long)ElVal->GetOffset(); s << " Length:" << ElVal->GetLength(); - //mesg += " Length:" + ElVal->GetLength(); ElVal->SetValue(s.str()); return; } @@ -783,11 +770,6 @@ 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"); @@ -798,7 +780,7 @@ void gdcmHeader::LoadElementValue(ElValue * ElVal) { item_read = fread(NewValue, (size_t)length, (size_t)1, fp); if ( item_read != 1 ) { free(NewValue); - Error::FileReadError(fp, "gdcmHeader::LoadElementValue"); + dbg.Verbose(1, "gdcmHeader::LoadElementValue","unread element value"); ElVal->SetValue("gdcm::UnRead"); return; } @@ -824,8 +806,12 @@ guint16 gdcmHeader::ReadInt16(void) { guint16 g; size_t item_read; item_read = fread (&g, (size_t)2,(size_t)1, fp); - if ( item_read != 1 ) - throw Error::FileReadError(fp, "gdcmHeader::ReadInt16"); + errno = 0; + if ( item_read != 1 ) { + dbg.Verbose(1, "gdcmHeader::ReadInt16", " File read error"); + errno = 1; + return 0; + } g = SwapShort(g); return g; } @@ -834,12 +820,62 @@ guint32 gdcmHeader::ReadInt32(void) { guint32 g; size_t item_read; item_read = fread (&g, (size_t)4,(size_t)1, fp); - if ( item_read != 1 ) - throw Error::FileReadError(fp, "gdcmHeader::ReadInt32"); + errno = 0; + if ( item_read != 1 ) { + dbg.Verbose(1, "gdcmHeader::ReadInt32", " File read error"); + errno = 1; + return 0; + } g = SwapLong(g); return g; } +/** + * \ingroup gdcmHeader + * \brief Build a new Element Value from all the low level arguments. + * Check for existence of dictionary entry, and build + * a default one when absent. + * @param Group group of the underlying DictEntry + * @param Elem element of the underlying DictEntry + */ +ElValue* gdcmHeader::NewElValueByKey(guint16 Group, guint16 Elem) { + // Find out if the tag we encountered is in the dictionaries: + gdcmDictEntry * NewTag = GetDictEntryByKey(Group, Elem); + if (!NewTag) + NewTag = new gdcmDictEntry(Group, Elem); + + ElValue* NewElVal = new ElValue(NewTag); + if (!NewElVal) { + dbg.Verbose(1, "gdcmHeader::NewElValueByKey", + "failed to allocate ElValue"); + return (ElValue*)0; + } + return NewElVal; +} + +/** + * \ingroup gdcmHeader + * \brief Build a new Element Value from all the low level arguments. + * Check for existence of dictionary entry, and build + * a default one when absent. + * @param Name Name of the underlying DictEntry + */ +ElValue* gdcmHeader::NewElValueByName(string Name) { + + gdcmDictEntry * NewTag = GetDictEntryByName(Name); + if (!NewTag) + NewTag = new gdcmDictEntry(0xffff, 0xffff, "LO", "Unknown", Name); + + ElValue* NewElVal = new ElValue(NewTag); + if (!NewElVal) { + dbg.Verbose(1, "gdcmHeader::ObtainElValueByName", + "failed to allocate ElValue"); + return (ElValue*)0; + } + return NewElVal; +} + + /** * \ingroup gdcmHeader * \brief Read the next tag without loading it's value @@ -851,32 +887,19 @@ ElValue * gdcmHeader::ReadNextElement(void) { guint16 n; ElValue * NewElVal; - try { - g = ReadInt16(); - n = ReadInt16(); - } - catch ( Error::FileReadError ) { + g = ReadInt16(); + n = ReadInt16(); + if (errno == 1) // We reached the EOF (or an error occured) and header parsing // has to be considered as finished. return (ElValue *)0; - } - - // Find out if the tag we encountered is in the dictionaries: - gdcmDictEntry * NewTag = IsInDicts(g, n); - if (!NewTag) - NewTag = new gdcmDictEntry(g, n); - - NewElVal = new ElValue(NewTag); - if (!NewElVal) { - dbg.Verbose(1, "ReadNextElement: failed to allocate ElValue"); - return (ElValue*)0; - } - + + NewElVal = NewElValueByKey(g, n); FindVR(NewElVal); - try { FindLength(NewElVal); } - catch ( Error::FileReadError ) { // Call it quits + FindLength(NewElVal); + if (errno == 1) + // Call it quits return (ElValue *)0; - } NewElVal->SetOffset(ftell(fp)); return NewElVal; } @@ -911,25 +934,9 @@ bool gdcmHeader::IsAnInteger(ElValue * ElVal) { return true; if ( (group == 0x0028) && (element == 0x0005) ) - // This tag is retained from ACR/NEMA - // CHECKME Why should "Image Dimensions" be a single integer ? - // - // "Image Dimensions", c'est en fait le 'nombre de dimensions' - // de l'objet ACR-NEMA stocké - // 1 : Signal - // 2 : Image - // 3 : Volume - // 4 : Sequence - // - // DICOM V3 ne retient pas cette information - // Par defaut, tout est 'Image', - // C'est a l'utilisateur d'explorer l'ensemble des entetes - // pour savoir à quoi il a a faire - // - // Le Dicom Multiframe peut etre utilise pour stocker, - // dans un seul fichier, une serie temporelle (cardio vasculaire GE, p.ex) - // ou un volume (medecine Nucleaire, p.ex) - // + // The "Image Dimensions" tag is retained from ACR/NEMA and contains + // the number of dimensions of the contained object (1 for Signal, + // 2 for Image, 3 for Volume, 4 for Sequence). return true; if ( (group == 0x0028) && (element == 0x0200) ) @@ -972,47 +979,60 @@ size_t gdcmHeader::GetPixelOffset(void) { return 0; } -gdcmDictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) { - // - // Y a-t-il une raison de lui passer des guint32 - // alors que group et element sont des guint16? - // +/** + * \ingroup gdcmHeader + * \brief Searches both the public and the shadow dictionary (when they + * exist) for the presence of the DictEntry with given + * group and element. The public dictionary has precedence on the + * shadow one. + * @param group group of the searched DictEntry + * @param element element of the searched DictEntry + * @return Corresponding DictEntry when it exists, NULL otherwise. + */ +gdcmDictEntry * gdcmHeader::GetDictEntryByKey(guint16 group, guint16 element) { gdcmDictEntry * found = (gdcmDictEntry*)0; if (!RefPubDict && !RefShaDict) { - //FIXME build a default dictionary ! - printf("FIXME in gdcmHeader::IsInDicts\n"); + dbg.Verbose(0, "FIXME in gdcmHeader::GetDictEntry", + "we SHOULD have a default dictionary"); } if (RefPubDict) { - found = RefPubDict->GetTag(group, element); + found = RefPubDict->GetTagByKey(group, element); if (found) return found; } if (RefShaDict) { - found = RefShaDict->GetTag(group, element); + found = RefShaDict->GetTagByKey(group, element); if (found) return found; } return found; } -list * gdcmHeader::GetPubTagNames(void) { - list * Result = new list; - TagHT entries = RefPubDict->GetEntries(); - - for (TagHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){ - Result->push_back( tag->second->GetName() ); +/** + * \ingroup gdcmHeader + * \brief Searches both the public and the shadow dictionary (when they + * exist) for the presence of the DictEntry with given name. + * The public dictionary has precedence on the shadow one. + * @earam Name name of the searched DictEntry + * @return Corresponding DictEntry when it exists, NULL otherwise. + */ +gdcmDictEntry * gdcmHeader::GetDictEntryByName(string Name) { + gdcmDictEntry * found = (gdcmDictEntry*)0; + if (!RefPubDict && !RefShaDict) { + dbg.Verbose(0, "FIXME in gdcmHeader::GetDictEntry", + "we SHOULD have a default dictionary"); } - return Result; -} - -map > * gdcmHeader::GetPubTagNamesByCategory(void) { - map > * Result = new map >; - TagHT entries = RefPubDict->GetEntries(); - - for (TagHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){ - (*Result)[tag->second->GetFourth()].push_back(tag->second->GetName()); + if (RefPubDict) { + found = RefPubDict->GetTagByName(Name); + if (found) + return found; + } + if (RefShaDict) { + found = RefShaDict->GetTagByName(Name); + if (found) + return found; } - return Result; + return found; } string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) { @@ -1059,7 +1079,6 @@ string gdcmHeader::GetShaElValRepByName(string TagName) { return elem->GetVR(); } - string gdcmHeader::GetElValByNumber(guint16 group, guint16 element) { string pub = GetPubElValByNumber(group, element); if (pub.length()) @@ -1090,59 +1109,65 @@ string gdcmHeader::GetElValRepByName(string TagName) { /** * \ingroup gdcmHeader - * \brief Modifie la valeur d'un ElValue déja existant - * \ dans le PubElVals du gdcmHeader, - * \ accédé par ses numero de groupe et d'element. + * \brief Accesses an existing ElValue in the PubElVals of this instance + * through it's (group, element) and modifies it's content with + * the given value. + * @param content new value to substitute with + * @param group group of the ElVal to modify + * @param element element of the ElVal to modify */ -int gdcmHeader::SetPubElValByNumber(string content, guint16 group, guint16 element) { - //TagKey key = gdcmDictEntry::TranslateToKey(group, element); - //PubElVals.tagHt[key]->SetValue(content); - +int gdcmHeader::SetPubElValByNumber(string content, guint16 group, + guint16 element) +{ + //CLEANME TagKey key = gdcmDictEntry::TranslateToKey(group, element); + //CLEANME PubElVals.tagHt[key]->SetValue(content); return ( PubElVals.SetElValueByNumber (content, group, element) ); } - /** * \ingroup gdcmHeader - * \brief Modifie la valeur d'un ElValue déja existant - * \ dans le PubElVals du gdcmHeader, - * \ accédé par son nom + * \brief Accesses an existing ElValue in the PubElVals of this instance + * through tag name and modifies it's content with the given value. + * @param content new value to substitute with + * @param TagName name of the tag to be modified */ int gdcmHeader::SetPubElValByName(string content, string TagName) { - //TagKey key = gdcmDictEntry::TranslateToKey(group, element); - //PubElVals.tagHt[key]->SetValue(content); - + //CLEANME TagKey key = gdcmDictEntry::TranslateToKey(group, element); + //CLEANME PubElVals.tagHt[key]->SetValue(content); return ( PubElVals.SetElValueByName (content, TagName) ); } - /** * \ingroup gdcmHeader - * \brief Modifie la valeur d'un ElValue déja existant - * \ dans le ShaElVals du gdcmHeader, - * \ accédé par ses numero de groupe et d'element. + * \brief Accesses an existing ElValue in the ShaElVals of this instance + * through it's (group, element) and modifies it's content with + * the given value. + * @param content new value to substitute with + * @param group group of the ElVal to modify + * @param element element of the ElVal to modify */ -int gdcmHeader::SetShaElValByNumber(string content, guint16 group, guint16 element) { - +int gdcmHeader::SetShaElValByNumber(string content, + guint16 group, guint16 element) +{ return ( ShaElVals.SetElValueByNumber (content, group, element) ); } - /** * \ingroup gdcmHeader - * \brief Modifie la valeur d'un ElValue déja existant - * \ dans le ShaElVals du gdcmHeader, - * \ accédé par son nom + * \brief Accesses an existing ElValue in the ShaElVals of this instance + * through tag name and modifies it's content with the given value. + * @param content new value to substitute with + * @param TagName name of the tag to be modified */ int gdcmHeader::SetShaElValByName(string content, string TagName) { - return ( ShaElVals.SetElValueByName (content, TagName) ); } + /** * \ingroup gdcmHeader - * \brief Parses the header of the file but does NOT load element values. + * \brief Parses the header of the file but WITHOUT loading element values. */ -void gdcmHeader::ParseHeader(void) { +void gdcmHeader::ParseHeader(bool exception_on_error) throw(gdcmFormatError) { ElValue * newElValue = (ElValue *)0; rewind(fp); @@ -1153,17 +1178,42 @@ void gdcmHeader::ParseHeader(void) { } } +/** + * \ingroup gdcmHeader + * \brief Once the header is parsed add some gdcm convenience/helper elements + * in the ElValSet. For example add: + * - gdcmImageType which is an entry containing a short for the + * type of image and whose value ranges in + * I8 (unsigned 8 bit image) + * I16 (unsigned 8 bit image) + * IS16 (signed 8 bit image) + * - gdcmXsize, gdcmYsize, gdcmZsize whose values are respectively + * the ones of the official DICOM fields Rows, Columns and Planes. + */ +void gdcmHeader::AddAndDefaultElements(void) { + ElValue* NewEntry = (ElValue*)0; + + NewEntry = NewElValueByName("gdcmXSize"); + NewEntry->SetValue(GetElValByName("Rows")); + PubElVals.Add(NewEntry); + + NewEntry = NewElValueByName("gdcmYSize"); + NewEntry->SetValue(GetElValByName("Columns")); + PubElVals.Add(NewEntry); + + NewEntry = NewElValueByName("gdcmZSize"); + NewEntry->SetValue(GetElValByName("Planes")); + PubElVals.Add(NewEntry); +} + /** * \ingroup gdcmHeader * \brief Loads the element values of all the elements present in the * public tag based hash table. */ void gdcmHeader::LoadElements(void) { - rewind(fp); - TagElValueHT ht = PubElVals.GetTagHt(); - for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag) { LoadElementValue(tag->second); }