changeset 3209:f6374c36a671

documentation of DICOMweb in SDK, compatibility with DCMTK 3.6.0
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 08 Feb 2019 12:35:35 +0100
parents c4e1977e5ed7
children 4a1c47e20685
files Core/DicomParsing/FromDcmtkBridge.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Include/orthanc/OrthancCPlugin.h UnitTestsSources/DicomMapTests.cpp
diffstat 4 files changed, 176 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomParsing/FromDcmtkBridge.cpp	Fri Feb 08 08:38:45 2019 +0100
+++ b/Core/DicomParsing/FromDcmtkBridge.cpp	Fri Feb 08 12:35:35 2019 +0100
@@ -90,13 +90,16 @@
 #include <dcmtk/dcmdata/dcvrss.h>
 #include <dcmtk/dcmdata/dcvrst.h>
 #include <dcmtk/dcmdata/dcvrtm.h>
-#include <dcmtk/dcmdata/dcvruc.h>
 #include <dcmtk/dcmdata/dcvrui.h>
 #include <dcmtk/dcmdata/dcvrul.h>
-#include <dcmtk/dcmdata/dcvrur.h>
 #include <dcmtk/dcmdata/dcvrus.h>
 #include <dcmtk/dcmdata/dcvrut.h>
 
+#if DCMTK_VERSION_NUMBER >= 361
+#  include <dcmtk/dcmdata/dcvruc.h>
+#  include <dcmtk/dcmdata/dcvrur.h>
+#endif
+
 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
 #  include <EmbeddedResources.h>
 #endif
@@ -1461,11 +1464,15 @@
       case EVR_PN:  // person name
         return new DcmPersonName(key);
 
+#if DCMTK_VERSION_NUMBER >= 361
       case EVR_UC:  // unlimited characters
         return new DcmUnlimitedCharacters(key);
-
+#endif
+
+#if DCMTK_VERSION_NUMBER >= 361
       case EVR_UR:  // URI/URL
         return new DcmUniversalResourceIdentifierOrLocator(key);
+#endif
           
         
       /**
@@ -1640,8 +1647,10 @@
         case EVR_UT:  // unlimited text
         case EVR_PN:  // person name
         case EVR_UI:  // unique identifier
+#if DCMTK_VERSION_NUMBER >= 361
         case EVR_UC:  // unlimited characters
         case EVR_UR:  // URI/URL
+#endif
         {
           ok = element.putString(decoded->c_str()).good();
           break;
--- a/Plugins/Engine/OrthancPlugins.cpp	Fri Feb 08 08:38:45 2019 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Fri Feb 08 12:35:35 2019 +0100
@@ -997,6 +997,9 @@
         sizeof(int32_t) != sizeof(OrthancPluginIdentifierConstraint) ||
         sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) ||
         sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) ||
+        sizeof(int32_t) != sizeof(OrthancPluginConstraintType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginMetricsType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode) ||
         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) ||
         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) ||
         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) ||
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Feb 08 08:38:45 2019 +0100
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Feb 08 12:35:35 2019 +0100
@@ -897,7 +897,7 @@
 
 
   /**
-   * The available type of metrics.
+   * The available types of metrics.
    **/
   typedef enum
   {
@@ -912,6 +912,18 @@
   } OrthancPluginMetricsType;
   
 
+  /**
+   * The available modes to export a binary DICOM tag into a DICOMweb
+   * JSON or XML document.
+   **/
+  typedef enum
+  {
+    OrthancPluginDicomWebBinaryMode_Ignore,        /*!< Don't include binary tags */
+    OrthancPluginDicomWebBinaryMode_InlineBinary,  /*!< Inline encoding using Base64 */
+    OrthancPluginDicomWebBinaryMode_BulkDataUri    /*!< Use a bulk data URI field */
+  } OrthancPluginDicomWebBinaryMode;
+
+  
 
   /**
    * @brief A memory buffer allocated by the core system of Orthanc.
@@ -1021,6 +1033,15 @@
    **/
   typedef struct _OrthancPluginJob_t OrthancPluginJob;  
 
