+ return true;
+}
+
+/**
+ * \brief Performs some consistency checking on various 'File related'
+ * (as opposed to 'DicomDir related') entries
+ * then writes in a file all the (Dicom Elements) included the Pixels
+ * @param fileName file name to write to
+ * @param writetype type of the file to be written
+ * (ACR, ExplicitVR, ImplicitVR)
+ */
+bool File::Write(std::string fileName, FileType writetype)
+{
+ std::ofstream *fp = new std::ofstream(fileName.c_str(),
+ std::ios::out | std::ios::binary);
+ if (*fp == NULL)
+ {
+ gdcmWarningMacro("Failed to open (write) File: " << fileName.c_str());
+ return false;
+ }
+
+ // Entry : 0002|0000 = group length -> recalculated
+ ValEntry*e0000 = GetValEntry(0x0002,0x0000);
+ if ( e0000 )
+ {
+ std::ostringstream sLen;
+ sLen << ComputeGroup0002Length(writetype);
+ e0000->SetValue(sLen.str());
+ }
+
+ int i_lgPix = GetEntryLength(GrPixel, NumPixel);
+ if (i_lgPix != -2)
+ {
+ // no (GrPixel, NumPixel) element
+ std::string s_lgPix = Util::Format("%d", i_lgPix+12);
+ s_lgPix = Util::DicomString( s_lgPix.c_str() );
+ InsertValEntry(s_lgPix,GrPixel, 0x0000);
+ }
+
+ Document::WriteContent(fp, writetype);
+
+ fp->close();
+ delete fp;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Protected
+
+
+//-----------------------------------------------------------------------------
+// Private
+/**
+ * \brief Parse pixel data from disk of [multi-]fragment RLE encoding.
+ * Compute the RLE extra information and store it in \ref RLEInfo
+ * for later pixel retrieval usage.
+ */
+void File::ComputeRLEInfo()
+{
+ std::string ts = GetTransferSyntax();
+ if ( !Global::GetTS()->IsRLELossless(ts) )
+ {
+ return;
+ }
+
+ // Encoded pixel data: for the time being we are only concerned with
+ // Jpeg or RLE Pixel data encodings.
+ // As stated in PS 3.5-2003, section 8.2 p44:
+ // "If sent in Encapsulated Format (i.e. other than the Native Format) the
+ // value representation OB is used".
+ // Hence we expect an OB value representation. Concerning OB VR,
+ // the section PS 3.5-2003, section A.4.c p 58-59, states:
+ // "For the Value Representations OB and OW, the encoding shall meet the
+ // following specifications depending on the Data element tag:"
+ // [...snip...]
+ // - the first item in the sequence of items before the encoded pixel
+ // data stream shall be basic offset table item. The basic offset table
+ // item value, however, is not required to be present"
+ ReadAndSkipEncapsulatedBasicOffsetTable();
+
+ // Encapsulated RLE Compressed Images (see PS 3.5-2003, Annex G)
+ // Loop on the individual frame[s] and store the information
+ // on the RLE fragments in a RLEFramesInfo.
+ // Note: - when only a single frame is present, this is a
+ // classical image.
+ // - when more than one frame are present, then we are in
+ // the case of a multi-frame image.
+ long frameLength;
+ while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
+ {
+ // Parse the RLE Header and store the corresponding RLE Segment
+ // Offset Table information on fragments of this current Frame.
+ // Note that the fragment pixels themselves are not loaded
+ // (but just skipped).
+ long frameOffset = Fp->tellg();
+
+ uint32_t nbRleSegments = ReadInt32();
+ if ( nbRleSegments > 16 )
+ {
+ // There should be at most 15 segments (refer to RLEFrame class)
+ gdcmWarningMacro( "Too many segments.");
+ }
+
+ uint32_t rleSegmentOffsetTable[16];
+ for( int k = 1; k <= 15; k++ )
+ {
+ rleSegmentOffsetTable[k] = ReadInt32();
+ }
+
+ // Deduce from both RLE Header and frameLength
+ // the fragment length, and again store this info
+ // in a RLEFramesInfo.
+ long rleSegmentLength[15];
+ // skipping (not reading) RLE Segments
+ if ( nbRleSegments > 1)
+ {
+ for(unsigned int k = 1; k <= nbRleSegments-1; k++)
+ {
+ rleSegmentLength[k] = rleSegmentOffsetTable[k+1]
+ - rleSegmentOffsetTable[k];
+ SkipBytes(rleSegmentLength[k]);
+ }
+ }
+
+ rleSegmentLength[nbRleSegments] = frameLength
+ - rleSegmentOffsetTable[nbRleSegments];
+ SkipBytes(rleSegmentLength[nbRleSegments]);
+
+ // Store the collected info
+ RLEFrame *newFrame = new RLEFrame;
+ newFrame->SetNumberOfFragments(nbRleSegments);
+ for( unsigned int uk = 1; uk <= nbRleSegments; uk++ )
+ {
+ newFrame->SetOffset(uk,frameOffset + rleSegmentOffsetTable[uk]);
+ newFrame->SetLength(uk,rleSegmentLength[uk]);
+ }
+ RLEInfo->AddFrame(newFrame);
+ }
+
+ // Make sure that we encounter a 'Sequence Delimiter Item'
+ // at the end of the item :
+ if ( !ReadTag(0xfffe, 0xe0dd) )
+ {
+ gdcmWarningMacro( "No sequence delimiter item at end of RLE item sequence");
+ }
+}
+
+/**
+ * \brief Parse pixel data from disk of [multi-]fragment Jpeg encoding.
+ * Compute the jpeg extra information (fragment[s] offset[s] and
+ * length) and store it[them] in \ref JPEGInfo for later pixel
+ * retrieval usage.
+ */
+void File::ComputeJPEGFragmentInfo()
+{
+ // If you need to, look for comments of ComputeRLEInfo().
+ std::string ts = GetTransferSyntax();
+ if ( ! Global::GetTS()->IsJPEG(ts) )
+ {
+ return;
+ }
+
+ ReadAndSkipEncapsulatedBasicOffsetTable();
+
+ // Loop on the fragments[s] and store the parsed information in a
+ // JPEGInfo.
+ long fragmentLength;
+ while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
+ {
+ long fragmentOffset = Fp->tellg();
+
+ // Store the collected info
+ JPEGFragment *newFragment = new JPEGFragment;
+ newFragment->SetOffset(fragmentOffset);
+ newFragment->SetLength(fragmentLength);
+ JPEGInfo->AddFragment(newFragment);
+
+ SkipBytes(fragmentLength);
+ }
+
+ // Make sure that we encounter a 'Sequence Delimiter Item'
+ // at the end of the item :
+ if ( !ReadTag(0xfffe, 0xe0dd) )
+ {
+ gdcmWarningMacro( "No sequence delimiter item at end of JPEG item sequence");
+ }
+}
+
+/**
+ * \brief Assuming the internal file pointer \ref Document::Fp
+ * is placed at the beginning of a tag check whether this
+ * tag is (TestGroup, TestElem).
+ * \warning On success the internal file pointer \ref Document::Fp
+ * is modified to point after the tag.
+ * On failure (i.e. when the tag wasn't the expected tag
+ * (TestGroup, TestElem) the internal file pointer
+ * \ref Document::Fp is restored to it's original position.
+ * @param testGroup The expected group of the tag.
+ * @param testElem The expected Element of the tag.
+ * @return True on success, false otherwise.
+ */
+bool File::ReadTag(uint16_t testGroup, uint16_t testElem)
+{
+ long positionOnEntry = Fp->tellg();
+ long currentPosition = Fp->tellg(); // On debugging purposes
+
+ // Read the Item Tag group and element, and make
+ // sure they are what we expected:
+ uint16_t itemTagGroup;
+ uint16_t itemTagElem;
+ try
+ {
+ itemTagGroup = ReadInt16();
+ itemTagElem = ReadInt16();
+ }
+ catch ( FormatError e )
+ {
+ //std::cerr << e << std::endl;
+ return false;
+ }
+ if ( itemTagGroup != testGroup || itemTagElem != testElem )
+ {
+ gdcmWarningMacro( "Wrong Item Tag found:"
+ << " We should have found tag ("
+ << std::hex << testGroup << "," << testElem << ")" << std::endl
+ << " but instead we encountered tag ("
+ << std::hex << itemTagGroup << "," << itemTagElem << ")"
+ << " at address: " << " 0x(" << (unsigned int)currentPosition << ")"
+ ) ;
+ Fp->seekg(positionOnEntry, std::ios::beg);
+
+ return false;
+ }
+ return true;
+}
+
+/**
+ * \brief Assuming the internal file pointer \ref Document::Fp
+ * is placed at the beginning of a tag (TestGroup, TestElement),
+ * read the length associated to the Tag.
+ * \warning On success the internal file pointer \ref Document::Fp
+ * is modified to point after the tag and it's length.
+ * On failure (i.e. when the tag wasn't the expected tag
+ * (TestGroup, TestElement) the internal file pointer
+ * \ref Document::Fp is restored to it's original position.
+ * @param testGroup The expected Group of the tag.
+ * @param testElem The expected Element of the tag.
+ * @return On success returns the length associated to the tag. On failure
+ * returns 0.
+ */
+uint32_t File::ReadTagLength(uint16_t testGroup, uint16_t testElem)
+{
+
+ if ( !ReadTag(testGroup, testElem) )
+ {
+ return 0;
+ }
+
+ //// Then read the associated Item Length
+ long currentPosition = Fp->tellg();
+ uint32_t itemLength = ReadInt32();
+ {
+ gdcmWarningMacro( "Basic Item Length is: "
+ << itemLength << std::endl
+ << " at address: " << std::hex << (unsigned int)currentPosition);
+ }
+ return itemLength;
+}
+
+/**
+ * \brief When parsing the Pixel Data of an encapsulated file, read
+ * the basic offset table (when present, and BTW dump it).
+ */
+void File::ReadAndSkipEncapsulatedBasicOffsetTable()
+{
+ //// Read the Basic Offset Table Item Tag length...
+ uint32_t itemLength = ReadTagLength(0xfffe, 0xe000);
+
+ // When present, read the basic offset table itself.
+ // Notes: - since the presence of this basic offset table is optional
+ // we can't rely on it for the implementation, and we will simply
+ // trash it's content (when present).
+ // - still, when present, we could add some further checks on the
+ // lengths, but we won't bother with such fuses for the time being.
+ if ( itemLength != 0 )
+ {
+ char *basicOffsetTableItemValue = new char[itemLength + 1];
+ Fp->read(basicOffsetTableItemValue, itemLength);
+
+#ifdef GDCM_DEBUG
+ for (unsigned int i=0; i < itemLength; i += 4 )
+ {
+ uint32_t individualLength = str2num( &basicOffsetTableItemValue[i],
+ uint32_t);
+ gdcmWarningMacro( "Read one length: " <<
+ std::hex << individualLength );
+ }
+#endif //GDCM_DEBUG
+
+ delete[] basicOffsetTableItemValue;
+ }
+}
+
+// These are the deprecated method that one day should be removed (after the next release)
+
+#ifndef GDCM_LEGACY_REMOVE
+/**
+ * \brief Constructor (DEPRECATED : temporaryly kept not to break the API)
+ * @param filename name of the file whose header we want to analyze
+ * @deprecated do not use any longer
+ */
+File::File( std::string const &filename )
+ :Document( )
+{
+ RLEInfo = new RLEFramesInfo;
+ JPEGInfo = new JPEGFragmentsInfo;
+
+ SetFileName( filename );
+ Load( ); // gdcm::Document is first Loaded, then the 'File part'
+}
+
+/**
+ * \brief Loader. (DEPRECATED : temporaryly kept not to break the API)
+ * @param fileName file to be open for parsing
+ * @return false if file cannot be open or no swap info was found,
+ * or no tag was found.
+ * @deprecated Use the Load() [ + SetLoadMode() ] + SetFileName() functions instead
+ */
+bool File::Load( std::string const &fileName )
+{
+ GDCM_LEGACY_REPLACED_BODY(File::Load(std::string), "1.2",
+ File::Load());
+ SetFileName( fileName );
+ if ( ! this->Document::Load( ) )
+ return false;
+
+ return DoTheLoadingJob( );
+}
+#endif
+
+// -----------------------------------------------------------------------------------------
+// THERALYS Algorithm to determine the most similar basic orientation
+//
+// Transliterated from original Python code.
+// Kept as close as possible to the original code
+// in order to speed up any further modif of Python code :-(
+// ------------------------------------------------------------------------------------------
+
+/**
+ * \brief THERALYS' Algorithm to determine the most similar basic orientation
+ * (Axial, Coronal, Sagital) of the image
+ * \note Should be run on the first gdcm::File of a 'coherent' Serie
+ * @return orientation code
+ * @return orientation code
+ * # 0 : Not Applicable (neither 0020,0037 Image Orientation Patient
+ * # nor 0020,0032Image Position found )
+ * # 1 : Axial
+ * # -1 : Axial invert
+ * # 2 : Coronal
+ * # -2 : Coronal invert
+ * # 3 : Sagital
+ * # -3 : Sagital invert
+ * # 4 : Heart Axial
+ * # -4 : Heart Axial invert
+ * # 5 : Heart Coronal
+ * # -5 : Heart Coronal invert
+ * # 6 : Heart Sagital
+ * # -6 : Heart Sagital invert
+ */
+double File::TypeOrientation( )
+{
+ float iop[6];
+ bool succ = GetImageOrientationPatient( iop );
+ if ( !succ )
+ {
+ return 0.;
+ }
+
+ vector3D ori1;
+ vector3D ori2;
+
+ ori1.x = iop[0]; ori1.y = iop[1]; ori1.z = iop[2];
+ ori1.x = iop[3]; ori2.y = iop[4]; ori2.z = iop[5];
+
+ // two perpendicular vectors describe one plane
+ double dicPlane[6][2][3] =
+ { { {1, 0, 0 },{0, 1, 0 } }, // Axial
+ { {1, 0, 0 },{0, 0, -1 } }, // Coronal
+ { {0, 1, 0 },{0, 0, -1 } }, // Sagittal
+ { { 0.8, 0.5, 0.0 },{-0.1, 0.1 , -0.95 } }, // Axial - HEART
+ { { 0.8, 0.5, 0.0 },{-0.6674, 0.687, 0.1794} }, // Coronal - HEART
+ { {-0.1, 0.1, -0.95},{-0.6674, 0.687, 0.1794} } // Sagittal - HEART
+ };
+
+ vector3D refA;
+ vector3D refB;
+ int i = 0;
+ Res res; // [ <result> , <memory of the last succes calcule> ]
+ res.first = 0;
+ res.second = 99999;
+ for (int numDicPlane=0; numDicPlane<6; numDicPlane++)
+ {
+ ++i;
+ // refA=plane[0]
+ refA.x = dicPlane[numDicPlane][0][0];
+ refA.y = dicPlane[numDicPlane][0][1];
+ refA.z = dicPlane[numDicPlane][0][2];
+ // refB=plane[1]
+ refB.x = dicPlane[numDicPlane][1][0];
+ refB.y = dicPlane[numDicPlane][1][1];
+ refB.z = dicPlane[numDicPlane][1][2];
+ res=VerfCriterion( i, CalculLikelyhood2Vec(refA,refB,ori1,ori2), res );
+ res=VerfCriterion( -i, CalculLikelyhood2Vec(refB,refA,ori1,ori2), res );
+ }
+ return res.first;
+/*
+// i=0
+// res=[0,99999] ## [ <result> , <memory of the last succes calculus> ]
+// for plane in dicPlane:
+// i=i+1
+// refA=plane[0]
+// refB=plane[1]
+// res=self.VerfCriterion( i , self.CalculLikelyhood2Vec(refA,refB,ori1,ori2) , res )
+// res=self.VerfCriterion( -i , self.CalculLikelyhood2Vec(refB,refA,ori1,ori2) , res )
+// return res[0]
+*/
+
+}
+
+Res
+File::VerfCriterion(int typeCriterion, double criterionNew, Res const & in)
+{
+ Res res;
+ double criterion = in.second;
+ if (criterionNew < criterion)
+ {
+ res.first = criterionNew;
+ res.second = typeCriterion;
+ }
+/*
+// type = res[0]
+// criterion = res[1]
+// # if criterionNew<0.1 and criterionNew<criterion:
+// if criterionNew<criterion:
+// criterion=criterionNew
+// type=typeCriterion
+// return [ type , criterion ]
+*/
+ return res;
+}
+
+inline double square_dist(vector3D const &v1, vector3D const & v2)
+{
+ double res;
+ res = (v1.x - v2.x)*(v1.x - v2.x) +
+ (v1.y - v2.y)*(v1.y - v2.y) +
+ (v1.z - v2.z)*(v1.z - v2.z);
+ return res;
+}
+
+//------------------------- Purpose : -----------------------------------
+//- This function determines the orientation similarity of two planes.
+// Each plane is described by two vectors.
+//------------------------- Parameters : --------------------------------
+//- <refA> : - type : vector 3D (double)
+//- <refB> : - type : vector 3D (double)
+// - Description of the first plane
+//- <ori1> : - type : vector 3D (double)
+//- <ori2> : - type : vector 3D (double)
+// - Description of the second plane
+//------------------------- Return : ------------------------------------
+// double : 0 if the planes are perpendicular. While the difference of
+// the orientation between the planes are big more enlarge is
+// the criterion.
+//------------------------- Other : -------------------------------------
+// The calculus is based with vectors normalice
+double
+File::CalculLikelyhood2Vec(vector3D const & refA, vector3D const & refB,
+ vector3D const & ori1, vector3D const & ori2 )
+{
+
+ vector3D ori3 = ProductVectorial(ori1,ori2);
+ vector3D refC = ProductVectorial(refA,refB);
+ double res = square_dist(refC, ori3);
+
+ return sqrt(res);
+}
+
+//------------------------- Purpose : -----------------------------------
+//- Calculus of the poduct vectorial between two vectors 3D
+//------------------------- Parameters : --------------------------------
+//- <vec1> : - type : vector 3D (double)
+//- <vec2> : - type : vector 3D (double)
+//------------------------- Return : ------------------------------------
+// (vec) : - Vector 3D
+//------------------------- Other : -------------------------------------
+vector3D
+File::ProductVectorial(vector3D const & vec1, vector3D const & vec2)
+{
+ vector3D vec3;
+ vec3.x = vec1.y*vec2.z - vec1.z*vec2.y;
+ vec3.y = -( vec1.x*vec2.z - vec1.z*vec2.x);
+ vec3.z = vec1.x*vec2.y - vec1.y*vec2.x;
+
+ return vec3;
+}
+
+//-----------------------------------------------------------------------------
+// Print
+
+//-----------------------------------------------------------------------------
+} // end namespace gdcm