Mercurial > hg > orthanc-stone
changeset 1484:121d01aa328e
SeriesThumbnailsLoader working on raw dicom files
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 22 Jun 2020 17:46:40 +0200 |
parents | 6abd819aa534 |
children | 60be4627ae52 |
files | Framework/Loaders/DicomResourcesLoader.cpp Framework/Loaders/DicomResourcesLoader.h Framework/Loaders/DicomSource.cpp Framework/Loaders/OracleScheduler.cpp Framework/Loaders/SeriesFramesLoader.cpp Framework/Loaders/SeriesThumbnailsLoader.cpp Framework/Loaders/SeriesThumbnailsLoader.h Framework/Oracle/GenericOracleRunner.cpp Framework/Oracle/ParseDicomFromFileCommand.h Framework/Oracle/ParseDicomFromWadoCommand.cpp Framework/Oracle/ParseDicomFromWadoCommand.h Framework/Oracle/ParseDicomSuccessMessage.h Framework/Oracle/WebAssemblyOracle.cpp Framework/Oracle/WebAssemblyOracle.h Framework/Toolbox/ImageToolbox.cpp Framework/Toolbox/ImageToolbox.h |
diffstat | 16 files changed, 391 insertions(+), 54 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Loaders/DicomResourcesLoader.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Loaders/DicomResourcesLoader.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -27,6 +27,7 @@ #if ORTHANC_ENABLE_DCMTK == 1 # include "../Oracle/ParseDicomFromFileCommand.h" +# include <DicomParsing/ParsedDicomDir.h> # include <DicomParsing/ParsedDicomFile.h> #endif @@ -864,7 +865,7 @@ boost::shared_ptr<Orthanc::IDynamicObject> protection(userPayload); #if ORTHANC_ENABLE_DCMTK == 1 - std::unique_ptr<ParseDicomFromFileCommand> command(new ParseDicomFromFileCommand(path)); + std::unique_ptr<ParseDicomFromFileCommand> command(new ParseDicomFromFileCommand(source, path)); command->SetPixelDataIncluded(includePixelData); command->AcquirePayload(new Handler(shared_from_this(), target, priority, source, protection));
--- a/Framework/Loaders/DicomResourcesLoader.h Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Loaders/DicomResourcesLoader.h Mon Jun 22 17:46:40 2020 +0200 @@ -27,11 +27,6 @@ # error The macro ORTHANC_ENABLE_DCMTK must be defined #endif -#if ORTHANC_ENABLE_DCMTK == 1 -# include "../Oracle/ParseDicomFromFileCommand.h" -# include <DicomParsing/ParsedDicomDir.h> -#endif - #include "../Oracle/HttpCommand.h" #include "../Oracle/OracleCommandExceptionMessage.h" #include "../Oracle/OrthancRestApiCommand.h" @@ -41,8 +36,19 @@ #include "LoadedDicomResources.h" #include "OracleScheduler.h" +namespace Orthanc +{ +#if ORTHANC_ENABLE_DCMTK == 1 + class ParsedDicomDir; +#endif +} + namespace OrthancStone { +#if ORTHANC_ENABLE_DCMTK == 1 + class ParseDicomFromFileCommand; +#endif + class DicomResourcesLoader : public ObserverBase<DicomResourcesLoader>, public IObservable
--- a/Framework/Loaders/DicomSource.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Loaders/DicomSource.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -190,11 +190,11 @@ { h[it->first] = it->second; } - + Json::Value body = Json::objectValue; body["Uri"] = uri; body["Arguments"] = args; - body["Headers"] = h; + body["HttpHeaders"] = h; std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); command->SetMethod(Orthanc::HttpMethod_Post);
--- a/Framework/Loaders/OracleScheduler.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Loaders/OracleScheduler.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -411,7 +411,7 @@ ParseDicomSuccessMessage bis( dynamic_cast<const OracleCommandBase&>(payload.GetOriginalCommand()), - message.GetDicom(), message.GetFileSize(), message.HasPixelData()); + message.GetSource(), message.GetDicom(), message.GetFileSize(), message.HasPixelData()); emitter_.EmitMessage(payload.GetOriginalReceiver(), bis); } #endif
--- a/Framework/Loaders/SeriesFramesLoader.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Loaders/SeriesFramesLoader.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -415,7 +415,7 @@ std::string file; if (dicomDir_->LookupStringValue(file, sopInstanceUid, Orthanc::DICOM_TAG_REFERENCED_FILE_ID)) { - std::unique_ptr<ParseDicomFromFileCommand> command(new ParseDicomFromFileCommand(dicomDirPath_, file)); + std::unique_ptr<ParseDicomFromFileCommand> command(new ParseDicomFromFileCommand(source, dicomDirPath_, file)); command->SetPixelDataIncluded(true); command->AcquirePayload(new Payload(source, index, sopInstanceUid, quality, protection.release())); @@ -474,7 +474,7 @@ const std::map<std::string, std::string> empty; std::unique_ptr<ParseDicomFromWadoCommand> command( - new ParseDicomFromWadoCommand(sopInstanceUid, source.CreateDicomWebCommand(uri, empty, empty, NULL))); + new ParseDicomFromWadoCommand(source, sopInstanceUid, source.CreateDicomWebCommand(uri, empty, empty, NULL))); command->AcquirePayload(payload.release()); {
--- a/Framework/Loaders/SeriesThumbnailsLoader.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Loaders/SeriesThumbnailsLoader.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -21,8 +21,13 @@ #include "SeriesThumbnailsLoader.h" +#include "LoadedDicomResources.h" +#include "../Oracle/ParseDicomFromWadoCommand.h" +#include "../Toolbox/ImageToolbox.h" + #include <DicomFormat/DicomMap.h> #include <DicomFormat/DicomInstanceHasher.h> +#include <Images/Image.h> #include <Images/ImageProcessing.h> #include <Images/JpegReader.h> #include <Images/JpegWriter.h> @@ -30,6 +35,12 @@ #include <boost/algorithm/string/predicate.hpp> +#if ORTHANC_ENABLE_DCMTK == 1 +# include <DicomParsing/ParsedDicomFile.h> +# include <DicomParsing/Internals/DicomImageDecoder.h> +#endif + + static const unsigned int JPEG_QUALITY = 70; // Only used for Orthanc source namespace OrthancStone @@ -111,7 +122,7 @@ assert(thumbnail != NULL); std::unique_ptr<Thumbnail> protection(thumbnail); - + Thumbnails::iterator found = thumbnails_.find(seriesInstanceUid); if (found == thumbnails_.end()) { @@ -120,10 +131,21 @@ else { assert(found->second != NULL); - delete found->second; - found->second = protection.release(); + if (protection->GetType() == SeriesThumbnailType_NotLoaded || + protection->GetType() == SeriesThumbnailType_Unsupported) + { + // Don't replace an old entry if the current one is worse + return; + } + else + { + delete found->second; + found->second = protection.release(); + } } + LOG(INFO) << "Thumbnail updated for series: " << seriesInstanceUid << ": " << thumbnail->GetType(); + SuccessMessage message(*this, source, studyInstanceUid, seriesInstanceUid, *thumbnail); BroadcastMessage(message); } @@ -284,7 +306,7 @@ std::map<std::string, std::string> arguments, headers; arguments["0020000D"] = GetStudyInstanceUid(); arguments["0020000E"] = GetSeriesInstanceUid(); - arguments["includefield"] = "00080016"; + arguments["includefield"] = "00080016"; // SOP Class UID std::unique_ptr<IOracleCommand> command( GetSource().CreateDicomWebCommand( @@ -419,6 +441,52 @@ }; +#if ORTHANC_ENABLE_DCMTK == 1 + class SeriesThumbnailsLoader::SelectDicomWebInstanceHandler : public SeriesThumbnailsLoader::Handler + { + public: + SelectDicomWebInstanceHandler(boost::shared_ptr<SeriesThumbnailsLoader> loader, + const DicomSource& source, + const std::string& studyInstanceUid, + const std::string& seriesInstanceUid) : + Handler(loader, source, studyInstanceUid, seriesInstanceUid) + { + } + + virtual void HandleSuccess(const std::string& body, + const std::map<std::string, std::string>& headers) + { + Json::Value json; + Json::Reader reader; + if (!reader.parse(body, json) || + json.type() != Json::arrayValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + LoadedDicomResources instances(Orthanc::DICOM_TAG_SOP_INSTANCE_UID); + instances.AddFromDicomWeb(json); + + std::string sopInstanceUid; + if (instances.GetSize() == 0 || + !instances.GetResource(0).LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false)) + { + LOG(ERROR) << "Series without an instance: " << GetSeriesInstanceUid(); + } + else + { + GetLoader()->Schedule( + ParseDicomFromWadoCommand::Create( + GetSource(), GetStudyInstanceUid(), GetSeriesInstanceUid(), sopInstanceUid, false, + Orthanc::DicomTransferSyntax_LittleEndianExplicit /* useless, as no transcoding */, + new ThumbnailInformation( + GetSource(), GetStudyInstanceUid(), GetSeriesInstanceUid()))); + } + } + }; +#endif + + void SeriesThumbnailsLoader::Schedule(IOracleCommand* command) { std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); @@ -443,6 +511,7 @@ void SeriesThumbnailsLoader::Handle(const GetOrthancImageCommand::SuccessMessage& message) { assert(message.GetOrigin().HasPayload()); + const ThumbnailInformation& info = dynamic_cast<ThumbnailInformation&>(message.GetOrigin().GetPayload()); std::unique_ptr<Orthanc::ImageAccessor> resized(Orthanc::ImageProcessing::FitSize(message.GetImage(), width_, height_)); @@ -451,12 +520,91 @@ writer.SetQuality(JPEG_QUALITY); writer.WriteToMemory(jpeg, *resized); - const ThumbnailInformation& info = dynamic_cast<ThumbnailInformation&>(message.GetOrigin().GetPayload()); AcquireThumbnail(info.GetDicomSource(), info.GetStudyInstanceUid(), info.GetSeriesInstanceUid(), new Thumbnail(jpeg, Orthanc::MIME_JPEG)); } +#if ORTHANC_ENABLE_DCMTK == 1 + void SeriesThumbnailsLoader::Handle(const ParseDicomSuccessMessage& message) + { + assert(message.GetOrigin().HasPayload()); + const ParseDicomFromWadoCommand& origin = + dynamic_cast<const ParseDicomFromWadoCommand&>(message.GetOrigin()); + const ThumbnailInformation& info = dynamic_cast<ThumbnailInformation&>(origin.GetPayload()); + + std::string tmp; + Orthanc::DicomTransferSyntax transferSyntax; + if (!message.GetDicom().LookupTransferSyntax(tmp)) + { + + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, + "DICOM instance without a transfer syntax: " + origin.GetSopInstanceUid()); + } + else if (!Orthanc::LookupTransferSyntax(transferSyntax, tmp) || + !ImageToolbox::IsDecodingSupported(transferSyntax)) + { + LOG(INFO) << "Asking the DICOMweb server to transcode, " + << "as I don't support this transfer syntax: " << tmp; + + Schedule(ParseDicomFromWadoCommand::Create( + origin.GetSource(), info.GetStudyInstanceUid(), info.GetSeriesInstanceUid(), + origin.GetSopInstanceUid(), true, Orthanc::DicomTransferSyntax_LittleEndianExplicit, + new ThumbnailInformation( + origin.GetSource(), info.GetStudyInstanceUid(), info.GetSeriesInstanceUid()))); + } + else + { + std::unique_ptr<Orthanc::ImageAccessor> frame( + Orthanc::DicomImageDecoder::Decode(message.GetDicom(), 0)); + + std::unique_ptr<Orthanc::ImageAccessor> thumbnail; + + if (frame->GetFormat() == Orthanc::PixelFormat_RGB24) + { + thumbnail.reset(Orthanc::ImageProcessing::FitSize(*frame, width_, height_)); + } + else + { + const unsigned int width = frame->GetWidth(); + const unsigned int height = frame->GetHeight(); + + std::unique_ptr<Orthanc::ImageAccessor> converted( + new Orthanc::Image(Orthanc::PixelFormat_Float32, width, height, false)); + Orthanc::ImageProcessing::Convert(*converted, *frame); + + std::unique_ptr<Orthanc::ImageAccessor> resized( + Orthanc::ImageProcessing::FitSize(*converted, width, height)); + + float minValue, maxValue; + Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *resized); + if (minValue + 0.01f < maxValue) + { + Orthanc::ImageProcessing::ShiftScale(*resized, -minValue, 255.0f / (maxValue - minValue), false); + } + else + { + Orthanc::ImageProcessing::Set(*resized, 0); + } + + converted.reset(NULL); + + thumbnail.reset(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); + Orthanc::ImageProcessing::Convert(*thumbnail, *resized); + } + + std::string jpeg; + Orthanc::JpegWriter writer; + writer.SetQuality(JPEG_QUALITY); + writer.WriteToMemory(jpeg, *thumbnail); + + AcquireThumbnail(info.GetDicomSource(), info.GetStudyInstanceUid(), + info.GetSeriesInstanceUid(), new Thumbnail(jpeg, Orthanc::MIME_JPEG)); + } + } +#endif + + void SeriesThumbnailsLoader::Handle(const OracleCommandExceptionMessage& message) { const OracleCommandBase& command = dynamic_cast<const OracleCommandBase&>(message.GetOrigin()); @@ -495,6 +643,11 @@ result->Register<HttpCommand::SuccessMessage>(stone.GetOracleObservable(), &SeriesThumbnailsLoader::Handle); result->Register<OracleCommandExceptionMessage>(stone.GetOracleObservable(), &SeriesThumbnailsLoader::Handle); result->Register<OrthancRestApiCommand::SuccessMessage>(stone.GetOracleObservable(), &SeriesThumbnailsLoader::Handle); + +#if ORTHANC_ENABLE_DCMTK == 1 + result->Register<ParseDicomSuccessMessage>(stone.GetOracleObservable(), &SeriesThumbnailsLoader::Handle); +#endif + return result; } @@ -556,33 +709,48 @@ { return; } - + if (source.IsDicomWeb()) { if (!source.HasDicomWebRendered()) { - // TODO - Could use DCMTK here - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, - "DICOMweb server is not able to generate renderings of DICOM series"); +#if ORTHANC_ENABLE_DCMTK == 1 + // Issue a QIDO-RS request to select one of the instances in the series + std::map<std::string, std::string> arguments, headers; + arguments["0020000D"] = studyInstanceUid; + arguments["0020000E"] = seriesInstanceUid; + arguments["includefield"] = "00080018"; // SOP Instance UID is mandatory + + std::unique_ptr<IOracleCommand> command( + source.CreateDicomWebCommand( + "/instances", arguments, headers, new SelectDicomWebInstanceHandler( + GetSharedObserver(), source, studyInstanceUid, seriesInstanceUid))); + Schedule(command.release()); +#else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, + "Stone of Orthanc was built without support to decode DICOM images"); +#endif } - - const std::string uri = ("/studies/" + studyInstanceUid + - "/series/" + seriesInstanceUid + "/rendered"); + else + { + const std::string uri = ("/studies/" + studyInstanceUid + + "/series/" + seriesInstanceUid + "/rendered"); - std::map<std::string, std::string> arguments, headers; - arguments["viewport"] = (boost::lexical_cast<std::string>(width_) + "," + - boost::lexical_cast<std::string>(height_)); + std::map<std::string, std::string> arguments, headers; + arguments["viewport"] = (boost::lexical_cast<std::string>(width_) + "," + + boost::lexical_cast<std::string>(height_)); - // Needed to set this header explicitly, as long as emscripten - // does not include macro "EMSCRIPTEN_FETCH_RESPONSE_HEADERS" - // https://github.com/emscripten-core/emscripten/pull/8486 - headers["Accept"] = Orthanc::MIME_JPEG; + // Needed to set this header explicitly, as long as emscripten + // does not include macro "EMSCRIPTEN_FETCH_RESPONSE_HEADERS" + // https://github.com/emscripten-core/emscripten/pull/8486 + headers["Accept"] = Orthanc::MIME_JPEG; - std::unique_ptr<IOracleCommand> command( - source.CreateDicomWebCommand( - uri, arguments, headers, new DicomWebThumbnailHandler( - shared_from_this(), source, studyInstanceUid, seriesInstanceUid))); - Schedule(command.release()); + std::unique_ptr<IOracleCommand> command( + source.CreateDicomWebCommand( + uri, arguments, headers, new DicomWebThumbnailHandler( + GetSharedObserver(), source, studyInstanceUid, seriesInstanceUid))); + Schedule(command.release()); + } scheduledSeries_.insert(seriesInstanceUid); } @@ -594,7 +762,7 @@ std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); command->SetUri("/series/" + hasher.HashSeries()); command->AcquirePayload(new SelectOrthancInstanceHandler( - shared_from_this(), source, studyInstanceUid, seriesInstanceUid)); + GetSharedObserver(), source, studyInstanceUid, seriesInstanceUid)); Schedule(command.release()); scheduledSeries_.insert(seriesInstanceUid);
--- a/Framework/Loaders/SeriesThumbnailsLoader.h Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Loaders/SeriesThumbnailsLoader.h Mon Jun 22 17:46:40 2020 +0200 @@ -21,6 +21,13 @@ #pragma once +#include "../OrthancStone.h" + +#if !defined(ORTHANC_ENABLE_DCMTK) +# error Macro ORTHANC_ENABLE_DCMTK must be defined +#endif + + #include "../Oracle/GetOrthancImageCommand.h" #include "../Oracle/HttpCommand.h" #include "../Oracle/OracleCommandExceptionMessage.h" @@ -141,6 +148,10 @@ class ThumbnailInformation; class OrthancSopClassHandler; class SelectOrthancInstanceHandler; + +#if ORTHANC_ENABLE_DCMTK == 1 + class SelectDicomWebInstanceHandler; +#endif // Maps a "Series Instance UID" to a thumbnail typedef std::map<std::string, Thumbnail*> Thumbnails; @@ -165,6 +176,10 @@ void Handle(const GetOrthancImageCommand::SuccessMessage& message); +#if ORTHANC_ENABLE_DCMTK == 1 + void Handle(const ParseDicomSuccessMessage& message); +#endif + void Handle(const OracleCommandExceptionMessage& message); SeriesThumbnailsLoader(ILoadersContext& context,
--- a/Framework/Oracle/GenericOracleRunner.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Oracle/GenericOracleRunner.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -345,7 +345,8 @@ reader.HasPixelData())) { // Reuse the DICOM file from the cache - ParseDicomSuccessMessage message(command, reader.GetDicom(), reader.GetFileSize(), reader.HasPixelData()); + ParseDicomSuccessMessage message(command, command.GetSource(), reader.GetDicom(), + reader.GetFileSize(), reader.HasPixelData()); emitter.EmitMessage(receiver, message); return; } @@ -362,7 +363,8 @@ { ParseDicomSuccessMessage message - (command, *parsed, static_cast<size_t>(fileSize), command.IsPixelDataIncluded()); + (command, command.GetSource(), *parsed, + static_cast<size_t>(fileSize), command.IsPixelDataIncluded()); emitter.EmitMessage(receiver, message); } @@ -393,7 +395,8 @@ reader.HasPixelData()) { // Reuse the DICOM file from the cache - ParseDicomSuccessMessage message(command, reader.GetDicom(), reader.GetFileSize(), reader.HasPixelData()); + ParseDicomSuccessMessage message(command, command.GetSource(), reader.GetDicom(), + reader.GetFileSize(), reader.HasPixelData()); emitter.EmitMessage(receiver, message); return; } @@ -421,7 +424,7 @@ std::unique_ptr<Orthanc::ParsedDicomFile> parsed(ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, answerHeaders)); { - ParseDicomSuccessMessage message(command, *parsed, fileSize, + ParseDicomSuccessMessage message(command, command.GetSource(), *parsed, fileSize, true /* pixel data always is included in WADO-RS */); emitter.EmitMessage(receiver, message); }
--- a/Framework/Oracle/ParseDicomFromFileCommand.h Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Oracle/ParseDicomFromFileCommand.h Mon Jun 22 17:46:40 2020 +0200 @@ -22,6 +22,7 @@ #pragma once #include "OracleCommandBase.h" +#include "../Loaders/DicomSource.h" #include <string> @@ -30,24 +31,30 @@ class ParseDicomFromFileCommand : public OracleCommandBase { private: + DicomSource source_; std::string path_; bool pixelDataIncluded_; ParseDicomFromFileCommand(const ParseDicomFromFileCommand& other) : + source_(other.source_), path_(other.path_), pixelDataIncluded_(other.pixelDataIncluded_) { } public: - ParseDicomFromFileCommand(const std::string& path) : + ParseDicomFromFileCommand(const DicomSource& source, + const std::string& path) : + source_(source), path_(path), pixelDataIncluded_(true) { } - ParseDicomFromFileCommand(const std::string& dicomDirPath, + ParseDicomFromFileCommand(const DicomSource& source, + const std::string& dicomDirPath, const std::string& file) : + source_(source), path_(GetDicomDirPath(dicomDirPath, file)), pixelDataIncluded_(true) { @@ -66,6 +73,11 @@ return new ParseDicomFromFileCommand(*this); } + const DicomSource& GetSource() const + { + return source_; + } + const std::string& GetPath() const { return path_;
--- a/Framework/Oracle/ParseDicomFromWadoCommand.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Oracle/ParseDicomFromWadoCommand.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -25,8 +25,10 @@ namespace OrthancStone { - ParseDicomFromWadoCommand::ParseDicomFromWadoCommand(const std::string& sopInstanceUid, + ParseDicomFromWadoCommand::ParseDicomFromWadoCommand(const DicomSource& source, + const std::string& sopInstanceUid, IOracleCommand* restCommand) : + source_(source), sopInstanceUid_(sopInstanceUid), restCommand_(restCommand) { @@ -46,7 +48,7 @@ IOracleCommand* ParseDicomFromWadoCommand::Clone() const { assert(restCommand_.get() != NULL); - return new ParseDicomFromWadoCommand(sopInstanceUid_, restCommand_->Clone()); + return new ParseDicomFromWadoCommand(source_, sopInstanceUid_, restCommand_->Clone()); } @@ -55,4 +57,47 @@ assert(restCommand_.get() != NULL); return *restCommand_; } + + + ParseDicomFromWadoCommand* ParseDicomFromWadoCommand::Create( + const DicomSource& source, + const std::string& studyInstanceUid, + const std::string& seriesInstanceUid, + const std::string& sopInstanceUid, + bool transcode, + Orthanc::DicomTransferSyntax transferSyntax, + Orthanc::IDynamicObject* payload) + { + std::unique_ptr<Orthanc::IDynamicObject> protection(payload); + + const std::string uri = ("/studies/" + studyInstanceUid + + "/series/" + seriesInstanceUid + + "/instances/" + sopInstanceUid); + + std::string s; + if (transcode) + { + s = Orthanc::GetTransferSyntaxUid(transferSyntax); + } + else + { + s = "*"; // No transcoding, keep source transfer syntax + } + + std::map<std::string, std::string> arguments, headers; + headers["Accept"] = ("multipart/related; type=\"application/dicom\"; transfer-syntax=" + s); + + std::unique_ptr<IOracleCommand> rest( + source.CreateDicomWebCommand(uri, arguments, headers, NULL)); + + std::unique_ptr<ParseDicomFromWadoCommand> command( + new ParseDicomFromWadoCommand(source, sopInstanceUid, rest.release())); + + if (protection.get() != NULL) + { + command->AcquirePayload(protection.release()); + } + + return command.release(); + } }
--- a/Framework/Oracle/ParseDicomFromWadoCommand.h Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Oracle/ParseDicomFromWadoCommand.h Mon Jun 22 17:46:40 2020 +0200 @@ -22,6 +22,9 @@ #pragma once #include "OracleCommandBase.h" +#include "../Loaders/DicomSource.h" + +#include <Enumerations.h> #include <string> @@ -30,11 +33,13 @@ class ParseDicomFromWadoCommand : public OracleCommandBase { private: - std::string sopInstanceUid_; + DicomSource source_; + std::string sopInstanceUid_; std::unique_ptr<IOracleCommand> restCommand_; public: - ParseDicomFromWadoCommand(const std::string& sopInstanceUid, + ParseDicomFromWadoCommand(const DicomSource& source, + const std::string& sopInstanceUid, IOracleCommand* restCommand); virtual Type GetType() const @@ -44,11 +49,24 @@ virtual IOracleCommand* Clone() const; + const DicomSource& GetSource() const + { + return source_; + } + const std::string& GetSopInstanceUid() const { return sopInstanceUid_; } const IOracleCommand& GetRestCommand() const; + + static ParseDicomFromWadoCommand* Create(const DicomSource& source, + const std::string& studyInstanceUid, + const std::string& seriesInstanceUid, + const std::string& sopInstanceUid, + bool transcode, + Orthanc::DicomTransferSyntax transferSyntax, + Orthanc::IDynamicObject* payload); }; }
--- a/Framework/Oracle/ParseDicomSuccessMessage.h Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Oracle/ParseDicomSuccessMessage.h Mon Jun 22 17:46:40 2020 +0200 @@ -44,27 +44,37 @@ namespace OrthancStone { + class DicomSource; + class ParseDicomSuccessMessage : public OriginMessage<OracleCommandBase> { ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); private: + const DicomSource& source_; Orthanc::ParsedDicomFile& dicom_; size_t fileSize_; bool hasPixelData_; public: ParseDicomSuccessMessage(const OracleCommandBase& command, + const DicomSource& source, Orthanc::ParsedDicomFile& dicom, size_t fileSize, bool hasPixelData) : OriginMessage(command), + source_(source), dicom_(dicom), fileSize_(fileSize), hasPixelData_(hasPixelData) { } - + + const DicomSource& GetSource() const + { + return source_; + } + Orthanc::ParsedDicomFile& GetDicom() const { return dicom_;
--- a/Framework/Oracle/WebAssemblyOracle.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Oracle/WebAssemblyOracle.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -29,6 +29,12 @@ static unsigned int BUCKET_SOP = 1; #endif +#include "GetOrthancImageCommand.h" +#include "GetOrthancWebViewerJpegCommand.h" +#include "HttpCommand.h" +#include "OrthancRestApiCommand.h" +#include "ParseDicomFromWadoCommand.h" + #include <OrthancException.h> #include <Toolbox.h> @@ -305,7 +311,7 @@ (ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, headers)); { - ParseDicomSuccessMessage message(command, *dicom, fileSize, true); + ParseDicomSuccessMessage message(command, command.GetSource(), *dicom, fileSize, true); context->EmitMessage(message); } @@ -674,7 +680,7 @@ reader.HasPixelData()) { // Reuse the DICOM file from the cache - ParseDicomSuccessMessage message(*protection, reader.GetDicom(), + ParseDicomSuccessMessage message(*protection, protection->GetSource(), reader.GetDicom(), reader.GetFileSize(), reader.HasPixelData()); EmitMessage(receiver, message); return;
--- a/Framework/Oracle/WebAssemblyOracle.h Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Oracle/WebAssemblyOracle.h Mon Jun 22 17:46:40 2020 +0200 @@ -32,12 +32,8 @@ #endif #include "../Messages/IObservable.h" -#include "GetOrthancImageCommand.h" -#include "GetOrthancWebViewerJpegCommand.h" -#include "HttpCommand.h" +#include "../Messages/IMessageEmitter.h" #include "IOracle.h" -#include "OrthancRestApiCommand.h" -#include "ParseDicomFromWadoCommand.h" #if ORTHANC_ENABLE_DCMTK == 1 # include "../Toolbox/ParsedDicomCache.h" @@ -48,6 +44,12 @@ namespace OrthancStone { + class GetOrthancImageCommand; + class GetOrthancWebViewerJpegCommand; + class HttpCommand; + class OrthancRestApiCommand; + class ParseDicomFromWadoCommand; + class WebAssemblyOracle : public IOracle, public IMessageEmitter
--- a/Framework/Toolbox/ImageToolbox.cpp Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Toolbox/ImageToolbox.cpp Mon Jun 22 17:46:40 2020 +0200 @@ -18,6 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ +#include "../OrthancStone.h" #include "ImageToolbox.h" #include "../StoneException.h" @@ -33,6 +34,19 @@ #include <vector> +#if !defined(ORTHANC_ENABLE_DCMTK) +# error ORTHANC_ENABLE_DCMTK is not defined +#endif + +#if !defined(ORTHANC_ENABLE_DCMTK_JPEG) +# error ORTHANC_ENABLE_DCMTK_JPEG is not defined +#endif + +#if !defined(ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS) +# error ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS is not defined +#endif + + namespace OrthancStone { namespace @@ -289,4 +303,35 @@ ss << "total pix. count: " << pixCount << "\n"; s = ss.str(); } + + + bool ImageToolbox::IsDecodingSupported(Orthanc::DicomTransferSyntax& transferSyntax) + { + switch (transferSyntax) + { + case Orthanc::DicomTransferSyntax_LittleEndianImplicit: + case Orthanc::DicomTransferSyntax_LittleEndianExplicit: + case Orthanc::DicomTransferSyntax_DeflatedLittleEndianExplicit: + case Orthanc::DicomTransferSyntax_BigEndianExplicit: + case Orthanc::DicomTransferSyntax_RLELossless: + return true; + +#if (ORTHANC_ENABLE_DCMTK == 1) && (ORTHANC_ENABLE_DCMTK_JPEG == 1) + case Orthanc::DicomTransferSyntax_JPEGProcess1: + case Orthanc::DicomTransferSyntax_JPEGProcess2_4: + case Orthanc::DicomTransferSyntax_JPEGProcess14: + case Orthanc::DicomTransferSyntax_JPEGProcess14SV1: + return true; +#endif + +#if (ORTHANC_ENABLE_DCMTK == 1) && (ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1) + case Orthanc::DicomTransferSyntax_JPEGLSLossless: + case Orthanc::DicomTransferSyntax_JPEGLSLossy: + return true; +#endif + + default: + return false; + } + } }
--- a/Framework/Toolbox/ImageToolbox.h Sat Jun 20 11:16:55 2020 +0200 +++ b/Framework/Toolbox/ImageToolbox.h Mon Jun 22 17:46:40 2020 +0200 @@ -73,4 +73,10 @@ void ComputeMinMax(const Orthanc::ImageAccessor& img, double& minValue, double& maxValue); + + class ImageToolbox + { + public: + static bool IsDecodingSupported(Orthanc::DicomTransferSyntax& transferSyntax); + }; }