# HG changeset patch # User am@osimis.io # Date 1542924924 -3600 # Node ID 660fe6f6bf4ac23aee691e253bab062a4499d97e # Parent 087237703d63d1a43b32b2a2b6ceb974127fbdb1 split Export in 2 diff -r 087237703d63 -r 660fe6f6bf4a Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Thu Nov 22 11:45:18 2018 +0100 +++ b/Framework/Radiography/RadiographyScene.cpp Thu Nov 22 23:15:24 2018 +0100 @@ -55,7 +55,7 @@ } } - + RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, double x, double y) : @@ -65,7 +65,7 @@ if (scene.LookupLayer(index_, x, y)) { Layers::iterator layer = scene.layers_.find(index_); - + if (layer == scene.layers_.end()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); @@ -119,7 +119,7 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - } + } @@ -145,12 +145,12 @@ useWindowing_ = false; foreground_ = foreground; } - - + + void SetAlpha(Orthanc::ImageAccessor* image) { std::auto_ptr raii(image); - + if (image == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); @@ -170,7 +170,7 @@ const std::string& utf8) { SetAlpha(font.RenderAlpha(utf8)); - } + } virtual bool GetDefaultWindowing(float& center, @@ -178,7 +178,7 @@ { return false; } - + virtual void Render(Orthanc::ImageAccessor& buffer, const AffineTransform2D& viewTransform, @@ -188,7 +188,7 @@ { return; } - + if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) { throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); @@ -198,12 +198,12 @@ GetCrop(cropX, cropY, cropWidth, cropHeight); const AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); + viewTransform, GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); Orthanc::ImageAccessor cropped; alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); - + Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); t.Apply(tmp, cropped, interpolation, true /* clear */); @@ -213,7 +213,7 @@ const unsigned int height = buffer.GetHeight(); float value = foreground_; - + if (useWindowing_) { float center, width; @@ -222,7 +222,7 @@ value = center + width / 2.0f; } } - + for (unsigned int y = 0; y < height; y++) { float *q = reinterpret_cast(buffer.GetRow(y)); @@ -231,13 +231,13 @@ for (unsigned int x = 0; x < width; x++, p++, q++) { float a = static_cast(*p) / 255.0f; - + *q = (a * value + (1.0f - a) * (*q)); } - } + } } - + virtual bool GetRange(float& minValue, float& maxValue) const { @@ -264,8 +264,8 @@ } } }; - - + + class RadiographyScene::DicomLayer : public RadiographyLayer { @@ -278,7 +278,7 @@ { return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); } - + void ApplyConverter() { @@ -288,7 +288,7 @@ converted_.reset(converter_->ConvertFrame(*source_)); } } - + public: void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) { @@ -298,7 +298,7 @@ std::string tmp; Vector pixelSpacing; - + if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && LinearAlgebra::ParseVector(pixelSpacing, tmp) && pixelSpacing.size() == 2) @@ -322,23 +322,23 @@ } } - + void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership { std::auto_ptr raii(image); - + if (image == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } SetSize(image->GetWidth(), image->GetHeight()); - + source_ = raii; ApplyConverter(); } - + virtual void Render(Orthanc::ImageAccessor& buffer, const AffineTransform2D& viewTransform, ImageInterpolation interpolation) const @@ -354,8 +354,8 @@ GetCrop(cropX, cropY, cropWidth, cropHeight); AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); + viewTransform, GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); Orthanc::ImageAccessor cropped; converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); @@ -411,7 +411,7 @@ } std::auto_ptr raii(layer); - + size_t index = countLayers_++; raii->SetIndex(index); layers_[index] = raii.release(); @@ -421,7 +421,7 @@ return *layer; } - + RadiographyScene::RadiographyScene(MessageBroker& broker) : IObserver(broker), @@ -508,7 +508,7 @@ return RegisterLayer(alpha.release()); } - + RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, unsigned int height) { @@ -539,7 +539,7 @@ return RegisterLayer(alpha.release()); } - + RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, const std::string& instance, unsigned int frame, @@ -550,12 +550,12 @@ { IWebService::HttpHeaders headers; std::string uri = "/instances/" + instance + "/tags"; - + orthanc.GetBinaryAsync( - uri, headers, - new Callable - (*this, &RadiographyScene::OnTagsReceived), NULL, - new Orthanc::SingleValueObject(layer.GetIndex())); + uri, headers, + new Callable + (*this, &RadiographyScene::OnTagsReceived), NULL, + new Orthanc::SingleValueObject(layer.GetIndex())); } { @@ -566,15 +566,15 @@ { headers["Accept-Encoding"] = "gzip"; } - + std::string uri = ("/instances/" + instance + "/frames/" + boost::lexical_cast(frame) + "/image-uint16"); - + orthanc.GetBinaryAsync( - uri, headers, - new Callable - (*this, &RadiographyScene::OnFrameReceived), NULL, - new Orthanc::SingleValueObject(layer.GetIndex())); + uri, headers, + new Callable + (*this, &RadiographyScene::OnFrameReceived), NULL, + new Orthanc::SingleValueObject(layer.GetIndex())); } return layer; @@ -585,25 +585,25 @@ { RadiographyLayer& layer = RegisterLayer(new DicomLayer); - + return layer; } - + void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) { size_t index = dynamic_cast&> - (message.GetPayload()).GetValue(); + (message.GetPayload()).GetValue(); LOG(INFO) << "JSON received: " << message.GetUri().c_str() << " (" << message.GetAnswerSize() << " bytes) for layer " << index; - + Layers::iterator layer = layers_.find(index); if (layer != layers_.end()) { assert(layer->second != NULL); - + OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); dynamic_cast(layer->second)->SetDicomTags(dicom); @@ -619,15 +619,15 @@ EmitMessage(GeometryChangedMessage(*this)); } } - + void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) { size_t index = dynamic_cast&>(message.GetPayload()).GetValue(); - + LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str() << " (" << message.GetAnswerSize() << " bytes) for layer " << index; - + Layers::iterator layer = layers_.find(index); if (layer != layers_.end()) { @@ -638,7 +638,7 @@ { content.assign(reinterpret_cast(message.GetAnswer()), message.GetAnswerSize()); } - + std::auto_ptr reader(new Orthanc::PamReader); reader->ReadFromMemory(content); dynamic_cast(layer->second)->SetSourceImage(reader.release()); @@ -661,7 +661,7 @@ return extent; } - + void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, const AffineTransform2D& viewTransform, @@ -704,13 +704,13 @@ return false; } - + void RadiographyScene::DrawBorder(CairoContext& context, unsigned int layer, double zoom) { Layers::const_iterator found = layers_.find(layer); - + if (found != layers_.end()) { context.SetSourceColor(255, 0, 0); @@ -723,7 +723,7 @@ float& maxValue) const { bool first = true; - + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) { @@ -754,22 +754,47 @@ } - // Export using PAM is faster than using PNG, but requires Orthanc - // core >= 1.4.3 void RadiographyScene::ExportDicom(OrthancApiClient& orthanc, const Orthanc::DicomMap& dicom, + const std::string& parentOrthancId, double pixelSpacingX, double pixelSpacingY, bool invert, ImageInterpolation interpolation, bool usePam) { + Json::Value createDicomRequestContent; + + Export(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); + + if (!parentOrthancId.empty()) + { + createDicomRequestContent["Parent"] = parentOrthancId; + } + + orthanc.PostJsonAsyncExpectJson( + "/tools/create-dicom", createDicomRequestContent, + new Callable + (*this, &RadiographyScene::OnDicomExported), + NULL, NULL); + } + + // Export using PAM is faster than using PNG, but requires Orthanc + // core >= 1.4.3 + void RadiographyScene::Export(Json::Value& createDicomRequestContent, + const Orthanc::DicomMap& dicom, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + ImageInterpolation interpolation, + bool usePam) + { if (pixelSpacingX <= 0 || pixelSpacingY <= 0) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - + LOG(INFO) << "Exporting DICOM"; Extent2D extent = GetSceneExtent(); @@ -787,9 +812,9 @@ static_cast(h), false); AffineTransform2D view = AffineTransform2D::Combine( - AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), - AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); - + AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), + AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); + Render(layers, view, interpolation); Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16, @@ -818,22 +843,21 @@ std::set tags; dicom.GetTags(tags); - Json::Value json = Json::objectValue; - json["Tags"] = Json::objectValue; - + createDicomRequestContent["Tags"] = Json::objectValue; + for (std::set::const_iterator - tag = tags.begin(); tag != tags.end(); ++tag) + tag = tags.begin(); tag != tags.end(); ++tag) { const Orthanc::DicomValue& value = dicom.GetValue(*tag); if (!value.IsNull() && !value.IsBinary()) { - json["Tags"][tag->Format()] = value.GetContent(); + createDicomRequestContent["Tags"][tag->Format()] = value.GetContent(); } } - json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] = - (invert ? "MONOCHROME1" : "MONOCHROME2"); + createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] = + (invert ? "MONOCHROME1" : "MONOCHROME2"); // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to // avoid floating-point numbers to grow over 16 characters, @@ -841,29 +865,24 @@ // ("dciodvfy" would complain). char buf[32]; sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); - - json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf; + + createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf; float center, width; if (GetWindowing(center, width)) { - json["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] = - boost::lexical_cast(boost::math::iround(center)); + createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] = + boost::lexical_cast(boost::math::iround(center)); - json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] = - boost::lexical_cast(boost::math::iround(width)); + createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] = + boost::lexical_cast(boost::math::iround(width)); } + // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme - json["Content"] = ("data:" + - std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + - ";base64," + base64); - - orthanc.PostJsonAsyncExpectJson( - "/tools/create-dicom", json, - new Callable - (*this, &RadiographyScene::OnDicomExported), - NULL, NULL); + createDicomRequestContent["Content"] = ("data:" + + std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + + ";base64," + base64); } @@ -880,7 +899,7 @@ const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); for (IWebService::HttpHeaders::const_iterator - it = h.begin(); it != h.end(); ++it) + it = h.begin(); it != h.end(); ++it) { printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); } diff -r 087237703d63 -r 660fe6f6bf4a Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Thu Nov 22 11:45:18 2018 +0100 +++ b/Framework/Radiography/RadiographyScene.h Thu Nov 22 23:15:24 2018 +0100 @@ -28,8 +28,8 @@ namespace OrthancStone { class RadiographyScene : - public IObserver, - public IObservable + public IObserver, + public IObservable { public: typedef OriginMessage GeometryChangedMessage; @@ -69,11 +69,11 @@ private: - class AlphaLayer; + class AlphaLayer; class DicomLayer; typedef std::map Layers; - + size_t countLayers_; bool hasWindowing_; float windowingCenter_; @@ -147,10 +147,20 @@ // core >= 1.4.3 void ExportDicom(OrthancApiClient& orthanc, const Orthanc::DicomMap& dicom, + const std::string& parentOrthancId, double pixelSpacingX, double pixelSpacingY, bool invert, ImageInterpolation interpolation, bool usePam); + + // temporary version used by VSOL because we need to send the same request at another url + void Export(Json::Value& createDicomRequestContent, + const Orthanc::DicomMap& dicom, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + ImageInterpolation interpolation, + bool usePam); }; }