Mercurial > hg > orthanc-stone
diff Samples/Sdl/Loader.cpp @ 754:92c400a09f1b
Merge from default
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Wed, 22 May 2019 16:13:46 +0200 |
parents | ab236bb5dbc7 |
children | f7c236894c1a |
line wrap: on
line diff
--- a/Samples/Sdl/Loader.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Wed May 22 16:13:46 2019 +0200 @@ -18,20 +18,27 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ + +#include "../../Framework/Toolbox/DicomInstanceParameters.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h" +#include "../../Framework/Oracle/GetOrthancImageCommand.h" +#include "../../Framework/Oracle/OrthancRestApiCommand.h" +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/OracleCommandExceptionMessage.h" +#include "../../Framework/Messages/IMessageEmitter.h" +#include "../../Framework/Oracle/OracleCommandWithPayload.h" +#include "../../Framework/Oracle/IOracle.h" + // From Stone #include "../../Framework/Loaders/BasicFetchingItemsSorter.h" #include "../../Framework/Loaders/BasicFetchingStrategy.h" -#include "../../Framework/Messages/ICallable.h" -#include "../../Framework/Messages/IMessage.h" -#include "../../Framework/Messages/IObservable.h" -#include "../../Framework/Messages/MessageBroker.h" -#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" -#include "../../Framework/Scene2D/FloatTextureSceneLayer.h" #include "../../Framework/Scene2D/Scene2D.h" #include "../../Framework/StoneInitialization.h" #include "../../Framework/Toolbox/GeometryToolbox.h" #include "../../Framework/Toolbox/SlicesSorter.h" #include "../../Framework/Volumes/ImageBuffer3D.h" +#include "../../Framework/Volumes/VolumeImageGeometry.h" // From Orthanc framework #include <Core/Compression/GzipCompressor.h> @@ -40,12 +47,7 @@ #include <Core/DicomFormat/DicomImageInformation.h> #include <Core/HttpClient.h> #include <Core/IDynamicObject.h> -#include <Core/Images/Image.h> #include <Core/Images/ImageProcessing.h> -#include <Core/Images/JpegReader.h> -#include <Core/Images/PamReader.h> -#include <Core/Images/PngReader.h> -#include <Core/Images/PngWriter.h> #include <Core/Logging.h> #include <Core/MultiThreading/SharedMessageQueue.h> #include <Core/OrthancException.h> @@ -61,1225 +63,24 @@ -namespace Refactoring +namespace OrthancStone { - class IOracleCommand : public boost::noncopyable - { - public: - enum Type - { - Type_OrthancRestApi, - Type_GetOrthancImage, - Type_GetOrthancWebViewerJpeg - }; - - virtual ~IOracleCommand() - { - } - - virtual Type GetType() const = 0; - }; - - - class IMessageEmitter : public boost::noncopyable - { - public: - virtual ~IMessageEmitter() - { - } - - virtual void EmitMessage(const OrthancStone::IObserver& observer, - const OrthancStone::IMessage& message) = 0; - }; - - - class IOracle : public boost::noncopyable - { - public: - virtual ~IOracle() - { - } - - virtual void Schedule(const OrthancStone::IObserver& receiver, - IOracleCommand* command) = 0; // Takes ownership - }; - - - - class IVolumeSlicer : public boost::noncopyable - { - public: - virtual ~IVolumeSlicer() - { - } - - virtual void SetViewportPlane(const OrthancStone::CoordinateSystem3D& plane) = 0; - }; - - - - class OracleCommandWithPayload : public IOracleCommand - { - private: - std::auto_ptr<Orthanc::IDynamicObject> payload_; - - public: - void SetPayload(Orthanc::IDynamicObject* payload) - { - if (payload == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - else - { - payload_.reset(payload); - } - } - - bool HasPayload() const - { - return (payload_.get() != NULL); - } - - const Orthanc::IDynamicObject& GetPayload() const - { - if (HasPayload()) - { - return *payload_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - }; - - - - class OracleCommandExceptionMessage : public OrthancStone::IMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const IOracleCommand& command_; - Orthanc::OrthancException exception_; - - public: - OracleCommandExceptionMessage(const IOracleCommand& command, - const Orthanc::OrthancException& exception) : - command_(command), - exception_(exception) - { - } - - OracleCommandExceptionMessage(const IOracleCommand& command, - const Orthanc::ErrorCode& error) : - command_(command), - exception_(error) - { - } - - const IOracleCommand& GetCommand() const - { - return command_; - } - - const Orthanc::OrthancException& GetException() const - { - return exception_; - } - }; - - - typedef std::map<std::string, std::string> HttpHeaders; - - class OrthancRestApiCommand : public OracleCommandWithPayload - { - public: - class SuccessMessage : public OrthancStone::OriginMessage<OrthancRestApiCommand> - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - HttpHeaders headers_; - std::string answer_; - - public: - SuccessMessage(const OrthancRestApiCommand& command, - const HttpHeaders& answerHeaders, - std::string& answer /* will be swapped to avoid a memcpy() */) : - OriginMessage(command), - headers_(answerHeaders), - answer_(answer) - { - } - - const std::string& GetAnswer() const - { - return answer_; - } - - void ParseJsonBody(Json::Value& target) const - { - Json::Reader reader; - if (!reader.parse(answer_, target)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - } - - const HttpHeaders& GetAnswerHeaders() const - { - return headers_; - } - }; - - - private: - Orthanc::HttpMethod method_; - std::string uri_; - std::string body_; - HttpHeaders headers_; - unsigned int timeout_; - - std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; - std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; - - public: - OrthancRestApiCommand() : - method_(Orthanc::HttpMethod_Get), - uri_("/"), - timeout_(10) - { - } - - virtual Type GetType() const - { - return Type_OrthancRestApi; - } - - void SetMethod(Orthanc::HttpMethod method) - { - method_ = method; - } - - void SetUri(const std::string& uri) - { - uri_ = uri; - } - - void SetBody(const std::string& body) - { - body_ = body; - } - - void SetBody(const Json::Value& json) - { - Json::FastWriter writer; - body_ = writer.write(json); - } - - void SetHttpHeaders(const HttpHeaders& headers) - { - headers_ = headers; - } - - void SetHttpHeader(const std::string& key, - const std::string& value) - { - headers_[key] = value; - } - - Orthanc::HttpMethod GetMethod() const - { - return method_; - } - - const std::string& GetUri() const - { - return uri_; - } - - const std::string& GetBody() const - { - if (method_ == Orthanc::HttpMethod_Post || - method_ == Orthanc::HttpMethod_Put) - { - return body_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - const HttpHeaders& GetHttpHeaders() const - { - return headers_; - } - - void SetTimeout(unsigned int seconds) - { - timeout_ = seconds; - } - - unsigned int GetTimeout() const - { - return timeout_; - } - }; - - - - - class GetOrthancImageCommand : public OracleCommandWithPayload - { - public: - class SuccessMessage : public OrthancStone::OriginMessage<GetOrthancImageCommand> - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - std::auto_ptr<Orthanc::ImageAccessor> image_; - Orthanc::MimeType mime_; - - public: - SuccessMessage(const GetOrthancImageCommand& command, - Orthanc::ImageAccessor* image, // Takes ownership - Orthanc::MimeType mime) : - OriginMessage(command), - image_(image), - mime_(mime) - { - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - const Orthanc::ImageAccessor& GetImage() const - { - return *image_; - } - - Orthanc::MimeType GetMimeType() const - { - return mime_; - } - }; - - - private: - std::string uri_; - HttpHeaders headers_; - unsigned int timeout_; - bool hasExpectedFormat_; - Orthanc::PixelFormat expectedFormat_; - - std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; - std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; - - public: - GetOrthancImageCommand() : - uri_("/"), - timeout_(10), - hasExpectedFormat_(false) - { - } - - virtual Type GetType() const - { - return Type_GetOrthancImage; - } - - void SetExpectedPixelFormat(Orthanc::PixelFormat format) - { - hasExpectedFormat_ = true; - expectedFormat_ = format; - } - - void SetUri(const std::string& uri) - { - uri_ = uri; - } - - void SetInstanceUri(const std::string& instance, - Orthanc::PixelFormat pixelFormat) - { - uri_ = "/instances/" + instance; - - switch (pixelFormat) - { - case Orthanc::PixelFormat_RGB24: - uri_ += "/preview"; - break; - - case Orthanc::PixelFormat_Grayscale16: - uri_ += "/image-uint16"; - break; - - case Orthanc::PixelFormat_SignedGrayscale16: - uri_ += "/image-int16"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - void SetHttpHeader(const std::string& key, - const std::string& value) - { - headers_[key] = value; - } - - const std::string& GetUri() const - { - return uri_; - } - - const HttpHeaders& GetHttpHeaders() const - { - return headers_; - } - - void SetTimeout(unsigned int seconds) - { - timeout_ = seconds; - } - - unsigned int GetTimeout() const - { - return timeout_; - } - - void ProcessHttpAnswer(IMessageEmitter& emitter, - const OrthancStone::IObserver& receiver, - const std::string& answer, - const HttpHeaders& answerHeaders) const - { - Orthanc::MimeType contentType = Orthanc::MimeType_Binary; - - for (HttpHeaders::const_iterator it = answerHeaders.begin(); - it != answerHeaders.end(); ++it) - { - std::string s; - Orthanc::Toolbox::ToLowerCase(s, it->first); - - if (s == "content-type") - { - contentType = Orthanc::StringToMimeType(it->second); - break; - } - } - - std::auto_ptr<Orthanc::ImageAccessor> image; - - switch (contentType) - { - case Orthanc::MimeType_Png: - { - image.reset(new Orthanc::PngReader); - dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer); - break; - } - - case Orthanc::MimeType_Pam: - { - image.reset(new Orthanc::PamReader); - dynamic_cast<Orthanc::PamReader&>(*image).ReadFromMemory(answer); - break; - } - - case Orthanc::MimeType_Jpeg: - { - image.reset(new Orthanc::JpegReader); - dynamic_cast<Orthanc::JpegReader&>(*image).ReadFromMemory(answer); - break; - } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, - "Unsupported HTTP Content-Type for an image: " + - std::string(Orthanc::EnumerationToString(contentType))); - } - - if (hasExpectedFormat_) - { - if (expectedFormat_ == Orthanc::PixelFormat_SignedGrayscale16 && - image->GetFormat() == Orthanc::PixelFormat_Grayscale16) - { - image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); - } - - if (expectedFormat_ != image->GetFormat()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - } - - SuccessMessage message(*this, image.release(), contentType); - emitter.EmitMessage(receiver, message); - } - }; - - - - class GetOrthancWebViewerJpegCommand : public OracleCommandWithPayload - { - public: - class SuccessMessage : public OrthancStone::OriginMessage<GetOrthancWebViewerJpegCommand> - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - std::auto_ptr<Orthanc::ImageAccessor> image_; - - public: - SuccessMessage(const GetOrthancWebViewerJpegCommand& command, - Orthanc::ImageAccessor* image) : // Takes ownership - OriginMessage(command), - image_(image) - { - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - const Orthanc::ImageAccessor& GetImage() const - { - return *image_; - } - }; - - private: - std::string instanceId_; - unsigned int frame_; - unsigned int quality_; - HttpHeaders headers_; - unsigned int timeout_; - Orthanc::PixelFormat expectedFormat_; - - std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; - std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; - - public: - GetOrthancWebViewerJpegCommand() : - frame_(0), - quality_(95), - timeout_(10), - expectedFormat_(Orthanc::PixelFormat_Grayscale8) - { - } - - virtual Type GetType() const - { - return Type_GetOrthancWebViewerJpeg; - } - - void SetExpectedPixelFormat(Orthanc::PixelFormat format) - { - expectedFormat_ = format; - } - - void SetInstance(const std::string& instanceId) - { - instanceId_ = instanceId; - } - - void SetFrame(unsigned int frame) - { - frame_ = frame; - } - - void SetQuality(unsigned int quality) - { - if (quality <= 0 || - quality > 100) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - quality_ = quality; - } - } - - void SetHttpHeader(const std::string& key, - const std::string& value) - { - headers_[key] = value; - } - - Orthanc::PixelFormat GetExpectedPixelFormat() const - { - return expectedFormat_; - } - - const std::string& GetInstanceId() const - { - return instanceId_; - } - - unsigned int GetFrame() const - { - return frame_; - } - - unsigned int GetQuality() const - { - return quality_; - } - - const HttpHeaders& GetHttpHeaders() const - { - return headers_; - } - - void SetTimeout(unsigned int seconds) - { - timeout_ = seconds; - } - - unsigned int GetTimeout() const - { - return timeout_; - } - - std::string GetUri() const - { - return ("/web-viewer/instances/jpeg" + boost::lexical_cast<std::string>(quality_) + - "-" + instanceId_ + "_" + boost::lexical_cast<std::string>(frame_)); - } - - void ProcessHttpAnswer(IMessageEmitter& emitter, - const OrthancStone::IObserver& receiver, - const std::string& answer) const - { - // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()" - - Json::Value encoded; - - { - Json::Reader reader; - if (!reader.parse(answer, encoded)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - } - - if (encoded.type() != Json::objectValue || - !encoded.isMember("Orthanc") || - encoded["Orthanc"].type() != Json::objectValue) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - const Json::Value& info = encoded["Orthanc"]; - if (!info.isMember("PixelData") || - !info.isMember("Stretched") || - !info.isMember("Compression") || - info["Compression"].type() != Json::stringValue || - info["PixelData"].type() != Json::stringValue || - info["Stretched"].type() != Json::booleanValue || - info["Compression"].asString() != "Jpeg") - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - bool isSigned = false; - bool isStretched = info["Stretched"].asBool(); - - if (info.isMember("IsSigned")) - { - if (info["IsSigned"].type() != Json::booleanValue) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - isSigned = info["IsSigned"].asBool(); - } - } - - std::auto_ptr<Orthanc::ImageAccessor> reader; - - { - std::string jpeg; - Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); - - reader.reset(new Orthanc::JpegReader); - dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg); - } - - if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image - { - if (expectedFormat_ != Orthanc::PixelFormat_RGB24) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - if (isSigned || isStretched) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - SuccessMessage message(*this, reader.release()); - emitter.EmitMessage(receiver, message); - return; - } - } - - if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - if (!isStretched) - { - if (expectedFormat_ != reader->GetFormat()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - SuccessMessage message(*this, reader.release()); - emitter.EmitMessage(receiver, message); - return; - } - } - - int32_t stretchLow = 0; - int32_t stretchHigh = 0; - - if (!info.isMember("StretchLow") || - !info.isMember("StretchHigh") || - info["StretchLow"].type() != Json::intValue || - info["StretchHigh"].type() != Json::intValue) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - stretchLow = info["StretchLow"].asInt(); - stretchHigh = info["StretchHigh"].asInt(); - - if (stretchLow < -32768 || - stretchHigh > 65535 || - (stretchLow < 0 && stretchHigh > 32767)) - { - // This range cannot be represented with a uint16_t or an int16_t - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - // Decode a grayscale JPEG 8bpp image coming from the Web viewer - std::auto_ptr<Orthanc::ImageAccessor> image - (new Orthanc::Image(expectedFormat_, reader->GetWidth(), reader->GetHeight(), false)); - - Orthanc::ImageProcessing::Convert(*image, *reader); - reader.reset(); - - float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; - - if (!OrthancStone::LinearAlgebra::IsCloseToZero(scaling)) - { - float offset = static_cast<float>(stretchLow) / scaling; - Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); - } - - SuccessMessage message(*this, image.release()); - emitter.EmitMessage(receiver, message); - } - }; - - - - - - class DicomInstanceParameters : - public Orthanc::IDynamicObject /* to be used as a payload of SlicesSorter */ - { - private: - struct Data // Struct to ease the copy constructor - { - std::string orthancInstanceId_; - std::string studyInstanceUid_; - std::string seriesInstanceUid_; - std::string sopInstanceUid_; - Orthanc::DicomImageInformation imageInformation_; - OrthancStone::SopClassUid sopClassUid_; - double thickness_; - double pixelSpacingX_; - double pixelSpacingY_; - OrthancStone::CoordinateSystem3D geometry_; - OrthancStone::Vector frameOffsets_; - bool isColor_; - bool hasRescale_; - double rescaleIntercept_; - double rescaleSlope_; - bool hasDefaultWindowing_; - float defaultWindowingCenter_; - float defaultWindowingWidth_; - Orthanc::PixelFormat expectedPixelFormat_; - - void ComputeDoseOffsets(const Orthanc::DicomMap& dicom) - { - // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html - - { - std::string increment; - - if (dicom.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false)) - { - Orthanc::Toolbox::ToUpperCase(increment); - if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag - { - LOG(ERROR) << "RT-DOSE: Bad value for the \"FrameIncrementPointer\" tag"; - return; - } - } - } - - if (!OrthancStone::LinearAlgebra::ParseVector(frameOffsets_, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) || - frameOffsets_.size() < imageInformation_.GetNumberOfFrames()) - { - LOG(ERROR) << "RT-DOSE: No information about the 3D location of some slice(s)"; - frameOffsets_.clear(); - } - else - { - if (frameOffsets_.size() >= 2) - { - thickness_ = frameOffsets_[1] - frameOffsets_[0]; - - if (thickness_ < 0) - { - thickness_ = -thickness_; - } - } - } - } - - Data(const Orthanc::DicomMap& dicom) : - imageInformation_(dicom) - { - if (imageInformation_.GetNumberOfFrames() <= 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - if (!dicom.CopyToString(studyInstanceUid_, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) || - !dicom.CopyToString(seriesInstanceUid_, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) || - !dicom.CopyToString(sopInstanceUid_, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - std::string s; - if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - sopClassUid_ = OrthancStone::StringToSopClassUid(s); - } - - if (!dicom.ParseDouble(thickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS)) - { - thickness_ = 100.0 * std::numeric_limits<double>::epsilon(); - } - - OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom); - - std::string position, orientation; - if (dicom.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && - dicom.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) - { - geometry_ = OrthancStone::CoordinateSystem3D(position, orientation); - } - - if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) - { - ComputeDoseOffsets(dicom); - } - - isColor_ = (imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome1 && - imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome2); - - double doseGridScaling; - - if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && - dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) - { - hasRescale_ = true; - } - else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) - { - hasRescale_ = true; - rescaleIntercept_ = 0; - rescaleSlope_ = doseGridScaling; - } - else - { - hasRescale_ = false; - } - - OrthancStone::Vector c, w; - if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && - OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && - c.size() > 0 && - w.size() > 0) - { - hasDefaultWindowing_ = true; - defaultWindowingCenter_ = static_cast<float>(c[0]); - defaultWindowingWidth_ = static_cast<float>(w[0]); - } - else - { - hasDefaultWindowing_ = false; - } - - if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) - { - switch (imageInformation_.GetBitsStored()) - { - case 16: - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; - break; - - case 32: - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - else if (isColor_) - { - expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; - } - else if (imageInformation_.IsSigned()) - { - expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; - } - else - { - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; - } - } - - OrthancStone::CoordinateSystem3D GetFrameGeometry(unsigned int frame) const - { - if (frame == 0) - { - return geometry_; - } - else if (frame >= imageInformation_.GetNumberOfFrames()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) - { - if (frame >= frameOffsets_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - return OrthancStone::CoordinateSystem3D( - geometry_.GetOrigin() + frameOffsets_[frame] * geometry_.GetNormal(), - geometry_.GetAxisX(), - geometry_.GetAxisY()); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - - // TODO - Is this necessary? - bool FrameContainsPlane(unsigned int frame, - const OrthancStone::CoordinateSystem3D& plane) const - { - if (frame >= imageInformation_.GetNumberOfFrames()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - OrthancStone::CoordinateSystem3D tmp = geometry_; - - if (frame != 0) - { - tmp = GetFrameGeometry(frame); - } - - double distance; - - return (OrthancStone::CoordinateSystem3D::GetDistance(distance, tmp, plane) && - distance <= thickness_ / 2.0); - } - - - void ApplyRescale(Orthanc::ImageAccessor& image, - bool useDouble) const - { - if (image.GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - if (hasRescale_) - { - const unsigned int width = image.GetWidth(); - const unsigned int height = image.GetHeight(); - - for (unsigned int y = 0; y < height; y++) - { - float* p = reinterpret_cast<float*>(image.GetRow(y)); - - if (useDouble) - { - // Slower, accurate implementation using double - for (unsigned int x = 0; x < width; x++, p++) - { - double value = static_cast<double>(*p); - *p = static_cast<float>(value * rescaleSlope_ + rescaleIntercept_); - } - } - else - { - // Fast, approximate implementation using float - for (unsigned int x = 0; x < width; x++, p++) - { - *p = (*p) * static_cast<float>(rescaleSlope_) + static_cast<float>(rescaleIntercept_); - } - } - } - } - } - }; - - - Data data_; - - - public: - DicomInstanceParameters(const DicomInstanceParameters& other) : - data_(other.data_) - { - } - - DicomInstanceParameters(const Orthanc::DicomMap& dicom) : - data_(dicom) - { - } - - void SetOrthancInstanceIdentifier(const std::string& id) - { - data_.orthancInstanceId_ = id; - } - - const std::string& GetOrthancInstanceIdentifier() const - { - return data_.orthancInstanceId_; - } - - const Orthanc::DicomImageInformation& GetImageInformation() const - { - return data_.imageInformation_; - } - - const std::string& GetStudyInstanceUid() const - { - return data_.studyInstanceUid_; - } - - const std::string& GetSeriesInstanceUid() const - { - return data_.seriesInstanceUid_; - } - - const std::string& GetSopInstanceUid() const - { - return data_.sopInstanceUid_; - } - - OrthancStone::SopClassUid GetSopClassUid() const - { - return data_.sopClassUid_; - } - - double GetThickness() const - { - return data_.thickness_; - } - - double GetPixelSpacingX() const - { - return data_.pixelSpacingX_; - } - - double GetPixelSpacingY() const - { - return data_.pixelSpacingY_; - } - - const OrthancStone::CoordinateSystem3D& GetGeometry() const - { - return data_.geometry_; - } - - OrthancStone::CoordinateSystem3D GetFrameGeometry(unsigned int frame) const - { - return data_.GetFrameGeometry(frame); - } - - // TODO - Is this necessary? - bool FrameContainsPlane(unsigned int frame, - const OrthancStone::CoordinateSystem3D& plane) const - { - return data_.FrameContainsPlane(frame, plane); - } - - bool IsColor() const - { - return data_.isColor_; - } - - bool HasRescale() const - { - return data_.hasRescale_; - } - - double GetRescaleIntercept() const - { - if (data_.hasRescale_) - { - return data_.rescaleIntercept_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - double GetRescaleSlope() const - { - if (data_.hasRescale_) - { - return data_.rescaleSlope_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - bool HasDefaultWindowing() const - { - return data_.hasDefaultWindowing_; - } - - float GetDefaultWindowingCenter() const - { - if (data_.hasDefaultWindowing_) - { - return data_.defaultWindowingCenter_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - float GetDefaultWindowingWidth() const - { - if (data_.hasDefaultWindowing_) - { - return data_.defaultWindowingWidth_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - Orthanc::PixelFormat GetExpectedPixelFormat() const - { - return data_.expectedPixelFormat_; - } - - - OrthancStone::TextureBaseSceneLayer* CreateTexture(const Orthanc::ImageAccessor& source) const - { - assert(sizeof(float) == 4); - - Orthanc::PixelFormat sourceFormat = source.GetFormat(); - - if (sourceFormat != GetExpectedPixelFormat()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - if (sourceFormat == Orthanc::PixelFormat_RGB24) - { - // This is the case of a color image. No conversion has to be done. - return new OrthancStone::ColorTextureSceneLayer(source); - } - else - { - if (sourceFormat != Orthanc::PixelFormat_Grayscale16 && - sourceFormat != Orthanc::PixelFormat_Grayscale32 && - sourceFormat != Orthanc::PixelFormat_SignedGrayscale16) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - std::auto_ptr<OrthancStone::FloatTextureSceneLayer> texture; - - { - // This is the case of a grayscale frame. Convert it to Float32. - std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, - source.GetWidth(), - source.GetHeight(), - false)); - Orthanc::ImageProcessing::Convert(*converted, source); - - // Correct rescale slope/intercept if need be - data_.ApplyRescale(*converted, (sourceFormat == Orthanc::PixelFormat_Grayscale32)); - - texture.reset(new OrthancStone::FloatTextureSceneLayer(*converted)); - } - - if (data_.hasDefaultWindowing_) - { - texture->SetCustomWindowing(data_.defaultWindowingCenter_, - data_.defaultWindowingWidth_); - } - - return texture.release(); - } - } - }; - - class DicomVolumeImage : public boost::noncopyable { private: - std::auto_ptr<OrthancStone::ImageBuffer3D> image_; - std::vector<DicomInstanceParameters*> slices_; - uint64_t revision_; - std::vector<uint64_t> slicesRevision_; - std::vector<unsigned int> slicesQuality_; + std::auto_ptr<ImageBuffer3D> image_; + std::auto_ptr<VolumeImageGeometry> geometry_; + std::vector<DicomInstanceParameters*> slices_; + uint64_t revision_; + std::vector<uint64_t> slicesRevision_; + std::vector<unsigned int> slicesQuality_; void CheckSlice(size_t index, const DicomInstanceParameters& reference) const { const DicomInstanceParameters& slice = *slices_[index]; - if (!OrthancStone::GeometryToolbox::IsParallel( + if (!GeometryToolbox::IsParallel( reference.GetGeometry().GetNormal(), slice.GetGeometry().GetNormal())) { @@ -1300,8 +101,8 @@ "The width/height of slices are not constant in the volume image"); } - if (!OrthancStone::LinearAlgebra::IsNear(reference.GetPixelSpacingX(), slice.GetPixelSpacingX()) || - !OrthancStone::LinearAlgebra::IsNear(reference.GetPixelSpacingY(), slice.GetPixelSpacingY())) + if (!LinearAlgebra::IsNear(reference.GetPixelSpacingX(), slice.GetPixelSpacingX()) || + !LinearAlgebra::IsNear(reference.GetPixelSpacingY(), slice.GetPixelSpacingY())) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, "The pixel spacing of the slices change across the volume image"); @@ -1336,6 +137,7 @@ void Clear() { image_.reset(); + geometry_.reset(); for (size_t i = 0; i < slices_.size(); i++) { @@ -1376,7 +178,7 @@ } // WARNING: The payload of "slices" must be of class "DicomInstanceParameters" - void SetGeometry(OrthancStone::SlicesSorter& slices) + void SetGeometry(SlicesSorter& slices) { Clear(); @@ -1386,11 +188,13 @@ "Cannot sort the 3D slices of a DICOM series"); } + geometry_.reset(new VolumeImageGeometry); + if (slices.GetSlicesCount() == 0) { // Empty volume - image_.reset(new OrthancStone::ImageBuffer3D(Orthanc::PixelFormat_Grayscale8, 0, 0, 0, - false /* don't compute range */)); + image_.reset(new ImageBuffer3D(Orthanc::PixelFormat_Grayscale8, 0, 0, 0, + false /* don't compute range */)); } else { @@ -1412,14 +216,15 @@ const DicomInstanceParameters& parameters = *slices_[0]; - image_.reset(new OrthancStone::ImageBuffer3D(parameters.GetExpectedPixelFormat(), - parameters.GetImageInformation().GetWidth(), - parameters.GetImageInformation().GetHeight(), - slices.GetSlicesCount(), false /* don't compute range */)); + image_.reset(new ImageBuffer3D(parameters.GetExpectedPixelFormat(), + parameters.GetImageInformation().GetWidth(), + parameters.GetImageInformation().GetHeight(), + slices.GetSlicesCount(), false /* don't compute range */)); - image_->GetGeometry().SetAxialGeometry(slices.GetSliceGeometry(0)); - image_->GetGeometry().SetVoxelDimensions(parameters.GetPixelSpacingX(), - parameters.GetPixelSpacingY(), spacingZ); + geometry_->SetSize(image_->GetWidth(), image_->GetHeight(), image_->GetDepth()); + geometry_->SetAxialGeometry(slices.GetSliceGeometry(0)); + geometry_->SetVoxelDimensions(parameters.GetPixelSpacingX(), + parameters.GetPixelSpacingY(), spacingZ); } image_->Clear(); @@ -1434,10 +239,11 @@ bool HasGeometry() const { - return (image_.get() != NULL); + return (image_.get() != NULL && + geometry_.get() != NULL); } - const OrthancStone::ImageBuffer3D& GetImage() const + const ImageBuffer3D& GetImage() const { if (!HasGeometry()) { @@ -1449,6 +255,18 @@ } } + const VolumeImageGeometry& GetGeometry() const + { + if (!HasGeometry()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *geometry_; + } + } + size_t GetSlicesCount() const { if (!HasGeometry()) @@ -1483,8 +301,8 @@ if (quality >= slicesQuality_[index]) { { - OrthancStone::ImageBuffer3D::SliceWriter writer - (*image_, OrthancStone::VolumeProjection_Axial, index); + ImageBuffer3D::SliceWriter writer + (*image_, VolumeProjection_Axial, index); Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); } @@ -1496,10 +314,10 @@ - class IDicomVolumeSource : public boost::noncopyable + class IDicomVolumeImageSource : public boost::noncopyable { public: - virtual ~IDicomVolumeSource() + virtual ~IDicomVolumeImageSource() { } @@ -1511,8 +329,8 @@ class VolumeSeriesOrthancLoader : - public OrthancStone::IObserver, - public IDicomVolumeSource + public IObserver, + public IDicomVolumeImageSource { private: static const unsigned int LOW_QUALITY = 0; @@ -1544,12 +362,11 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } - std::auto_ptr<Refactoring::OracleCommandWithPayload> command; + std::auto_ptr<OracleCommandWithPayload> command; if (quality == BEST_QUALITY) { - std::auto_ptr<Refactoring::GetOrthancImageCommand> tmp( - new Refactoring::GetOrthancImageCommand); + std::auto_ptr<GetOrthancImageCommand> tmp(new GetOrthancImageCommand); tmp->SetHttpHeader("Accept-Encoding", "gzip"); tmp->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); tmp->SetInstanceUri(instance, slice.GetExpectedPixelFormat()); @@ -1558,8 +375,7 @@ } else { - std::auto_ptr<Refactoring::GetOrthancWebViewerJpegCommand> tmp( - new Refactoring::GetOrthancWebViewerJpegCommand); + std::auto_ptr<GetOrthancWebViewerJpegCommand> tmp(new GetOrthancWebViewerJpegCommand); tmp->SetHttpHeader("Accept-Encoding", "gzip"); tmp->SetInstance(instance); tmp->SetQuality((quality == 0 ? 50 : 90)); @@ -1586,7 +402,7 @@ { Json::Value::Members instances = body.getMemberNames(); - OrthancStone::SlicesSorter slices; + SlicesSorter slices; for (size_t i = 0; i < instances.size(); i++) { @@ -1596,7 +412,7 @@ std::auto_ptr<DicomInstanceParameters> instance(new DicomInstanceParameters(dicom)); instance->SetOrthancInstanceIdentifier(instances[i]); - OrthancStone::CoordinateSystem3D geometry = instance->GetGeometry(); + CoordinateSystem3D geometry = instance->GetGeometry(); slices.AddSlice(geometry, instance.release()); } @@ -1605,8 +421,8 @@ if (volume_.GetSlicesCount() != 0) { - strategy_.reset(new OrthancStone::BasicFetchingStrategy( - new OrthancStone::BasicFetchingItemsSorter(volume_.GetSlicesCount()), BEST_QUALITY)); + strategy_.reset(new BasicFetchingStrategy( + new BasicFetchingItemsSorter(volume_.GetSlicesCount()), BEST_QUALITY)); for (unsigned int i = 0; i < 4; i++) // Schedule up to 4 simultaneous downloads (TODO - parameter) { @@ -1616,7 +432,7 @@ } - void LoadBestQualitySliceContent(const Refactoring::GetOrthancImageCommand::SuccessMessage& message) + void LoadBestQualitySliceContent(const GetOrthancImageCommand::SuccessMessage& message) { volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), BEST_QUALITY); @@ -1625,7 +441,7 @@ } - void LoadJpegSliceContent(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message) + void LoadJpegSliceContent(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) { unsigned int quality; @@ -1653,25 +469,25 @@ bool active_; DicomVolumeImage volume_; - std::auto_ptr<OrthancStone::IFetchingStrategy> strategy_; + std::auto_ptr<IFetchingStrategy> strategy_; public: VolumeSeriesOrthancLoader(IOracle& oracle, - OrthancStone::IObservable& oracleObservable) : + IObservable& oracleObservable) : IObserver(oracleObservable.GetBroker()), oracle_(oracle), active_(false) { oracleObservable.RegisterObserverCallback( - new OrthancStone::Callable<VolumeSeriesOrthancLoader, OrthancRestApiCommand::SuccessMessage> + new Callable<VolumeSeriesOrthancLoader, OrthancRestApiCommand::SuccessMessage> (*this, &VolumeSeriesOrthancLoader::LoadGeometry)); oracleObservable.RegisterObserverCallback( - new OrthancStone::Callable<VolumeSeriesOrthancLoader, GetOrthancImageCommand::SuccessMessage> + new Callable<VolumeSeriesOrthancLoader, GetOrthancImageCommand::SuccessMessage> (*this, &VolumeSeriesOrthancLoader::LoadBestQualitySliceContent)); oracleObservable.RegisterObserverCallback( - new OrthancStone::Callable<VolumeSeriesOrthancLoader, GetOrthancWebViewerJpegCommand::SuccessMessage> + new Callable<VolumeSeriesOrthancLoader, GetOrthancWebViewerJpegCommand::SuccessMessage> (*this, &VolumeSeriesOrthancLoader::LoadJpegSliceContent)); } @@ -1684,7 +500,7 @@ active_ = true; - std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); + std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); command->SetUri("/series/" + seriesId + "/instances-tags"); oracle_.Schedule(*this, command.release()); @@ -1728,7 +544,7 @@ // TODO => Should be part of a second call if needed - std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); + std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c"); command->SetPayload(new LoadInstanceGeometryHandler(*this)); @@ -1740,16 +556,16 @@ /* class VolumeSlicerBase : public IVolumeSlicer { private: - OrthancStone::Scene2D& scene_; + Scene2D& scene_; int layerDepth_; bool first_; - OrthancStone::CoordinateSystem3D lastPlane_; + CoordinateSystem3D lastPlane_; protected: - bool HasViewportPlaneChanged(const OrthancStone::CoordinateSystem3D& plane) const + bool HasViewportPlaneChanged(const CoordinateSystem3D& plane) const { if (first_ || - !OrthancStone::LinearAlgebra::IsCloseToZero( + !LinearAlgebra::IsCloseToZero( boost::numeric::ublas::norm_2(lastPlane_.GetNormal() - plane.GetNormal()))) { // This is the first rendering, or the plane has not the same orientation @@ -1759,17 +575,17 @@ { double offset1 = lastPlane_.ProjectAlongNormal(plane.GetOrigin()); double offset2 = lastPlane_.ProjectAlongNormal(lastPlane_.GetOrigin()); - return OrthancStone::LinearAlgebra::IsCloseToZero(offset2 - offset1); + return LinearAlgebra::IsCloseToZero(offset2 - offset1); } } - void SetLastViewportPlane(const OrthancStone::CoordinateSystem3D& plane) + void SetLastViewportPlane(const CoordinateSystem3D& plane) { first_ = false; lastPlane_ = plane; } - void SetLayer(OrthancStone::ISceneLayer* layer) + void SetLayer(ISceneLayer* layer) { scene_.SetLayer(layerDepth_, layer); } @@ -1780,7 +596,7 @@ } public: - VolumeSlicerBase(OrthancStone::Scene2D& scene, + VolumeSlicerBase(Scene2D& scene, int layerDepth) : scene_(scene), layerDepth_(layerDepth), @@ -1791,22 +607,33 @@ + class IVolumeSlicer : public boost::noncopyable + { + public: + virtual ~IVolumeSlicer() + { + } + + virtual void SetViewportPlane(const CoordinateSystem3D& plane) = 0; + }; + + class DicomVolumeMPRSlicer : public IVolumeSlicer { private: - bool linearInterpolation_; - OrthancStone::Scene2D& scene_; - int layerDepth_; - IDicomVolumeSource& source_; - bool first_; - OrthancStone::VolumeProjection lastProjection_; - unsigned int lastSliceIndex_; - uint64_t lastSliceRevision_; + bool linearInterpolation_; + Scene2D& scene_; + int layerDepth_; + IDicomVolumeImageSource& source_; + bool first_; + VolumeProjection lastProjection_; + unsigned int lastSliceIndex_; + uint64_t lastSliceRevision_; public: - DicomVolumeMPRSlicer(OrthancStone::Scene2D& scene, + DicomVolumeMPRSlicer(Scene2D& scene, int layerDepth, - IDicomVolumeSource& source) : + IDicomVolumeImageSource& source) : linearInterpolation_(false), scene_(scene), layerDepth_(layerDepth), @@ -1825,7 +652,7 @@ return linearInterpolation_; } - virtual void SetViewportPlane(const OrthancStone::CoordinateSystem3D& plane) + virtual void SetViewportPlane(const CoordinateSystem3D& plane) { if (!source_.GetVolume().HasGeometry() || source_.GetVolume().GetSlicesCount() == 0) @@ -1834,9 +661,9 @@ return; } - const OrthancStone::VolumeImageGeometry& geometry = source_.GetVolume().GetImage().GetGeometry(); + const VolumeImageGeometry& geometry = source_.GetVolume().GetGeometry(); - OrthancStone::VolumeProjection projection; + VolumeProjection projection; unsigned int sliceIndex; if (!geometry.DetectSlice(projection, sliceIndex, plane)) { @@ -1847,7 +674,7 @@ } uint64_t sliceRevision; - if (projection == OrthancStone::VolumeProjection_Axial) + if (projection == VolumeProjection_Axial) { sliceRevision = source_.GetVolume().GetSliceRevision(sliceIndex); @@ -1878,17 +705,17 @@ lastSliceIndex_ = sliceIndex; lastSliceRevision_ = sliceRevision; - std::auto_ptr<OrthancStone::TextureBaseSceneLayer> texture; + std::auto_ptr<TextureBaseSceneLayer> texture; { const DicomInstanceParameters& parameters = source_.GetVolume().GetSliceParameters - (projection == OrthancStone::VolumeProjection_Axial ? sliceIndex : 0); + (projection == VolumeProjection_Axial ? sliceIndex : 0); - OrthancStone::ImageBuffer3D::SliceReader reader(source_.GetVolume().GetImage(), projection, sliceIndex); + ImageBuffer3D::SliceReader reader(source_.GetVolume().GetImage(), projection, sliceIndex); texture.reset(parameters.CreateTexture(reader.GetAccessor())); } - const OrthancStone::CoordinateSystem3D& system = geometry.GetProjectionGeometry(projection); + const CoordinateSystem3D& system = geometry.GetProjectionGeometry(projection); double x0, y0, x1, y1; system.ProjectPoint(x0, y0, system.GetOrigin()); @@ -1897,13 +724,13 @@ double dx = x1 - x0; double dy = y1 - y0; - if (!OrthancStone::LinearAlgebra::IsCloseToZero(dx) || - !OrthancStone::LinearAlgebra::IsCloseToZero(dy)) + if (!LinearAlgebra::IsCloseToZero(dx) || + !LinearAlgebra::IsCloseToZero(dy)) { texture->SetAngle(atan2(dy, dx)); } - OrthancStone::Vector tmp; + Vector tmp; geometry.GetVoxelDimensions(projection); texture->SetPixelSpacing(tmp[0], tmp[1]); @@ -1913,350 +740,14 @@ } } }; - - - - - - class NativeOracle : public IOracle - { - private: - class Item : public Orthanc::IDynamicObject - { - private: - const OrthancStone::IObserver& receiver_; - std::auto_ptr<IOracleCommand> command_; - - public: - Item(const OrthancStone::IObserver& receiver, - IOracleCommand* command) : - receiver_(receiver), - command_(command) - { - if (command == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - const OrthancStone::IObserver& GetReceiver() const - { - return receiver_; - } - - const IOracleCommand& GetCommand() const - { - assert(command_.get() != NULL); - return *command_; - } - }; - - - enum State - { - State_Setup, - State_Running, - State_Stopped - }; - - - IMessageEmitter& emitter_; - Orthanc::WebServiceParameters orthanc_; - Orthanc::SharedMessageQueue queue_; - State state_; - boost::mutex mutex_; - std::vector<boost::thread*> workers_; - - - void CopyHttpHeaders(Orthanc::HttpClient& client, - const HttpHeaders& headers) - { - for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - } - - - void DecodeAnswer(std::string& answer, - const HttpHeaders& headers) - { - Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None; - - for (HttpHeaders::const_iterator it = headers.begin(); - it != headers.end(); ++it) - { - std::string s; - Orthanc::Toolbox::ToLowerCase(s, it->first); - - if (s == "content-encoding") - { - if (it->second == "gzip") - { - contentEncoding = Orthanc::HttpCompression_Gzip; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, - "Unsupported HTTP Content-Encoding: " + it->second); - } - - break; - } - } - - if (contentEncoding == Orthanc::HttpCompression_Gzip) - { - std::string compressed; - answer.swap(compressed); - - Orthanc::GzipCompressor compressor; - compressor.Uncompress(answer, compressed.c_str(), compressed.size()); - } - } - - - void Execute(const OrthancStone::IObserver& receiver, - const OrthancRestApiCommand& command) - { - Orthanc::HttpClient client(orthanc_, command.GetUri()); - client.SetMethod(command.GetMethod()); - client.SetTimeout(command.GetTimeout()); - - CopyHttpHeaders(client, command.GetHttpHeaders()); - - if (command.GetMethod() == Orthanc::HttpMethod_Post || - command.GetMethod() == Orthanc::HttpMethod_Put) - { - client.SetBody(command.GetBody()); - } - - std::string answer; - HttpHeaders answerHeaders; - client.ApplyAndThrowException(answer, answerHeaders); - - DecodeAnswer(answer, answerHeaders); - - OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer); - emitter_.EmitMessage(receiver, message); - } - - - void Execute(const OrthancStone::IObserver& receiver, - const GetOrthancImageCommand& command) - { - Orthanc::HttpClient client(orthanc_, command.GetUri()); - client.SetTimeout(command.GetTimeout()); - - CopyHttpHeaders(client, command.GetHttpHeaders()); - - std::string answer; - HttpHeaders answerHeaders; - client.ApplyAndThrowException(answer, answerHeaders); - - DecodeAnswer(answer, answerHeaders); - - command.ProcessHttpAnswer(emitter_, receiver, answer, answerHeaders); - } - - - void Execute(const OrthancStone::IObserver& receiver, - const GetOrthancWebViewerJpegCommand& command) - { - Orthanc::HttpClient client(orthanc_, command.GetUri()); - client.SetTimeout(command.GetTimeout()); - - CopyHttpHeaders(client, command.GetHttpHeaders()); - - std::string answer; - HttpHeaders answerHeaders; - client.ApplyAndThrowException(answer, answerHeaders); - - DecodeAnswer(answer, answerHeaders); - - command.ProcessHttpAnswer(emitter_, receiver, answer); - } - - - void Step() - { - std::auto_ptr<Orthanc::IDynamicObject> object(queue_.Dequeue(100)); - - if (object.get() != NULL) - { - const Item& item = dynamic_cast<Item&>(*object); - - try - { - switch (item.GetCommand().GetType()) - { - case IOracleCommand::Type_OrthancRestApi: - Execute(item.GetReceiver(), - dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand())); - break; - - case IOracleCommand::Type_GetOrthancImage: - Execute(item.GetReceiver(), - dynamic_cast<const GetOrthancImageCommand&>(item.GetCommand())); - break; - - case IOracleCommand::Type_GetOrthancWebViewerJpeg: - Execute(item.GetReceiver(), - dynamic_cast<const GetOrthancWebViewerJpegCommand&>(item.GetCommand())); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception within the oracle: " << e.What(); - emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e)); - } - catch (...) - { - LOG(ERROR) << "Native exception within the oracle"; - emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage - (item.GetCommand(), Orthanc::ErrorCode_InternalError)); - } - } - } - - - static void Worker(NativeOracle* that) - { - assert(that != NULL); - - for (;;) - { - { - boost::mutex::scoped_lock lock(that->mutex_); - if (that->state_ != State_Running) - { - return; - } - } - - that->Step(); - } - } - - - void StopInternal() - { - { - boost::mutex::scoped_lock lock(mutex_); - - if (state_ == State_Setup || - state_ == State_Stopped) - { - return; - } - else - { - state_ = State_Stopped; - } - } - - for (size_t i = 0; i < workers_.size(); i++) - { - if (workers_[i] != NULL) - { - if (workers_[i]->joinable()) - { - workers_[i]->join(); - } - - delete workers_[i]; - } - } - } - - - public: - NativeOracle(IMessageEmitter& emitter) : - emitter_(emitter), - state_(State_Setup), - workers_(4) - { - } - - virtual ~NativeOracle() - { - StopInternal(); - } - - void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc) - { - boost::mutex::scoped_lock lock(mutex_); - - if (state_ != State_Setup) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - orthanc_ = orthanc; - } - } - - void SetWorkersCount(unsigned int count) - { - boost::mutex::scoped_lock lock(mutex_); - - if (count <= 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else if (state_ != State_Setup) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - workers_.resize(count); - } - } - - void Start() - { - boost::mutex::scoped_lock lock(mutex_); - - if (state_ != State_Setup) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - state_ = State_Running; - - for (unsigned int i = 0; i < workers_.size(); i++) - { - workers_[i] = new boost::thread(Worker, this); - } - } - } - - void Stop() - { - StopInternal(); - } - - virtual void Schedule(const OrthancStone::IObserver& receiver, - IOracleCommand* command) - { - queue_.Enqueue(new Item(receiver, command)); - } - }; class NativeApplicationContext : public IMessageEmitter { private: boost::shared_mutex mutex_; - OrthancStone::MessageBroker broker_; - OrthancStone::IObservable oracleObservable_; + MessageBroker broker_; + IObservable oracleObservable_; public: NativeApplicationContext() : @@ -2265,8 +756,8 @@ } - virtual void EmitMessage(const OrthancStone::IObserver& observer, - const OrthancStone::IMessage& message) + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) { try { @@ -2288,8 +779,8 @@ public: ReaderLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) + that_(that), + lock_(that.mutex_) { } }; @@ -2303,17 +794,17 @@ public: WriterLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) + that_(that), + lock_(that.mutex_) { } - OrthancStone::MessageBroker& GetBroker() + MessageBroker& GetBroker() { return that_.broker_; } - OrthancStone::IObservable& GetOracleObservable() + IObservable& GetOracleObservable() { return that_.oracleObservable_; } @@ -2326,7 +817,12 @@ class Toto : public OrthancStone::IObserver { private: - void Handle(const Refactoring::OrthancRestApiCommand::SuccessMessage& message) + void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message) + { + printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue()); + } + + void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) { Json::Value v; message.ParseJsonBody(v); @@ -2334,24 +830,24 @@ printf("ICI [%s]\n", v.toStyledString().c_str()); } - void Handle(const Refactoring::GetOrthancImageCommand::SuccessMessage& message) + void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message) { printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); } - void Handle(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message) + void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message) { printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); } - void Handle(const Refactoring::OracleCommandExceptionMessage& message) + void Handle(const OrthancStone::OracleCommandExceptionMessage& message) { printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); switch (message.GetCommand().GetType()) { - case Refactoring::IOracleCommand::Type_GetOrthancWebViewerJpeg: - printf("URI: [%s]\n", dynamic_cast<const Refactoring::GetOrthancWebViewerJpegCommand&> + case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg: + printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&> (message.GetCommand()).GetUri().c_str()); break; @@ -2366,43 +862,47 @@ { oracle.RegisterObserverCallback (new OrthancStone::Callable - <Toto, Refactoring::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); + <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle)); + + oracle.RegisterObserverCallback + (new OrthancStone::Callable + <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); oracle.RegisterObserverCallback (new OrthancStone::Callable - <Toto, Refactoring::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); + <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); oracle.RegisterObserverCallback (new OrthancStone::Callable - <Toto, Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); + <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); oracle.RegisterObserverCallback (new OrthancStone::Callable - <Toto, Refactoring::OracleCommandExceptionMessage>(*this, &Toto::Handle)); + <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); } }; -void Run(Refactoring::NativeApplicationContext& context, - Refactoring::IOracle& oracle) +void Run(OrthancStone::NativeApplicationContext& context, + OrthancStone::IOracle& oracle) { std::auto_ptr<Toto> toto; - std::auto_ptr<Refactoring::VolumeSeriesOrthancLoader> loader1, loader2; + std::auto_ptr<OrthancStone::VolumeSeriesOrthancLoader> loader1, loader2; { - Refactoring::NativeApplicationContext::WriterLock lock(context); + OrthancStone::NativeApplicationContext::WriterLock lock(context); toto.reset(new Toto(lock.GetOracleObservable())); - loader1.reset(new Refactoring::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable())); - loader2.reset(new Refactoring::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable())); + loader1.reset(new OrthancStone::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable())); + loader2.reset(new OrthancStone::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable())); } - if (1) + if (0) { Json::Value v = Json::objectValue; v["Level"] = "Series"; v["Query"] = Json::objectValue; - std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); + std::auto_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); command->SetMethod(Orthanc::HttpMethod_Post); command->SetUri("/tools/find"); command->SetBody(v); @@ -2410,50 +910,50 @@ oracle.Schedule(*toto, command.release()); } - if (1) + if (0) { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + std::auto_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg))); command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); oracle.Schedule(*toto, command.release()); } - if (1) + if (0) { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + std::auto_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); oracle.Schedule(*toto, command.release()); } - if (1) + if (0) { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + std::auto_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); oracle.Schedule(*toto, command.release()); } - if (1) + if (0) { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + std::auto_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); command->SetHttpHeader("Accept-Encoding", "gzip"); command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); oracle.Schedule(*toto, command.release()); } - if (1) + if (0) { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); + std::auto_ptr<OrthancStone::GetOrthancImageCommand> command(new OrthancStone::GetOrthancImageCommand); command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); oracle.Schedule(*toto, command.release()); } - if (1) + if (0) { - std::auto_ptr<Refactoring::GetOrthancWebViewerJpegCommand> command(new Refactoring::GetOrthancWebViewerJpegCommand); + std::auto_ptr<OrthancStone::GetOrthancWebViewerJpegCommand> command(new OrthancStone::GetOrthancWebViewerJpegCommand); command->SetHttpHeader("Accept-Encoding", "gzip"); command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e"); command->SetQuality(90); @@ -2461,12 +961,23 @@ } + if (0) + { + for (unsigned int i = 0; i < 10; i++) + { + std::auto_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000)); + command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i)); + oracle.Schedule(*toto, command.release()); + } + } + // 2017-11-17-Anonymized //loader1->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT //loader2->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE // Delphine - loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT + //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT + loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm LOG(WARNING) << "...Waiting for Ctrl-C..."; Orthanc::SystemToolbox::ServerBarrier(); @@ -2487,9 +998,9 @@ try { - Refactoring::NativeApplicationContext context; + OrthancStone::NativeApplicationContext context; - Refactoring::NativeOracle oracle(context); + OrthancStone::ThreadedOracle oracle(context); { Orthanc::WebServiceParameters p;