changeset 3745:113a7b994a12

extracting the raw frame in the transcoder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 12 Mar 2020 21:48:35 +0100
parents accf1b60b108
children 875308321fa8 7b6058f8f7aa edabdf07ee83
files Core/DicomParsing/Internals/DicomFrameIndex.cpp Core/DicomParsing/Internals/DicomFrameIndex.h Core/DicomParsing/ParsedDicomFile.cpp UnitTestsSources/FromDcmtkTests.cpp
diffstat 4 files changed, 67 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomParsing/Internals/DicomFrameIndex.cpp	Thu Mar 12 17:58:34 2020 +0100
+++ b/Core/DicomParsing/Internals/DicomFrameIndex.cpp	Thu Mar 12 21:48:35 2020 +0100
@@ -68,7 +68,10 @@
       uint32_t length = item->getLength();
       if (length == 0)
       {
-        table.clear();
+        // Degenerate case: Empty offset table means only one frame
+        // that overlaps all the fragments
+        table.resize(1);
+        table[0] = 0;
         return;
       }
 
@@ -146,7 +149,6 @@
         throw OrthancException(ErrorCode_BadFileFormat);
       }
 
-
       // Loop over the fragments (ignoring the offset table). This is
       // an alternative, faster implementation to DCMTK's
       // "DcmCodec::determineStartFragment()".
@@ -318,46 +320,10 @@
   };
 
 
-
-  bool DicomFrameIndex::IsVideo(DcmFileFormat& dicom)
+  unsigned int DicomFrameIndex::GetFramesCount(DcmDataset& dicom)
   {
-    // Retrieve the transfer syntax from the DICOM header
-    const char* value = NULL;
-    if (!dicom.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, value).good() ||
-        value == NULL)
-    {
-      return false;
-    }
-
-    const std::string transferSyntax(value);
-
-    // Video standards supported in DICOM 2016a
-    // http://dicom.nema.org/medical/dicom/2016a/output/html/part05.html
-    if (transferSyntax == "1.2.840.10008.1.2.4.100" ||  // MPEG2 MP@ML option of ISO/IEC MPEG2
-        transferSyntax == "1.2.840.10008.1.2.4.101" ||  // MPEG2 MP@HL option of ISO/IEC MPEG2
-        transferSyntax == "1.2.840.10008.1.2.4.102" ||  // MPEG-4 AVC/H.264 High Profile / Level 4.1 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.103" ||  // MPEG-4 AVC/H.264 BD-compat High Profile / Level 4.1 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.104" ||  // MPEG-4 AVC/H.264 High Profile / Level 4.2 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.105" ||  // MPEG-4 AVC/H.264 High Profile / Level 4.2 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.106")    // MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2 of the ITU-T H.264
-    {
-      return true;
-    }
-
-    return false;
-  }
-
-
-  unsigned int DicomFrameIndex::GetFramesCount(DcmFileFormat& dicom)
-  {
-    // Assume 1 frame for video transfer syntaxes
-    if (IsVideo(dicom))
-    {
-      return 1;
-    }        
-
     const char* tmp = NULL;
-    if (!dicom.getDataset()->findAndGetString(DCM_NumberOfFrames, tmp).good() ||
+    if (!dicom.findAndGetString(DCM_NumberOfFrames, tmp).good() ||
         tmp == NULL)
     {
       return 1;
@@ -378,12 +344,12 @@
     }
     else
     {
-      return count;
+      return static_cast<unsigned int>(count);
     }
   }
 
 
-  DicomFrameIndex::DicomFrameIndex(DcmFileFormat& dicom)
+  DicomFrameIndex::DicomFrameIndex(DcmDataset& dicom)
   {
     countFrames_ = GetFramesCount(dicom);
     if (countFrames_ == 0)
@@ -392,10 +358,8 @@
       return;
     }
 
-    DcmDataset& dataset = *dicom.getDataset();
-
     // Test whether this image is composed of a sequence of fragments
-    DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
+    DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dicom);
     if (pixelSequence != NULL)
     {
       index_.reset(new FragmentIndex(pixelSequence, countFrames_));
@@ -404,18 +368,18 @@
 
     // Extract information about the image structure
     DicomMap tags;
-    FromDcmtkBridge::ExtractDicomSummary(tags, dataset);
+    FromDcmtkBridge::ExtractDicomSummary(tags, dicom);
 
     DicomImageInformation information(tags);
 
     // Access to the raw pixel data
-    if (DicomImageDecoder::IsPsmctRle1(dataset))
+    if (DicomImageDecoder::IsPsmctRle1(dicom))
     {
-      index_.reset(new PsmctRle1Index(dataset, countFrames_, information.GetFrameSize()));
+      index_.reset(new PsmctRle1Index(dicom, countFrames_, information.GetFrameSize()));
     }
     else
     {
-      index_.reset(new UncompressedIndex(dataset, countFrames_, information.GetFrameSize()));
+      index_.reset(new UncompressedIndex(dicom, countFrames_, information.GetFrameSize()));
     }
   }
 
