# HG changeset patch # User Sebastien Jodogne # Date 1451996811 -3600 # Node ID 8b0ee8d5e6d07c3354e661e6efd4657d5dca876c # Parent 50234539a0dd3d0c4956674b36625b2adc7b9944 Refactoring leading to speedups with custom image decoders diff -r 50234539a0dd -r 8b0ee8d5e6d0 NEWS --- a/NEWS Mon Jan 04 14:20:41 2016 +0100 +++ b/NEWS Tue Jan 05 13:26:51 2016 +0100 @@ -3,6 +3,7 @@ * Support of optional tags for counting resources in C-Find: 0008-0061, 0008-0062, 0020-1200, 0020-1202, 0020-1204, 0020-1206, 0020-1208, 0020-1209 +* Refactoring leading to speedups with custom image decoders (including Web viewer plugin) Version 1.0.0 (2015/12/15) diff -r 50234539a0dd -r 8b0ee8d5e6d0 OrthancServer/DefaultDicomImageDecoder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/DefaultDicomImageDecoder.h Tue Jan 05 13:26:51 2016 +0100 @@ -0,0 +1,52 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 "ParsedDicomFile.h" +#include "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 50234539a0dd -r 8b0ee8d5e6d0 OrthancServer/IDicomImageDecoder.h --- a/OrthancServer/IDicomImageDecoder.h Mon Jan 04 14:20:41 2016 +0100 +++ b/OrthancServer/IDicomImageDecoder.h Tue Jan 05 13:26:51 2016 +0100 @@ -38,8 +38,6 @@ namespace Orthanc { - class ParsedDicomFile; - class IDicomImageDecoder : public boost::noncopyable { public: @@ -47,7 +45,8 @@ { } - virtual ImageAccessor* Decode(ParsedDicomFile& dicom, + virtual ImageAccessor* Decode(const void* dicom, + size_t size, unsigned int frame) = 0; }; } diff -r 50234539a0dd -r 8b0ee8d5e6d0 OrthancServer/Internals/DicomImageDecoder.cpp --- a/OrthancServer/Internals/DicomImageDecoder.cpp Mon Jan 04 14:20:41 2016 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Tue Jan 05 13:26:51 2016 +0100 @@ -84,6 +84,8 @@ #include "../../Core/OrthancException.h" #include "../../Core/Images/Image.h" #include "../../Core/Images/ImageProcessing.h" +#include "../../Core/Images/PngWriter.h" +#include "../../Core/Images/JpegWriter.h" #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h" #include "../ToDcmtkBridge.h" #include "../FromDcmtkBridge.h" @@ -571,7 +573,8 @@ } } - return NULL; + LOG(ERROR) << "Cannot decode a DICOM image with the built-in decoder"; + throw OrthancException(ErrorCode_BadFileFormat); } @@ -653,4 +656,77 @@ throw OrthancException(ErrorCode_NotImplemented); } } + + + void DicomImageDecoder::ApplyExtractionMode(std::auto_ptr& image, + ImageExtractionMode mode) + { + if (image.get() == NULL) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + bool ok = false; + + switch (mode) + { + case ImageExtractionMode_UInt8: + ok = TruncateDecodedImage(image, PixelFormat_Grayscale8, false); + break; + + case ImageExtractionMode_UInt16: + ok = TruncateDecodedImage(image, PixelFormat_Grayscale16, false); + break; + + case ImageExtractionMode_Int16: + ok = TruncateDecodedImage(image, PixelFormat_SignedGrayscale16, false); + break; + + case ImageExtractionMode_Preview: + ok = PreviewDecodedImage(image); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + if (ok) + { + assert(image.get() != NULL); + } + else + { + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void DicomImageDecoder::ExtractPngImage(std::string& result, + std::auto_ptr& image, + ImageExtractionMode mode) + { + ApplyExtractionMode(image, mode); + + PngWriter writer; + writer.WriteToMemory(result, *image); + } + + + void DicomImageDecoder::ExtractJpegImage(std::string& result, + std::auto_ptr& image, + ImageExtractionMode mode, + uint8_t quality) + { + if (mode != ImageExtractionMode_UInt8 && + mode != ImageExtractionMode_Preview) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + ApplyExtractionMode(image, mode); + + JpegWriter writer; + writer.SetQuality(quality); + writer.WriteToMemory(result, *image); + } } diff -r 50234539a0dd -r 8b0ee8d5e6d0 OrthancServer/Internals/DicomImageDecoder.h --- a/OrthancServer/Internals/DicomImageDecoder.h Mon Jan 04 14:20:41 2016 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.h Tue Jan 05 13:26:51 2016 +0100 @@ -32,19 +32,23 @@ #pragma once -#include +#include "../ParsedDicomFile.h" -#include "../IDicomImageDecoder.h" +#include class DcmDataset; namespace Orthanc { - class DicomImageDecoder : public IDicomImageDecoder + class DicomImageDecoder : public boost::noncopyable { private: class ImageSource; + DicomImageDecoder() // This is a fully abstract class, no constructor + { + } + static ImageAccessor* DecodeUncompressedImageInternal(DcmDataset& dataset, unsigned int frame); @@ -62,14 +66,26 @@ unsigned int frame); #endif - public: - virtual ImageAccessor *Decode(ParsedDicomFile& dicom, - unsigned int frame); - static bool TruncateDecodedImage(std::auto_ptr& image, PixelFormat format, bool allowColorConversion); static bool PreviewDecodedImage(std::auto_ptr& image); + + static void ApplyExtractionMode(std::auto_ptr& image, + ImageExtractionMode mode); + + public: + static ImageAccessor *Decode(ParsedDicomFile& dicom, + unsigned int frame); + + static void ExtractPngImage(std::string& result, + std::auto_ptr& image, + ImageExtractionMode mode); + + static void ExtractJpegImage(std::string& result, + std::auto_ptr& image, + ImageExtractionMode mode, + uint8_t quality); }; } diff -r 50234539a0dd -r 8b0ee8d5e6d0 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Jan 04 14:20:41 2016 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Tue Jan 05 13:26:51 2016 +0100 @@ -266,58 +266,34 @@ class ImageToEncode { private: - IDicomImageDecoder& decoder_; - std::string format_; - std::string encoded_; - ParsedDicomFile& dicom_; - unsigned int frame_; - ImageExtractionMode mode_; + std::auto_ptr& image_; + ImageExtractionMode mode_; + std::string format_; + std::string answer_; public: - ImageToEncode(IDicomImageDecoder& decoder, - ParsedDicomFile& dicom, - unsigned int frame, + ImageToEncode(std::auto_ptr& image, ImageExtractionMode mode) : - decoder_(decoder), - dicom_(dicom), - frame_(frame), + image_(image), mode_(mode) { } - ParsedDicomFile& GetDicom() const - { - return dicom_; - } - - unsigned int GetFrame() const + void Answer(RestApiOutput& output) { - return frame_; - } - - ImageExtractionMode GetMode() const - { - return mode_; + output.AnswerBuffer(answer_, format_); } - void SetFormat(const std::string& format) + void EncodeUsingPng() { - format_ = format; + format_ = "image/png"; + DicomImageDecoder::ExtractPngImage(answer_, image_, mode_); } - std::string& GetTarget() - { - return encoded_; - } - - void Answer(RestApiOutput& output) + void EncodeUsingJpeg(uint8_t quality) { - output.AnswerBuffer(encoded_, format_); - } - - IDicomImageDecoder& GetDecoder() const - { - return decoder_; + format_ = "image/jpeg"; + DicomImageDecoder::ExtractJpegImage(answer_, image_, mode_, quality); } }; @@ -336,9 +312,7 @@ { assert(type == "image"); assert(subtype == "png"); - image_.GetDicom().ExtractPngImage(image_.GetTarget(), image_.GetDecoder(), - image_.GetFrame(), image_.GetMode()); - image_.SetFormat("image/png"); + image_.EncodeUsingPng(); } }; @@ -377,9 +351,7 @@ { assert(type == "image"); assert(subtype == "jpeg"); - image_.GetDicom().ExtractJpegImage(image_.GetTarget(), image_.GetDecoder(), - image_.GetFrame(), image_.GetMode(), quality_); - image_.SetFormat("image/jpeg"); + image_.EncodeUsingJpeg(quality_); } }; } @@ -406,17 +378,17 @@ std::string dicomContent; context.ReadFile(dicomContent, publicId, FileContentType_Dicom); - ParsedDicomFile dicom(dicomContent); - try { #if ORTHANC_PLUGINS_ENABLED == 1 IDicomImageDecoder& decoder = context.GetPlugins(); #else - DicomImageDecoder decoder; // This is Orthanc's built-in decoder + DefaultDicomImageDecoder decoder; // This is Orthanc's built-in decoder #endif - ImageToEncode image(decoder, dicom, frame, mode); + std::auto_ptr decoded(decoder.Decode(dicomContent.c_str(), dicomContent.size(), frame)); + + ImageToEncode image(decoded, mode); HttpContentNegociation negociation; EncodePng png(image); negociation.Register("image/png", png); @@ -471,11 +443,10 @@ #if ORTHANC_PLUGINS_ENABLED == 1 IDicomImageDecoder& decoder = context.GetPlugins(); #else - DicomImageDecoder decoder; // This is Orthanc's built-in decoder + DefaultDicomImageDecoder decoder; // This is Orthanc's built-in decoder #endif - ParsedDicomFile dicom(dicomContent); - std::auto_ptr decoded(dicom.ExtractImage(decoder, frame)); + std::auto_ptr decoded(decoder.Decode(dicomContent.c_str(), dicomContent.size(), frame)); std::string result; decoded->ToMatlabString(result); diff -r 50234539a0dd -r 8b0ee8d5e6d0 OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Mon Jan 04 14:20:41 2016 +0100 +++ b/OrthancServer/ParsedDicomFile.cpp Tue Jan 05 13:26:51 2016 +0100 @@ -84,12 +84,8 @@ #include "ServerToolbox.h" #include "FromDcmtkBridge.h" #include "ToDcmtkBridge.h" -#include "Internals/DicomImageDecoder.h" -#include "../Core/DicomFormat/DicomIntegerPixelAccessor.h" -#include "../Core/Images/JpegWriter.h" #include "../Core/Images/JpegReader.h" #include "../Core/Images/PngReader.h" -#include "../Core/Images/PngWriter.h" #include "../Core/Logging.h" #include "../Core/OrthancException.h" #include "../Core/Toolbox.h" @@ -1047,99 +1043,6 @@ } - ImageAccessor* ParsedDicomFile::ExtractImage(IDicomImageDecoder& decoder, - unsigned int frame) - { - std::auto_ptr decoded(decoder.Decode(*this, frame)); - - if (decoded.get() == NULL) - { - LOG(ERROR) << "Cannot decode a DICOM image"; - throw OrthancException(ErrorCode_BadFileFormat); - } - else - { - return decoded.release(); - } - } - - - ImageAccessor* ParsedDicomFile::ExtractImage(IDicomImageDecoder& decoder, - unsigned int frame, - ImageExtractionMode mode) - { - std::auto_ptr decoded(ExtractImage(decoder, frame)); - - bool ok = false; - - switch (mode) - { - case ImageExtractionMode_UInt8: - ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_Grayscale8, false); - break; - - case ImageExtractionMode_UInt16: - ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_Grayscale16, false); - break; - - case ImageExtractionMode_Int16: - ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_SignedGrayscale16, false); - break; - - case ImageExtractionMode_Preview: - ok = DicomImageDecoder::PreviewDecodedImage(decoded); - break; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - if (ok) - { - assert(decoded.get() != NULL); - return decoded.release(); - } - else - { - throw OrthancException(ErrorCode_NotImplemented); - } - } - - - void ParsedDicomFile::ExtractPngImage(std::string& result, - IDicomImageDecoder& decoder, - unsigned int frame, - ImageExtractionMode mode) - { - std::auto_ptr decoded(ExtractImage(decoder, frame, mode)); - assert(decoded.get() != NULL); - - PngWriter writer; - writer.WriteToMemory(result, *decoded); - } - - - void ParsedDicomFile::ExtractJpegImage(std::string& result, - IDicomImageDecoder& decoder, - unsigned int frame, - ImageExtractionMode mode, - uint8_t quality) - { - if (mode != ImageExtractionMode_UInt8 && - mode != ImageExtractionMode_Preview) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - std::auto_ptr decoded(ExtractImage(decoder, frame, mode)); - assert(decoded.get() != NULL); - - JpegWriter writer; - writer.SetQuality(quality); - writer.WriteToMemory(result, *decoded); - } - - Encoding ParsedDicomFile::GetEncoding() const { return FromDcmtkBridge::DetectEncoding(*pimpl_->file_->getDataset()); diff -r 50234539a0dd -r 8b0ee8d5e6d0 OrthancServer/ParsedDicomFile.h --- a/OrthancServer/ParsedDicomFile.h Mon Jan 04 14:20:41 2016 +0100 +++ b/OrthancServer/ParsedDicomFile.h Tue Jan 05 13:26:51 2016 +0100 @@ -33,9 +33,9 @@ #pragma once #include "../Core/DicomFormat/DicomInstanceHasher.h" +#include "../Core/Images/ImageAccessor.h" #include "../Core/IDynamicObject.h" #include "../Core/RestApi/RestApiOutput.h" -#include "IDicomImageDecoder.h" #include "ServerEnumerations.h" class DcmDataset; @@ -126,24 +126,6 @@ void EmbedImage(const std::string& mime, const std::string& content); - ImageAccessor* ExtractImage(IDicomImageDecoder& decoder, - unsigned int frame); - - ImageAccessor* ExtractImage(IDicomImageDecoder& decoder, - unsigned int frame, - ImageExtractionMode mode); - - void ExtractPngImage(std::string& result, - IDicomImageDecoder& decoder, - unsigned int frame, - ImageExtractionMode mode); - - void ExtractJpegImage(std::string& result, - IDicomImageDecoder& decoder, - unsigned int frame, - ImageExtractionMode mode, - uint8_t quality); - Encoding GetEncoding() const; void SetEncoding(Encoding encoding); diff -r 50234539a0dd -r 8b0ee8d5e6d0 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Mon Jan 04 14:20:41 2016 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Jan 05 13:26:51 2016 +0100 @@ -58,6 +58,7 @@ #include "../../Core/Images/JpegReader.h" #include "../../Core/Images/JpegWriter.h" #include "../../Core/Images/ImageProcessing.h" +#include "../../OrthancServer/DefaultDicomImageDecoder.h" #include "PluginsEnumerations.h" #include @@ -1309,8 +1310,7 @@ case OrthancPluginImageFormat_Dicom: { - ParsedDicomFile dicom(p.data, p.size); - image.reset(Decode(dicom, 0)); + image.reset(Decode(p.data, p.size, 0)); break; } @@ -1563,8 +1563,7 @@ case _OrthancPluginService_DecodeDicomImage: { - ParsedDicomFile dicom(p.constBuffer, p.bufferSize); - result.reset(Decode(dicom, p.frameIndex)); + result.reset(Decode(p.constBuffer, p.bufferSize, p.frameIndex)); break; } @@ -2376,18 +2375,16 @@ } - ImageAccessor* OrthancPlugins::Decode(ParsedDicomFile& dicom, + ImageAccessor* OrthancPlugins::Decode(const void* dicom, + size_t size, unsigned int frame) { { boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_); if (pimpl_->decodeImageCallback_ != NULL) { - std::string s; - dicom.SaveToMemoryBuffer(s); - OrthancPluginImage* pluginImage = NULL; - if (pimpl_->decodeImageCallback_(&pluginImage, s.c_str(), s.size(), frame) == OrthancPluginErrorCode_Success && + if (pimpl_->decodeImageCallback_(&pluginImage, dicom, size, frame) == OrthancPluginErrorCode_Success && pluginImage != NULL) { return reinterpret_cast(pluginImage); @@ -2397,7 +2394,7 @@ } } - DicomImageDecoder defaultDecoder; - return defaultDecoder.Decode(dicom, frame); + DefaultDicomImageDecoder defaultDecoder; + return defaultDecoder.Decode(dicom, size, frame); } } diff -r 50234539a0dd -r 8b0ee8d5e6d0 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Mon Jan 04 14:20:41 2016 +0100 +++ b/Plugins/Engine/OrthancPlugins.h Tue Jan 05 13:26:51 2016 +0100 @@ -234,7 +234,8 @@ virtual IWorklistRequestHandler* ConstructWorklistRequestHandler(); - virtual ImageAccessor* Decode(ParsedDicomFile& dicom, + virtual ImageAccessor* Decode(const void* dicom, + size_t size, unsigned int frame); }; }