changeset 62:f45cec2c32e2

Speed-up in the Web viewer plugin
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 25 Nov 2016 18:21:15 +0100
parents 147bd6dc28db
children 3252fbf149e3
files Framework/DicomToolbox.cpp Framework/DicomToolbox.h Framework/Inputs/DicomPyramid.cpp Framework/Inputs/DicomPyramidInstance.cpp Framework/Targets/OrthancTarget.cpp NEWS Resources/Orthanc/Plugins/Samples/Common/IOrthancConnection.cpp ViewerPlugin/CMakeLists.txt
diffstat 8 files changed, 142 insertions(+), 143 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/DicomToolbox.cpp	Fri Nov 25 17:15:55 2016 +0100
+++ b/Framework/DicomToolbox.cpp	Fri Nov 25 18:21:15 2016 +0100
@@ -30,8 +30,6 @@
 #  include <dcmtk/dcmdata/dcsequen.h>
 #endif
 
-#include <boost/lexical_cast.hpp>
-
 namespace OrthancWSI
 {
   namespace DicomToolbox
@@ -160,92 +158,5 @@
       }
     }
 #endif
-
-
-    bool GetStringTag(std::string& result,
-                      const Json::Value& simplifiedTags,
-                      const std::string& tagName,
-                      const std::string& defaultValue)
-    {
-      if (simplifiedTags.type() != Json::objectValue)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-
-      if (!simplifiedTags.isMember(tagName))
-      {
-        result = defaultValue;
-        return false;
-      }
-      else if (simplifiedTags[tagName].type() != Json::stringValue)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-      else
-      {
-        result = simplifiedTags[tagName].asString();
-        return true;
-      }
-    }
-
-
-    std::string GetMandatoryStringTag(const Json::Value& simplifiedTags,
-                                      const std::string& tagName)
-    {
-      std::string s;
-      if (GetStringTag(s, simplifiedTags, tagName, ""))
-      {
-        return s;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentTag);        
-      }
-    }
-
-
-    const Json::Value& GetSequenceTag(const Json::Value& simplifiedTags,
-                                      const std::string& tagName)
-    {
-      if (simplifiedTags.type() != Json::objectValue ||
-          !simplifiedTags.isMember(tagName) ||
-          simplifiedTags[tagName].type() != Json::arrayValue)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-
-      return simplifiedTags[tagName];
-    }
-
-
-    int GetIntegerTag(const Json::Value& simplifiedTags,
-                      const std::string& tagName)
-    {
-      try
-      {
-        std::string s = Orthanc::Toolbox::StripSpaces(GetMandatoryStringTag(simplifiedTags, tagName));
-        return boost::lexical_cast<int>(s);
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);        
-      }
-    }
-
-
-    unsigned int GetUnsignedIntegerTag(const Json::Value& simplifiedTags,
-                                       const std::string& tagName)
-    {
-      int value = GetIntegerTag(simplifiedTags, tagName);
-
-      if (value >= 0)
-      {
-        return static_cast<unsigned int>(value);
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-    }
   }
 }
--- a/Framework/DicomToolbox.h	Fri Nov 25 17:15:55 2016 +0100
+++ b/Framework/DicomToolbox.h	Fri Nov 25 18:21:15 2016 +0100
@@ -62,22 +62,5 @@
     std::string GetStringTag(DcmItem& dataset,
                              const DcmTagKey& key);
 #endif
-
-    bool GetStringTag(std::string& result,
-                      const Json::Value& simplifiedTags,
-                      const std::string& tagName,
-                      const std::string& defaultValue);
-
-    std::string GetMandatoryStringTag(const Json::Value& simplifiedTags,
-                                      const std::string& tagName);
-
-    const Json::Value& GetSequenceTag(const Json::Value& simplifiedTags,
-                                      const std::string& tagName);
-
-    int GetIntegerTag(const Json::Value& simplifiedTags,
-                      const std::string& tagName);
-
-    unsigned int GetUnsignedIntegerTag(const Json::Value& simplifiedTags,
-                                       const std::string& tagName);
   }
 }