+
+
+  /**
+   * @brief Opaque structure that represents a node in a JSON or XML
+   * document used in DICOMweb.
+   * @ingroup Toolbox
+   **/
+  typedef struct _OrthancPluginDicomWebNode_t OrthancPluginDicomWebNode;
+
   
 
   /**
@@ -1069,12 +1090,26 @@
 
   /**
    * @brief Signature of a function to free dynamic memory.
+   * @ingroup Callbacks
    **/
   typedef void (*OrthancPluginFree) (void* buffer);
 
 
 
   /**
+   * @brief Signature of a function to set the content of a node
+   * encoding a binary DICOM tag, into a JSON or XML document
+   * generated for DICOMweb.
+   * @ingroup Callbacks
+   **/
+  typedef void (*OrthancPluginDicomWebSetBinaryNode) (
+    OrthancPluginDicomWebNode*       node,
+    OrthancPluginDicomWebBinaryMode  mode,
+    const char*                      bulkDataUri);
+    
+
+
+  /**
    * @brief Callback for writing to the storage area.
    *
    * Signature of a callback function that is triggered when Orthanc writes a file to the storage area.
@@ -1472,13 +1507,50 @@
    * acquire.
    * 
    * @see OrthancPluginRegisterRefreshMetrics()
-   * @ingroups Callbacks
+   * @ingroup Callbacks
    **/
   typedef void (*OrthancPluginRefreshMetricsCallback) ();
 
   
 
   /**
+   * @brief Callback executed to encode a binary tag in DICOMweb.
+   * 
+   * Signature of a callback function that is called by Orthanc
+   * whenever a DICOM tag that contains a binary value must be written
+   * to a JSON or XML node, while a DICOMweb document is being
+   * generated. The value representation (VR) of the DICOM tag can be
+   * OB, OD, OF, OL, OW, or UN.
+   * 
+   * @see OrthancPluginEncodeDicomWebJson() and OrthancPluginEncodeDicomWebXml()
+   * @param node The node being generated, as provided by Orthanc.
+   * @param setter The setter to be used to encode the content of the node. If
+   * the setter is not called, the binary tag is not written to the output document.
+   * @param levelDepth The depth of the node in the DICOM hierarchy of sequences.
+   * This parameter gives the number of elements in the "levelTagGroup", 
+   * "levelTagElement", and "levelIndex" arrays.
+   * @param levelTagGroup The group of the parent DICOM tags in the hierarchy.
+   * @param levelTagElement The element of the parent DICOM tags in the hierarchy.
+   * @param levelIndex The index of the node in the parent sequences of the hiearchy.
+   * @param tagGroup The group of the DICOM tag of interest.
+   * @param tagElement The element of the DICOM tag of interest.
+   * @param vr The value representation of the binary DICOM node.
+   * @ingroup Callbacks
+   **/
+  typedef void (*OrthancPluginDicomWebBinaryCallback) (
+    OrthancPluginDicomWebNode*          node,
+    OrthancPluginDicomWebSetBinaryNode  setter,
+    uint32_t                            levelDepth,
+    const uint16_t*                     levelTagGroup,
+    const uint16_t*                     levelTagElement,
+    const uint32_t*                     levelIndex,
+    uint16_t                            tagGroup,
+    uint16_t                            tagElement,
+    OrthancPluginValueRepresentation    vr);
+
+
+
+  /**
    * @brief Data structure that contains information about the Orthanc core.
    **/
   typedef struct _OrthancPluginContext_t
@@ -1571,7 +1643,8 @@
         sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) ||
         sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) ||
         sizeof(int32_t) != sizeof(OrthancPluginConstraintType) ||
