changeset 1824:b530c3dfe2a6

refactoring image decoding
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Nov 2015 14:14:32 +0100
parents 0ef4e6e66b56
children f0f8a94c0858
files NEWS OrthancServer/IDicomImageDecoder.h OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/Internals/DicomImageDecoder.h OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/ParsedDicomFile.h
diffstat 7 files changed, 119 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Nov 25 10:32:54 2015 +0100
+++ b/NEWS	Wed Nov 25 14:14:32 2015 +0100
@@ -60,7 +60,7 @@
 -----------
 
 * Full indexation of the patient/study tags to speed up searches and C-Find
-* Full refactoring of the searching features
+* Many refactorings, notably of the searching features and of the image decoding
 * C-Move SCP for studies using AccessionNumber tag
 * Fix issue 4 (C-Store Association not renegotiated on Specific-to-specific transfer syntax change)
 * "--logdir" flag creates a single log file instead of 3 separate files for errors/warnings/infos
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/IDicomImageDecoder.h	Wed Nov 25 14:14:32 2015 +0100
@@ -0,0 +1,54 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Core/Images/ImageBuffer.h"
+
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class ParsedDicomFile;
+
+  class IDicomImageDecoder : public boost::noncopyable
+  {
+  public:
+    virtual ~IDicomImageDecoder()
+    {
+    }
+
+    virtual bool Decode(ImageBuffer& target,
+                        ParsedDicomFile& dicom,
+                        unsigned int frame) = 0;
+  };
+}
--- a/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Nov 25 10:32:54 2015 +0100
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Nov 25 14:14:32 2015 +0100
@@ -87,6 +87,7 @@
 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h"
 #include "../ToDcmtkBridge.h"
 #include "../FromDcmtkBridge.h"
+#include "../ParsedDicomFile.h"
 
 #include <boost/lexical_cast.hpp>
 
