# HG changeset patch # User Sebastien Jodogne # Date 1566398837 -7200 # Node ID 109631ed35643fdf24c4d3dfa8be584035703f1c # Parent cc3e408165eb4501691d4aa45ae35a925d91ecde DicomMap::FromDicomWeb() diff -r cc3e408165eb -r 109631ed3564 Core/DicomFormat/DicomMap.cpp --- 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(value[i].asInt()); + break; + + case Json::uintValue: + s += boost::lexical_cast(value[i].asUInt()); + break; + + case Json::realValue: + s += boost::lexical_cast(value[i].asDouble()); + break; + + default: + break; + } + } + + if (supported) + { + SetValue(tag, s, false /* not binary */); + } + } + } + } + } } diff -r cc3e408165eb -r 109631ed3564 Core/DicomFormat/DicomMap.h --- 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); }; } diff -r cc3e408165eb -r 109631ed3564 UnitTestsSources/DicomMapTests.cpp --- 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 v; + Orthanc::Toolbox::TokenizeString(v, s, '\\'); + ASSERT_FLOAT_EQ(1.0f, boost::lexical_cast(v[0])); + ASSERT_FLOAT_EQ(2.3f, boost::lexical_cast(v[1])); + ASSERT_FLOAT_EQ(4.0f, boost::lexical_cast(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 v; + Orthanc::Toolbox::TokenizeString(v, s, '\\'); + ASSERT_FLOAT_EQ(1.5f, boost::lexical_cast(v[0])); + ASSERT_TRUE(v[1].empty()); + ASSERT_TRUE(v[2].empty()); + ASSERT_FLOAT_EQ(2.5f, boost::lexical_cast(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 + } } diff -r cc3e408165eb -r 109631ed3564 UnitTestsSources/FromDcmtkTests.cpp --- 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 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 v; + Toolbox::TokenizeString(v, s, '='); + ASSERT_EQ(3u, v.size()); + ASSERT_EQ("Yamada^Tarou", v[0]); + ASSERT_EQ(utf8, s); + } }