# HG changeset patch # User Sebastien Jodogne # Date 1612449060 -3600 # Node ID 39192eb9b43d197c3aec58e20e705700df049086 # Parent b57ca702a4304cd555e3e27c6248d20234dc3469 New metadata automatically computed at the instance level: "PixelDataOffset" diff -r b57ca702a430 -r 39192eb9b43d NEWS --- a/NEWS Thu Feb 04 11:42:25 2021 +0100 +++ b/NEWS Thu Feb 04 15:31:00 2021 +0100 @@ -1,6 +1,7 @@ Pending changes in the mainline =============================== +* New metadata automatically computed at the instance level: "PixelDataOffset" * Fix build on big-endian architectures diff -r b57ca702a430 -r 39192eb9b43d OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp --- a/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp Thu Feb 04 15:31:00 2021 +0100 @@ -27,8 +27,12 @@ #include #include +#include +#include +#include + namespace Orthanc { static bool IsNormalizationNeeded(const std::string& source, @@ -619,6 +623,66 @@ { return pixelDataOffset_; } + + static bool LookupPixelDataOffset(uint64_t& offset, + std::istream& stream) + { + PixelDataVisitor visitor; + bool isLittleEndian; + + { + DicomStreamReader reader(stream); + + try + { + reader.Consume(visitor); + isLittleEndian = reader.IsLittleEndian(); + } + catch (OrthancException& e) + { + // Invalid DICOM file + return false; + } + } + + if (visitor.HasPixelData()) + { + // Sanity check if we face an unsupported DICOM file: Make + // sure that we can read DICOM_TAG_PIXEL_DATA at the reported + // position in the stream + stream.seekg(visitor.GetPixelDataOffset(), stream.beg); + + std::string s; + s.resize(4); + stream.read(&s[0], s.size()); + + if (!isLittleEndian) + { + // Byte swapping if reading a file whose transfer syntax is + // 1.2.840.10008.1.2.2 (big endian explicit) + std::swap(s[0], s[1]); + std::swap(s[2], s[3]); + } + + if (stream.gcount() == static_cast(s.size()) && + s[0] == char(0xe0) && + s[1] == char(0x7f) && + s[2] == char(0x10) && + s[3] == char(0x00)) + { + offset = visitor.GetPixelDataOffset(); + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } }; @@ -626,29 +690,17 @@ const std::string& dicom) { std::stringstream stream(dicom); - - DicomStreamReader reader(stream); - - PixelDataVisitor visitor; + return PixelDataVisitor::LookupPixelDataOffset(offset, stream); + } + - try - { - reader.Consume(visitor); - } - catch (OrthancException& e) - { - // Invalid DICOM file - return false; - } - - if (visitor.HasPixelData()) - { - offset = visitor.GetPixelDataOffset(); - return true; - } - else - { - return false; - } + bool DicomStreamReader::LookupPixelDataOffset(uint64_t& offset, + 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); } } + diff -r b57ca702a430 -r 39192eb9b43d OrthancFramework/Sources/DicomFormat/DicomStreamReader.h --- a/OrthancFramework/Sources/DicomFormat/DicomStreamReader.h Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancFramework/Sources/DicomFormat/DicomStreamReader.h Thu Feb 04 15:31:00 2021 +0100 @@ -129,5 +129,9 @@ static bool LookupPixelDataOffset(uint64_t& offset, const std::string& dicom); + + static bool LookupPixelDataOffset(uint64_t& offset, + const void* buffer, + size_t size); }; } diff -r b57ca702a430 -r 39192eb9b43d OrthancFramework/Sources/DicomFormat/StreamBlockReader.cpp --- a/OrthancFramework/Sources/DicomFormat/StreamBlockReader.cpp Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancFramework/Sources/DicomFormat/StreamBlockReader.cpp Thu Feb 04 15:31:00 2021 +0100 @@ -66,8 +66,16 @@ { while (blockPos_ < block_.size()) { + /** + * WARNING: Do NOT use "stream_.readsome()", as it does not + * work properly on non-buffered stream (which is the case in + * "DicomStreamReader::LookupPixelDataOffset()" for buffers) + **/ + size_t remainingBytes = block_.size() - blockPos_; - std::streamsize r = stream_.readsome(&block_[blockPos_], remainingBytes); + stream_.read(&block_[blockPos_], remainingBytes); + + std::streamsize r = stream_.gcount(); if (r == 0) { return false; diff -r b57ca702a430 -r 39192eb9b43d OrthancFramework/UnitTestsSources/DicomMapTests.cpp --- a/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Thu Feb 04 15:31:00 2021 +0100 @@ -876,17 +876,17 @@ 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", 0x037c)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.2.dcm", 0x03e8)); // Big Endian - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.50.dcm", 0x04ac)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.51.dcm", 0x072c)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.57.dcm", 0x0620)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.70.dcm", 0x065a)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.80.dcm", 0x0b46)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.81.dcm", 0x073e)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.90.dcm", 0x0b66)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.4.91.dcm", 0x19b8)); - sources.push_back(std::make_pair(PATH + "1.2.840.10008.1.2.5.dcm", 0x0b0a)); + 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)); { std::string dicom; @@ -912,6 +912,12 @@ ASSERT_EQ(it->second, offset); } + { + uint64_t offset; + ASSERT_TRUE(DicomStreamReader::LookupPixelDataOffset(offset, dicom.c_str(), dicom.size())); + ASSERT_EQ(it->second, offset); + } + ParsedDicomFile a(dicom); Json::Value aa; a.DatasetToJson(aa, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0); diff -r b57ca702a430 -r 39192eb9b43d OrthancServer/Sources/ServerEnumerations.cpp --- a/OrthancServer/Sources/ServerEnumerations.cpp Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancServer/Sources/ServerEnumerations.cpp Thu Feb 04 15:31:00 2021 +0100 @@ -70,6 +70,7 @@ dictMetadataType_.Add(MetadataType_Instance_RemoteIp, "RemoteIP"); dictMetadataType_.Add(MetadataType_Instance_CalledAet, "CalledAET"); dictMetadataType_.Add(MetadataType_Instance_HttpUsername, "HttpUsername"); + dictMetadataType_.Add(MetadataType_Instance_PixelDataOffset, "PixelDataOffset"); dictContentType_.Add(FileContentType_Dicom, "dicom"); dictContentType_.Add(FileContentType_DicomAsJson, "dicom-as-json"); diff -r b57ca702a430 -r 39192eb9b43d OrthancServer/Sources/ServerEnumerations.h --- a/OrthancServer/Sources/ServerEnumerations.h Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancServer/Sources/ServerEnumerations.h Thu Feb 04 15:31:00 2021 +0100 @@ -149,12 +149,13 @@ MetadataType_ModifiedFrom = 5, MetadataType_AnonymizedFrom = 6, MetadataType_LastUpdate = 7, - MetadataType_Instance_Origin = 8, // New in Orthanc 0.9.5 - MetadataType_Instance_TransferSyntax = 9, // New in Orthanc 1.2.0 - MetadataType_Instance_SopClassUid = 10, // New in Orthanc 1.2.0 - MetadataType_Instance_RemoteIp = 11, // New in Orthanc 1.4.0 - MetadataType_Instance_CalledAet = 12, // New in Orthanc 1.4.0 - MetadataType_Instance_HttpUsername = 13, // New in Orthanc 1.4.0 + MetadataType_Instance_Origin = 8, // New in Orthanc 0.9.5 + MetadataType_Instance_TransferSyntax = 9, // New in Orthanc 1.2.0 + MetadataType_Instance_SopClassUid = 10, // New in Orthanc 1.2.0 + MetadataType_Instance_RemoteIp = 11, // New in Orthanc 1.4.0 + MetadataType_Instance_CalledAet = 12, // New in Orthanc 1.4.0 + MetadataType_Instance_HttpUsername = 13, // New in Orthanc 1.4.0 + MetadataType_Instance_PixelDataOffset = 14, // New in Orthanc 1.9.0 // Make sure that the value "65535" can be stored into this enumeration MetadataType_StartUser = 1024, diff -r b57ca702a430 -r 39192eb9b43d OrthancServer/Sources/ServerIndex.cpp --- a/OrthancServer/Sources/ServerIndex.cpp Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancServer/Sources/ServerIndex.cpp Thu Feb 04 15:31:00 2021 +0100 @@ -39,6 +39,7 @@ #endif #include "../../OrthancFramework/Sources/DicomFormat/DicomArray.h" +#include "../../OrthancFramework/Sources/DicomFormat/DicomStreamReader.h" #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h" #include "../../OrthancFramework/Sources/Logging.h" @@ -760,6 +761,23 @@ const Attachments& attachments, bool overwrite) { + std::string pixelDataOffset; + + { + // Determining the pixel data offset is costly, don't do it + // within the mutex (new in Orthanc 1.9.1) + uint64_t offset; + if (DicomStreamReader::LookupPixelDataOffset(offset, instanceToStore.GetBufferData(), + instanceToStore.GetBufferSize())) + { + pixelDataOffset = boost::lexical_cast(offset); + } + else + { + pixelDataOffset.clear(); + } + } + boost::mutex::scoped_lock lock(mutex_); const DicomMap& dicomSummary = instanceToStore.GetSummary(); @@ -971,6 +989,10 @@ } } + // New in Orthanc 1.9.1 + SetInstanceMetadata(content, instanceMetadata, instanceId, + MetadataType_Instance_PixelDataOffset, pixelDataOffset); + const DicomValue* value; if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL && diff -r b57ca702a430 -r 39192eb9b43d OrthancServer/UnitTestsSources/ServerIndexTests.cpp --- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Thu Feb 04 11:42:25 2021 +0100 +++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Thu Feb 04 15:31:00 2021 +0100 @@ -729,11 +729,12 @@ toStore.SetSummary(instance); ASSERT_EQ(StoreStatus_Success, index.Store(instanceMetadata, toStore, attachments, false /* don't overwrite */)); - ASSERT_EQ(5u, instanceMetadata.size()); + ASSERT_EQ(6u, 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()); // The default transfer syntax depends on the OS endianness std::string s = instanceMetadata[MetadataType_Instance_TransferSyntax];