diff --git a/Modules/PhotoacousticsLib/include/mitkPAVessel.h b/Modules/PhotoacousticsLib/include/mitkPAVessel.h index 8670e8c684..9b62ca173a 100644 --- a/Modules/PhotoacousticsLib/include/mitkPAVessel.h +++ b/Modules/PhotoacousticsLib/include/mitkPAVessel.h @@ -1,117 +1,116 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKVESSEL_H #define MITKVESSEL_H #include "mitkVector.h" #include "mitkPAVesselMeanderStrategy.h" #include "mitkPAInSilicoTissueVolume.h" #include "mitkPAVector.h" #include "mitkPAVesselProperties.h" #include //Includes for smart pointer usage #include "mitkCommon.h" #include "itkLightObject.h" namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT Vessel : public itk::LightObject { public: mitkClassMacroItkParent(Vessel, itk::LightObject) mitkNewMacro1Param(Self, VesselProperties::Pointer) /** * Callback function definition of a VesselMeanderStrategy */ typedef void (VesselMeanderStrategy::*CalculateNewVesselPositionCallback) (Vector::Pointer, Vector::Pointer, double, std::mt19937*); /** * @brief ExpandVessel makes this Vessel expand one step in its current direction. * After expanding, the vessel will draw itself into the given InSilicoTissueVolume. * * @param volume volume for the vessel to draw itself in * @param calculateNewPosition a callback function of the VesselMeanderStrategy class. * It is used to calculate the final position after taking the step. * @param bendingFactor a metric of how much the Vessel should bend. If set to 0 the vessel will go in a straight line. */ void ExpandVessel(mitk::pa::InSilicoTissueVolume::Pointer volume, CalculateNewVesselPositionCallback calculateNewPosition, double bendingFactor, std::mt19937* rng); /** * @brief CanBifurcate * @return true if the Vessel is ready to Bifurcate() */ bool CanBifurcate(); /** * @brief Bifurcate bifurcates this vessel into two new ones. Makes sure that the volume of the vessels stays the same. * * @return a new vessel split up from the current one. */ Vessel::Pointer Bifurcate(std::mt19937* rng); /** * @brief IsFinished * @return true if the vessel cannot expand any further */ bool IsFinished(); itkGetConstMacro(VesselProperties, VesselProperties::Pointer); protected: Vessel(VesselProperties::Pointer parameters); virtual ~Vessel(); private: const double MINIMUM_VESSEL_RADIUS = 1; const double SCALING_FACTOR = 0.33; - const double VESSEL_BIFURCATION_VARIATION_SIGMA = 0.2; const double NEW_RADIUS_MINIMUM_RELATIVE_SIZE = 0.6; const double NEW_RADIUS_MAXIMUM_RELATIVE_SIZE = 0.8; void DrawVesselInVolume(Vector::Pointer toPosition, mitk::pa::InSilicoTissueVolume::Pointer volume); VesselProperties::Pointer m_VesselProperties; VesselMeanderStrategy::Pointer m_VesselMeanderStrategy; bool m_Finished; double m_WalkedDistance; std::uniform_real_distribution<> m_RangeDistribution; std::uniform_real_distribution<> m_SignDistribution; std::uniform_real_distribution<> m_RadiusRangeDistribution; int GetSign(std::mt19937* rng); }; /** * @brief Equal A function comparing two vessels for beeing equal * * @param rightHandSide A vessel to be compared * @param leftHandSide A vessel to be compared * @param eps tolarence for comparison. You can use mitk::eps in most cases. * @param verbose flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKPHOTOACOUSTICSLIB_EXPORT bool Equal(const Vessel::Pointer leftHandSide, const Vessel::Pointer rightHandSide, double eps, bool verbose); } } #endif // MITKVESSEL_H diff --git a/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp b/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp index 64fc41bc6d..f4fe4d4ea9 100644 --- a/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp +++ b/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp @@ -1,975 +1,971 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlanarFigureMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkColorProperty.h" #include "vtkContext2D.h" #include "vtkContextDevice2D.h" #include "vtkOpenGLContextDevice2D.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include "vtkTextProperty.h" #define _USE_MATH_DEFINES #include -// offset which moves the planarfigures on top of the other content -// the crosshair is rendered into the z = 1 layer. -static const float PLANAR_OFFSET = 0.5f; - mitk::PlanarFigureMapper2D::PlanarFigureMapper2D() : m_NodeModified(true), m_NodeModifiedObserverTag(0), m_NodeModifiedObserverAdded(false), m_Initialized(false) { this->InitializeDefaultPlanarFigureProperties(); } mitk::PlanarFigureMapper2D::~PlanarFigureMapper2D() { if (m_NodeModifiedObserverAdded && GetDataNode() != nullptr) { GetDataNode()->RemoveObserver(m_NodeModifiedObserverTag); } } void mitk::PlanarFigureMapper2D::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists GetDataNode()->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(rgba[3], renderer, "opacity"); this->m_Pen->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]); } void mitk::PlanarFigureMapper2D::Initialize(mitk::BaseRenderer *) { this->m_Pen = vtkSmartPointer::New(); vtkOpenGLContextDevice2D *device = NULL; device = vtkOpenGLContextDevice2D::New(); if (device) { this->m_Context->Begin(device); device->Delete(); this->m_Initialized = true; this->m_Context->ApplyPen(this->m_Pen); } else { } } void mitk::PlanarFigureMapper2D::MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) { if (type != mitk::VtkPropRenderer::Overlay) return; if (!this->m_Initialized) { this->Initialize(renderer); } vtkOpenGLContextDevice2D::SafeDownCast( this->m_Context->GetDevice())->Begin(renderer->GetVtkRenderer()); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; // Get PlanarFigure from input mitk::PlanarFigure *planarFigure = const_cast(static_cast(GetDataNode()->GetData())); // Check if PlanarFigure has already been placed; otherwise, do nothing if (!planarFigure->IsPlaced()) { return; } // Get 2D geometry frame of PlanarFigure const mitk::PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry(); if (planarFigurePlaneGeometry == nullptr) { MITK_ERROR << "PlanarFigure does not have valid PlaneGeometry!"; return; } // Get current world 2D geometry from renderer const mitk::PlaneGeometry *rendererPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); // If the PlanarFigure geometry is a plane geometry, check if current // world plane is parallel to and within the planar figure geometry bounds // (otherwise, display nothing) if ((planarFigurePlaneGeometry != nullptr) && (rendererPlaneGeometry != nullptr)) { double planeThickness = planarFigurePlaneGeometry->GetExtentInMM(2); if (!planarFigurePlaneGeometry->IsParallel(rendererPlaneGeometry) || !(planarFigurePlaneGeometry->DistanceFromPlane(rendererPlaneGeometry) < planeThickness / 3.0)) { // Planes are not parallel or renderer plane is not within PlanarFigure // geometry bounds --> exit return; } } else { // Plane is not valid (curved reformations are not possible yet) return; } // Apply visual appearance properties from the PropertyList ApplyColorAndOpacityProperties(renderer); // Get properties from node (if present) const mitk::DataNode *node = this->GetDataNode(); this->InitializePlanarFigurePropertiesFromDataNode(node); PlanarFigureDisplayMode lineDisplayMode = PF_DEFAULT; if (m_IsSelected) { lineDisplayMode = PF_SELECTED; } else if (m_IsHovering) { lineDisplayMode = PF_HOVER; } mitk::Point2D anchorPoint; anchorPoint[0] = 0; anchorPoint[1] = 1; // render the actual lines of the PlanarFigure RenderLines(lineDisplayMode, planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); // position-offset of the annotations, is set in RenderAnnotations() and // used in RenderQuantities() double annotationOffset = 0.0; // Get Global Opacity float globalOpacity = 1.0; node->GetFloatProperty("opacity", globalOpacity); if (m_DrawControlPoints) { // draw the control-points RenderControlPoints(planarFigure, lineDisplayMode, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } // draw name near the anchor point (point located on the right) const std::string name = node->GetName(); if (m_DrawName && !name.empty()) { RenderAnnotations(renderer, name, anchorPoint, globalOpacity, lineDisplayMode, annotationOffset); } // draw feature quantities (if requested) next to the anchor point, // but under the name (that is where 'annotationOffset' is used) if (m_DrawQuantities) { RenderQuantities(planarFigure, renderer, anchorPoint, annotationOffset, globalOpacity, lineDisplayMode); } this->m_Context->GetDevice()->End(); } void mitk::PlanarFigureMapper2D::PaintPolyLine(const mitk::PlanarFigure::PolyLineType vertices, bool closed, Point2D &anchorPoint, const PlaneGeometry *planarFigurePlaneGeometry, const PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { mitk::Point2D rightMostPoint; rightMostPoint.Fill(itk::NumericTraits::min()); // transform all vertices into Point2Ds in display-Coordinates and store them in vector std::vector pointlist; for (auto iter = vertices.cbegin(); iter != vertices.cend(); ++iter) { // Draw this 2D point as OpenGL vertex mitk::Point2D displayPoint; this->TransformObjectToDisplay(*iter, displayPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); pointlist.push_back(displayPoint); if (displayPoint[0] > rightMostPoint[0]) rightMostPoint = displayPoint; } // If the planarfigure is closed, we add the first control point again. // Thus we can always use 'GL_LINE_STRIP' and get rid of strange flickering // effect when using the MESA OpenGL library. if (closed) { mitk::Point2D displayPoint; this->TransformObjectToDisplay( vertices.cbegin()[0], displayPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); pointlist.push_back(displayPoint); } // now paint all the points in one run float* points = new float[pointlist.size()*2]; for (unsigned int i = 0 ; i < pointlist.size() ; ++i) { points[i * 2] = pointlist[i][0]; points[i * 2 + 1] = pointlist[i][1]; } this->m_Context->DrawPoly(points,pointlist.size()); anchorPoint = rightMostPoint; } void mitk::PlanarFigureMapper2D::DrawMainLines(mitk::PlanarFigure *figure, Point2D &anchorPoint, const PlaneGeometry *planarFigurePlaneGeometry, const PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { const auto numberOfPolyLines = figure->GetPolyLinesSize(); for (auto loop = 0; loop < numberOfPolyLines; ++loop) { const auto polyline = figure->GetPolyLine(loop); this->PaintPolyLine( polyline, figure->IsClosed(), anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } } void mitk::PlanarFigureMapper2D::DrawHelperLines(mitk::PlanarFigure *figure, Point2D &anchorPoint, const PlaneGeometry *planarFigurePlaneGeometry, const PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { const auto numberOfHelperPolyLines = figure->GetHelperPolyLinesSize(); // Draw helper objects for (unsigned int loop = 0; loop < numberOfHelperPolyLines; ++loop) { const auto helperPolyLine = figure->GetHelperPolyLine(loop, renderer->GetScaleFactorMMPerDisplayUnit(), renderer->GetViewportSize()[1]); // Check if the current helper objects is to be painted if (!figure->IsHelperToBePainted(loop)) { continue; } // ... and once normally above the shadow. this->PaintPolyLine(helperPolyLine, false, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } } void mitk::PlanarFigureMapper2D::TransformObjectToDisplay(const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry * /*rendererGeometry*/, const mitk::BaseRenderer *renderer) { mitk::Point3D point3D; // Map circle point from local 2D geometry into 3D world space objectGeometry->Map(point2D, point3D); // Project 3D world point onto display geometry renderer->WorldToView(point3D, displayPoint); } void mitk::PlanarFigureMapper2D::DrawMarker(const mitk::Point2D &point, float *lineColor, float lineOpacity, float *markerColor, float markerOpacity, float lineWidth, PlanarFigureControlPointStyleProperty::Shape shape, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry *rendererGeometry, const mitk::BaseRenderer *renderer) { if (this->GetDataNode() != nullptr && this->GetDataNode()->GetDataInteractor().IsNull()) return; if (markerOpacity == 0 && lineOpacity == 0) return; mitk::Point2D displayPoint; this->TransformObjectToDisplay(point, displayPoint, objectGeometry, rendererGeometry, renderer); this->m_Context->GetPen()->SetColorF((double)markerColor[0], (double)markerColor[1], (double)markerColor[2], markerOpacity); this->m_Context->GetPen()->SetWidth(lineWidth); switch (shape) { case PlanarFigureControlPointStyleProperty::Square: default: { // Paint filled square if (markerOpacity > 0) { m_Context->DrawRect(displayPoint[0] - 4, displayPoint[1] - 4, 8, 8); } // Paint outline this->m_Context->GetPen()->SetColorF((double)lineColor[0], (double)lineColor[1], (double)lineColor[2], (double)lineOpacity); float* outline = new float[8]; outline[0] = displayPoint[0] - 4; outline[1] = displayPoint[1] - 4; outline[2] = outline[0]; outline[3] = displayPoint[1] + 4; outline[4] = displayPoint[0] + 4; outline[5] = outline[3]; outline[6] = outline[4]; outline[7] = outline[1]; m_Context->DrawLines(outline, 4); break; } case PlanarFigureControlPointStyleProperty::Circle: { // TODO: This code can not be reached using the properties provided in the GUI /*float radius = 4.0; if (markerOpacity > 0) { // Paint filled circle glBegin(GL_POLYGON); for (int angle = 0; angle < 8; ++angle) { float angleRad = angle * (float)3.14159 / 4.0; float x = displayPoint[0] + radius * (float)cos(angleRad); float y = displayPoint[1] + radius * (float)sin(angleRad); glVertex3f(x, y, PLANAR_OFFSET); } glEnd(); } // Paint outline glColor4f(lineColor[0], lineColor[1], lineColor[2], lineOpacity); glBegin(GL_LINE_LOOP); for (int angle = 0; angle < 8; ++angle) { float angleRad = angle * (float)3.14159 / 4.0; float x = displayPoint[0] + radius * (float)cos(angleRad); float y = displayPoint[1] + radius * (float)sin(angleRad); glVertex3f(x, y, PLANAR_OFFSET); } glEnd();*/ break; } } // end switch } void mitk::PlanarFigureMapper2D::InitializeDefaultPlanarFigureProperties() { m_IsSelected = false; m_IsHovering = false; m_DrawOutline = false; m_DrawQuantities = false; m_DrawShadow = false; m_DrawControlPoints = false; m_DrawName = true; m_DrawDashed = false; m_DrawHelperDashed = false; m_AnnotationsShadow = false; m_ShadowWidthFactor = 1.2; m_LineWidth = 1.0; m_OutlineWidth = 4.0; m_HelperlineWidth = 2.0; m_DevicePixelRatio = 1.0; m_ControlPointShape = PlanarFigureControlPointStyleProperty::Square; this->SetColorProperty(m_LineColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetFloatProperty(m_LineOpacity, PF_DEFAULT, 1.0); this->SetColorProperty(m_OutlineColor, PF_DEFAULT, 0.0, 0.0, 1.0); this->SetFloatProperty(m_OutlineOpacity, PF_DEFAULT, 1.0); this->SetColorProperty(m_HelperlineColor, PF_DEFAULT, 0.4, 0.8, 0.2); this->SetFloatProperty(m_HelperlineOpacity, PF_DEFAULT, 0.4); this->SetColorProperty(m_MarkerlineColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerlineOpacity, PF_DEFAULT, 1.0); this->SetColorProperty(m_MarkerColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerOpacity, PF_DEFAULT, 0.0); this->SetColorProperty(m_AnnotationColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetColorProperty(m_LineColor, PF_HOVER, 1.0, 0.7, 0.0); this->SetFloatProperty(m_LineOpacity, PF_HOVER, 1.0); this->SetColorProperty(m_OutlineColor, PF_HOVER, 0.0, 0.0, 1.0); this->SetFloatProperty(m_OutlineOpacity, PF_HOVER, 1.0); this->SetColorProperty(m_HelperlineColor, PF_HOVER, 0.4, 0.8, 0.2); this->SetFloatProperty(m_HelperlineOpacity, PF_HOVER, 0.4); this->SetColorProperty(m_MarkerlineColor, PF_HOVER, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerlineOpacity, PF_HOVER, 1.0); this->SetColorProperty(m_MarkerColor, PF_HOVER, 1.0, 0.6, 0.0); this->SetFloatProperty(m_MarkerOpacity, PF_HOVER, 0.2); this->SetColorProperty(m_AnnotationColor, PF_HOVER, 1.0, 0.7, 0.0); this->SetColorProperty(m_LineColor, PF_SELECTED, 1.0, 0.0, 0.0); this->SetFloatProperty(m_LineOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_OutlineColor, PF_SELECTED, 0.0, 0.0, 1.0); this->SetFloatProperty(m_OutlineOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_HelperlineColor, PF_SELECTED, 0.4, 0.8, 0.2); this->SetFloatProperty(m_HelperlineOpacity, PF_SELECTED, 0.4); this->SetColorProperty(m_MarkerlineColor, PF_SELECTED, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerlineOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_MarkerColor, PF_SELECTED, 1.0, 0.6, 0.0); this->SetFloatProperty(m_MarkerOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_AnnotationColor, PF_SELECTED, 1.0, 0.0, 0.0); } void mitk::PlanarFigureMapper2D::InitializePlanarFigurePropertiesFromDataNode(const mitk::DataNode *node) { if (node == nullptr) { return; } // if we have not added an observer for ModifiedEvents on the DataNode, // we add one now. if (!m_NodeModifiedObserverAdded) { itk::SimpleMemberCommand::Pointer nodeModifiedCommand = itk::SimpleMemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &mitk::PlanarFigureMapper2D::OnNodeModified); m_NodeModifiedObserverTag = node->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); m_NodeModifiedObserverAdded = true; } // If the DataNode has not been modified since the last execution of // this method, we do not run it now. if (!m_NodeModified) return; // Mark the current properties as unmodified m_NodeModified = false; // Get Global Opacity float globalOpacity = 1.0; node->GetFloatProperty("opacity", globalOpacity); node->GetBoolProperty("selected", m_IsSelected); node->GetBoolProperty("planarfigure.ishovering", m_IsHovering); node->GetBoolProperty("planarfigure.drawoutline", m_DrawOutline); node->GetBoolProperty("planarfigure.drawshadow", m_DrawShadow); node->GetBoolProperty("planarfigure.drawquantities", m_DrawQuantities); node->GetBoolProperty("planarfigure.drawcontrolpoints", m_DrawControlPoints); node->GetBoolProperty("planarfigure.drawname", m_DrawName); node->GetBoolProperty("planarfigure.drawdashed", m_DrawDashed); node->GetBoolProperty("planarfigure.helperline.drawdashed", m_DrawHelperDashed); node->GetFloatProperty("planarfigure.line.width", m_LineWidth); node->GetFloatProperty("planarfigure.shadow.widthmodifier", m_ShadowWidthFactor); node->GetFloatProperty("planarfigure.outline.width", m_OutlineWidth); node->GetFloatProperty("planarfigure.helperline.width", m_HelperlineWidth); node->GetFloatProperty("planarfigure.devicepixelratio", m_DevicePixelRatio); node->GetStringProperty("planarfigure.annotations.font.family", m_AnnotationFontFamily); node->GetBoolProperty("planarfigure.annotations.font.bold", m_DrawAnnotationBold); node->GetBoolProperty("planarfigure.annotations.font.italic", m_DrawAnnotationItalic); node->GetIntProperty("planarfigure.annotations.font.size", m_AnnotationSize); if (!node->GetBoolProperty("planarfigure.annotations.shadow", m_AnnotationsShadow)) { node->GetBoolProperty("planarfigure.drawshadow", m_AnnotationsShadow); } PlanarFigureControlPointStyleProperty::Pointer styleProperty = dynamic_cast(node->GetProperty("planarfigure.controlpointshape")); if (styleProperty.IsNotNull()) { m_ControlPointShape = styleProperty->GetShape(); } // Set default color and opacity // If property "planarfigure.default.*.color" exists, then use that color. Otherwise global "color" property is used. if (!node->GetColor(m_LineColor[PF_DEFAULT], nullptr, "planarfigure.default.line.color")) { node->GetColor(m_LineColor[PF_DEFAULT], nullptr, "color"); } node->GetFloatProperty("planarfigure.default.line.opacity", m_LineOpacity[PF_DEFAULT]); if (!node->GetColor(m_OutlineColor[PF_DEFAULT], nullptr, "planarfigure.default.outline.color")) { node->GetColor(m_OutlineColor[PF_DEFAULT], nullptr, "color"); } node->GetFloatProperty("planarfigure.default.outline.opacity", m_OutlineOpacity[PF_DEFAULT]); if (!node->GetColor(m_HelperlineColor[PF_DEFAULT], nullptr, "planarfigure.default.helperline.color")) { node->GetColor(m_HelperlineColor[PF_DEFAULT], nullptr, "color"); } node->GetFloatProperty("planarfigure.default.helperline.opacity", m_HelperlineOpacity[PF_DEFAULT]); node->GetColor(m_MarkerlineColor[PF_DEFAULT], nullptr, "planarfigure.default.markerline.color"); node->GetFloatProperty("planarfigure.default.markerline.opacity", m_MarkerlineOpacity[PF_DEFAULT]); node->GetColor(m_MarkerColor[PF_DEFAULT], nullptr, "planarfigure.default.marker.color"); node->GetFloatProperty("planarfigure.default.marker.opacity", m_MarkerOpacity[PF_DEFAULT]); if (!node->GetColor(m_AnnotationColor[PF_DEFAULT], nullptr, "planarfigure.default.annotation.color")) { if (!node->GetColor(m_AnnotationColor[PF_DEFAULT], nullptr, "planarfigure.default.line.color")) { node->GetColor(m_AnnotationColor[PF_DEFAULT], nullptr, "color"); } } // Set hover color and opacity node->GetColor(m_LineColor[PF_HOVER], nullptr, "planarfigure.hover.line.color"); node->GetFloatProperty("planarfigure.hover.line.opacity", m_LineOpacity[PF_HOVER]); node->GetColor(m_OutlineColor[PF_HOVER], nullptr, "planarfigure.hover.outline.color"); node->GetFloatProperty("planarfigure.hover.outline.opacity", m_OutlineOpacity[PF_HOVER]); node->GetColor(m_HelperlineColor[PF_HOVER], nullptr, "planarfigure.hover.helperline.color"); node->GetFloatProperty("planarfigure.hover.helperline.opacity", m_HelperlineOpacity[PF_HOVER]); node->GetColor(m_MarkerlineColor[PF_HOVER], nullptr, "planarfigure.hover.markerline.color"); node->GetFloatProperty("planarfigure.hover.markerline.opacity", m_MarkerlineOpacity[PF_HOVER]); node->GetColor(m_MarkerColor[PF_HOVER], nullptr, "planarfigure.hover.marker.color"); node->GetFloatProperty("planarfigure.hover.marker.opacity", m_MarkerOpacity[PF_HOVER]); if (!node->GetColor(m_AnnotationColor[PF_HOVER], nullptr, "planarfigure.hover.annotation.color")) { if (!node->GetColor(m_AnnotationColor[PF_HOVER], nullptr, "planarfigure.hover.line.color")) { node->GetColor(m_AnnotationColor[PF_HOVER], nullptr, "color"); } } // Set selected color and opacity node->GetColor(m_LineColor[PF_SELECTED], nullptr, "planarfigure.selected.line.color"); node->GetFloatProperty("planarfigure.selected.line.opacity", m_LineOpacity[PF_SELECTED]); node->GetColor(m_OutlineColor[PF_SELECTED], nullptr, "planarfigure.selected.outline.color"); node->GetFloatProperty("planarfigure.selected.outline.opacity", m_OutlineOpacity[PF_SELECTED]); node->GetColor(m_HelperlineColor[PF_SELECTED], nullptr, "planarfigure.selected.helperline.color"); node->GetFloatProperty("planarfigure.selected.helperline.opacity", m_HelperlineOpacity[PF_SELECTED]); node->GetColor(m_MarkerlineColor[PF_SELECTED], nullptr, "planarfigure.selected.markerline.color"); node->GetFloatProperty("planarfigure.selected.markerline.opacity", m_MarkerlineOpacity[PF_SELECTED]); node->GetColor(m_MarkerColor[PF_SELECTED], nullptr, "planarfigure.selected.marker.color"); node->GetFloatProperty("planarfigure.selected.marker.opacity", m_MarkerOpacity[PF_SELECTED]); if (!node->GetColor(m_AnnotationColor[PF_SELECTED], nullptr, "planarfigure.selected.annotation.color")) { if (!node->GetColor(m_AnnotationColor[PF_SELECTED], nullptr, "planarfigure.selected.line.color")) { node->GetColor(m_AnnotationColor[PF_SELECTED], nullptr, "color"); } } // adapt opacity values to global "opacity" property for (unsigned int i = 0; i < PF_COUNT; ++i) { m_LineOpacity[i] *= globalOpacity; m_OutlineOpacity[i] *= globalOpacity; m_HelperlineOpacity[i] *= globalOpacity; m_MarkerlineOpacity[i] *= globalOpacity; m_MarkerOpacity[i] *= globalOpacity; } } void mitk::PlanarFigureMapper2D::OnNodeModified() { m_NodeModified = true; } void mitk::PlanarFigureMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("visible", mitk::BoolProperty::New(true), renderer, overwrite); // node->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.isextendable", mitk::BoolProperty::New(false)); // node->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); node->AddProperty("planarfigure.drawoutline", mitk::BoolProperty::New(false)); // node->AddProperty( "planarfigure.drawquantities", mitk::BoolProperty::New(true) ); node->AddProperty("planarfigure.drawshadow", mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.drawcontrolpoints", mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.drawname", mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.drawdashed", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.helperline.drawdashed", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.annotations.font.family", mitk::StringProperty::New("Arial")); node->AddProperty("planarfigure.annotations.font.bold", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.annotations.font.italic", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.annotations.font.size", mitk::IntProperty::New(12)); node->AddProperty("planarfigure.line.width", mitk::FloatProperty::New(2.0)); node->AddProperty("planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(2.0)); node->AddProperty("planarfigure.outline.width", mitk::FloatProperty::New(2.0)); node->AddProperty("planarfigure.helperline.width", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.line.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.outline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.helperline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.markerline.color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); node->AddProperty("planarfigure.default.markerline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.marker.color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); node->AddProperty("planarfigure.default.marker.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.line.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.line.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.outline.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.outline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.helperline.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.helperline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.markerline.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.markerline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.marker.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.marker.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.line.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.line.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.outline.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.outline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.helperline.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.helperline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.markerline.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.markerline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.marker.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.marker.opacity", mitk::FloatProperty::New(1.0)); } void mitk::PlanarFigureMapper2D::RenderControlPoints(const mitk::PlanarFigure *planarFigure, const PlanarFigureDisplayMode lineDisplayMode, const mitk::PlaneGeometry *planarFigurePlaneGeometry, const mitk::PlaneGeometry *rendererPlaneGeometry, mitk::BaseRenderer *renderer) { bool isEditable = true; m_DataNode->GetBoolProperty("planarfigure.iseditable", isEditable); PlanarFigureDisplayMode pointDisplayMode = PF_DEFAULT; const unsigned int selectedControlPointsIdx = (unsigned int)planarFigure->GetSelectedControlPoint(); const unsigned int numberOfControlPoints = planarFigure->GetNumberOfControlPoints(); // Draw markers at control points (selected control point will be colored) for (unsigned int i = 0; i < numberOfControlPoints; ++i) { // Only if planar figure is marked as editable: display markers (control points) in a // different style if mouse is over them or they are selected if (isEditable) { if (i == selectedControlPointsIdx) { pointDisplayMode = PF_SELECTED; } else if (m_IsHovering && isEditable) { pointDisplayMode = PF_HOVER; } } if (m_MarkerOpacity[pointDisplayMode] == 0 && m_MarkerlineOpacity[pointDisplayMode] == 0) { continue; } if (m_DrawOutline) { // draw outlines for markers as well // linewidth for the contour is only half, as full width looks // much too thick! this->DrawMarker(planarFigure->GetControlPoint(i), m_OutlineColor[lineDisplayMode], m_MarkerlineOpacity[pointDisplayMode], m_OutlineColor[lineDisplayMode], m_MarkerOpacity[pointDisplayMode], m_OutlineWidth / 2, m_ControlPointShape, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } this->DrawMarker(planarFigure->GetControlPoint(i), m_MarkerlineColor[pointDisplayMode], m_MarkerlineOpacity[pointDisplayMode], m_MarkerColor[pointDisplayMode], m_MarkerOpacity[pointDisplayMode], m_LineWidth, m_ControlPointShape, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } if (planarFigure->IsPreviewControlPointVisible()) { this->DrawMarker(planarFigure->GetPreviewControlPoint(), m_MarkerlineColor[PF_HOVER], m_MarkerlineOpacity[PF_HOVER], m_MarkerColor[PF_HOVER], m_MarkerOpacity[PF_HOVER], m_LineWidth, m_ControlPointShape, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } } void mitk::PlanarFigureMapper2D::RenderAnnotations(mitk::BaseRenderer *, const std::string name, const mitk::Point2D anchorPoint, float globalOpacity, const PlanarFigureDisplayMode lineDisplayMode, double &annotationOffset) { if (anchorPoint[0] < mitk::eps || anchorPoint[1] < mitk::eps) { return; } vtkTextProperty* textProp = vtkTextProperty::New(); textProp->SetFontSize(m_AnnotationSize); textProp->SetFontFamilyAsString(m_AnnotationFontFamily.c_str()); textProp->SetJustificationToLeft(); textProp->SetOpacity(globalOpacity); textProp->SetShadow(0); textProp->SetBold(m_DrawAnnotationBold); textProp->SetItalic(m_DrawAnnotationItalic); mitk::Point2D offset; offset.Fill(5); mitk::Point2D scaledAnchorPoint; scaledAnchorPoint[0] = anchorPoint[0] * m_DevicePixelRatio; scaledAnchorPoint[1] = anchorPoint[1] * m_DevicePixelRatio; offset[0] = offset[0] * m_DevicePixelRatio; offset[1] = offset[1] * m_DevicePixelRatio; if(m_DrawShadow) { textProp->SetColor(0.0,0.0,0.0); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0]+1, scaledAnchorPoint[1]+offset[1]-1, name.c_str()); } textProp->SetColor(m_AnnotationColor[lineDisplayMode][0], m_AnnotationColor[lineDisplayMode][1], m_AnnotationColor[lineDisplayMode][2]); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0], scaledAnchorPoint[1]+offset[1], name.c_str()); annotationOffset -= 15.0; // annotationOffset -= m_AnnotationAnnotation->GetBoundsOnDisplay( renderer ).Size[1]; textProp->Delete(); } void mitk::PlanarFigureMapper2D::RenderQuantities(const mitk::PlanarFigure *planarFigure, mitk::BaseRenderer *, const mitk::Point2D anchorPoint, double &annotationOffset, float globalOpacity, const PlanarFigureDisplayMode lineDisplayMode) { if (anchorPoint[0] < mitk::eps || anchorPoint[1] < mitk::eps) { return; } std::stringstream quantityString; quantityString.setf(ios::fixed, ios::floatfield); quantityString.precision(1); bool firstActiveFeature = true; for (unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i) { if (planarFigure->IsFeatureActive(i) && planarFigure->IsFeatureVisible(i)) { if (!firstActiveFeature) { quantityString << " x "; } quantityString << planarFigure->GetQuantity(i) << " "; quantityString << planarFigure->GetFeatureUnit(i); firstActiveFeature = false; } } vtkTextProperty* textProp = vtkTextProperty::New(); textProp->SetFontSize(m_AnnotationSize); textProp->SetFontFamilyAsString(m_AnnotationFontFamily.c_str()); textProp->SetJustificationToLeft(); textProp->SetOpacity(globalOpacity); textProp->SetShadow(0); textProp->SetBold(m_DrawAnnotationBold); textProp->SetItalic(m_DrawAnnotationItalic); mitk::Point2D offset; offset.Fill(5); mitk::Point2D scaledAnchorPoint; scaledAnchorPoint[0] = anchorPoint[0] * m_DevicePixelRatio; scaledAnchorPoint[1] = anchorPoint[1] * m_DevicePixelRatio; offset[0] = offset[0] * m_DevicePixelRatio; offset[1] = offset[1] * m_DevicePixelRatio; if(m_DrawShadow) { textProp->SetColor(0,0,0); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0]+1, scaledAnchorPoint[1]+offset[1]-1, quantityString.str().c_str()); } textProp->SetColor(m_AnnotationColor[lineDisplayMode][0], m_AnnotationColor[lineDisplayMode][1], m_AnnotationColor[lineDisplayMode][2]); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0], scaledAnchorPoint[1]+offset[1], quantityString.str().c_str()); annotationOffset -= 15.0; // annotationOffset -= m_AnnotationAnnotation->GetBoundsOnDisplay( renderer ).Size[1]; textProp->Delete(); } void mitk::PlanarFigureMapper2D::RenderLines(const PlanarFigureDisplayMode lineDisplayMode, mitk::PlanarFigure *planarFigure, mitk::Point2D &anchorPoint, const mitk::PlaneGeometry *planarFigurePlaneGeometry, const mitk::PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { // If we want to draw an outline, we do it here if (m_DrawOutline) { const float *color = m_OutlineColor[lineDisplayMode]; const float opacity = m_OutlineOpacity[lineDisplayMode]; // convert to a float array that also contains opacity, faster GL float *colorVector = new float[4]; colorVector[0] = color[0]; colorVector[1] = color[1]; colorVector[2] = color[2]; colorVector[3] = opacity; // set the color and opacity here as it is common for all outlines this->m_Context->GetPen()->SetColorF((double)color[0], (double)color[1], (double)color[2], opacity); this->m_Context->GetPen()->SetWidth(m_OutlineWidth); if (m_DrawDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all polylines if requested this->DrawMainLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); this->m_Context->GetPen()->SetWidth(m_HelperlineWidth); if (m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all helper objects if requested this->DrawHelperLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); // cleanup delete[] colorVector; } // If we want to draw a shadow, we do it here if (m_DrawShadow) { // determine the shadow opacity const float opacity = m_OutlineOpacity[lineDisplayMode]; float shadowOpacity = 0.0f; if (opacity > 0.2f) shadowOpacity = opacity - 0.2f; // convert to a float array that also contains opacity, faster GL float *shadow = new float[4]; shadow[0] = 0; shadow[1] = 0; shadow[2] = 0; shadow[3] = shadowOpacity; // set the color and opacity here as it is common for all shadows this->m_Context->GetPen()->SetColorF(0, 0, 0, shadowOpacity); this->m_Context->GetPen()->SetWidth(m_OutlineWidth * m_ShadowWidthFactor); if (m_DrawDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all polylines if requested this->DrawMainLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); this->m_Context->GetPen()->SetWidth(m_HelperlineWidth); if (m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all helper objects if requested this->DrawHelperLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); // cleanup delete[] shadow; } // set this in brackets to avoid duplicate variables in the same scope { const float *color = m_LineColor[lineDisplayMode]; const float opacity = m_LineOpacity[lineDisplayMode]; // set the color and opacity here as it is common for all mainlines this->m_Context->GetPen()->SetColorF((double)color[0], (double)color[1], (double)color[2], (double)opacity); this->m_Context->GetPen()->SetWidth(m_LineWidth); if (m_DrawDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the main line for all polylines this->DrawMainLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); const float *helperColor = m_HelperlineColor[lineDisplayMode]; const float helperOpacity = m_HelperlineOpacity[lineDisplayMode]; // we only set the color for the helperlines as the linewidth is unchanged this->m_Context->GetPen()->SetColorF((double)helperColor[0], (double)helperColor[1], (double)helperColor[2], (double)helperOpacity); this->m_Context->GetPen()->SetWidth(m_HelperlineWidth); if (m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw helper objects this->DrawHelperLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } if (m_DrawDashed || m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); } diff --git a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h index ac83fc8041..ba64226ce7 100644 --- a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h +++ b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h @@ -1,284 +1,284 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef __itkContourExtractor2DImageFilter_h #define __itkContourExtractor2DImageFilter_h #include "itkConceptChecking.h" #include "itkImageToPathFilter.h" #include "itkNumericTraits.h" #include "itkPolyLineParametricPath.h" #include "vcl_deque.h" #include "vcl_list.h" #include namespace itk { /** \class ContourExtractor2DImageFilter * \brief Computes a list of PolyLineParametricPath objects from the contours in * a 2D image. * * Uses the "marching squares" method to compute a the iso-valued contours of * the input 2D image for a given intensity value. Multiple outputs may be * produced because an image can have multiple contours at a given level, so it * is advised to call GetNumberOfOutputs() and GetOutput(n) to retrieve all of * the contours. The contour value to be extracted can be set with * SetContourValue(). Image intensities will be linearly interpolated to provide * sub-pixel resolution for the output contours. * * The marching squares algorithm is a special case of the marching cubes * algorithm (Lorensen, William and Harvey E. Cline. Marching Cubes: A High * Resolution 3D Surface Construction Algorithm. Computer Graphics (SIGGRAPH 87 * Proceedings) 21(4) July 1987, p. 163-170). A simple explanation is available * here: http://www.essi.fr/~lingrand/MarchingCubes/algo.html * * There is a single ambiguous case in the marching squares algorithm: if a * given 2x2-pixel square has two high-valued and two low-valued pixels, each * pair diagonally adjacent. (Where high- and low-valued is with respect to the * contour value sought.) In this case, either the high-valued pixels can be * connected into the same "object" (where groups of pixels encircled by a given * contour are considered an object), or the low-valued pixels can be connected. * This is the "face connected" versus "face + vertex connected" (or 4- versus * 4-connected) distinction: high-valued pixels most be treated as one, and * low-valued as the other. By default, high-valued pixels are treated as * "face-connected" and low-valued pixels are treated as "face + vertex" * connected. To reverse this, call VertexConnectHighPixelsOn(); * * Outputs are not guaranteed to be closed paths: contours which intersect the * image edge will be left open. All other paths will be closed. (The * closed-ness of a path can be tested by checking whether the beginning point * is the same as the end point.) * * Produced paths are oriented. Following the path from beginning to end, image * intensity values lower than the contour value are to the left of the path and * intensity values grater than the contour value are to the right. In other * words, the image gradient at a path segment is (approximately) in the direct * of that segment rotated right by 90 degrees, because the image intensity * values increase from left-to-right across the segment. This means that the * generated contours will circle clockwise around "hills" of * above-contour-value intensity, and counter-clockwise around "depressions" of * below-contour-value intensity. This convention can be reversed by calling * ReverseContourOrientationOn(). * * By default the input image's largest possible region will be processed; call * SetRequestedRegion() to process a different region, or ClearRequestedRegion() * to revert to the default value. Note that the requested regions are usually * set on the output; however since paths have no notion of a "region", this * must be set at the filter level. * * This class was contributed to the Insight Journal by Zachary Pincus. * http://insight-journal.org/midas/handle.php?handle=1926/165 * * \sa Image * \sa Path * \sa PolyLineParametricPath * */ template class ITK_EXPORT ContourExtractor2DImageFilter : public ImageToPathFilter> { public: /** Extract dimension from input and output image. */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); /** Convenient typedefs for simplifying declarations. */ typedef TInputImage InputImageType; typedef PolyLineParametricPath<2> OutputPathType; /** Standard class typedefs. */ typedef ContourExtractor2DImageFilter Self; typedef ImageToPathFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(ContourExtractor2DImageFilter, ImageToPathFilter); /** Image and path typedef support. */ typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::PixelType InputPixelType; typedef typename InputImageType::IndexType InputIndexType; typedef typename InputImageType::OffsetType InputOffsetType; typedef typename InputImageType::RegionType InputRegionType; typedef typename OutputPathType::Pointer OutputPathPointer; typedef typename OutputPathType::VertexType VertexType; typedef typename OutputPathType::VertexListType VertexListType; /** Real type associated to the input pixel type. */ typedef typename NumericTraits::RealType InputRealType; typedef typename VertexListType::ConstPointer VertexListConstPointer; /** Control the orientation of the contours with reference to the image * gradient. (See class documentation.) */ itkSetMacro(ReverseContourOrientation, bool); itkGetConstReferenceMacro(ReverseContourOrientation, bool); itkBooleanMacro(ReverseContourOrientation); /** Control whether high- or low-valued pixels are vertex-connected. * Default is for low-valued pixels to be vertex-connected. * (See class documentation.) */ itkSetMacro(VertexConnectHighPixels, bool); itkGetConstReferenceMacro(VertexConnectHighPixels, bool); itkBooleanMacro(VertexConnectHighPixels); /** Control whether the largest possible input region is used, or if a * custom requested region is to be used. */ void SetRequestedRegion(const InputRegionType region); itkGetConstReferenceMacro(RequestedRegion, InputRegionType); void ClearRequestedRegion(); /** Set/Get the image intensity value that the contours should follow. * This is the equivalent of an iso-value in Marching Squares. */ itkSetMacro(ContourValue, InputRealType); itkGetConstReferenceMacro(ContourValue, InputRealType); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(DimensionShouldBe2, (Concept::SameDimension)); itkConceptMacro(InputPixelTypeComparable, (Concept::Comparable)); itkConceptMacro(InputHasPixelTraitsCheck, (Concept::HasPixelTraits)); itkConceptMacro(InputHasNumericTraitsCheck, (Concept::HasNumericTraits)); /** End concept checking */ #endif protected: ContourExtractor2DImageFilter(); virtual ~ContourExtractor2DImageFilter(); - void PrintSelf(std::ostream &os, Indent indent) const; + void PrintSelf(std::ostream &os, Indent indent) const override; void GenerateData(); /** ContourExtractor2DImageFilter manually controls the input requested * region via SetRequestedRegion and ClearRequestedRegion, so it must * override the superclass method. */ virtual void GenerateInputRequestedRegion(); private: VertexType InterpolateContourPosition(InputPixelType fromValue, InputPixelType toValue, InputIndexType fromIndex, InputOffsetType toOffset); void AddSegment(const VertexType from, const VertexType to); void FillOutputs(); ContourExtractor2DImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented InputRealType m_ContourValue; bool m_ReverseContourOrientation; bool m_VertexConnectHighPixels; bool m_UseCustomRegion; InputRegionType m_RequestedRegion; unsigned int m_NumberOfContoursCreated; // Represent each contour as deque of vertices to facilitate addition of // nodes at beginning or end. At the end of the processing, we will copy // the contour into a PolyLineParametricPath. // We subclass the deque to store an additional bit of information: an // identification number for each growing contour. We use this number so // that when it becomes necessary to merge two growing contours, we can // merge the newer one into the older one. This helps because then we can // guarantee that the output contour list is ordered from left to right, // top to bottom (in terms of the first pixel of the contour encountered // by the marching squares). Currently we make no guarantees that this // pixel is the first pixel in the contour list, just that the contours // are so ordered in the output. Ensuring this latter condition (first // pixel traversed = first pixel in contour) would be possible by either // changing the merging rules, which would make the contouring operation // slower, or by storing additional data as to which pixel was first. class ContourType : public vcl_deque { public: unsigned int m_ContourNumber; }; // Store all the growing contours in a list. We may need to delete contours // from anywhere in the sequence (when we merge them together), so we need to // use a list instead of a vector or similar. typedef vcl_list ContourContainer; typedef typename ContourContainer::iterator ContourRef; // declare the hash function we are using for the hash_map. struct VertexHash { typedef typename VertexType::CoordRepType CoordinateType; inline size_t operator()(const VertexType &k) const { // Xor the hashes of the vertices together, after multiplying the // first by some number, so that identical (x,y) vertex indices // don't all hash to the same bucket. This is a decent if not // optimal hash. const size_t hashVertex1 = this->float_hash(k[0] * 0xbeef); const size_t hashVertex2 = this->float_hash(k[1]); const size_t hashValue = hashVertex1 ^ hashVertex2; return hashValue; } // Define hash function for floats. Based on method from // http://www.brpreiss.com/books/opus4/html/page217.html inline size_t float_hash(const CoordinateType &k) const { if (k == 0) { return 0; } int exponent; CoordinateType mantissa = vcl_frexp(k, &exponent); size_t value = static_cast(vcl_fabs(mantissa)); value = (2 * value - 1) * ~0U; return value; } }; // We use a hash to associate the endpoints of each contour with the // contour itself. This makes it easy to look up which contour we should add // a new arc to. // We can't store the contours themselves in the hashtable because we // need to have two tables (one to hash from beginpoint -> contour and one // for endpoint -> contour), and sometimes will remove a contour from the // tables (if it has been closed or merged with another contour). So in the // hash table we store a reference to the contour. Because sometimes we will // need to merge contours, we need to be able to quickly remove contours // from our list when they have been merged into another. Thus, we store // an iterator pointing to the contour in the list. typedef itksys::hash_map VertexToContourMap; typedef typename VertexToContourMap::iterator VertexMapIterator; typedef typename VertexToContourMap::value_type VertexContourRefPair; // The contours we find in the image are stored here ContourContainer m_Contours; // And indexed by their beginning and ending points here VertexToContourMap m_ContourStarts; VertexToContourMap m_ContourEnds; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkContourExtractor2DImageFilter.txx" #endif #endif diff --git a/Modules/Segmentation/Algorithms/itkImageToPathFilter.h b/Modules/Segmentation/Algorithms/itkImageToPathFilter.h index aa014b6df5..cfe7c047a3 100644 --- a/Modules/Segmentation/Algorithms/itkImageToPathFilter.h +++ b/Modules/Segmentation/Algorithms/itkImageToPathFilter.h @@ -1,86 +1,86 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef __itkImageToPathFilter_h #define __itkImageToPathFilter_h #include "itkImage.h" #include "itkPathSource.h" namespace itk { /** \class ImageToPathFilter * \brief Base class for filters that take an image as input and produce an path as output. * * ImageToPathFilter is the base class for all process objects that output * path data and require image data as input. Specifically, this class * defines the SetInput() method for defining the input to a filter. * * \ingroup ImageFilters */ template class ITK_EXPORT ImageToPathFilter : public PathSource { public: /** Standard class typedefs. */ typedef ImageToPathFilter Self; typedef PathSource Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(ImageToPathFilter, PathSource); /** Some convenient typedefs. */ typedef TInputImage InputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::ConstPointer InputImageConstPointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename InputImageType::PixelType InputImagePixelType; /** ImageDimension constants */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); /** Set/Get the image input of this process object. */ using Superclass::SetInput; virtual void SetInput(const InputImageType *image); virtual void SetInput(unsigned int, const TInputImage *image); const InputImageType *GetInput(void); const InputImageType *GetInput(unsigned int idx); protected: ImageToPathFilter(); ~ImageToPathFilter(); - virtual void PrintSelf(std::ostream &os, Indent indent) const; + virtual void PrintSelf(std::ostream &os, Indent indent) const override; private: ImageToPathFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkImageToPathFilter.txx" #endif #endif diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.cpp b/Modules/Segmentation/Interactions/mitkPickingTool.cpp index 7c8fb0caa4..e93186f137 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPickingTool.cpp @@ -1,254 +1,257 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPickingTool.h" #include "mitkProperties.h" #include "mitkToolManager.h" // us #include #include #include #include #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkImageTimeSelector.h" #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, PickingTool, "PickingTool"); } mitk::PickingTool::PickingTool() : m_WorkingData(nullptr) { m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->GetPropertyList()->SetProperty("name", mitk::StringProperty::New("Picking_Seedpoint")); m_PointSetNode->GetPropertyList()->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PointSet = mitk::PointSet::New(); m_PointSetNode->SetData(m_PointSet); // Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &mitk::PickingTool::OnPointAdded); m_PointSetAddObserverTag = m_PointSet->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); // create new node for picked region m_ResultNode = mitk::DataNode::New(); // set some properties m_ResultNode->SetProperty("name", mitk::StringProperty::New("result")); m_ResultNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ResultNode->SetProperty("color", mitk::ColorProperty::New(0, 1, 0)); m_ResultNode->SetProperty("layer", mitk::IntProperty::New(1)); m_ResultNode->SetProperty("opacity", mitk::FloatProperty::New(0.33f)); } mitk::PickingTool::~PickingTool() { m_PointSet->RemoveObserver(m_PointSetAddObserverTag); } const char **mitk::PickingTool::GetXPM() const { return nullptr; } const char *mitk::PickingTool::GetName() const { return "Picking"; } us::ModuleResource mitk::PickingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Pick_48x48.png"); return resource; } void mitk::PickingTool::Activated() { Superclass::Activated(); DataStorage *dataStorage = this->GetDataStorage(); m_WorkingData = this->GetWorkingData(); // add to datastorage and enable interaction if (!dataStorage->Exists(m_PointSetNode)) dataStorage->Add(m_PointSetNode, m_WorkingData); m_SeedPointInteractor = mitk::SinglePointDataInteractor::New(); m_SeedPointInteractor->LoadStateMachine("PointSet.xml"); m_SeedPointInteractor->SetEventConfig("PointSetConfig.xml"); m_SeedPointInteractor->SetDataNode(m_PointSetNode); // now add result to data tree dataStorage->Add(m_ResultNode, m_WorkingData); } void mitk::PickingTool::Deactivated() { m_PointSet->Clear(); // remove from data storage and disable interaction GetDataStorage()->Remove(m_PointSetNode); GetDataStorage()->Remove(m_ResultNode); Superclass::Deactivated(); } mitk::DataNode *mitk::PickingTool::GetReferenceData() { return this->m_ToolManager->GetReferenceData(0); } mitk::DataStorage *mitk::PickingTool::GetDataStorage() { return this->m_ToolManager->GetDataStorage(); } mitk::DataNode *mitk::PickingTool::GetWorkingData() { return this->m_ToolManager->GetWorkingData(0); } mitk::DataNode::Pointer mitk::PickingTool::GetPointSetNode() { return m_PointSetNode; } void mitk::PickingTool::OnPointAdded() { if (m_WorkingData != this->GetWorkingData()) { DataStorage *dataStorage = this->GetDataStorage(); if (dataStorage->Exists(m_PointSetNode)) { dataStorage->Remove(m_PointSetNode); dataStorage->Add(m_PointSetNode, this->GetWorkingData()); } if (dataStorage->Exists(m_ResultNode)) { dataStorage->Remove(m_ResultNode); dataStorage->Add(m_ResultNode, this->GetWorkingData()); } m_WorkingData = this->GetWorkingData(); } // Perform region growing/picking int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetTimeStep(); mitk::PointSet::PointType seedPoint = m_PointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); // as we want to pick a region from our segmentation image use the working data from ToolManager mitk::Image::Pointer orgImage = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { // there may be 4D segmentation data even though we currently don't support that mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); mitk::Image *timedImage = timeSelector->GetOutput(); AccessByItk_2(timedImage, StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); } this->m_PointSet->Clear(); } } template void mitk::PickingTool::StartRegionGrowing(itk::Image *itkImage, mitk::BaseGeometry *imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // convert world coordinates to image indices IndexType seedIndex; imageGeometry->WorldToIndex(seedPoint, seedIndex); // perform region growing in desired segmented region regionGrower->SetInput(itkImage); regionGrower->AddSeed(seedIndex); - regionGrower->SetLower(1); - regionGrower->SetUpper(255); + // TODO: conversion added to silence warning and + // maintain existing behaviour, should be fixed + // since it's not correct e.g. for signed char + regionGrower->SetLower(static_cast(1)); + regionGrower->SetUpper(static_cast(255)); try { regionGrower->Update(); } catch (const itk::ExceptionObject &) { return; // can't work } catch (...) { return; } // Store result and preview mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput(), imageGeometry)->Clone(); mitk::LabelSetImage::Pointer resultLabelSetImage = mitk::LabelSetImage::New(); resultLabelSetImage->InitializeByLabeledImage(resultImage); m_ResultNode->SetData(resultLabelSetImage); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PickingTool::ConfirmSegmentation() { mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetProperty("name", mitk::StringProperty::New(m_WorkingData->GetName() + "_picked")); float rgb[3] = {1.0f, 0.0f, 0.0f}; m_WorkingData->GetColor(rgb); newNode->SetProperty("color", mitk::ColorProperty::New(rgb)); float opacity = 1.0f; m_WorkingData->GetOpacity(opacity, nullptr); newNode->SetProperty("opacity", mitk::FloatProperty::New(opacity)); newNode->SetData(m_ResultNode->GetData()); GetDataStorage()->Add(newNode, this->GetReferenceData()); m_WorkingData->SetVisibility(false); m_ResultNode->SetData(nullptr); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); }