-        sizeof(int32_t) != sizeof(OrthancPluginMetricsType))
+        sizeof(int32_t) != sizeof(OrthancPluginMetricsType) ||
+        sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode))
     {
       /* Mismatch in the size of the enumerations */
       return 0;
@@ -6630,32 +6703,6 @@
 
 
 
-  typedef enum
-  {
-    OrthancPluginDicomWebBinaryMode_Ignore,
-    OrthancPluginDicomWebBinaryMode_InlineBinary,
-    OrthancPluginDicomWebBinaryMode_BulkDataUri
-  } OrthancPluginDicomWebBinaryMode;
-
-  
-  typedef struct _OrthancPluginDicomWebNode_t OrthancPluginDicomWebNode;
-
-  typedef void (*OrthancPluginDicomWebSetBinaryNode) (
-    OrthancPluginDicomWebNode*       node,
-    OrthancPluginDicomWebBinaryMode  mode,
-    const char*                      bulkDataUri);
-    
-  typedef void (*OrthancPluginDicomWebBinaryCallback) (
-    OrthancPluginDicomWebNode*          node,
-    OrthancPluginDicomWebSetBinaryNode  setter,
-    uint32_t                            levelDepth,
-    const uint16_t*                     levelTagGroup,
-    const uint16_t*                     levelTagElement,
-    const uint32_t*                     levelIndex,
-    uint16_t                            tagGroup,
-    uint16_t                            tagElement,
-    OrthancPluginValueRepresentation    vr);
-
   typedef struct
   {
     char**                               target;
@@ -6664,6 +6711,21 @@
     OrthancPluginDicomWebBinaryCallback  callback;
   } _OrthancPluginEncodeDicomWeb;
 
+  /**
+   * @brief Convert a DICOM instance to DICOMweb JSON.
+   *
+   * This function converts a memory buffer containing a DICOM instance,
+   * into its DICOMweb JSON representation.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param dicom Pointer to the DICOM instance.
+   * @param dicomSize Size of the DICOM instance.
+   * @param callback Callback to set the value of the binary tags.
+   * @see OrthancPluginCreateDicom()
+   * @return The NULL value in case of error, or the JSON document. This string must
+   * be freed by OrthancPluginFreeString().
+   * @ingroup Toolbox
+   **/
   ORTHANC_PLUGIN_INLINE char* OrthancPluginEncodeDicomWebJson(
     OrthancPluginContext*                context,
     const void*                          dicom,
@@ -6689,6 +6751,22 @@
     }
   }
 
+
+  /**
+   * @brief Convert a DICOM instance to DICOMweb XML.
+   *
+   * This function converts a memory buffer containing a DICOM instance,
+   * into its DICOMweb XML representation.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param dicom Pointer to the DICOM instance.
+   * @param dicomSize Size of the DICOM instance.
+   * @param callback Callback to set the value of the binary tags.
+   * @return The NULL value in case of error, or the JSON document. This string must
+   * be freed by OrthancPluginFreeString().
+   * @see OrthancPluginCreateDicom()
+   * @ingroup Toolbox
+   **/
   ORTHANC_PLUGIN_INLINE char* OrthancPluginEncodeDicomWebXml(
     OrthancPluginContext*                context,
     const void*                          dicom,
--- a/UnitTestsSources/DicomMapTests.cpp	Fri Feb 08 08:38:45 2019 +0100
+++ b/UnitTestsSources/DicomMapTests.cpp	Fri Feb 08 12:35:35 2019 +0100
@@ -37,6 +37,7 @@
 #include "../Core/OrthancException.h"
 #include "../Core/DicomFormat/DicomMap.h"
 #include "../Core/DicomParsing/FromDcmtkBridge.h"
+#include "../Core/DicomParsing/ToDcmtkBridge.h"
 #include "../Core/DicomParsing/ParsedDicomFile.h"
 #include "../Core/DicomParsing/DicomWebJsonVisitor.h"
 
@@ -44,6 +45,7 @@
 
 #include <memory>
 #include <dcmtk/dcmdata/dcdeftag.h>
+#include <dcmtk/dcmdata/dcvrat.h>
 
 using namespace Orthanc;
 
@@ -621,6 +623,26 @@
 }
 
 
