changeset 3005:8265a6b56100

DicomMap::FromDicomAsJson
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 12 Dec 2018 15:42:33 +0100
parents 80d1a3452807
children 0e1755e5efd0
files Core/DicomFormat/DicomMap.cpp Core/DicomFormat/DicomMap.h OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/Search/LookupResource.cpp OrthancServer/Search/LookupResource.h OrthancServer/ServerContext.cpp UnitTestsSources/DicomMapTests.cpp
diffstat 7 files changed, 139 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomMap.cpp	Wed Dec 12 09:08:34 2018 +0100
+++ b/Core/DicomFormat/DicomMap.cpp	Wed Dec 12 15:42:33 2018 +0100
@@ -982,6 +982,45 @@
   }
 
   
+  void DicomMap::FromDicomAsJson(const Json::Value& dicomAsJson)
+  {
+    Clear();
+    
+    Json::Value::Members tags = dicomAsJson.getMemberNames();
+    for (Json::Value::Members::const_iterator
+           it = tags.begin(); it != tags.end(); ++it)
+    {
+      DicomTag tag(0, 0);
+      if (!DicomTag::ParseHexadecimal(tag, it->c_str()))
+      {
+        throw OrthancException(ErrorCode_CorruptedFile);
+      }
+
+      const Json::Value& value = dicomAsJson[*it];
+
+      if (value.type() != Json::objectValue ||
+          !value.isMember("Type") ||
+          !value.isMember("Value") ||
+          value["Type"].type() != Json::stringValue)
+      {
+        throw OrthancException(ErrorCode_CorruptedFile);
+      }
+
+      if (value["Type"] == "String")
+      {
+        if (value["Value"].type() != Json::stringValue)
+        {
+          throw OrthancException(ErrorCode_CorruptedFile);
+        }
+        else
+        {
+          SetValue(tag, value["Value"].asString(), false /* not binary */);
+        }
+      }
+    }
+  }
+
+
   void DicomMap::Serialize(Json::Value& target) const
   {
     target = Json::objectValue;
--- a/Core/DicomFormat/DicomMap.h	Wed Dec 12 09:08:34 2018 +0100
+++ b/Core/DicomFormat/DicomMap.h	Wed Dec 12 15:42:33 2018 +0100
@@ -217,6 +217,8 @@
     bool ParseDouble(double& result,
                      const DicomTag& tag) const;
 
+    void FromDicomAsJson(const Json::Value& dicomAsJson);
+    
     void Serialize(Json::Value& target) const;
 
     void Unserialize(const Json::Value& source);
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Wed Dec 12 09:08:34 2018 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Wed Dec 12 15:42:33 2018 +0100
@@ -690,52 +690,8 @@
 
     size_t limit = (level == ResourceType_Instance) ? maxInstances_ : maxResults_;
 
-
-#if 1
     LookupVisitor visitor(answers, context_, level, *filteredInput, sequencesToReturn);
     context_.Apply(visitor, lookup, 0 /* "since" is not relevant to C-FIND */, limit);
-
-#else
-    // Backup - Implementation of Orthanc <= 1.5.0
-    // TODO - Remove this code
-
-    // TODO - Use ServerContext::Apply() at this point, in order to
-    // share the code with the "/tools/find" REST URI
-    std::vector<std::string> resources, instances;
-    context_.GetIndex().FindCandidates(resources, instances, lookup);
-
-    LOG(INFO) << "Number of candidate resources after fast DB filtering: " << resources.size();
-
-    assert(resources.size() == instances.size());
-    bool complete = true;
-
-    for (size_t i = 0; i < instances.size(); i++)
-    {
-      // TODO - Don't read the full JSON from the disk if only "main
-      // DICOM tags" are to be returned
-      Json::Value dicom;
-      context_.ReadDicomAsJson(dicom, instances[i]);
-      
-      if (lookup.IsMatch(dicom))
-      {
-        if (limit != 0 &&
-            answers.GetSize() >= limit)
-        {
-          complete = false;
-          break;
-        }
-        else
-        {
-          std::auto_ptr<DicomMap> counters(ComputeCounters(context_, instances[i], level, *filteredInput));
-          AddAnswer(answers, dicom, query, sequencesToReturn, counters.get());
-        }
-      }
-    }
-
-    LOG(INFO) << "Number of matching resources: " << answers.GetSize();
-
-    answers.SetComplete(complete);
-#endif
   }
 
 
