]> Creatis software - gdcm.git/blobdiff - src/gdcmDocument.cxx
* CLEANUP_ROUND (6) for gdcmPixelConvert (man, I need a paddle bad)
[gdcm.git] / src / gdcmDocument.cxx
index b7c00a192b03676da7c9d1fb56432def85ca1e2b..857f12de05ca1a44e4acbc750d1599e2752f1c29 100644 (file)
@@ -3,12 +3,12 @@
   Program:   gdcm
   Module:    $RCSfile: gdcmDocument.cxx,v $
   Language:  C++
-  Date:      $Date: 2004/09/23 10:17:26 $
-  Version:   $Revision: 1.90 $
+  Date:      $Date: 2004/10/06 22:31:31 $
+  Version:   $Revision: 1.96 $
                                                                                 
   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
   l'Image). All rights reserved. See Doc/License.txt or
-  http://www.creatis.insa-lyon.fr/Public/Gdcm/License.htm for details.
+  http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
                                                                                 
      This software is distributed WITHOUT ANY WARRANTY; without even
      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
@@ -20,7 +20,6 @@
 #include "gdcmValEntry.h"
 #include "gdcmBinEntry.h"
 #include "gdcmSeqEntry.h"
-
 #include "gdcmGlobal.h"
 #include "gdcmUtil.h"
 #include "gdcmDebug.h"
@@ -107,7 +106,7 @@ gdcmDocument::gdcmDocument( std::string const & filename )
    long beg = ftell(Fp);
    lgt -= beg;
    
-   (void)ParseDES( this, beg, lgt, false); // le Load sera fait a la volee
+   ParseDES( this, beg, lgt, false); // le Load sera fait a la volee
 
    rewind(Fp);
    
@@ -230,7 +229,7 @@ void gdcmDocument::PrintShaDict(std::ostream & os)
 /**
  * \brief   Get the public dictionary used
  */
-gdcmDict *gdcmDocument::GetPubDict()
+gdcmDictgdcmDocument::GetPubDict()
 {
    return RefPubDict;
 }
@@ -238,7 +237,7 @@ gdcmDict *gdcmDocument::GetPubDict()
 /**
  * \brief   Get the shadow dictionary used
  */
-gdcmDict *gdcmDocument::GetShaDict()
+gdcmDictgdcmDocument::GetShaDict()
 {
    return RefShaDict;
 }
@@ -450,6 +449,22 @@ bool gdcmDocument::IsJPEG2000()
            || IsGivenTransferSyntax(UI1_2_840_10008_1_2_4_91) );
 }
 
+/**
+ * \brief   Determines if the Transfer Syntax corresponds to encapsulated
+ *          of encoded Pixel Data (as opposed to native).
+ * @return  True when encapsulated. False when native.
+ */
+bool gdcmDocument::IsEncapsulateTransferSyntax()
+{
+   return (   IsJPEGBaseLineProcess1TransferSyntax()
+           || IsJPEGExtendedProcess2_4TransferSyntax()
+           || IsJPEGExtendedProcess3_5TransferSyntax()
+           || IsJPEGSpectralSelectionProcess6_8TransferSyntax()
+           || IsRLELossLessTransferSyntax()
+           || IsJPEGLossless()
+           || IsJPEG2000() );
+}
+
 /**
  * \brief   Predicate for dicom version 3 file.
  * @return  True when the file is a dicom version 3.
@@ -594,7 +609,7 @@ void gdcmDocument::Write(FILE* fp,FileType filetype)
  * \return  pointer to the modified/created Header Entry (NULL when creation
  *          failed).
  */ 