--- a/Framework/Inputs/DicomPyramid.cpp	Fri Nov 25 17:15:55 2016 +0100
+++ b/Framework/Inputs/DicomPyramid.cpp	Fri Nov 25 18:21:15 2016 +0100
@@ -65,12 +65,14 @@
     Json::Value series;
     OrthancPlugins::IOrthancConnection::RestApiGet(series, orthanc_, "/series/" + seriesId);
 
-    if (series.type() != Json::objectValue)
+    if (series.type() != Json::objectValue ||
+        !series.isMember("Instances") ||
+        series["Instances"].type() != Json::arrayValue)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
     }
 
-    const Json::Value& instances = DicomToolbox::GetSequenceTag(series, "Instances");
+    const Json::Value& instances = series["Instances"];
     instances_.reserve(instances.size());
 
     for (Json::Value::ArrayIndex i = 0; i < instances.size(); i++)
--- a/Framework/Inputs/DicomPyramidInstance.cpp	Fri Nov 25 17:15:55 2016 +0100
+++ b/Framework/Inputs/DicomPyramidInstance.cpp	Fri Nov 25 18:21:15 2016 +0100
@@ -24,16 +24,24 @@
 #include "../../Resources/Orthanc/Core/Logging.h"
 #include "../../Resources/Orthanc/Core/OrthancException.h"
 #include "../../Resources/Orthanc/Core/Toolbox.h"
