# HG changeset patch # User Sebastien Jodogne # Date 1592840800 -7200 # Node ID 121d01aa328ee44952f9a690bbca7070eaffe3f7 # Parent 6abd819aa5340038b4e7b9a64eebda1de194e2db SeriesThumbnailsLoader working on raw dicom files diff -r 6abd819aa534 -r 121d01aa328e Framework/Loaders/DicomResourcesLoader.cpp --- 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 # include #endif @@ -864,7 +865,7 @@ boost::shared_ptr protection(userPayload); #if ORTHANC_ENABLE_DCMTK == 1 - std::unique_ptr command(new ParseDicomFromFileCommand(path)); + std::unique_ptr command(new ParseDicomFromFileCommand(source, path)); command->SetPixelDataIncluded(includePixelData); command->AcquirePayload(new Handler(shared_from_this(), target, priority, source, protection)); diff -r 6abd819aa534 -r 121d01aa328e Framework/Loaders/DicomResourcesLoader.h --- 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 -#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, public IObservable diff -r 6abd819aa534 -r 121d01aa328e Framework/Loaders/DicomSource.cpp --- 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 command(new OrthancRestApiCommand); command->SetMethod(Orthanc::HttpMethod_Post); diff -r 6abd819aa534 -r 121d01aa328e Framework/Loaders/OracleScheduler.cpp --- 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(payload.GetOriginalCommand()), - message.GetDicom(), message.GetFileSize(), message.HasPixelData()); + message.GetSource(), message.GetDicom(), message.GetFileSize(), message.HasPixelData()); emitter_.EmitMessage(payload.GetOriginalReceiver(), bis); } #endif diff -r 6abd819aa534 -r 121d01aa328e Framework/Loaders/SeriesFramesLoader.cpp --- 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 command(new ParseDicomFromFileCommand(dicomDirPath_, file)); + std::unique_ptr 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 empty; std::unique_ptr command( - new ParseDicomFromWadoCommand(sopInstanceUid, source.CreateDicomWebCommand(uri, empty, empty, NULL))); + new ParseDicomFromWadoCommand(source, sopInstanceUid, source.CreateDicomWebCommand(uri, empty, empty, NULL))); command->AcquirePayload(payload.release()); { diff -r 6abd819aa534 -r 121d01aa328e Framework/Loaders/SeriesThumbnailsLoader.cpp --- 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 #include +#include #include #include #include @@ -30,6 +35,12 @@ #include +#if ORTHANC_ENABLE_DCMTK == 1 +# include +# include +#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 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 arguments, headers; arguments["0020000D"] = GetStudyInstanceUid(); arguments["0020000E"] = GetSeriesInstanceUid(); - arguments["includefield"] = "00080016"; + arguments["includefield"] = "00080016"; // SOP Class UID std::unique_ptr command( GetSource().CreateDicomWebCommand( @@ -419,6 +441,52 @@ }; +#if ORTHANC_ENABLE_DCMTK == 1 + class SeriesThumbnailsLoader::SelectDicomWebInstanceHandler : public SeriesThumbnailsLoader::Handler + { + public: + SelectDicomWebInstanceHandler(boost::shared_ptr 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& 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 lock(context_.Lock()); @@ -443,6 +511,7 @@ void SeriesThumbnailsLoader::Handle(const GetOrthancImageCommand::SuccessMessage& message) { assert(message.GetOrigin().HasPayload()); + const ThumbnailInformation& info = dynamic_cast(message.GetOrigin().GetPayload()); std::unique_ptr 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(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(message.GetOrigin()); + const ThumbnailInformation& info = dynamic_cast(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 frame( + Orthanc::DicomImageDecoder::Decode(message.GetDicom(), 0)); + + std::unique_ptr 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 converted( + new Orthanc::Image(Orthanc::PixelFormat_Float32, width, height, false)); + Orthanc::ImageProcessing::Convert(*converted, *frame); + + std::unique_ptr 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(message.GetOrigin()); @@ -495,6 +643,11 @@ result->Register(stone.GetOracleObservable(), &SeriesThumbnailsLoader::Handle); result->Register(stone.GetOracleObservable(), &SeriesThumbnailsLoader::Handle); result->Register(stone.GetOracleObservable(), &SeriesThumbnailsLoader::Handle); + +#if ORTHANC_ENABLE_DCMTK == 1 + result->Register(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 arguments, headers; + arguments["0020000D"] = studyInstanceUid; + arguments["0020000E"] = seriesInstanceUid; + arguments["includefield"] = "00080018"; // SOP Instance UID is mandatory + + std::unique_ptr 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 arguments, headers; - arguments["viewport"] = (boost::lexical_cast(width_) + "," + - boost::lexical_cast(height_)); + std::map arguments, headers; + arguments["viewport"] = (boost::lexical_cast(width_) + "," + + boost::lexical_cast(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 command( - source.CreateDicomWebCommand( - uri, arguments, headers, new DicomWebThumbnailHandler( - shared_from_this(), source, studyInstanceUid, seriesInstanceUid))); - Schedule(command.release()); + std::unique_ptr 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 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); diff -r 6abd819aa534 -r 121d01aa328e Framework/Loaders/SeriesThumbnailsLoader.h --- 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 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, diff -r 6abd819aa534 -r 121d01aa328e Framework/Oracle/GenericOracleRunner.cpp --- 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(fileSize), command.IsPixelDataIncluded()); + (command, command.GetSource(), *parsed, + static_cast(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 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); } diff -r 6abd819aa534 -r 121d01aa328e Framework/Oracle/ParseDicomFromFileCommand.h --- 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 @@ -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_; diff -r 6abd819aa534 -r 121d01aa328e Framework/Oracle/ParseDicomFromWadoCommand.cpp --- 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 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 arguments, headers; + headers["Accept"] = ("multipart/related; type=\"application/dicom\"; transfer-syntax=" + s); + + std::unique_ptr rest( + source.CreateDicomWebCommand(uri, arguments, headers, NULL)); + + std::unique_ptr command( + new ParseDicomFromWadoCommand(source, sopInstanceUid, rest.release())); + + if (protection.get() != NULL) + { + command->AcquirePayload(protection.release()); + } + + return command.release(); + } } diff -r 6abd819aa534 -r 121d01aa328e Framework/Oracle/ParseDicomFromWadoCommand.h --- 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 #include @@ -30,11 +33,13 @@ class ParseDicomFromWadoCommand : public OracleCommandBase { private: - std::string sopInstanceUid_; + DicomSource source_; + std::string sopInstanceUid_; std::unique_ptr 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); }; } diff -r 6abd819aa534 -r 121d01aa328e Framework/Oracle/ParseDicomSuccessMessage.h --- 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 { 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_; diff -r 6abd819aa534 -r 121d01aa328e Framework/Oracle/WebAssemblyOracle.cpp --- 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 #include @@ -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; diff -r 6abd819aa534 -r 121d01aa328e Framework/Oracle/WebAssemblyOracle.h --- 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 diff -r 6abd819aa534 -r 121d01aa328e Framework/Toolbox/ImageToolbox.cpp --- 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 . **/ +#include "../OrthancStone.h" #include "ImageToolbox.h" #include "../StoneException.h" @@ -33,6 +34,19 @@ #include +#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; + } + } } diff -r 6abd819aa534 -r 121d01aa328e Framework/Toolbox/ImageToolbox.h --- 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); + }; }