-gdcmValEntry * gdcmDocument::ReplaceOrCreateByNumber(
+gdcmValEntry* gdcmDocument::ReplaceOrCreateByNumber(
                                          std::string const & value, 
                                          uint16_t group, 
                                          uint16_t elem,
@@ -611,7 +626,7 @@ gdcmValEntry * gdcmDocument::ReplaceOrCreateByNumber(
 
    // Find out if the tag we received is in the dictionaries:
       gdcmDict *pubDict = gdcmGlobal::GetDicts()->GetDefaultPubDict();
-      gdcmDictEntry *dictEntry = pubDict->GetDictEntryByNumber(group, elem);
+      gdcmDictEntrydictEntry = pubDict->GetDictEntryByNumber(group, elem);
       if (!dictEntry)
       {
          currentEntry = NewDocEntryByNumber(group, elem,VR);
@@ -672,12 +687,12 @@ gdcmValEntry * gdcmDocument::ReplaceOrCreateByNumber(
  * \return  pointer to the modified/created Header Entry (NULL when creation
  *          failed).
  */
-gdcmBinEntry * gdcmDocument::ReplaceOrCreateByNumber(
+gdcmBinEntry* gdcmDocument::ReplaceOrCreateByNumber(
                                          uint8_t* binArea,
                                          int lgth, 
                                          uint16_t group, 
                                          uint16_t elem,
-                                         std::string const & VR )
+                                         std::string const& VR )
 {
    gdcmBinEntry* binEntry = 0;
    gdcmDocEntry* currentEntry = GetDocEntryByNumber( group, elem);
@@ -751,7 +766,7 @@ gdcmBinEntry * gdcmDocument::ReplaceOrCreateByNumber(
  * \return  pointer to the modified/created SeqEntry (NULL when creation
  *          failed).
  */
-gdcmSeqEntry * gdcmDocument::ReplaceOrCreateByNumber(
+gdcmSeqEntry* gdcmDocument::ReplaceOrCreateByNumber(
                                          uint16_t group, 
                                          uint16_t elem)
 {
@@ -811,9 +826,9 @@ bool gdcmDocument::CheckIfEntryExistByNumber(uint16_t group, uint16_t element )
  * @return  Corresponding element value when it exists,
  *          and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
  */
-std::string gdcmDocument::GetEntryByName(TagName const & tagName)
+std::string gdcmDocument::GetEntryByName(TagName const& tagName)
 {
-   gdcmDictEntry *dictEntry = RefPubDict->GetDictEntryByName(tagName); 
+   gdcmDictEntrydictEntry = RefPubDict->GetDictEntryByName(tagName); 
    if( !dictEntry )
    {
       return GDCM_UNFOUND;
@@ -835,7 +850,7 @@ std::string gdcmDocument::GetEntryByName(TagName const & tagName)
  * @return  Corresponding element value representation when it exists,
  *          and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
  */
-std::string gdcmDocument::GetEntryVRByName(TagName const & tagName)
+std::string gdcmDocument::GetEntryVRByName(TagName const& tagName)
 {
    gdcmDictEntry *dictEntry = RefPubDict->GetDictEntryByName(tagName); 
    if( dictEntry == NULL)
@@ -936,7 +951,7 @@ bool gdcmDocument::SetEntryByName(std::string const & content,std::string const
  * @param   group     group number of the Dicom Element to modify
  * @param   element element number of the Dicom Element to modify
  */
-bool gdcmDocument::SetEntryByNumber(std::string const & content, 
+bool gdcmDocument::SetEntryByNumber(std::string const& content, 
                                     uint16_t group,
                                     uint16_t element) 
 {
@@ -983,7 +998,7 @@ bool gdcmDocument::SetEntryByNumber(std::string const & content,
  * \brief   Accesses an existing gdcmDocEntry (i.e. a Dicom Element)
  *          through it's (group, element) and modifies it's content with
  *          the given value.
- * @param   content new value (void * -> uint8_t*) to substitute with
+ * @param   content new value (void -> uint8_t*) to substitute with
  * @param   lgth new value length
  * @param   group     group number of the Dicom Element to modify
  * @param   element element number of the Dicom Element to modify
@@ -1070,7 +1085,7 @@ size_t gdcmDocument::GetEntryOffsetByNumber(uint16_t group, uint16_t elem)
  * @param elem  element number of the Entry
  * @return Pointer to the 'non string' area
  */
-void * gdcmDocument::GetEntryBinAreaByNumber(uint16_t group, uint16_t elem) 
+void gdcmDocument::GetEntryBinAreaByNumber(uint16_t group, uint16_t elem) 
 {
    gdcmDocEntry* entry = GetDocEntryByNumber(group, elem);
    if (!entry) 
@@ -1121,7 +1136,7 @@ void* gdcmDocument::LoadEntryBinArea(uint16_t group, uint16_t elem)
  *                when a string is not suitable
  * @param element  Entry whose binArea is going to be loaded
  */
-void *gdcmDocument::LoadEntryBinArea(gdcmBinEntry *element) 
+void* gdcmDocument::LoadEntryBinArea(gdcmBinEntry* element) 
 {
    size_t o =(size_t)element->GetOffset();
    fseek(Fp, o, SEEK_SET);
@@ -1362,14 +1377,12 @@ uint16_t gdcmDocument::UnswapShort(uint16_t a)
  * \brief   Parses a DocEntrySet (Zero-level DocEntries or SQ Item DocEntries)
  * @return  length of the parsed set. 
  */ 
-
-long gdcmDocument::ParseDES(gdcmDocEntrySet *set,
+void gdcmDocument::ParseDES(gdcmDocEntrySet *set,
                             long offset,
                             long l_max,
                             bool delim_mode)
 {
    gdcmDocEntry *newDocEntry = 0;
-   unsigned long l = 0;
    
    while (true)
    { 
@@ -1439,7 +1452,6 @@ long gdcmDocument::ParseDES(gdcmDocEntrySet *set,
             // form ( group, elem )...
             if (gdcmDocument* dummy = dynamic_cast< gdcmDocument* > ( set ) )
             {
-               (void)dummy;
                newBinEntry->SetKey( newBinEntry->GetKey() );
             }
             // but when "this" is a SQItem, we are inserting this new
@@ -1458,29 +1470,28 @@ long gdcmDocument::ParseDES(gdcmDocEntrySet *set,
          if (newDocEntry->GetGroup()   == 0x7fe0 && 
              newDocEntry->GetElement() == 0x0010 )
          {
-             if (newDocEntry->GetReadLength()==0xffffffff)
+             if ( IsRLELossLessTransferSyntax() ) 
              {
-                // Broken US.3405.1.dcm
-                Parse7FE0(); // to skip the pixels 
-                             // (multipart JPEG/RLE are trouble makers)
+                long PositionOnEntry = ftell(Fp);
+                fseek(Fp, newDocEntry->GetOffset(), SEEK_SET);
+                ComputeRLEInfo();
+                fseek(Fp, PositionOnEntry, SEEK_SET);
              }
              else
              {
                 SkipToNextDocEntry(newDocEntry);
-                l = newDocEntry->GetFullLength(); 
              }
          }
          else
          {
              // to be sure we are at the beginning 
              SkipToNextDocEntry(newDocEntry);
-             l = newDocEntry->GetFullLength(); 
          }
       }
       else
       {
          // VR = "SQ"
-         l = newDocEntry->GetReadLength();            
+         unsigned long l = newDocEntry->GetReadLength();            
          if ( l != 0 ) // don't mess the delim_mode for zero-length sequence
          {
             if ( l == 0xffffffff )
@@ -1520,9 +1531,9 @@ long gdcmDocument::ParseDES(gdcmDocEntrySet *set,
 
          if ( l != 0 )
          {  // Don't try to parse zero-length sequences
-            (void)ParseSQ( newSeqEntry, 
-                           newDocEntry->GetOffset(),
-                           l, delim_mode);
+            ParseSQ( newSeqEntry, 
+                     newDocEntry->GetOffset(),
+                     l, delim_mode);
          }
          set->AddEntry( newSeqEntry );
          if ( !delim_mode && (ftell(Fp)-offset) >= l_max)
@@ -1532,14 +1543,13 @@ long gdcmDocument::ParseDES(gdcmDocEntrySet *set,
       }
       delete newDocEntry;
    }
-   return l; // Probably useless 
 }
 
 /**
  * \brief   Parses a Sequence ( SeqEntry after SeqEntry)
  * @return  parsed length for this level
  */ 
-long gdcmDocument::ParseSQ( gdcmSeqEntry* seqEntry,
+void gdcmDocument::ParseSQ( gdcmSeqEntry* seqEntry,
                             long offset, long l_max, bool delim_mode)
 {
    int SQItemNumber = 0;
@@ -1584,7 +1594,7 @@ long gdcmDocument::ParseSQ( gdcmSeqEntry* seqEntry,
          dlm_mod = false;
       }
    
-      (void)ParseDES(itemSQ, newDocEntry->GetOffset(), l, dlm_mod);
+      ParseDES(itemSQ, newDocEntry->GetOffset(), l, dlm_mod);
       
       seqEntry->AddEntry( itemSQ, SQItemNumber ); 
       SQItemNumber++;
@@ -1593,9 +1603,6 @@ long gdcmDocument::ParseSQ( gdcmSeqEntry* seqEntry,
          break;
       }
    }
-
-   int lgth = ftell(Fp) - offset;
-   return lgth;
 }
 
 /**
@@ -1783,11 +1790,13 @@ void gdcmDocument::FindDocEntryLength( gdcmDocEntry *entry )
          fseek(Fp, 2L, SEEK_CUR);
          uint32_t length32 = ReadInt32();
 
-         if ( vr == "OB" && length32 == 0xffffffff ) 
+         if ( (vr == "OB" || vr == "OW") && length32 == 0xffffffff ) 
          {
             uint32_t lengthOB;
             try 
             {
+               /// \todo rename that to FindDocEntryLengthOBOrOW since
+               ///       the above test is on both OB and OW...
                lengthOB = FindDocEntryLengthOB();
             }
             catch ( gdcmFormatUnexpected )
@@ -2818,106 +2827,119 @@ uint32_t gdcmDocument::ReadTagLength(uint16_t testGroup, uint16_t testElement)
 }
 
 /**
- * \brief   Parse pixel data from disk for multi-fragment Jpeg/Rle files
- *          No other way so 'skip' the Data
+ * \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 gdcmDocument::Parse7FE0 ()
+void gdcmDocument::ComputeRLEInfo()
 {
-   gdcmDocEntry* element = GetDocEntryByNumber(0x0002, 0x0010);
-   if ( !element )
-   {
-      // Should warn user FIXME
-      return;
-   }
-      
-   if (   IsImplicitVRLittleEndianTransferSyntax()
-       || IsExplicitVRLittleEndianTransferSyntax()
-       || IsExplicitVRBigEndianTransferSyntax() /// \todo 1.2.2 ??? A verifier !
-       || IsDeflatedExplicitVRLittleEndianTransferSyntax() )
+   if ( ! IsRLELossLessTransferSyntax() )
    {
       return;
    }
-
-   // ---------------- for Parsing : Position on begining of Jpeg/RLE Pixels 
+   // Encoded pixel data: for the time being we are only concerned with
+   // Jpeg or RLE Pixel data encodings.
+   // As stated in ps-3.3, 8.2:
+   // "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 PS3.3, A.4.c (p58 and p59), 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"
 
    //// Read the Basic Offset Table Item Tag length...
    uint32_t itemLength = ReadTagLength(0xfffe, 0xe000);
 
-   //// ... and then read length[s] itself[themselves]. We don't use
-   // the values read (BTW  what is the purpous of those lengths ?)
+   // 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 )
    {
-      // BTW, what is the purpous of those length anyhow !? 
       char* basicOffsetTableItemValue = new char[itemLength + 1];
       fread(basicOffsetTableItemValue, itemLength, 1, Fp);
 
       for (unsigned int i=0; i < itemLength; i += 4 )
       {
-         uint32_t individualLength = str2num(&basicOffsetTableItemValue[i],uint32_t);
+         uint32_t individualLength = str2num( &basicOffsetTableItemValue[i],
+                                              uint32_t);
          std::ostringstream s;
          s << "   Read one length: ";
          s << std::hex << individualLength << std::endl;
-         dbg.Verbose(0, "gdcmDocument::Parse7FE0: ", s.str().c_str());
+         dbg.Verbose(0, "gdcmDocument::ComputeRLEInfo: ", s.str().c_str());
       }
       delete[] basicOffsetTableItemValue;
    }
 
-   if ( ! IsRLELossLessTransferSyntax() )
-   {
-      // JPEG Image
-      
-      //// We then skip (not reading them) all the fragments of images:
-      while ( (itemLength = ReadTagLength(0xfffe, 0xe000)) )
-      {
-         SkipBytes(itemLength);
-      }
-   }
-   else
-   {
-      // RLE Image
-      long ftellRes;
-      long rleSegmentLength[15], fragmentLength;
+   // Encapsulated RLE Compressed Images (see PS-3.3, Annex G).
+   // Loop on the frame[s] and store the parsed information in a
+   // gdcmRLEFramesInfo.
+   long frameLength;
 
-      // While we find some items:
-      while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) )
-      { 
-         // Parse fragments of the current Fragment (Frame)    
-         //------------------ scanning (not reading) fragment pixels
-         uint32_t nbRleSegments = ReadInt32();
+   // Loop on the individual frame[s] and store the information
+   // on the RLE fragments in a gdcmRLEFramesInfo.
+   // 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.
+   while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) )
+   { 
+      // 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 = ftell(Fp);
+
+      uint32_t nbRleSegments = ReadInt32();
  
-         //// Reading RLE Segments Offset Table
-         uint32_t rleSegmentOffsetTable[15];
-         for(int k=1; k<=15; k++)
-         {
-            ftellRes = ftell(Fp);
-            rleSegmentOffsetTable[k] = ReadInt32();
-         }
+      uint32_t rleSegmentOffsetTable[15];
+      for( int k = 1; k <= 15; k++ )
+      {
+         rleSegmentOffsetTable[k] = ReadInt32();
+      }
 
-         // skipping (not reading) RLE Segments
-         if ( nbRleSegments > 1)
+      // Deduce from both the RLE Header and the frameLength the
+      // fragment length, and again store this info in a
+      // gdcmRLEFramesInfo.
+      long rleSegmentLength[15];
+      // skipping (not reading) RLE Segments
+      if ( nbRleSegments > 1)
+      {
+         for(unsigned int k = 1; k <= nbRleSegments-1; k++)
          {
-            for(unsigned int k = 1; k <= nbRleSegments-1; k++)
-            {
-                rleSegmentLength[k] =  rleSegmentOffsetTable[k+1]
-                                     - rleSegmentOffsetTable[k];
-                ftellRes = ftell(Fp);
-                SkipBytes(rleSegmentLength[k]);
-             }
+             rleSegmentLength[k] =  rleSegmentOffsetTable[k+1]
+                                  - rleSegmentOffsetTable[k];
+             SkipBytes(rleSegmentLength[k]);
           }
+       }
 
-          rleSegmentLength[nbRleSegments] = fragmentLength 
-                                          - rleSegmentOffsetTable[nbRleSegments];
-          ftellRes = ftell(Fp);
-          SkipBytes(rleSegmentLength[nbRleSegments]);
-      }
+       rleSegmentLength[nbRleSegments] = frameLength 
+                                      - rleSegmentOffsetTable[nbRleSegments];
+       SkipBytes(rleSegmentLength[nbRleSegments]);
 
-      // Make sure that at the end of the item we encounter a 'Sequence
-      // Delimiter Item':
-      if ( !ReadTag(0xfffe, 0xe0dd) )
-      {
-         dbg.Verbose(0, "gdcmDocument::Parse7FE0: no sequence delimiter item");
-         dbg.Verbose(0, "    at end of RLE item sequence");
-      }
+       // Store the collected info
+       gdcmRLEFrame* newFrameInfo = new gdcmRLEFrame;
+       newFrameInfo->NumberFragments = nbRleSegments;
+       for( unsigned int k = 1; k <= nbRleSegments; k++ )
+       {
+          newFrameInfo->Offset[k] = frameOffset + rleSegmentOffsetTable[k];
+          newFrameInfo->Length[k] = rleSegmentLength[k];
+       }
+       RLEInfo.Frames.push_back( newFrameInfo );
+   }
+
+   // Make sure that at the end of the item we encounter a 'Sequence
+   // Delimiter Item':
+   if ( !ReadTag(0xfffe, 0xe0dd) )
+   {
+      dbg.Verbose(0, "gdcmDocument::ComputeRLEInfo: no sequence delimiter ");
+      dbg.Verbose(0, "    item at end of RLE item sequence");
    }
 }