--- a/OrthancServer/Search/LookupResource.cpp	Wed Dec 12 09:08:34 2018 +0100
+++ b/OrthancServer/Search/LookupResource.cpp	Wed Dec 12 15:42:33 2018 +0100
@@ -113,6 +113,7 @@
     }
     else
     {
+      // This is not a main DICOM tag
       return false;
     }
   }
--- a/OrthancServer/Search/LookupResource.h	Wed Dec 12 09:08:34 2018 +0100
+++ b/OrthancServer/Search/LookupResource.h	Wed Dec 12 15:42:33 2018 +0100
@@ -70,7 +70,7 @@
 
     ResourceType                    level_;
     Levels                          levels_;
-    Constraints                     unoptimizedConstraints_; 
+    Constraints                     unoptimizedConstraints_;   // Constraints on non-main DICOM tags
     std::auto_ptr<ListConstraint>   modalitiesInStudy_;
 
     bool AddInternal(ResourceType level,
--- a/OrthancServer/ServerContext.cpp	Wed Dec 12 09:08:34 2018 +0100
+++ b/OrthancServer/ServerContext.cpp	Wed Dec 12 15:42:33 2018 +0100
@@ -781,7 +781,7 @@
     std::vector<std::string> resources, instances;
     GetIndex().FindCandidates(resources, instances, lookup);
 
-    LOG(INFO) << "Number of candidate resources after fast DB filtering: " << resources.size();
+    LOG(INFO) << "Number of candidate resources after fast DB filtering on main DICOM tags: " << resources.size();
 
     assert(resources.size() == instances.size());
 
--- a/UnitTestsSources/DicomMapTests.cpp	Wed Dec 12 09:08:34 2018 +0100
+++ b/UnitTestsSources/DicomMapTests.cpp	Wed Dec 12 15:42:33 2018 +0100
@@ -37,8 +37,12 @@
 #include "../Core/OrthancException.h"
 #include "../Core/DicomFormat/DicomMap.h"
 #include "../Core/DicomParsing/FromDcmtkBridge.h"
+#include "../Core/DicomParsing/ParsedDicomFile.h"
+
+#include "../OrthancServer/DicomInstanceToStore.h"
 
 #include <memory>
+#include <dcmtk/dcmdata/dcdeftag.h>
 
 using namespace Orthanc;
 
@@ -409,3 +413,94 @@
     ASSERT_THROW(v->GetContent(), OrthancException);
   }
 }
