# HG changeset patch # User Sebastien Jodogne # Date 1589455865 -7200 # Node ID 4cdc875510d1699e07bff676cecef73c93543672 # Parent 2910b0d30fe02f5dcf7df200ab77670e8e50230c ServerContext::DecodeDicomFrame() diff -r 2910b0d30fe0 -r 4cdc875510d1 OrthancServer/DefaultDicomImageDecoder.h --- a/OrthancServer/DefaultDicomImageDecoder.h Thu May 14 07:37:44 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IDicomImageDecoder.h" -#include "../Core/DicomParsing/ParsedDicomFile.h" -#include "../Core/DicomParsing/Internals/DicomImageDecoder.h" - -namespace Orthanc -{ - class DefaultDicomImageDecoder : public IDicomImageDecoder - { - public: - virtual ImageAccessor* Decode(const void* dicom, - size_t size, - unsigned int frame) - { - ParsedDicomFile parsed(dicom, size); - return DicomImageDecoder::Decode(parsed, frame); - } - }; -} diff -r 2910b0d30fe0 -r 4cdc875510d1 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu May 14 07:37:44 2020 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu May 14 13:31:05 2020 +0200 @@ -44,7 +44,6 @@ #include "../../Core/Images/ImageProcessing.h" #include "../../Core/Logging.h" #include "../../Core/MultiThreading/Semaphore.h" -#include "../DefaultDicomImageDecoder.h" #include "../OrthancConfiguration.h" #include "../Search/DatabaseLookup.h" #include "../ServerContext.h" @@ -556,44 +555,23 @@ { std::string publicId = call.GetUriComponent("id", ""); -#if ORTHANC_ENABLE_PLUGINS == 1 - if (context.GetPlugins().HasCustomImageDecoder()) - { - // TODO create a cache of file - std::string dicomContent; - context.ReadDicom(dicomContent, publicId); - decoded.reset(context.GetPlugins().DecodeUnsafe(dicomContent.c_str(), dicomContent.size(), frame)); - - /** - * Note that we call "DecodeUnsafe()": We do not fallback to - * the builtin decoder if no installed decoder plugin is able - * to decode the image. This allows us to take advantage of - * the cache below. - **/ - - if (handler.RequiresDicomTags() && - decoded.get() != NULL) - { - // TODO Optimize this lookup for photometric interpretation: - // It should be implemented by the plugin to avoid parsing - // twice the DICOM file - ParsedDicomFile parsed(dicomContent); - parsed.ExtractDicomSummary(dicom); - } - } -#endif + decoded.reset(context.DecodeDicomFrame(publicId, frame)); if (decoded.get() == NULL) { - // Use Orthanc's built-in decoder, using the cache to speed-up - // things on multi-frame images - ServerContext::DicomCacheLocker locker(context, publicId); - decoded.reset(DicomImageDecoder::Decode(locker.GetDicom(), frame)); - - if (handler.RequiresDicomTags()) - { - locker.GetDicom().ExtractDicomSummary(dicom); - } + throw OrthancException(ErrorCode_NotImplemented, + "Cannot decode DICOM instance with ID: " + publicId); + } + + if (handler.RequiresDicomTags()) + { + /** + * Retrieve a summary of the DICOM tags, which is + * necessary to deal with MONOCHROME1 photometric + * interpretation, and with windowing parameters. + **/ + ServerContext::DicomCacheLocker locker(context, publicId); + locker.GetDicom().ExtractDicomSummary(dicom); } } catch (OrthancException& e) @@ -982,21 +960,19 @@ } std::string publicId = call.GetUriComponent("id", ""); - std::string dicomContent; - context.ReadDicom(dicomContent, publicId); + std::unique_ptr decoded(context.DecodeDicomFrame(publicId, frame)); -#if ORTHANC_ENABLE_PLUGINS == 1 - IDicomImageDecoder& decoder = context.GetPlugins(); -#else - DefaultDicomImageDecoder decoder; // This is Orthanc's built-in decoder -#endif - - std::unique_ptr decoded(decoder.Decode(dicomContent.c_str(), dicomContent.size(), frame)); - - std::string result; - decoded->ToMatlabString(result); - - call.GetOutput().AnswerBuffer(result, MimeType_PlainText); + if (decoded.get() == NULL) + { + throw OrthancException(ErrorCode_NotImplemented, + "Cannot decode DICOM instance with ID: " + publicId); + } + else + { + std::string result; + decoded->ToMatlabString(result); + call.GetOutput().AnswerBuffer(result, MimeType_PlainText); + } } diff -r 2910b0d30fe0 -r 4cdc875510d1 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Thu May 14 07:37:44 2020 +0200 +++ b/OrthancServer/ServerContext.cpp Thu May 14 13:31:05 2020 +0200 @@ -34,6 +34,7 @@ #include "PrecompiledHeadersServer.h" #include "ServerContext.h" +#include "../Core/DicomParsing/Internals/DicomImageDecoder.h" #include "../Core/Cache/SharedArchive.h" #include "../Core/DicomParsing/DcmtkTranscoder.h" #include "../Core/DicomParsing/FromDcmtkBridge.h" @@ -1193,6 +1194,69 @@ } + ImageAccessor* ServerContext::DecodeDicomFrame(const std::string& publicId, + unsigned int frameIndex) + { + // TODO => Reorder given the global parameter + +#if ORTHANC_ENABLE_PLUGINS == 1 + if (HasPlugins() && + GetPlugins().HasCustomImageDecoder()) + { + // TODO: Store the raw buffer in the DicomCacheLocker + std::string dicomContent; + ReadDicom(dicomContent, publicId); + + std::unique_ptr decoded( + GetPlugins().Decode(dicomContent.c_str(), dicomContent.size(), frameIndex)); + if (decoded.get() != NULL) + { + return decoded.release(); + } + else + { + LOG(INFO) << "The installed image decoding plugins cannot handle an image, " + << "fallback to the built-in decoder"; + } + } +#endif + + { + // Use Orthanc's built-in decoder, using the cache to speed-up + // things on multi-frame images + ServerContext::DicomCacheLocker locker(*this, publicId); + return DicomImageDecoder::Decode(locker.GetDicom(), frameIndex); + } + } + + + ImageAccessor* ServerContext::DecodeDicomFrame(const DicomInstanceToStore& dicom, + unsigned int frameIndex) + { + // TODO => Reorder given the global parameter + +#if ORTHANC_ENABLE_PLUGINS == 1 + if (HasPlugins() && + GetPlugins().HasCustomImageDecoder()) + { + std::unique_ptr decoded( + GetPlugins().Decode(dicom.GetBufferData(), dicom.GetBufferSize(), frameIndex)); + if (decoded.get() != NULL) + { + return decoded.release(); + } + else + { + LOG(INFO) << "The installed image decoding plugins cannot handle an image, " + << "fallback to the built-in decoder"; + } + } +#endif + + return DicomImageDecoder::Decode(dicom.GetParsedDicomFile(), frameIndex); + } + + void ServerContext::StoreWithTranscoding(std::string& sopClassUid, std::string& sopInstanceUid, DicomStoreUserConnection& connection, diff -r 2910b0d30fe0 -r 4cdc875510d1 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Thu May 14 07:37:44 2020 +0200 +++ b/OrthancServer/ServerContext.h Thu May 14 13:31:05 2020 +0200 @@ -459,6 +459,12 @@ return *storageCommitmentReports_; } + ImageAccessor* DecodeDicomFrame(const std::string& publicId, + unsigned int frameIndex); + + ImageAccessor* DecodeDicomFrame(const DicomInstanceToStore& dicom, + unsigned int frameIndex); + void StoreWithTranscoding(std::string& sopClassUid, std::string& sopInstanceUid, DicomStoreUserConnection& connection, diff -r 2910b0d30fe0 -r 4cdc875510d1 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Thu May 14 07:37:44 2020 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Thu May 14 13:31:05 2020 +0200 @@ -62,7 +62,6 @@ #include "../../Core/OrthancException.h" #include "../../Core/SerializationToolbox.h" #include "../../Core/Toolbox.h" -#include "../../OrthancServer/DefaultDicomImageDecoder.h" #include "../../OrthancServer/OrthancConfiguration.h" #include "../../OrthancServer/OrthancFindRequestHandler.h" #include "../../OrthancServer/Search/HierarchicalMatcher.h" @@ -2757,6 +2756,11 @@ // Images returned to plugins are assumed to be writeable. If the // input image is read-only, we return a copy so that it can be modified. + if (image.get() == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + if (image->IsReadOnly()) { std::unique_ptr copy(new Image(image->GetFormat(), image->GetWidth(), image->GetHeight(), false)); @@ -2810,22 +2814,17 @@ case _OrthancPluginService_GetInstanceDecodedFrame: { - std::unique_ptr decoded; if (p.targetImage == NULL) { throw OrthancException(ErrorCode_NullPointer); } - else if (HasCustomImageDecoder()) + + std::unique_ptr decoded; { - // TODO - This call could be speeded up the future, if a - // "decoding context" gets introduced in the decoder plugins - decoded.reset(Decode(instance.GetBufferData(), instance.GetBufferSize(), p.frameIndex)); + PImpl::ServerContextLock lock(*pimpl_); + decoded.reset(lock.GetContext().DecodeDicomFrame(instance, p.frameIndex)); } - else - { - decoded.reset(DicomImageDecoder::Decode(instance.GetParsedDicomFile(), p.frameIndex)); - } - + *(p.targetImage) = ReturnImage(decoded); return; } @@ -2859,8 +2858,7 @@ static_cast(p.flags), p.maxStringLength); Json::FastWriter writer; - *p.targetStringToFree = CopyString(writer.write(json)); - + *p.targetStringToFree = CopyString(writer.write(json)); return; } @@ -4825,9 +4823,9 @@ } - ImageAccessor* OrthancPlugins::DecodeUnsafe(const void* dicom, - size_t size, - unsigned int frame) + ImageAccessor* OrthancPlugins::Decode(const void* dicom, + size_t size, + unsigned int frame) { boost::shared_lock lock(pimpl_->decodeImageCallbackMutex_); @@ -4846,25 +4844,6 @@ return NULL; } - - ImageAccessor* OrthancPlugins::Decode(const void* dicom, - size_t size, - unsigned int frame) - { - ImageAccessor* result = DecodeUnsafe(dicom, size, frame); - - if (result != NULL) - { - return result; - } - else - { - LOG(INFO) << "The installed image decoding plugins cannot handle an image, fallback to the built-in decoder"; - DefaultDicomImageDecoder defaultDecoder; - return defaultDecoder.Decode(dicom, size, frame); - } - } - bool OrthancPlugins::IsAllowed(HttpMethod method, const char* uri, diff -r 2910b0d30fe0 -r 4cdc875510d1 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Thu May 14 07:37:44 2020 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Thu May 14 13:31:05 2020 +0200 @@ -328,13 +328,6 @@ bool HasCustomImageDecoder(); - // Contrarily to "Decode()", this method does not fallback to the - // builtin image decoder, if no installed custom decoder can - // handle the image (it returns NULL in this case). - ImageAccessor* DecodeUnsafe(const void* dicom, - size_t size, - unsigned int frame); - virtual ImageAccessor* Decode(const void* dicom, size_t size, unsigned int frame) ORTHANC_OVERRIDE;