# HG changeset patch # User am@osimis.io # Date 1543841609 -3600 # Node ID 4eb96c6b4e965f93a84dd29344c4fc6dbdb3cd4e # Parent 26b90b1107194173ab0719ccbff41673e4f5be88 improved handling of MONOCHROME1, background and invertion diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyAlphaLayer.cpp --- 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 } } diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyDicomLayer.cpp --- 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 diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyLayer.cpp --- 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(); } diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyLayer.h --- 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; diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyScene.cpp --- 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& 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, diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyScene.h --- 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); diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyWidget.cpp --- 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 #include +#include 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(floatBuffer_->GetConstRow(y)); @@ -88,7 +127,7 @@ v = static_cast(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 - (*this, &RadiographyWidget::OnGeometryChanged)); + new Callable + (*this, &RadiographyWidget::OnGeometryChanged)); scene_->RegisterObserverCallback( - new Callable - (*this, &RadiographyWidget::OnContentChanged)); + new Callable + (*this, &RadiographyWidget::OnContentChanged)); + + NotifyContentChanged(); // force redraw FitContent(); diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/Radiography/RadiographyWidget.h --- 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 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) diff -r 26b90b110719 -r 4eb96c6b4e96 Framework/StoneEnumerations.h --- 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);