+
+
+
+TEST(DicomMap, DicomAsJson)
+{
+  // This is a Latin-1 test string: "crane" with a circumflex accent
+  const unsigned char raw[] = { 0x63, 0x72, 0xe2, 0x6e, 0x65 };
+  std::string latin1((char*) &raw[0], sizeof(raw) / sizeof(char));
+
+  std::string utf8 = Toolbox::ConvertToUtf8(latin1, Encoding_Latin1);
+
+  ParsedDicomFile dicom(false);
+  dicom.SetEncoding(Encoding_Latin1);
+  dicom.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Hello");
+  dicom.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, utf8);
+  dicom.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, std::string(ORTHANC_MAXIMUM_TAG_LENGTH, 'a'));
+  dicom.ReplacePlainString(DICOM_TAG_MANUFACTURER, std::string(ORTHANC_MAXIMUM_TAG_LENGTH + 1, 'a'));
+  dicom.ReplacePlainString(DICOM_TAG_PIXEL_DATA, "binary");
+  dicom.ReplacePlainString(DICOM_TAG_ROWS, "512");
+
+  DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
+  dataset.insertEmptyElement(DCM_StudyID, OFFalse);
+
+  {
+    std::auto_ptr<DcmSequenceOfItems> sequence(new DcmSequenceOfItems(DCM_ReferencedSeriesSequence));
+
+    {
+      std::auto_ptr<DcmItem> item(new DcmItem);
+      item->putAndInsertString(DCM_ReferencedSOPInstanceUID, "nope", OFFalse);
+      ASSERT_TRUE(sequence->insert(item.release(), false, false).good());
+    }
+
+    ASSERT_TRUE(dataset.insert(sequence.release(), false, false).good());
+  }
+  
+                          
+  // Check re-encoding
+  DcmElement* element = NULL;
+  ASSERT_TRUE(dataset.findAndGetElement(DCM_StudyDescription, element).good() &&
+              element != NULL);
+
+  char* c = NULL;
+  ASSERT_TRUE(element != NULL &&
+              element->isLeaf() &&
+              element->isaString() &&
+              element->getString(c).good());
+  ASSERT_EQ(0, memcmp(c, raw, latin1.length()));
+
+  ASSERT_TRUE(dataset.findAndGetElement(DCM_Rows, element).good() &&
+              element != NULL &&
+              element->getTag().getEVR() == EVR_US);
+
+  DicomInstanceToStore toStore;
+  toStore.SetParsedDicomFile(dicom);
+
+  DicomMap m;
+  m.FromDicomAsJson(toStore.GetJson());
+
+  ASSERT_EQ("ISO_IR 100", m.GetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET).GetContent());
+  
+  ASSERT_FALSE(m.GetValue(DICOM_TAG_PATIENT_NAME).IsBinary());
+  ASSERT_EQ("Hello", m.GetValue(DICOM_TAG_PATIENT_NAME).GetContent());
+  
+  ASSERT_FALSE(m.GetValue(DICOM_TAG_STUDY_DESCRIPTION).IsBinary());
+  ASSERT_EQ(utf8, m.GetValue(DICOM_TAG_STUDY_DESCRIPTION).GetContent());
+
+  ASSERT_FALSE(m.HasTag(DICOM_TAG_MANUFACTURER));                // Too long
+  ASSERT_FALSE(m.HasTag(DICOM_TAG_PIXEL_DATA));                  // Pixel data
+  ASSERT_FALSE(m.HasTag(DICOM_TAG_REFERENCED_SERIES_SEQUENCE));  // Sequence
+  ASSERT_EQ(DICOM_TAG_REFERENCED_SERIES_SEQUENCE.GetGroup(), DCM_ReferencedSeriesSequence.getGroup());
+  ASSERT_EQ(DICOM_TAG_REFERENCED_SERIES_SEQUENCE.GetElement(), DCM_ReferencedSeriesSequence.getElement());
+
+  ASSERT_TRUE(m.HasTag(DICOM_TAG_SERIES_DESCRIPTION));  // Maximum length
+  ASSERT_FALSE(m.GetValue(DICOM_TAG_SERIES_DESCRIPTION).IsBinary());
+  ASSERT_EQ(ORTHANC_MAXIMUM_TAG_LENGTH, m.GetValue(DICOM_TAG_SERIES_DESCRIPTION).GetContent().length());
+
+  ASSERT_FALSE(m.GetValue(DICOM_TAG_ROWS).IsBinary());
+  ASSERT_EQ("512", m.GetValue(DICOM_TAG_ROWS).GetContent());
+
+  ASSERT_FALSE(m.GetValue(DICOM_TAG_STUDY_ID).IsNull());
+  ASSERT_FALSE(m.GetValue(DICOM_TAG_STUDY_ID).IsBinary());
+  ASSERT_EQ("", m.GetValue(DICOM_TAG_STUDY_ID).GetContent());
+
+  DicomArray a(m);
+  ASSERT_EQ(6u, a.GetSize());
+
+  
+  //dicom.SaveToFile("/tmp/test.dcm"); 
+  //std::cout << toStore.GetJson() << std::endl;
+  //a.Print(stdout);
+}