# HG changeset patch # User Sebastien Jodogne # Date 1687416518 -7200 # Node ID 03501a258d9e4e6334e76a08f55aa17aa1cc1825 # Parent 592507a8e227aa12def43c5b228c0408a884378a added instance metadata "PixelDataVR" diff -r 592507a8e227 -r 03501a258d9e OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp --- 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(buffer), size); boost::iostreams::stream stream(source); - return PixelDataVisitor::LookupPixelDataOffset(offset, stream); + return PixelDataVisitor::LookupPixelDataOffset(offset, vr, stream); } } diff -r 592507a8e227 -r 03501a258d9e OrthancFramework/Sources/DicomFormat/DicomStreamReader.h --- 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); }; diff -r 592507a8e227 -r 03501a258d9e OrthancFramework/UnitTestsSources/DicomMapTests.cpp --- 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 +#include using namespace Orthanc; @@ -1288,50 +1289,61 @@ { static const std::string PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/"; - typedef std::list< std::pair > Sources; + typedef boost::tuple Source; + typedef std::list 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()); diff -r 592507a8e227 -r 03501a258d9e OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- 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(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 { diff -r 592507a8e227 -r 03501a258d9e OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- 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, diff -r 592507a8e227 -r 03501a258d9e OrthancServer/Sources/ServerContext.cpp --- 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(pixelDataOffset)); + index_.OverwriteMetadata(instancePublicId, MetadataType_Instance_PixelDataVR, + EnumerationToString(pixelDataVR)); if (!area_.HasReadRange() || compressionEnabled_) diff -r 592507a8e227 -r 03501a258d9e OrthancServer/Sources/ServerEnumerations.cpp --- 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"); diff -r 592507a8e227 -r 03501a258d9e OrthancServer/Sources/ServerEnumerations.h --- 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, diff -r 592507a8e227 -r 03501a258d9e OrthancServer/Sources/ServerIndex.cpp --- 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); } diff -r 592507a8e227 -r 03501a258d9e OrthancServer/Sources/ServerIndex.h --- 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, diff -r 592507a8e227 -r 03501a258d9e OrthancServer/UnitTestsSources/ServerIndexTests.cpp --- 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];