Mercurial > hg > orthanc
comparison UnitTestsSources/DicomMapTests.cpp @ 3201:b69fe409cb4d
dicomweb json to xml
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 06 Feb 2019 15:01:37 +0100 |
parents | 1f4a2c58e7fa |
children | ef4d86d05503 |
comparison
equal
deleted
inserted
replaced
3200:1f4a2c58e7fa | 3201:b69fe409cb4d |
---|---|
555 | 555 |
556 | 556 |
557 | 557 |
558 | 558 |
559 #include <boost/math/special_functions/round.hpp> | 559 #include <boost/math/special_functions/round.hpp> |
560 #include <pugixml.hpp> | |
560 | 561 |
561 | 562 |
562 static const char* const KEY_ALPHABETIC = "Alphabetic"; | 563 static const char* const KEY_ALPHABETIC = "Alphabetic"; |
563 static const char* const KEY_BULK_DATA_URI = "BulkDataURI"; | 564 static const char* const KEY_BULK_DATA_URI = "BulkDataURI"; |
564 static const char* const KEY_INLINE_BINARY = "InlineBinary"; | 565 static const char* const KEY_INLINE_BINARY = "InlineBinary"; |
566 static const char* const KEY_VALUE = "Value"; | 567 static const char* const KEY_VALUE = "Value"; |
567 static const char* const KEY_VR = "vr"; | 568 static const char* const KEY_VR = "vr"; |
568 | 569 |
569 namespace Orthanc | 570 namespace Orthanc |
570 { | 571 { |
572 static void ExploreDataset(pugi::xml_node& target, | |
573 const Json::Value& source) | |
574 { | |
575 assert(source.type() == Json::objectValue); | |
576 | |
577 Json::Value::Members members = source.getMemberNames(); | |
578 for (size_t i = 0; i < members.size(); i++) | |
579 { | |
580 const DicomTag tag = FromDcmtkBridge::ParseTag(members[i]); | |
581 const Json::Value& content = source[members[i]]; | |
582 | |
583 assert(content.type() == Json::objectValue && | |
584 content.isMember("vr") && | |
585 content["vr"].type() == Json::stringValue); | |
586 const std::string vr = content["vr"].asString(); | |
587 | |
588 const std::string keyword = FromDcmtkBridge::GetTagName(tag, ""); | |
589 | |
590 pugi::xml_node node = target.append_child("DicomAttribute"); | |
591 node.append_attribute("tag").set_value(members[i].c_str()); | |
592 node.append_attribute("vr").set_value(vr.c_str()); | |
593 | |
594 if (keyword != std::string(DcmTag_ERROR_TagName)) | |
595 { | |
596 node.append_attribute("keyword").set_value(keyword.c_str()); | |
597 } | |
598 | |
599 if (content.isMember(KEY_VALUE)) | |
600 { | |
601 assert(content[KEY_VALUE].type() == Json::arrayValue); | |
602 | |
603 for (Json::Value::ArrayIndex j = 0; j < content[KEY_VALUE].size(); j++) | |
604 { | |
605 std::string number = boost::lexical_cast<std::string>(j + 1); | |
606 | |
607 if (vr == "SQ") | |
608 { | |
609 if (content[KEY_VALUE][j].type() == Json::objectValue) | |
610 { | |
611 pugi::xml_node child = node.append_child("Item"); | |
612 child.append_attribute("number").set_value(number.c_str()); | |
613 ExploreDataset(child, content[KEY_VALUE][j]); | |
614 } | |
615 } | |
616 if (vr == "PN") | |
617 { | |
618 if (content[KEY_VALUE][j].isMember(KEY_ALPHABETIC) && | |
619 content[KEY_VALUE][j][KEY_ALPHABETIC].type() == Json::stringValue) | |
620 { | |
621 std::vector<std::string> tokens; | |
622 Toolbox::TokenizeString(tokens, content[KEY_VALUE][j][KEY_ALPHABETIC].asString(), '^'); | |
623 | |
624 pugi::xml_node child = node.append_child("PersonName"); | |
625 child.append_attribute("number").set_value(number.c_str()); | |
626 | |
627 pugi::xml_node name = child.append_child(KEY_ALPHABETIC); | |
628 | |
629 if (tokens.size() >= 1) | |
630 { | |
631 name.append_child("FamilyName").text() = tokens[0].c_str(); | |
632 } | |
633 | |
634 if (tokens.size() >= 2) | |
635 { | |
636 name.append_child("GivenName").text() = tokens[1].c_str(); | |
637 } | |
638 | |
639 if (tokens.size() >= 3) | |
640 { | |
641 name.append_child("MiddleName").text() = tokens[2].c_str(); | |
642 } | |
643 | |
644 if (tokens.size() >= 4) | |
645 { | |
646 name.append_child("NamePrefix").text() = tokens[3].c_str(); | |
647 } | |
648 | |
649 if (tokens.size() >= 5) | |
650 { | |
651 name.append_child("NameSuffix").text() = tokens[4].c_str(); | |
652 } | |
653 } | |
654 } | |
655 else | |
656 { | |
657 pugi::xml_node child = node.append_child("Value"); | |
658 child.append_attribute("number").set_value(number.c_str()); | |
659 | |
660 switch (content[KEY_VALUE][j].type()) | |
661 { | |
662 case Json::stringValue: | |
663 child.text() = content[KEY_VALUE][j].asCString(); | |
664 break; | |
665 | |
666 case Json::realValue: | |
667 child.text() = content[KEY_VALUE][j].asFloat(); | |
668 break; | |
669 | |
670 case Json::intValue: | |
671 child.text() = content[KEY_VALUE][j].asInt(); | |
672 break; | |
673 | |
674 case Json::uintValue: | |
675 child.text() = content[KEY_VALUE][j].asUInt(); | |
676 break; | |
677 | |
678 default: | |
679 break; | |
680 } | |
681 } | |
682 } | |
683 } | |
684 else if (content.isMember(KEY_BULK_DATA_URI) && | |
685 content[KEY_BULK_DATA_URI].type() == Json::stringValue) | |
686 { | |
687 pugi::xml_node child = node.append_child("BulkData"); | |
688 child.append_attribute("URI").set_value(content[KEY_BULK_DATA_URI].asCString()); | |
689 } | |
690 else if (content.isMember(KEY_INLINE_BINARY) && | |
691 content[KEY_INLINE_BINARY].type() == Json::stringValue) | |
692 { | |
693 pugi::xml_node child = node.append_child("InlineBinary"); | |
694 child.text() = content[KEY_INLINE_BINARY].asCString(); | |
695 } | |
696 } | |
697 } | |
698 | |
699 | |
700 static void DicomWebJsonToXml(pugi::xml_document& target, | |
701 const Json::Value& source) | |
702 { | |
703 pugi::xml_node root = target.append_child("NativeDicomModel"); | |
704 root.append_attribute("xmlns").set_value("http://dicom.nema.org/PS3.19/models/NativeDICOM"); | |
705 root.append_attribute("xsi:schemaLocation").set_value("http://dicom.nema.org/PS3.19/models/NativeDICOM"); | |
706 root.append_attribute("xmlns:xsi").set_value("http://www.w3.org/2001/XMLSchema-instance"); | |
707 | |
708 ExploreDataset(root, source); | |
709 | |
710 pugi::xml_node decl = target.prepend_child(pugi::node_declaration); | |
711 decl.append_attribute("version").set_value("1.0"); | |
712 decl.append_attribute("encoding").set_value("utf-8"); | |
713 } | |
714 | |
715 | |
716 enum DicomWebBinaryMode | |
717 { | |
718 DicomWebBinaryMode_Ignore, | |
719 DicomWebBinaryMode_BulkDataUri, | |
720 DicomWebBinaryMode_InlineBinary | |
721 }; | |
722 | |
723 class IDicomWebBinaryFormatter : public boost::noncopyable | |
724 { | |
725 public: | |
726 virtual ~IDicomWebBinaryFormatter() | |
727 { | |
728 } | |
729 | |
730 virtual DicomWebBinaryMode Format(std::string& bulkDataUri, | |
731 const std::vector<DicomTag>& parentTags, | |
732 const std::vector<size_t>& parentIndexes, | |
733 const DicomTag& tag, | |
734 ValueRepresentation vr) = 0; | |
735 }; | |
736 | |
571 class DicomWebJsonVisitor : public ITagVisitor | 737 class DicomWebJsonVisitor : public ITagVisitor |
572 { | 738 { |
573 public: | |
574 enum BinaryMode | |
575 { | |
576 BinaryMode_Ignore, | |
577 BinaryMode_BulkDataUri, | |
578 BinaryMode_InlineBinary | |
579 }; | |
580 | |
581 class IBinaryFormatter : public boost::noncopyable | |
582 { | |
583 public: | |
584 virtual ~IBinaryFormatter() | |
585 { | |
586 } | |
587 | |
588 virtual BinaryMode Format(std::string& bulkDataUri, | |
589 const std::vector<DicomTag>& parentTags, | |
590 const std::vector<size_t>& parentIndexes, | |
591 const DicomTag& tag, | |
592 ValueRepresentation vr) = 0; | |
593 }; | |
594 | |
595 | |
596 private: | 739 private: |
597 Json::Value result_; | 740 Json::Value result_; |
598 IBinaryFormatter *formatter_; | 741 IDicomWebBinaryFormatter *formatter_; |
599 | 742 |
600 static std::string FormatTag(const DicomTag& tag) | 743 static std::string FormatTag(const DicomTag& tag) |
601 { | 744 { |
602 char buf[16]; | 745 char buf[16]; |
603 sprintf(buf, "%04X%04X", tag.GetGroup(), tag.GetElement()); | 746 sprintf(buf, "%04X%04X", tag.GetGroup(), tag.GetElement()); |
703 formatter_(NULL) | 846 formatter_(NULL) |
704 { | 847 { |
705 Clear(); | 848 Clear(); |
706 } | 849 } |
707 | 850 |
708 void SetFormatter(IBinaryFormatter& formatter) | 851 void SetFormatter(IDicomWebBinaryFormatter& formatter) |
709 { | 852 { |
710 formatter_ = &formatter; | 853 formatter_ = &formatter; |
711 } | 854 } |
712 | 855 |
713 void Clear() | 856 void Clear() |
716 } | 859 } |
717 | 860 |
718 const Json::Value& GetResult() const | 861 const Json::Value& GetResult() const |
719 { | 862 { |
720 return result_; | 863 return result_; |
864 } | |
865 | |
866 void FormatXml(pugi::xml_document& target) const | |
867 { | |
868 DicomWebJsonToXml(target, result_); | |
721 } | 869 } |
722 | 870 |
723 virtual void VisitNotSupported(const std::vector<DicomTag>& parentTags, | 871 virtual void VisitNotSupported(const std::vector<DicomTag>& parentTags, |
724 const std::vector<size_t>& parentIndexes, | 872 const std::vector<size_t>& parentIndexes, |
725 const DicomTag& tag, | 873 const DicomTag& tag, |
752 vr == ValueRepresentation_OtherWord || | 900 vr == ValueRepresentation_OtherWord || |
753 vr == ValueRepresentation_Unknown); | 901 vr == ValueRepresentation_Unknown); |
754 | 902 |
755 if (tag.GetElement() != 0x0000) | 903 if (tag.GetElement() != 0x0000) |
756 { | 904 { |
757 BinaryMode mode; | 905 DicomWebBinaryMode mode; |
758 std::string bulkDataUri; | 906 std::string bulkDataUri; |
759 | 907 |
760 if (formatter_ == NULL) | 908 if (formatter_ == NULL) |
761 { | 909 { |
762 mode = BinaryMode_InlineBinary; | 910 mode = DicomWebBinaryMode_InlineBinary; |
763 } | 911 } |
764 else | 912 else |
765 { | 913 { |
766 mode = formatter_->Format(bulkDataUri, parentTags, parentIndexes, tag, vr); | 914 mode = formatter_->Format(bulkDataUri, parentTags, parentIndexes, tag, vr); |
767 } | 915 } |
768 | 916 |
769 if (mode != BinaryMode_Ignore) | 917 /*mode = DicomWebBinaryMode_BulkDataUri; |
918 bulkDataUri = "http://localhost/" + tag.Format();*/ | |
919 | |
920 if (mode != DicomWebBinaryMode_Ignore) | |
770 { | 921 { |
771 Json::Value& node = CreateNode(parentTags, parentIndexes, tag); | 922 Json::Value& node = CreateNode(parentTags, parentIndexes, tag); |
772 node[KEY_VR] = EnumerationToString(vr); | 923 node[KEY_VR] = EnumerationToString(vr); |
773 | 924 |
774 switch (mode) | 925 switch (mode) |
775 { | 926 { |
776 case BinaryMode_BulkDataUri: | 927 case DicomWebBinaryMode_BulkDataUri: |
777 node[KEY_BULK_DATA_URI] = bulkDataUri; | 928 node[KEY_BULK_DATA_URI] = bulkDataUri; |
778 break; | 929 break; |
779 | 930 |
780 case BinaryMode_InlineBinary: | 931 case DicomWebBinaryMode_InlineBinary: |
781 { | 932 { |
782 std::string tmp(static_cast<const char*>(data), size); | 933 std::string tmp(static_cast<const char*>(data), size); |
783 | 934 |
784 std::string base64; | 935 std::string base64; |
785 Toolbox::EncodeBase64(base64, tmp); | 936 Toolbox::EncodeBase64(base64, tmp); |
980 return Action_None; | 1131 return Action_None; |
981 } | 1132 } |
982 }; | 1133 }; |
983 } | 1134 } |
984 | 1135 |
1136 | |
1137 | |
1138 | |
1139 | |
1140 | |
985 #include "../Core/SystemToolbox.h" | 1141 #include "../Core/SystemToolbox.h" |
986 | 1142 |
987 | 1143 |
988 /* | 1144 /* |
989 | 1145 |
1016 | 1172 |
1017 Orthanc::DicomWebJsonVisitor visitor; | 1173 Orthanc::DicomWebJsonVisitor visitor; |
1018 dicom.Apply(visitor); | 1174 dicom.Apply(visitor); |
1019 | 1175 |
1020 Orthanc::SystemToolbox::WriteFile(visitor.GetResult().toStyledString(), "tutu.json"); | 1176 Orthanc::SystemToolbox::WriteFile(visitor.GetResult().toStyledString(), "tutu.json"); |
1177 | |
1178 pugi::xml_document xml; | |
1179 visitor.FormatXml(xml); | |
1180 xml.print(std::cout); | |
1021 } | 1181 } |
1022 | 1182 |
1023 | 1183 |
1024 TEST(DicomWebJson, Multiplicity) | 1184 TEST(DicomWebJson, Multiplicity) |
1025 { | 1185 { |
1026 // http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_F.2.4.html | 1186 // http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_F.2.4.html |
1027 | 1187 |
1028 ParsedDicomFile dicom(false); | 1188 ParsedDicomFile dicom(false); |
1189 dicom.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "SB1^SB2^SB3^SB4^SB5"); | |
1029 dicom.ReplacePlainString(DICOM_TAG_IMAGE_ORIENTATION_PATIENT, "1\\2.3\\4"); | 1190 dicom.ReplacePlainString(DICOM_TAG_IMAGE_ORIENTATION_PATIENT, "1\\2.3\\4"); |
1030 dicom.ReplacePlainString(DICOM_TAG_IMAGE_POSITION_PATIENT, ""); | 1191 dicom.ReplacePlainString(DICOM_TAG_IMAGE_POSITION_PATIENT, ""); |
1031 | 1192 |
1032 Orthanc::DicomWebJsonVisitor visitor; | 1193 Orthanc::DicomWebJsonVisitor visitor; |
1033 dicom.Apply(visitor); | 1194 dicom.Apply(visitor); |
1048 { | 1209 { |
1049 const Json::Value& tag = visitor.GetResult() ["00200032"]; | 1210 const Json::Value& tag = visitor.GetResult() ["00200032"]; |
1050 ASSERT_EQ(EnumerationToString(ValueRepresentation_DecimalString), tag["vr"].asString()); | 1211 ASSERT_EQ(EnumerationToString(ValueRepresentation_DecimalString), tag["vr"].asString()); |
1051 ASSERT_EQ(1u, tag.getMemberNames().size()); | 1212 ASSERT_EQ(1u, tag.getMemberNames().size()); |
1052 } | 1213 } |
1214 | |
1215 pugi::xml_document xml; | |
1216 visitor.FormatXml(xml); | |
1217 xml.print(std::cout); | |
1053 } | 1218 } |
1054 | 1219 |
1055 | 1220 |
1056 TEST(DicomWebJson, NullValue) | 1221 TEST(DicomWebJson, NullValue) |
1057 { | 1222 { |
1075 ASSERT_EQ(Json::nullValue, value[2].type()); | 1240 ASSERT_EQ(Json::nullValue, value[2].type()); |
1076 ASSERT_EQ(Json::realValue, value[3].type()); | 1241 ASSERT_EQ(Json::realValue, value[3].type()); |
1077 ASSERT_FLOAT_EQ(1.5f, value[0].asFloat()); | 1242 ASSERT_FLOAT_EQ(1.5f, value[0].asFloat()); |
1078 ASSERT_FLOAT_EQ(2.5f, value[3].asFloat()); | 1243 ASSERT_FLOAT_EQ(2.5f, value[3].asFloat()); |
1079 } | 1244 } |
1245 | |
1246 pugi::xml_document xml; | |
1247 visitor.FormatXml(xml); | |
1248 xml.print(std::cout); | |
1080 } | 1249 } |
1081 | 1250 |
1082 | 1251 |
1083 TEST(DicomWebJson, ValueRepresentation) | 1252 TEST(DicomWebJson, ValueRepresentation) |
1084 { | 1253 { |
1213 ASSERT_EQ("US", visitor.GetResult() ["00080301"]["vr"].asString()); | 1382 ASSERT_EQ("US", visitor.GetResult() ["00080301"]["vr"].asString()); |
1214 ASSERT_EQ(17, visitor.GetResult() ["00080301"]["Value"][0].asUInt()); | 1383 ASSERT_EQ(17, visitor.GetResult() ["00080301"]["Value"][0].asUInt()); |
1215 | 1384 |
1216 ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["vr"].asString()); | 1385 ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["vr"].asString()); |
1217 ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["Value"][0].asString()); | 1386 ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["Value"][0].asString()); |
1387 | |
1388 | |
1389 pugi::xml_document xml; | |
1390 visitor.FormatXml(xml); | |
1391 xml.print(std::cout); | |
1218 } | 1392 } |
1219 | 1393 |
1220 | 1394 |
1221 TEST(DicomWebJson, Sequence) | 1395 TEST(DicomWebJson, Sequence) |
1222 { | 1396 { |
1254 | 1428 |
1255 ASSERT_EQ(3u, items.size()); | 1429 ASSERT_EQ(3u, items.size()); |
1256 ASSERT_TRUE(items.find("item0") != items.end()); | 1430 ASSERT_TRUE(items.find("item0") != items.end()); |
1257 ASSERT_TRUE(items.find("item1") != items.end()); | 1431 ASSERT_TRUE(items.find("item1") != items.end()); |
1258 ASSERT_TRUE(items.find("item2") != items.end()); | 1432 ASSERT_TRUE(items.find("item2") != items.end()); |
1433 | |
1434 pugi::xml_document xml; | |
1435 visitor.FormatXml(xml); | |
1436 xml.print(std::cout); | |
1259 } | 1437 } |