changeset 5316:03501a258d9e

added instance metadata "PixelDataVR"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 22 Jun 2023 08:48:38 +0200
parents 592507a8e227
children 9875e1f24394
files OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp OrthancFramework/Sources/DicomFormat/DicomStreamReader.h OrthancFramework/UnitTestsSources/DicomMapTests.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerEnumerations.cpp OrthancServer/Sources/ServerEnumerations.h OrthancServer/Sources/ServerIndex.cpp OrthancServer/Sources/ServerIndex.h OrthancServer/UnitTestsSources/ServerIndexTests.cpp
diffstat 11 files changed, 86 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp	Thu Jun 22 08:48:38 2023 +0200
@@ -579,13 +579,15 @@
   class DicomStreamReader::PixelDataVisitor : public DicomStreamReader::IVisitor
   {
   private:
-    bool      hasPixelData_;
-    uint64_t  pixelDataOffset_;
+    bool                 hasPixelData_;
+    uint64_t             pixelDataOffset_;
+    ValueRepresentation  pixelDataVR_;
     
   public:
     PixelDataVisitor() :
       hasPixelData_(false),
-      pixelDataOffset_(0)
+      pixelDataOffset_(0),
+      pixelDataVR_(ValueRepresentation_Unknown)
     {
     }
     
@@ -609,6 +611,7 @@
       {
         hasPixelData_ = true;
         pixelDataOffset_ = fileOffset;
+        pixelDataVR_ = vr;
       }
 
       // Stop processing once pixel data has been passed
@@ -625,7 +628,13 @@
       return pixelDataOffset_;
     }
 
+    ValueRepresentation GetPixelDataVR() const
+    {
+      return pixelDataVR_;
+    }
+
     static bool LookupPixelDataOffset(uint64_t& offset,
+                                      ValueRepresentation& vr,
                                       std::istream& stream)
     {
       PixelDataVisitor visitor;
@@ -672,6 +681,7 @@
             s[3] == char(0x00))
         {
           offset = visitor.GetPixelDataOffset();
+          vr = visitor.GetPixelDataVR();
           return true;
         }
         else
@@ -688,20 +698,22 @@
 
   
   bool DicomStreamReader::LookupPixelDataOffset(uint64_t& offset,
+                                                ValueRepresentation& vr,
                                                 const std::string& dicom)
   {
     std::stringstream stream(dicom);
-    return PixelDataVisitor::LookupPixelDataOffset(offset, stream);
+    return PixelDataVisitor::LookupPixelDataOffset(offset, vr, stream);
   }
   
 
   bool DicomStreamReader::LookupPixelDataOffset(uint64_t& offset,
+                                                ValueRepresentation& vr,
                                                 const void* buffer,
                                                 size_t size)
   {
     boost::iostreams::array_source source(reinterpret_cast<const char*>(buffer), size);
     boost::iostreams::stream<boost::iostreams::array_source> stream(source);
-    return PixelDataVisitor::LookupPixelDataOffset(offset, stream);
+    return PixelDataVisitor::LookupPixelDataOffset(offset, vr, stream);
   }
 }
 
--- a/OrthancFramework/Sources/DicomFormat/DicomStreamReader.h	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancFramework/Sources/DicomFormat/DicomStreamReader.h	Thu Jun 22 08:48:38 2023 +0200
@@ -128,10 +128,12 @@
 
     uint64_t GetProcessedBytes() const;
 
-    static bool LookupPixelDataOffset(uint64_t& offset,
+    static bool LookupPixelDataOffset(uint64_t& offset /* out */,
+                                      ValueRepresentation& vr /* out */,
                                       const std::string& dicom);
 
-    static bool LookupPixelDataOffset(uint64_t& offset,
+    static bool LookupPixelDataOffset(uint64_t& offset /* out */,
+                                      ValueRepresentation& vr /* out */,
                                       const void* buffer,
                                       size_t size);
   };
--- a/OrthancFramework/UnitTestsSources/DicomMapTests.cpp	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancFramework/UnitTestsSources/DicomMapTests.cpp	Thu Jun 22 08:48:38 2023 +0200
@@ -42,6 +42,7 @@
 #include "../Sources/DicomParsing/DicomWebJsonVisitor.h"
 
 #include <boost/lexical_cast.hpp>
+#include <boost/tuple/tuple.hpp>
 
 using namespace Orthanc;
 
@@ -1288,50 +1289,61 @@
 {
   static const std::string PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/";
 
-  typedef std::list< std::pair<std::string, uint64_t> >  Sources;
+  typedef boost::tuple<std::string, uint64_t, ValueRepresentation> Source;
+  typedef std::list<Source>  Sources;
+
+  // $ ~/Subversion/orthanc-tests/Tests/GetPixelDataVR.py ~/Subversion/orthanc-tests/Database/ColorTestMalaterre.dcm ~/Subversion/orthanc-tests/Database/ColorTestImageJ.dcm ~/Subversion/orthanc-tests/Database/Knee/T1/IM-0001-0001.dcm ~/Subversion/orthanc-tests/Database/TransferSyntaxes/*.dcm
 
   Sources sources;
-  sources.push_back(std::make_pair(PATH + "../ColorTestMalaterre.dcm", 0x03a0u));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.1.dcm", 0x037cu));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.2.dcm", 0x03e8u));  // Big Endian
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.50.dcm", 0x04acu));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.51.dcm", 0x072cu));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.57.dcm", 0x0620u));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.70.dcm", 0x065au));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.80.dcm", 0x0b46u));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.81.dcm", 0x073eu));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.90.dcm", 0x0b66u));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.91.dcm", 0x19b8u));
-  sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.5.dcm", 0x0b0au));
+  sources.push_back(Source(PATH + "../ColorTestMalaterre.dcm",   0x03a0u, ValueRepresentation_Unknown));  // This file has strange VR
+  sources.push_back(Source(PATH + "../ColorTestImageJ.dcm",      0x00924, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "../Knee/T1/IM-0001-0001.dcm", 0x00c78, ValueRepresentation_OtherWord));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.1.dcm",     0x037cu, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.2.dcm",     0x03e8u, ValueRepresentation_OtherByte));  // Big Endian
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.50.dcm",  0x04acu, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.51.dcm",  0x072cu, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.57.dcm",  0x0620u, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.70.dcm",  0x065au, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.80.dcm",  0x0b46u, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.81.dcm",  0x073eu, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.90.dcm",  0x0b66u, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.4.91.dcm",  0x19b8u, ValueRepresentation_OtherByte));
+  sources.push_back(Source(PATH + "1.2.840.10008.1.2.5.dcm",     0x0b0au, ValueRepresentation_OtherByte));
 
   {
     std::string dicom;
 
     uint64_t offset;
+    ValueRepresentation vr;
+
     // Not a DICOM image
     SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.50.png", false);
-    ASSERT_FALSE(DicomStreamReader::LookupPixelDataOffset(offset, dicom));
+    ASSERT_FALSE(DicomStreamReader::LookupPixelDataOffset(offset, vr, dicom));
 
     // Image without valid DICOM preamble
     SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.dcm", false);
-    ASSERT_FALSE(DicomStreamReader::LookupPixelDataOffset(offset, dicom));
+    ASSERT_FALSE(DicomStreamReader::LookupPixelDataOffset(offset, vr, dicom));
   }
   
   for (Sources::const_iterator it = sources.begin(); it != sources.end(); ++it)
   {
     std::string dicom;
-    SystemToolbox::ReadFile(dicom, it->first, false);
+    SystemToolbox::ReadFile(dicom, it->get<0>(), false);
 
     {
       uint64_t offset;
-      ASSERT_TRUE(DicomStreamReader::LookupPixelDataOffset(offset, dicom));
-      ASSERT_EQ(it->second, offset);
+      ValueRepresentation vr;
+      ASSERT_TRUE(DicomStreamReader::LookupPixelDataOffset(offset, vr, dicom));
+      ASSERT_EQ(it->get<1>(), offset);
+      ASSERT_EQ(it->get<2>(), vr);
     }
     
     {
       uint64_t offset;
-      ASSERT_TRUE(DicomStreamReader::LookupPixelDataOffset(offset, dicom.c_str(), dicom.size()));
-      ASSERT_EQ(it->second, offset);
+      ValueRepresentation vr;
+      ASSERT_TRUE(DicomStreamReader::LookupPixelDataOffset(offset, vr, dicom.c_str(), dicom.size()));
+      ASSERT_EQ(it->get<1>(), offset);
+      ASSERT_EQ(it->get<2>(), vr);
     }
     
     ParsedDicomFile a(dicom);
@@ -1355,7 +1367,7 @@
 
     r.Consume(visitor);
 
-    ASSERT_EQ(it->second, visitor.GetPixelDataOffset());
+    ASSERT_EQ(it->get<1>(), visitor.GetPixelDataOffset());
 
     // Truncate the original DICOM up to pixel data
     dicom.resize(visitor.GetPixelDataOffset());
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu Jun 22 08:48:38 2023 +0200
@@ -2956,6 +2956,7 @@
                                                  DicomTransferSyntax transferSyntax,
                                                  bool hasPixelDataOffset,
                                                  uint64_t pixelDataOffset,
+                                                 ValueRepresentation pixelDataVR,
                                                  MaxStorageMode maximumStorageMode,
                                                  uint64_t maximumStorageSize,
                                                  unsigned int maximumPatients,
@@ -2975,6 +2976,7 @@
       DicomTransferSyntax                  transferSyntax_;
       bool                                 hasPixelDataOffset_;
       uint64_t                             pixelDataOffset_;
+      ValueRepresentation                  pixelDataVR_;
       MaxStorageMode                       maximumStorageMode_;
       uint64_t                             maximumStorageSize_;
       unsigned int                         maximumPatientCount_;
@@ -3078,6 +3080,7 @@
                  DicomTransferSyntax transferSyntax,
                  bool hasPixelDataOffset,
                  uint64_t pixelDataOffset,
+                 ValueRepresentation pixelDataVR,
                  MaxStorageMode maximumStorageMode,
                  uint64_t maximumStorageSize,
                  unsigned int maximumPatientCount,
@@ -3093,6 +3096,7 @@
         transferSyntax_(transferSyntax),
         hasPixelDataOffset_(hasPixelDataOffset),
         pixelDataOffset_(pixelDataOffset),
+        pixelDataVR_(pixelDataVR),
         maximumStorageMode_(maximumStorageMode),
         maximumStorageSize_(maximumStorageSize),
         maximumPatientCount_(maximumPatientCount),
@@ -3355,6 +3359,11 @@
             SetInstanceMetadata(content, instanceMetadata_, instanceId,
                                 MetadataType_Instance_PixelDataOffset,
                                 boost::lexical_cast<std::string>(pixelDataOffset_));
+
+            // New in Orthanc 1.12.1
+            SetInstanceMetadata(content, instanceMetadata_, instanceId,
+                                MetadataType_Instance_PixelDataVR,
+                                EnumerationToString(pixelDataVR_));
           }
       
           const DicomValue* value;
