# HG changeset patch # User Sebastien Jodogne # Date 1613037605 -3600 # Node ID 8f9090b137f14f1abe4297ac4a9537e85bb864b6 # Parent b4c58795f3a8d278c77d8ec2a6616053bf1245a8 Optimization in C-STORE SCP by avoiding an unnecessary DICOM parsing diff -r b4c58795f3a8 -r 8f9090b137f1 NEWS --- a/NEWS Thu Feb 11 09:33:48 2021 +0100 +++ b/NEWS Thu Feb 11 11:00:05 2021 +0100 @@ -2,6 +2,7 @@ =============================== * New metadata automatically computed at the instance level: "PixelDataOffset" +* Optimization in C-STORE SCP by avoiding an unnecessary DICOM parsing * Fix build on big-endian architectures * Possibility to generate a static library containing the Orthanc Framework diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp --- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -970,7 +970,10 @@ void ParsedDicomFile::SaveToMemoryBuffer(std::string& buffer) { - FromDcmtkBridge::SaveToMemoryBuffer(buffer, *GetDcmtkObject().getDataset()); + if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, *GetDcmtkObject().getDataset())) + { + throw OrthancException(ErrorCode_InternalError, "Cannot write DICOM file to memory"); + } } diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Plugins/Engine/OrthancPlugins.cpp --- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -2127,16 +2127,17 @@ class OrthancPlugins::DicomInstanceFromBuffer : public IDicomInstance { private: - std::string buffer_; - DicomInstanceToStore instance_; + std::string buffer_; + std::unique_ptr instance_; public: DicomInstanceFromBuffer(const void* buffer, size_t size) { buffer_.assign(reinterpret_cast(buffer), size); - instance_.SetBuffer(buffer_.empty() ? NULL : buffer_.c_str(), buffer_.size()); - instance_.SetOrigin(DicomInstanceOrigin::FromPlugins()); + + instance_.reset(DicomInstanceToStore::CreateFromBuffer(buffer_)); + instance_->SetOrigin(DicomInstanceOrigin::FromPlugins()); } virtual bool CanBeFreed() const ORTHANC_OVERRIDE @@ -2146,7 +2147,7 @@ virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE { - return instance_; + return *instance_; }; }; @@ -2154,15 +2155,20 @@ class OrthancPlugins::DicomInstanceFromTranscoded : public IDicomInstance { private: - std::unique_ptr parsed_; - DicomInstanceToStore instance_; + std::unique_ptr parsed_; + std::unique_ptr instance_; public: explicit DicomInstanceFromTranscoded(IDicomTranscoder::DicomImage& transcoded) : parsed_(transcoded.ReleaseAsParsedDicomFile()) { - instance_.SetParsedDicomFile(*parsed_); - instance_.SetOrigin(DicomInstanceOrigin::FromPlugins()); + if (parsed_.get() == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + instance_.reset(DicomInstanceToStore::CreateFromParsedDicomFile(*parsed_)); + instance_->SetOrigin(DicomInstanceOrigin::FromPlugins()); } virtual bool CanBeFreed() const ORTHANC_OVERRIDE @@ -2172,7 +2178,7 @@ virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE { - return instance_; + return *instance_; }; }; diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/DicomInstanceToStore.cpp --- a/OrthancServer/Sources/DicomInstanceToStore.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/DicomInstanceToStore.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -37,6 +37,8 @@ #include "OrthancConfiguration.h" #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" +#include "../../OrthancFramework/Sources/DicomParsing/Internals/DicomFrameIndex.h" +#include "../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.h" #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h" #include "../../OrthancFramework/Sources/Logging.h" #include "../../OrthancFramework/Sources/OrthancException.h" @@ -47,356 +49,249 @@ namespace Orthanc { - // Anonymous namespace to avoid clashes between compilation modules - namespace + class DicomInstanceToStore::FromBuffer : public DicomInstanceToStore { - template - class SmartContainer - { - private: - T* content_; - bool toDelete_; - bool isReadOnly_; - - void Deallocate() - { - if (content_ && toDelete_) - { - delete content_; - toDelete_ = false; - content_ = NULL; - } - } - - public: - SmartContainer() : content_(NULL), toDelete_(false), isReadOnly_(true) - { - } - - ~SmartContainer() - { - Deallocate(); - } - - void Allocate() - { - Deallocate(); - content_ = new T; - toDelete_ = true; - isReadOnly_ = false; - } - - void TakeOwnership(T* content) - { - if (content == NULL) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - Deallocate(); - content_ = content; - toDelete_ = true; - isReadOnly_ = false; - } + private: + const void* buffer_; + size_t size_; + std::unique_ptr parsed_; - void SetReference(T& content) // Read and write assign, without transfering ownership - { - Deallocate(); - content_ = &content; - toDelete_ = false; - isReadOnly_ = false; - } - - void SetConstReference(const T& content) // Read-only assign, without transfering ownership - { - Deallocate(); - content_ = &const_cast(content); - toDelete_ = false; - isReadOnly_ = true; - } - - bool HasContent() const - { - return content_ != NULL; - } - - T& GetContent() - { - if (content_ == NULL) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - if (isReadOnly_) - { - throw OrthancException(ErrorCode_ReadOnly); - } - - return *content_; - } - - const T& GetConstContent() const - { - if (content_ == NULL) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - return *content_; - } - }; - } - - - class DicomInstanceToStore::PImpl - { public: - DicomInstanceOrigin origin_; - bool hasBuffer_; - std::unique_ptr ownBuffer_; - const void* bufferData_; - size_t bufferSize_; - SmartContainer parsed_; - MetadataMap metadata_; - - PImpl() : - hasBuffer_(false), - bufferData_(NULL), - bufferSize_(0) + FromBuffer(const void* buffer, + size_t size) : + buffer_(buffer), + size_(size) { } + virtual ParsedDicomFile& GetParsedDicomFile() const ORTHANC_OVERRIDE + { + if (parsed_.get() == NULL) + { + const_cast(*this).parsed_.reset(new ParsedDicomFile(buffer_, size_)); + } + + return *parsed_; + } + + virtual const void* GetBufferData() const ORTHANC_OVERRIDE + { + return buffer_; + } + + virtual size_t GetBufferSize() const ORTHANC_OVERRIDE + { + return size_; + } + }; + + + class DicomInstanceToStore::FromParsedDicomFile : public DicomInstanceToStore + { private: - void ComputeParsedDicomFileIfMissing() + ParsedDicomFile& parsed_; + std::unique_ptr buffer_; + + void SerializeToBuffer() { - if (!parsed_.HasContent()) + if (buffer_.get() == NULL) { - if (!hasBuffer_) + buffer_.reset(new std::string); + parsed_.SaveToMemoryBuffer(*buffer_); + } + } + + public: + FromParsedDicomFile(ParsedDicomFile& parsed) : + parsed_(parsed) + { + } + + virtual ParsedDicomFile& GetParsedDicomFile() const ORTHANC_OVERRIDE + { + return parsed_; + } + + virtual const void* GetBufferData() const ORTHANC_OVERRIDE + { + const_cast(*this).SerializeToBuffer(); + + assert(buffer_.get() != NULL); + return (buffer_->empty() ? NULL : buffer_->c_str()); + } + + virtual size_t GetBufferSize() const ORTHANC_OVERRIDE + { + const_cast(*this).SerializeToBuffer(); + + assert(buffer_.get() != NULL); + return buffer_->size(); + } + }; + + + class DicomInstanceToStore::FromDcmDataset : public DicomInstanceToStore + { + private: + DcmDataset& dataset_; + std::unique_ptr buffer_; + std::unique_ptr parsed_; + + void SerializeToBuffer() + { + if (buffer_.get() == NULL) + { + buffer_.reset(new std::string); + + if (!FromDcmtkBridge::SaveToMemoryBuffer(*buffer_, dataset_)) { - throw OrthancException(ErrorCode_InternalError); - } - - if (ownBuffer_.get() != NULL) - { - parsed_.TakeOwnership(new ParsedDicomFile(*ownBuffer_)); - } - else - { - parsed_.TakeOwnership(new ParsedDicomFile(bufferData_, bufferSize_)); + throw OrthancException(ErrorCode_InternalError, "Cannot write DICOM file to memory"); } } } - void ComputeDicomBufferIfMissing() + public: + FromDcmDataset(DcmDataset& dataset) : + dataset_(dataset) { - if (!hasBuffer_) + } + + virtual ParsedDicomFile& GetParsedDicomFile() const ORTHANC_OVERRIDE + { + if (parsed_.get() == NULL) { - if (!parsed_.HasContent()) - { - throw OrthancException(ErrorCode_NotImplemented); - } + // This operation is costly, as it creates a clone of the + // dataset. This explains why the default implementations + // are overridden below to use "dataset_" as much as possible + const_cast(*this).parsed_.reset(new ParsedDicomFile(dataset_)); + } - // Serialize the parsed DICOM file - ownBuffer_.reset(new std::string); - if (!FromDcmtkBridge::SaveToMemoryBuffer(*ownBuffer_, - *parsed_.GetContent().GetDcmtkObject().getDataset())) - { - throw OrthancException(ErrorCode_InternalError, - "Unable to serialize a DICOM file to a memory buffer"); - } - - hasBuffer_ = true; - } + return *parsed_; } - - public: - void SetBuffer(const void* data, - size_t size) + virtual const void* GetBufferData() const ORTHANC_OVERRIDE { - ownBuffer_.reset(NULL); - bufferData_ = data; - bufferSize_ = size; - hasBuffer_ = true; - } - - const void* GetBufferData() - { - ComputeDicomBufferIfMissing(); + const_cast(*this).SerializeToBuffer(); - if (!hasBuffer_) - { - throw OrthancException(ErrorCode_InternalError); - } + assert(buffer_.get() != NULL); + return (buffer_->empty() ? NULL : buffer_->c_str()); + } - if (ownBuffer_.get() != NULL) - { - if (ownBuffer_->empty()) - { - return NULL; - } - else - { - return ownBuffer_->c_str(); - } - } - else - { - return bufferData_; - } + virtual size_t GetBufferSize() const ORTHANC_OVERRIDE + { + const_cast(*this).SerializeToBuffer(); + + assert(buffer_.get() != NULL); + return buffer_->size(); } - - size_t GetBufferSize() + virtual bool HasPixelData() const ORTHANC_OVERRIDE { - ComputeDicomBufferIfMissing(); - - if (!hasBuffer_) - { - throw OrthancException(ErrorCode_InternalError); - } + DcmTag key(DICOM_TAG_PIXEL_DATA.GetGroup(), + DICOM_TAG_PIXEL_DATA.GetElement()); + return dataset_.tagExists(key); + } - if (ownBuffer_.get() != NULL) - { - return ownBuffer_->size(); - } - else - { - return bufferSize_; - } + virtual void GetSummary(DicomMap& summary) const ORTHANC_OVERRIDE + { + OrthancConfiguration::DefaultExtractDicomSummary(summary, dataset_); + } + + virtual void GetDicomAsJson(Json::Value& dicomAsJson) const ORTHANC_OVERRIDE + { + OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, dataset_); } - - bool LookupTransferSyntax(DicomTransferSyntax& result) + virtual void DatasetToJson(Json::Value& target, + DicomToJsonFormat format, + DicomToJsonFlags flags, + unsigned int maxStringLength) const ORTHANC_OVERRIDE { - DicomMap header; - if (DicomMap::ParseDicomMetaInformation(header, GetBufferData(), GetBufferSize())) - { - const DicomValue* value = header.TestAndGetValue(DICOM_TAG_TRANSFER_SYNTAX_UID); - if (value != NULL && - !value->IsBinary() && - !value->IsNull()) - { - return ::Orthanc::LookupTransferSyntax(result, Toolbox::StripSpaces(value->GetContent())); - } - } - else - { - // This is a DICOM file without a proper meta-header. Fallback - // to DCMTK, which will fully parse the dataset to retrieve - // the transfer syntax. Added in Orthanc 1.8.2. - return GetParsedDicomFile().LookupTransferSyntax(result); - } - - return false; + std::set ignoreTagLength; + FromDcmtkBridge::ExtractDicomAsJson( + target, dataset_, format, flags, maxStringLength, ignoreTagLength); } - - ParsedDicomFile& GetParsedDicomFile() + virtual unsigned int GetFramesCount() const ORTHANC_OVERRIDE { - ComputeParsedDicomFileIfMissing(); - - if (parsed_.HasContent()) - { - return parsed_.GetContent(); - } - else - { - throw OrthancException(ErrorCode_InternalError); - } + return DicomFrameIndex::GetFramesCount(dataset_); + } + + virtual ImageAccessor* DecodeFrame(unsigned int frame) const ORTHANC_OVERRIDE + { + return DicomImageDecoder::Decode(dataset_, frame); } }; - - DicomInstanceToStore::DicomInstanceToStore() : - pimpl_(new PImpl) + + DicomInstanceToStore* DicomInstanceToStore::CreateFromBuffer(const void* buffer, + size_t size) { - } - - - void DicomInstanceToStore::SetOrigin(const DicomInstanceOrigin& origin) - { - pimpl_->origin_ = origin; + return new FromBuffer(buffer, size); } - - const DicomInstanceOrigin& DicomInstanceToStore::GetOrigin() const + + DicomInstanceToStore* DicomInstanceToStore::CreateFromBuffer(const std::string& buffer) { - return pimpl_->origin_; - } - - - void DicomInstanceToStore::SetBuffer(const void* dicom, - size_t size) - { - pimpl_->SetBuffer(dicom, size); - } - - - void DicomInstanceToStore::SetParsedDicomFile(ParsedDicomFile& parsed) - { - pimpl_->parsed_.SetReference(parsed); + return new FromBuffer(buffer.empty() ? NULL : buffer.c_str(), buffer.size()); } - const DicomInstanceToStore::MetadataMap& DicomInstanceToStore::GetMetadata() const + DicomInstanceToStore* DicomInstanceToStore::CreateFromParsedDicomFile(ParsedDicomFile& dicom) { - return pimpl_->metadata_; + return new FromParsedDicomFile(dicom); } - - void DicomInstanceToStore::ClearMetadata() + + DicomInstanceToStore* DicomInstanceToStore::CreateFromDcmDataset(DcmDataset& dataset) { - pimpl_->metadata_.clear(); + return new FromDcmDataset(dataset); } - - void DicomInstanceToStore::AddMetadata(ResourceType level, - MetadataType metadata, - const std::string& value) - { - pimpl_->metadata_[std::make_pair(level, metadata)] = value; - } - - - const void* DicomInstanceToStore::GetBufferData() const - { - return const_cast(*pimpl_).GetBufferData(); - } - - - size_t DicomInstanceToStore::GetBufferSize() const - { - return const_cast(*pimpl_).GetBufferSize(); - } - - + bool DicomInstanceToStore::LookupTransferSyntax(DicomTransferSyntax& result) const { - return const_cast(*pimpl_).LookupTransferSyntax(result); + DicomMap header; + if (DicomMap::ParseDicomMetaInformation(header, GetBufferData(), GetBufferSize())) + { + const DicomValue* value = header.TestAndGetValue(DICOM_TAG_TRANSFER_SYNTAX_UID); + if (value != NULL && + !value->IsBinary() && + !value->IsNull()) + { + return ::Orthanc::LookupTransferSyntax(result, Toolbox::StripSpaces(value->GetContent())); + } + } + else + { + // This is a DICOM file without a proper meta-header. Fallback + // to DCMTK, which will fully parse the dataset to retrieve + // the transfer syntax. Added in Orthanc 1.8.2. + return GetParsedDicomFile().LookupTransferSyntax(result); + } + + return false; } bool DicomInstanceToStore::HasPixelData() const { - return const_cast(*pimpl_).GetParsedDicomFile().HasTag(DICOM_TAG_PIXEL_DATA); + return GetParsedDicomFile().HasTag(DICOM_TAG_PIXEL_DATA); } - ParsedDicomFile& DicomInstanceToStore::GetParsedDicomFile() const - { - return const_cast(*pimpl_).GetParsedDicomFile(); - } - + void DicomInstanceToStore::GetSummary(DicomMap& summary) const { OrthancConfiguration::DefaultExtractDicomSummary(summary, GetParsedDicomFile()); } + void DicomInstanceToStore::GetDicomAsJson(Json::Value& dicomAsJson) const { OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, GetParsedDicomFile()); } + void DicomInstanceToStore::DatasetToJson(Json::Value& target, DicomToJsonFormat format, DicomToJsonFlags flags, @@ -405,11 +300,13 @@ return GetParsedDicomFile().DatasetToJson(target, format, flags, maxStringLength); } + unsigned int DicomInstanceToStore::GetFramesCount() const { return GetParsedDicomFile().GetFramesCount(); } - + + ImageAccessor* DicomInstanceToStore::DecodeFrame(unsigned int frame) const { return GetParsedDicomFile().DecodeFrame(frame); diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/DicomInstanceToStore.h --- a/OrthancServer/Sources/DicomInstanceToStore.h Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/DicomInstanceToStore.h Thu Feb 11 11:00:05 2021 +0100 @@ -39,6 +39,8 @@ #include +class DcmDataset; + namespace Orthanc { class ImageAccessor; @@ -50,54 +52,80 @@ typedef std::map, std::string> MetadataMap; private: - class PImpl; - boost::shared_ptr pimpl_; + class FromBuffer; + class FromParsedDicomFile; + class FromDcmDataset; + + MetadataMap metadata_; + DicomInstanceOrigin origin_; public: - DicomInstanceToStore(); + virtual ~DicomInstanceToStore() + { + } - void SetOrigin(const DicomInstanceOrigin& origin); - - const DicomInstanceOrigin& GetOrigin() const; + // WARNING: The source in the factory methods is *not* copied and + // must *not* be deallocated as long as this wrapper object is alive + static DicomInstanceToStore* CreateFromBuffer(const void* buffer, + size_t size); + + static DicomInstanceToStore* CreateFromBuffer(const std::string& buffer); + + static DicomInstanceToStore* CreateFromParsedDicomFile(ParsedDicomFile& dicom); + + static DicomInstanceToStore* CreateFromDcmDataset(DcmDataset& dataset); + - // WARNING: The buffer is not copied, it must not be removed as - // long as the "DicomInstanceToStore" object is alive - void SetBuffer(const void* dicom, - size_t size); + + void SetOrigin(const DicomInstanceOrigin& origin) + { + origin_ = origin; + } + + const DicomInstanceOrigin& GetOrigin() const + { + return origin_; + } + + const MetadataMap& GetMetadata() const + { + return metadata_; + } - // WARNING: The "ParsedDicomFile" is not copied - void SetParsedDicomFile(ParsedDicomFile& parsed); - - const MetadataMap& GetMetadata() const; - - void ClearMetadata(); + void ClearMetadata() + { + metadata_.clear(); + } // This function is notably used by modify/anonymize operations void AddMetadata(ResourceType level, MetadataType metadata, - const std::string& value); - - const void* GetBufferData() const; - - size_t GetBufferSize() const; + const std::string& value) + { + metadata_[std::make_pair(level, metadata)] = value; + } bool LookupTransferSyntax(DicomTransferSyntax& result) const; - bool HasPixelData() const; + virtual ParsedDicomFile& GetParsedDicomFile() const = 0; - ParsedDicomFile& GetParsedDicomFile() const; + virtual const void* GetBufferData() const = 0; - void GetSummary(DicomMap& summary) const; + virtual size_t GetBufferSize() const = 0; - void GetDicomAsJson(Json::Value& dicomAsJson) const; + virtual bool HasPixelData() const; + + virtual void GetSummary(DicomMap& summary) const; - void DatasetToJson(Json::Value& target, - DicomToJsonFormat format, - DicomToJsonFlags flags, - unsigned int maxStringLength) const; + virtual void GetDicomAsJson(Json::Value& dicomAsJson) const; - unsigned int GetFramesCount() const; + virtual void DatasetToJson(Json::Value& target, + DicomToJsonFormat format, + DicomToJsonFlags flags, + unsigned int maxStringLength) const; + + virtual unsigned int GetFramesCount() const; - ImageAccessor* DecodeFrame(unsigned int frame) const; + virtual ImageAccessor* DecodeFrame(unsigned int frame) const; }; } diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/OrthancConfiguration.cpp --- a/OrthancServer/Sources/OrthancConfiguration.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/OrthancConfiguration.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -34,6 +34,7 @@ #include "PrecompiledHeadersServer.h" #include "OrthancConfiguration.h" +#include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h" #include "../../OrthancFramework/Sources/HttpServer/HttpServer.h" #include "../../OrthancFramework/Sources/Logging.h" @@ -1021,12 +1022,29 @@ } + void OrthancConfiguration::DefaultExtractDicomSummary(DicomMap& target, + DcmDataset& dicom) + { + std::set ignoreTagLength; + FromDcmtkBridge::ExtractDicomSummary(target, dicom, ORTHANC_MAXIMUM_TAG_LENGTH, ignoreTagLength); + } + + void OrthancConfiguration::DefaultDicomDatasetToJson(Json::Value& target, const ParsedDicomFile& dicom) { std::set ignoreTagLength; DefaultDicomDatasetToJson(target, dicom, ignoreTagLength); } + + + void OrthancConfiguration::DefaultDicomDatasetToJson(Json::Value& target, + DcmDataset& dicom) + { + std::set ignoreTagLength; + FromDcmtkBridge::ExtractDicomAsJson(target, dicom, DicomToJsonFormat_Full, DicomToJsonFlags_Default, + ORTHANC_MAXIMUM_TAG_LENGTH, ignoreTagLength); + } void OrthancConfiguration::DefaultDicomDatasetToJson(Json::Value& target, diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/OrthancConfiguration.h --- a/OrthancServer/Sources/OrthancConfiguration.h Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/OrthancConfiguration.h Thu Feb 11 11:00:05 2021 +0100 @@ -43,6 +43,8 @@ #include #include +class DcmDataset; + namespace Orthanc { class DicomMap; @@ -251,11 +253,17 @@ static void DefaultExtractDicomSummary(DicomMap& target, const ParsedDicomFile& dicom); + + static void DefaultExtractDicomSummary(DicomMap& target, + DcmDataset& dicom); static void DefaultDicomDatasetToJson(Json::Value& target, const ParsedDicomFile& dicom); static void DefaultDicomDatasetToJson(Json::Value& target, + DcmDataset& dicom); + + static void DefaultDicomDatasetToJson(Json::Value& target, const ParsedDicomFile& dicom, const std::set& ignoreTagLength); diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -396,12 +396,11 @@ ParsedDicomFile& dicom, bool sendAnswer) { - DicomInstanceToStore toStore; - toStore.SetOrigin(DicomInstanceOrigin::FromRest(call)); - toStore.SetParsedDicomFile(dicom); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(dicom)); + toStore->SetOrigin(DicomInstanceOrigin::FromRest(call)); ServerContext& context = OrthancRestApi::GetContext(call); - StoreStatus status = context.Store(id, toStore, StoreInstanceMode_Default); + StoreStatus status = context.Store(id, *toStore, StoreInstanceMode_Default); if (status == StoreStatus_Failure) { @@ -410,7 +409,7 @@ if (sendAnswer) { - OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status, id); + OrthancRestApi::GetApi(call).AnswerStoredInstance(call, *toStore, status, id); } } diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -191,19 +191,18 @@ if (!content.empty()) { LOG(INFO) << "Uploading DICOM file from ZIP archive: " << filename; - - DicomInstanceToStore toStore; - toStore.SetOrigin(DicomInstanceOrigin::FromRest(call)); - toStore.SetBuffer(content.c_str(), content.size()); + + std::unique_ptr toStore(DicomInstanceToStore::CreateFromBuffer(content)); + toStore->SetOrigin(DicomInstanceOrigin::FromRest(call)); std::string publicId; try { - StoreStatus status = context.Store(publicId, toStore, StoreInstanceMode_Default); + StoreStatus status = context.Store(publicId, *toStore, StoreInstanceMode_Default); Json::Value info; - SetupResourceAnswer(info, toStore, status, publicId); + SetupResourceAnswer(info, *toStore, status, publicId); answer.append(info); } catch (OrthancException& e) @@ -228,24 +227,25 @@ // latter can possibly store a reference to the former (*) std::string dicom; - DicomInstanceToStore toStore; - toStore.SetOrigin(DicomInstanceOrigin::FromRest(call)); + std::unique_ptr toStore; if (boost::iequals(call.GetHttpHeader("content-encoding", ""), "gzip")) { GzipCompressor compressor; compressor.Uncompress(dicom, call.GetBodyData(), call.GetBodySize()); - toStore.SetBuffer(dicom.c_str(), dicom.size()); // (*) + toStore.reset(DicomInstanceToStore::CreateFromBuffer(dicom)); // (*) } else { - toStore.SetBuffer(call.GetBodyData(), call.GetBodySize()); + toStore.reset(DicomInstanceToStore::CreateFromBuffer(call.GetBodyData(), call.GetBodySize())); } + toStore->SetOrigin(DicomInstanceOrigin::FromRest(call)); + std::string publicId; - StoreStatus status = context.Store(publicId, toStore, StoreInstanceMode_Default); + StoreStatus status = context.Store(publicId, *toStore, StoreInstanceMode_Default); - OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status, publicId); + OrthancRestApi::GetApi(call).AnswerStoredInstance(call, *toStore, status, publicId); } } diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/OrthancWebDav.cpp --- a/OrthancServer/Sources/OrthancWebDav.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/OrthancWebDav.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -1282,15 +1282,14 @@ { LOG(INFO) << "Uploading DICOM file extracted from a ZIP archive in WebDAV: " << filename; - DicomInstanceToStore instance; - instance.SetOrigin(DicomInstanceOrigin::FromWebDav()); - instance.SetBuffer(uncompressedFile.c_str(), uncompressedFile.size()); + std::unique_ptr instance(DicomInstanceToStore::CreateFromBuffer(uncompressedFile)); + instance->SetOrigin(DicomInstanceOrigin::FromWebDav()); std::string publicId; try { - context_.Store(publicId, instance, StoreInstanceMode_Default); + context_.Store(publicId, *instance, StoreInstanceMode_Default); } catch (OrthancException& e) { @@ -1306,14 +1305,13 @@ } else { - DicomInstanceToStore instance; - instance.SetOrigin(DicomInstanceOrigin::FromWebDav()); - instance.SetBuffer(content.c_str(), content.size()); + std::unique_ptr instance(DicomInstanceToStore::CreateFromBuffer(content)); + instance->SetOrigin(DicomInstanceOrigin::FromWebDav()); try { std::string publicId; - StoreStatus status = context_.Store(publicId, instance, StoreInstanceMode_Default); + StoreStatus status = context_.Store(publicId, *instance, StoreInstanceMode_Default); if (status == StoreStatus_Success || status == StoreStatus_AlreadyStored) { diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -728,11 +728,10 @@ { std::unique_ptr tmp(transcoded.ReleaseAsParsedDicomFile()); - DicomInstanceToStore toStore; - toStore.SetParsedDicomFile(*tmp); - toStore.SetOrigin(dicom.GetOrigin()); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*tmp)); + toStore->SetOrigin(dicom.GetOrigin()); - StoreStatus ok = StoreAfterTranscoding(resultPublicId, toStore, mode); + StoreStatus ok = StoreAfterTranscoding(resultPublicId, *toStore, mode); assert(resultPublicId == tmp->GetHasher().HashInstance()); return ok; @@ -1736,9 +1735,8 @@ size_t size, unsigned int frameIndex) { - DicomInstanceToStore instance; - instance.SetBuffer(dicom, size); - return DecodeDicomFrame(instance, frameIndex); + std::unique_ptr instance(DicomInstanceToStore::CreateFromBuffer(dicom, size)); + return DecodeDicomFrame(*instance, frameIndex); } diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/ServerJobs/MergeStudyJob.cpp --- a/OrthancServer/Sources/ServerJobs/MergeStudyJob.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/ServerJobs/MergeStudyJob.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -147,12 +147,11 @@ // Fix since Orthanc 1.5.8: Assign new "SOPInstanceUID", as the instance has been modified modified->ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); - DicomInstanceToStore toStore; - toStore.SetOrigin(origin_); - toStore.SetParsedDicomFile(*modified); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*modified)); + toStore->SetOrigin(origin_); std::string modifiedInstance; - if (GetContext().Store(modifiedInstance, toStore, + if (GetContext().Store(modifiedInstance, *toStore, StoreInstanceMode_Default) != StoreStatus_Success) { LOG(ERROR) << "Error while storing a modified instance " << instance; diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/ServerJobs/Operations/ModifyInstanceOperation.cpp --- a/OrthancServer/Sources/ServerJobs/Operations/ModifyInstanceOperation.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/ServerJobs/Operations/ModifyInstanceOperation.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -104,16 +104,15 @@ { modification_->Apply(*modified); - DicomInstanceToStore toStore; + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*modified)); assert(origin_ == RequestOrigin_Lua); - toStore.SetOrigin(DicomInstanceOrigin::FromLua()); - toStore.SetParsedDicomFile(*modified); + toStore->SetOrigin(DicomInstanceOrigin::FromLua()); // TODO other metadata - toStore.AddMetadata(ResourceType_Instance, MetadataType_ModifiedFrom, instance.GetId()); + toStore->AddMetadata(ResourceType_Instance, MetadataType_ModifiedFrom, instance.GetId()); std::string modifiedId; - context_.Store(modifiedId, toStore, StoreInstanceMode_Default); + context_.Store(modifiedId, *toStore, StoreInstanceMode_Default); // Only chain with other commands if this command succeeds outputs.Append(new DicomInstanceOperationValue(instance.GetServerContext(), modifiedId)); diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp --- a/OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -210,9 +210,8 @@ assert(modifiedUid == IDicomTranscoder::GetSopInstanceUid(modified->GetDcmtkObject())); - DicomInstanceToStore toStore; - toStore.SetOrigin(origin_); - toStore.SetParsedDicomFile(*modified); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*modified)); + toStore->SetOrigin(origin_); /** @@ -228,21 +227,21 @@ if (originalHasher->HashSeries() != modifiedHasher.HashSeries()) { - toStore.AddMetadata(ResourceType_Series, metadataType, originalHasher->HashSeries()); + toStore->AddMetadata(ResourceType_Series, metadataType, originalHasher->HashSeries()); } if (originalHasher->HashStudy() != modifiedHasher.HashStudy()) { - toStore.AddMetadata(ResourceType_Study, metadataType, originalHasher->HashStudy()); + toStore->AddMetadata(ResourceType_Study, metadataType, originalHasher->HashStudy()); } if (originalHasher->HashPatient() != modifiedHasher.HashPatient()) { - toStore.AddMetadata(ResourceType_Patient, metadataType, originalHasher->HashPatient()); + toStore->AddMetadata(ResourceType_Patient, metadataType, originalHasher->HashPatient()); } assert(instance == originalHasher->HashInstance()); - toStore.AddMetadata(ResourceType_Instance, metadataType, instance); + toStore->AddMetadata(ResourceType_Instance, metadataType, instance); /** @@ -250,7 +249,7 @@ **/ std::string modifiedInstance; - if (GetContext().Store(modifiedInstance, toStore, + if (GetContext().Store(modifiedInstance, *toStore, StoreInstanceMode_Default) != StoreStatus_Success) { throw OrthancException(ErrorCode_CannotStoreInstance, diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/ServerJobs/SplitStudyJob.cpp --- a/OrthancServer/Sources/ServerJobs/SplitStudyJob.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/ServerJobs/SplitStudyJob.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -139,12 +139,11 @@ targetStudy_ = modified->GetHasher().HashStudy(); } - DicomInstanceToStore toStore; - toStore.SetOrigin(origin_); - toStore.SetParsedDicomFile(*modified); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*modified)); + toStore->SetOrigin(origin_); std::string modifiedInstance; - if (GetContext().Store(modifiedInstance, toStore, + if (GetContext().Store(modifiedInstance, *toStore, StoreInstanceMode_Default) != StoreStatus_Success) { LOG(ERROR) << "Error while storing a modified instance " << instance; diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/Sources/main.cpp --- a/OrthancServer/Sources/main.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/Sources/main.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -88,22 +88,15 @@ const std::string& remoteAet, const std::string& calledAet) ORTHANC_OVERRIDE { - std::string dicomFile; - - if (!FromDcmtkBridge::SaveToMemoryBuffer(dicomFile, dicom)) - { - throw OrthancException(ErrorCode_InternalError, "Cannot write DICOM file to memory"); - } + std::unique_ptr toStore(DicomInstanceToStore::CreateFromDcmDataset(dicom)); - if (dicomFile.size() > 0) + if (toStore->GetBufferSize() > 0) { - DicomInstanceToStore toStore; - toStore.SetOrigin(DicomInstanceOrigin::FromDicomProtocol - (remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str())); - toStore.SetBuffer(dicomFile.c_str(), dicomFile.size()); + toStore->SetOrigin(DicomInstanceOrigin::FromDicomProtocol + (remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str())); std::string id; - context_.Store(id, toStore, StoreInstanceMode_Default); + context_.Store(id, *toStore, StoreInstanceMode_Default); } } }; diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/UnitTestsSources/ServerIndexTests.cpp --- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -727,20 +727,19 @@ ParsedDicomFile dicom(instance, GetDefaultDicomEncoding(), false /* be strict */); - DicomInstanceToStore toStore; - toStore.SetParsedDicomFile(dicom); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(dicom)); std::map instanceMetadata; { DicomMap summary; - OrthancConfiguration::DefaultExtractDicomSummary(summary, toStore.GetParsedDicomFile()); + OrthancConfiguration::DefaultExtractDicomSummary(summary, toStore->GetParsedDicomFile()); DicomTransferSyntax transferSyntax; bool hasTransferSyntax = dicom.LookupTransferSyntax(transferSyntax); ASSERT_EQ(StoreStatus_Success, index.Store( - instanceMetadata, summary, attachments, toStore.GetMetadata(), - toStore.GetOrigin(), false /* don't overwrite */, + instanceMetadata, summary, attachments, toStore->GetMetadata(), + toStore->GetOrigin(), false /* don't overwrite */, hasTransferSyntax, transferSyntax, true /* pixel data offset */, 42)); } @@ -834,13 +833,12 @@ DicomInstanceHasher hasher(instance); - DicomInstanceToStore toStore; - toStore.SetParsedDicomFile(dicom); - toStore.SetOrigin(DicomInstanceOrigin::FromPlugins()); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(dicom)); + toStore->SetOrigin(DicomInstanceOrigin::FromPlugins()); ASSERT_EQ(id, hasher.HashInstance()); std::string id2; - ASSERT_EQ(StoreStatus_Success, context.Store(id2, toStore, StoreInstanceMode_Default)); + ASSERT_EQ(StoreStatus_Success, context.Store(id2, *toStore, StoreInstanceMode_Default)); ASSERT_EQ(id, id2); } @@ -872,13 +870,12 @@ ParsedDicomFile dicom(instance2, GetDefaultDicomEncoding(), false /* be strict */); - DicomInstanceToStore toStore; - toStore.SetParsedDicomFile(dicom); - toStore.SetOrigin(DicomInstanceOrigin::FromPlugins()); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(dicom)); + toStore->SetOrigin(DicomInstanceOrigin::FromPlugins()); std::string id2; ASSERT_EQ(overwrite ? StoreStatus_Success : StoreStatus_AlreadyStored, - context.Store(id2, toStore, StoreInstanceMode_Default)); + context.Store(id2, *toStore, StoreInstanceMode_Default)); ASSERT_EQ(id, id2); } diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/UnitTestsSources/ServerJobsTests.cpp --- a/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -535,10 +535,9 @@ dicom.Replace(DICOM_TAG_PATIENT_NAME, std::string("JODOGNE"), false, DicomReplaceMode_InsertIfAbsent, ""); - DicomInstanceToStore toStore; - toStore.SetParsedDicomFile(dicom); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(dicom)); - return (context_->Store(id, toStore, StoreInstanceMode_Default) == StoreStatus_Success); + return (context_->Store(id, *toStore, StoreInstanceMode_Default) == StoreStatus_Success); } }; } diff -r b4c58795f3a8 -r 8f9090b137f1 OrthancServer/UnitTestsSources/UnitTestsMain.cpp --- a/OrthancServer/UnitTestsSources/UnitTestsMain.cpp Thu Feb 11 09:33:48 2021 +0100 +++ b/OrthancServer/UnitTestsSources/UnitTestsMain.cpp Thu Feb 11 11:00:05 2021 +0100 @@ -247,11 +247,10 @@ element != NULL && element->getTag().getEVR() == EVR_US); - DicomInstanceToStore toStore; - toStore.SetParsedDicomFile(dicom); + std::unique_ptr toStore(DicomInstanceToStore::CreateFromParsedDicomFile(dicom)); Json::Value dicomAsJson; - OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, toStore.GetParsedDicomFile()); + OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, toStore->GetParsedDicomFile()); DicomMap m; m.FromDicomAsJson(dicomAsJson);