changeset 3496:109631ed3564

DicomMap::FromDicomWeb()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 21 Aug 2019 16:47:17 +0200
parents cc3e408165eb
children db71bd11affc
files Core/DicomFormat/DicomMap.cpp Core/DicomFormat/DicomMap.h UnitTestsSources/DicomMapTests.cpp UnitTestsSources/FromDcmtkTests.cpp
diffstat 4 files changed, 263 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomMap.cpp	Mon Aug 19 16:02:00 2019 +0200
+++ b/Core/DicomFormat/DicomMap.cpp	Wed Aug 21 16:47:17 2019 +0200
@@ -40,6 +40,7 @@
 #include "../Endianness.h"
 #include "../Logging.h"
 #include "../OrthancException.h"
+#include "../Toolbox.h"
 
 
 namespace Orthanc
@@ -1133,4 +1134,140 @@
       map_[tag] = value.release();
     }
   }
+
+
+  void DicomMap::FromDicomWeb(const Json::Value& source)
+  {
+    static const char* const ALPHABETIC = "Alphabetic";
+    static const char* const IDEOGRAPHIC = "Ideographic";
+    static const char* const INLINE_BINARY = "InlineBinary";
+    static const char* const PHONETIC = "Phonetic";
+    static const char* const VALUE = "Value";
+    static const char* const VR = "vr";
+  
+    Clear();
+
+    if (source.type() != Json::objectValue)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  
+    Json::Value::Members tags = source.getMemberNames();
+
+    for (size_t i = 0; i < tags.size(); i++)
+    {
+      const Json::Value& item = source[tags[i]];
+      DicomTag tag(0, 0);
+
+      if (item.type() != Json::objectValue ||
+          !item.isMember(VR) ||
+          item[VR].type() != Json::stringValue ||
+          !DicomTag::ParseHexadecimal(tag, tags[i].c_str()))
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+
+      ValueRepresentation vr = StringToValueRepresentation(item[VR].asString(), false);
+
+      if (item.isMember(INLINE_BINARY))
+      {
+        const Json::Value& value = item[INLINE_BINARY];
+
+        if (value.type() == Json::stringValue)
+        {
+          std::string decoded;
+          Toolbox::DecodeBase64(decoded, value.asString());
+          SetValue(tag, decoded, true /* binary data */);
+        }
+      }
+      else if (!item.isMember(VALUE))
+      {
+        // Tag is present, but it has a null value
+        SetValue(tag, "", false /* not binary */);
+      }
+      else
+      {
+        const Json::Value& value = item[VALUE];
+
+        if (value.type() == Json::arrayValue)
+        {
+          bool supported = true;
+          
+          std::string s;
+          for (Json::Value::ArrayIndex i = 0; i < value.size() && supported; i++)
+          {
+            if (!s.empty())
+            {
+              s += '\\';
+            }
+
+            switch (value[i].type())
+            {
+              case Json::objectValue:
+                if (vr == ValueRepresentation_PersonName &&
+                    value[i].type() == Json::objectValue)
+                {
+                  if (value[i].isMember(ALPHABETIC) &&
+                      value[i][ALPHABETIC].type() == Json::stringValue)
+                  {
+                    s += value[i][ALPHABETIC].asString();
+                  }
+
+                  bool hasIdeographic = false;
+                  
+                  if (value[i].isMember(IDEOGRAPHIC) &&
+                      value[i][IDEOGRAPHIC].type() == Json::stringValue)
+                  {
+                    s += '=' + value[i][IDEOGRAPHIC].asString();
+                    hasIdeographic = true;
+                  }
+                  
+                  if (value[i].isMember(PHONETIC) &&
+                      value[i][PHONETIC].type() == Json::stringValue)
+                  {
+                    if (!hasIdeographic)
+                    {
+                      s += '=';
+                    }
+                      
+                    s += '=' + value[i][PHONETIC].asString();
+                  }
+                }
+                else
+                {
+                  // This is the case of sequences
+                  supported = false;
+                }
+
+                break;
+            
+              case Json::stringValue:
+                s += value[i].asString();
+                break;
+              
+              case Json::intValue:
+                s += boost::lexical_cast<std::string>(value[i].asInt());
+                break;
+              
+              case Json::uintValue:
+                s += boost::lexical_cast<std::string>(value[i].asUInt());
+                break;
+              
+              case Json::realValue:
+                s += boost::lexical_cast<std::string>(value[i].asDouble());
+                break;
+              
+              default:
+                break;
+            }
+          }
+
+          if (supported)
+          {
+            SetValue(tag, s, false /* not binary */);
+          }
+        }
+      }
+    }
+  }
 }
--- a/Core/DicomFormat/DicomMap.h	Mon Aug 19 16:02:00 2019 +0200
+++ b/Core/DicomFormat/DicomMap.h	Wed Aug 21 16:47:17 2019 +0200
@@ -231,5 +231,7 @@
     void Serialize(Json::Value& target) const;
 
     void Unserialize(const Json::Value& source);
+
+    void FromDicomWeb(const Json::Value& source);
   };
 }
--- a/UnitTestsSources/DicomMapTests.cpp	Mon Aug 19 16:02:00 2019 +0200
+++ b/UnitTestsSources/DicomMapTests.cpp	Wed Aug 21 16:47:17 2019 +0200
@@ -570,7 +570,7 @@
   dicom.Apply(visitor);
 
   {
-    const Json::Value& tag = visitor.GetResult() ["00200037"];
+    const Json::Value& tag = visitor.GetResult() ["00200037"];  // ImageOrientationPatient
     const Json::Value& value = tag["Value"];
   
     ASSERT_EQ(EnumerationToString(ValueRepresentation_DecimalString), tag["vr"].asString());
@@ -583,13 +583,33 @@
   }
 
   {
-    const Json::Value& tag = visitor.GetResult() ["00200032"];
+    const Json::Value& tag = visitor.GetResult() ["00200032"];  // ImagePositionPatient
     ASSERT_EQ(EnumerationToString(ValueRepresentation_DecimalString), tag["vr"].asString());
     ASSERT_EQ(1u, tag.getMemberNames().size());
   }
 
   std::string xml;
   visitor.FormatXml(xml);
+
+  {
+    DicomMap m;
+    m.FromDicomWeb(visitor.GetResult());
+    ASSERT_EQ(3u, m.GetSize());
+
+    std::string s;
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_PATIENT_NAME, false));
+    ASSERT_EQ("SB1^SB2^SB3^SB4^SB5", s);
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_IMAGE_POSITION_PATIENT, false));
+    ASSERT_TRUE(s.empty());
+
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false));
+
+    std::vector<std::string> v;
+    Orthanc::Toolbox::TokenizeString(v, s, '\\');
+    ASSERT_FLOAT_EQ(1.0f, boost::lexical_cast<float>(v[0]));
+    ASSERT_FLOAT_EQ(2.3f, boost::lexical_cast<float>(v[1]));
+    ASSERT_FLOAT_EQ(4.0f, boost::lexical_cast<float>(v[2]));
+  }
 }
 
 
@@ -620,6 +640,22 @@
 
   std::string xml;
   visitor.FormatXml(xml);
+  
+  {
+    DicomMap m;
+    m.FromDicomWeb(visitor.GetResult());
+    ASSERT_EQ(1u, m.GetSize());
+
+    std::string s;
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false));
+
+    std::vector<std::string> v;
+    Orthanc::Toolbox::TokenizeString(v, s, '\\');
+    ASSERT_FLOAT_EQ(1.5f, boost::lexical_cast<float>(v[0]));
+    ASSERT_TRUE(v[1].empty());
+    ASSERT_TRUE(v[2].empty());
+    ASSERT_FLOAT_EQ(2.5f, boost::lexical_cast<float>(v[3]));
+  }
 }
 
 
@@ -679,12 +715,16 @@
   dicom.ReplacePlainString(DicomTag(0x0008, 0x0120), "UR");
   dicom.ReplacePlainString(DicomTag(0x0008, 0x0301), "17");   // US
   dicom.ReplacePlainString(DicomTag(0x0040, 0x0031), "UT");  
-  
+
   Orthanc::DicomWebJsonVisitor visitor;
   dicom.Apply(visitor);
 
   std::string s;
-  
+
+  // The tag (0002,0002) is "Media Storage SOP Class UID" and is
+  // automatically copied by DCMTK from tag (0008,0016)
+  ASSERT_EQ("UI", visitor.GetResult() ["00020002"]["vr"].asString());
+  ASSERT_EQ("UI", visitor.GetResult() ["00020002"]["Value"][0].asString());
   ASSERT_EQ("AE", visitor.GetResult() ["00400241"]["vr"].asString());
   ASSERT_EQ("AE", visitor.GetResult() ["00400241"]["Value"][0].asString());
   ASSERT_EQ("AS", visitor.GetResult() ["00101010"]["vr"].asString());
@@ -804,6 +844,45 @@
 
   std::string xml;
   visitor.FormatXml(xml);
+  
+  {
+    DicomMap m;
+    m.FromDicomWeb(visitor.GetResult());
+    ASSERT_EQ(31u, m.GetSize());
+
+    std::string s;
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0002, 0x0002), false));  ASSERT_EQ("UI", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0040, 0x0241), false));  ASSERT_EQ("AE", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0010, 0x1010), false));  ASSERT_EQ("AS", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0020, 0x9165), false));  ASSERT_EQ("00100020", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0052), false));  ASSERT_EQ("CS", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0012), false));  ASSERT_EQ("DA", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0010, 0x1020), false));  ASSERT_EQ("42", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x002a), false));  ASSERT_EQ("DT", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0010, 0x9431), false));  ASSERT_EQ("43", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x1163), false));  ASSERT_EQ("44", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x1160), false));  ASSERT_EQ("45", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0070), false));  ASSERT_EQ("LO", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0010, 0x4000), false));  ASSERT_EQ("LT", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0028, 0x2000), true));   ASSERT_EQ("OB", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x7fe0, 0x0009), true));   ASSERT_EQ("OD", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0064, 0x0009), true));   ASSERT_EQ("OF", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0066, 0x0040), false));  ASSERT_EQ("46", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0028, 0x1201), true));   ASSERT_EQ("OWOW", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0010, 0x0010), false));  ASSERT_EQ("PN", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0050), false));  ASSERT_EQ("SH", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0018, 0x6020), false));  ASSERT_EQ("-15", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0018, 0x9219), false));  ASSERT_EQ("-16", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0081), false));  ASSERT_EQ("ST", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0013), false));  ASSERT_EQ("TM", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0119), false));  ASSERT_EQ("UC", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0016), false));  ASSERT_EQ("UI", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x1161), false));  ASSERT_EQ("128", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x4342, 0x1234), true));   ASSERT_EQ("UN", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0120), false));  ASSERT_EQ("UR", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0008, 0x0301), false));  ASSERT_EQ("17", s);
+    ASSERT_TRUE(m.CopyToString(s, DicomTag(0x0040, 0x0031), false));  ASSERT_EQ("UT", s);
+  }
 }
 
 
@@ -848,4 +927,10 @@
 
   std::string xml;
   visitor.FormatXml(xml);
+
+  {
+    DicomMap m;
+    m.FromDicomWeb(visitor.GetResult());
+    ASSERT_EQ(0, m.GetSize());  // Sequences are not handled by Orthanc::DicomMap
+  }
 }
--- a/UnitTestsSources/FromDcmtkTests.cpp	Mon Aug 19 16:02:00 2019 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Wed Aug 21 16:47:17 2019 +0200
@@ -1607,7 +1607,24 @@
 
   node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Phonetic/GivenName");
   ASSERT_EQ(utf8.substr(28), node.node().text().as_string());
-#endif  
+#endif
+  
+  {
+    DicomMap m;
+    m.FromDicomWeb(visitor.GetResult());
+    ASSERT_EQ(2u, m.GetSize());
+
+    std::string s;
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_SPECIFIC_CHARACTER_SET, false));
+    ASSERT_EQ("ISO 2022 IR 149", s);
+
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_PATIENT_NAME, false));
+    std::vector<std::string> v;
+    Toolbox::TokenizeString(v, s, '=');
+    ASSERT_EQ(3u, v.size());
+    ASSERT_EQ("Hong^Gildong", v[0]);
+    ASSERT_EQ(utf8, s);
+  }
 }
 
 
@@ -1688,6 +1705,23 @@
   node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Phonetic/GivenName");
   ASSERT_EQ(utf8.substr(37), node.node().text().as_string());
 #endif  
+  
+  {
+    DicomMap m;
+    m.FromDicomWeb(visitor.GetResult());
+    ASSERT_EQ(2u, m.GetSize());
+
+    std::string s;
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_SPECIFIC_CHARACTER_SET, false));
+    ASSERT_EQ("ISO 2022 IR 87", s);
+
+    ASSERT_TRUE(m.CopyToString(s, DICOM_TAG_PATIENT_NAME, false));
+    std::vector<std::string> v;
+    Toolbox::TokenizeString(v, s, '=');
+    ASSERT_EQ(3u, v.size());
+    ASSERT_EQ("Yamada^Tarou", v[0]);
+    ASSERT_EQ(utf8, s);
+  }
 }