# HG changeset patch # User Alain Mazy # Date 1554305957 -7200 # Node ID 841ab71cd91f9acc4fd599e3091b0d82ba3aae10 # Parent 92305ee35b1ca9b1d8f9fd1223e28431c608ccb4# Parent ac4ad0cc6fc2a7bd1a0d74b8115954cf573e622b merge diff -r ac4ad0cc6fc2 -r 841ab71cd91f Framework/Radiography/RadiographyDicomLayer.h --- a/Framework/Radiography/RadiographyDicomLayer.h Wed Apr 03 09:09:14 2019 +0200 +++ b/Framework/Radiography/RadiographyDicomLayer.h Wed Apr 03 17:39:17 2019 +0200 @@ -66,6 +66,11 @@ void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership + const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();} // currently need this access to serialize scene in plain old data to send to a WASM worker + + const DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker + void SetDicomFrameConverter(DicomFrameConverter* converter) {converter_.reset(converter);} // Takes ownership + virtual void Render(Orthanc::ImageAccessor& buffer, const AffineTransform2D& viewTransform, ImageInterpolation interpolation) const; diff -r ac4ad0cc6fc2 -r 841ab71cd91f Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Wed Apr 03 09:09:14 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.cpp Wed Apr 03 17:39:17 2019 +0200 @@ -328,6 +328,29 @@ return RegisterLayer(alpha.release()); } + RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership + const std::string& instance, + unsigned int frame, + DicomFrameConverter* converter, // takes ownership + PhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyLayer::Geometry* geometry) + { + RadiographyDicomLayer& layer = dynamic_cast(RegisterLayer(new RadiographyDicomLayer(IObservable::GetBroker(), *this))); + + layer.SetInstance(instance, frame); + + if (geometry != NULL) + { + layer.SetGeometry(*geometry); + } + + layer.SetDicomFrameConverter(converter); + layer.SetSourceImage(dicomImage); + layer.SetPreferredPhotomotricDisplayMode(preferredPhotometricDisplayMode); + + return layer; + } + RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, const std::string& instance, unsigned int frame, @@ -582,7 +605,7 @@ Render(layers, view, interpolation); std::auto_ptr rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16, - layers.GetWidth(), layers.GetHeight(), false)); + layers.GetWidth(), layers.GetHeight(), false)); Orthanc::ImageProcessing::Convert(*rendered, layers); if (invert) diff -r ac4ad0cc6fc2 -r 841ab71cd91f Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Wed Apr 03 09:09:14 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.h Wed Apr 03 17:39:17 2019 +0200 @@ -30,6 +30,7 @@ namespace OrthancStone { class RadiographyDicomLayer; + class DicomFrameConverter; class RadiographyScene : public IObserver, @@ -194,6 +195,13 @@ RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, // takes ownership RadiographyLayer::Geometry* geometry); + virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership + const std::string& instance, + unsigned int frame, + DicomFrameConverter* converter, // takes ownership + PhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyLayer::Geometry* geometry); + virtual RadiographyLayer& LoadDicomFrame(OrthancApiClient& orthanc, const std::string& instance, unsigned int frame, diff -r ac4ad0cc6fc2 -r 841ab71cd91f Framework/Radiography/RadiographySceneReader.cpp --- a/Framework/Radiography/RadiographySceneReader.cpp Wed Apr 03 09:09:14 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.cpp Wed Apr 03 17:39:17 2019 +0200 @@ -28,6 +28,112 @@ namespace OrthancStone { + + void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */, + DicomFrameConverter* dicomFrameConverter /* takes ownership */, + PhotometricDisplayMode preferredPhotometricDisplayMode + ) + { + dicomImage_.reset(dicomImage); + dicomFrameConverter_.reset(dicomFrameConverter); + preferredPhotometricDisplayMode_ = preferredPhotometricDisplayMode; + Read(input); + } + + RadiographyDicomLayer* RadiographySceneBuilder::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + return dynamic_cast(&(scene_.LoadDicomImage(dicomImage_.release(), instanceId, frame, dicomFrameConverter_.release(), preferredPhotometricDisplayMode_, geometry))); + } + + + RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + return dynamic_cast(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry))); + } + + void RadiographySceneBuilder::Read(const Json::Value& input) + { + unsigned int version = input["version"].asUInt(); + + if (version != 1) + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + RadiographyDicomLayer* dicomLayer = NULL; + for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) + { + const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; + RadiographyLayer::Geometry geometry; + + if (jsonLayer["type"].asString() == "dicom") + { + ReadLayerGeometry(geometry, jsonLayer); + dicomLayer = LoadDicom(jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), &geometry); + } + else if (jsonLayer["type"].asString() == "mask") + { + if (dicomLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask + } + ReadLayerGeometry(geometry, jsonLayer); + + float foreground = jsonLayer["foreground"].asFloat(); + std::vector corners; + for (size_t i = 0; i < jsonLayer["corners"].size(); i++) + { + Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(), + jsonLayer["corners"][(int)i]["y"].asInt()); + corners.push_back(corner); + } + + scene_.LoadMask(corners, *dicomLayer, foreground, &geometry); + } + else if (jsonLayer["type"].asString() == "text") + { + if (fontRegistry_ == NULL || fontRegistry_->GetSize() == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); // you must provide a FontRegistry if you need to re-create text layers. + } + + ReadLayerGeometry(geometry, jsonLayer); + const Orthanc::Font* font = fontRegistry_->FindFont(jsonLayer["fontName"].asString()); + if (font == NULL) // if not found, take the first font in the registry + { + font = &(fontRegistry_->GetFont(0)); + } + scene_.LoadText(*font, jsonLayer["text"].asString(), &geometry); + } + else if (jsonLayer["type"].asString() == "alpha") + { + ReadLayerGeometry(geometry, jsonLayer); + + const std::string& pngContentBase64 = jsonLayer["content"].asString(); + std::string pngContent; + std::string mimeType; + Orthanc::Toolbox::DecodeDataUriScheme(mimeType, pngContent, pngContentBase64); + + std::auto_ptr image; + if (mimeType == "image/png") + { + image.reset(new Orthanc::PngReader()); + dynamic_cast(image.get())->ReadFromMemory(pngContent); + } + else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + RadiographyAlphaLayer& layer = dynamic_cast(scene_.LoadAlphaBitmap(image.release(), &geometry)); + + if (!jsonLayer["isUsingWindowing"].asBool()) + { + layer.SetForegroundValue((float)(jsonLayer["foreground"].asDouble())); + } + } + else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + void RadiographySceneReader::Read(const Json::Value& input) { unsigned int version = input["version"].asUInt(); @@ -110,7 +216,7 @@ } } - void RadiographySceneReader::ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& jsonLayer) + void RadiographySceneBuilder::ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& jsonLayer) { {// crop unsigned int x, y, width, height; diff -r ac4ad0cc6fc2 -r 841ab71cd91f Framework/Radiography/RadiographySceneReader.h --- a/Framework/Radiography/RadiographySceneReader.h Wed Apr 03 09:09:14 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.h Wed Apr 03 17:39:17 2019 +0200 @@ -33,28 +33,57 @@ { class OrthancApiClient; - class RadiographySceneReader : public boost::noncopyable + // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene + // from a serialized scene that is passed to web-workers. + // It needs some architecturing... + class RadiographySceneBuilder : public boost::noncopyable { - RadiographyScene& scene_; - OrthancApiClient& orthancApiClient_; - const Orthanc::FontRegistry* fontRegistry_; + protected: + RadiographyScene& scene_; + const Orthanc::FontRegistry* fontRegistry_; + std::auto_ptr dicomImage_; + std::auto_ptr dicomFrameConverter_; + PhotometricDisplayMode preferredPhotometricDisplayMode_; public: - RadiographySceneReader(RadiographyScene& scene, OrthancApiClient& orthancApiClient) : + RadiographySceneBuilder(RadiographyScene& scene) : scene_(scene), - orthancApiClient_(orthancApiClient), fontRegistry_(NULL) { } - void Read(const Json::Value& output); + void Read(const Json::Value& input); + void Read(const Json::Value& input, + Orthanc::ImageAccessor* dicomImage, // takes ownership + DicomFrameConverter* dicomFrameConverter, // takes ownership + PhotometricDisplayMode preferredPhotometricDisplayMode + ); void SetFontRegistry(const Orthanc::FontRegistry& fontRegistry) { fontRegistry_ = &fontRegistry; } - private: - void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& output); + protected: + void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + }; + + + class RadiographySceneReader : public RadiographySceneBuilder + { + OrthancApiClient& orthancApiClient_; + + public: + RadiographySceneReader(RadiographyScene& scene, OrthancApiClient& orthancApiClient) : + RadiographySceneBuilder(scene), + orthancApiClient_(orthancApiClient) + { + } + + void Read(const Json::Value& input); + + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); }; } diff -r ac4ad0cc6fc2 -r 841ab71cd91f Framework/Toolbox/OrthancApiClient.h --- a/Framework/Toolbox/OrthancApiClient.h Wed Apr 03 09:09:14 2019 +0200 +++ b/Framework/Toolbox/OrthancApiClient.h Wed Apr 03 17:39:17 2019 +0200 @@ -163,6 +163,8 @@ { } + const std::string& GetBaseUrl() const {return baseUrl_;} + // schedule a GET request expecting a JSON response. void GetJsonAsync(const std::string& uri, MessageHandler* successCallback,