changeset 2174:2410a171ebfb

refactoring using DicomWebDataset and OrthancNativeDataset
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 22 Oct 2024 21:52:34 +0200
parents 239fb2c893c1
children 43ef60388fa2
files OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp OrthancStone/Sources/Toolbox/DicomInstanceParameters.h OrthancStone/Sources/Toolbox/OrthancDatasets/DicomWebDataset.cpp OrthancStone/Sources/Toolbox/OrthancDatasets/DicomWebDataset.h OrthancStone/Sources/Toolbox/OrthancDatasets/OrthancNativeDataset.cpp OrthancStone/Sources/Toolbox/OrthancDatasets/OrthancNativeDataset.h
diffstat 7 files changed, 640 insertions(+), 88 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Oct 22 15:41:38 2024 +0200
+++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Oct 22 21:52:34 2024 +0200
@@ -226,8 +226,10 @@
 
 list(APPEND ORTHANC_STONE_SOURCES
   ${ORTHANC_STONE_ROOT}/Toolbox/OrthancDatasets/DicomDatasetReader.cpp
+  ${ORTHANC_STONE_ROOT}/Toolbox/OrthancDatasets/DicomWebDataset.cpp
   ${ORTHANC_STONE_ROOT}/Toolbox/OrthancDatasets/FullOrthancDataset.cpp
   ${ORTHANC_STONE_ROOT}/Toolbox/OrthancDatasets/IOrthancConnection.cpp
+  ${ORTHANC_STONE_ROOT}/Toolbox/OrthancDatasets/OrthancNativeDataset.cpp
 
   ${ORTHANC_STONE_ROOT}/Fonts/FontRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Fonts/Glyph.cpp
--- a/OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp	Tue Oct 22 15:41:38 2024 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp	Tue Oct 22 21:52:34 2024 +0200
@@ -27,6 +27,9 @@
 #include "../Scene2D/FloatTextureSceneLayer.h"
 #include "GeometryToolbox.h"
 #include "ImageToolbox.h"
+#include "OrthancDatasets/DicomDatasetReader.h"
+#include "OrthancDatasets/DicomWebDataset.h"
+#include "OrthancDatasets/OrthancNativeDataset.h"
 
 #include <Images/Image.h>
 #include <Images/ImageProcessing.h>
@@ -231,68 +234,103 @@
     {
       instanceNumber_ = 0;
     }
+  }
 
 
+  void DicomInstanceParameters::InjectSequenceTags(const IDicomDataset& dataset)
+  {
+    /**
+     * Use DICOM tag "SequenceOfUltrasoundRegions" (0018,6011) in
+     * order to derive the pixel spacing on ultrasound (US) images
+     **/
+
+    static const Orthanc::DicomTag DICOM_TAG_SEQUENCE_OF_ULTRASOUND_REGIONS(0x0018, 0x6011);
+    static const Orthanc::DicomTag DICOM_TAG_PHYSICAL_UNITS_X_DIRECTION(0x0018, 0x6024);
+    static const Orthanc::DicomTag DICOM_TAG_PHYSICAL_UNITS_Y_DIRECTION(0x0018, 0x6026);
+    static const Orthanc::DicomTag DICOM_TAG_PHYSICAL_DELTA_X(0x0018, 0x602c);
+    static const Orthanc::DicomTag DICOM_TAG_PHYSICAL_DELTA_Y(0x0018, 0x602e);
+
+    DicomDatasetReader reader(dataset);
+
+    size_t size;
+
+    if (!data_.hasPixelSpacing_ &&
+        dataset.GetSequenceSize(size, Orthanc::DicomPath(DICOM_TAG_SEQUENCE_OF_ULTRASOUND_REGIONS)) &&
+        size == 1)
+    {
+      int directionX, directionY;
+      double deltaX, deltaY;
+
+      if (reader.GetIntegerValue(directionX, Orthanc::DicomPath(DICOM_TAG_SEQUENCE_OF_ULTRASOUND_REGIONS,
+                                                                0, DICOM_TAG_PHYSICAL_UNITS_X_DIRECTION)) &&
+          reader.GetIntegerValue(directionY, Orthanc::DicomPath(DICOM_TAG_SEQUENCE_OF_ULTRASOUND_REGIONS,
+                                                                0, DICOM_TAG_PHYSICAL_UNITS_Y_DIRECTION)) &&
+          reader.GetDoubleValue(deltaX, Orthanc::DicomPath(DICOM_TAG_SEQUENCE_OF_ULTRASOUND_REGIONS,
+                                                           0, DICOM_TAG_PHYSICAL_DELTA_X)) &&
+          reader.GetDoubleValue(deltaY, Orthanc::DicomPath(DICOM_TAG_SEQUENCE_OF_ULTRASOUND_REGIONS,
+                                                           0, DICOM_TAG_PHYSICAL_DELTA_Y)) &&
+          directionX == 0x0003 &&  // Centimeters
+          directionY == 0x0003)    // Centimeters
+      {
+        // Scene coordinates are expressed in millimeters => multiplication by 10
+        SetPixelSpacing(10.0 * deltaX, 10.0 * deltaY);
+      }
+    }
+
+
+    /**
+     * New in Stone Web viewer 2.2: Deal with Philips multiframe
+     * (cf. mail from Tomas Kenda on 2021-08-17). This cannot be done
+     * in LoadSeriesDetailsFromInstance, as the "Per Frame Functional
+     * Groups Sequence" is not available at that point.
+     **/
+
     static const Orthanc::DicomTag DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE(0x5200, 0x9230);
     static const Orthanc::DicomTag DICOM_TAG_FRAME_VOI_LUT_SEQUENCE_ATTRIBUTE(0x0028, 0x9132);
 
-    const Orthanc::DicomValue* frames = dicom.TestAndGetValue(DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE);
-    if (frames != NULL &&
-        hasNumberOfFrames_ &&
-        frames->IsSequence())
+    if (dataset.GetSequenceSize(size, Orthanc::DicomPath(DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE)))
     {
-      /**
-       * New in Stone Web viewer 2.2: Deal with Philips multiframe
-       * (cf. mail from Tomas Kenda on 2021-08-17). This cannot be done
-       * in LoadSeriesDetailsFromInstance, as the "Per Frame Functional Groups Sequence"
-       * is not available at that point.
-       **/
-
-      const Json::Value& sequence = frames->GetSequenceContent();
-
-      perFrameWindowing_.resize(numberOfFrames_);
+      data_.perFrameWindowing_.resize(data_.numberOfFrames_);
 
       // This corresponds to "ParsedDicomFile::GetDefaultWindowing()"
-      for (Json::ArrayIndex i = 0; i < sequence.size(); i++)
+      for (size_t i = 0; i < size; i++)
       {
-        if (i < numberOfFrames_ &&
-            sequence[i].isMember(DICOM_TAG_FRAME_VOI_LUT_SEQUENCE_ATTRIBUTE.Format()))
-        {
-          const Json::Value& v = sequence[i][DICOM_TAG_FRAME_VOI_LUT_SEQUENCE_ATTRIBUTE.Format()];
-
-          static const char* KEY_VALUE = "Value";
+        size_t tmp;
+        double center, width;
 
-          if (v.isMember(KEY_VALUE) &&
-              v[KEY_VALUE].type() == Json::arrayValue &&
-              v[KEY_VALUE].size() >= 1 &&
-              v[KEY_VALUE][0].isMember(Orthanc::DICOM_TAG_WINDOW_CENTER.Format()) &&
-              v[KEY_VALUE][0].isMember(Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()) &&
-              v[KEY_VALUE][0][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()].isMember(KEY_VALUE) &&
-              v[KEY_VALUE][0][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()].isMember(KEY_VALUE))
-          {
-            const Json::Value& scenter = v[KEY_VALUE][0][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()][KEY_VALUE];
-            const Json::Value& swidth = v[KEY_VALUE][0][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()][KEY_VALUE];
-
-            double center, width;
-            if (scenter.isString() &&
-                swidth.isString() &&
-                Orthanc::SerializationToolbox::ParseDouble(center, scenter.asString()) &&
-                Orthanc::SerializationToolbox::ParseDouble(width, swidth.asString()))
-            {
-              perFrameWindowing_[i] = Windowing(center, width);
-            }
-            else if (scenter.isNumeric() &&
-                     swidth.isNumeric())
-            {
-              perFrameWindowing_[i] = Windowing(scenter.asDouble(), swidth.asDouble());
-            }
-          }
+        if (dataset.GetSequenceSize(tmp, Orthanc::DicomPath(DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE, i,
+                                                            DICOM_TAG_FRAME_VOI_LUT_SEQUENCE_ATTRIBUTE)) &&
+            tmp == 1 &&
+            reader.GetDoubleValue(center, Orthanc::DicomPath(DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE, i,
+                                                             DICOM_TAG_FRAME_VOI_LUT_SEQUENCE_ATTRIBUTE, 0,
+                                                             Orthanc::DICOM_TAG_WINDOW_CENTER)) &&
+            reader.GetDoubleValue(width, Orthanc::DicomPath(DICOM_TAG_PER_FRAME_FUNCTIONAL_GROUPS_SEQUENCE, i,
+                                                            DICOM_TAG_FRAME_VOI_LUT_SEQUENCE_ATTRIBUTE, 0,
+                                                            Orthanc::DICOM_TAG_WINDOW_WIDTH)))
+        {
+          data_.perFrameWindowing_[i] = Windowing(center, width);
         }
       }
     }
   }
 
 
+  DicomInstanceParameters::DicomInstanceParameters(const DicomInstanceParameters& other) :
+    data_(other.data_),
+    tags_(other.tags_->Clone())
+  {
+  }
+
+
+  DicomInstanceParameters::DicomInstanceParameters(const Orthanc::DicomMap& dicom) :
+    data_(dicom),
+    tags_(dicom.Clone())
+  {
+    OrthancNativeDataset dataset(dicom);
+    InjectSequenceTags(dataset);
+  }
+
+
   double DicomInstanceParameters::GetSliceThickness() const
   {
     if (data_.hasSliceThickness_)
@@ -814,38 +852,8 @@
 
   void DicomInstanceParameters::EnrichUsingDicomWeb(const Json::Value& dicomweb)
   {
-    /**
-     * Use DICOM tag "SequenceOfUltrasoundRegions" (0018,6011) in
-     * order to derive the pixel spacing on ultrasound (US) images
-     **/
-    
-    if (!data_.hasPixelSpacing_)
-    {
-      const Json::Value* region = LookupDicomWebSingleValue(dicomweb, "00186011", "SQ");
-      if (region != NULL)
-      {
-        const Json::Value* physicalUnitsXDirection = LookupDicomWebSingleValue(*region, "00186024", "US");
-        const Json::Value* physicalUnitsYDirection = LookupDicomWebSingleValue(*region, "00186026", "US");
-        const Json::Value* physicalDeltaX = LookupDicomWebSingleValue(*region, "0018602C", "FD");
-        const Json::Value* physicalDeltaY = LookupDicomWebSingleValue(*region, "0018602E", "FD");
-        
-        if (physicalUnitsXDirection != NULL &&
-            physicalUnitsYDirection != NULL &&
-            physicalDeltaX != NULL &&
-            physicalDeltaY != NULL &&
-            physicalUnitsXDirection->type() == Json::intValue &&
-            physicalUnitsYDirection->type() == Json::intValue &&
-            physicalUnitsXDirection->asInt() == 0x0003 &&  // Centimeters
-            physicalUnitsYDirection->asInt() == 0x0003 &&  // Centimeters
-            physicalDeltaX->isNumeric() &&
-            physicalDeltaY->isNumeric())
-        {
-          // Scene coordinates are expressed in millimeters => multiplication by 10
-          SetPixelSpacing(10.0 * physicalDeltaX->asDouble(),
-                          10.0 * physicalDeltaY->asDouble());
-        }
-      }
-    }
+    DicomWebDataset dataset(dicomweb);
+    InjectSequenceTags(dataset);
   }
 
 
--- a/OrthancStone/Sources/Toolbox/DicomInstanceParameters.h	Tue Oct 22 15:41:38 2024 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomInstanceParameters.h	Tue Oct 22 21:52:34 2024 +0200
@@ -77,18 +77,12 @@
     std::unique_ptr<Orthanc::DicomMap>               tags_;
     std::unique_ptr<Orthanc::DicomImageInformation>  imageInformation_;  // Lazy evaluation
 
+    void InjectSequenceTags(const IDicomDataset& dataset);
+
   public:
-    explicit DicomInstanceParameters(const DicomInstanceParameters& other) :
-      data_(other.data_),
-      tags_(other.tags_->Clone())
-    {
-    }
+    explicit DicomInstanceParameters(const DicomInstanceParameters& other);
 
-    explicit DicomInstanceParameters(const Orthanc::DicomMap& dicom) :
-      data_(dicom),
-      tags_(dicom.Clone())
-    {
-    }
+    explicit DicomInstanceParameters(const Orthanc::DicomMap& dicom);
 
     DicomInstanceParameters* Clone() const
     {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/OrthancDatasets/DicomWebDataset.cpp	Tue Oct 22 21:52:34 2024 +0200
@@ -0,0 +1,231 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DicomWebDataset.h"
+
+#include <OrthancException.h>
+
+#include <boost/lexical_cast.hpp>
+
+
+static const char* const VALUE = "Value";
+static const char* const VR = "vr";
+static const char* const SQ = "SQ";
+static const char* const ALPHABETIC = "Alphabetic";
+
+
+namespace OrthancStone
+{
+  static const Json::Value* GetValue(std::string& vr,
+                                     const Json::Value& node,
+                                     const Orthanc::DicomTag& tag)
+  {
+    char id[16];
+    sprintf(id, "%04X%04X", tag.GetGroup(), tag.GetElement());
+
+    if (node.type() != Json::objectValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    if (!node.isMember(id))
+    {
+      return NULL;
+    }
+
+    if (node[id].type() == Json::objectValue &&
+        node[id].isMember(VALUE) &&
+        node[id].isMember(VR) &&
+        node[id][VR].type() == Json::stringValue)
+    {
+      vr = node[id][VR].asString();
+      return &node[id][VALUE];
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  static const Json::Value* GetSequenceArray(const Json::Value& node,
+                                             const Orthanc::DicomTag& tag)
+  {
+    std::string vr;
+    const Json::Value* value = GetValue(vr, node, tag);
+
+    if (value != NULL &&
+        vr == SQ &&
+        value->type() == Json::arrayValue)
+    {
+      return value;
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  const Json::Value* DicomWebDataset::LookupValue(std::string& vr,
+                                                  const Orthanc::DicomPath& path) const
+  {
+    const Json::Value* current = &dicomweb_;
+
+    for (size_t i = 0; i < path.GetPrefixLength(); i++)
+    {
+      if (path.IsPrefixUniversal(i))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
+      Json::ArrayIndex index = path.GetPrefixIndex(i);
+
+      const Json::Value* next = GetSequenceArray(*current, path.GetPrefixTag(i));
+      if (next != NULL &&
+          index < next->size())
+      {
+        current = &((*next) [index]);
+      }
+      else
+      {
+        return NULL;
+      }
+    }
+
+    return GetValue(vr, *current, path.GetFinalTag());
+  }
+
+
+  DicomWebDataset::DicomWebDataset(const Json::Value& dicomweb) :
+    dicomweb_(dicomweb)
+  {
+    if (dicomweb.type() != Json::objectValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  bool DicomWebDataset::GetStringValue(std::string& result,
+                                       const Orthanc::DicomPath& path) const
+  {
+    std::string vr;
+    const Json::Value* value = LookupValue(vr, path);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else if (value->type() == Json::arrayValue &&
+             value->size() == 1u &&
+             (*value) [0].type() == Json::stringValue && (
+               // This is the list of all the string value representations:
+               // https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
+               vr == "AE" ||
+               vr == "AS" ||
+               vr == "CS" ||
+               vr == "DA" ||
+               vr == "DS" ||
+               vr == "DT" ||
+               vr == "IS" ||
+               vr == "LO" ||
+               vr == "LT" ||
+               vr == "SH" ||
+               vr == "ST" ||
+               vr == "TM" ||
+               vr == "UC" ||
+               vr == "UI" ||
+               vr == "UR" ||
+               vr == "UT"))
+    {
+      result = (*value) [0].asString();
+      return true;
+    }
+    else if (value->type() == Json::arrayValue &&
+             value->size() == 1u &&
+             vr == "PN" &&
+             (*value) [0].type() == Json::objectValue &&
+             (*value) [0].isMember(ALPHABETIC) &&
+             (*value) [0][ALPHABETIC].type() == Json::stringValue)
+    {
+      result = (*value) [0][ALPHABETIC].asString();
+      return true;
+    }
+    else if (value->type() == Json::arrayValue &&
+             value->size() == 1u &&
+             (vr == "FD" || vr == "FL") &&
+             (*value) [0].isDouble())
+    {
+      result = boost::lexical_cast<std::string>((*value) [0].asDouble());
+      return true;
+    }
+    else if (value->type() == Json::arrayValue &&
+             value->size() == 1u &&
+             (vr == "UL" ||
+              vr == "US") &&
+             (*value) [0].isUInt64())
+    {
+      result = boost::lexical_cast<std::string>((*value) [0].asUInt64());
+      return true;
+    }
+    else if (value->type() == Json::arrayValue &&
+             value->size() == 1u &&
+             (vr == "SL" ||
+              vr == "SS") &&
+             (*value) [0].isInt64())
+    {
+      result = boost::lexical_cast<std::string>((*value) [0].asInt64());
+      return true;
+    }
+    else if (value->type() == Json::arrayValue &&
+             vr == "SQ")
+    {
+      return false;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+  }
+
+
+  bool DicomWebDataset::GetSequenceSize(size_t& size,
+                                        const Orthanc::DicomPath& path) const
+  {
+    std::string vr;
+    const Json::Value* value = LookupValue(vr, path);
+
+    if (value != NULL &&
+        vr == SQ &&
+        value->type() == Json::arrayValue)
+    {
+      size = value->size();
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/OrthancDatasets/DicomWebDataset.h	Tue Oct 22 21:52:34 2024 +0200
@@ -0,0 +1,49 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IDicomDataset.h"
+
+#include <json/value.h>
+
+
+namespace OrthancStone
+{
+  class DicomWebDataset : public IDicomDataset
+  {
+  private:
+    Json::Value  dicomweb_;
+
+    const Json::Value* LookupValue(std::string& vr,
+                                   const Orthanc::DicomPath& path) const;
+
+  public:
+    DicomWebDataset(const Json::Value& dicomweb);
+
+    virtual bool GetStringValue(std::string& result,
+                                const Orthanc::DicomPath& path) const ORTHANC_OVERRIDE;
+    virtual bool GetSequenceSize(size_t& size,
+                                 const Orthanc::DicomPath& path) const ORTHANC_OVERRIDE;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/OrthancDatasets/OrthancNativeDataset.cpp	Tue Oct 22 21:52:34 2024 +0200
@@ -0,0 +1,213 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrthancNativeDataset.h"
+
+#include <OrthancException.h>
+
+
+static const char* const NAME = "Name";
+static const char* const TYPE = "Type";
+static const char* const VALUE = "Value";
+
+
+namespace OrthancStone
+{
+  const Json::Value* OrthancNativeDataset::LookupValue(std::string& type,
+                                                       const Orthanc::DicomPath& path) const
+  {
+    if (path.IsPrefixUniversal(0))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    const Orthanc::DicomValue* rootSequence = dicom_->TestAndGetValue(path.GetPrefixTag(0));
+    if (rootSequence == NULL ||
+        !rootSequence->IsSequence())
+    {
+      return NULL;
+    }
+
+    Json::ArrayIndex index = path.GetPrefixIndex(0);
+
+    if (rootSequence->GetSequenceContent().type() != Json::arrayValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    if (index >= rootSequence->GetSequenceContent().size())
+    {
+      return NULL;
+    }
+
+    const Json::Value* current = &(rootSequence->GetSequenceContent() [index]);
+
+    for (size_t i = 1; i < path.GetPrefixLength(); i++)
+    {
+      if (path.IsPrefixUniversal(i))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
+      index = path.GetPrefixIndex(i);
+      std::string tag = path.GetPrefixTag(i).Format();
+
+      if (current->type() != Json::objectValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+
+      if (!current->isMember(tag))
+      {
+        return NULL;
+      }
+
+      if ((*current) [tag].type() != Json::objectValue ||
+          !(*current) [tag].isMember(NAME) ||
+          !(*current) [tag].isMember(TYPE) ||
+          !(*current) [tag].isMember(VALUE) ||
+          (*current) [tag][NAME].type() != Json::stringValue ||
+          (*current) [tag][TYPE].type() != Json::stringValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+
+      if ((*current) [tag][TYPE].asString() != "Sequence" ||
+          (*current) [tag][VALUE].type() != Json::arrayValue ||
+          index >= (*current) [tag][VALUE].size())
+      {
+        return NULL;
+      }
+
+      current = &(*current) [tag][VALUE][index];
+    }
+
+    std::string tag = path.GetFinalTag().Format();
+
+    if (current->type() != Json::objectValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    if (!current->isMember(tag))
+    {
+      return NULL;
+    }
+
+    if ((*current) [tag].type() != Json::objectValue ||
+        !(*current) [tag].isMember(NAME) ||
+        !(*current) [tag].isMember(TYPE) ||
+        !(*current) [tag].isMember(VALUE) ||
+        (*current) [tag][NAME].type() != Json::stringValue ||
+        (*current) [tag][TYPE].type() != Json::stringValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    else
+    {
+      type = (*current) [tag][TYPE].asString();
+      return &((*current) [tag][VALUE]);
+    }
+  }
+
+
+  OrthancNativeDataset::OrthancNativeDataset(const Json::Value& dicom) :
+    dicom_(new Orthanc::DicomMap)
+  {
+    dicom_->FromDicomAsJson(dicom, false, true /* parse sequences */);
+  }
+
+
+  bool OrthancNativeDataset::GetStringValue(std::string& result,
+                                            const Orthanc::DicomPath& path) const
+  {
+    if (path.GetPrefixLength() == 0)
+    {
+      return dicom_->LookupStringValue(result, path.GetFinalTag(), false);
+    }
+    else
+    {
+      std::string type;
+      const Json::Value* value = LookupValue(type, path);
+
+      if (value == NULL)
+      {
+        return false;
+      }
+      else if (type == "String" &&
+               value->type() == Json::stringValue)
+      {
+        result = value->asString();
+        return true;
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+    }
+  }
+
+
+  bool OrthancNativeDataset::GetSequenceSize(size_t& size,
+                                             const Orthanc::DicomPath& path) const
+  {
+    if (path.GetPrefixLength() == 0)
+    {
+      const Orthanc::DicomValue* value = dicom_->TestAndGetValue(path.GetFinalTag());
+      if (value == NULL ||
+          !value->IsSequence())
+      {
+        return false;
+      }
+      else if (value->GetSequenceContent().type() != Json::arrayValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+      else
+      {
+        size = value->GetSequenceContent().size();
+        return true;
+      }
+    }
+    else
+    {
+      std::string type;
+      const Json::Value* value = LookupValue(type, path);
+
+      if (value == NULL ||
+          type != "Sequence")
+      {
+        return false;
+      }
+      else if (value->type() != Json::arrayValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+      else
+      {
+        size = value->size();
+        return true;
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/OrthancDatasets/OrthancNativeDataset.h	Tue Oct 22 21:52:34 2024 +0200
@@ -0,0 +1,55 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IDicomDataset.h"
+
+#include <DicomFormat/DicomMap.h>
+
+
+namespace OrthancStone
+{
+  class OrthancNativeDataset : public IDicomDataset
+  {
+  private:
+    std::unique_ptr<Orthanc::DicomMap>  dicom_;
+
+    const Json::Value* LookupValue(std::string& type,
+                                   const Orthanc::DicomPath& path) const;
+
+  public:
+    OrthancNativeDataset(const Orthanc::DicomMap& dicom) :
+      dicom_(dicom.Clone())
+    {
+    }
+
+    OrthancNativeDataset(const Json::Value& dicom);
+
+    virtual bool GetStringValue(std::string& result,
+                                const Orthanc::DicomPath& path) const ORTHANC_OVERRIDE;
+
+    virtual bool GetSequenceSize(size_t& size,
+                                 const Orthanc::DicomPath& path) const ORTHANC_OVERRIDE;
+  };
+}