+#include "../../Resources/Orthanc/Plugins/Samples/Common/DicomDatasetReader.h"
+#include "../../Resources/Orthanc/Plugins/Samples/Common/FullOrthancDataset.h"
 #include "../DicomToolbox.h"
 
 #include <cassert>
 
 namespace OrthancWSI
 {
-  static ImageCompression DetectImageCompression(const Json::Value& header)
+  static ImageCompression DetectImageCompression(OrthancPlugins::IOrthancConnection& orthanc,
+                                                 const std::string& instanceId)
   {
+    using namespace OrthancPlugins;
+
+    DicomDatasetReader header(new FullOrthancDataset
+                              (orthanc, "/instances/" + instanceId + "/header"));
+
     std::string s = Orthanc::Toolbox::StripSpaces
-      (DicomToolbox::GetMandatoryStringTag(header, "TransferSyntaxUID"));
+      (header.GetMandatoryStringValue(DICOM_TAG_TRANSFER_SYNTAX_UID));
 
     if (s == "1.2.840.10008.1.2" ||
         s == "1.2.840.10008.1.2.1")
@@ -57,10 +65,12 @@
   }
 
 
-  static Orthanc::PixelFormat DetectPixelFormat(const Json::Value& dicom)
+  static Orthanc::PixelFormat DetectPixelFormat(OrthancPlugins::DicomDatasetReader& reader)
   {
+    using namespace OrthancPlugins;
+
     std::string photometric = Orthanc::Toolbox::StripSpaces
-      (DicomToolbox::GetMandatoryStringTag(dicom, "PhotometricInterpretation"));
+      (reader.GetMandatoryStringValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION));
 
     if (photometric == "PALETTE")
     {
@@ -68,9 +78,9 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
     }
 
-    unsigned int bitsStored = DicomToolbox::GetUnsignedIntegerTag(dicom, "BitsStored");
-    unsigned int samplesPerPixel = DicomToolbox::GetUnsignedIntegerTag(dicom, "SamplesPerPixel");
-    bool isSigned = (DicomToolbox::GetUnsignedIntegerTag(dicom, "PixelRepresentation") != 0);
+    unsigned int bitsStored = reader.GetUnsignedIntegerValue(DICOM_TAG_BITS_STORED);
+    unsigned int samplesPerPixel = reader.GetUnsignedIntegerValue(DICOM_TAG_SAMPLES_PER_PIXEL);
+    bool isSigned = (reader.GetUnsignedIntegerValue(DICOM_TAG_PIXEL_REPRESENTATION) != 0);
 
     if (bitsStored == 8 &&
         samplesPerPixel == 1 &&
@@ -105,12 +115,8 @@
 
     if (!hasCompression_)
     {
-      Json::Value header;
-      OrthancPlugins::IOrthancConnection::RestApiGet
-        (header, orthanc, "/instances/" + instanceId_ + "/header?simplify");
-
+      compression_ = DetectImageCompression(orthanc, instanceId_);
       hasCompression_ = true;
-      compression_ = DetectImageCompression(header);
     }
 
     return compression_;
@@ -122,43 +128,49 @@
     instanceId_(instanceId),
     hasCompression_(false)
   {
-    Json::Value dicom;
-    OrthancPlugins::IOrthancConnection::RestApiGet
-      (dicom, orthanc, "/instances/" + instanceId + "/tags?simplify");
+    using namespace OrthancPlugins;
 
-    if (DicomToolbox::GetMandatoryStringTag(dicom, "SOPClassUID") != "1.2.840.10008.5.1.4.1.1.77.1.6" ||
-        DicomToolbox::GetMandatoryStringTag(dicom, "Modality") != "SM")
+    DicomDatasetReader reader(new FullOrthancDataset(orthanc, "/instances/" + instanceId + "/tags"));
+
+    if (reader.GetMandatoryStringValue(DICOM_TAG_SOP_CLASS_UID) != "1.2.840.10008.5.1.4.1.1.77.1.6" ||
+        reader.GetMandatoryStringValue(DICOM_TAG_MODALITY) != "SM")
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
 
-    format_ = DetectPixelFormat(dicom);
-    tileWidth_ = DicomToolbox::GetUnsignedIntegerTag(dicom, "Columns");
-    tileHeight_ = DicomToolbox::GetUnsignedIntegerTag(dicom, "Rows");
-    totalWidth_ = DicomToolbox::GetUnsignedIntegerTag(dicom, "TotalPixelMatrixColumns");
-    totalHeight_ = DicomToolbox::GetUnsignedIntegerTag(dicom, "TotalPixelMatrixRows");
+    format_ = DetectPixelFormat(reader);
+    tileWidth_ = reader.GetUnsignedIntegerValue(DICOM_TAG_COLUMNS);
+    tileHeight_ = reader.GetUnsignedIntegerValue(DICOM_TAG_ROWS);
+    totalWidth_ = reader.GetUnsignedIntegerValue(DICOM_TAG_TOTAL_PIXEL_MATRIX_COLUMNS);
+    totalHeight_ = reader.GetUnsignedIntegerValue(DICOM_TAG_TOTAL_PIXEL_MATRIX_ROWS);
 
-    const Json::Value& frames = DicomToolbox::GetSequenceTag(dicom, "PerFrameFunctionalGroupsSequence");
+    size_t countFrames;
+    if (!reader.GetDataset().GetSequenceSize(countFrames, DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
 
-    if (frames.size() != DicomToolbox::GetUnsignedIntegerTag(dicom, "NumberOfFrames"))
+    if (countFrames != reader.GetUnsignedIntegerValue(DICOM_TAG_NUMBER_OF_FRAMES))
     {
       LOG(ERROR) << "Mismatch between the number of frames in instance: " << instanceId;
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
 
-    frames_.resize(frames.size());
+    frames_.resize(countFrames);
 
-    for (Json::Value::ArrayIndex i = 0; i < frames.size(); i++)
+    for (size_t i = 0; i < countFrames; i++)
     {
-      const Json::Value& frame = DicomToolbox::GetSequenceTag(frames[i], "PlanePositionSlideSequence");
-      if (frame.size() != 1)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
+      int xx = reader.GetIntegerValue(DicomPath(DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE, i,
+                                                DICOM_TAG_PLANE_POSITION_SLIDE_SEQUENCE, 0,
+                                                DICOM_TAG_COLUMN_POSITION_IN_TOTAL_IMAGE_PIXEL_MATRIX));
+
+      int yy = reader.GetIntegerValue(DicomPath(DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE, i,
+                                                DICOM_TAG_PLANE_POSITION_SLIDE_SEQUENCE, 0,
+                                                DICOM_TAG_ROW_POSITION_IN_TOTAL_IMAGE_PIXEL_MATRIX));
 
       // "-1", because coordinates are shifted by 1 in DICOM
-      int xx = DicomToolbox::GetIntegerTag(frame[0], "ColumnPositionInTotalImagePixelMatrix") - 1;
-      int yy = DicomToolbox::GetIntegerTag(frame[0], "RowPositionInTotalImagePixelMatrix") - 1;
+      xx -= 1;
+      yy -= 1;
 
       unsigned int x = static_cast<unsigned int>(xx);
       unsigned int y = static_cast<unsigned int>(yy);
--- a/Framework/Targets/OrthancTarget.cpp	Fri Nov 25 17:15:55 2016 +0100
+++ b/Framework/Targets/OrthancTarget.cpp	Fri Nov 25 18:21:15 2016 +0100
@@ -40,14 +40,28 @@
     Json::Value result;
     OrthancPlugins::IOrthancConnection::RestApiPost(result, *orthanc_, "/instances", file);
 
-    std::string instanceId = DicomToolbox::GetMandatoryStringTag(result, "ID");
+    if (result.type() != Json::objectValue ||
+        !result.isMember("ID") ||
+        result["ID"].type() != Json::stringValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+    }
+
+    std::string instanceId = result["ID"].asString();
 
     if (first_)
     {
       Json::Value instance;
       OrthancPlugins::IOrthancConnection::RestApiGet(instance, *orthanc_, "/instances/" + instanceId);
 
-      std::string seriesId = DicomToolbox::GetMandatoryStringTag(instance, "ParentSeries");
+      if (instance.type() != Json::objectValue ||
+          !instance.isMember("ParentSeries") ||
+          instance["ParentSeries"].type() != Json::stringValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+      }
+
+      std::string seriesId = instance["ParentSeries"].asString();
 
       LOG(WARNING) << "ID of the whole-slide image series in Orthanc: " << seriesId;
       first_ = false;
--- a/NEWS	Fri Nov 25 17:15:55 2016 +0100
+++ b/NEWS	Fri Nov 25 18:21:15 2016 +0100
@@ -1,6 +1,8 @@
 Pending changes in the mainline
 ===============================
 
+* Speed-up in the Web viewer plugin
+* Refactorings
 
 
 Version 0.1 (2016/10/28)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Orthanc/Plugins/Samples/Common/IOrthancConnection.cpp	Fri Nov 25 18:21:15 2016 +0100
@@ -0,0 +1,72 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 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/>.
+ **/
+
+
+#include "IOrthancConnection.h"
+
+#include "OrthancPluginCppWrapper.h"
+
+#include <json/reader.h>
+
+namespace OrthancPlugins
+{
+  void IOrthancConnection::ParseJson(Json::Value& result,
+                                     const std::string& content)
+  {
+    Json::Reader reader;
+    
+    if (!reader.parse(content, result))
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(OrthancPluginErrorCode_BadFileFormat);
+    }
+  }
+
+
+  void IOrthancConnection::RestApiGet(Json::Value& result,
+                                      IOrthancConnection& orthanc,
+                                      const std::string& uri)
+  {
+    std::string content;
+    orthanc.RestApiGet(content, uri);
+    ParseJson(result, content);
+  }
+
+
+  void IOrthancConnection::RestApiPost(Json::Value& result,
+                                       IOrthancConnection& orthanc,
+                                       const std::string& uri,
+                                       const std::string& body)
+  {
+    std::string content;
+    orthanc.RestApiPost(content, uri, body);
+    ParseJson(result, content);
+  }
+}
--- a/ViewerPlugin/CMakeLists.txt	Fri Nov 25 17:15:55 2016 +0100
+++ b/ViewerPlugin/CMakeLists.txt	Fri Nov 25 18:21:15 2016 +0100
@@ -187,6 +187,9 @@
   ${ORTHANC_ROOT}/Core/MultiThreading/Semaphore.cpp
   ${ORTHANC_ROOT}/Core/SystemToolbox.cpp
   ${ORTHANC_ROOT}/Core/Toolbox.cpp
+  ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp
+  ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp
+  ${ORTHANC_ROOT}/Plugins/Samples/Common/FullOrthancDataset.cpp
   ${ORTHANC_ROOT}/Plugins/Samples/Common/IOrthancConnection.cpp
   ${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginConnection.cpp
   ${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp