// gdcmHeader.cxx
//-----------------------------------------------------------------------------
#include "gdcmHeader.h"
-
-#include <stdio.h>
-#include <cerrno>
-#include <cctype> // for isalpha
-
+#include "gdcmGlobal.h"
#include "gdcmUtil.h"
+#include "gdcmDebug.h"
#include "gdcmTS.h"
+#include <vector>
//-----------------------------------------------------------------------------
// Constructor / Destructor
/**
- * \ingroup gdcmHeader
* \brief Constructor
- * @param InFilename
- * @param exception_on_error
- * @param enable_sequences = true to allow the header
- * to be parsed *inside* the SeQuences,
- * when they have an actual length
- * @param ignore_shadow = true if user wants to skip shadow groups
- * during parsing, to save memory space
+ * @param InFilename name of the file whose header we want to analyze
+ * @param exception_on_error whether we want to throw an exception or not
+ * @param enable_sequences = true to allow the header
+ * to be parsed *inside* the SeQuences, when they have an actual length
+ * @param ignore_shadow = true if user wants to skip shadow groups
+ * during parsing, to save memory space
*/
gdcmHeader::gdcmHeader(const char *InFilename,
bool exception_on_error,
bool enable_sequences,
- bool ignore_shadow):
+ bool ignore_shadow):
gdcmParser(InFilename,exception_on_error,enable_sequences,ignore_shadow)
{
+
+ typedef struct {
+ guint32 totalSQlength;
+ guint32 alreadyParsedlength;
+ } pileElem;
+
// for some ACR-NEMA images GrPixel, NumPixel is *not* 7fe0,0010
// We may encounter the 'RETired' (0x0028, 0x0200) tag
// This IS the right place for the code
- std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
- if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
- GrPixel = 0x7fe0; // default value
- } else {
- GrPixel = (guint16) atoi( ImageLocation.c_str() );
- }
- if (GrPixel == 0xe07f) // sometimes Image Location value doesn't follow
- GrPixel = 0x7fe0; // the supposed processor endianity.
- // see gdcmData/cr172241.dcm
- if (GrPixel != 0x7fe0)
- // This is a kludge for old dirty Philips imager.
- NumPixel = 0x1010;
- else
- NumPixel = 0x0010;
-
- TagKey key = gdcmDictEntry::TranslateToKey(GrPixel, NumPixel);
- countGrPixel = GetEntry().count(key);
+ std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
+ if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
+ GrPixel = 0x7fe0; // default value
+ } else {
+ GrPixel = (guint16) atoi( ImageLocation.c_str() );
+ }
+ if (GrPixel == 0xe07f) // sometimes Image Location value doesn't follow
+ GrPixel = 0x7fe0; // the supposed processor endianity.
+ // see gdcmData/cr172241.dcm
+ if (GrPixel != 0x7fe0)
+ // This is a kludge for old dirty Philips imager.
+ NumPixel = 0x1010;
+ else
+ NumPixel = 0x0010;
+
+ TagKey key = gdcmDictEntry::TranslateToKey(GrPixel, NumPixel);
+ countGrPixel = GetEntry().count(key);
+
+ // we set the SQ Depth of each Header Entry
+
+ int top =-1;
+ int countSQ = 0;
+ pileElem pile[100]; // Hope embedded sequence depth is no that long !
+
+ int currentParsedlength = 0;
+ int totalElementlength;
+ std::ostringstream tab;
+ tab << " ";
+
+ // GDCM_DEBUG
+ // Sorry; Dealing with e-film breaker images
+ // will (certainly) cause a lot of troubles ...
+ // I prefer keeping my 'trace' on .
+
+ for (ListTag::iterator i = listEntries.begin();
+ i != listEntries.end();
+ ++i) {
+ (*i)->SetSQDepthLevel(countSQ);
+ if ( (*i)->GetVR() == "SQ" && (*i)->GetReadLength() != 0) { // SQ found
+ countSQ++;
+ top ++;
+ if ( top >= 20) {
+#ifdef GDCM_DEBUG
+ std::cout << "Kaie ! Kaie! SQ Stack Overflow" << std::endl;
+#endif //GDCM_DEBUG
+ return;
+ }
+#ifdef GDCM_DEBUG
+ std::cout << "\n >>>>> empile niveau " << top
+ << "; Lgr SeQ: " << (*i)->GetReadLength()
+ << "\n" <<std::endl;
+#endif //GDCM_DEBUG
+
+ pile[top].totalSQlength = (*i)->GetReadLength();
+ pile[top].alreadyParsedlength = 0;
+ currentParsedlength = 0;
+
+ } else { // non SQ found
+ if (countSQ != 0) { // we are 'inside a SeQuence'
+ if ( (*i)->GetGroup()==0xfffe && (*i)->GetElement()==0xe0dd) {
+ // we just found 'end of SeQuence'
+
+#ifdef GDCM_DEBUG
+ std::cout << "fffe,e0dd : depile" << std::endl;
+#endif //GDCM_DEBUG
+
+ currentParsedlength += 8; // gr:2 elem:2 vr:2 lgt:2
+ countSQ --;
+ top --;
+ pile[top].alreadyParsedlength += currentParsedlength;
+ } else {
+ // we are on a 'standard' elem
+ // or a Zero-length SeQuence
+
+ totalElementlength = (*i)->GetFullLength();
+ currentParsedlength += totalElementlength;
+ pile[top].alreadyParsedlength += totalElementlength;
+
+ if (pile[top].totalSQlength == 0xffffffff) {
+#ifdef GDCM_DEBUG
+ std::cout << "totalSeQlength == 0xffffffff" << std::endl;
+#endif //GDCM_DEBUG
+ } else {
+#ifdef GDCM_DEBUG
+ std::cout << "alrdyPseLgt:"
+ << pile[top].alreadyParsedlength << " totSeQlgt: "
+ << pile[top].totalSQlength << " curPseLgt: "
+ << currentParsedlength
+ << std::endl;
+#endif //GDCM_DEBUG
+ while (pile[top].alreadyParsedlength==pile[top].totalSQlength) {
+#ifdef GDCM_DEBUG
+ std::cout << " \n<<<<<< On depile niveau " << top
+ << " \n" << std::endl;
+#endif //GDCM_DEBUG
+ (*i)->SetSQDepthLevel(countSQ);
+ currentParsedlength = pile[top].alreadyParsedlength;
+ countSQ --;
+ top --;
+ if (top >=0) {
+ pile[top].alreadyParsedlength += currentParsedlength +12;
+ // 12 : length of 'SQ embedded' SQ element
+ currentParsedlength += 8; // gr:2 elem:2 vr:2 lgt:2
+
+#ifdef GDCM_DEBUG
+ std::cout << pile[top].alreadyParsedlength << " "
+ << pile[top].totalSQlength << " "
+ << currentParsedlength
+ << std::endl;
+#endif //GDCM_DEBUG
+ }
+ if (top == -1) {
+ currentParsedlength = 0;
+ break;
+ }
+ }
+ }
+ }
+ } // end : 'inside a SeQuence'
+ }
+#ifdef GDCM_DEBUG
+ for (int k=0; k<(*i)->GetSQDepthLevel();k++) {
+ std::cout << tab;
+ }
+ (*i)->SetPrintLevel(2);
+ (*i)->Print();
+#endif //GDCM_DEBUG
+ } // end for
}
/**
- * \ingroup gdcmHeader
* \brief Constructor
- * @param exception_on_error
+ * @param exception_on_error whether we want to throw an exception or not
*/
gdcmHeader::gdcmHeader(bool exception_on_error) :
gdcmParser(exception_on_error)
// Print
// see gdcmParser.cxx
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Prints the Header Entries (Dicom Elements)
+ * from the chained list
+ * and skips the elements belonging to a SeQuence
+ * @return
+ */
+void gdcmHeader::PrintEntryNoSQ(std::ostream & os) {
+
+ int depth;
+ for (ListTag::iterator i = listEntries.begin();
+ i != listEntries.end();
+ ++i)
+ {
+ depth= (*i)->GetSQDepthLevel();
+ if ( depth != 0 /*|| (*i)->GetVR() =="SQ" */){
+ continue;
+ }
+ (*i)->SetPrintLevel(printLevel);
+ (*i)->Print(os);
+ }
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Prints the Header Entries (Dicom Elements)
+ * from the chained list
+ * and indents the elements belonging to any SeQuence
+ * \warning : will be removed
+ * @return
+ */
+void gdcmHeader::PrintEntryNiceSQ(std::ostream & os) {
+ std::ostringstream tab;
+ tab << " ";
+
+ int depth;
+ for (ListTag::iterator i = listEntries.begin();
+ i != listEntries.end();
+ ++i) {
+ depth= (*i)->GetSQDepthLevel();
+ if (depth != 0) {
+ for (int k=0;k<depth;k++)
+ os << tab.str();
+ }
+ (*i)->SetPrintLevel(printLevel);
+ (*i)->Print(os);
+
+ } // end for
+}
+
//-----------------------------------------------------------------------------
// Public
// pb : sometimes , (0x0088,0x0200) exists, but doesn't contain *anything*
// see gdcmData/MxTwinLossLess.dcm ...
- //std::string icone = GetEntryByNumber(0x0088,0x0200); //icone image sequence
+ /**
+ * \todo Clean me
+ *std::string icone = GetEntryByNumber(0x0088,0x0200); //icone image sequence
+ */
IterHT it = GetHeaderEntrySameNumber(GrPixel,NumPixel);
TagKey key = gdcmDictEntry::TranslateToKey(GrPixel,NumPixel);
if (PixelElement) {
return PixelElement->GetOffset();
} else {
-/* std::cout << "Big trouble : Pixel Element ("
+#ifdef GDCM_DEBUG
+ std::cout << "Big trouble : Pixel Element ("
<< std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
- << std::endl; */
+ << std::endl;
+#endif //GDCM_DEBUG
return 0;
}
}
if (PixelElement) {
return PixelElement->GetLength();
} else {
-/* std::cout << "Big trouble : Pixel Element ("
+#ifdef GDCM_DEBUG
+ std::cout << "Big trouble : Pixel Element ("
<< std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
- << std::endl;*/
+ << std::endl;
+#endif //GDCM_DEBUG
return 0;
}
}
return false;
// Blue Palette Color Lookup Table Data
if ( !GetHeaderEntryByNumber(0x0028,0x1203) )
- return false;
+ return false;
+ // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
+ // NOT taken into account, but we don't know how to use it ...
return true;
}
// if Photometric Interpretation # PALETTE COLOR, no LUT to be done
if (GetEntryByNumber(0x0028,0x0004) != "PALETTE COLOR ") {
- return NULL;
+ return NULL;
}
int lengthR, debR, nbitsR;
int lengthG, debG, nbitsG;
GetEntryVoidAreaByNumber(0x0028,0x1203);
if (!lutR || !lutG || !lutB ) {
- return NULL;
+ return NULL;
}
// forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
- unsigned char *LUTRGBA = (unsigned char *)calloc(1024,1); // 256 * 4 (R, G, B, Alpha)
+ unsigned char *LUTRGBA = new (unsigned char)[1024]; // 256 * 4 (R, G, B, Alpha)
if (!LUTRGBA) {
return NULL;
}
memset(LUTRGBA, 0, 1024);
- // Bits Allocated
+ // Bits Allocated
int nb;
std::string str_nb = GetEntryByNumber(0x0028,0x0100);
if (str_nb == GDCM_UNFOUND ) {
void gdcmHeader::SetImageDataSize(size_t ImageDataSize) {
std::string content1;
char car[20];
-
+
// Assumes HeaderEntry (GrPixel, NumPixel) is unique ...
- // TODO deal with multiplicity (see gdcmData/icone.dcm)
+ //\todo deal with multiplicity (see gdcmData/icone.dcm)
sprintf(car,"%d",ImageDataSize);
gdcmHeaderEntry *a = GetHeaderEntryByNumber(GrPixel, NumPixel);
a->SetLength(ImageDataSize);
-
+
ImageDataSize+=8;
sprintf(car,"%d",ImageDataSize);
- content1=car;
+ content1=car;
SetEntryByNumber(content1, GrPixel, NumPixel);
}
s1=this->GetEntryByNumber(0x0010,0x0010);
s2=header.GetEntryByNumber(0x0010,0x0010);
if(s1 < s2)
- return(true);
+ return(true);
else if(s1 > s2)
- return(false);
+ return(false);
else
{
// Patient ID
s1=this->GetEntryByNumber(0x0010,0x0020);
s2=header.GetEntryByNumber(0x0010,0x0020);
if (s1 < s2)
- return(true);
+ return(true);
else if (s1 > s2)
return(1);
else
{
- // Study Instance UID
+ // Study Instance UID
s1=this->GetEntryByNumber(0x0020,0x000d);
s2=header.GetEntryByNumber(0x0020,0x000d);
if (s1 < s2)
- return(true);
+ return(true);
else if(s1 > s2)
- return(false);
+ return(false);
else
{
- // Serie Instance UID
+ // Serie Instance UID
s1=this->GetEntryByNumber(0x0020,0x000e);
s2=header.GetEntryByNumber(0x0020,0x000e);
if (s1 < s2)
return(false);
}
+bool gdcmHeader::WriteEntry(gdcmHeaderEntry *tag, FILE *_fp,FileType type)
+{
+ guint32 length = tag->GetLength();
+
+ // The value of a tag MUST (see the DICOM norm) be an odd number of
+ // bytes. When this is not the case, pad with an additional byte:
+ if(length%2==1)
+ {
+ tag->SetValue(tag->GetValue()+"\0");
+ tag->SetLength(tag->GetReadLength()+1);
+ }
+
+ WriteEntryTagVRLength(tag, _fp, type);
+
+ // Pixels are never loaded in the element !
+ // we stop writting when Pixel are processed
+ // FIX : we loose trailing elements (RAB, right now)
+ guint16 el = tag->GetElement();
+ guint16 group = tag->GetGroup();
+ int compte =0;
+ if ((group == GrPixel) && (el == NumPixel) ) {
+ compte++;
+ if (compte == countGrPixel) {// we passed *all* the GrPixel,NumPixel
+ return false;
+ }
+ }
+ WriteEntryValue(tag, _fp, type);
+ return true;
+}
+
//-----------------------------------------------------------------------------
// Protected
* \ingroup gdcmHeader
* \brief anonymize a Header (removes Patient's personal info)
* (read the code to see which ones ...)
- * @param
*/
bool gdcmHeader::anonymizeHeader() {
if (StudyInstanceUID !=GDCM_UNFOUND)
ReplaceOrCreateByNumber(StudyInstanceUID, 0x0010, 0x0010);
else
- ReplaceOrCreateByNumber("anonymised", 0x0010, 0x0010);
+ ReplaceOrCreateByNumber(std::string("anonymised"), 0x0010, 0x0010);
}
// Just for fun :-(
- // (if any) remove or replace
+ // (if any) remove or replace all the stuff that contains a Date
//0008 0012 DA ID Instance Creation Date
//0008 0020 DA ID Study Date
//300a 022c DA RT Air Kerma Rate Reference Date
//300e 0004 DA RT Review Date
return true;
- }
+}
//-----------------------------------------------------------------------------
// Private