changeset 6625:7f44bfb58e7c

improved support for OF and OD value representations
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 17 Mar 2026 16:07:57 +0100
parents bc0fce665a1c
children 71400f1c6143
files NEWS OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp
diffstat 2 files changed, 118 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Mar 17 08:54:41 2026 +0100
+++ b/NEWS	Tue Mar 17 16:07:57 2026 +0100
@@ -6,7 +6,6 @@
 
 * New experimental configuration "PatientLevelEnabled" (TODO: work in progree)
 
-
 REST API
 --------
 
@@ -23,6 +22,9 @@
 * New option "Utf8" available in the "{...}/archive" and "/tools/create-archive" routes
   to use UTF-8 filenames in the generated ZIP archives. It defaults to the value
   of the new configuration option "ZipUseUtf8".
+* Support for OF and OD value representations in "/instances/{...}/file" with content type
+  "application/dicom+json" (i.e. DICOMweb) and in "/instances/{...}/tags" (contribution by
+  Yusuf Sayıta, Philips).
 
 Lua
 ---
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Tue Mar 17 08:54:41 2026 +0100
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Tue Mar 17 16:07:57 2026 +0100
@@ -803,7 +803,6 @@
          **/
 
         case EVR_OB:  // other byte
-        case EVR_OF:  // other float
         case EVR_OW:  // other word
         case EVR_UN:  // unknown value representation
         case EVR_ox:  // OB or OW depending on context
@@ -876,6 +875,63 @@
           return ApplyDcmtkToCTypeConverter<DcmtkToFloat64Converter>(element);
         }
 
+        case EVR_OF:  // other float - binary array of 32-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OF stores a binary array of 32-bit IEEE floats. Unlike FL where getVM()
+           * returns the count of values, for OF getVM() returns 1 (the entire binary
+           * blob is considered one "value"). We must use getFloat32Array() to access
+           * the raw float buffer, then build a string of all values.
+           * The resulting string is formatted as in ApplyDcmtkToCTypeConverter().
+           **/
+          DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element);
+          Float32* floatArray = NULL;
+          if (content.getFloat32Array(floatArray).good() && floatArray != NULL)
+          {
+            const unsigned long numFloats = element.getLength() / sizeof(Float32);
+            std::string result;
+            for (unsigned long i = 0; i < numFloats; i++)
+            {
+              if (i > 0)
+              {
+                result += "\\";
+              }
+              result += boost::lexical_cast<std::string>(floatArray[i]);
+            }
+            return new DicomValue(result, false);
+          }
+          return new DicomValue;
+        }
+
+#if DCMTK_VERSION_NUMBER >= 361
+        case EVR_OD:  // other double - binary array of 64-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OD stores a binary array of 64-bit IEEE doubles. Similar to OF,
+           * getVM() returns 1 for OD. We must use getFloat64Array() to access
+           * the raw double buffer.
+           * The resulting string is formatted as in ApplyDcmtkToCTypeConverter().
+           **/
+          DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element);
+          Float64* doubleArray = NULL;
+          if (content.getFloat64Array(doubleArray).good() && doubleArray != NULL)
+          {
+            const unsigned long numDoubles = element.getLength() / sizeof(Float64);
+            std::string result;
+            for (unsigned long i = 0; i < numDoubles; i++)
+            {
+              if (i > 0)
+              {
+                result += "\\";
+              }
+              result += boost::lexical_cast<std::string>(doubleArray[i]);
+            }
+            return new DicomValue(result, false);
+          }
+          return new DicomValue;
+        }
+#endif
+
 
         /**
          * Attribute tag.
@@ -3039,7 +3095,6 @@
         }
 
         case EVR_FL:  // float single-precision
-        case EVR_OF:
         {
           DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element);
 
@@ -3060,9 +3115,6 @@
         }
 
         case EVR_FD:  // float double-precision
-#if DCMTK_VERSION_NUMBER >= 361
-        case EVR_OD:
-#endif
         {
           DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element);
 
@@ -3082,6 +3134,64 @@
           break;
         }
 
+        case EVR_OF:  // other float - binary array of 32-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OF stores a binary array of 32-bit IEEE floats. Unlike FL where getVM()
+           * returns the count of values, for OF getVM() returns 1 (the entire binary
+           * blob is considered one "value"). We must use getFloat32Array() to access
+           * the raw float buffer, then iterate over all values.
+           **/
+          DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element);
+
+          std::vector<double> values;
+
+          Float32* floatArray = NULL;
+          if (content.getFloat32Array(floatArray).good() && floatArray != NULL)
+          {
+            const unsigned long numFloats = static_cast<unsigned long>(element.getLength() / sizeof(Float32));
+            values.reserve(numFloats);
+
+            for (unsigned long i = 0; i < numFloats; i++)
+            {
+              values.push_back(static_cast<double>(floatArray[i]));
+            }
+          }
+
+          action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
+          break;
+        }
+
+#if DCMTK_VERSION_NUMBER >= 361
+        case EVR_OD:  // other double - binary array of 64-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OD stores a binary array of 64-bit IEEE doubles. Unlike FD where getVM()
+           * returns the count of values, for OD getVM() returns 1 (the entire binary
+           * blob is considered one "value"). We must use getFloat64Array() to access
+           * the raw double buffer, then iterate over all values.
+           **/
+          DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element);
+
+          std::vector<double> values;
+
+          Float64* doubleArray = NULL;
+          if (content.getFloat64Array(doubleArray).good() && doubleArray != NULL)
+          {
+            const unsigned long numDoubles = static_cast<unsigned long>(element.getLength() / sizeof(Float64));
+            values.reserve(numDoubles);
+
+            for (unsigned long i = 0; i < numDoubles; i++)
+            {
+              values.push_back(doubleArray[i]);
+            }
+          }
+
+          action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
+          break;
+        }
+#endif
+
 
         /**
          * Attribute tag.