-#define HEADER_LENGHT_TO_READ 256 // on ne lit plus que le debut
-
-//FIXME: this looks dirty to me...
-#define str2num(str, typeNum) *((typeNum *)(str))
-
-VRHT * gdcmHeader::dicom_vr = (VRHT*)0;
-gdcmDictSet* gdcmHeader::Dicts = new gdcmDictSet();
-
-void gdcmHeader::Initialise(void) {
- if (!gdcmHeader::dicom_vr)
- InitVRDict();
- PixelPosition = (size_t)0;
- PixelsTrouves = false;
- RefPubDict = gdcmHeader::Dicts->GetDefaultPublicDict();
- RefShaDict = (gdcmDict*)0;
-}
-
-gdcmHeader::gdcmHeader (char* InFilename) {
- filename = InFilename;
- Initialise();
- fp=fopen(InFilename,"rw");
- dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename);
- BuildHeader();
- fclose(fp);
-}
-
-gdcmHeader::~gdcmHeader (void) {
- return;
-}
-
-void gdcmHeader::InitVRDict (void) {
- if (dicom_vr) {
- dbg.Verbose(0, "gdcmHeader::InitVRDict:", "VR dictionary allready set");
- return;
- }
- VRHT *vr = new VRHT;
- (*vr)["AE"] = "Application Entity"; // 16 car max
- (*vr)["AS"] = "Age String"; // 4 car fixe
- (*vr)["AT"] = "Attribute Tag"; // 2 unsigned short int
- (*vr)["CS"] = "Code String"; // 16 car max
- (*vr)["DA"] = "Date"; // 8 car fixe
- (*vr)["DS"] = "Decimal String"; // Decimal codé Binaire 16 max
- (*vr)["DT"] = "Date Time"; // 26 car max
- (*vr)["FL"] = "Floating Point Single"; // 4 octets IEEE 754:1985
- (*vr)["FD"] = "Floating Point Double"; // 8 octets IEEE 754:1985
- (*vr)["IS"] = "Integer String"; // en format externe 12 max
- (*vr)["LO"] = "Long String"; // 64 octets max
- (*vr)["LT"] = "Long Text"; // 10240 max
- (*vr)["OB"] = "Other Byte String";
- (*vr)["OW"] = "Other Word String";
- (*vr)["PN"] = "Person Name";
- (*vr)["SH"] = "Short String"; // 16 car max
- (*vr)["SL"] = "Signed Long";
- (*vr)["SQ"] = "Sequence of Items"; // Not Applicable
- (*vr)["SS"] = "Signed Short"; // 2 octets
- (*vr)["ST"] = "Short Text"; // 1024 car max
- (*vr)["TM"] = "Time"; // 16 car max
- (*vr)["UI"] = "Unique Identifier"; // 64 car max
- (*vr)["UN"] = "Unknown";
- (*vr)["UT"] = "Unlimited Text"; // 2 puissance 32 -1 car max
- (*vr)["UL"] = "Unsigned Long "; // 4 octets fixe
- (*vr)["US"] = "Unsigned Short "; // 2 octets fixe
- dicom_vr = vr;
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief La seule maniere sure que l'on aie pour determiner
- * si on est en LITTLE_ENDIAN, BIG-ENDIAN,
- * BAD-LITTLE-ENDIAN, BAD-BIG-ENDIAN
- * est de trouver l'element qui donne la longueur d'un 'GROUP'
- * (on sait que la longueur de cet element vaut 0x00000004)
- * et de regarder comment cette longueur est codee en memoire
- *
- * Le probleme vient de ce que parfois, il n'y en a pas ...
- *
- * On fait alors le pari qu'on a a faire a du LITTLE_ENDIAN propre.
- * (Ce qui est la norme -pas respectee- depuis ACR-NEMA)
- * Si ce n'est pas le cas, on ne peut rien faire.
- *
- * (il faudrait avoir des fonctions auxquelles
- * on passe le code Swap en parametre, pour faire des essais 'manuels')
- */
-void gdcmHeader::CheckSwap()
-{
- guint32 s;
- guint32 x=4; // x : pour ntohs
- bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
-
- int lgrLue;
- char * entCur;
- char deb[HEADER_LENGHT_TO_READ];
-
- // First, compare HostByteOrder and NetworkByteOrder in order to
- // determine if we shall need to swap bytes (i.e. the Endian type).
- if (x==ntohs(x))
- net2host = true;
- else
- 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".
- lgrLue = fread(deb, 1, HEADER_LENGHT_TO_READ, fp);
-
- entCur = deb + 128;
- if(memcmp(entCur, "DICM", (size_t)4) == 0) {
- filetype = TrueDicom;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:", "looks like DICOM Version3");
- } else {
- filetype = Unknown;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:", "not a DICOM Version3 file");
- }
-
- if(filetype == TrueDicom) {
- // Next, determine the value representation (VR). Let's skip to the
- // first element (0002, 0000) and check there if we find "UL", in
- // which case we (almost) know it is explicit VR.
- // WARNING: if it happens to be implicit VR then what we will read
- // is the length of the group. If this ascii representation of this
- // length happens to be "UL" then we shall believe it is explicit VR.
- // FIXME: in order to fix the above warning, we could read the next
- // element value (or a couple of elements values) in order to make
- // sure we are not commiting a big mistake.
- // We need to skip :
- // * the 128 bytes of File Preamble (often padded with zeroes),
- // * the 4 bytes of "DICM" string,
- // * the 4 bytes of the first tag (0002, 0000),
- // i.e. a total of 136 bytes.
- entCur = deb + 136;
- if(memcmp(entCur, "UL", (size_t)2) == 0) {
- filetype = ExplicitVR;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "explicit Value Representation");
- } else {
- filetype = ImplicitVR;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "not an explicit Value Representation");
- }
-
- if (net2host) {
- sw = 4321;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "HostByteOrder != NetworkByteOrder");
- } else {
- sw = 0;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "HostByteOrder = NetworkByteOrder");
- }
-
- // Position the file position indicator at first tag (i.e.
- // after the file preamble and the "DICM" string).
- rewind(fp);
- fseek (fp, 132L, SEEK_SET);
- return;
- } // End of TrueDicom
-
- // Alas, this is not a DicomV3 file and whatever happens there is no file
- // preamble. We can reset the file position indicator to where the data
- // is (i.e. the beginning of the file).
- rewind(fp);
-
- // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
- // By clean we mean that the length of the first tag is written down.
- // If this is the case and since the length of the first group HAS to be
- // four (bytes), then determining the proper swap code is straightforward.
-
- entCur = deb + 4;
- s = str2num(entCur, int);
-
- switch (s) {
- case 0x00040000 :
- sw=3412;
- filetype = ACR;
- return;
- case 0x04000000 :
- sw=4321;
- filetype = ACR;
- return;
- case 0x00000400 :
- sw=2143;
- filetype = ACR;
- return;
- case 0x00000004 :
- sw=0;
- filetype = ACR;
- return;
- default :
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "ACE/NEMA unfound swap info (time to raise bets)");
- }
-
- // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
- // It is time for despaired wild guesses. So, let's assume this file
- // happens to be 'dirty' ACR/NEMA, i.e. the length of the group it
- // not present. Then the only info we have is the net2host one.
- //FIXME Si c'est du RAW, ca degagera + tard
-
- if (! net2host )
- sw = 0;
- else
- sw = 4321;
- return;
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief recupere la longueur d'un champ DICOM.
- * Preconditions:
- * 1/ le fichier doit deja avoir ete ouvert,
- * 2/ CheckSwap() doit avoir ete appele
- * 3/ la partie 'group' ainsi que la partie 'elem'
- * de l'acr_element doivent avoir ete lues.
- *
- * ACR-NEMA : we allways get
- * GroupNumber (2 Octets)
- * ElementNumber (2 Octets)
- * ElementSize (4 Octets)
- * DICOM en implicit Value Representation :
- * GroupNumber (2 Octets)
- * ElementNumber (2 Octets)
- * ElementSize (4 Octets)
- *
- * DICOM en explicit Value Representation :
- * GroupNumber (2 Octets)
- * ElementNumber (2 Octets)
- * ValueRepresentation (2 Octets)
- * ElementSize (2 Octets)
- *
- * ATTENTION : dans le cas ou ValueRepresentation = OB, OW, SQ, UN
- * GroupNumber (2 Octets)
- * ElementNumber (2 Octets)
- * ValueRepresentation (2 Octets)
- * zone reservee (2 Octets)
- * ElementSize (4 Octets)
- *
- * @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
- */
-
-void gdcmHeader::FindVR( ElValue *pleCourant) {
- char VR[3];
- int lgrLue;
- long PositionOnEntry = ftell(fp);
-
- if (filetype != ExplicitVR)
- return;
-
- lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
- VR[2]=0;
-
- // Warning: we believe this is explicit VR (Value Representation) because
- // we used a heuristic that found "UL" in the first tag. Alas this
- // doesn't guarantee that all the tags will be in explicit VR. In some
- // cases (see e-film filtered files) one finds implicit VR tags mixed
- // within an explicit VR file. Hence we make sure the present tag
- // is in explicit VR and try to fix things if it happens not to be
- // the case.
-
- // FIXME There should be only one occurence returned. Avoid the
- // first extraction by calling proper method.
- VRAtr FoundVR = dicom_vr->find(string(VR))->first;
- if ( ! FoundVR.empty()) {
- pleCourant->SetVR(FoundVR);
- return;
- }
-
- // We thought this was explicit VR, but we end up with an
- // implicit VR tag. Let's backtrack.
- pleCourant->SetVR("Implicit");
- fseek(fp, PositionOnEntry, SEEK_SET);
-}
-
-void gdcmHeader::FindLength( ElValue *pleCourant) {
- int lgrLue;
- guint32 l_gr;
- unsigned short int l_gr_2;
-
- string vr = pleCourant->GetVR();
-
- if ( (filetype == ExplicitVR) && (vr != "Implicit") ) {
- if ( ( vr == "OB" ) || ( vr == "OW" )
- || ( vr == "SQ" ) || ( vr == "UN" ) ) {
-
- // The following two bytes are reserved, so we skip them,
- // and we proceed on reading the length on 4 bytes.
- fseek(fp, 2L,SEEK_CUR);
- lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
- l_gr = SwapLong((guint32)l_gr);
-
- } else {
- // Length is encoded on 2 bytes.
- lgrLue=fread (&l_gr_2, (size_t)2,(size_t)1, fp);
-
- l_gr_2 = SwapShort((unsigned short)l_gr_2);
-
- if ( l_gr_2 == 0xffff) {
- l_gr = 0;
- } else {
- l_gr = l_gr_2;
- }
- }
- } else {
- // Either implicit VR or an explicit VR that (at least for this
- // element) lied a little bit. Length is on 4 bytes.
- lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
- l_gr= SwapLong((long)l_gr);
- }
-
- // Traitement des curiosites sur la longueur
- if ( l_gr == 0xffffffff)
- l_gr=0;
-
- pleCourant->SetLength(l_gr);
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief remet les octets dans un ordre compatible avec celui du processeur
-
- * @return longueur retenue pour le champ
- */
-guint32 gdcmHeader::SwapLong(guint32 a) {
- // FIXME: il pourrait y avoir un pb pour les entiers negatifs ...
- switch (sw) {
- case 0 :
- break;
- case 4321 :
- a=( ((a<<24) & 0xff000000) | ((a<<8) & 0x00ff0000) |
- ((a>>8) & 0x0000ff00) | ((a>>24) & 0x000000ff) );
- break;
-
- case 3412 :
- a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
- break;
-
- case 2143 :
- a=( ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff) );
- break;
- default :
- dbg.Error(" gdcmHeader::SwapLong : unset swap code");
- a=0;
- }
- return(a);
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief remet les octets dans un ordre compatible avec celui du processeur
-
- * @return longueur retenue pour le champ
- */
-short int gdcmHeader::SwapShort(short int a) {
- if ( (sw==4321) || (sw==2143) )
- a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
- return (a);
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief lit le dicom_element suivant.
- * (le fichier doit deja avoir ete ouvert,
- * _IdAcrCheckSwap(ID_DCM_HDR *e) avoir ete appele)
- * @param e ID_DCM_HDR dans lequel effectuer la recherche.
- * @param sw code swap.
- * @return En cas de succes, 1
- * 0 en cas d'echec.
- */
-
-ElValue * gdcmHeader::ReadNextElement(void) {
- guint16 g;
- guint16 n;
- guint32 l;
- size_t lgrLue;
- ElValue * nouvDcmElem;
-
- // ------------------------- Lecture Num group : g
- lgrLue=fread (&g, (size_t)2,(size_t)1, fp);
-
- if (feof(fp)) {
- dbg.Verbose(1, "ReadNextElement: EOF encountered");
- return (NULL);
- }
- if (ferror(fp)){
- dbg.Verbose(1, "ReadNextElement: failed to read NumGr");
- return (NULL);
- }
-
- if (sw) g= SwapShort(((short)g));
-
- // ------------------------- Lecture Num Elem : n
- lgrLue=fread (&n, (size_t)2,(size_t)1, fp);
-
- if (feof(fp)) {
- dbg.Verbose(1, "ReadNextElement: EOF encountered");
- return (NULL);
- }
- if (ferror(fp)){
- dbg.Verbose(1, "ReadNextElement: failed to read NumElem");
- return (NULL);
- }
-
- if(sw) n= SwapShort(((short)n));
-
- // Find out if the tag we encountered is in the dictionaries:
- gdcmDictEntry * NewTag = IsInDicts(g, n);
- if (!NewTag)
- NewTag = new gdcmDictEntry(g, n, "Unknown", "Unknown", "Unkown");
-
- nouvDcmElem = new ElValue(NewTag);
- if (!nouvDcmElem) {
- dbg.Verbose(1, "ReadNextElement: failed to allocate ElValue");
- return(NULL);
- }
-
- FindVR(nouvDcmElem);
- FindLength(nouvDcmElem);
- nouvDcmElem->SetOffset(ftell(fp));
- l = nouvDcmElem->GetLength();
-
- //FIXMEif(!memcmp( VR,"SQ",(size_t)2 )) { // ca annonce une SEQUENCE d'items ?!
- //FIXME l_gr=0; // on lira donc les items de la sequence
- //FIXME}
- //FIXMEreturn(l_gr);
-
- // Une sequence contient un ensemble de group element repetes n fois
- // et g=fffe indique la fin (contient une longueur bidon).
- if(g==0xfffe) l=0; // pour sauter les indicateurs de 'SQ'
-
-
- // ------------------------- Lecture Valeur element
-
- // FIXME The exact size should be l if we move to strings or whatever
- // CLEAN ME NEWValue used to be nouvDcmElem->valeurElem
- char* NewValue = (char*)g_malloc(l+1);
- if(NewValue) {
- NewValue[l]= 0;
- } else {
- return (NULL);
- }
-
- // FIXME les elements trop long (seuil a fixer a la main) ne devraient
- // pas etre charge's !!!! Voir TODO.
- lgrLue=fread (NewValue, (size_t)l,(size_t)1, fp);
-
- if ( IsAnInteger(g, n, NewTag->GetVR(), l) ) {
- // CLEANME THe following is really UGLY !
- if( l == 4 ) {
- *(guint32 *) NewValue = SwapLong ((*(guint32 *) NewValue));
- } else {
- if( l == 2 )
- *(guint16 *) NewValue = SwapShort ((*(guint16 *)NewValue));
- }
- //FIXME: don't we have to distinguish guin16 and guint32
- //FIXME: make the following an util fonction
- ostringstream s;
- s << *(guint32 *) NewValue;
- nouvDcmElem->value = s.str();
- g_free(NewValue);
- } else
- nouvDcmElem->value = NewValue;
- return nouvDcmElem;
-}
-
-bool gdcmHeader::IsAnInteger(guint16 group, guint16 element,
- string vr, guint32 length ) {
- // When we have some semantics on the element we just read, and we
- // a priori now we are dealing with an integer, then we can swap it's
- // element value properly.
- if ( element == 0 ) { // This is the group length of the group
- if (length != 4)
- dbg.Error("gdcmHeader::ShouldBeSwaped", "should be four");
- return true;
- }
-
- if ( group % 2 != 0 )
- // We only have some semantics on documented elements, which are
- // the even ones.
- return false;
-
- if ( (length != 4) && ( length != 2) )
- // Swapping only make sense on integers which are 2 or 4 bytes long.
- return false;
-
- if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
- return true;
-
- if ( (group == 0x0028) && (element == 0x0005) )
- // This tag is retained from ACR/NEMA
- // CHECKME Why should "Image Dimensions" be a single integer ?
- return true;
-
- if ( (group == 0x0028) && (element == 0x0200) )
- // This tag is retained from ACR/NEMA
- return true;
-
- return false;
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief If we encountered the offset of the pixels in the file
- * (Pixel Data) then keep the info aside.
- */
-void gdcmHeader::SetAsidePixelData(ElValue* elem) {
- // They are two cases :
- // * the pixel data (i.e. the image or the volume) is pointed by it's
- // default official tag (0x7fe0,0x0010),
- // * the writer of this file decided to put the image "address" (i.e the
- // offset from the begining of the file) at a different tag.
- // Then the "Pixel Data" offset might be found by indirection through
- // the "Image Location" tag (0x0028, 0x0200). In other terms the Image
- // Location tag contains the group where the "Pixel Data" offset is and
- // inside this group the element is conventionally at element 0x0010
- // (when the norm is respected).
- //
- // Hence getting our hands on the Pixel Data is a two stage process:
- // 1/ * find if the "Pixel Data" tag exists.
- // * if it does not exist, look for the "Pixel Location" tag.
- // 2/ look at the proper tag ("Pixel Data" or "Pixel Location" when
- // it exists) what the offset is.
- cout << "aaaaaaaaaaaaaaaaaaaaa";
- // PubElVals.PrintByName(cout);
- ostringstream val;
- val << hex << GetPubElValByName("Image Location");
- cout << GetPubElValByName("Image Location") << endl;
- cout <<hex << GetPubElValByName("Image Location") << dec << endl;
- cout << "aaaa" << hex << val << dec << endl;
- if (val)
- //grPixel = val.hex().str();
- grPixel = 0;
- else
- grPixel = 0x7FE0;
- return;
-
- guint16 g;
- guint16 n;
- g = elem->GetGroup();
- n = elem->GetElement();
- if (!grPixelTrouve) { // on n a pas encore trouve les pixels
- if (g > 0x0028) {
- if (n > 0x0200 || g == 0x7FE0 ) { // on a depasse (28,200)
- grPixel = 0x7FE0;
- numPixel = 0x0010;
- grPixelTrouve = true;
- }
- } else { // on est sur (28,200)
- if (g == 0x0028) {
- if (n == 0x0200) {
- grPixelTrouve = 1;
- char* NewValue = (char*)g_malloc(elem->GetLength()+1);
- // FIXME: not very elegant conversion
- for(int i=0;i<4;i++)
- *((char*)(&grPixel)+i) = *(NewValue+i);
- elem->SetValue(NewValue);
-
- if (grPixel != 0x7FE0) // Vieux pb Philips
- numPixel = 0x1010; // encore utile ??
- else
- numPixel = 0x0010;
- }
- }
- }
- } else { // on vient de trouver les pixels
- if (g == grPixel) {
- if (n == numPixel) {
- PixelPosition = elem->Offset;
- PixelsTrouves = true;
- dbg.Verbose(0, "gdcmHeader::SetAsidePixelData:",
- "Pixel data found");
- }
- }
- }
-}
-
-gdcmDictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) {
- gdcmDictEntry * found = (gdcmDictEntry*)0;
- if (!RefPubDict && !RefShaDict) {
- //FIXME build a default dictionary !
- printf("FIXME in gdcmHeader::IsInDicts\n");
- }
- if (RefPubDict) {
- found = RefPubDict->GetTag(group, element);
- if (found)
- return found;
- }
- if (RefShaDict) {
- found = RefShaDict->GetTag(group, element);
- if (found)
- return found;
- }
- return found;
-}
-
-string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) {
- return PubElVals.GetElValue(group, element);
-}
-
-string gdcmHeader::GetPubElValByName(string TagName) {
- return PubElVals.GetElValue(TagName);
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief renvoie un pointeur sur le ID_DCM_HDR correspondant au fichier
- * @param filename Nom du fichier ACR / LibIDO / DICOM
- * @return le ID_DCM_HDR
+ strImPos = GetEntryByNumber(0x0020,0x0030); // For ACR-NEMA images
+ if ( strImPos != GDCM_UNFOUND )
+ {
+ if( sscanf( strImPos.c_str(),
+ "%f\\%f\\%f", &xImPos, &yImPos, &zImPos ) != 3 )
+ {
+ dbg.Verbose(0, "Header::GetZImagePosition: wrong Image Position (RET) (0020,0030)");
+ return 0.; // bug in the element 0x0020,0x0032
+ }
+ else
+ {
+ return zImPos;
+ }
+ }
+
+ std::string strSliceLocation = GetEntryByNumber(0x0020,0x1041); // for *very* old ACR-NEMA images
+ if ( strSliceLocation != GDCM_UNFOUND )
+ {
+ if( sscanf( strSliceLocation.c_str(), "%f", &zImPos) != 1)
+ {
+ dbg.Verbose(0, "Header::GetZImagePosition: wrong Slice Location (0020,1041)");
+ return 0.; // bug in the element 0x0020,0x1041
+ }
+ else
+ {
+ return zImPos;
+ }
+ }
+ dbg.Verbose(0, "Header::GetZImagePosition: unfound Slice Location (0020,1041)");
+
+ std::string strLocation = GetEntryByNumber(0x0020,0x0050);
+ if ( strLocation != GDCM_UNFOUND )
+ {
+ if( sscanf( strLocation.c_str(), "%f", &zImPos) != 1)
+ {
+ dbg.Verbose(0, "Header::GetZImagePosition: wrong Location (0020,0050)");
+ return 0.; // bug in the element 0x0020,0x0050
+ }
+ else
+ {
+ return zImPos;
+ }
+ }
+ dbg.Verbose(0, "Header::GetYImagePosition: unfound Location (0020,0050)");
+
+ return 0.; // Hopeless
+}
+
+/**
+ * \brief gets the info from 0020,0013 : Image Number else 0.
+ * @return image number
+ */
+int Header::GetImageNumber()
+{
+ // The function i atoi() takes the address of an area of memory as
+ // parameter and converts the string stored at that location to an integer
+ // using the external decimal to internal binary conversion rules. This may
+ // be preferable to sscanf() since atoi() is a much smaller, simpler and
+ // faster function. sscanf() can do all possible conversions whereas
+ // atoi() can only do single decimal integer conversions.
+ //0020 0013 IS REL Image Number
+ std::string strImNumber = GetEntryByNumber(0x0020,0x0013);
+ if ( strImNumber != GDCM_UNFOUND )
+ {
+ return atoi( strImNumber.c_str() );
+ }
+ return 0; //Hopeless
+}
+
+/**
+ * \brief gets the info from 0008,0060 : Modality
+ * @return Modality Type
+ */
+ModalityType Header::GetModality()
+{
+ // 0008 0060 CS ID Modality
+ std::string strModality = GetEntryByNumber(0x0008,0x0060);
+ if ( strModality != GDCM_UNFOUND )
+ {
+ if ( strModality.find("AU") < strModality.length()) return AU;
+ else if ( strModality.find("AS") < strModality.length()) return AS;
+ else if ( strModality.find("BI") < strModality.length()) return BI;
+ else if ( strModality.find("CF") < strModality.length()) return CF;
+ else if ( strModality.find("CP") < strModality.length()) return CP;
+ else if ( strModality.find("CR") < strModality.length()) return CR;
+ else if ( strModality.find("CT") < strModality.length()) return CT;
+ else if ( strModality.find("CS") < strModality.length()) return CS;
+ else if ( strModality.find("DD") < strModality.length()) return DD;
+ else if ( strModality.find("DF") < strModality.length()) return DF;
+ else if ( strModality.find("DG") < strModality.length()) return DG;
+ else if ( strModality.find("DM") < strModality.length()) return DM;
+ else if ( strModality.find("DS") < strModality.length()) return DS;
+ else if ( strModality.find("DX") < strModality.length()) return DX;
+ else if ( strModality.find("ECG") < strModality.length()) return ECG;
+ else if ( strModality.find("EPS") < strModality.length()) return EPS;
+ else if ( strModality.find("FA") < strModality.length()) return FA;
+ else if ( strModality.find("FS") < strModality.length()) return FS;
+ else if ( strModality.find("HC") < strModality.length()) return HC;
+ else if ( strModality.find("HD") < strModality.length()) return HD;
+ else if ( strModality.find("LP") < strModality.length()) return LP;
+ else if ( strModality.find("LS") < strModality.length()) return LS;
+ else if ( strModality.find("MA") < strModality.length()) return MA;
+ else if ( strModality.find("MR") < strModality.length()) return MR;
+ else if ( strModality.find("NM") < strModality.length()) return NM;
+ else if ( strModality.find("OT") < strModality.length()) return OT;
+ else if ( strModality.find("PT") < strModality.length()) return PT;
+ else if ( strModality.find("RF") < strModality.length()) return RF;
+ else if ( strModality.find("RG") < strModality.length()) return RG;
+ else if ( strModality.find("RTDOSE") < strModality.length()) return RTDOSE;
+ else if ( strModality.find("RTIMAGE") < strModality.length()) return RTIMAGE;
+ else if ( strModality.find("RTPLAN") < strModality.length()) return RTPLAN;
+ else if ( strModality.find("RTSTRUCT") < strModality.length()) return RTSTRUCT;
+ else if ( strModality.find("SM") < strModality.length()) return SM;
+ else if ( strModality.find("ST") < strModality.length()) return ST;
+ else if ( strModality.find("TG") < strModality.length()) return TG;
+ else if ( strModality.find("US") < strModality.length()) return US;
+ else if ( strModality.find("VF") < strModality.length()) return VF;
+ else if ( strModality.find("XA") < strModality.length()) return XA;
+ else if ( strModality.find("XC") < strModality.length()) return XC;
+
+ else
+ {
+ /// \todo throw error return value ???
+ /// specified <> unknow in our database
+ return Unknow;
+ }
+ }
+
+ return Unknow;
+}
+
+/**
+ * \brief Retrieve the number of Bits Stored (actually used)
+ * (as opposite to number of Bits Allocated)
+ * @return The encountered number of Bits Stored, 0 by default.
+ * 0 means the file is NOT USABLE. The caller has to check it !
+ */
+int Header::GetBitsStored()
+{
+ std::string strSize = GetEntryByNumber( 0x0028, 0x0101 );
+ if ( strSize == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::GetBitsStored: this is supposed to "
+ "be mandatory");
+ return 0; // It's supposed to be mandatory
+ // the caller will have to check
+ }
+ return atoi( strSize.c_str() );
+}
+
+/**
+ * \brief Retrieve the high bit position.
+ * \warning The method defaults to 0 when information is absent.
+ * The responsability of checking this value is left to the caller.
+ * @return The high bit positin when present. 0 when absent.
+ */
+int Header::GetHighBitPosition()
+{
+ std::string strSize = GetEntryByNumber( 0x0028, 0x0102 );
+ if ( strSize == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::GetHighBitPosition: this is supposed "
+ "to be mandatory");
+ return 0;
+ }
+ return atoi( strSize.c_str() );
+}
+
+/**
+ * \brief Check wether the pixels are signed or UNsigned data.
+ * \warning The method defaults to false (UNsigned) when information is absent.
+ * The responsability of checking this value is left to the caller.
+ * @return True when signed, false when UNsigned
+ */
+bool Header::IsSignedPixelData()
+{
+ std::string strSize = GetEntryByNumber( 0x0028, 0x0103 );
+ if ( strSize == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::IsSignedPixelData: this is supposed "
+ "to be mandatory");
+ return false;
+ }
+ int sign = atoi( strSize.c_str() );
+ if ( sign == 0 )
+ {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * \brief Retrieve the number of Bits Allocated
+ * (8, 12 -compacted ACR-NEMA files, 16, ...)
+ * @return The encountered number of Bits Allocated, 0 by default.
+ * 0 means the file is NOT USABLE. The caller has to check it !
+ */
+int Header::GetBitsAllocated()
+{
+ std::string strSize = GetEntryByNumber(0x0028,0x0100);
+ if ( strSize == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::GetBitsStored: this is supposed to "
+ "be mandatory");
+ return 0; // It's supposed to be mandatory
+ // the caller will have to check
+ }
+ return atoi( strSize.c_str() );
+}
+
+/**
+ * \brief Retrieve the number of Samples Per Pixel
+ * (1 : gray level, 3 : RGB -1 or 3 Planes-)
+ * @return The encountered number of Samples Per Pixel, 1 by default.
+ * (Gray level Pixels)
+ */
+int Header::GetSamplesPerPixel()
+{
+ std::string strSize = GetEntryByNumber(0x0028,0x0002);
+ if ( strSize == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::GetBitsStored: this is supposed to "
+ "be mandatory");
+ return 1; // Well, it's supposed to be mandatory ...
+ // but sometimes it's missing : *we* assume Gray pixels
+ }
+ return atoi( strSize.c_str() );
+}
+
+/**
+ * \brief Check wether this a monochrome picture or not by accessing
+ * the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
+ * @return true when "MONOCHROME1" or "MONOCHROME2". False otherwise.
+ */
+bool Header::IsMonochrome()
+{
+ std::string PhotometricInterp = GetEntryByNumber( 0x0028, 0x0004 );
+ if ( PhotometricInterp == "MONOCHROME1 "
+ || PhotometricInterp == "MONOCHROME2 " )
+ {
+ return true;
+ }
+ if ( PhotometricInterp == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::IsMonochrome: absent Photometric "
+ "Interpretation");
+ }
+ return false;
+}
+
+/**
+ * \brief Check wether this a "PALETTE COLOR" picture or not by accessing
+ * the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
+ * @return true when "PALETTE COLOR". False otherwise.
+ */
+bool Header::IsPaletteColor()
+{
+ std::string PhotometricInterp = GetEntryByNumber( 0x0028, 0x0004 );
+ if ( PhotometricInterp == "PALETTE COLOR " )
+ {
+ return true;
+ }
+ if ( PhotometricInterp == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::IsPaletteColor: absent Photometric "
+ "Interpretation");
+ }
+ return false;
+}
+
+/**
+ * \brief Check wether this a "YBR_FULL" color picture or not by accessing
+ * the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
+ * @return true when "YBR_FULL". False otherwise.
+ */
+bool Header::IsYBRFull()
+{
+ std::string PhotometricInterp = GetEntryByNumber( 0x0028, 0x0004 );
+ if ( PhotometricInterp == "YBR_FULL" )
+ {
+ return true;
+ }
+ if ( PhotometricInterp == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::IsYBRFull: absent Photometric "
+ "Interpretation");
+ }
+ return false;
+}
+
+/**
+ * \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 Header::GetPlanarConfiguration()
+{
+ std::string strSize = GetEntryByNumber(0x0028,0x0006);
+ if ( strSize == GDCM_UNFOUND )
+ {
+ return 0;
+ }
+ return atoi( strSize.c_str() );
+}
+
+/**
+ * \brief Return the size (in bytes) of a single pixel of data.
+ * @return The size in bytes of a single pixel of data; 0 by default
+ * 0 means the file is NOT USABLE; the caller will have to check