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 }