diff OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp @ 4515:8734caa12448

Improved precision of floating-point numbers in DICOM-as-JSON and DICOM summary
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 16 Feb 2021 14:42:04 +0100
parents 5b929e6b3c36
children 671ee7c1fd46
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Tue Feb 16 12:18:41 2021 +0100
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Tue Feb 16 14:42:04 2021 +0100
@@ -191,26 +191,68 @@
     };
 
     
-#define DCMTK_TO_CTYPE_CONVERTER(converter, cType, dcmtkType, getter)   \
+    ORTHANC_FORCE_INLINE
+    static std::string FloatToString(float v)
+    {
+      /**
+       * From "boost::lexical_cast" documentation: "For more involved
+       * conversions, such as where precision or formatting need tighter
+       * control than is offered by the default behavior of
+       * lexical_cast, the conventional stringstream approach is
+       * recommended."
+       * https://www.boost.org/doc/libs/1_65_0/doc/html/boost_lexical_cast.html
+       * http://www.gotw.ca/publications/mill19.htm
+       *
+       * The precision of 17 corresponds to "defaultRealPrecision" in JsonCpp:
+       * https://github.com/open-source-parsers/jsoncpp/blob/master/include/json/value.h
+       **/
+
+      //return boost::lexical_cast<std::string>(v);  // This was used in Orthanc <= 1.9.0
+      
+      std::ostringstream ss;
+      ss << std::setprecision(17) << v;
+      return ss.str();
+    }
+
+
+    ORTHANC_FORCE_INLINE
+    static std::string DoubleToString(double v)
+    {
+      //return boost::lexical_cast<std::string>(v);  // This was used in Orthanc <= 1.9.0
+      
+      std::ostringstream ss;
+      ss << std::setprecision(17) << v;
+      return ss.str();
+    }
+
+    
+#define DCMTK_TO_CTYPE_CONVERTER(converter, cType, dcmtkType, getter, toStringFunction)  \
                                                                         \
     struct converter                                                    \
     {                                                                   \
       typedef cType CType;                                              \
                                                                         \
+      ORTHANC_FORCE_INLINE                                              \
       static bool Apply(CType& result,                                  \
                         DcmElement& element,                            \
                         size_t i)                                       \
       {                                                                 \
         return dynamic_cast<dcmtkType&>(element).getter(result, i).good(); \
       }                                                                 \
+                                                                        \
+      ORTHANC_FORCE_INLINE                                              \
+      static std::string ToString(CType value)                          \
+      {                                                                 \
+        return toStringFunction(value);                                 \
+      }                                                                 \
     };
 
-DCMTK_TO_CTYPE_CONVERTER(DcmtkToSint32Converter, Sint32, DcmSignedLong, getSint32)
-DCMTK_TO_CTYPE_CONVERTER(DcmtkToSint16Converter, Sint16, DcmSignedShort, getSint16)
-DCMTK_TO_CTYPE_CONVERTER(DcmtkToUint32Converter, Uint32, DcmUnsignedLong, getUint32)
-DCMTK_TO_CTYPE_CONVERTER(DcmtkToUint16Converter, Uint16, DcmUnsignedShort, getUint16)
-DCMTK_TO_CTYPE_CONVERTER(DcmtkToFloat32Converter, Float32, DcmFloatingPointSingle, getFloat32)
-DCMTK_TO_CTYPE_CONVERTER(DcmtkToFloat64Converter, Float64, DcmFloatingPointDouble, getFloat64)
+    DCMTK_TO_CTYPE_CONVERTER(DcmtkToSint32Converter, Sint32, DcmSignedLong, getSint32, boost::lexical_cast<std::string>)
+    DCMTK_TO_CTYPE_CONVERTER(DcmtkToSint16Converter, Sint16, DcmSignedShort, getSint16, boost::lexical_cast<std::string>)
+    DCMTK_TO_CTYPE_CONVERTER(DcmtkToUint32Converter, Uint32, DcmUnsignedLong, getUint32, boost::lexical_cast<std::string>)
+    DCMTK_TO_CTYPE_CONVERTER(DcmtkToUint16Converter, Uint16, DcmUnsignedShort, getUint16, boost::lexical_cast<std::string>)
+    DCMTK_TO_CTYPE_CONVERTER(DcmtkToFloat32Converter, Float32, DcmFloatingPointSingle, getFloat32, FloatToString)
+    DCMTK_TO_CTYPE_CONVERTER(DcmtkToFloat64Converter, Float64, DcmFloatingPointDouble, getFloat64, DoubleToString)
 
 
     template <typename F>
@@ -226,19 +268,18 @@
         std::vector<std::string> strings;
         for (size_t i = 0; i < count; i++) {
           if (f.Apply(value, element, i)) {
-            strings.push_back(boost::lexical_cast<std::string>(value));
+            strings.push_back(F::ToString(value));
           }
         }
         return new DicomValue(boost::algorithm::join(strings, "\\"), false);
       }
       else if (f.Apply(value, element, 0)) {
-        return new DicomValue(boost::lexical_cast<std::string>(value), false);
+        return new DicomValue(F::ToString(value), false);
       }
       else {
         return new DicomValue;
       }
     }
-
   }