@@ -3409,9 +3418,9 @@
     };
 
 
-    Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin,
-                          overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset,
-                          pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct);
+    Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin, overwrite,
+                          hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset,
+                          pixelDataVR, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct);
 
     try
     {
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Thu Jun 22 08:48:38 2023 +0200
@@ -759,6 +759,7 @@
                       DicomTransferSyntax transferSyntax,
                       bool hasPixelDataOffset,
                       uint64_t pixelDataOffset,
+                      ValueRepresentation pixelDataVR,
                       MaxStorageMode maximumStorageMode,
                       uint64_t maximumStorageSize,
                       unsigned int maximumPatients,
--- a/OrthancServer/Sources/ServerContext.cpp	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Thu Jun 22 08:48:38 2023 +0200
@@ -552,8 +552,9 @@
 
     bool hasPixelDataOffset;
     uint64_t pixelDataOffset;
+    ValueRepresentation pixelDataVR;
     hasPixelDataOffset = DicomStreamReader::LookupPixelDataOffset(
-      pixelDataOffset, dicom.GetBufferData(), dicom.GetBufferSize());
+      pixelDataOffset, pixelDataVR, dicom.GetBufferData(), dicom.GetBufferSize());
 
     DicomTransferSyntax transferSyntax;
     bool hasTransferSyntax = dicom.LookupTransferSyntax(transferSyntax);
@@ -653,7 +654,7 @@
       InstanceMetadata  instanceMetadata;
       result.SetStatus(index_.Store(
         instanceMetadata, summary, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite,
-        hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset, isReconstruct));
+        hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset, pixelDataVR, isReconstruct));
 
       // Only keep the metadata for the "instance" level
       dicom.ClearMetadata();
@@ -1094,11 +1095,14 @@
            * Orthanc have failed. Try again this precomputation now
            * for future calls.
            **/
