#else
#include <netinet/in.h>
#endif
+#include <map>
#define LGR_ENTETE_A_LIRE 256 // on ne lit plus que le debut
#define DEBUG 1
//FIXME: this looks dirty to me...
#define str2num(str, typeNum) *((typeNum *)(str))
+VRHT * gdcmHeader::dicom_vr = (VRHT*)0;
+
+gdcmHeader::gdcmHeader () {
+ if (!gdcmHeader::dicom_vr)
+ InitVRDict();
+ bool grPixelTrouve = false;
+ PixelPosition = (size_t)0;
+ PixelsTrouves = false;
+}
+
+void gdcmHeader::InitVRDict (void) {
+ if (dicom_vr && DEBUG) {
+ printf ("InitVRDict : VR dictionary allready set\n");
+ 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
// FIXME sw n'est plus un argument necessaire
long int gdcmHeader::RecupLgr(
- _ID_DCM_ELEM *pleCourant, int sw, int *skippedLength, int *longueurLue)
+ ElValue *pleCourant,
+ int *skippedLength)
{
guint32 l_gr;
unsigned short int l_gr_2;
- int i, trouve;
+ int i;
char VR[5];
int lgrLue;
lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
VR[2]=0;
- // ATTENTION :
- // Ce n'est pas parce qu'on a trouve UL la premiere fois qu'on respecte
- // Explicit VR tout le temps (cf e=film ...)
-
- for(i=0,trouve=0;i<nbCode;i++) {
- if(memcmp(_ID_dicom_vr[i].dicom_VR,VR,(size_t)2)==0) {
- (pleCourant)->VR=_ID_dicom_vr[i].dicom_VR;
- trouve=1;
- break;
- }
- }
-
- if ( trouve == 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 ...
if(DEBUG)
printf("IdDcmRecupLgr : lgr deduite : %08x , %d\n",l_gr,l_gr);
- *longueurLue=l_gr;
+ pleCourant->SetLgrLue(l_gr);
if ( (int)l_gr == -1)
l_gr=0;
//on lit la lgr sur DEUX octets
lgrLue=fread (&l_gr_2, (size_t)2,(size_t)1, fp);
- if(sw) l_gr_2 = _IdDcmSWAP_SHORT((unsigned short)l_gr_2,sw);
+ if(sw) l_gr_2 = SWAP_SHORT((unsigned short)l_gr_2);
- *longueurLue=l_gr_2;
+ pleCourant->SetLgrLue(l_gr_2);
if ( l_gr_2 == 0xffff) {
*skippedLength = 4;
}
- *longueurLue=l_gr;
+ pleCourant->SetLgrLue(l_gr);
// Traitement des curiosites sur la longueur
* @return longueur retenue pour le champ
*/
-
guint32 gdcmHeader::SWAP_LONG(guint32 a) {
// FIXME: il pourrait y avoir un pb pour les entiers negatifs ...
switch (sw) {
}
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::SWAP_SHORT(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;
+
+ if (DEBUG) printf(" ===> entree ds _IdDcmReadNextElement\n");
+
+ // 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
+ if (DEBUG) printf(" On a atteint la fin du fichier\n");
+ return(NULL);
+ } else {
+ if (DEBUG) {
+ posFich = ftell(fp);
+ printf("lgrFich %f positionDsFich %f offset courant %f\n",
+ (float)taille_fich,
+ (float)posFich,
+ (float)offsetCourant);
+ }
+ }
+
+ // ------------------------- Lecture Num group : g
+ lgrLue=fread (&g, (size_t)2,(size_t)1, fp);
+
+ if (feof(fp)) {
+ if (DEBUG) printf("_IdDcmReadNextElement : eof trouve\n");
+ return (NULL);
+ }
+ if (ferror(fp)){
+ if (DEBUG) printf(" IdDcmReadNextElement : echec lecture NumGr\n");
+ return (NULL);
+ }
+
+ if (DEBUG) printf("_IdDcmReadNextElement : gr %04x\n",g );
+
+ if (sw) g= SWAP_SHORT(((short)g));
+
+ //CLEANME nouvDcmElem->Gr=g;
+ //FIXME this might be usefull for detecting at parse time that
+ //something is screwy in the file
+ //e->__NumeroGroupePrecedent =g;
+
+
+ // ------------------------- Lecture Num Elem : n
+ lgrLue=fread (&n, (size_t)2,(size_t)1, fp);
+
+ if (feof(fp)) {
+ if (DEBUG) printf("_IdDcmReadNextElement : eof trouve\n");
+ return (NULL);
+ }
+ if (ferror(fp)){
+ if (DEBUG) printf(" IdDcmReadNextElement : echec lecture NumElem\n");
+ return (NULL);
+ }
+
+ if (DEBUG) printf("_IdDcmReadNextElement : num %04x\n",n );
+
+ if(sw) n= SWAP_SHORT(((short)n));
+ //CLEANMEnouvDcmElem->Num=n;
+
+ // Find out if the tag we encountered is in the dictionaries:
+ DictEntry * NewTag = IsInDicts(g, n);
+ if (!NewTag)
+ NewTag = new DictEntry(g, n, "Unknown", "Unknown");
+
+ nouvDcmElem = new ElValue(NewTag);
+ if (!nouvDcmElem) {
+ printf("Echec alloc ElValue *nouvDcmElem\n");
+ return(NULL);
+ }
+
+ // ------------------------- Lecture longueur element : l
+
+ l = RecupLgr(nouvDcmElem, &skL);
+
+ if(g==0xfffe) l=0; // pour sauter les indicateurs de 'SQ'
+
+ nouvDcmElem->LgrElem=l;
+
+ if (DEBUG)
+ if (n!=0)
+ printf("_IdDcmReadNextElement : "
+ " gr %04x\tnum %04x\tlong %08x (%d)\n", g,n,l,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 {
+ if (DEBUG)
+ printf(" IdDcmReadNextElement : echec Alloc valeurElem lgr : %d\n",l);
+ 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 = SWAP_LONG ((*(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
+ if (DEBUG)
+ printf("Consultation Dictionary DICOM g %04x n %0xx l %d\n",
+ g,n,l);
+
+ // 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 =
+ SWAP_LONG ((*(guint32 *) NewValue));
+ } else {
+ if(l==2)
+ *(unsigned short *) NewValue =
+ SWAP_SHORT ((*(unsigned short *)NewValue));
+ }
+ }
+ } /* fin if l==2 ==4 */
+ } /* fin if g pair */
+ } /* fin sw */
+ }
+ nouvDcmElem->value = NewValue;
+ 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;
+ if (DEBUG)
+ printf("------------------------grPixel %04x numPixel %04x\n",
+ grPixel,numPixel);
+ }
+ } 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 (DEBUG)
+ printf("------------------------GrPixel %04x\n", grPixel);
+
+ if (grPixel != 0x7FE0) // Vieux pb Philips
+ numPixel = 0x1010; // encore utile ??
+ else
+ numPixel = 0x0010;
+ if (DEBUG)
+ printf("------------------------grPixel %04x numPixel %04x\n",
+ grPixel,numPixel);
+ }
+ }
+ }
+ } else { // on vient de trouver les pixels
+ if (g == grPixel) {
+ if (n == numPixel) {
+ PixelPosition = elem->Offset;
+ PixelsTrouves = true;
+ if (DEBUG)
+ printf(" \t===> Pixels Trouves\n");
+ }
+ }
+ }
+}
+
+DictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) {
+ DictEntry * found = (DictEntry*)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;
+}
////// and the element within a DictEntry. What is the point
////// of storing the equivalent of a TagKey within the information
////// accessed through that TagKey !?
- guint16 group; // e.g. 0x0010
- guint16 element; // e.g. 0x0010
- string name; // e.g. "Patient_Name"
- string ValRep; // Value Representation i.e. some clue about the nature
- // of the data represented e.g. "FD" short for
- // "Floating Point Double"
+ guint16 group; // e.g. 0x0010
+ guint16 element; // e.g. 0x0010
+ string name; // e.g. "Patient_Name"
+ string ValRep; // Value Representation i.e. some clue about the nature
+ // of the data represented e.g. "FD" short for
+ // "Floating Point Double"
// DCMTK has many fields for handling a DictEntry (see below). What are the
// relevant ones for gdcmlib ?
// struct DBI_SimpleEntry {
public:
DictEntry();
DictEntry(guint16 group, guint16 element, string name, string VR);
+ void SetVR(string);
+ string GetVR(void);
+ guint16 GetGroup(void) { return group;};
+ guint16 GetElement(void) {return element;};
};
// A single DICOM dictionary i.e. a container for a collection of dictionary
TagHT entries;
public:
Dict();
- Dict(string filename); // Read Dict from disk
+ Dict(string filename); // Read Dict from disk
int AppendEntry(DictEntry* NewEntry);
+ DictEntry * GetTag(guint32, guint32); // Is this tag in this Dict ?
};
// Container for managing a set of loaded dictionaries. Sharing dictionaries
map<DictId, Dict*> dicts;
int AppendDict(Dict* NewDict);
public:
- DictSet(); // Default constructor loads THE DICOM v3 dictionary
+ DictSet(); // Default constructor loads THE DICOM v3 dictionary
int LoadDictFromFile(string filename);
///// QUESTION: the following function might not be thread safe !? Maybe
///// we need some mutex here, to avoid concurent creation of
// The dicom header of a Dicom file contains a set of such ELement VALUES
// (when successfuly parsed against a given Dicom dictionary)
class ElValue {
- DictEntry entry;
- string value;
+private:
+ DictEntry *entry;
+ guint32 LgrLueElem; // Longueur Lue
+ // Might prove of some interest (see _ID_DCM_ELEM)
+ // int Swap;
+public:
+ guint32 LgrElem; // FIXME probably for bad reasons at parse time !
+ string value; // used to be char * valeurElem
+ size_t Offset; // Offset from the begining of file for direct user access
+ ElValue(DictEntry*);
+ void SetVR(string);
+ void SetLgrLue(guint32);
+ guint16 GetGroup(void) { return entry->GetGroup(); };
+ guint16 GetElement(void) { return entry->GetElement(); };
+ guint32 GetLgrElem(void) { return LgrElem; };
+ void SetValue(string val) { value = val; };
};
// Container for a set of succefully parsed ElValues.
int Add(ElValue);
};
+// The various entries of the explicit value representation (VR) shall
+// be managed within a dictionary.
+typedef string VRKey;
+typedef string VRAtr;
+typedef map<TagKey, VRAtr> VRHT; // Value Representation Hash Table
+
// The typical usage of objects of this class is to classify a set of
// dicom files according to header information e.g. to create a file hierachy
// reflecting the Patient/Study/Serie informations, or extracting a given
// Notes:
// * the gdcmHeader::Set*Tag* family members cannot be defined as protected
// (Swig limitations for as Has_a dependency between gdcmFile and gdcmHeader)
-typedef int _ID_DCM_ELEM;
class gdcmHeader {
//enum EndianType {
//LittleEndian,
ACR,
ACR_LIBIDO};
private:
+ // All instances share the same valur representation dictionary
+ static VRHT *dicom_vr;
static DictSet* Dicts; // Global dictionary container
Dict* RefPubDict; // Public Dictionary
Dict* RefShaDict; // Shadow Dictionary (optional)
ElValSet PubElVals; // Element Values parsed with Public Dictionary
ElValSet ShaElVals; // Element Values parsed with Shadow Dictionary
FileType filetype;
+ // In order to inspect/navigate through the file
+ size_t taille_fich;
FILE * fp;
- long int offsetCourant;
+ size_t offsetCourant;
+ // The tag Image Location ((0028,0200) containing the adress of
+ // the pixels) is not allways present. When we store this information
+ // FIXME
+ // outside of the elements:
+ guint16 grPixel;
+ guint16 numPixel;
+ bool PixelsTrouves;
+ bool grPixelTrouve;
+ size_t PixelPosition;
int sw;
void CheckSwap(void);
void setAcrLibido(void);
- long int RecupLgr(_ID_DCM_ELEM *pleCourant, int sw,
- int *skippedLength, int *longueurLue);
+ long int RecupLgr(ElValue *, int *);
guint32 SWAP_LONG(guint32);
+ short int SWAP_SHORT(short int);
+ void InitVRDict(void);
+ ElValue * ReadNextElement(void);
+ DictEntry * IsInDicts(guint32, guint32);
+ void SetAsidePixelData(ElValue*);
protected:
///// QUESTION: Maybe Print is a better name than write !?
int write(ostream&);