- * \ingroup gdcmFile
- * \brief computes the length (in bytes) to ALLOCATE to receive the
- * image(s) pixels (multiframes taken into account)
- * \warning : it is NOT the group 7FE0 length
- * (no interest for compressed images).
- * @return length to allocate
- */
-void gdcmFile::SetPixelDataSizeFromHeader() {
- // see PS 3.3-2003 : C.7.6.3.2.1
- //
- // MONOCHROME1
- // MONOCHROME2
- // PALETTE COLOR
- // RGB
- // HSV (Retired)
- // ARGB (Retired)
- // CMYK (Retired)
- // YBR_FULL
- // YBR_FULL_422 (no LUT, no Palette)
- // YBR_PARTIAL_422
- // YBR_ICT
- // YBR_RCT
-
- // LUT's
- // ex : gdcm-US-ALOKA-16.dcm
- // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
- // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
- // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
-
- // ex : OT-PAL-8-face.dcm
- // 0028|1201 [US] [Red Palette Color Lookup Table Data]
- // 0028|1202 [US] [Green Palette Color Lookup Table Data]
- // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
-
- int nb;
- std::string str_nb;
- str_nb=Header->GetEntryByNumber(0x0028,0x0100);
- if (str_nb == GDCM_UNFOUND ) {
- nb = 16;
- } else {
- nb = atoi(str_nb.c_str() );
- if (nb == 12) nb =16;
- }
- lgrTotale = lgrTotaleRaw = Header->GetXSize() * Header->GetYSize()
- * Header->GetZSize() * (nb/8)* Header->GetSamplesPerPixel();
- std::string str_PhotometricInterpretation =
- Header->GetEntryByNumber(0x0028,0x0004);
-
- /*if ( str_PhotometricInterpretation == "PALETTE COLOR " )*/
- // pb when undealt Segmented Palette Color
-
- if (Header->HasLUT()) {
- lgrTotale*=3;
- }
-}
-
-/**
- * \ingroup gdcmFile
- * \brief Returns the size (in bytes) of required memory to hold
- * the pixel data represented in this file.
- * @return The size of pixel data in bytes.
- */
-size_t gdcmFile::GetImageDataSize() {
- return lgrTotale;
-}
-
-/**
- * \ingroup gdcmFile
- * \brief Returns the size (in bytes) of required memory to hold
- * the pixel data represented in this file, when user DOESN'T want
- * to get RGB pixels image when it's stored as a PALETTE COLOR image
- * -the (vtk) user is supposed to know how deal with LUTs-
- * \warning to be used with GetImagePixelsRaw()
- * @return The size of pixel data in bytes.
- */
-size_t gdcmFile::GetImageDataSizeRaw() {
- return lgrTotaleRaw;
-}
-
-/**
- * \ingroup gdcmFile
- * \brief Allocates necessary memory, copies the pixel data
- * (image[s]/volume[s]) to newly allocated zone.
- * Transforms YBR pixels into RGB pixels if any
- * Transforms 3 planes R, G, B into a single RGB Plane
- * Transforms single Grey plane + 3 Palettes into a RGB Plane
- * @return Pointer to newly allocated pixel data.
- * NULL if alloc fails
- */
-void * gdcmFile::GetImageData () {
- PixelData = new unsigned char[lgrTotale]; //consistant with GetImageDataIntoVector
- if (PixelData) {
- GetImageDataIntoVector(PixelData, lgrTotale);
- GetHeader()->SetEntryVoidAreaByNumber(PixelData,
- GetHeader()->GetGrPixel(),
- GetHeader()->GetNumPixel());
- }
- PixelRead=0; // no PixelRaw
-
- return PixelData;
-}
-
-/**
- * \ingroup gdcmFile
- * \brief Copies at most MaxSize bytes of pixel data to caller's
- * memory space.
- * \warning This function was designed to avoid people that want to build
- * a volume from an image stack to need first to get the image pixels
- * and then move them to the volume area.
- * It's absolutely useless for any VTK user since vtk chooses
- * to invert the lines of an image, that is the last line comes first
- * (for some axis related reasons?). Hence he will have
- * to load the image line by line, starting from the end.
- * VTK users have to call GetImageData
- *
- * @param destination Address (in caller's memory space) at which the
- * pixel data should be copied
- * @param MaxSize Maximum number of bytes to be copied. When MaxSize
- * is not sufficient to hold the pixel data the copy is not
- * executed (i.e. no partial copy).
- * @return On success, the number of bytes actually copied. Zero on
- * failure e.g. MaxSize is lower than necessary.
- */
-size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) {
- //size_t l = GetImageDataIntoVectorRaw (destination, MaxSize);
- GetImageDataIntoVectorRaw (destination, MaxSize);
- PixelRead=0 ; // no PixelRaw
- if (!Header->HasLUT())
- return lgrTotale;
-
- // from Lut R + Lut G + Lut B
- unsigned char *newDest = new unsigned char[lgrTotale];
- unsigned char *a = (unsigned char *)destination;
- unsigned char *lutRGBA = Header->GetLUTRGBA();
-
- if (lutRGBA) {
- int l = lgrTotaleRaw;
- memmove(newDest, destination, l);// move Gray pixels to temp area
- int j;
- for (int i=0;i<l; i++) { // Build RGB Pixels
- j=newDest[i]*4;
- *a++ = lutRGBA[j];
- *a++ = lutRGBA[j+1];
- *a++ = lutRGBA[j+2];
- }
- delete[] newDest;
-
- // now, it's an RGB image
- // Lets's write it in the Header
-
- // CreateOrReplaceIfExist ?
-
- std::string spp = "3"; // Samples Per Pixel
- Header->SetEntryByNumber(spp,0x0028,0x0002);
- std::string rgb= "RGB "; // Photometric Interpretation
- Header->SetEntryByNumber(rgb,0x0028,0x0004);
- std::string planConfig = "0"; // Planar Configuration
- Header->SetEntryByNumber(planConfig,0x0028,0x0006);
-
- } else {
- // need to make RGB Pixels (?)
- // from grey Pixels (?!)
- // and Gray Lut (!?!)
- // or Segmented xxx Palette Color Lookup Table Data and so on
-
- // Oops! I get one (gdcm-US-ALOKA-16.dcm)
- // No idea how to manage such an image
- // It seems that *no Dicom Viewer* has any idea :-(
- // Segmented xxx Palette Color are *more* than 65535 long ?!?
-
- std::string rgb= "MONOCHROME1 "; // Photometric Interpretation
- Header->SetEntryByNumber(rgb,0x0028,0x0004);
- }
- /// \todo Drop Palette Color out of the Header?
- return lgrTotale;
-}
-
-/**
- * \ingroup gdcmFile
- * \brief Allocates necessary memory, copies the pixel data
- * (image[s]/volume[s]) to newly allocated zone.
- * Transforms YBR pixels into RGB pixels if any
- * Transforms 3 planes R, G, B into a single RGB Plane
- * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
- * @return Pointer to newly allocated pixel data.
- * \ NULL if alloc fails
- */
-void * gdcmFile::GetImageDataRaw () {
- if (Header->HasLUT())
- /// \todo Let gdcmHeadar user a chance to get the right value
- /// Create a member lgrTotaleRaw ???
- lgrTotale /= 3;
- PixelData = new unsigned char[lgrTotale];
-
- if (PixelData) {
- GetImageDataIntoVectorRaw(PixelData, lgrTotale);
- GetHeader()->SetEntryVoidAreaByNumber(PixelData,
- GetHeader()->GetGrPixel(),
- GetHeader()->GetNumPixel());
- }
- PixelRead=1; // PixelRaw
-
- return PixelData;
-}
-
-/**
- * \ingroup gdcmFile
- * \brief Copies at most MaxSize bytes of pixel data to caller's
- * memory space.
- * \warning This function was designed to avoid people that want to build
- * a volume from an image stack to need first to get the image pixels
- * and then move them to the volume area.
- * It's absolutely useless for any VTK user since vtk chooses
- * to invert the lines of an image, that is the last line comes first
- * (for some axis related reasons?). Hence he will have
- * to load the image line by line, starting from the end.
- * VTK users hace to call GetImageData
- * \warning DOES NOT transform the Grey Plane + Palette Color (if any)
- * into a single RGB Pixels Plane
- * the (VTK) user will manage the palettes
- *
- * @param destination Address (in caller's memory space) at which the
- * pixel data should be copied
- * @param MaxSize Maximum number of bytes to be copied. When MaxSize
- * is not sufficient to hold the pixel data the copy is not
- * executed (i.e. no partial copy).
- * @return On success, the number of bytes actually copied. Zero on
- * failure e.g. MaxSize is lower than necessary.
- */
-size_t gdcmFile::GetImageDataIntoVectorRaw (void *destination, size_t MaxSize) {
-
- int nb, nbu, highBit, signe;
- std::string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe;
- PixelRead=1 ; // PixelRaw
-
- if ( lgrTotale > MaxSize ) {
- dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger"
- "than caller's expected MaxSize");
- return (size_t)0;
- }
-
- ReadPixelData(destination);
-
- // Number of Bits Allocated for storing a Pixel
- str_nb = Header->GetEntryByNumber(0x0028,0x0100);
- if (str_nb == GDCM_UNFOUND ) {
- nb = 16;
- } else {
- nb = atoi(str_nb.c_str() );
- }
- // Number of Bits actually used
- str_nbu=Header->GetEntryByNumber(0x0028,0x0101);
- if (str_nbu == GDCM_UNFOUND ) {
- nbu = nb;
- } else {
- nbu = atoi(str_nbu.c_str() );
- }
- // High Bit Position
- str_highBit=Header->GetEntryByNumber(0x0028,0x0102);
- if (str_highBit == GDCM_UNFOUND ) {
- highBit = nb - 1;
- } else {
- highBit = atoi(str_highBit.c_str() );
- }
- // Pixel sign
- // 0 = Unsigned
- // 1 = Signed
- str_signe=Header->GetEntryByNumber(0x0028,0x0103);
- if (str_signe == GDCM_UNFOUND ) {
- signe = 0; // default is unsigned
- } else {
- signe = atoi(str_signe.c_str() );
- }
-
- // re arange bytes inside the integer (processor endianity)
- if (nb != 8)
- SwapZone(destination, Header->GetSwapCode(), lgrTotale, nb);
-
- // to avoid pb with some xmedcon breakers images
- if (nb==16 && nbu<nb && signe==0) {
- int l = (int)lgrTotale / (nb/8);
- guint16 *deb = (guint16 *)destination;
- for(int i = 0; i<l; i++) {
- if(*deb == 0xffff)
- *deb=0;
- deb++;
- }
+ * \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 = GetEntryValue(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 = GetEntryValue(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 columns of image.
+ * @return The encountered size when found, 0 by default.
+ * 0 means the file is NOT USABLE. The caller will have to check
+ */
+int File::GetXSize()
+{
+ const std::string &strSize = GetEntryValue(0x0028,0x0011);
+ if ( strSize == GDCM_UNFOUND )
+ {
+ return 0;
+ }
+
+ return atoi( strSize.c_str() );
+}
+
+/**
+ * \brief Retrieve the number of lines of image.
+ * \warning The defaulted value is 1 as opposed to File::GetXSize()
+ * @return The encountered size when found, 1 by default
+ * (The ACR-NEMA file contains a Signal, not an Image).
+ */
+int File::GetYSize()
+{
+ const std::string &strSize = GetEntryValue(0x0028,0x0010);
+ if ( strSize != GDCM_UNFOUND )
+ {
+ return atoi( strSize.c_str() );
+ }
+ if ( IsDicomV3() )
+ {
+ return 0;
+ }
+
+ // The Rows (0028,0010) entry was optional for ACR/NEMA. It might
+ // hence be a signal (1D image). So we default to 1:
+ return 1;
+}
+
+/**
+ * \brief Retrieve the number of planes of volume or the number
+ * of frames of a multiframe.
+ * \warning When present we consider the "Number of Frames" as the third
+ * dimension. When Missing we consider the third dimension as
+ * being the ACR-NEMA "Planes" tag content.
+ * @return The encountered size when found, 1 by default (single image).
+ */
+int File::GetZSize()
+{
+ // Both DicomV3 and ACR/Nema consider the "Number of Frames"
+ // as the third dimension.
+ const std::string &strSize = GetEntryValue(0x0028,0x0008);
+ if ( strSize != GDCM_UNFOUND )
+ {
+ return atoi( strSize.c_str() );
+ }
+
+ // We then consider the "Planes" entry as the third dimension
+ const std::string &strSize2 = GetEntryValue(0x0028,0x0012);
+ if ( strSize2 != GDCM_UNFOUND )
+ {
+ return atoi( strSize2.c_str() );
+ }
+
+ return 1;
+}
+
+/**
+ * \brief gets the info from 0028,0030 : Pixel Spacing
+ * else 1.0
+ * @return X dimension of a pixel
+ */
+float File::GetXSpacing()
+{
+ float xspacing, yspacing;
+ const std::string &strSpacing = GetEntryValue(0x0028,0x0030);
+
+ if ( strSpacing == GDCM_UNFOUND )
+ {
+ gdcmWarningMacro( "Unfound Pixel Spacing (0028,0030)" );
+ return 1.;
+ }
+
+ int nbValues;
+ if( ( nbValues = sscanf( strSpacing.c_str(),
+ "%f\\%f", &yspacing, &xspacing)) != 2 )
+ {
+ // if single value is found, xspacing is defaulted to yspacing
+ if ( nbValues == 1 )
+ {
+ xspacing = yspacing;
+ }
+
+ if ( xspacing == 0.0 )
+ xspacing = 1.0;
+
+ return xspacing;
+
+ }
+
+ // to avoid troubles with David Clunie's-like images
+ if ( xspacing == 0. && yspacing == 0.)
+ return 1.;
+
+ if ( xspacing == 0.)
+ {
+ gdcmWarningMacro("gdcmData/CT-MONO2-8-abdo.dcm problem");
+ // seems to be a bug in the header ...
+ nbValues = sscanf( strSpacing.c_str(), "%f\\0\\%f", &yspacing, &xspacing);
+ gdcmAssertMacro( nbValues == 2 );
+ }
+
+ return xspacing;
+}
+
+/**
+ * \brief gets the info from 0028,0030 : Pixel Spacing
+ * else 1.0
+ * @return Y dimension of a pixel
+ */
+float File::GetYSpacing()
+{
+ float yspacing = 1.;
+ std::string strSpacing = GetEntryValue(0x0028,0x0030);
+
+ if ( strSpacing == GDCM_UNFOUND )
+ {
+ gdcmWarningMacro("Unfound Pixel Spacing (0028,0030)");
+ return 1.;