+// \todo find a way to inform the caller nothing was found
+// \todo How to tell the caller a wrong number of values was found?
+//
+// ---------------------------------------------------------------
+//
+
+/**
+ * \brief gets the info from 0020,0032 : Image Position Patient
+ * else from 0020,0030 : Image Position (RET)
+ * else 0.
+ * @return up-left image corner X position
+ */
+float File::GetXOrigin()
+{
+ float xImPos, yImPos, zImPos;
+ std::string strImPos = GetEntry(0x0020,0x0032);
+
+ if ( strImPos == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)");
+ strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images
+ if ( strImPos == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)");
+ /// \todo How to tell the caller nothing was found ?
+ return 0.;
+ }
+ }
+
+ if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
+ {
+ return 0.;
+ }
+
+ return xImPos;
+}
+
+/**
+ * \brief gets the info from 0020,0032 : Image Position Patient
+ * else from 0020,0030 : Image Position (RET)
+ * else 0.
+ * @return up-left image corner Y position
+ */
+float File::GetYOrigin()
+{
+ float xImPos, yImPos, zImPos;
+ std::string strImPos = GetEntry(0x0020,0x0032);
+
+ if ( strImPos == GDCM_UNFOUND)
+ {
+ gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)");
+ strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images
+ if ( strImPos == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)");
+ /// \todo How to tell the caller nothing was found ?
+ return 0.;
+ }
+ }
+
+ if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3 )
+ {
+ return 0.;
+ }
+
+ return yImPos;
+}
+
+/**
+ * \brief gets the info from 0020,0032 : Image Position Patient
+ * else from 0020,0030 : Image Position (RET)
+ * else from 0020,1041 : Slice Location
+ * else from 0020,0050 : Location
+ * else 0.
+ * @return up-left image corner Z position
+ */
+float File::GetZOrigin()
+{
+ float xImPos, yImPos, zImPos;
+ std::string strImPos = GetEntry(0x0020,0x0032);
+
+ if ( strImPos != GDCM_UNFOUND )
+ {
+ if( sscanf( strImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
+ {
+ gdcmVerboseMacro( "Wrong Image Position Patient (0020,0032)");
+ return 0.; // bug in the element 0x0020,0x0032
+ }
+ else
+ {
+ return zImPos;
+ }
+ }
+
+ strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images
+ if ( strImPos != GDCM_UNFOUND )
+ {
+ if( sscanf( strImPos.c_str(),
+ "%f\\%f\\%f", &xImPos, &yImPos, &zImPos ) != 3 )
+ {
+ gdcmVerboseMacro( "Wrong Image Position (RET) (0020,0030)");
+ return 0.; // bug in the element 0x0020,0x0032
+ }
+ else
+ {
+ return zImPos;
+ }
+ }
+
+ std::string strSliceLocation = GetEntry(0x0020,0x1041); // for *very* old ACR-NEMA images
+ if ( strSliceLocation != GDCM_UNFOUND )
+ {
+ if( sscanf( strSliceLocation.c_str(), "%f", &zImPos) != 1)
+ {
+ gdcmVerboseMacro( "Wrong Slice Location (0020,1041)");
+ return 0.; // bug in the element 0x0020,0x1041
+ }
+ else
+ {
+ return zImPos;
+ }
+ }
+ gdcmVerboseMacro( "Unfound Slice Location (0020,1041)");
+
+ std::string strLocation = GetEntry(0x0020,0x0050);
+ if ( strLocation != GDCM_UNFOUND )
+ {
+ if( sscanf( strLocation.c_str(), "%f", &zImPos) != 1)
+ {
+ gdcmVerboseMacro( "Wrong Location (0020,0050)");
+ return 0.; // bug in the element 0x0020,0x0050
+ }
+ else
+ {
+ return zImPos;
+ }
+ }
+ gdcmVerboseMacro( "Unfound Location (0020,0050)");
+
+ return 0.; // Hopeless
+}
+
+/**
+ * \brief gets the info from 0020,0013 : Image Number else 0.
+ * @return image number
+ */
+int File::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 = GetEntry(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 File::GetModality()
+{
+ // 0008 0060 CS ID Modality
+ std::string strModality = GetEntry(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 <> unknown 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 File::GetBitsStored()
+{
+ std::string strSize = GetEntry( 0x0028, 0x0101 );
+ if ( strSize == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro("(0028,0101) 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 Missing.
+ * The responsability of checking this value is left to the caller.
+ * @return The high bit positin when present. 0 when Missing.
+ */
+int File::GetHighBitPosition()
+{
+ std::string strSize = GetEntry( 0x0028, 0x0102 );
+ if ( strSize == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "(0028,0102) is supposed to be mandatory");
+ return 0;
+ }
+ return atoi( strSize.c_str() );
+}
+
+/**
+ * \brief Check whether the pixels are signed or UNsigned data.
+ * \warning The method defaults to false (UNsigned) when information is Missing.
+ * The responsability of checking this value is left to the caller.
+ * @return True when signed, false when UNsigned
+ */
+bool File::IsSignedPixelData()
+{
+ std::string strSize = GetEntry( 0x0028, 0x0103 );
+ if ( strSize == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "(0028,0103) 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 File::GetBitsAllocated()
+{
+ std::string strSize = GetEntry(0x0028,0x0100);
+ if ( strSize == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "(0028,0100) 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 File::GetSamplesPerPixel()
+{
+ const std::string& strSize = GetEntry(0x0028,0x0002);
+ if ( strSize == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "(0028,0002) 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 whether this a monochrome picture or not by accessing
+ * the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
+ * @return true when "MONOCHROME1" or "MONOCHROME2". False otherwise.
+ */
+bool File::IsMonochrome()
+{
+ const std::string& PhotometricInterp = GetEntry( 0x0028, 0x0004 );
+ if ( Util::DicomStringEqual(PhotometricInterp, "MONOCHROME1")
+ || Util::DicomStringEqual(PhotometricInterp, "MONOCHROME2") )
+ {
+ return true;
+ }
+ if ( PhotometricInterp == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Not found : Photometric Interpretation (0028,0004)");
+ }
+ return false;
+}
+
+/**
+ * \brief Check whether this a "PALETTE COLOR" picture or not by accessing
+ * the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
+ * @return true when "PALETTE COLOR". False otherwise.
+ */
+bool File::IsPaletteColor()
+{
+ std::string PhotometricInterp = GetEntry( 0x0028, 0x0004 );
+ if ( PhotometricInterp == "PALETTE COLOR " )
+ {
+ return true;
+ }
+ if ( PhotometricInterp == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Not found : Palette color (0028,0004)");
+ }
+ return false;
+}
+
+/**
+ * \brief Check whether this a "YBR_FULL" color picture or not by accessing
+ * the "Photometric Interpretation" tag ( 0x0028, 0x0004 ).
+ * @return true when "YBR_FULL". False otherwise.
+ */
+bool File::IsYBRFull()
+{
+ std::string PhotometricInterp = GetEntry( 0x0028, 0x0004 );
+ if ( PhotometricInterp == "YBR_FULL" )
+ {
+ return true;
+ }
+ if ( PhotometricInterp == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Not found : YBR Full (0028,0004)");
+ }
+ 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 File::GetPlanarConfiguration()
+{
+ std::string strSize = GetEntry(0x0028,0x0006);
+ if ( strSize == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Not found : Planar Configuration (0028,0006)");
+ 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 File::GetPixelSize()
+{
+ // 0028 0100 US IMG Bits Allocated
+ // (in order no to be messed up by old RGB images)
+ // if (File::GetEntry(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;
+ }
+ gdcmVerboseMacro( "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 File::GetPixelType()
+{
+ std::string bitsAlloc = GetEntry(0x0028, 0x0100); // Bits Allocated
+ if ( bitsAlloc == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Missing Bits Allocated (0028,0100)");
+ bitsAlloc = "16"; // default and arbitrary value, not to polute the output
+ }
+
+ if ( bitsAlloc == "64" )
+ {
+ return "FD";
+ }
+ else 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 = GetEntry(0x0028, 0x0103);//"Pixel Representation"
+
+ if (sign == GDCM_UNFOUND )
+ {
+ gdcmVerboseMacro( "Missing Pixel Representation (0028,0103)");
+ sign = "U"; // default and arbitrary value, not to polute the output
+ }
+ else 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 File::GetPixelOffset()
+{
+ DocEntry* pxlElement = GetDocEntry(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 File::GetPixelAreaLength()
+{
+ DocEntry* pxlElement = GetDocEntry(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 File::HasLUT()
+{
+ // Check the presence of the LUT Descriptors, and LUT Tables
+ // LutDescriptorRed
+ if ( !GetDocEntry(0x0028,0x1101) )
+ {
+ return false;
+ }
+ // LutDescriptorGreen
+ if ( !GetDocEntry(0x0028,0x1102) )
+ {
+ return false;
+ }
+ // LutDescriptorBlue
+ if ( !GetDocEntry(0x0028,0x1103) )
+ {
+ return false;
+ }
+ // Red Palette Color Lookup Table Data
+ if ( !GetDocEntry(0x0028,0x1201) )
+ {
+ return false;
+ }
+ // Green Palette Color Lookup Table Data
+ if ( !GetDocEntry(0x0028,0x1202) )
+ {
+ return false;
+ }
+ // Blue Palette Color Lookup Table Data
+ if ( !GetDocEntry(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 File::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 = GetEntry(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;
+}
+
+
+//-----------------------------------------------------------------------------
+// Protected
+
+/**
+ * \brief anonymize a File (removes Patient's personal info)
+ * (read the code to see which ones ...)
+ */
+bool File::AnonymizeFile()
+{
+ // If exist, replace by spaces
+ SetEntry (" ",0x0010, 0x2154); // Telephone
+ SetEntry (" ",0x0010, 0x1040); // Adress
+ SetEntry (" ",0x0010, 0x0020); // Patient ID
+
+ DocEntry* patientNameHE = GetDocEntry (0x0010, 0x0010);