@@ -531,9 +532,11 @@
 
 
   bool DicomImageDecoder::Decode(ImageBuffer& target,
-                                 DcmDataset& dataset,
+                                 ParsedDicomFile& dicom,
                                  unsigned int frame)
   {
+    DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
+
     if (IsUncompressedImage(dataset))
     {
       DecodeUncompressedImage(target, dataset, frame);
@@ -588,20 +591,11 @@
   }
 
 
-  bool DicomImageDecoder::DecodeAndTruncate(ImageBuffer& target,
-                                            DcmDataset& dataset,
-                                            unsigned int frame,
-                                            PixelFormat format,
-                                            bool allowColorConversion)
+  bool DicomImageDecoder::TruncateDecodedImage(ImageBuffer& target,
+                                               ImageBuffer& source,
+                                               PixelFormat format,
+                                               bool allowColorConversion)
   {
-    // TODO Special case for uncompressed images
-    
-    ImageBuffer source;
-    if (!Decode(source, dataset, frame))
-    {
-      return false;
-    }
-
     // If specified, prevent the conversion between color and
     // grayscale images
     bool isSourceColor = IsColorImage(source.GetFormat());
@@ -634,18 +628,9 @@
   }
 
 
-  bool DicomImageDecoder::DecodePreview(ImageBuffer& target,
-                                        DcmDataset& dataset,
-                                        unsigned int frame)
+  bool DicomImageDecoder::PreviewDecodedImage(ImageBuffer& target,
+                                              ImageBuffer& source)
   {
-    // TODO Special case for uncompressed images
-    
-    ImageBuffer source;
-    if (!Decode(source, dataset, frame))
-    {
-      return false;
-    }
-
     switch (source.GetFormat())
     {
       case PixelFormat_RGB24:
--- a/OrthancServer/Internals/DicomImageDecoder.h	Wed Nov 25 10:32:54 2015 +0100
+++ b/OrthancServer/Internals/DicomImageDecoder.h	Wed Nov 25 14:14:32 2015 +0100
@@ -34,11 +34,11 @@
 
 #include <dcmtk/dcmdata/dcfilefo.h>
 
-#include "../../Core/Images/ImageBuffer.h"
+#include "../IDicomImageDecoder.h"
 
 namespace Orthanc
 {
-  class DicomImageDecoder
+  class DicomImageDecoder : public IDicomImageDecoder
   {
   private:
     class ImageSource;
@@ -65,18 +65,16 @@
 #endif
 
   public:
-    static bool Decode(ImageBuffer& target,
-                       DcmDataset& dataset,
-                       unsigned int frame);
+    virtual bool Decode(ImageBuffer& target,
+                        ParsedDicomFile& dicom,
+                        unsigned int frame);
 
-    static bool DecodeAndTruncate(ImageBuffer& target,
-                                  DcmDataset& dataset,
-                                  unsigned int frame,
-                                  PixelFormat format,
-                                  bool allowColorConversion);
+    static bool TruncateDecodedImage(ImageBuffer& target,
+                                     ImageBuffer& source,
+                                     PixelFormat format,
+                                     bool allowColorConversion);
 
-    static bool DecodePreview(ImageBuffer& target,
-                              DcmDataset& dataset,
-                              unsigned int frame);
+    static bool PreviewDecodedImage(ImageBuffer& target,
+                                    ImageBuffer& source);
   };
 }
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Nov 25 10:32:54 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Nov 25 14:14:32 2015 +0100
@@ -39,6 +39,7 @@
 #include "../FromDcmtkBridge.h"
 #include "../ServerContext.h"
 #include "../SliceOrdering.h"
+#include "../Internals/DicomImageDecoder.h"
 
 
 namespace Orthanc
@@ -265,6 +266,7 @@
     class ImageToEncode
     {
     private:
+      IDicomImageDecoder& decoder_;
       std::string         format_;
       std::string         encoded_;
       ParsedDicomFile&    dicom_;
@@ -272,9 +274,11 @@
       ImageExtractionMode mode_;
 
     public:
-      ImageToEncode(ParsedDicomFile& dicom,
+      ImageToEncode(IDicomImageDecoder& decoder,
+                    ParsedDicomFile& dicom,
                     unsigned int frame,
                     ImageExtractionMode mode) : 
+        decoder_(decoder),
         dicom_(dicom),
         frame_(frame),
         mode_(mode)
@@ -310,6 +314,11 @@
       {
         output.AnswerBuffer(encoded_, format_);
       }
+
+      IDicomImageDecoder& GetDecoder() const
+      {
+        return decoder_;
+      }
     };
 
     class EncodePng : public HttpContentNegociation::IHandler
@@ -327,7 +336,8 @@
       {
         assert(type == "image");
         assert(subtype == "png");
-        image_.GetDicom().ExtractPngImage(image_.GetTarget(), image_.GetFrame(), image_.GetMode());
+        image_.GetDicom().ExtractPngImage(image_.GetTarget(), image_.GetDecoder(), 
+                                          image_.GetFrame(), image_.GetMode());
         image_.SetFormat("image/png");
       }
     };
@@ -367,7 +377,8 @@
       {
         assert(type == "image");
         assert(subtype == "jpeg");
-        image_.GetDicom().ExtractJpegImage(image_.GetTarget(), image_.GetFrame(), image_.GetMode(), quality_);
+        image_.GetDicom().ExtractJpegImage(image_.GetTarget(), image_.GetDecoder(), 
+                                           image_.GetFrame(), image_.GetMode(), quality_);
         image_.SetFormat("image/jpeg");
       }
     };
@@ -399,7 +410,8 @@
 
     try
     {
-      ImageToEncode image(dicom, frame, mode);
+      DicomImageDecoder decoder;  // TODO plugins
+      ImageToEncode image(decoder, dicom, frame, mode);
 
       HttpContentNegociation negociation;
       EncodePng png(image);          negociation.Register("image/png", png);
@@ -453,7 +465,8 @@
 
     ParsedDicomFile dicom(dicomContent);
     ImageBuffer buffer;
-    dicom.ExtractImage(buffer, frame);
+    DicomImageDecoder decoder;  // TODO plugin
+    dicom.ExtractImage(buffer, decoder, frame);
 
     ImageAccessor accessor(buffer.GetConstAccessor());
 
--- a/OrthancServer/ParsedDicomFile.cpp	Wed Nov 25 10:32:54 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.cpp	Wed Nov 25 14:14:32 2015 +0100
@@ -888,7 +888,7 @@
   }
 
 
-  DcmFileFormat& ParsedDicomFile::GetDcmtkObject()
+  DcmFileFormat& ParsedDicomFile::GetDcmtkObject() const
   {
     return *pimpl_->file_.get();
   }
@@ -1049,11 +1049,10 @@
 
   
   void ParsedDicomFile::ExtractImage(ImageBuffer& result,
+                                     IDicomImageDecoder& decoder,
                                      unsigned int frame)
   {
-    DcmDataset& dataset = *pimpl_->file_->getDataset();
-
-    if (!DicomImageDecoder::Decode(result, dataset, frame))
+    if (!decoder.Decode(result, *this, frame))
     {
       throw OrthancException(ErrorCode_BadFileFormat);
     }
@@ -1061,29 +1060,34 @@
 
 
   void ParsedDicomFile::ExtractImage(ImageBuffer& result,
+                                     IDicomImageDecoder& decoder,
                                      unsigned int frame,
                                      ImageExtractionMode mode)
   {
-    DcmDataset& dataset = *pimpl_->file_->getDataset();
+    ImageBuffer source;
+    if (!decoder.Decode(source, *this, frame))
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
 
     bool ok = false;
 
     switch (mode)
     {
       case ImageExtractionMode_UInt8:
-        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_Grayscale8, false);
+        ok = DicomImageDecoder::TruncateDecodedImage(result, source, PixelFormat_Grayscale8, false);
         break;
 
       case ImageExtractionMode_UInt16:
-        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_Grayscale16, false);
+        ok = DicomImageDecoder::TruncateDecodedImage(result, source, PixelFormat_Grayscale16, false);
         break;
 
       case ImageExtractionMode_Int16:
-        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_SignedGrayscale16, false);
+        ok = DicomImageDecoder::TruncateDecodedImage(result, source, PixelFormat_SignedGrayscale16, false);
         break;
 
       case ImageExtractionMode_Preview:
-        ok = DicomImageDecoder::DecodePreview(result, dataset, frame);
+        ok = DicomImageDecoder::PreviewDecodedImage(result, source);
         break;
 
       default:
@@ -1092,17 +1096,18 @@
 
     if (!ok)
     {
-      throw OrthancException(ErrorCode_BadFileFormat);
+      throw OrthancException(ErrorCode_NotImplemented);
     }
   }
 
 
   void ParsedDicomFile::ExtractPngImage(std::string& result,
+                                        IDicomImageDecoder& decoder,
                                         unsigned int frame,
                                         ImageExtractionMode mode)
   {
     ImageBuffer buffer;
-    ExtractImage(buffer, frame, mode);
+    ExtractImage(buffer, decoder, frame, mode);
 
     ImageAccessor accessor(buffer.GetConstAccessor());
     PngWriter writer;
@@ -1111,6 +1116,7 @@
 
 
   void ParsedDicomFile::ExtractJpegImage(std::string& result,
+                                         IDicomImageDecoder& decoder,
                                          unsigned int frame,
                                          ImageExtractionMode mode,
                                          uint8_t quality)
@@ -1122,7 +1128,7 @@
     }
 
     ImageBuffer buffer;
-    ExtractImage(buffer, frame, mode);
+    ExtractImage(buffer, decoder, frame, mode);
 
     ImageAccessor accessor(buffer.GetConstAccessor());
     JpegWriter writer;
--- a/OrthancServer/ParsedDicomFile.h	Wed Nov 25 10:32:54 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.h	Wed Nov 25 14:14:32 2015 +0100
@@ -33,11 +33,10 @@
 #pragma once
 
 #include "../Core/DicomFormat/DicomInstanceHasher.h"
+#include "../Core/IDynamicObject.h"
 #include "../Core/RestApi/RestApiOutput.h"
+#include "IDicomImageDecoder.h"
 #include "ServerEnumerations.h"
-#include "../Core/Images/ImageAccessor.h"
-#include "../Core/Images/ImageBuffer.h"
-#include "../Core/IDynamicObject.h"
 
 class DcmDataset;
 class DcmFileFormat;
@@ -77,7 +76,7 @@
 
     ~ParsedDicomFile();
 
-    DcmFileFormat& GetDcmtkObject();
+    DcmFileFormat& GetDcmtkObject() const;
 
     ParsedDicomFile* Clone();
 
@@ -128,17 +127,21 @@
                     const std::string& content);
 
     void ExtractImage(ImageBuffer& result,
+                      IDicomImageDecoder& decoder,
                       unsigned int frame);
 
     void ExtractImage(ImageBuffer& result,
+                      IDicomImageDecoder& decoder,
                       unsigned int frame,
                       ImageExtractionMode mode);
 
     void ExtractPngImage(std::string& result,
+                         IDicomImageDecoder& decoder,
                          unsigned int frame,
                          ImageExtractionMode mode);
 
     void ExtractJpegImage(std::string& result,
+                          IDicomImageDecoder& decoder,
                           unsigned int frame,
                           ImageExtractionMode mode,
                           uint8_t quality);