# HG changeset patch # User Sebastien Jodogne # Date 1623148668 -7200 # Node ID 7182f5732480cd5666f13252251ba05ded2275b2 # Parent d38a7040474aa9f090d6cd217f53e2c340c00c70 use of DicomPath in ParsedDicomFile diff -r d38a7040474a -r 7182f5732480 OrthancFramework/Sources/DicomFormat/DicomPath.cpp --- a/OrthancFramework/Sources/DicomFormat/DicomPath.cpp Mon Jun 07 18:35:46 2021 +0200 +++ b/OrthancFramework/Sources/DicomFormat/DicomPath.cpp Tue Jun 08 12:37:48 2021 +0200 @@ -141,6 +141,27 @@ } + DicomPath::DicomPath(const std::vector& parentTags, + const std::vector parentIndexes, + const Orthanc::DicomTag& finalTag) : + finalTag_(finalTag) + { + if (parentTags.size() != parentIndexes.size()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + prefix_.reserve(parentTags.size()); + + for (size_t i = 0; i < prefix_.size(); i++) + { + prefix_.push_back(PrefixItem::CreateIndexed(parentTags[i], parentIndexes[i])); + } + } + } + + void DicomPath::AddIndexedTagToPrefix(const Orthanc::DicomTag& tag, size_t index) { @@ -259,4 +280,39 @@ return path; } + + + bool DicomPath::IsMatch(const DicomPath& pattern, + const DicomPath& path) + { + if (path.HasUniversal()) + { + throw OrthancException(ErrorCode_BadParameterType); + } + else if (path.GetPrefixLength() < pattern.GetPrefixLength()) + { + return false; + } + else + { + for (size_t i = 0; i < pattern.GetPrefixLength(); i++) + { + if (path.GetPrefixTag(i) != pattern.GetPrefixTag(i) || + (!pattern.IsPrefixUniversal(i) && + path.GetPrefixIndex(i) != pattern.GetPrefixIndex(i))) + { + return false; + } + } + + if (path.GetPrefixLength() == pattern.GetPrefixLength()) + { + return (path.GetFinalTag() == pattern.GetFinalTag()); + } + else + { + return (path.GetPrefixTag(pattern.GetPrefixLength()) == pattern.GetFinalTag()); + } + } + } } diff -r d38a7040474a -r 7182f5732480 OrthancFramework/Sources/DicomFormat/DicomPath.h --- a/OrthancFramework/Sources/DicomFormat/DicomPath.h Mon Jun 07 18:35:46 2021 +0200 +++ b/OrthancFramework/Sources/DicomFormat/DicomPath.h Tue Jun 08 12:37:48 2021 +0200 @@ -99,6 +99,10 @@ size_t index3, const Orthanc::DicomTag& tag); + DicomPath(const std::vector& parentTags, + const std::vector parentIndexes, + const Orthanc::DicomTag& finalTag); + void AddIndexedTagToPrefix(const Orthanc::DicomTag& tag, size_t index); @@ -134,5 +138,8 @@ std::string Format() const; static DicomPath Parse(const std::string& s); + + static bool IsMatch(const DicomPath& pattern, + const DicomPath& path); }; } diff -r d38a7040474a -r 7182f5732480 OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp --- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Mon Jun 07 18:35:46 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Tue Jun 08 12:37:48 2021 +0200 @@ -2798,16 +2798,20 @@ static void ApplyInternal(FromDcmtkBridge::IDicomPathVisitor& visitor, DcmItem& item, - const DicomPath& path, - size_t level) + const DicomPath& pattern, + const DicomPath& actualPath) { - if (level == path.GetPrefixLength()) + const size_t level = actualPath.GetPrefixLength(); + + if (level == pattern.GetPrefixLength()) { - visitor.Visit(item, path.GetFinalTag()); + visitor.Visit(item, actualPath); } else { - const DicomTag& tmp = path.GetPrefixTag(level); + assert(level < pattern.GetPrefixLength()); + + const DicomTag& tmp = pattern.GetPrefixTag(level); DcmTagKey tag(tmp.GetGroup(), tmp.GetElement()); DcmSequenceOfItems *sequence = NULL; @@ -2816,13 +2820,16 @@ { for (unsigned long i = 0; i < sequence->card(); i++) { - if (path.IsPrefixUniversal(level) || - path.GetPrefixIndex(level) == static_cast(i)) + if (pattern.IsPrefixUniversal(level) || + pattern.GetPrefixIndex(level) == static_cast(i)) { DcmItem *child = sequence->getItem(i); if (child != NULL) { - ApplyInternal(visitor, *child, path, level + 1); + DicomPath childPath = actualPath; + childPath.AddIndexedTagToPrefix(pattern.GetPrefixTag(level), static_cast(i)); + + ApplyInternal(visitor, *child, pattern, childPath); } } } @@ -2835,7 +2842,8 @@ DcmDataset& dataset, const DicomPath& path) { - ApplyInternal(visitor, dataset, path, 0); + DicomPath actualPath(path.GetFinalTag()); + ApplyInternal(visitor, dataset, path, actualPath); } @@ -2846,10 +2854,10 @@ { public: virtual void Visit(DcmItem& item, - const DicomTag& tag) ORTHANC_OVERRIDE + const DicomPath& path) ORTHANC_OVERRIDE { - DcmTagKey tmp(tag.GetGroup(), tag.GetElement()); - std::unique_ptr removed(item.remove(tmp)); + DcmTagKey key(path.GetFinalTag().GetGroup(), path.GetFinalTag().GetElement()); + std::unique_ptr removed(item.remove(key)); } }; @@ -2858,18 +2866,62 @@ } + void FromDcmtkBridge::ClearPath(DcmDataset& dataset, + const DicomPath& path, + bool onlyIfExists) + { + class Visitor : public FromDcmtkBridge::IDicomPathVisitor + { + public: + bool onlyIfExists_; + + public: + Visitor(bool onlyIfExists) : + onlyIfExists_(onlyIfExists) + { + } + + virtual void Visit(DcmItem& item, + const DicomPath& path) ORTHANC_OVERRIDE + { + DcmTagKey key(path.GetFinalTag().GetGroup(), path.GetFinalTag().GetElement()); + + if (onlyIfExists_ && + !item.tagExists(key)) + { + // The tag is non-existing, do not clear it + } + else + { + if (!item.insertEmptyElement(key, OFTrue /* replace old value */).good()) + { + throw OrthancException(ErrorCode_InternalError); + } + } + } + }; + + Visitor visitor(onlyIfExists); + Apply(visitor, dataset, path); + } + + void FromDcmtkBridge::ReplacePath(DcmDataset& dataset, const DicomPath& path, - const DcmElement& element) + const DcmElement& element, + DicomReplaceMode mode) { class Visitor : public FromDcmtkBridge::IDicomPathVisitor { private: std::unique_ptr element_; + DicomReplaceMode mode_; public: - Visitor(const DcmElement& element) : - element_(dynamic_cast(element.clone())) + Visitor(const DcmElement& element, + DicomReplaceMode mode) : + element_(dynamic_cast(element.clone())), + mode_(mode) { if (element_.get() == NULL) { @@ -2878,7 +2930,7 @@ } virtual void Visit(DcmItem& item, - const DicomTag& tag) ORTHANC_OVERRIDE + const DicomPath& path) ORTHANC_OVERRIDE { std::unique_ptr cloned(dynamic_cast(element_->clone())); if (cloned.get() == NULL) @@ -2887,25 +2939,44 @@ } else { - DcmTagKey tmp(tag.GetGroup(), tag.GetElement()); + DcmTagKey key(path.GetFinalTag().GetGroup(), path.GetFinalTag().GetElement()); + + if (!item.tagExists(key)) + { + switch (mode_) + { + case DicomReplaceMode_InsertIfAbsent: + break; // Fine, we can proceed with insertion + + case DicomReplaceMode_ThrowIfAbsent: + throw OrthancException(ErrorCode_InexistentItem, "Cannot replace inexistent tag: " + GetTagName(*element_)); + + case DicomReplaceMode_IgnoreIfAbsent: + return; // Don't proceed with insertion + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + if (!item.insert(cloned.release(), OFTrue /* replace old */).good()) { - throw OrthancException(ErrorCode_InternalError, "Cannot replace an element"); + throw OrthancException(ErrorCode_InternalError, "Cannot replace an element: " + GetTagName(*element_)); } } } }; - DcmTagKey tmp(path.GetFinalTag().GetGroup(), path.GetFinalTag().GetElement()); + DcmTagKey key(path.GetFinalTag().GetGroup(), path.GetFinalTag().GetElement()); - if (element.getTag() != tmp) + if (element.getTag() != key) { throw OrthancException(ErrorCode_ParameterOutOfRange, "The final tag must be the same as the tag of the element during a replacement"); } else { - Visitor visitor(element); + Visitor visitor(element, mode); Apply(visitor, dataset, path); } } diff -r d38a7040474a -r 7182f5732480 OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h --- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h Mon Jun 07 18:35:46 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h Tue Jun 08 12:37:48 2021 +0200 @@ -70,7 +70,7 @@ } virtual void Visit(DcmItem& item, - const DicomTag& tag) = 0; + const DicomPath& path) = 0; }; @@ -249,8 +249,13 @@ static void RemovePath(DcmDataset& dataset, const DicomPath& path); + static void ClearPath(DcmDataset& dataset, + const DicomPath& path, + bool onlyIfExists); + static void ReplacePath(DcmDataset& dataset, const DicomPath& path, - const DcmElement& element); + const DcmElement& element, + DicomReplaceMode mode); }; } diff -r d38a7040474a -r 7182f5732480 OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp --- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Mon Jun 07 18:35:46 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Tue Jun 08 12:37:48 2021 +0200 @@ -482,44 +482,14 @@ void ParsedDicomFile::Remove(const DicomTag& tag) { - InvalidateCache(); - - DcmTagKey key(tag.GetGroup(), tag.GetElement()); - DcmElement* element = GetDcmtkObject().getDataset()->remove(key); - if (element != NULL) - { - delete element; - } + RemovePath(DicomPath(tag)); } void ParsedDicomFile::Clear(const DicomTag& tag, bool onlyIfExists) { - if (tag.GetElement() == 0x0000) - { - // Prevent manually modifying generic group length tags: This is - // handled by DCMTK serialization - return; - } - - InvalidateCache(); - - DcmItem* dicom = GetDcmtkObject().getDataset(); - DcmTagKey key(tag.GetGroup(), tag.GetElement()); - - if (onlyIfExists && - !dicom->tagExists(key)) - { - // The tag is non-existing, do not clear it - } - else - { - if (!dicom->insertEmptyElement(key, OFTrue /* replace old value */).good()) - { - throw OrthancException(ErrorCode_InternalError); - } - } + ClearPath(DicomPath(tag), onlyIfExists); } @@ -678,7 +648,8 @@ return true; case DicomReplaceMode_ThrowIfAbsent: - throw OrthancException(ErrorCode_InexistentItem); + throw OrthancException(ErrorCode_InexistentItem, "Cannot replace inexistent tag: " + + FromDcmtkBridge::GetTagName(DicomTag(tag.getGroup(), tag.getElement()), "")); case DicomReplaceMode_IgnoreIfAbsent: return false; @@ -758,36 +729,59 @@ // handled by DCMTK serialization return; } - - InvalidateCache(); - - DcmDataset& dicom = *GetDcmtkObject().getDataset(); - if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode)) + else { - // Either the tag was previously existing (and now removed), or - // the replace mode was set to "InsertIfAbsent" + InvalidateCache(); + + DcmDataset& dicom = *GetDcmtkObject().getDataset(); + if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode)) + { + // Either the tag was previously existing (and now removed), or + // the replace mode was set to "InsertIfAbsent" + + if (decodeDataUriScheme && + (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT || + tag == DICOM_TAG_PIXEL_DATA)) + { + if (EmbedContentInternal(utf8Value)) + { + return; + } + } + + std::unique_ptr element(FromDcmtkBridge::CreateElementForTag(tag, privateCreator)); - if (decodeDataUriScheme && - (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT || - tag == DICOM_TAG_PIXEL_DATA)) - { - if (EmbedContentInternal(utf8Value)) + if (!utf8Value.empty()) + { + bool hasCodeExtensions; + Encoding encoding = DetectEncoding(hasCodeExtensions); + FromDcmtkBridge::FillElementWithString(*element, utf8Value, decodeDataUriScheme, encoding); + } + + InsertInternal(dicom, element.release()); + + if (tag == DICOM_TAG_SOP_CLASS_UID || + tag == DICOM_TAG_SOP_INSTANCE_UID) { - return; + if (decodeDataUriScheme && + boost::starts_with(utf8Value, URI_SCHEME_PREFIX_BINARY)) + { + std::string mime, decoded; + if (!Toolbox::DecodeDataUriScheme(mime, decoded, utf8Value)) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + else + { + UpdateStorageUid(tag, decoded, false); + } + } + else + { + UpdateStorageUid(tag, utf8Value, false); + } } } - - std::unique_ptr element(FromDcmtkBridge::CreateElementForTag(tag, privateCreator)); - - if (!utf8Value.empty()) - { - bool hasCodeExtensions; - Encoding encoding = DetectEncoding(hasCodeExtensions); - FromDcmtkBridge::FillElementWithString(*element, utf8Value, decodeDataUriScheme, encoding); - } - - InsertInternal(dicom, element.release()); - UpdateStorageUid(tag, utf8Value, false); } } @@ -804,39 +798,30 @@ // handled by DCMTK serialization return; } - - InvalidateCache(); - - DcmDataset& dicom = *GetDcmtkObject().getDataset(); - if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode)) + else if (value.type() == Json::stringValue) { - // Either the tag was previously existing (and now removed), or - // the replace mode was set to "InsertIfAbsent" - - if (decodeDataUriScheme && - value.type() == Json::stringValue && - (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT || - tag == DICOM_TAG_PIXEL_DATA)) - { - if (EmbedContentInternal(value.asString())) - { - return; - } - } - - bool hasCodeExtensions; - Encoding encoding = DetectEncoding(hasCodeExtensions); - InsertInternal(dicom, FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, encoding, privateCreator)); - + Replace(tag, value.asString(), decodeDataUriScheme, mode, privateCreator); + } + else + { if (tag == DICOM_TAG_SOP_CLASS_UID || tag == DICOM_TAG_SOP_INSTANCE_UID) { - if (value.type() != Json::stringValue) - { - throw OrthancException(ErrorCode_BadParameterType); - } + // Must be a string + throw OrthancException(ErrorCode_BadParameterType); + } + + InvalidateCache(); - UpdateStorageUid(tag, value.asString(), decodeDataUriScheme); + DcmDataset& dicom = *GetDcmtkObject().getDataset(); + if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode)) + { + // Either the tag was previously existing (and now removed), or + // the replace mode was set to "InsertIfAbsent" + + bool hasCodeExtensions; + Encoding encoding = DetectEncoding(hasCodeExtensions); + InsertInternal(dicom, FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, encoding, privateCreator)); } } } @@ -1724,6 +1709,74 @@ } + static bool HasGenericGroupLength(const DicomPath& path) + { + for (size_t i = 0; i < path.GetPrefixLength(); i++) + { + if (path.GetPrefixTag(i).GetElement() == 0x0000) + { + return true; + } + } + + return (path.GetFinalTag().GetElement() == 0x0000); + } + + + void ParsedDicomFile::ReplacePath(const DicomPath& path, + const Json::Value& value, + bool decodeDataUriScheme, + DicomReplaceMode mode, + const std::string& privateCreator) + { + if (HasGenericGroupLength(path)) + { + // Prevent manually modifying generic group length tags: This is + // handled by DCMTK serialization + return; + } + else if (path.GetPrefixLength() == 0) + { + Replace(path.GetFinalTag(), value, decodeDataUriScheme, mode, privateCreator); + } + else + { + InvalidateCache(); + + bool hasCodeExtensions; + Encoding encoding = DetectEncoding(hasCodeExtensions); + std::unique_ptr element( + FromDcmtkBridge::FromJson(path.GetFinalTag(), value, decodeDataUriScheme, encoding, privateCreator)); + + FromDcmtkBridge::ReplacePath(*GetDcmtkObject().getDataset(), path, *element, mode); + } + } + + + void ParsedDicomFile::RemovePath(const DicomPath& path) + { + InvalidateCache(); + FromDcmtkBridge::RemovePath(*GetDcmtkObject().getDataset(), path); + } + + + void ParsedDicomFile::ClearPath(const DicomPath& path, + bool onlyIfExists) + { + if (HasGenericGroupLength(path)) + { + // Prevent manually modifying generic group length tags: This is + // handled by DCMTK serialization + return; + } + else + { + InvalidateCache(); + FromDcmtkBridge::ClearPath(*GetDcmtkObject().getDataset(), path, onlyIfExists); + } + } + + #if ORTHANC_BUILDING_FRAMEWORK_LIBRARY == 1 // Alias for binary compatibility with Orthanc Framework 1.7.2 => don't use it anymore void ParsedDicomFile::DatasetToJson(Json::Value& target, diff -r d38a7040474a -r 7182f5732480 OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h --- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Mon Jun 07 18:35:46 2021 +0200 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Tue Jun 08 12:37:48 2021 +0200 @@ -54,6 +54,7 @@ #include "ITagVisitor.h" #include "../DicomFormat/DicomInstanceHasher.h" +#include "../DicomFormat/DicomPath.h" #include "../Images/ImageAccessor.h" #include "../IDynamicObject.h" #include "../Toolbox.h" @@ -273,5 +274,16 @@ // Decode the given frame, using the built-in DICOM decoder of Orthanc ImageAccessor* DecodeFrame(unsigned int frame) const; + + void ReplacePath(const DicomPath& path, + const Json::Value& value, // Assumed to be encoded with UTF-8 + bool decodeDataUriScheme, + DicomReplaceMode mode, + const std::string& privateCreator /* used only for private tags */); + + void RemovePath(const DicomPath& path); + + void ClearPath(const DicomPath& path, + bool onlyIfExists); }; } diff -r d38a7040474a -r 7182f5732480 OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp --- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Mon Jun 07 18:35:46 2021 +0200 +++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp Tue Jun 08 12:37:48 2021 +0200 @@ -2358,118 +2358,260 @@ ASSERT_THROW(DicomPath::Parse("(0010,0010[].PatientID"), OrthancException); ASSERT_THROW(DicomPath::Parse("(0010,0010)0].PatientID"), OrthancException); ASSERT_THROW(DicomPath::Parse("(0010,0010)[-1].PatientID"), OrthancException); + + ASSERT_TRUE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)"), + DicomPath::Parse("(0010,0010)"))); + ASSERT_FALSE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)"), + DicomPath::Parse("(0010,0020)"))); + ASSERT_TRUE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)"))); + ASSERT_FALSE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[1].(0010,0020)"), + DicomPath::Parse("(0010,0010)"))); + ASSERT_TRUE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[1].(0010,0020)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)"))); + ASSERT_TRUE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[*].(0010,0020)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)"))); + ASSERT_FALSE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[2].(0010,0020)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)"))); + ASSERT_THROW(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[1].(0010,0020)"), + DicomPath::Parse("(0010,0010)[*].(0010,0020)")), OrthancException); + ASSERT_TRUE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[*].(0010,0020)[*].(0010,0030)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)[2].(0010,0030)[3].(0010,0040)"))); + ASSERT_TRUE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[1].(0010,0020)[2].(0010,0030)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)[2].(0010,0030)[3].(0010,0040)"))); + ASSERT_FALSE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[1].(0010,0020)[3].(0010,0030)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)[2].(0010,0030)[3].(0010,0040)"))); + ASSERT_FALSE(DicomPath::IsMatch(DicomPath::Parse("(0010,0010)[2].(0010,0020)[2].(0010,0030)"), + DicomPath::Parse("(0010,0010)[1].(0010,0020)[2].(0010,0030)[3].(0010,0040)"))); } -TEST(ParsedDicomFile, RemovePath) +TEST(ParsedDicomFile, DicomPath) { + Json::Value v = Json::objectValue; + v["PatientName"] = "Hello"; + v["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4"; + + { + Json::Value a = Json::arrayValue; + + { + Json::Value item = Json::objectValue; + item["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4"; + item["ReferencedSOPInstanceUID"] = "1.2.840.113619.2.176.2025.1499492.7040.1171286241.719"; + a.append(item); + } + + { + Json::Value item = Json::objectValue; + item["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4"; // ReferencedSOPClassUID + item["ReferencedSOPInstanceUID"] = "1.2.840.113619.2.176.2025.1499492.7040.1171286241.726"; + a.append(item); + } + + v["ReferencedImageSequence"] = a; + } + + { + Json::Value a = Json::arrayValue; + + { + Json::Value item = Json::objectValue; + item["StudyInstanceUID"] = "1.2.840.113704.1.111.7016.1342451220.40"; + + { + Json::Value b = Json::arrayValue; + + { + Json::Value c = Json::objectValue; + c["CodeValue"] = "122403"; + c["0010,0010"] = "WORLD"; // Patient name + b.append(c); + } + + item["PurposeOfReferenceCodeSequence"] = b; + } + + a.append(item); + } + + v["RelatedSeriesSequence"] = a; + } + + static const char* CODE_VALUE = "0008,0100"; + static const char* PATIENT_ID = "0010,0020"; + static const char* PATIENT_NAME = "0010,0010"; + static const char* PURPOSE_CODE_SEQ = "0040,a170"; + static const char* REF_IM_SEQ = "0008,1140"; + static const char* REF_SOP_CLASS = "0008,1150"; + static const char* REF_SOP_INSTANCE = "0008,1155"; + static const char* REL_SERIES_SEQ = "0008,1250"; + { - Json::Value v = Json::arrayValue; - - Json::Value item = Json::objectValue; - item["PatientID"] = "HELLO"; - v.append(item); - - std::unique_ptr d(FromDcmtkBridge::FromJson(DICOM_TAG_SOURCE_IMAGE_SEQUENCE, - v, false, Encoding_Latin1, "")); - d->writeXML(std::cout); + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ(5u, vv.size()); + ASSERT_TRUE(vv.isMember(PATIENT_NAME)); + ASSERT_EQ(2u, vv[REF_IM_SEQ].size()); + ASSERT_EQ(1u, vv[REL_SERIES_SEQ].size()); + ASSERT_EQ(2u, vv[REF_IM_SEQ][0].size()); + ASSERT_EQ(2u, vv[REL_SERIES_SEQ][0].size()); + ASSERT_EQ(1u, vv[REL_SERIES_SEQ][0][PURPOSE_CODE_SEQ].size()); + + ASSERT_TRUE(vv[REF_IM_SEQ][0].isMember(REF_SOP_CLASS)); + ASSERT_TRUE(vv[REF_IM_SEQ][1].isMember(REF_SOP_CLASS)); + ASSERT_TRUE(vv[REL_SERIES_SEQ][0][PURPOSE_CODE_SEQ][0].isMember(CODE_VALUE)); + } + + { + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->RemovePath(DicomPath::Parse("ReferencedImageSequence[*].ReferencedSOPClassUID")); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ(2u, vv[REF_IM_SEQ].size()); + ASSERT_EQ(1u, vv[REF_IM_SEQ][0].size()); + ASSERT_EQ(1u, vv[REF_IM_SEQ][1].size()); + ASSERT_FALSE(vv[REF_IM_SEQ][0].isMember(REF_SOP_CLASS)); + ASSERT_FALSE(vv[REF_IM_SEQ][1].isMember(REF_SOP_CLASS)); + } + + { + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->RemovePath(DicomPath::Parse("ReferencedImageSequence[0].ReferencedSOPClassUID")); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ(2u, vv[REF_IM_SEQ].size()); + ASSERT_EQ(1u, vv[REF_IM_SEQ][0].size()); + ASSERT_EQ(2u, vv[REF_IM_SEQ][1].size()); + ASSERT_FALSE(vv[REF_IM_SEQ][0].isMember(REF_SOP_CLASS)); + ASSERT_TRUE(vv[REF_IM_SEQ][1].isMember(REF_SOP_CLASS)); + } + + { + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->RemovePath(DicomPath::Parse("ReferencedImageSequence[1].ReferencedSOPClassUID")); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ(2u, vv[REF_IM_SEQ].size()); + ASSERT_EQ(2u, vv[REF_IM_SEQ][0].size()); + ASSERT_EQ(1u, vv[REF_IM_SEQ][1].size()); + ASSERT_TRUE(vv[REF_IM_SEQ][0].isMember(REF_SOP_CLASS)); + ASSERT_FALSE(vv[REF_IM_SEQ][1].isMember(REF_SOP_CLASS)); + } + + { + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->RemovePath(DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence[0].CodeValue")); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ("WORLD", vv[REL_SERIES_SEQ][0][PURPOSE_CODE_SEQ][0][PATIENT_NAME].asString()); + ASSERT_FALSE(vv[REL_SERIES_SEQ][0][PURPOSE_CODE_SEQ][0].isMember(CODE_VALUE)); + } + + { + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->RemovePath(DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence")); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ(1u, vv[REL_SERIES_SEQ][0].size()); + ASSERT_FALSE(vv[REL_SERIES_SEQ][0].isMember(PURPOSE_CODE_SEQ)); + } + + { + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->RemovePath(DicomPath::Parse("RelatedSeriesSequence")); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_FALSE(vv.isMember(REL_SERIES_SEQ)); } { - Json::Value v = "Hello"; - std::unique_ptr d(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_ID, - v, false, Encoding_Latin1, "")); - d->writeXML(std::cout); + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->RemovePath(DicomPath(DICOM_TAG_PATIENT_NAME)); + dicom->ReplacePath(DicomPath::Parse("ReferencedImageSequence[*].ReferencedSOPClassUID"), + "Hello1", false, DicomReplaceMode_ThrowIfAbsent, ""); + ASSERT_THROW(dicom->ReplacePath(DicomPath::Parse("ReferencedImageSequence[*].PatientID"), + "Hello2", false, DicomReplaceMode_ThrowIfAbsent, ""), OrthancException); + dicom->ReplacePath(DicomPath::Parse("ReferencedImageSequence[*].PatientID"), + "Hello3", false, DicomReplaceMode_InsertIfAbsent, ""); + dicom->ReplacePath(DicomPath::Parse("ReferencedImageSequence[*].PatientName"), + "Hello4", false, DicomReplaceMode_IgnoreIfAbsent, ""); + dicom->ReplacePath(DicomPath::Parse("RelatedSeriesSequence[*].PurposeOfReferenceCodeSequence[*].CodeValue"), + "Hello5", false, DicomReplaceMode_ThrowIfAbsent, ""); + + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ(4u, vv.size()); + ASSERT_FALSE(vv.isMember(PATIENT_NAME)); + ASSERT_EQ("Hello1", vv[REF_IM_SEQ][0][REF_SOP_CLASS].asString()); + ASSERT_EQ("Hello3", vv[REF_IM_SEQ][0][PATIENT_ID].asString()); + ASSERT_EQ("Hello1", vv[REF_IM_SEQ][1][REF_SOP_CLASS].asString()); + ASSERT_EQ("Hello3", vv[REF_IM_SEQ][1][PATIENT_ID].asString()); + ASSERT_EQ("Hello5", vv[REL_SERIES_SEQ][0][PURPOSE_CODE_SEQ][0][CODE_VALUE].asString()); } - printf("\n"); - { - Json::Value v = Json::objectValue; - v["PatientID"] = "Hello"; - - { - Json::Value a = Json::arrayValue; - - { - Json::Value item = Json::objectValue; - item["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4"; - item["ReferencedSOPInstanceUID"] = "1.2.840.113619.2.176.2025.1499492.7040.1171286241.719"; - a.append(item); - } - - { - Json::Value item = Json::objectValue; - item["ReferencedSOPClassUID"] = "1.2.840.10008.5.1.4.1.1.4"; - item["ReferencedSOPInstanceUID"] = "1.2.840.113619.2.176.2025.1499492.7040.1171286241.726"; - a.append(item); - } + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->ReplacePath(DicomPath::Parse("ReferencedImageSequence[1].ReferencedSOPClassUID"), + "Hello1", false, DicomReplaceMode_ThrowIfAbsent, ""); + dicom->ReplacePath(DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence[0].CodeValue"), + "Hello2", false, DicomReplaceMode_ThrowIfAbsent, ""); - v["ReferencedImageSequence"] = a; - } - - { - Json::Value a = Json::arrayValue; - - { - Json::Value item = Json::objectValue; - item["StudyInstanceUID"] = "1.2.840.113704.1.111.7016.1342451220.40"; - - { - Json::Value b = Json::arrayValue; - - { - Json::Value c = Json::objectValue; - c["CodeValue"] = "122403"; - b.append(c); - } - - item["PurposeOfReferenceCodeSequence"] = b; - } - - a.append(item); - } + Json::Value vv; + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ("1.2.840.10008.5.1.4.1.1.4", vv[REF_IM_SEQ][0][REF_SOP_CLASS].asString()); + ASSERT_EQ("Hello1", vv[REF_IM_SEQ][1][REF_SOP_CLASS].asString()); + ASSERT_EQ("Hello2", vv[REL_SERIES_SEQ][0][PURPOSE_CODE_SEQ][0][CODE_VALUE].asString()); + } + + { + std::unique_ptr dicom(ParsedDicomFile::CreateFromJson(v, DicomFromJsonFlags_None, "")); + + dicom->ClearPath(DicomPath::Parse("ReferencedImageSequence[1].ReferencedSOPClassUID"), true); + dicom->ClearPath(DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence[0].CodeValue"), true); + dicom->ClearPath(DicomPath::Parse("ReferencedImageSequence[0].PatientID"), false); + dicom->ClearPath(DicomPath::Parse("ReferencedImageSequence[0].PatientName"), true); - v["RelatedSeriesSequence"] = a; - } - - std::unique_ptr d(FromDcmtkBridge::FromJson(v, false /* generate UID */, false, Encoding_Latin1, "")); - - static const DicomTag DICOM_TAG_REFERENCED_SOP_CLASS_UID(0x0008, 0x1150); - static const DicomTag DICOM_TAG_REFERENCED_IMAGE_SEQUENCE(0x0008, 0x1140); - - DicomPath path(DICOM_TAG_REFERENCED_SOP_CLASS_UID); - path.AddIndexedTagToPrefix(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE, 2); - //path.AddUniversalTagToPrefix(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE); - - //DicomPath path(DicomTag(0x0008, 0x0100)); - //path.AddIndexedTagToPrefix(DicomTag(0x0008, 0x1250), 0); - //path.AddIndexedTagToPrefix(DicomTag(0x0040, 0xa170), 1); - - //FromDcmtkBridge::RemovePath(*d, DicomPath::Parse("ReferencedImageSequence[*].ReferencedSOPClassUID")); - //FromDcmtkBridge::RemovePath(*d, DicomPath::Parse("ReferencedImageSequence[0].ReferencedSOPClassUID")); - //FromDcmtkBridge::RemovePath(*d, DicomPath::Parse("ReferencedImageSequence[1].ReferencedSOPClassUID")); - FromDcmtkBridge::RemovePath(*d, DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence[0].CodeValue")); - //FromDcmtkBridge::RemovePath(*d, DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence")); - //FromDcmtkBridge::RemovePath(*d, DicomPath::Parse("RelatedSeriesSequence")); - - { - Json::Value v = "Hello"; - std::unique_ptr e(FromDcmtkBridge::FromJson(DicomTag(0x0008, 0x0100), v, false, Encoding_Latin1, "")); - FromDcmtkBridge::ReplacePath(*d, DicomPath::Parse("RelatedSeriesSequence[0].PurposeOfReferenceCodeSequence[0].CodeValue"), *e); - } - - { - Json::Value v = "Hello"; - std::unique_ptr e(FromDcmtkBridge::FromJson(DicomTag(0x0008, 0x1150), v, false, Encoding_Latin1, "")); - FromDcmtkBridge::ReplacePath(*d, DicomPath::Parse("ReferencedImageSequence[*].ReferencedSOPClassUID"), *e); - } - Json::Value vv; - std::set ignoreTagLength; - FromDcmtkBridge::ExtractDicomAsJson(vv, *d, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0, ignoreTagLength); - std::cout << vv.toStyledString(); + dicom->DatasetToJson(vv, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); + + ASSERT_EQ(3u, vv[REF_IM_SEQ][0].size()); + ASSERT_EQ(2u, vv[REF_IM_SEQ][1].size()); + + ASSERT_EQ("1.2.840.10008.5.1.4.1.1.4", vv[REF_IM_SEQ][0][REF_SOP_CLASS].asString()); + ASSERT_EQ("1.2.840.113619.2.176.2025.1499492.7040.1171286241.719", vv[REF_IM_SEQ][0][REF_SOP_INSTANCE].asString()); + ASSERT_EQ("", vv[REF_IM_SEQ][0][PATIENT_ID].asString()); + + ASSERT_EQ("", vv[REF_IM_SEQ][1][REF_SOP_CLASS].asString()); + ASSERT_EQ("1.2.840.113619.2.176.2025.1499492.7040.1171286241.726", vv[REF_IM_SEQ][1][REF_SOP_INSTANCE].asString()); + + ASSERT_EQ("", vv[REL_SERIES_SEQ][0][PURPOSE_CODE_SEQ][0][CODE_VALUE].asString()); } }