-#define LGR_ENTETE_A_LIRE 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();
- bool grPixelTrouve = false;
- 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[LGR_ENTETE_A_LIRE];
-
- // On teste le processeur
- if (x==ntohs(x)) {
- net2host = true;
- } else {
- net2host = false;
- }
-
- // On commence par verifier si c'est du DICOM 'actuel'
- // -------------
- lgrLue = fread(deb,1,LGR_ENTETE_A_LIRE, 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) {
- // on saute le File Preamble (souvent a ZERO) : 128 Octets
- // + le DICM (4), et le (0002, 0000) soit 4 (136 = 128 + 4 + 4)
- entCur = deb+136;
- if(memcmp(entCur, "UL", (size_t)2) == 0) {
- // les 2 premiers octets de la lgr peuvent valoir UL --> Explicit VR
- 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) { // HostByteOrder is different from NetworkByteOrder
- sw = 0; // on est sur PC ou DEC --> LITTLE-ENDIAN -> Rien a faire
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "HostByteOrder = NetworkByteOrder");
- } else { /* on est sur une Sun ou une SGI */
- sw = 4321;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "HostByteOrder != NetworkByteOrder");
- }
-
- rewind(fp);
- fseek (fp, 132L, SEEK_SET); //On se positionne sur le debut des info
- offsetCourant=132;
- } // End of TrueDicom
-
- // Pas du TrueDicom : permiere hypothese c'est de l'ACR 'propre', auquel
- // cas la lgr du premier element du groupe est FORCEMENT 4
- entCur=deb + 4;
- s=str2num(entCur,int);
-
- switch (s) {
- case 0x00040000 :
- sw=3412;
- filetype = ACR;
- break;
- case 0x04000000 :
- sw=4321;
- filetype = ACR;
- break;
- case 0x00000400 :
- sw=2143;
- filetype = ACR;
- break;
- case 0x00000004 :
- sw=0;
- filetype = ACR;
- break;
- default :
- sw = -1;
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "unfound swap info (time to raise bets)");
- }
- if(sw!=-1) {
- rewind(fp); // les info commencent au debut
- offsetCourant=0;
- return;
- }
-
- // Deuxieme hypothese : c'est de l'ACR 'pas propre' i.e. il manque
- // la lgr du groupe
-
- // On n'a pas trouve l'info de swap.
- // Si c'est du VRAI ACR NEMA et
- // * si on est sur une DEC ou un PC alors swap=0,
- // * si on est sur SUN ou SGI, alors swap=4321
- // Si c'est du RAW, ca degagera + tard
- dbg.Verbose(0, "gdcmHeader::CheckSwap:",
- "time for wild guesses...");
-
- if (x!=ntohs(x)) // HostByteOrder is different from NetworkByteOrder
- // on est sur PC ou DEC --> LITTLE-ENDIAN -> Rien a faire
- sw = 0;
- else
- // on est sur Sun ou SGI
- sw = 4321;
- rewind(fp); // les info commencent au debut
- offsetCourant=0;
- return;
-}
-
-/**
- * \ingroup gdcmHeader
- * \brief Pour les fichiers non TrueDicom, si le recognition
- * code (0008,0010) s'avere etre "ACR_LIBIDO", alors
- * valide la reconnaissance du fichier en positionnant
- * filetype.
- */
-void gdcmHeader::setAcrLibido() {
- string RecCode;
-
- if ( filetype != TrueDicom) {
- printf("_setAcrLibido expects a presumably ACR file\n");
- // Recognition Code --> n'existe plus en DICOM V3 ...
- RecCode = GetPubElValByNumber(0x0008, 0x0010);
- // FIXME NOW
- if (RecCode == "ACRNEMA_LIBIDO" ||
- RecCode == "CANRME_AILIBOD" )
- filetype = ACR_LIBIDO;
- else
- filetype = ACR;
- }
- 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
- */
-
-long int gdcmHeader::RecupLgr( ElValue *pleCourant, int *skippedLength) {
- guint32 l_gr;
- unsigned short int l_gr_2;
- int i;
- char VR[5];
- int lgrLue;
-
- // FIX ME
- // ATTENTION : nbCode correspond au nombre d'elements dans la table
- // de type DICOM_VR. A nettoyer.
- //
- int nbCode=26;
-
- if (filetype == ExplicitVR) {
- 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);
- } else {
-
- // On est mal : implicit VR repere
- // mais ce n'est pas un code connu ...
- // On reconstitue la longueur
-
- dbg.Verbose(1, "gdcmHeader::RecupLgr:",
- "Explicit VR, but no known code");
- memcpy(&l_gr, VR,(size_t)2);
- lgrLue=fread ( ((char*)&l_gr)+2, (size_t)2, (size_t)1, fp);
- l_gr = SwapLong((guint32)l_gr);
- pleCourant->SetLgrLue(l_gr);
- if ( (int)l_gr == -1)
- l_gr=0;
-
- *skippedLength = 4;
- return(l_gr);
- }
-
- // On repart dans la sequence 'sensee'
- if ( (!memcmp( VR,"OB",(size_t)2 )) ||
- (!memcmp( VR,"OW",(size_t)2 )) ||
- (!memcmp( VR,"SQ",(size_t)2 )) ||
- (!memcmp( VR,"UN",(size_t)2 )) ) {
-
- // les 2 octets suivants sont reserves: on les saute
- fseek(fp, 2L,SEEK_CUR);
-
- //on lit la lgr sur QUATRE octets
- lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
- l_gr = SwapLong((guint32)l_gr);
- *skippedLength = 8;
-
- } else {
- //on lit la lgr sur DEUX octets
- lgrLue=fread (&l_gr_2, (size_t)2,(size_t)1, fp);
-
- if(sw) l_gr_2 = SwapShort((unsigned short)l_gr_2);
-
- pleCourant->SetLgrLue(l_gr_2);
-
- if ( l_gr_2 == 0xffff) {
- l_gr = 0;
- } else {
- l_gr = l_gr_2;
- }
- *skippedLength = 4;
- }
- } else {
- // Explicit VR = 0
- //on lit la lgr sur QUATRE octets
-
- lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
-
- l_gr= SwapLong((long)l_gr);
- *skippedLength = 4;
- }
-
- pleCourant->SetLgrLue(l_gr);
-
- // Traitement des curiosites sur la longueur
-
- if ( (int)l_gr == 0xffffffff)
- l_gr=0;
-
- if(!memcmp( VR,"SQ",(size_t)2 )) { // ca annonce une SEQUENCE d'items ?!
- l_gr=0; // on lira donc les items de la sequence
- }
-
- return(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) {
- unsigned short g;
- unsigned short n;
- guint32 l;
- long int posFich;
- int skL;
- size_t lgrLue;
- //CLEANME DICOM_ELEMENTS *t;
- ElValue * nouvDcmElem;
-
- // FIXME la probabilte pour depasser sans s'en rendre compte
- // est grande avec le test d'egalite' suivant !
- if(offsetCourant == taille_fich) { // On a atteint la fin du fichier
- dbg.Verbose(1, "ReadNextElement: EOF reached");
- return(NULL);
- }
-
- // ------------------------- 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);
- }
-
- // ------------------------- Lecture longueur element : l
-
- l = RecupLgr(nouvDcmElem, &skL);
-
- if(g==0xfffe) l=0; // pour sauter les indicateurs de 'SQ'
-
- nouvDcmElem->LgrElem=l;
-
- // ------------------------- 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);
-
- offsetCourant += 2 + 2 + skL; // gr + num + lgr
- nouvDcmElem->Offset = offsetCourant;
- offsetCourant += l; // debut elem suivant
-
- // ------------------------- Doit-on le Swapper ?
-
- if ((n==0) && sw) { // n=0 : lgr du groupe : guint32
- *(guint32 *) NewValue = SwapLong ((*(guint32 *) NewValue));
- } else {
- if(sw) {
- if ( (g/2)*2-g==0) { /* on ne teste pas les groupes impairs */
-
- if ((l==4)||(l==2)) { // pour eviter de swapper les chaines
- // de lgr 2 ou 4
-
- // FIXME make reference to nouvDcmElem->GetTag
- string VR = NewTag->GetVR();
- if ( (VR == "UL") || (VR == "US")
- || (VR == "SL") || (VR == "SS")
- || (g == 0x0028 && ( n == 0x0005 || n == 0x0200) )) {
- // seuls (28,5) de vr RET et (28,200) sont des entiers
- // ... jusqu'a preuve du contraire
-
- if(l==4) {
- *(guint32 *) NewValue =
- SwapLong ((*(guint32 *) NewValue));
- } else {
- if(l==2)
- *(unsigned short *) NewValue =
- SwapShort ((*(unsigned short *)NewValue));
- }
- }
- } /* fin if l==2 ==4 */
- } /* fin if g pair */
- } /* fin sw */
- }
- nouvDcmElem->value = NewValue;
- // CLEAN ME: simply trash the following line and postpone the process !
- // SetAsidePixelData(nouvDcmElem);
- return nouvDcmElem;
-}
-
-/**
- * \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) {
- /// FIXME this wall process is bizarre:
- // on peut pas lire pixel data et pixel location puis
- // a la fin de la lecture aller interpreter ce qui existe ?
- // penser a nettoyer les variables globales associes genre
- // PixelsTrouve ou grPixelTrouve...
- //
- // 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 it.
- 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->GetLgrElem()+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(unsigned short, unsigned short) {
-}
-
-/**
- * \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
+ 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
+ */
+int Header::GetPixelSize()
+{
+ // 0028 0100 US IMG Bits Allocated
+ // (in order no to be messed up by old RGB images)
+ // if (Header::GetEntryByNumber(0x0028,0x0100) == "24")
+ // return 3;
+
+ std::string pixelType = GetPixelType();
+ if ( pixelType == "8U" || pixelType == "8S" )
+ {
+ return 1;
+ }
+ if ( pixelType == "16U" || pixelType == "16S")
+ {
+ return 2;
+ }
+ if ( pixelType == "32U" || pixelType == "32S")
+ {
+ return 4;
+ }
+ if ( pixelType == "FD" )
+ {
+ return 8;
+ }
+ dbg.Verbose(0, "Header::GetPixelSize: Unknown pixel type");
+ return 0;
+}
+
+/**
+ * \brief Build the Pixel Type of the image.
+ * Possible values are:
+ * - 8U unsigned 8 bit,
+ * - 8S signed 8 bit,
+ * - 16U unsigned 16 bit,
+ * - 16S signed 16 bit,
+ * - 32U unsigned 32 bit,
+ * - 32S signed 32 bit,
+ * - FD floating double 64 bits (Not kosher DICOM, but so usefull!)
+ * \warning 12 bit images appear as 16 bit.
+ * 24 bit images appear as 8 bit
+ * @return 0S if nothing found. NOT USABLE file. The caller has to check
+ */
+std::string Header::GetPixelType()
+{
+ std::string bitsAlloc = GetEntryByNumber(0x0028, 0x0100); // Bits Allocated
+ if ( bitsAlloc == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::GetPixelType: unfound Bits Allocated");
+ bitsAlloc = "16";
+ }
+
+ if ( bitsAlloc == "64" )
+ {
+ return "FD";
+ }
+ if ( bitsAlloc == "12" )
+ {
+ // It will be unpacked
+ bitsAlloc = "16";
+ }
+ else if ( bitsAlloc == "24" )
+ {
+ // (in order no to be messed up
+ bitsAlloc = "8"; // by old RGB images)
+ }
+
+ std::string sign = GetEntryByNumber(0x0028, 0x0103);//"Pixel Representation"
+
+ if (sign == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::GetPixelType: unfound Pixel Representation");
+ bitsAlloc = "0";
+ }
+ if ( sign == "0" )
+ {
+ sign = "U";
+ }
+ else
+ {
+ sign = "S";
+ }
+ return bitsAlloc + sign;
+}
+
+
+/**
+ * \brief Recover the offset (from the beginning of the file)
+ * of *image* pixels (not *icone image* pixels, if any !)
+ * @return Pixel Offset
+ */
+size_t Header::GetPixelOffset()
+{
+ DocEntry* pxlElement = GetDocEntryByNumber(GrPixel,NumPixel);
+ if ( pxlElement )
+ {
+ return pxlElement->GetOffset();
+ }
+ else
+ {
+#ifdef GDCM_DEBUG
+ std::cout << "Big trouble : Pixel Element ("
+ << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
+ << std::endl;
+#endif //GDCM_DEBUG
+ return 0;
+ }
+}
+
+/// \todo TODO : unify those two (previous one and next one)
+/**
+ * \brief Recover the pixel area length (in Bytes)
+ * @return Pixel Element Length, as stored in the header
+ * (NOT the memory space necessary to hold the Pixels
+ * -in case of embeded compressed image-)
+ * 0 : NOT USABLE file. The caller has to check.
+ */
+size_t Header::GetPixelAreaLength()
+{
+ DocEntry* pxlElement = GetDocEntryByNumber(GrPixel,NumPixel);
+ if ( pxlElement )
+ {
+ return pxlElement->GetLength();
+ }
+ else
+ {
+#ifdef GDCM_DEBUG
+ std::cout << "Big trouble : Pixel Element ("
+ << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
+ << std::endl;
+#endif //GDCM_DEBUG
+ return 0;
+ }
+}
+
+/**
+ * \brief tells us if LUT are used
+ * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
+ * are NOT considered as LUT, since nobody knows
+ * how to deal with them
+ * Please warn me if you know sbdy that *does* know ... jprx
+ * @return true if LUT Descriptors and LUT Tables were found
+ */
+bool Header::HasLUT()
+{
+ // Check the presence of the LUT Descriptors, and LUT Tables
+ // LutDescriptorRed
+ if ( !GetDocEntryByNumber(0x0028,0x1101) )
+ {
+ return false;
+ }
+ // LutDescriptorGreen
+ if ( !GetDocEntryByNumber(0x0028,0x1102) )
+ {
+ return false;
+ }
+ // LutDescriptorBlue
+ if ( !GetDocEntryByNumber(0x0028,0x1103) )
+ {
+ return false;
+ }
+ // Red Palette Color Lookup Table Data
+ if ( !GetDocEntryByNumber(0x0028,0x1201) )
+ {
+ return false;
+ }
+ // Green Palette Color Lookup Table Data
+ if ( !GetDocEntryByNumber(0x0028,0x1202) )
+ {
+ return false;
+ }
+ // Blue Palette Color Lookup Table Data
+ if ( !GetDocEntryByNumber(0x0028,0x1203) )
+ {
+ return false;
+ }
+
+ // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
+ // NOT taken into account, but we don't know how to use it ...
+ return true;
+}
+
+/**
+ * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
+ * else 0
+ * @return Lookup Table number of Bits , 0 by default
+ * when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
+ * @ return bit number of each LUT item
+ */
+int Header::GetLUTNbits()
+{
+ std::vector<std::string> tokens;
+ int lutNbits;
+
+ //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red
+ // = Lookup Table Desc-Blue
+ // Consistency already checked in GetLUTLength
+ std::string lutDescription = GetEntryByNumber(0x0028,0x1101);
+ if ( lutDescription == GDCM_UNFOUND )
+ {
+ return 0;
+ }
+
+ tokens.clear(); // clean any previous value
+ Util::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;
+}
+
+/**
+ * \brief Accesses the info from 0002,0010 : Transfert Syntax and TS
+ * else 1.
+ * @return The full Transfert Syntax Name (as opposed to Transfert Syntax UID)
+ */
+std::string Header::GetTransfertSyntaxName()
+{
+ // use the TS (TS : Transfert Syntax)
+ std::string transfertSyntax = GetEntryByNumber(0x0002,0x0010);
+
+ if ( transfertSyntax == GDCM_NOTLOADED )
+ {
+ std::cout << "Transfert Syntax not loaded. " << std::endl
+ << "Better you increase MAX_SIZE_LOAD_ELEMENT_VALUE"
+ << std::endl;
+ return "Uncompressed ACR-NEMA";
+ }
+ if ( transfertSyntax == GDCM_UNFOUND )
+ {
+ dbg.Verbose(0, "Header::GetTransfertSyntaxName:"
+ " unfound Transfert Syntax (0002,0010)");
+ return "Uncompressed ACR-NEMA";
+ }
+
+ while ( ! isdigit(transfertSyntax[transfertSyntax.length()-1]) )
+ {
+ transfertSyntax.erase(transfertSyntax.length()-1, 1);
+ }
+ // we do it only when we need it
+ TS* ts = Global::GetTS();
+ std::string tsName = ts->GetValue( transfertSyntax );
+
+ //delete ts; /// \todo Seg Fault when deleted ?!
+ return tsName;
+}
+
+/**
+ * \brief Sets the Pixel Area size in the Header
+ * --> not-for-rats function
+ * @param ImageDataSize new Pixel Area Size
+ * warning : nothing else is checked