# HG changeset patch # User Sebastien Jodogne # Date 1773760077 -3600 # Node ID 7f44bfb58e7ccfd4aaf33037d766dc03bf631c1d # Parent bc0fce665a1c3b46f590f9d552606615d12a705a improved support for OF and OD value representations diff -r bc0fce665a1c -r 7f44bfb58e7c NEWS --- 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 --- diff -r bc0fce665a1c -r 7f44bfb58e7c OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp --- 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(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(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(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(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(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(element); @@ -3060,9 +3115,6 @@ } case EVR_FD: // float double-precision -#if DCMTK_VERSION_NUMBER >= 361 - case EVR_OD: -#endif { DcmFloatingPointDouble& content = dynamic_cast(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(element); + + std::vector values; + + Float32* floatArray = NULL; + if (content.getFloat32Array(floatArray).good() && floatArray != NULL) + { + const unsigned long numFloats = static_cast(element.getLength() / sizeof(Float32)); + values.reserve(numFloats); + + for (unsigned long i = 0; i < numFloats; i++) + { + values.push_back(static_cast(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(element); + + std::vector values; + + Float64* doubleArray = NULL; + if (content.getFloat64Array(doubleArray).good() && doubleArray != NULL) + { + const unsigned long numDoubles = static_cast(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.