+static void SetTagKey(ParsedDicomFile& dicom,
+                      const DicomTag& tag,
+                      const DicomTag& value)
+{
+  // This function emulates a call to function
+  // "dicom.GetDcmtkObject().getDataset()->putAndInsertTagKey(tag,
+  // value)" that was not available in DCMTK 3.6.0
+
+  std::auto_ptr<DcmAttributeTag> element(new DcmAttributeTag(ToDcmtkBridge::Convert(tag)));
+
+  DcmTagKey v = ToDcmtkBridge::Convert(value);
+  if (!element->putTagVal(v).good())
+  {
+    throw OrthancException(ErrorCode_InternalError);
+  }
+
+  dicom.GetDcmtkObject().getDataset()->insert(element.release());
+}
+
+
 TEST(DicomWebJson, ValueRepresentation)
 {
   // http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_F.2.3.html
@@ -628,9 +650,7 @@
   ParsedDicomFile dicom(false);
   dicom.ReplacePlainString(DicomTag(0x0040, 0x0241), "AE");
   dicom.ReplacePlainString(DicomTag(0x0010, 0x1010), "AS");
-  ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->
-              putAndInsertTagKey(DcmTag(0x0020, 0x9165),
-                                 DcmTagKey(0x0010, 0x0020)).good());
+  SetTagKey(dicom, DicomTag(0x0020, 0x9165), DicomTag(0x0010, 0x0020));
   dicom.ReplacePlainString(DicomTag(0x0008, 0x0052), "CS");
   dicom.ReplacePlainString(DicomTag(0x0008, 0x0012), "DA");
   dicom.ReplacePlainString(DicomTag(0x0010, 0x1020), "42");  // DS
@@ -639,15 +659,11 @@
   dicom.ReplacePlainString(DicomTag(0x0008, 0x1163), "44");  // FD
   dicom.ReplacePlainString(DicomTag(0x0008, 0x1160), "45");  // IS
   dicom.ReplacePlainString(DicomTag(0x0008, 0x0070), "LO");
-  dicom.ReplacePlainString(DicomTag(0x0008, 0x0108), "LT");
+  dicom.ReplacePlainString(DicomTag(0x0010, 0x4000), "LT");
   dicom.ReplacePlainString(DicomTag(0x0028, 0x2000), "OB");
   dicom.ReplacePlainString(DicomTag(0x7fe0, 0x0009), "OD");
   dicom.ReplacePlainString(DicomTag(0x0064, 0x0009), "OF");
-
-#if DCMTK_VERSION_NUMBER >= 362 
   dicom.ReplacePlainString(DicomTag(0x0066, 0x0040), "OLOL");
-#endif
-
   ASSERT_THROW(dicom.ReplacePlainString(DicomTag(0x0028, 0x1201), "O"), OrthancException);
   dicom.ReplacePlainString(DicomTag(0x0028, 0x1201), "OWOW");
   dicom.ReplacePlainString(DicomTag(0x0010, 0x0010), "PN");
@@ -662,8 +678,7 @@
   dicom.ReplacePlainString(DicomTag(0x4342, 0x1234), "UN");   // Inexistent tag
   dicom.ReplacePlainString(DicomTag(0x0008, 0x0120), "UR");
   dicom.ReplacePlainString(DicomTag(0x0008, 0x0301), "17");   // US
-  dicom.ReplacePlainString(DicomTag(0x0040, 0x0031), "UT");
-  
+  dicom.ReplacePlainString(DicomTag(0x0040, 0x0031), "UT");  
   
   Orthanc::DicomWebJsonVisitor visitor;
   dicom.Apply(visitor);
@@ -692,14 +707,19 @@
   ASSERT_FLOAT_EQ(45.0f, visitor.GetResult() ["00081160"]["Value"][0].asFloat());
   ASSERT_EQ("LO", visitor.GetResult() ["00080070"]["vr"].asString());
   ASSERT_EQ("LO", visitor.GetResult() ["00080070"]["Value"][0].asString());