--- a/Core/DicomParsing/Internals/DicomFrameIndex.h	Thu Mar 12 17:58:34 2020 +0100
+++ b/Core/DicomParsing/Internals/DicomFrameIndex.h	Thu Mar 12 21:48:35 2020 +0100
@@ -67,7 +67,7 @@
     unsigned int             countFrames_;
 
   public:
-    DicomFrameIndex(DcmFileFormat& dicom);
+    DicomFrameIndex(DcmDataset& dicom);
 
     unsigned int GetFramesCount() const
     {
@@ -77,8 +77,6 @@
     void GetRawFrame(std::string& frame,
                      unsigned int index) const;
 
-    static bool IsVideo(DcmFileFormat& dicom);
-
-    static unsigned int GetFramesCount(DcmFileFormat& dicom);
+    static unsigned int GetFramesCount(DcmDataset& dicom);
   };
 }
--- a/Core/DicomParsing/ParsedDicomFile.cpp	Thu Mar 12 17:58:34 2020 +0100
+++ b/Core/DicomParsing/ParsedDicomFile.cpp	Thu Mar 12 21:48:35 2020 +0100
@@ -1557,7 +1557,9 @@
   {
     if (pimpl_->frameIndex_.get() == NULL)
     {
-      pimpl_->frameIndex_.reset(new DicomFrameIndex(*pimpl_->file_));
+      assert(pimpl_->file_ != NULL &&
+             pimpl_->file_->getDataset() != NULL);
+      pimpl_->frameIndex_.reset(new DicomFrameIndex(*pimpl_->file_->getDataset()));
     }
 
     pimpl_->frameIndex_->GetRawFrame(target, frameId);
@@ -1589,7 +1591,9 @@
 
   unsigned int ParsedDicomFile::GetFramesCount() const
   {
-    return DicomFrameIndex::GetFramesCount(*pimpl_->file_);
+    assert(pimpl_->file_ != NULL &&
+           pimpl_->file_->getDataset() != NULL);
+    return DicomFrameIndex::GetFramesCount(*pimpl_->file_->getDataset());
   }
 
 
--- a/UnitTestsSources/FromDcmtkTests.cpp	Thu Mar 12 17:58:34 2020 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Thu Mar 12 21:48:35 2020 +0100
@@ -1917,7 +1917,7 @@
 
 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
 
-#include "../Core/DicomFormat/DicomImageInformation.h"
+#include "../Core/DicomParsing/Internals/DicomFrameIndex.h"
 
 #include <dcmtk/dcmdata/dcostrmb.h>
 #include <dcmtk/dcmdata/dcpixel.h>
@@ -1954,12 +1954,11 @@
   class DcmtkTranscoder : public IDicomTranscoder
   {
   private:
-    std::unique_ptr<DcmFileFormat>          dicom_;
-    DicomTransferSyntax                     transferSyntax_;
-    std::string                             sopClassUid_;
-    std::string                             sopInstanceUid_;
-    DicomMap                                tags_;
-    std::unique_ptr<DicomImageInformation>  info_;
+    std::unique_ptr<DcmFileFormat>    dicom_;
+    std::unique_ptr<DicomFrameIndex>  index_;
+    DicomTransferSyntax               transferSyntax_;
+    std::string                       sopClassUid_;
+    std::string                       sopInstanceUid_;
 
     void Setup(DcmFileFormat* dicom)
     {
@@ -1972,12 +1971,8 @@
       }
 
       DcmDataset& dataset = *dicom_->getDataset();
+      index_.reset(new DicomFrameIndex(dataset));
 
-      tags_.Clear();
-      FromDcmtkBridge::ExtractDicomSummary(tags_, dataset);
-
-      info_.reset(new DicomImageInformation(tags_));
-      
       E_TransferSyntax xfer = dataset.getOriginalXfer();
       if (xfer == EXS_Unknown)
       {
@@ -1997,12 +1992,20 @@
           "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer));
       }
 
-      if (!tags_.LookupStringValue(sopClassUid_, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) ||
-          !tags_.LookupStringValue(sopInstanceUid_, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
+      const char* a = NULL;
+      const char* b = NULL;
+
+      if (!dataset.findAndGetString(DCM_SOPClassUID, a).good() ||
+          !dataset.findAndGetString(DCM_SOPInstanceUID, b).good() ||
+          a == NULL ||
+          b == NULL)
       {
         throw OrthancException(ErrorCode_BadFileFormat,
                                "Missing SOP class/instance UID in DICOM instance");
       }
+
+      sopClassUid_.assign(a);
+      sopInstanceUid_.assign(b);
     }
     
   public:
@@ -2034,7 +2037,7 @@
 
     virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE
     {
-      return info_->GetNumberOfFrames();
+      return index_->GetFramesCount();
     }
 
     virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE
@@ -2046,55 +2049,27 @@
     virtual void GetCompressedFrame(std::string& target,
                                     unsigned int frame) ORTHANC_OVERRIDE
     {
+#if 1
+      index_->GetRawFrame(target, frame);
+      printf("%d: %d\n", frame, target.size());
+#endif
+
+#if 1
       assert(dicom_->getDataset() != NULL);
       DcmDataset& dataset = *dicom_->getDataset();
       
       DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
 
-      if (pixelSequence == NULL)
-      {
-        // This is an uncompressed frame
-
-        DcmElement* element = NULL;
-        if (dataset.findAndGetElement(DCM_PixelData, element).good() &&
-            element != NULL)
-        {
-          Uint8* pixelData = NULL;
-        
-          if (element->getUint8Array(pixelData).good() &&
-              pixelData != NULL)
-          {    
-            // TODO => use "pixelData"
-            printf("RAW %d\n", element->getLength());
-          }
-          else
-          {
-            throw OrthancException(ErrorCode_BadFileFormat,
-                                   "Cannot access uncompressed pixel data");
-          }
-        }
-        else
-        {
-          std::string decoded;
-          if (DicomImageDecoder::DecodePsmctRle1(decoded, dataset))
-          {
-            // TODO => use "decoded"
-          }
-          else
-          {
-            throw OrthancException(ErrorCode_BadFileFormat,
-                                   "Cannot access uncompressed pixel data");
-          }
-        }
-      }
-      else
+      if (pixelSequence != NULL &&
+          frame == 0 &&
+          pixelSequence->card() != GetFramesCount() + 1)
       {
         printf("COMPRESSED\n");
-
+        
         // Check out "djcodecd.cc"
-
+        
         printf("%d fragments\n", pixelSequence->card());
-
+        
         // Skip the first fragment, that is the offset table
         for (unsigned long i = 1; ;i++)
         {
@@ -2109,6 +2084,7 @@
           }
         }
       }
+#endif
     }
 
     virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes,
@@ -2210,8 +2186,20 @@
          transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(),
          transcoder.GetFramesCount());
 
-  std::string f;
-  transcoder.GetCompressedFrame(f, 0);
+  for (size_t i = 0; i < transcoder.GetFramesCount(); i++)
+  {
+    std::string f;
+    transcoder.GetCompressedFrame(f, i);
+
+    if (i == 0)
+    {
+      static unsigned int i = 0;
+      char buf[1024];
+      sprintf(buf, "/tmp/frame-%06d.dcm", i++);
+      printf(">> %s\n", buf);
+      Orthanc::SystemToolbox::WriteFile(f, buf);
+    }
+  }
 
   printf("\n");
 }
@@ -2271,6 +2259,7 @@
     }
 
     TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm");
+    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm");
   }
 }