X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmHeader.cxx;h=afe3ed329bf5ce308a6f188720a1b6501f4f4afa;hb=5581a576a5749e2335205ece834b92afad111ad8;hp=dd49a4593625189a8cc13dd27d70fca35f55f6a4;hpb=9d439450a43135ddd30761ed598dd2bbfcfe26b8;p=gdcm.git diff --git a/src/gdcmHeader.cxx b/src/gdcmHeader.cxx index dd49a459..afe3ed32 100644 --- a/src/gdcmHeader.cxx +++ b/src/gdcmHeader.cxx @@ -2,6 +2,7 @@ #include "gdcm.h" #include +#include // For nthos: #ifdef _MSC_VER #include @@ -13,22 +14,19 @@ #include #include "gdcmUtil.h" -#define HEADER_LENGTH_TO_READ 256 // on ne lit plus que le debut - -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); - } - }; -} +#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 //FIXME: this looks dirty to me... + #define str2num(str, typeNum) *((typeNum *)(str)) +// str est un pointeur dans un tableau de caractères, qui doit contenir, +// à cet endroit la, la représentation binaire d'un entier (16 ou 32 bits) +// je veux récupérer ça ... dans un entier. +// s'il y a une autre solution, évitant des cast et les indirections, +// je suis preneur + VRHT * gdcmHeader::dicom_vr = (VRHT*)0; gdcmDictSet* gdcmHeader::Dicts = new gdcmDictSet(); @@ -40,12 +38,13 @@ void gdcmHeader::Initialise(void) { } gdcmHeader::gdcmHeader (const char* InFilename) { - SetMaxSizeLoadElementValue(1024); + SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_); filename = InFilename; Initialise(); fp=fopen(InFilename,"rw"); dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename); ParseHeader(); + AddAndDefaultElements(); } gdcmHeader::~gdcmHeader (void) { @@ -116,7 +115,7 @@ void gdcmHeader::CheckSwap() net2host = false; // The easiest case is the one of a DICOM header, since it possesses a - // file preamble where it suffice to look for the sting "DICM". + // file preamble where it suffice to look for the string "DICM". lgrLue = fread(deb, 1, HEADER_LENGTH_TO_READ, fp); entCur = deb + 128; @@ -236,11 +235,11 @@ void gdcmHeader::SwitchSwapToBigEndian(void) { sw = 3412; } -void gdcmHeader::GetPixels(size_t lgrTotale, void* Pixels) { +void gdcmHeader::GetPixels(size_t lgrTotale, void* _Pixels) { size_t pixelsOffset; pixelsOffset = GetPixelOffset(); fseek(fp, pixelsOffset, SEEK_SET); - fread(Pixels, 1, lgrTotale, fp); + fread(_Pixels, 1, lgrTotale, fp); } @@ -490,6 +489,8 @@ bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) { // Il y en a encore DIX-SEPT, comme ça. // Il faudrait trouver qq chose + rusé ... // +// --> probablement TOUS les supprimer (Eric dixit) +// void gdcmHeader::FixFoundLength(ElValue * ElVal, guint32 FoundLength) { @@ -511,18 +512,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 @@ -570,7 +575,7 @@ void gdcmHeader::FindLength(ElValue * ElVal) { // in little endian, and big endian coding only starts at the next // group. The corresponding code can be hard to analyse and adds // many additional unnecessary tests for regular tags. - // * the second strategy consist in waiting for trouble, that shall appear + // * the second strategy consists in waiting for trouble, that shall appear // when we find the first group with big endian encoding. This is // easy to detect since the length of a "Group Length" tag (the // ones with zero as element number) has to be of 4 (0x0004). When we @@ -585,15 +590,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); @@ -721,7 +729,9 @@ void gdcmHeader::LoadElementValue(ElValue * ElVal) { // The group length doesn't represent data to be loaded in memory, since // each element of the group shall be loaded individualy. if( elem == 0 ) - SkipLoad = true; + //SkipLoad = true; // modif sauvage JPR + // On charge la longueur du groupe + // quand l'element 0x0000 est présent ! if ( SkipLoad ) { // FIXME the following skip is not necessary @@ -771,11 +781,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"); @@ -786,7 +791,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; } @@ -812,8 +817,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; } @@ -822,12 +831,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 @@ -839,32 +898,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; } @@ -899,25 +945,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) ) @@ -942,7 +972,7 @@ size_t gdcmHeader::GetPixelOffset(void) { guint16 grPixel; guint16 numPixel; string ImageLocation = GetPubElValByName("Image Location"); - if ( ImageLocation == "UNFOUND" ) { + if ( ImageLocation == "gdcm::Unfound" ) { grPixel = 0x7fe0; } else { grPixel = (guint16) atoi( ImageLocation.c_str() ); @@ -960,23 +990,56 @@ 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 + * @earam 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) { + dbg.Verbose(0, "FIXME in gdcmHeader::GetDictEntry", + "we SHOULD have a default dictionary"); + } + if (RefPubDict) { + found = RefPubDict->GetTagByKey(group, element); + if (found) + return found; + } + if (RefShaDict) { + found = RefShaDict->GetTagByKey(group, element); + if (found) + return found; + } + return found; +} + +/** + * \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) { - //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->GetTagByName(Name); if (found) return found; } if (RefShaDict) { - found = RefShaDict->GetTag(group, element); + found = RefShaDict->GetTagByName(Name); if (found) return found; } @@ -985,9 +1048,9 @@ gdcmDictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) { list * gdcmHeader::GetPubTagNames(void) { list * Result = new list; - TagHT entries = RefPubDict->GetEntries(); + TagKeyHT entries = RefPubDict->GetEntries(); - for (TagHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){ + for (TagKeyHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){ Result->push_back( tag->second->GetName() ); } return Result; @@ -995,9 +1058,9 @@ list * gdcmHeader::GetPubTagNames(void) { map > * gdcmHeader::GetPubTagNamesByCategory(void) { map > * Result = new map >; - TagHT entries = RefPubDict->GetEntries(); + TagKeyHT entries = RefPubDict->GetEntries(); - for (TagHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){ + for (TagKeyHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){ (*Result)[tag->second->GetFourth()].push_back(tag->second->GetName()); } return Result; @@ -1047,7 +1110,6 @@ string gdcmHeader::GetShaElValRepByName(string TagName) { return elem->GetVR(); } - string gdcmHeader::GetElValByNumber(guint16 group, guint16 element) { string pub = GetPubElValByNumber(group, element); if (pub.length()) @@ -1078,7 +1140,63 @@ string gdcmHeader::GetElValRepByName(string TagName) { /** * \ingroup gdcmHeader - * \brief Parses the header of the file but does NOT load element values. + * \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) +{ + //CLEANME TagKey key = gdcmDictEntry::TranslateToKey(group, element); + //CLEANME PubElVals.tagHt[key]->SetValue(content); + return ( PubElVals.SetElValueByNumber (content, group, element) ); +} + +/** + * \ingroup gdcmHeader + * \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) { + //CLEANME TagKey key = gdcmDictEntry::TranslateToKey(group, element); + //CLEANME PubElVals.tagHt[key]->SetValue(content); + return ( PubElVals.SetElValueByName (content, TagName) ); +} + +/** + * \ingroup gdcmHeader + * \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) +{ + return ( ShaElVals.SetElValueByNumber (content, group, element) ); +} + +/** + * \ingroup gdcmHeader + * \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 WITHOUT loading element values. */ void gdcmHeader::ParseHeader(void) { ElValue * newElValue = (ElValue *)0; @@ -1091,16 +1209,45 @@ 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); + rewind(fp); TagElValueHT ht = PubElVals.GetTagHt(); - for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag) + for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag) { LoadElementValue(tag->second); + } } void gdcmHeader::PrintPubElVal(ostream & os) {