-  ASSERT_EQ("LT", visitor.GetResult() ["00080108"]["vr"].asString());
-  ASSERT_EQ("LT", visitor.GetResult() ["00080108"]["Value"][0].asString());
+  ASSERT_EQ("LT", visitor.GetResult() ["00104000"]["vr"].asString());
+  ASSERT_EQ("LT", visitor.GetResult() ["00104000"]["Value"][0].asString());
 
   ASSERT_EQ("OB", visitor.GetResult() ["00282000"]["vr"].asString());
   Toolbox::DecodeBase64(s, visitor.GetResult() ["00282000"]["InlineBinary"].asString());
   ASSERT_EQ("OB", s);
 
+#if DCMTK_VERSION_NUMBER >= 362
   ASSERT_EQ("OD", visitor.GetResult() ["7FE00009"]["vr"].asString());
+#else
+  ASSERT_EQ("UN", visitor.GetResult() ["7FE00009"]["vr"].asString());
+#endif
+
   Toolbox::DecodeBase64(s, visitor.GetResult() ["7FE00009"]["InlineBinary"].asString());
   ASSERT_EQ("OD", s);
 
@@ -709,9 +729,12 @@
 
 #if DCMTK_VERSION_NUMBER >= 362
   ASSERT_EQ("OL", visitor.GetResult() ["00660040"]["vr"].asString());
+#else
+  ASSERT_EQ("UN", visitor.GetResult() ["00660040"]["vr"].asString());
+#endif
+
   Toolbox::DecodeBase64(s, visitor.GetResult() ["00660040"]["InlineBinary"].asString());
   ASSERT_EQ("OLOL", s);
-#endif
 
   ASSERT_EQ("OW", visitor.GetResult() ["00281201"]["vr"].asString());
   Toolbox::DecodeBase64(s, visitor.GetResult() ["00281201"]["InlineBinary"].asString());
@@ -735,8 +758,14 @@
   ASSERT_EQ("TM", visitor.GetResult() ["00080013"]["vr"].asString());
   ASSERT_EQ("TM", visitor.GetResult() ["00080013"]["Value"][0].asString());
 
+#if DCMTK_VERSION_NUMBER >= 361
   ASSERT_EQ("UC", visitor.GetResult() ["00080119"]["vr"].asString());
   ASSERT_EQ("UC", visitor.GetResult() ["00080119"]["Value"][0].asString());
+#else
+  ASSERT_EQ("UN", visitor.GetResult() ["00080119"]["vr"].asString());
+  Toolbox::DecodeBase64(s, visitor.GetResult() ["00080119"]["InlineBinary"].asString());
+  ASSERT_EQ("UC", s);
+#endif
 
   ASSERT_EQ("UI", visitor.GetResult() ["00080016"]["vr"].asString());
   ASSERT_EQ("UI", visitor.GetResult() ["00080016"]["Value"][0].asString());
@@ -748,16 +777,27 @@
   Toolbox::DecodeBase64(s, visitor.GetResult() ["43421234"]["InlineBinary"].asString());
   ASSERT_EQ("UN", s);
 
+#if DCMTK_VERSION_NUMBER >= 361
   ASSERT_EQ("UR", visitor.GetResult() ["00080120"]["vr"].asString());
   ASSERT_EQ("UR", visitor.GetResult() ["00080120"]["Value"][0].asString());
+#else
+  ASSERT_EQ("UN", visitor.GetResult() ["00080120"]["vr"].asString());
+  Toolbox::DecodeBase64(s, visitor.GetResult() ["00080120"]["InlineBinary"].asString());
+  ASSERT_EQ("UR", s);
+#endif
 
+#if DCMTK_VERSION_NUMBER >= 361
   ASSERT_EQ("US", visitor.GetResult() ["00080301"]["vr"].asString());
   ASSERT_EQ(17, visitor.GetResult() ["00080301"]["Value"][0].asUInt());
+#else
+  ASSERT_EQ("UN", visitor.GetResult() ["00080301"]["vr"].asString());
+  Toolbox::DecodeBase64(s, visitor.GetResult() ["00080301"]["InlineBinary"].asString());
+  ASSERT_EQ("17", s);
+#endif
 
   ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["vr"].asString());
   ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["Value"][0].asString());
 
-
   std::string xml;
   visitor.FormatXml(xml);
 }