Mercurial > hg > orthanc
view OrthancFramework/Sources/DataSource/TranscoderDataSource.cpp @ 7144:a2cd12438c41 default tip
fix comment
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Mon, 31 Aug 2026 09:16:01 +0200 |
| parents | ff382e45466a |
| children |
line wrap: on
line source
/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium * Copyright (C) 2017-2023 Osimis S.A., Belgium * Copyright (C) 2024-2026 Orthanc Team SRL, Belgium * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/>. **/ #include "../PrecompiledHeaders.h" #include "TranscoderDataSource.h" #include "../DicomParsing/ParsedDicomFile.h" #include "../Logging.h" #include "../OrthancException.h" #include "BaseDataIdentifier.h" #include "DataSourceReader.h" #include "StorageAreaDataSource.h" #include <boost/lexical_cast.hpp> #include <dcmtk/dcmdata/dcfilefo.h> namespace Orthanc { static bool IsLosslessTransferSyntax(DicomTransferSyntax syntax) { return (syntax == DicomTransferSyntax_LittleEndianImplicit || syntax == DicomTransferSyntax_LittleEndianExplicit || syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit || syntax == DicomTransferSyntax_BigEndianExplicit || syntax == DicomTransferSyntax_JPEGLSLossless || syntax == DicomTransferSyntax_JPEG2000LosslessOnly || syntax == DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly || syntax == DicomTransferSyntax_RLELossless || syntax == DicomTransferSyntax_JPEGXLLossless || syntax == DicomTransferSyntax_HighThroughputJPEG2000LosslessOnly); } class TranscoderDataSource::Value : public IDynamicObject { private: Mutex parsedMutex_; std::unique_ptr<ParsedDicomFile> parsed_; std::unique_ptr<std::string> raw_; size_t size_; public: explicit Value(IDicomTranscoder::DicomImage* transcoded) { std::unique_ptr<IDicomTranscoder::DicomImage> protection(transcoded); if (transcoded == NULL) { throw OrthancException(ErrorCode_NullPointer); } if (!transcoded->HasParsed() && !transcoded->HasInternalBuffer()) { throw OrthancException(ErrorCode_InternalError, "No transcoded image is available"); } /** * Note that both "transcoded->HasParsed()" and * "transcoded->HasInternalBuffer()" could be true in debug * mode, because of "IDicomTranscoder::CheckTranscoding()". **/ if (transcoded->HasParsed()) { parsed_.reset(transcoded->ReleaseAsParsedDicomFile()); } if (transcoded->HasInternalBuffer()) { raw_.reset(transcoded->ReleaseInternalBuffer()); size_ = raw_->size(); // This is more accurate than "f.calcElementLength()" below } else { assert(parsed_.get() != NULL); DcmFileFormat& f = parsed_->GetDcmtkObject(); size_ = f.calcElementLength(f.getDataset()->getOriginalXfer(), EET_ExplicitLength); } } size_t GetSize() const { return size_; } bool HasParsed() const { return parsed_.get() != NULL; } bool HasRawBuffer() const { return raw_.get() != NULL; } Mutex::ScopedLock* LockParsed() { return new Mutex::ScopedLock(parsedMutex_); } ParsedDicomFile& GetParsed() const { if (parsed_.get() == NULL) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return *parsed_; } } const std::string& GetRawBuffer() const { if (raw_.get() == NULL) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return *raw_; } } }; class TranscoderDataSource::Identifier : public BaseDataIdentifier { private: FileInfo attachment_; DicomTransferSyntax targetSyntax_; TranscodingSopInstanceUidMode mode_; bool hasLossyQuality_; unsigned int lossyQuality_; public: Identifier(const FileInfo& attachment, DicomTransferSyntax targetSyntax, TranscodingSopInstanceUidMode mode) : attachment_(attachment), targetSyntax_(targetSyntax), mode_(mode), hasLossyQuality_(false), lossyQuality_(0) { } void SetLossyQuality(unsigned int quality) { hasLossyQuality_ = true; lossyQuality_ = quality; } virtual bool GetCacheKey(std::string& key) const ORTHANC_OVERRIDE { key = (attachment_.GetUuid() + "|" + GetTransferSyntaxUid(targetSyntax_) + "|" + boost::lexical_cast<std::string>(mode_)); if (hasLossyQuality_) { key += "|" + boost::lexical_cast<std::string>(lossyQuality_); } return true; } virtual bool EstimateValueSize(size_t& target) const ORTHANC_OVERRIDE { target = attachment_.GetUncompressedSize(); return true; } Value* Load(IDicomTranscoder& transcoder, DataSourceReader& storageAreaReader) const { std::unique_ptr<StorageAreaDataSource::Range> range( StorageAreaDataSource::Execute( storageAreaReader, StorageAreaDataSource::CreateAttachmentRequest(attachment_, true /* uncompress */))); IDicomTranscoder::DicomImage source; source.SetExternalBuffer(range->GetData(), range->GetSize()); std::unique_ptr<IDicomTranscoder::DicomImage> target(new IDicomTranscoder::DicomImage); std::set<DicomTransferSyntax> allowedSyntaxes; allowedSyntaxes.insert(targetSyntax_); LOG(INFO) << "Transcoding DICOM attachment to " << GetTransferSyntaxUid(targetSyntax_) << ": " << attachment_.GetUuid(); bool success; if (hasLossyQuality_) { success = transcoder.Transcode(*target, source, allowedSyntaxes, mode_, lossyQuality_); } else { success = transcoder.Transcode(*target, source, allowedSyntaxes, mode_); } if (success) { return new Value(target.release()); } else { throw OrthancException(ErrorCode_NotImplemented); } } }; TranscoderDataSource::TranscoderDataSource(const boost::shared_ptr<IDicomTranscoder>& transcoder, const boost::shared_ptr<DataSourceReader>& storageAreaReader) : transcoder_(transcoder), storageAreaReader_(storageAreaReader) { if (!transcoder_ || !storageAreaReader_) { throw OrthancException(ErrorCode_NullPointer); } } IDynamicObject* TranscoderDataSource::Load(const IDataIdentifier& id, const boost::shared_ptr<SharedObjectCache>& readerCache) { return dynamic_cast<const Identifier&>(id).Load(*transcoder_, *storageAreaReader_); } size_t TranscoderDataSource::GetValueSize(const IDynamicObject& value) const { return dynamic_cast<const Value&>(value).GetSize(); } TranscoderDataSource::Transcoded::Transcoded(DataSourceAnswer::Item* item) : item_(item) { if (!item) { throw OrthancException(ErrorCode_NullPointer); } } TranscoderDataSource::Transcoded::LockAsParsed::LockAsParsed(Transcoded& that) { Value& value = dynamic_cast<Value&>(*that.item_->GetValue()); if (value.HasParsed()) { lock_.reset(value.LockParsed()); content_ = &value.GetParsed(); } else { assert(value.HasRawBuffer()); parsed_.reset(new ParsedDicomFile(value.GetRawBuffer())); content_ = parsed_.get(); } assert(content_ != NULL); } TranscoderDataSource::Transcoded::LockAsBuffer::LockAsBuffer(Transcoded& that) { Value& value = dynamic_cast<Value&>(*that.item_->GetValue()); if (value.HasParsed()) { std::unique_ptr<Mutex::ScopedLock> lock(value.LockParsed()); serialized_.reset(new std::string); value.GetParsed().SaveToMemoryBuffer(*serialized_); content_ = serialized_.get(); } else { assert(value.HasRawBuffer()); content_ = &value.GetRawBuffer(); } assert(content_ != NULL); } IDataIdentifier* TranscoderDataSource::CreateRequest(const FileInfo& attachment, DicomTransferSyntax targetSyntax, TranscodingSopInstanceUidMode mode, bool hasLossyQuality, unsigned int lossyQuality) { std::unique_ptr<Identifier> id(new Identifier(attachment, targetSyntax, mode)); if (hasLossyQuality && !IsLosslessTransferSyntax(targetSyntax)) { id->SetLossyQuality(lossyQuality); } return id.release(); } TranscoderDataSource::Transcoded* TranscoderDataSource::Execute(DataSourceReader& reader, IDataIdentifier* request) { if (request == NULL) { throw OrthancException(ErrorCode_NullPointer); } else { std::unique_ptr<DataSourceAnswer::Item> item(reader.ReadSingle(request)); return new Transcoded(item.release()); } } }
