Mercurial > hg > orthanc
changeset 4494:39192eb9b43d
New metadata automatically computed at the instance level: "PixelDataOffset"
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 04 Feb 2021 15:31:00 +0100 |
parents | b57ca702a430 |
children | fa2311f94d9f |
files | NEWS OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp OrthancFramework/Sources/DicomFormat/DicomStreamReader.h OrthancFramework/Sources/DicomFormat/StreamBlockReader.cpp OrthancFramework/UnitTestsSources/DicomMapTests.cpp OrthancServer/Sources/ServerEnumerations.cpp OrthancServer/Sources/ServerEnumerations.h OrthancServer/Sources/ServerIndex.cpp OrthancServer/UnitTestsSources/ServerIndexTests.cpp |
diffstat | 9 files changed, 138 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- 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
--- 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 <cassert> #include <sstream> +#include <boost/iostreams/device/array.hpp> +#include <boost/iostreams/stream.hpp> +#include <iostream> + 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<std::streamsize>(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<const char*>(buffer), size); + boost::iostreams::stream<boost::iostreams::array_source> stream(source); + return PixelDataVisitor::LookupPixelDataOffset(offset, stream); } } +
--- 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); }; }
--- 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;
--- 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);
--- 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");
--- 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,
--- 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<std::string>(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 &&
--- 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];