Mercurial > hg > orthanc-stone
changeset 432:4eb96c6b4e96 am-vsol-upgrade
improved handling of MONOCHROME1, background and invertion
author | am@osimis.io |
---|---|
date | Mon, 03 Dec 2018 13:53:29 +0100 |
parents | 26b90b110719 |
children | 8999823db8b8 |
files | Framework/Radiography/RadiographyAlphaLayer.cpp Framework/Radiography/RadiographyDicomLayer.cpp Framework/Radiography/RadiographyLayer.cpp Framework/Radiography/RadiographyLayer.h Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Radiography/RadiographyWidget.cpp Framework/Radiography/RadiographyWidget.h Framework/StoneEnumerations.h |
diffstat | 9 files changed, 113 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Radiography/RadiographyAlphaLayer.cpp Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyAlphaLayer.cpp Mon Dec 03 13:53:29 2018 +0100 @@ -86,7 +86,7 @@ float center, width; if (scene_.GetWindowing(center, width)) { - value = center + width / 2.0f; // TODO: shouldn't it be center alone ? + value = center + width / 2.0f; // set it to the maximum pixel value of the image } }
--- a/Framework/Radiography/RadiographyDicomLayer.cpp Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyDicomLayer.cpp Mon Dec 03 13:53:29 2018 +0100 @@ -63,8 +63,6 @@ SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); } - //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY()); - OrthancPlugins::DicomDatasetReader reader(dataset); unsigned int width, height; @@ -77,6 +75,18 @@ { SetSize(width, height); } + + if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION))) + { + if (tmp == "MONOCHROME1") + { + SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome1); + } + else if (tmp == "MONOCHROME2") + { + SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome2); + } + } } void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership
--- a/Framework/Radiography/RadiographyLayer.cpp Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyLayer.cpp Mon Dec 03 13:53:29 2018 +0100 @@ -185,7 +185,8 @@ index_(0), hasSize_(false), width_(0), - height_(0) + height_(0), + prefferedPhotometricDisplayMode_(PhotometricDisplayMode_Default) { UpdateTransform(); }
--- a/Framework/Radiography/RadiographyLayer.h Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyLayer.h Mon Dec 03 13:53:29 2018 +0100 @@ -30,7 +30,7 @@ class RadiographyLayer : public boost::noncopyable { friend class RadiographyScene; - + public: class Geometry { @@ -139,6 +139,8 @@ AffineTransform2D transform_; AffineTransform2D transformInverse_; Geometry geometry_; + PhotometricDisplayMode prefferedPhotometricDisplayMode_; + protected: const AffineTransform2D& GetTransform() const @@ -146,6 +148,11 @@ return transform_; } + void SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode prefferedPhotometricDisplayMode) + { + prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode; + } + private: void UpdateTransform(); @@ -257,6 +264,11 @@ virtual bool GetDefaultWindowing(float& center, float& width) const = 0; + PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const + { + return prefferedPhotometricDisplayMode_; + } + virtual void Render(Orthanc::ImageAccessor& buffer, const AffineTransform2D& viewTransform, ImageInterpolation interpolation) const = 0;
--- a/Framework/Radiography/RadiographyScene.cpp Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyScene.cpp Mon Dec 03 13:53:29 2018 +0100 @@ -166,6 +166,21 @@ } } + PhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const + { + // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer) + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + if (it->second->GetPreferredPhotomotricDisplayMode() != PhotometricDisplayMode_Default) + { + return it->second->GetPreferredPhotomotricDisplayMode(); + } + } + + return PhotometricDisplayMode_Default; + } + + void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const { for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) @@ -423,8 +438,6 @@ const AffineTransform2D& viewTransform, ImageInterpolation interpolation) const { - Orthanc::ImageProcessing::Set(buffer, 0); - // Render layers in the background-to-foreground order for (size_t index = 0; index < countLayers_; index++) { @@ -571,6 +584,9 @@ AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); + // wipe background before rendering + Orthanc::ImageProcessing::Set(layers, 0); + Render(layers, view, interpolation); Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16,
--- a/Framework/Radiography/RadiographyScene.h Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyScene.h Mon Dec 03 13:53:29 2018 +0100 @@ -23,7 +23,7 @@ #include "RadiographyLayer.h" #include "../Toolbox/OrthancApiClient.h" - +#include "Framework/StoneEnumerations.h" namespace OrthancStone { @@ -139,6 +139,8 @@ void SetWindowing(float center, float width); + PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const; + RadiographyLayer& LoadText(const Orthanc::Font& font, const std::string& utf8, RadiographyLayer::Geometry* geometry);
--- a/Framework/Radiography/RadiographyWidget.cpp Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyWidget.cpp Mon Dec 03 13:53:29 2018 +0100 @@ -21,18 +21,53 @@ #include "RadiographyWidget.h" +#include <Core/OrthancException.h> #include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> namespace OrthancStone { + + bool RadiographyWidget::IsInvertedInternal() const + { + return (scene_->GetPreferredPhotomotricDisplayMode() == PhotometricDisplayMode_Monochrome1) ^ invert_; // MONOCHROME1 images must be inverted and the user can invert the image too -> XOR the two + } + + void RadiographyWidget::RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue) + { + // wipe background before rendering + float backgroundValue = minValue; + + switch (scene_->GetPreferredPhotomotricDisplayMode()) + { + case PhotometricDisplayMode_Monochrome1: + case PhotometricDisplayMode_Default: + if (IsInvertedInternal()) + backgroundValue = maxValue; + else + backgroundValue = minValue; + break; + case PhotometricDisplayMode_Monochrome2: + if (IsInvertedInternal()) + backgroundValue = minValue; + else + backgroundValue = maxValue; + break; + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + Orthanc::ImageProcessing::Set(image, backgroundValue); + } + bool RadiographyWidget::RenderInternal(unsigned int width, unsigned int height, ImageInterpolation interpolation) { float windowCenter, windowWidth; scene_->GetWindowingWithDefault(windowCenter, windowWidth); - + float x0 = windowCenter - windowWidth / 2.0f; float x1 = windowCenter + windowWidth / 2.0f; @@ -56,8 +91,10 @@ cairoBuffer_.reset(new CairoSurface(width, height)); } + RenderBackground(*floatBuffer_, x0, x1); + scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation); - + // Conversion from Float32 to BGRA32 (cairo). Very similar to // GrayscaleFrameRenderer => TODO MERGE? @@ -65,7 +102,9 @@ cairoBuffer_->GetWriteableAccessor(target); float scaling = 255.0f / (x1 - x0); - + + bool invert = IsInvertedInternal(); + for (unsigned int y = 0; y < height; y++) { const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y)); @@ -88,7 +127,7 @@ v = static_cast<uint8_t>(scaling * (*p - x0)); // (*) } - if (invert_) + if (invert) { v = 255 - v; } @@ -172,14 +211,15 @@ void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message) { - LOG(INFO) << "Geometry has changed"; + LOG(INFO) << "Scene geometry has changed"; + FitContent(); } void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message) { - LOG(INFO) << "Content has changed"; + LOG(INFO) << "Scene content has changed"; NotifyContentChanged(); } @@ -220,12 +260,14 @@ scene_ = scene; scene_->RegisterObserverCallback( - new Callable<RadiographyWidget, RadiographyScene::GeometryChangedMessage> - (*this, &RadiographyWidget::OnGeometryChanged)); + new Callable<RadiographyWidget, RadiographyScene::GeometryChangedMessage> + (*this, &RadiographyWidget::OnGeometryChanged)); scene_->RegisterObserverCallback( - new Callable<RadiographyWidget, RadiographyScene::ContentChangedMessage> - (*this, &RadiographyWidget::OnContentChanged)); + new Callable<RadiographyWidget, RadiographyScene::ContentChangedMessage> + (*this, &RadiographyWidget::OnContentChanged)); + + NotifyContentChanged(); // force redraw FitContent();
--- a/Framework/Radiography/RadiographyWidget.h Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/Radiography/RadiographyWidget.h Mon Dec 03 13:53:29 2018 +0100 @@ -53,6 +53,10 @@ virtual bool RenderScene(CairoContext& context, const ViewportGeometry& view); + virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue); + + bool IsInvertedInternal() const; + public: RadiographyWidget(MessageBroker& broker, boost::shared_ptr<RadiographyScene> scene, // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now)
--- a/Framework/StoneEnumerations.h Thu Nov 29 19:25:15 2018 +0100 +++ b/Framework/StoneEnumerations.h Mon Dec 03 13:53:29 2018 +0100 @@ -180,6 +180,14 @@ Corner_BottomRight }; + enum PhotometricDisplayMode + { + PhotometricDisplayMode_Default, + + PhotometricDisplayMode_Monochrome1, + PhotometricDisplayMode_Monochrome2 + }; + bool StringToSopClassUid(SopClassUid& result, const std::string& source);