-          if (DicomStreamReader::LookupPixelDataOffset(pixelDataOffset, dicom) &&
+          ValueRepresentation pixelDataVR;
+          if (DicomStreamReader::LookupPixelDataOffset(pixelDataOffset, pixelDataVR, dicom) &&
               pixelDataOffset < dicom.size())
           {
             index_.OverwriteMetadata(instancePublicId, MetadataType_Instance_PixelDataOffset,
                                      boost::lexical_cast<std::string>(pixelDataOffset));
+            index_.OverwriteMetadata(instancePublicId, MetadataType_Instance_PixelDataVR,
+                                     EnumerationToString(pixelDataVR));
 
             if (!area_.HasReadRange() ||
                 compressionEnabled_)
--- a/OrthancServer/Sources/ServerEnumerations.cpp	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/Sources/ServerEnumerations.cpp	Thu Jun 22 08:48:38 2023 +0200
@@ -62,6 +62,7 @@
     dictMetadataType_.Add(MetadataType_Instance_PixelDataOffset, "PixelDataOffset");
     dictMetadataType_.Add(MetadataType_MainDicomTagsSignature, "MainDicomTagsSignature");
     dictMetadataType_.Add(MetadataType_MainDicomSequences, "MainDicomSequences");
+    dictMetadataType_.Add(MetadataType_Instance_PixelDataVR, "PixelDataVR");
 
     dictContentType_.Add(FileContentType_Dicom, "dicom");
     dictContentType_.Add(FileContentType_DicomAsJson, "dicom-as-json");
--- a/OrthancServer/Sources/ServerEnumerations.h	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/Sources/ServerEnumerations.h	Thu Jun 22 08:48:38 2023 +0200
@@ -160,6 +160,7 @@
     MetadataType_Instance_PixelDataOffset = 14,  // New in Orthanc 1.9.0
     MetadataType_MainDicomTagsSignature = 15,    // New in Orthanc 1.11.0
     MetadataType_MainDicomSequences = 16,        // New in Orthanc 1.11.1
+    MetadataType_Instance_PixelDataVR = 17,      // New in Orthanc 1.12.1
     
     // Make sure that the value "65535" can be stored into this enumeration
     MetadataType_StartUser = 1024,
--- a/OrthancServer/Sources/ServerIndex.cpp	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/Sources/ServerIndex.cpp	Thu Jun 22 08:48:38 2023 +0200
@@ -538,6 +538,7 @@
                                  DicomTransferSyntax transferSyntax,
                                  bool hasPixelDataOffset,
                                  uint64_t pixelDataOffset,
+                                 ValueRepresentation pixelDataVR,
                                  bool isReconstruct)
   {
     uint64_t maximumStorageSize;
@@ -553,7 +554,8 @@
 
     return StatelessDatabaseOperations::Store(
       instanceMetadata, dicomSummary, attachments, metadata, origin, overwrite, hasTransferSyntax,
-      transferSyntax, hasPixelDataOffset, pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct);
+      transferSyntax, hasPixelDataOffset, pixelDataOffset, pixelDataVR, maximumStorageMode,
+      maximumStorageSize, maximumPatients, isReconstruct);
   }
 
   
--- a/OrthancServer/Sources/ServerIndex.h	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/Sources/ServerIndex.h	Thu Jun 22 08:48:38 2023 +0200
@@ -88,7 +88,8 @@
                       DicomTransferSyntax transferSyntax,
                       bool hasPixelDataOffset,
                       uint64_t pixelDataOffset,
-                      bool isResonstruct);
+                      ValueRepresentation pixelDataVR,
+                      bool isReconstruct);
 
     StoreStatus AddAttachment(int64_t& newRevision /*out*/,
                               const FileInfo& attachment,
--- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp	Mon Jun 19 19:20:53 2023 +0200
+++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp	Thu Jun 22 08:48:38 2023 +0200
@@ -742,18 +742,21 @@
       ASSERT_EQ(StoreStatus_Success, index.Store(
                   instanceMetadata, summary, attachments, toStore->GetMetadata(),
                   toStore->GetOrigin(), false /* don't overwrite */,
-                  hasTransferSyntax, transferSyntax, true /* pixel data offset */, 42, false));
+                  hasTransferSyntax, transferSyntax, true /* has pixel data */, 42 /* pixel data offset */,
+                  ValueRepresentation_PersonName /* pixel data VR */, false));
     }
     
-    ASSERT_EQ(7u, instanceMetadata.size());
+    ASSERT_EQ(8u, instanceMetadata.size());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_RemoteAet) != instanceMetadata.end());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_ReceptionDate) != instanceMetadata.end());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_TransferSyntax) != instanceMetadata.end());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_SopClassUid) != instanceMetadata.end());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_PixelDataOffset) != instanceMetadata.end());
     ASSERT_TRUE(instanceMetadata.find(MetadataType_MainDicomTagsSignature) != instanceMetadata.end());
+    ASSERT_TRUE(instanceMetadata.find(MetadataType_Instance_PixelDataVR) != instanceMetadata.end());
 
     ASSERT_EQ("42", instanceMetadata[MetadataType_Instance_PixelDataOffset]);
+    ASSERT_EQ("PN", instanceMetadata[MetadataType_Instance_PixelDataVR]);
 
     // The default transfer syntax depends on the OS endianness
     std::string s = instanceMetadata[MetadataType_Instance_TransferSyntax];