# HG changeset patch # User Sebastien Jodogne # Date 1549395866 -3600 # Node ID 880e4161c31233e0fe50863d9fcdb5595ea4a1f9 # Parent 47ef29168698ace3db721c94a4bc2cd87ba25265 cont diff -r 47ef29168698 -r 880e4161c312 Core/DicomParsing/DicomModification.cpp --- a/Core/DicomParsing/DicomModification.cpp Tue Feb 05 18:34:27 2019 +0100 +++ b/Core/DicomParsing/DicomModification.cpp Tue Feb 05 20:44:26 2019 +0100 @@ -79,10 +79,16 @@ { } - virtual void VisitUnknown(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr) + virtual void VisitNotSupported(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr) + { + } + + virtual void VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) { } @@ -114,7 +120,6 @@ virtual void VisitAttributes(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, - ValueRepresentation vr, const std::vector& value) { } diff -r 47ef29168698 -r 880e4161c312 Core/DicomParsing/FromDcmtkBridge.cpp --- a/Core/DicomParsing/FromDcmtkBridge.cpp Tue Feb 05 18:34:27 2019 +0100 +++ b/Core/DicomParsing/FromDcmtkBridge.cpp Tue Feb 05 20:44:26 2019 +0100 @@ -2253,7 +2253,7 @@ } else { - visitor.VisitUnknown(parentTags, parentIndexes, tag, vr); + visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); } break; @@ -2405,7 +2405,8 @@ } } - visitor.VisitAttributes(parentTags, parentIndexes, tag, vr, values); + assert(vr == ValueRepresentation_AttributeTag); + visitor.VisitAttributes(parentTags, parentIndexes, tag, values); break; } @@ -2437,7 +2438,7 @@ case EVR_pixelItem: // used internally for pixel items in a compressed image case EVR_PixelData: // used internally for uncompressed pixeld data case EVR_OverlayData: // used internally for overlay data - visitor.VisitUnknown(parentTags, parentIndexes, tag, vr); + visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); return; @@ -2481,16 +2482,23 @@ // etc. are not." The following dynamic_cast is thus OK. DcmSequenceOfItems& sequence = dynamic_cast(element); - std::vector tags = parentTags; - std::vector indexes = parentIndexes; - tags.push_back(tag); - indexes.push_back(0); - - for (unsigned long i = 0; i < sequence.card(); i++) + if (sequence.card() == 0) + { + visitor.VisitEmptySequence(parentTags, parentIndexes, tag); + } + else { - indexes.back() = static_cast(i); - DcmItem* child = sequence.getItem(i); - ApplyVisitorToDataset(*child, visitor, tags, indexes, encoding); + std::vector tags = parentTags; + std::vector indexes = parentIndexes; + tags.push_back(tag); + indexes.push_back(0); + + for (unsigned long i = 0; i < sequence.card(); i++) + { + indexes.back() = static_cast(i); + DcmItem* child = sequence.getItem(i); + ApplyVisitorToDataset(*child, visitor, tags, indexes, encoding); + } } } } diff -r 47ef29168698 -r 880e4161c312 Core/DicomParsing/ITagVisitor.h --- a/Core/DicomParsing/ITagVisitor.h Tue Feb 05 18:34:27 2019 +0100 +++ b/Core/DicomParsing/ITagVisitor.h Tue Feb 05 20:44:26 2019 +0100 @@ -53,11 +53,38 @@ { } - virtual void VisitUnknown(const std::vector& parentTags, + // Visiting a DICOM element that is internal to DCMTK + virtual void VisitNotSupported(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr) = 0; + + // SQ + virtual void VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) = 0; + + // SL, SS, UL, US + virtual void VisitIntegers(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr, + const std::vector& values) = 0; + + // FL, FD + virtual void VisitDoubles(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, - ValueRepresentation vr) = 0; + ValueRepresentation vr, + const std::vector& values) = 0; + // AT + virtual void VisitAttributes(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + const std::vector& values) = 0; + + // Visiting a binary buffer virtual void VisitBinary(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, @@ -65,24 +92,7 @@ const void* data, size_t size) = 0; - virtual void VisitIntegers(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& values) = 0; - - virtual void VisitDoubles(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& values) = 0; - - virtual void VisitAttributes(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr, - const std::vector& values) = 0; - + // Visiting an UTF-8 string virtual Action VisitString(std::string& newValue, const std::vector& parentTags, const std::vector& parentIndexes, diff -r 47ef29168698 -r 880e4161c312 UnitTestsSources/DicomMapTests.cpp --- a/UnitTestsSources/DicomMapTests.cpp Tue Feb 05 18:34:27 2019 +0100 +++ b/UnitTestsSources/DicomMapTests.cpp Tue Feb 05 20:44:26 2019 +0100 @@ -559,6 +559,13 @@ #include + +static const char* const KEY_ALPHABETIC = "Alphabetic"; +static const char* const KEY_INLINE_BINARY = "InlineBinary"; +static const char* const KEY_SQ = "SQ"; +static const char* const KEY_VALUE = "Value"; +static const char* const KEY_VR = "vr"; + namespace Orthanc { class DicomJsonVisitor : public ITagVisitor @@ -589,25 +596,25 @@ if (!node->isMember(t)) { Json::Value item = Json::objectValue; - item["vr"] = "SQ"; - item["Value"] = Json::arrayValue; - item["Value"].append(Json::objectValue); + item[KEY_VR] = KEY_SQ; + item[KEY_VALUE] = Json::arrayValue; + item[KEY_VALUE].append(Json::objectValue); (*node) [t] = item; - node = &(*node)[t]["Value"][0]; + node = &(*node)[t][KEY_VALUE][0]; } else if ((*node) [t].type() != Json::objectValue || - !(*node) [t].isMember("vr") || - (*node) [t]["vr"].type() != Json::stringValue || - (*node) [t]["vr"].asString() != "SQ" || - !(*node) [t].isMember("Value") || - (*node) [t]["Value"].type() != Json::arrayValue) + !(*node) [t].isMember(KEY_VR) || + (*node) [t][KEY_VR].type() != Json::stringValue || + (*node) [t][KEY_VR].asString() != KEY_SQ || + !(*node) [t].isMember(KEY_VALUE) || + (*node) [t][KEY_VALUE].type() != Json::arrayValue) { throw OrthancException(ErrorCode_InternalError); } else { - size_t currentSize = (*node) [t]["Value"].size(); + size_t currentSize = (*node) [t][KEY_VALUE].size(); if (parentIndexes[i] < currentSize) { @@ -615,14 +622,14 @@ } else if (parentIndexes[i] == currentSize) { - (*node) [t]["Value"].append(Json::objectValue); + (*node) [t][KEY_VALUE].append(Json::objectValue); } else { throw OrthancException(ErrorCode_InternalError); } - node = &(*node) [t]["Value"][Json::ArrayIndex(parentIndexes[i])]; + node = &(*node) [t][KEY_VALUE][Json::ArrayIndex(parentIndexes[i])]; } } @@ -689,13 +696,22 @@ return result_; } - virtual void VisitUnknown(const std::vector& parentTags, - const std::vector& parentIndexes, - const DicomTag& tag, - ValueRepresentation vr) ORTHANC_OVERRIDE + virtual void VisitNotSupported(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag, + ValueRepresentation vr) ORTHANC_OVERRIDE { - Json::Value& node = CreateNode(parentTags, parentIndexes, tag); - node["vr"] = EnumerationToString(vr); + } + + virtual void VisitEmptySequence(const std::vector& parentTags, + const std::vector& parentIndexes, + const DicomTag& tag) ORTHANC_OVERRIDE + { + if (tag.GetElement() != 0x0000) + { + Json::Value& node = CreateNode(parentTags, parentIndexes, tag); + node[KEY_VR] = EnumerationToString(ValueRepresentation_Sequence); + } } virtual void VisitBinary(const std::vector& parentTags, @@ -705,11 +721,19 @@ const void* data, size_t size) ORTHANC_OVERRIDE { - if (vr != ValueRepresentation_NotSupported && - !bulkUriRoot_.empty()) + if (tag.GetElement() != 0x0000 && + vr != ValueRepresentation_NotSupported /*&& + !bulkUriRoot_.empty()*/) { Json::Value& node = CreateNode(parentTags, parentIndexes, tag); - node["vr"] = EnumerationToString(vr); + node[KEY_VR] = EnumerationToString(vr); + + std::string tmp(static_cast(data), size); + + std::string base64; + Toolbox::EncodeBase64(base64, tmp); + + node[KEY_INLINE_BINARY] = base64; } } @@ -719,18 +743,22 @@ ValueRepresentation vr, const std::vector& values) ORTHANC_OVERRIDE { - if (vr != ValueRepresentation_NotSupported) + if (tag.GetElement() != 0x0000 && + vr != ValueRepresentation_NotSupported) { Json::Value& node = CreateNode(parentTags, parentIndexes, tag); - node["vr"] = EnumerationToString(vr); + node[KEY_VR] = EnumerationToString(vr); - Json::Value content = Json::arrayValue; - for (size_t i = 0; i < values.size(); i++) + if (!values.empty()) { - content.append(FormatInteger(values[i])); + Json::Value content = Json::arrayValue; + for (size_t i = 0; i < values.size(); i++) + { + content.append(FormatInteger(values[i])); + } + + node[KEY_VALUE] = content; } - - node["Value"] = content; } } @@ -740,32 +768,39 @@ ValueRepresentation vr, const std::vector& values) ORTHANC_OVERRIDE { - if (vr != ValueRepresentation_NotSupported) + if (tag.GetElement() != 0x0000 && + vr != ValueRepresentation_NotSupported) { Json::Value& node = CreateNode(parentTags, parentIndexes, tag); - node["vr"] = EnumerationToString(vr); + node[KEY_VR] = EnumerationToString(vr); - Json::Value content = Json::arrayValue; - for (size_t i = 0; i < values.size(); i++) + if (!values.empty()) { - content.append(FormatDouble(values[i])); + Json::Value content = Json::arrayValue; + for (size_t i = 0; i < values.size(); i++) + { + content.append(FormatDouble(values[i])); + } + + node[KEY_VALUE] = content; } - node["Value"] = content; } } virtual void VisitAttributes(const std::vector& parentTags, const std::vector& parentIndexes, const DicomTag& tag, - ValueRepresentation vr, const std::vector& values) ORTHANC_OVERRIDE { - if (vr != ValueRepresentation_NotSupported) + if (tag.GetElement() != 0x0000) { Json::Value& node = CreateNode(parentTags, parentIndexes, tag); - node["vr"] = EnumerationToString(vr); + node[KEY_VR] = EnumerationToString(ValueRepresentation_AttributeTag); - + if (!values.empty()) + { + // TODO + } } } @@ -776,61 +811,68 @@ ValueRepresentation vr, const std::string& value) ORTHANC_OVERRIDE { - if (vr != ValueRepresentation_NotSupported) + if (tag.GetElement() != 0x0000 && + vr != ValueRepresentation_NotSupported) { Json::Value& node = CreateNode(parentTags, parentIndexes, tag); - node["vr"] = EnumerationToString(vr); - - std::vector tokens; - Toolbox::TokenizeString(tokens, value, '\\'); + node[KEY_VR] = EnumerationToString(vr); - node["Value"] = Json::arrayValue; - for (size_t i = 0; i < tokens.size(); i++) + if (!value.empty()) { - try + std::vector tokens; + Toolbox::TokenizeString(tokens, value, '\\'); + + node[KEY_VALUE] = Json::arrayValue; + for (size_t i = 0; i < tokens.size(); i++) { - switch (vr) + try { - case ValueRepresentation_PersonName: - { - Json::Value value = Json::objectValue; - value["Alphabetic"] = tokens[i]; - node["Value"].append(value); - break; - } - - case ValueRepresentation_IntegerString: + switch (vr) { - int64_t value = boost::lexical_cast(tokens[i]); - node["Value"].append(FormatInteger(value)); - break; - } - - case ValueRepresentation_DecimalString: - { - double value = boost::lexical_cast(tokens[i]); - node["Value"].append(FormatDouble(value)); - break; - } + case ValueRepresentation_PersonName: + { + Json::Value value = Json::objectValue; + if (!tokens[i].empty()) + { + value[KEY_ALPHABETIC] = tokens[i]; + } + node[KEY_VALUE].append(value); + break; + } + + case ValueRepresentation_IntegerString: + { + int64_t value = boost::lexical_cast(tokens[i]); + node[KEY_VALUE].append(FormatInteger(value)); + break; + } - default: - { - size_t l = tokens[i].size(); + case ValueRepresentation_DecimalString: + { + double value = boost::lexical_cast(tokens[i]); + node[KEY_VALUE].append(FormatDouble(value)); + break; + } + + default: + { + size_t l = tokens[i].size(); - if (l > 0 && - tokens[i][l - 1] == '\0') - { - tokens[i] = tokens[i].substr(0, l - 1); + if (l > 0 && + tokens[i][l - 1] == '\0') + { + tokens[i] = tokens[i].substr(0, l - 1); + } + + node[KEY_VALUE].append(tokens[i]); + break; } - - node["Value"].append(tokens[i]); - break; } } - } - catch (boost::bad_lexical_cast&) - { - throw OrthancException(ErrorCode_BadFileFormat); + catch (boost::bad_lexical_cast&) + { + throw OrthancException(ErrorCode_BadFileFormat); + } } } } @@ -845,6 +887,12 @@ /* +MarekLatin2.dcm +HierarchicalAnonymization/StructuredReports/IM0 +DummyCT.dcm +Brainix/Epi/IM-0001-0018.dcm + + cat << EOF > /tmp/tutu.py import json import sys @@ -852,7 +900,7 @@ print(json.dumps(j, indent=4, sort_keys=True, ensure_ascii=False).encode('utf-8')) EOF -DCMDICTPATH=/home/jodogne/Downloads/dcmtk-3.6.2/dcmdata/data/dicom.dic /home/jodogne/Downloads/dcmtk-3.6.2/i/bin/dcm2json ~/Subversion/orthanc-tests/Database/DummyCT.dcm | tr -d '\0' | sed 's/\\u0000//g' | sed 's/\.0$//' | python /tmp/tutu.py | grep -v 'InlineBinary' > /tmp/a.json +DCMDICTPATH=/home/jodogne/Downloads/dcmtk-3.6.4/dcmdata/data/dicom.dic /home/jodogne/Downloads/dcmtk-3.6.4/i/bin/dcm2json ~/Subversion/orthanc-tests/Database/HierarchicalAnonymization/StructuredReports/IM0 | tr -d '\0' | sed 's/\\u0000//g' | sed 's/\.0$//' | python /tmp/tutu.py > /tmp/a.json make -j4 && ./UnitTests --gtest_filter=DicomWeb* && python /tmp/tutu.py < /tmp/tutu.json > /tmp/b.json && diff -i /tmp/a.json /tmp/b.json @@ -861,7 +909,7 @@ TEST(DicomWebJson, Basic) { std::string content; - Orthanc::SystemToolbox::ReadFile(content, "/home/jodogne/Subversion/orthanc-tests/Database/DummyCT.dcm"); + Orthanc::SystemToolbox::ReadFile(content, "/home/jodogne/Subversion/orthanc-tests/Database/HierarchicalAnonymization/StructuredReports/IM0"); Orthanc::ParsedDicomFile dicom(content);