diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index 6070305b1d..aab3e65047 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -1,61 +1,62 @@ set(LIBPOSTFIX "Ext") # Modules must be listed according to their dependencies set(module_dirs SceneSerializationBase PlanarFigure ImageExtraction ImageStatistics LegacyAdaptors IpPicSupport MitkExt SceneSerialization GraphAlgorithms Segmentation + PlanarFigureSegmentation Qmitk QmitkExt Properties DiffusionImaging GPGPU IGT CameraCalibration IGTUI RigidRegistration RigidRegistrationUI DeformableRegistration DeformableRegistrationUI OpenCL OpenCVVideoSupport Overlays InputDevices ToFHardware ToFProcessing ToFUI US ClippingTools USUI DicomUI Simulation Remeshing Python ) set(MITK_DEFAULT_SUBPROJECTS MITK-Modules) foreach(module_dir ${module_dirs}) add_subdirectory(${module_dir}) endforeach() if(MITK_PRIVATE_MODULES) file(GLOB all_subdirs RELATIVE ${MITK_PRIVATE_MODULES} ${MITK_PRIVATE_MODULES}/*) foreach(subdir ${all_subdirs}) string(FIND ${subdir} "." _result) if(_result EQUAL -1) if(EXISTS ${MITK_PRIVATE_MODULES}/${subdir}/CMakeLists.txt) message(STATUS "Found private module ${subdir}") add_subdirectory(${MITK_PRIVATE_MODULES}/${subdir} private_modules/${subdir}) endif() endif() endforeach() endif(MITK_PRIVATE_MODULES) diff --git a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp index 9eb5967c04..da8e2286d2 100644 --- a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp +++ b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.cpp @@ -1,754 +1,781 @@ /*=================================================================== 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 "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkGL.h" #include "mitkVtkPropRenderer.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() { this->InitializeDefaultPlanarFigureProperties(); } mitk::PlanarFigureMapper2D::~PlanarFigureMapper2D() { } void mitk::PlanarFigureMapper2D::Paint( mitk::BaseRenderer *renderer ) { - bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; + // Get PlanarFigure from input mitk::PlanarFigure *planarFigure = const_cast< mitk::PlanarFigure * >( static_cast< const mitk::PlanarFigure * >( GetDataNode()->GetData() ) ); // Check if PlanarFigure has already been placed; otherwise, do nothing if ( !planarFigure->IsPlaced() ) { return; } // Get 2D geometry frame of PlanarFigure mitk::Geometry2D *planarFigureGeometry2D = dynamic_cast< Geometry2D * >( planarFigure->GetGeometry( 0 ) ); if ( planarFigureGeometry2D == NULL ) { MITK_ERROR << "PlanarFigure does not have valid Geometry2D!"; return; } // Get current world 2D geometry from renderer const mitk::Geometry2D *rendererGeometry2D = renderer->GetCurrentWorldGeometry2D(); // 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) mitk::PlaneGeometry *planarFigurePlaneGeometry = dynamic_cast< PlaneGeometry * >( planarFigureGeometry2D ); const mitk::PlaneGeometry *rendererPlaneGeometry = dynamic_cast< const PlaneGeometry * >( rendererGeometry2D ); if ( (planarFigurePlaneGeometry != NULL) && (rendererPlaneGeometry != NULL) ) { 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; } // Get display geometry mitk::DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); assert( displayGeometry != NULL ); // Apply visual appearance properties from the PropertyList ApplyColorAndOpacityProperties( renderer ); // Enable line antialiasing glEnable( GL_LINE_SMOOTH ); glHint( GL_LINE_SMOOTH_HINT, GL_NICEST ); glEnable(GL_DEPTH_TEST); // 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 firstPoint; firstPoint[0] = 0; firstPoint[1] = 1; if ( m_DrawOutline ) { // Draw the outline for all polylines if requested this->DrawMainLines( planarFigure, m_OutlineColor[lineDisplayMode], m_OutlineOpacity[lineDisplayMode], m_DrawShadow, m_OutlineWidth, m_ShadowWidthFactor, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); // Draw the outline for all helper objects if requested this->DrawHelperLines( planarFigure, m_OutlineColor[lineDisplayMode], m_OutlineOpacity[lineDisplayMode], m_DrawShadow, m_OutlineWidth, m_ShadowWidthFactor, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } // Draw the main line for all polylines this->DrawMainLines( planarFigure, m_LineColor[lineDisplayMode], m_LineOpacity[lineDisplayMode], m_DrawShadow, m_LineWidth, m_ShadowWidthFactor, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); double annotationOffset = 0.0; //Get Global Opacity float globalOpacity = 1.0; node->GetFloatProperty("opacity", globalOpacity); // draw name near the first point (if present) std::string name = node->GetName(); if ( m_DrawName && !name.empty() ) { mitk::VtkPropRenderer* openGLrenderer = dynamic_cast( renderer ); if ( openGLrenderer ) { openGLrenderer->WriteSimpleText( name, firstPoint[0] + 6.0, firstPoint[1] + 4.0, 0, 0, 0, globalOpacity ); //this is a shadow openGLrenderer->WriteSimpleText( name, firstPoint[0] + 5.0, firstPoint[1] + 5.0, m_LineColor[lineDisplayMode][0], m_LineColor[lineDisplayMode][1], m_LineColor[lineDisplayMode][2], globalOpacity ); // If drawing is successful, add approximate height to annotation offset annotationOffset -= 15.0; } } // draw feature quantities (if requested) new the first point if ( m_DrawQuantities ) { 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; } } mitk::VtkPropRenderer* openGLrenderer = dynamic_cast( renderer ); if ( openGLrenderer ) { openGLrenderer->WriteSimpleText( quantityString.str().c_str(), firstPoint[0] + 6.0, firstPoint[1] + 4.0 + annotationOffset, 0, 0, 0, globalOpacity ); //this is a shadow openGLrenderer->WriteSimpleText( quantityString.str().c_str(), firstPoint[0] + 5.0, firstPoint[1] + 5.0 + annotationOffset, m_LineColor[lineDisplayMode][0], m_LineColor[lineDisplayMode][1], m_LineColor[lineDisplayMode][2], globalOpacity ); // If drawing is successful, add approximate height to annotation offset annotationOffset -= 15.0; } } // Draw helper objects this->DrawHelperLines( planarFigure, m_HelperlineColor[lineDisplayMode], m_HelperlineOpacity[lineDisplayMode], m_DrawShadow, m_LineWidth, m_ShadowWidthFactor, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); if ( m_DrawControlPoints ) { // Draw markers at control points (selected control point will be colored) for ( unsigned int i = 0; i < planarFigure->GetNumberOfControlPoints(); ++i ) { bool isEditable = true; m_DataNode->GetBoolProperty( "planarfigure.iseditable", isEditable ); PlanarFigureDisplayMode pointDisplayMode = PF_DEFAULT; // 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 == (unsigned int) planarFigure->GetSelectedControlPoint() ) { pointDisplayMode = PF_SELECTED; } else if ( m_IsHovering ) { pointDisplayMode = PF_HOVER; } } 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, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } this->DrawMarker( planarFigure->GetControlPoint( i ), m_MarkerlineColor[pointDisplayMode], m_MarkerlineOpacity[pointDisplayMode], m_MarkerColor[pointDisplayMode], m_MarkerOpacity[pointDisplayMode], m_LineWidth, m_ControlPointShape, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } 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, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } } glLineWidth( 1.0f ); } void mitk::PlanarFigureMapper2D::PaintPolyLine( mitk::PlanarFigure::PolyLineType vertices, bool closed, float* color, float opacity, float lineWidth, Point2D& firstPoint, const Geometry2D* planarFigureGeometry2D, const Geometry2D* rendererGeometry2D, const DisplayGeometry* displayGeometry) { glColor4f( color[0], color[1], color[2], opacity ); glLineWidth(lineWidth); - glBegin( GL_LINE_STRIP ); + + if ( closed ) + { + glBegin( GL_LINE_LOOP ); + } + else + { + glBegin( GL_LINE_STRIP ); + } for ( PlanarFigure::PolyLineType::iterator iter = vertices.begin(); iter!=vertices.end(); iter++ ) { // Draw this 2D point as OpenGL vertex mitk::Point2D displayPoint; this->TransformObjectToDisplay( iter->Point, displayPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); if(iter == vertices.begin()) firstPoint = displayPoint; glVertex3f( displayPoint[0], displayPoint[1], PLANAR_OFFSET ); } if(closed && (vertices.begin() != vertices.end())) { // complete line loop to the first point again mitk::Point2D displayPoint; this->TransformObjectToDisplay( vertices.begin()->Point, displayPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); glVertex3f( displayPoint[0], displayPoint[1], PLANAR_OFFSET ); } glEnd(); } void mitk::PlanarFigureMapper2D::DrawMainLines( mitk::PlanarFigure* figure, float* color, float opacity, bool drawShadow, float lineWidth, float shadowWidthFactor, Point2D& firstPoint, const Geometry2D* planarFigureGeometry2D, const Geometry2D* rendererGeometry2D, const DisplayGeometry* displayGeometry) { + + if ( m_DrawDashed ) + { + glLineStipple(1, 0x00FF); + glEnable(GL_LINE_STIPPLE); + } + for ( unsigned short loop = 0; loop < figure->GetPolyLinesSize(); ++loop ) { PlanarFigure::PolyLineType polyline = figure->GetPolyLine(loop); if ( drawShadow ) { float* shadow = new float[3]; shadow[0] = 0; shadow[1] = 0; shadow[2] = 0; //set shadow opacity float shadowOpacity = 0.0f; if( opacity > 0.2f ) shadowOpacity = opacity - 0.2f; this->PaintPolyLine( polyline, figure->IsClosed(), shadow, shadowOpacity, lineWidth*shadowWidthFactor, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); delete shadow; } this->PaintPolyLine( polyline, figure->IsClosed(), color, opacity, lineWidth, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } + + if ( m_DrawDashed ) + { + glDisable(GL_LINE_STIPPLE); + } + } void mitk::PlanarFigureMapper2D::DrawHelperLines( mitk::PlanarFigure* figure, float* color, float opacity, bool drawShadow, float lineWidth, float shadowWidthFactor, Point2D& firstPoint, const Geometry2D* planarFigureGeometry2D, const Geometry2D* rendererGeometry2D, const DisplayGeometry* displayGeometry) { // Draw helper objects for ( unsigned int loop = 0; loop < figure->GetHelperPolyLinesSize(); ++loop ) { const mitk::PlanarFigure::PolyLineType helperPolyLine = figure->GetHelperPolyLine(loop, displayGeometry->GetScaleFactorMMPerDisplayUnit(), displayGeometry->GetDisplayHeight() ); // Check if the current helper objects is to be painted if ( !figure->IsHelperToBePainted( loop ) ) { continue; } // check if shadow shall be painted around the figure if ( drawShadow ) { float* shadow = new float[3]; shadow[0] = 0; shadow[1] = 0; shadow[2] = 0; //set shadow opacity float shadowOpacity = 0.0f; if( opacity > 0.2f ) shadowOpacity = opacity - 0.2f; // paint shadow by painting the figure twice // one in black with a slightly broader line-width ... this->PaintPolyLine( helperPolyLine, false, shadow, shadowOpacity, lineWidth*shadowWidthFactor, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); delete shadow; } // ... and once normally above the shadow. this->PaintPolyLine( helperPolyLine, false, color, opacity, lineWidth, firstPoint, planarFigureGeometry2D, rendererGeometry2D, displayGeometry ); } } void mitk::PlanarFigureMapper2D::TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::Geometry2D *objectGeometry, const mitk::Geometry2D *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) { 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 rendererGeometry->Map( point3D, displayPoint ); displayGeometry->WorldToDisplay( displayPoint, displayPoint ); } void mitk::PlanarFigureMapper2D::DrawMarker( const mitk::Point2D &point, float* lineColor, float lineOpacity, float* markerColor, float markerOpacity, float lineWidth, PlanarFigureControlPointStyleProperty::Shape shape, const mitk::Geometry2D *objectGeometry, const mitk::Geometry2D *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) { mitk::Point2D displayPoint; if ( markerOpacity == 0 && lineOpacity == 0 ) return; this->TransformObjectToDisplay( point, displayPoint, objectGeometry, rendererGeometry, displayGeometry ); glColor4f( markerColor[0], markerColor[1], markerColor[2], markerOpacity ); glLineWidth( lineWidth ); switch ( shape ) { case PlanarFigureControlPointStyleProperty::Square: default: // Paint filled square // Disable line antialiasing (does not look nice for squares) glDisable( GL_LINE_SMOOTH ); glRectf( displayPoint[0] - 4, displayPoint[1] - 4, displayPoint[0] + 4, displayPoint[1] + 4 ); // Paint outline glColor4f( lineColor[0], lineColor[1], lineColor[2], lineOpacity ); glBegin( GL_LINE_LOOP ); glVertex3f( displayPoint[0] - 4, displayPoint[1] - 4, PLANAR_OFFSET ); glVertex3f( displayPoint[0] - 4, displayPoint[1] + 4, PLANAR_OFFSET ); glVertex3f( displayPoint[0] + 4, displayPoint[1] + 4, PLANAR_OFFSET ); glVertex3f( displayPoint[0] + 4, displayPoint[1] - 4, PLANAR_OFFSET ); glEnd(); break; case PlanarFigureControlPointStyleProperty::Circle: // Paint filled circle glBegin( GL_POLYGON ); float radius = 4.0; 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 = true; m_ShadowWidthFactor = 1.2; m_LineWidth = 1.0; m_OutlineWidth = 4.0; m_HelperlineWidth = 2.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_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_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 ); } void mitk::PlanarFigureMapper2D::InitializePlanarFigurePropertiesFromDataNode( const mitk::DataNode* node ) { if ( node == NULL ) { return; } //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->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 ); PlanarFigureControlPointStyleProperty::Pointer styleProperty = dynamic_cast< PlanarFigureControlPointStyleProperty* >( 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], NULL, "planarfigure.default.line.color")) { node->GetColor( m_LineColor[PF_DEFAULT], NULL, "color" ); } node->GetFloatProperty( "planarfigure.default.line.opacity", m_LineOpacity[PF_DEFAULT] ); if( !node->GetColor( m_OutlineColor[PF_DEFAULT], NULL, "planarfigure.default.outline.color")) { node->GetColor( m_OutlineColor[PF_DEFAULT], NULL, "color" ); } node->GetFloatProperty( "planarfigure.default.outline.opacity", m_OutlineOpacity[PF_DEFAULT] ); if( !node->GetColor( m_HelperlineColor[PF_DEFAULT], NULL, "planarfigure.default.helperline.color")) { node->GetColor( m_HelperlineColor[PF_DEFAULT], NULL, "color" ); } node->GetFloatProperty( "planarfigure.default.helperline.opacity", m_HelperlineOpacity[PF_DEFAULT] ); node->GetColor( m_MarkerlineColor[PF_DEFAULT], NULL, "planarfigure.default.markerline.color" ); node->GetFloatProperty( "planarfigure.default.markerline.opacity", m_MarkerlineOpacity[PF_DEFAULT] ); node->GetColor( m_MarkerColor[PF_DEFAULT], NULL, "planarfigure.default.marker.color" ); node->GetFloatProperty( "planarfigure.default.marker.opacity", m_MarkerOpacity[PF_DEFAULT] ); //Set hover color and opacity node->GetColor( m_LineColor[PF_HOVER], NULL, "planarfigure.hover.line.color" ); node->GetFloatProperty( "planarfigure.hover.line.opacity", m_LineOpacity[PF_HOVER] ); node->GetColor( m_OutlineColor[PF_HOVER], NULL, "planarfigure.hover.outline.color" ); node->GetFloatProperty( "planarfigure.hover.outline.opacity", m_OutlineOpacity[PF_HOVER] ); node->GetColor( m_HelperlineColor[PF_HOVER], NULL, "planarfigure.hover.helperline.color" ); node->GetFloatProperty( "planarfigure.hover.helperline.opacity", m_HelperlineOpacity[PF_HOVER] ); node->GetColor( m_MarkerlineColor[PF_HOVER], NULL, "planarfigure.hover.markerline.color" ); node->GetFloatProperty( "planarfigure.hover.markerline.opacity", m_MarkerlineOpacity[PF_HOVER] ); node->GetColor( m_MarkerColor[PF_HOVER], NULL, "planarfigure.hover.marker.color" ); node->GetFloatProperty( "planarfigure.hover.marker.opacity", m_MarkerOpacity[PF_HOVER] ); //Set selected color and opacity node->GetColor( m_LineColor[PF_SELECTED], NULL, "planarfigure.selected.line.color" ); node->GetFloatProperty( "planarfigure.selected.line.opacity", m_LineOpacity[PF_SELECTED] ); node->GetColor( m_OutlineColor[PF_SELECTED], NULL, "planarfigure.selected.outline.color" ); node->GetFloatProperty( "planarfigure.selected.outline.opacity", m_OutlineOpacity[PF_SELECTED] ); node->GetColor( m_HelperlineColor[PF_SELECTED], NULL, "planarfigure.selected.helperline.color" ); node->GetFloatProperty( "planarfigure.selected.helperline.opacity", m_HelperlineOpacity[PF_SELECTED] ); node->GetColor( m_MarkerlineColor[PF_SELECTED], NULL, "planarfigure.selected.markerline.color" ); node->GetFloatProperty( "planarfigure.selected.markerline.opacity", m_MarkerlineOpacity[PF_SELECTED] ); node->GetColor( m_MarkerColor[PF_SELECTED], NULL, "planarfigure.selected.marker.color" ); node->GetFloatProperty( "planarfigure.selected.marker.opacity", m_MarkerOpacity[PF_SELECTED] ); //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::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->SetProperty("planarfigure.isextendable",mitk::BoolProperty::New(true)); //node->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); node->AddProperty( "planarfigure.drawoutline", mitk::BoolProperty::New(true) ); //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.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(2.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)); } diff --git a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h index cac9489522..48538220b1 100644 --- a/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h +++ b/Modules/PlanarFigure/Rendering/mitkPlanarFigureMapper2D.h @@ -1,244 +1,245 @@ /*=================================================================== 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 MITK_PLANAR_FIGURE_MAPPER_2D_H_ #define MITK_PLANAR_FIGURE_MAPPER_2D_H_ #include "mitkCommon.h" #include "PlanarFigureExports.h" #include "mitkGLMapper.h" #include "mitkPlanarFigure.h" #include "mitkPlanarFigureControlPointStyleProperty.h" namespace mitk { class BaseRenderer; class Contour; /** * \brief OpenGL-based mapper to render display sub-class instances of mitk::PlanarFigure * * The appearance of planar figures can be configured through properties. If no properties are specified, * default values will be used. There are four elements a planar figure consists of: * *
    *
  1. "line": the main line segments of the planar figure (note: text is drawn in the same style) *
  2. "helperline": additional line segments of planar figures, such as arrow tips, arches of angles, etc. *
  3. "outline": background which is drawn behind the lines and helperlines of the planar figure (optional) *
  4. "marker": the markers (control points) of a planar figure *
  5. "markerline": the lines by which markers (control points) are surrounded *
* * In the following, all appearance-related planar figure properties are listed: * *
    *
  1. General properties for the planar figure *
      *
    • "planarfigure.drawoutline": if true, the "outline" lines is drawn *
    • "planarfigure.drawquantities": if true, the quantities (text) associated with the planar figure is drawn *
    • "planarfigure.drawname": if true, the name specified by the dataNode is drawn *
    • "planarfigure.drawshadow": if true, a black shadow is drawn around the planar figure *
    • "planarfigure.controlpointshape": style of the control points (enum) *
    *
  2. Line widths of planar figure elements *
      *
    • "planarfigure.line.width": width of "line" segments (float value, in mm) *
    • "planarfigure.shadow.widthmodifier": the width of the shadow is defined by width of the "line" * this modifier *
    • "planarfigure.outline.width": width of "outline" segments (float value, in mm) *
    • "planarfigure.helperline.width": width of "helperline" segments (float value, in mm) *
    *
  3. Color/opacity of planar figure elements in normal mode (unselected) *
      *
    • "planarfigure.default.line.color" *
    • "planarfigure.default.line.opacity" *
    • "planarfigure.default.outline.color" *
    • "planarfigure.default.outline.opacity" *
    • "planarfigure.default.helperline.color" *
    • "planarfigure.default.helperline.opacity" *
    • "planarfigure.default.markerline.color" *
    • "planarfigure.default.markerline.opacity" *
    • "planarfigure.default.marker.color" *
    • "planarfigure.default.marker.opacity" *
    *
  4. Color/opacity of planar figure elements in hover mode (mouse-over) *
      *
    • "planarfigure.hover.line.color" *
    • "planarfigure.hover.line.opacity" *
    • "planarfigure.hover.outline.color" *
    • "planarfigure.hover.outline.opacity" *
    • "planarfigure.hover.helperline.color" *
    • "planarfigure.hover.helperline.opacity" *
    • "planarfigure.hover.markerline.color" *
    • "planarfigure.hover.markerline.opacity" *
    • "planarfigure.hover.marker.color" *
    • "planarfigure.hover.marker.opacity" *
    *
  5. Color/opacity of planar figure elements in selected mode *
      *
    • "planarfigure.selected.line.color" *
    • "planarfigure.selected.line.opacity" *
    • "planarfigure.selected.outline.color" *
    • "planarfigure.selected.outline.opacity" *
    • "planarfigure.selected.helperline.color" *
    • "planarfigure.selected.helperline.opacity" *
    • "planarfigure.selected.markerline.color;" *
    • "planarfigure.selected.markerline.opacity" *
    • "planarfigure.selected.marker.color" *
    • "planarfigure.selected.marker.opacity" *
    *
* * \ingroup Mapper */ class PlanarFigure_EXPORT PlanarFigureMapper2D : public GLMapper { public: mitkClassMacro(PlanarFigureMapper2D, GLMapper); itkNewMacro(Self); /** * reimplemented from Baseclass */ virtual void Paint(BaseRenderer * renderer); static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); protected: enum PlanarFigureDisplayMode { PF_DEFAULT = 0, PF_HOVER = 1, PF_SELECTED = 2, PF_COUNT = 3 //helper variable }; PlanarFigureMapper2D(); virtual ~PlanarFigureMapper2D(); void TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::Geometry2D *objectGeometry, const mitk::Geometry2D *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ); void DrawMarker( const mitk::Point2D &point, float* lineColor, float lineOpacity, float* markerColor, float markerOpacity, float lineWidth, PlanarFigureControlPointStyleProperty::Shape shape, const mitk::Geometry2D *objectGeometry, const mitk::Geometry2D *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ); void PaintPolyLine( mitk::PlanarFigure::PolyLineType vertices, bool closed, float* color, float opacity, float lineWidth, Point2D& firstPoint, const Geometry2D* planarFigureGeometry2D, const Geometry2D* rendererGeometry2D, const DisplayGeometry* displayGeometry); void DrawMainLines( mitk::PlanarFigure* figure, float* color, float opacity, bool drawShadow, float lineWidth, float shadowWidthFactor, Point2D& firstPoint, const Geometry2D* planarFigureGeometry2D, const Geometry2D* rendererGeometry2D, const DisplayGeometry* displayGeometry) ; void DrawHelperLines( mitk::PlanarFigure* figure, float* color, float opacity, bool drawShadow, float lineWidth, float shadowWidthFactor, Point2D& firstPoint, const Geometry2D* planarFigureGeometry2D, const Geometry2D* rendererGeometry2D, const DisplayGeometry* displayGeometry) ; void InitializeDefaultPlanarFigureProperties(); void InitializePlanarFigurePropertiesFromDataNode( const mitk::DataNode* node ); void SetColorProperty( float property[3][3], PlanarFigureDisplayMode mode, float red, float green, float blue ) { property[mode][0] = red; property[mode][1] = green; property[mode][2] = blue; } void SetFloatProperty( float* property, PlanarFigureDisplayMode mode, float value ) { property[mode] = value; } private: bool m_IsSelected; bool m_IsHovering; bool m_DrawOutline; bool m_DrawQuantities; bool m_DrawShadow; bool m_DrawControlPoints; bool m_DrawName; + bool m_DrawDashed; // the width of the shadow is defined as 'm_LineWidth * m_ShadowWidthFactor' float m_LineWidth; float m_ShadowWidthFactor; float m_OutlineWidth; float m_HelperlineWidth; float m_PointWidth; PlanarFigureControlPointStyleProperty::Shape m_ControlPointShape; float m_LineColor[3][3]; float m_LineOpacity[3]; float m_OutlineColor[3][3]; float m_OutlineOpacity[3]; float m_HelperlineColor[3][3]; float m_HelperlineOpacity[3]; float m_MarkerlineColor[3][3]; float m_MarkerlineOpacity[3]; float m_MarkerColor[3][3]; float m_MarkerOpacity[3]; }; } // namespace mitk #endif /* MITK_PLANAR_FIGURE_MAPPER_2D_H_ */ diff --git a/Modules/PlanarFigureSegmentation/CMakeLists.txt b/Modules/PlanarFigureSegmentation/CMakeLists.txt new file mode 100644 index 0000000000..0401788c7e --- /dev/null +++ b/Modules/PlanarFigureSegmentation/CMakeLists.txt @@ -0,0 +1,4 @@ + +MITK_CREATE_MODULE( PlanarFigureSegmentation + DEPENDS PlanarFigure Segmentation +) diff --git a/Modules/PlanarFigureSegmentation/files.cmake b/Modules/PlanarFigureSegmentation/files.cmake new file mode 100644 index 0000000000..4a2f2baa73 --- /dev/null +++ b/Modules/PlanarFigureSegmentation/files.cmake @@ -0,0 +1,3 @@ +set(CPP_FILES + mitkPlanarFigureSegmentationController.cpp +) diff --git a/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp new file mode 100644 index 0000000000..02588ff49f --- /dev/null +++ b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp @@ -0,0 +1,307 @@ +/*=================================================================== + +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 "mitkPlanarFigureSegmentationController.h" + +#include "mitkSurfaceToImageFilter.h" + +#include +#include +#include +#include + +#include +#include +#include "mitkImageWriter.h" +#include "mitkSurfaceVtkWriter.h" +#include "mitkImageToSurfaceFilter.h" + +#include "mitkImageAccessByItk.h" + +#include "mitkImageCast.h" + + +mitk::PlanarFigureSegmentationController::PlanarFigureSegmentationController() +: itk::Object() +, m_ReduceFilter( NULL ) +, m_NormalsFilter( NULL ) +, m_DistanceImageCreator( NULL ) +, m_ReferenceImage( NULL ) +, m_SegmentationAsImage( NULL ) +{ + InitializeFilters(); +} + +mitk::PlanarFigureSegmentationController::~PlanarFigureSegmentationController() +{ +} + +void mitk::PlanarFigureSegmentationController::SetReferenceImage( mitk::Image::Pointer referenceImage ) +{ + m_ReferenceImage = referenceImage; +} + +void mitk::PlanarFigureSegmentationController::AddPlanarFigure( mitk::PlanarFigure::Pointer planarFigure ) +{ + if ( planarFigure.IsNull() ) + return; + + bool newFigure = true; + int indexOfFigure = -1; + for( int i=0; iCreateSurfaceFromPlanarFigure( planarFigure ); + m_SurfaceList.push_back( figureAsSurface ); + indexOfFigure = m_PlanarFigureList.size() -1 ; + } + else + { + figureAsSurface = this->CreateSurfaceFromPlanarFigure( planarFigure ); + m_SurfaceList.at(indexOfFigure) = figureAsSurface; + } + + m_ReduceFilter->SetInput( indexOfFigure, figureAsSurface ); + + m_NormalsFilter->SetInput( indexOfFigure, m_ReduceFilter->GetOutput( indexOfFigure ) ); + m_DistanceImageCreator->SetInput( indexOfFigure, m_NormalsFilter->GetOutput( indexOfFigure ) ); +} + +void mitk::PlanarFigureSegmentationController::RemovePlanarFigure( mitk::PlanarFigure::Pointer planarFigure ) +{ + if ( planarFigure.IsNull() ) + return; + + bool figureFound = false; + int indexOfFigure = -1; + for( int i=0; iRemoveInputs( m_NormalsFilter->GetOutput( indexOfFigure ) ); +// m_NormalsFilter->RemoveInput( m_ReduceFilter->GetOutput( indexOfFigure ) ); +// m_ReduceFilter->RemoveInput( const_cast(m_ReduceFilter->GetInput(indexOfFigure)) ); + } + else + { + // this is not very nice! If the figure that has been removed is NOT the last + // one in the list we have to create new filters and add all remaining + // inputs again. + // + // Has to be done as the filters do not work when removing an input + // other than the last one. + + // create new filters + InitializeFilters(); + + // and add all existing surfaces + SurfaceListType::iterator surfaceIter = m_SurfaceList.begin(); + for ( surfaceIter = m_SurfaceList.begin(); surfaceIter!=m_SurfaceList.end(); surfaceIter++ ) + { + m_ReduceFilter->SetInput( indexOfFigure, (*surfaceIter) ); + m_NormalsFilter->SetInput( indexOfFigure, m_ReduceFilter->GetOutput( indexOfFigure ) ); + m_DistanceImageCreator->SetInput( indexOfFigure, m_NormalsFilter->GetOutput( indexOfFigure ) ); + } + } + + PlanarFigureListType::iterator whereIter = m_PlanarFigureList.begin(); + whereIter += indexOfFigure; + m_PlanarFigureList.erase( whereIter ); + + SurfaceListType::iterator surfaceIter = m_SurfaceList.begin(); + surfaceIter += indexOfFigure; + m_SurfaceList.erase( surfaceIter ); +} + +template +void mitk::PlanarFigureSegmentationController::GetImageBase(itk::Image* input, itk::ImageBase<3>::Pointer& result) +{ + result = input; +} + +mitk::Image::Pointer mitk::PlanarFigureSegmentationController::GetInterpolationResult() +{ + m_SegmentationAsImage = NULL; + + if ( m_PlanarFigureList.size() == 0 ) + { + m_SegmentationAsImage = mitk::Image::New(); + m_SegmentationAsImage->Initialize(mitk::MakeScalarPixelType() , *m_ReferenceImage->GetTimeSlicedGeometry()); + + return m_SegmentationAsImage; + } + + itk::ImageBase<3>::Pointer itkImage; + AccessFixedDimensionByItk_1( m_ReferenceImage.GetPointer(), GetImageBase, 3, itkImage ); + m_DistanceImageCreator->SetReferenceImage( itkImage.GetPointer() ); + + m_ReduceFilter->Update(); + m_NormalsFilter->Update(); + m_DistanceImageCreator->Update(); + + mitk::Image::Pointer distanceImage = m_DistanceImageCreator->GetOutput(); + + // Cleanup the pipeline + distanceImage->DisconnectPipeline(); + m_DistanceImageCreator = NULL; + m_NormalsFilter = NULL; + m_ReduceFilter = NULL; + itkImage = NULL; + + // If this bool flag is true, the distanceImage will be written to the + // filesystem as nrrd-image and as surface-representation. + bool debugOutput(false); + if ( debugOutput ) + { + mitk::ImageWriter::Pointer imageWriter = mitk::ImageWriter::New(); + imageWriter->SetInput( distanceImage ); + imageWriter->SetExtension( ".nrrd" ); + imageWriter->SetFileName( "v:/DistanceImage" ); + imageWriter->Update(); + } + + mitk::ImageToSurfaceFilter::Pointer imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New(); + imageToSurfaceFilter->SetInput( distanceImage ); + imageToSurfaceFilter->SetThreshold( 0 ); + imageToSurfaceFilter->Update(); + + mitk::Surface::Pointer segmentationAsSurface = imageToSurfaceFilter->GetOutput(); + + // Cleanup the pipeline + segmentationAsSurface->DisconnectPipeline(); + imageToSurfaceFilter = NULL; + + if ( debugOutput ) + { + mitk::SurfaceVtkWriter::Pointer surfaceWriter = mitk::SurfaceVtkWriter::New(); + surfaceWriter->SetInput( segmentationAsSurface ); + surfaceWriter->SetExtension( ".vtk" ); + surfaceWriter->SetFileName( "v:/DistanceImageAsSurface.vtk" ); + surfaceWriter->Update(); + } + + + mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); + surfaceToImageFilter->SetInput( segmentationAsSurface ); + surfaceToImageFilter->SetImage( m_ReferenceImage ); + surfaceToImageFilter->SetMakeOutputBinary(true); + surfaceToImageFilter->Update(); + + m_SegmentationAsImage = surfaceToImageFilter->GetOutput(); + + // Cleanup the pipeline + m_SegmentationAsImage->DisconnectPipeline(); + + return m_SegmentationAsImage; +} + + +mitk::Surface::Pointer mitk::PlanarFigureSegmentationController::CreateSurfaceFromPlanarFigure( mitk::PlanarFigure::Pointer figure ) +{ + if ( figure.IsNull() ) + { + MITK_ERROR << "Given PlanarFigure is NULL. Please provide valid PlanarFigure."; + return NULL; + } + + mitk::Surface::Pointer newSurface = mitk::Surface::New(); + + vtkSmartPointer points = vtkSmartPointer::New(); + vtkSmartPointer polygon = vtkSmartPointer::New(); + vtkSmartPointer cells = vtkSmartPointer::New(); + vtkSmartPointer polyData = vtkSmartPointer::New(); + + const mitk::Geometry2D* figureGeometry = figure->GetGeometry2D(); + + // Get the polyline + mitk::PlanarFigure::PolyLineType planarPolyLine = figure->GetPolyLine(0); + mitk::PlanarFigure::PolyLineType::iterator iter; + + // iterate over the polyline, ... + int pointCounter = 0; + for( iter = planarPolyLine.begin(); iter != planarPolyLine.end(); iter++ ) + { + // ... determine the world-coordinates + mitk::Point2D polyLinePoint = iter->Point; + mitk::Point3D pointInWorldCoordiantes; + figureGeometry->Map( polyLinePoint, pointInWorldCoordiantes ); + + // and add them as new points to the vtkPoints + points->InsertNextPoint( pointInWorldCoordiantes[0], pointInWorldCoordiantes[1], pointInWorldCoordiantes[2] ); + ++pointCounter; + } + + // create a polygon with the points of the polyline + polygon->GetPointIds()->SetNumberOfIds( pointCounter ); + for(unsigned int i = 0; i < pointCounter; i++) + { + polygon->GetPointIds()->SetId(i,i); + } + + // initialize the vtkCellArray and vtkPolyData + cells->InsertNextCell(polygon); + polyData->SetPoints(points); + polyData->SetPolys( cells ); + + // set the polydata to the surface + newSurface->SetVtkPolyData( polyData ); + + return newSurface; +} + +mitk::PlanarFigureSegmentationController::PlanarFigureListType mitk::PlanarFigureSegmentationController::GetAllPlanarFigures() +{ + return m_PlanarFigureList; +} + +void mitk::PlanarFigureSegmentationController::InitializeFilters() +{ + m_ReduceFilter = mitk::ReduceContourSetFilter::New(); + m_ReduceFilter->SetReductionType(ReduceContourSetFilter::NTH_POINT); + m_ReduceFilter->SetStepSize( 10 ); + m_NormalsFilter = mitk::ComputeContourSetNormalsFilter::New(); + m_DistanceImageCreator = mitk::CreateDistanceImageFromSurfaceFilter::New(); +} + + diff --git a/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.h b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.h new file mode 100644 index 0000000000..4a1c53706e --- /dev/null +++ b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.h @@ -0,0 +1,158 @@ +/*=================================================================== + +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 mitkPlanarFigureSegmentationController_h_Included +#define mitkPlanarFigureSegmentationController_h_Included + +#include "mitkCommon.h" +#include "PlanarFigureSegmentationExports.h" +#include "mitkImage.h" + +#include "mitkPlanarFigure.h" +#include +#include "mitkReduceContourSetFilter.h" +#include +#include + + +namespace mitk +{ + +/** +* \brief This class creates a binary image from a set of PlanarFigures. +* +* The class offers a convenient way to create a binary image from a +* variable number of contours that are represented by PlanarFigures. +* +* The first step is to create a mitkSurface for each PlanarFigure. +* +* The actual segmentation is done by interpolating these surfaces +* by means of the mitkCreateDistanceImageFromSurfaceFilter. +* +* Using the vtkMarchingCubes a surface is created from the resulting +* distance-image. This surface is then turned into a binary image using the +* mitkSurfaceToImageFilter. +*/ + +class PlanarFigureSegmentation_EXPORT PlanarFigureSegmentationController : public itk::Object +{ + public: + + mitkClassMacro(PlanarFigureSegmentationController, itk::Object); + itkNewMacro(PlanarFigureSegmentationController); /// specify the segmentation image that should be interpolated + + virtual ~PlanarFigureSegmentationController(); + + typedef std::vector PlanarFigureListType; + typedef std::vector SurfaceListType; + + /** + * \brief Setter for the reference image. + * + * The reference image is the image on which the segmentation is + * performed. It's needed to set the correct geometry information + * on the segmentation result (pixel-spacing, slice-thickness, etc.). + */ + void SetReferenceImage( Image::Pointer referenceImage ); + + /** + * \brief Adds a new PlanarFigure to be considered in the interpolation. + * + * This method can be used to add a new contour, represented by a + * PlanarFigure, to be considered in the interpolation. + * + * It is not possible to add the same PlanarFigure twice. + * + * \warn The method does NOT check if there are two PlanarFigures on + * the same slice. Doing this will lead to wrong segmentation results. + */ + void AddPlanarFigure( PlanarFigure::Pointer planarFigure ); + + void RemovePlanarFigure( mitk::PlanarFigure::Pointer planarFigure ); + + /** + * \brief Performs the interpolation and returns the result + * as binary image. + * + * This method updates the CreateDistanceImageFromSurfaceFilter + * and creates a binary image from the resulting distance-image. + * + * This is done by creating an intermediate mitk::Surface that + * represents the interpolated 3D contour using the vtkMarchingCubes. + * + * The binary image is then extracted from the surface by means of + * the mitkSurfaceToImageFilter. + */ + Image::Pointer GetInterpolationResult(); + + + /** + * \brief Method to create a surface from a PlanarFigure + * + * This method takes any kind of PlanarFigure and creates a + * surface-representation. + * The resulting surface contains of only ONE vtkPolygon that contains + * all points of the PlanarFigure's polyline. + */ + static Surface::Pointer CreateSurfaceFromPlanarFigure( PlanarFigure::Pointer figure ); + + PlanarFigureListType GetAllPlanarFigures(); + + void SetReferenceDirectionVector( mitk::Vector3D vector ); + + + + protected: + + PlanarFigureSegmentationController(); + + void InitializeFilters(); + + template + void GetImageBase(itk::Image* input, itk::ImageBase<3>::Pointer& result); + + PlanarFigureListType m_PlanarFigureList; + SurfaceListType m_SurfaceList; + + /** + * \brief Filter to reduce the number of control points. + */ + ReduceContourSetFilter::Pointer m_ReduceFilter; + + /** + * \brief Filter to compute the normals for the created surface + */ + ComputeContourSetNormalsFilter::Pointer m_NormalsFilter; + + /** + * \brief Filter to create "distance-image" from contours. + */ + CreateDistanceImageFromSurfaceFilter::Pointer m_DistanceImageCreator; + + Image::Pointer m_ReferenceImage; + + Image::Pointer m_SegmentationAsImage; + + mitk::Vector3D m_ReferenceDirectionVector; + + +}; + +} // namespace + +#endif + + diff --git a/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp b/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp index 22f8aaa001..e3f3d39d60 100644 --- a/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp @@ -1,585 +1,676 @@ /*=================================================================== 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 "mitkCreateDistanceImageFromSurfaceFilter.h" - mitk::CreateDistanceImageFromSurfaceFilter::CreateDistanceImageFromSurfaceFilter() { m_DistanceImageVolume = 50000; this->m_UseProgressBar = false; this->m_ProgressStepSize = 5; mitk::Image::Pointer output = mitk::Image::New(); this->SetNthOutput(0, output.GetPointer()); } mitk::CreateDistanceImageFromSurfaceFilter::~CreateDistanceImageFromSurfaceFilter() { } void mitk::CreateDistanceImageFromSurfaceFilter::GenerateData() { //First of all we have to build the equation-system from the existing contour-edge-points this->CreateSolutionMatrixAndFunctionValues(); //Then we solve the equation-system via QR - decomposition. The interpolation weights are obtained in that way vnl_qr solver (m_SolutionMatrix); m_Weights = solver.solve(m_FunctionValues); //Setting progressbar if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(2); //The last step is to create the distance map with the interpolated distance function this->CreateDistanceImage(); m_Centers.clear(); m_FunctionValues.clear(); m_Normals.clear(); m_Weights.clear(); m_SolutionMatrix.clear(); //Setting progressbar if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(3); } void mitk::CreateDistanceImageFromSurfaceFilter::CreateSolutionMatrixAndFunctionValues() { unsigned int numberOfInputs = this->GetNumberOfIndexedInputs(); if (numberOfInputs == 0) { MITK_ERROR << "mitk::CreateDistanceImageFromSurfaceFilter: No input available. Please set an input!" << std::endl; itkExceptionMacro("mitk::CreateDistanceImageFromSurfaceFilter: No input available. Please set an input!"); return; } //First of all we have to extract the nomals and the surface points. //Duplicated points can be eliminated Surface* currentSurface; vtkSmartPointer polyData; vtkSmartPointer currentCellNormals; vtkSmartPointer existingPolys; vtkSmartPointer existingPoints; double p[3]; PointType currentPoint; PointType normal; for (unsigned int i = 0; i < numberOfInputs; i++) { currentSurface = const_cast( this->GetInput(i) ); polyData = currentSurface->GetVtkPolyData(); if (polyData->GetNumberOfPolys() == 0) { MITK_INFO << "mitk::CreateDistanceImageFromSurfaceFilter: No input-polygons available. Please be sure the input surface consists of polygons!" << std::endl; } currentCellNormals = vtkDoubleArray::SafeDownCast(polyData->GetCellData()->GetNormals()); existingPolys = polyData->GetPolys(); existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); vtkIdType* cell (NULL); vtkIdType cellSize (0); for( existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { for ( unsigned int j = 0; j < cellSize; j++ ) { existingPoints->GetPoint(cell[j], p); currentPoint.copy_in(p); int count = std::count(m_Centers.begin() ,m_Centers.end(),currentPoint); if (count == 0) { double currentNormal[3]; currentCellNormals->GetTuple(cell[j], currentNormal); normal.copy_in(currentNormal); m_Normals.push_back(normal); m_Centers.push_back(currentPoint); } }//end for all points }//end for all cells }//end for all outputs //For we can now calculate the exact size of the centers we initialize the data structures unsigned int numberOfCenters = m_Centers.size(); m_Centers.reserve(numberOfCenters*3); m_FunctionValues.set_size(numberOfCenters*3); m_FunctionValues.fill(0); //Create inner points for (unsigned int i = 0; i < numberOfCenters; i++) { currentPoint = m_Centers.at(i); normal = m_Normals.at(i); currentPoint[0] = currentPoint[0] - normal[0]; currentPoint[1] = currentPoint[1] - normal[1]; currentPoint[2] = currentPoint[2] - normal[2]; m_Centers.push_back(currentPoint); m_FunctionValues.put(numberOfCenters+i, -1); } //Create outer points for (unsigned int i = 0; i < numberOfCenters; i++) { currentPoint = m_Centers.at(i); normal = m_Normals.at(i); currentPoint[0] = currentPoint[0] + normal[0]; currentPoint[1] = currentPoint[1] + normal[1]; currentPoint[2] = currentPoint[2] + normal[2]; m_Centers.push_back(currentPoint); m_FunctionValues.put(numberOfCenters*2+i, 1); } //Now we have created all centers and all function values. Next step is to create the solution matrix numberOfCenters = m_Centers.size(); m_SolutionMatrix.set_size(numberOfCenters, numberOfCenters); m_Weights.set_size(numberOfCenters); PointType p1; PointType p2; double norm; for (unsigned int i = 0; i < numberOfCenters; i++) { for (unsigned int j = 0; j < numberOfCenters; j++) { //Calculate the RBF value. Currently using Phi(r) = r with r is the euclidian distance between two points p1 = m_Centers.at(i); p2 = m_Centers.at(j); p1 = p1 - p2; norm = p1.two_norm(); m_SolutionMatrix(i,j) = norm; } } } void mitk::CreateDistanceImageFromSurfaceFilter::CreateDistanceImage() { DistanceImageType::Pointer distanceImg = DistanceImageType::New(); - //Determin the bounding box of the delineated contours - double xmin = m_Centers.at(0)[0]; - double ymin = m_Centers.at(0)[1]; - double zmin = m_Centers.at(0)[2]; - double xmax = m_Centers.at(0)[0]; - double ymax = m_Centers.at(0)[1]; - double zmax = m_Centers.at(0)[2]; + // Determine the bounds of the input points in index- and world-coordinates + DistanceImageType::PointType minPointInWorldCoordinates, maxPointInWorldCoordinates; + DistanceImageType::IndexType minPointInIndexCoordinates, maxPointInIndexCoordinates; - for (unsigned int i = 1; i < m_Centers.size(); i++) - { - if (xmin > m_Centers.at(i)[0]) - { - xmin = m_Centers.at(i)[0]; - } - if (ymin > m_Centers.at(i)[1]) - { - ymin = m_Centers.at(i)[1]; - } - if (zmin > m_Centers.at(i)[2]) - { - zmin = m_Centers.at(i)[2]; - } - if (xmax < m_Centers.at(i)[0]) - { - xmax = m_Centers.at(i)[0]; - } - if (ymax < m_Centers.at(i)[1]) - { - ymax = m_Centers.at(i)[1]; - } - if (zmax < m_Centers.at(i)[2]) - { - zmax = m_Centers.at(i)[2]; - } - } + DetermineBounds( minPointInWorldCoordinates, maxPointInWorldCoordinates, + minPointInIndexCoordinates, maxPointInIndexCoordinates ); - Vector3D extentMM; - extentMM[0] = xmax - xmin + 5; - extentMM[1] = ymax - ymin + 5; - extentMM[2] = zmax - zmin + 5; - //Shifting the distance image's offest to achieve an exact distance calculation - xmin = xmin - 5; - ymin = ymin - 5; - zmin = zmin - 5; + // Calculate the extent of the region that contains all given points in MM. + // To do this, we take the difference between the maximal and minimal + // index-coordinates (must not be less than 1) and multiply it with the + // spacing of the reference-image. + Vector3D extentMM; + for (unsigned int dim = 0; dim < 3; ++dim) + { + extentMM[dim] = (int) + ( + (std::max( std::abs(maxPointInIndexCoordinates[dim] - minPointInIndexCoordinates[dim]), + (DistanceImageType::IndexType::IndexValueType) 1 + ) + 1.0) // (max-index - min-index)+1 because the pixels between index 3 and 5 cover 2+1=3 pixels (pixel 3,4, and 5) + * m_ReferenceImage->GetSpacing()[dim] + ) + 1; // (int) ((...) + 1) -> we round up to the next BIGGER int value + } /* - Now create an empty distance image. The create image will always have the same size, independent from - the original image (e.g. always consists of 500000 pixels) and will have an isotropic spacing. - The spacing is calculated like the following: - The image's volume = 500000 Pixels = extentX*spacing*extentY*spacing*extentZ*spacing - So the spacing is: spacing = ( 500000 / extentX*extentY*extentZ )^(1/3) + * Now create an empty distance image. The create image will always have the same sizeOfRegion, independent from + * the original image (e.g. always consists of 500000 pixels) and will have an isotropic spacing. + * The spacing is calculated like the following: + * The image's volume = 500000 Pixels = extentX*spacing*extentY*spacing*extentZ*spacing + * So the spacing is: spacing = ( 500000 / extentX*extentY*extentZ )^(1/3) */ - double basis = (extentMM[0]*extentMM[1]*extentMM[2]) / m_DistanceImageVolume; double exponent = 1.0/3.0; double distImgSpacing = pow(basis, exponent); int tempSpacing = (distImgSpacing+0.05)*10; m_DistanceImageSpacing = (double)tempSpacing/10.0; + // calculate the number of pixels of the distance image for each direction unsigned int numberOfXPixel = extentMM[0] / m_DistanceImageSpacing; unsigned int numberOfYPixel = extentMM[1] / m_DistanceImageSpacing; unsigned int numberOfZPixel = extentMM[2] / m_DistanceImageSpacing; - DistanceImageType::SizeType size; + // We increase the sizeOfRegion by 4 as we decrease the origin by 2 later. + // This expansion of the region is necessary to achieve a complete + // interpolation. + DistanceImageType::SizeType sizeOfRegion; + sizeOfRegion[0] = numberOfXPixel + 4; + sizeOfRegion[1] = numberOfYPixel + 4; + sizeOfRegion[2] = numberOfZPixel + 4; - //Increase the distance image's size a little bit to achieve an exact distance calculation - size[0] = numberOfXPixel + 5; - size[1] = numberOfYPixel + 5; - size[2] = numberOfZPixel + 5; + // The region starts at index 0,0,0 + DistanceImageType::IndexType initialOriginAsIndex; + initialOriginAsIndex.Fill(0); - DistanceImageType::IndexType start; - start[0] = 0; - start[1] = 0; - start[2] = 0; + DistanceImageType::PointType originAsWorld = minPointInWorldCoordinates; DistanceImageType::RegionType lpRegion; - - lpRegion.SetSize(size); - lpRegion.SetIndex(start); - + lpRegion.SetSize(sizeOfRegion); + lpRegion.SetIndex(initialOriginAsIndex); + + // We initialize the itk::Image with + // * origin and direction to have it correctly placed and rotated in the world + // * the largest possible region to set the extent to be calculated + // * the isotropic spacing that we have calculated above + distanceImg->SetOrigin( originAsWorld ); + distanceImg->SetDirection( m_ReferenceImage->GetDirection() ); distanceImg->SetRegions( lpRegion ); distanceImg->SetSpacing( m_DistanceImageSpacing ); distanceImg->Allocate(); //First of all the image is initialized with the value 10 for each pixel distanceImg->FillBuffer(10); - /* - Now we must caculate the distance for each pixel. But instead of calculating the distance value - for all of the image's pixels we proceed similar to the region growing algorithm: - - 1. Take the first pixel from the narrowband_point_list and calculate the distance for each neighbor (6er) - 2. If the current index's distance value is below a certain threshold push it into the list - 3. Next iteration take the next index from the list and start with 1. again + // Now we move the origin of the distanceImage 2 index-Coordinates + // in all directions + DistanceImageType::IndexType originAsIndex; + distanceImg->TransformPhysicalPointToIndex( originAsWorld, originAsIndex ); + originAsIndex[0] -= 2; + originAsIndex[1] -= 2; + originAsIndex[2] -= 2; + distanceImg->TransformIndexToPhysicalPoint( originAsIndex, originAsWorld ); + distanceImg->SetOrigin( originAsWorld ); - This is done until the narrowband_point_list is empty. + /* + * Now we must calculate the distance for each pixel. But instead of calculating the distance value + * for all of the image's pixels we proceed similar to the region growing algorithm: + * + * 1. Take the first pixel from the narrowband_point_list and calculate the distance for each neighbor (6er) + * 2. If the current index's distance value is below a certain threshold push it into the list + * 3. Next iteration take the next index from the list and originAsIndex with 1. again + * + * This is done until the narrowband_point_list is empty. */ std::queue narrowbandPoints; PointType currentPoint = m_Centers.at(0); double distance = this->CalculateDistanceValue(currentPoint); + // create itk::Point from vnl_vector + DistanceImageType::PointType currentPointAsPoint; + currentPointAsPoint[0] = currentPoint[0]; + currentPointAsPoint[1] = currentPoint[1]; + currentPointAsPoint[2] = currentPoint[2]; + + // Transform the input point in world-coordinates to index-coordinates DistanceImageType::IndexType currentIndex; - currentIndex[0] = ( currentPoint[0]-xmin ) / m_DistanceImageSpacing; - currentIndex[1] = ( currentPoint[1]-ymin ) / m_DistanceImageSpacing; - currentIndex[2] = ( currentPoint[2]-zmin ) / m_DistanceImageSpacing; + distanceImg->TransformPhysicalPointToIndex( currentPointAsPoint, currentIndex ); + + assert( lpRegion.IsInside(currentIndex) ); // we are quite certain this should hold narrowbandPoints.push(currentIndex); distanceImg->SetPixel(currentIndex, distance); NeighborhoodImageIterator::RadiusType radius; radius.Fill(1); NeighborhoodImageIterator nIt(radius, distanceImg, distanceImg->GetLargestPossibleRegion()); unsigned int relativeNbIdx[] = {4, 10, 12, 14, 16, 22}; bool isInBounds = false; - while ( !narrowbandPoints.empty() ) { nIt.SetLocation(narrowbandPoints.front()); narrowbandPoints.pop(); + unsigned int* relativeNb = &relativeNbIdx[0]; for (int i = 0; i < 6; i++) { - nIt.GetPixel(relativeNbIdx[i], isInBounds); - if( isInBounds && nIt.GetPixel(relativeNbIdx[i]) == 10) + nIt.GetPixel(*relativeNb, isInBounds); + if( isInBounds && nIt.GetPixel(*relativeNb) == 10) { - currentIndex = nIt.GetIndex(relativeNbIdx[i]); + currentIndex = nIt.GetIndex(*relativeNb); + + // Transform the currently checked point from index-coordinates to + // world-coordinates + distanceImg->TransformIndexToPhysicalPoint( currentIndex, currentPointAsPoint ); - currentPoint[0] = currentIndex[0]*m_DistanceImageSpacing + xmin; - currentPoint[1] = currentIndex[1]*m_DistanceImageSpacing + ymin; - currentPoint[2] = currentIndex[2]*m_DistanceImageSpacing + zmin; + // create a vnl_vector + currentPoint[0] = currentPointAsPoint[0]; + currentPoint[1] = currentPointAsPoint[1]; + currentPoint[2] = currentPointAsPoint[2]; + // and check the distance distance = this->CalculateDistanceValue(currentPoint); if ( abs(distance) <= m_DistanceImageSpacing ) { - nIt.SetPixel(relativeNbIdx[i], distance); + nIt.SetPixel(*relativeNb, distance); narrowbandPoints.push(currentIndex); } } + relativeNb++; } } // Fist we set the border slices of the image to value 1000 so that we can perform a // region growing afterwards starting from the middle of the image DistanceImageType::SizeType reqSize; reqSize[0] = distanceImg->GetLargestPossibleRegion().GetSize()[0]; reqSize[1] = distanceImg->GetLargestPossibleRegion().GetSize()[1]; reqSize[2] = 1; DistanceImageType::IndexType reqStart; reqStart[0] = 0; reqStart[1] = 0; reqStart[2] = 0; DistanceImageType::RegionType reqRegion; reqRegion.SetSize(reqSize); reqRegion.SetIndex(reqStart); this->FillImageRegion(reqRegion, 1000, distanceImg); reqStart[0] = 0; reqStart[1] = 0; reqStart[2] = distanceImg->GetLargestPossibleRegion().GetSize()[2]-1; reqRegion.SetIndex(reqStart); this->FillImageRegion(reqRegion, 1000, distanceImg); reqSize[0] = 1; reqSize[1] = distanceImg->GetLargestPossibleRegion().GetSize()[1]; reqSize[2] = distanceImg->GetLargestPossibleRegion().GetSize()[2];; reqStart[0] = 0; reqStart[1] = 0; reqStart[2] = 0; reqRegion.SetSize(reqSize); reqRegion.SetIndex(reqStart); this->FillImageRegion(reqRegion, 1000, distanceImg); reqStart[0] = distanceImg->GetLargestPossibleRegion().GetSize()[0]-1; reqStart[1] = 0; reqStart[2] = 0; reqRegion.SetIndex(reqStart); this->FillImageRegion(reqRegion, 1000, distanceImg); reqSize[0] = distanceImg->GetLargestPossibleRegion().GetSize()[0]; reqSize[1] = 1; reqSize[2] = distanceImg->GetLargestPossibleRegion().GetSize()[2];; reqStart[0] = 0; reqStart[1] = 0; reqStart[2] = 0; reqRegion.SetSize(reqSize); reqRegion.SetIndex(reqStart); this->FillImageRegion(reqRegion, 1000, distanceImg); reqStart[0] = 0; reqStart[1] = distanceImg->GetLargestPossibleRegion().GetSize()[1]-1; reqStart[2] = 0; reqRegion.SetIndex(reqStart); this->FillImageRegion(reqRegion, 1000, distanceImg); // Now we make some kind of region growing from the middle of the image to set all // inner pixels to -10. In this way we assure to extract a valid surface NeighborhoodImageIterator nIt2(radius, distanceImg, distanceImg->GetLargestPossibleRegion()); currentIndex[0] = distanceImg->GetLargestPossibleRegion().GetSize()[0]*0.5; currentIndex[1] = distanceImg->GetLargestPossibleRegion().GetSize()[1]*0.5; currentIndex[2] = distanceImg->GetLargestPossibleRegion().GetSize()[2]*0.5; narrowbandPoints.push(currentIndex); distanceImg->SetPixel(currentIndex, -10); while ( !narrowbandPoints.empty() ) { nIt2.SetLocation(narrowbandPoints.front()); narrowbandPoints.pop(); for (int i = 0; i < 6; i++) { if( nIt2.GetPixel(relativeNbIdx[i]) == 10) { currentIndex = nIt2.GetIndex(relativeNbIdx[i]); nIt2.SetPixel(relativeNbIdx[i], -10); narrowbandPoints.push(currentIndex); } } } Image::Pointer resultImage = this->GetOutput(); - Point3D origin; - origin[0] = xmin; - origin[1] = ymin; - origin[2] = zmin; - + // Cast the created distance-Image from itk::Image to the mitk::Image + // that is our output. CastToMitkImage(distanceImg, resultImage); - resultImage->GetGeometry()->SetOrigin(origin); } void mitk::CreateDistanceImageFromSurfaceFilter::FillImageRegion(DistanceImageType::RegionType reqRegion, DistanceImageType::PixelType pixelValue, DistanceImageType::Pointer image) { image->SetRequestedRegion(reqRegion); ImageIterator it (image, image->GetRequestedRegion()); while (!it.IsAtEnd()) { it.Set(pixelValue); ++it; } } double mitk::CreateDistanceImageFromSurfaceFilter::CalculateDistanceValue(PointType p) { double distanceValue (0); PointType p1; PointType p2; double norm; - for (unsigned int i = 0; i < m_Centers.size(); i++) + CenterList::iterator centerIter; + InterpolationWeights::iterator weightsIter; + + for ( centerIter=m_Centers.begin(), weightsIter=m_Weights.begin(); + centerIter!=m_Centers.end(), weightsIter!=m_Weights.end(); + centerIter++, weightsIter++ ) { - p1 = m_Centers.at(i); + p1 = *centerIter; p2 = p-p1; norm = p2.two_norm(); - distanceValue = distanceValue + norm*m_Weights.get(i); + distanceValue = distanceValue + norm* (*weightsIter); } return distanceValue; } void mitk::CreateDistanceImageFromSurfaceFilter::GenerateOutputInformation() { } void mitk::CreateDistanceImageFromSurfaceFilter::PrintEquationSystem() { std::ofstream esfile; esfile.open("C:/Users/fetzer/Desktop/equationSystem/es.txt"); esfile<<"Nummber of rows: "<SetInput( 0, const_cast( surface ) ); } void mitk::CreateDistanceImageFromSurfaceFilter::SetInput( unsigned int idx, const mitk::Surface* surface ) { if ( this->GetInput(idx) != surface ) { this->SetNthInput( idx, const_cast( surface ) ); this->Modified(); } } const mitk::Surface* mitk::CreateDistanceImageFromSurfaceFilter::GetInput() { if (this->GetNumberOfIndexedInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::Surface* mitk::CreateDistanceImageFromSurfaceFilter::GetInput( unsigned int idx) { if (this->GetNumberOfIndexedInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::CreateDistanceImageFromSurfaceFilter::RemoveInputs(mitk::Surface* input) { DataObjectPointerArraySizeType nb = this->GetNumberOfIndexedInputs(); for(DataObjectPointerArraySizeType i = 0; i < nb; i++) { if( this->GetInput(i) == input ) { this->RemoveInput(i); return; } } } void mitk::CreateDistanceImageFromSurfaceFilter::Reset() { for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++) { this->PopBackInput(); } this->SetNumberOfIndexedInputs(0); this->SetNumberOfIndexedOutputs(1); mitk::Image::Pointer output = mitk::Image::New(); this->SetNthOutput(0, output.GetPointer()); } void mitk::CreateDistanceImageFromSurfaceFilter::SetUseProgressBar(bool status) { this->m_UseProgressBar = status; } void mitk::CreateDistanceImageFromSurfaceFilter::SetProgressStepSize(unsigned int stepSize) { this->m_ProgressStepSize = stepSize; } + +void mitk::CreateDistanceImageFromSurfaceFilter::SetReferenceImage( itk::ImageBase<3>::Pointer referenceImage ) +{ + m_ReferenceImage = referenceImage; +} + +void mitk::CreateDistanceImageFromSurfaceFilter::DetermineBounds( DistanceImageType::PointType &minPointInWorldCoordinates, + DistanceImageType::PointType &maxPointInWorldCoordinates, + DistanceImageType::IndexType &minPointInIndexCoordinates, + DistanceImageType::IndexType &maxPointInIndexCoordinates ) +{ + PointType firstCenter = m_Centers.at(0); + DistanceImageType::PointType tmpPoint; + tmpPoint[0] = firstCenter[0]; + tmpPoint[1] = firstCenter[1]; + tmpPoint[2] = firstCenter[2]; + + // transform the first point from world-coordinates to index-coordinates + DistanceImageType::IndexType tmpIndex; + m_ReferenceImage->TransformPhysicalPointToIndex( tmpPoint, tmpIndex ); + + // initialize the variables with this first point + int xmin = tmpIndex[0]; + int ymin = tmpIndex[1]; + int zmin = tmpIndex[2]; + int xmax = tmpIndex[0]; + int ymax = tmpIndex[1]; + int zmax = tmpIndex[2]; + + // iterate over the rest of the points + CenterList::iterator centerIter = m_Centers.begin(); + for ( ++centerIter; centerIter!=m_Centers.end(); centerIter++) + { + tmpPoint[0] = (*centerIter)[0]; + tmpPoint[1] = (*centerIter)[1]; + tmpPoint[2] = (*centerIter)[2]; + + // transform each point from world-coordinates to index-coordinates + m_ReferenceImage->TransformPhysicalPointToIndex( tmpPoint, tmpIndex ); + + // and set the variables accordingly to find the minimum + // and maximum in all directions in index-coordinates + if (xmin > tmpIndex[0]) + { + xmin = tmpIndex[0]; + } + if (ymin > tmpIndex[1]) + { + ymin = tmpIndex[1]; + } + if (zmin > tmpIndex[2]) + { + zmin = tmpIndex[2]; + } + if (xmax < tmpIndex[0]) + { + xmax = tmpIndex[0]; + } + if (ymax < tmpIndex[1]) + { + ymax = tmpIndex[1]; + } + if (zmax < tmpIndex[2]) + { + zmax = tmpIndex[2]; + } + } + + // put the found coordinates into Index-Points + minPointInIndexCoordinates[0] = xmin; + minPointInIndexCoordinates[1] = ymin; + minPointInIndexCoordinates[2] = zmin; + + maxPointInIndexCoordinates[0] = xmax; + maxPointInIndexCoordinates[1] = ymax; + maxPointInIndexCoordinates[2] = zmax; + + // and transform them into world-coordinates + m_ReferenceImage->TransformIndexToPhysicalPoint( minPointInIndexCoordinates, minPointInWorldCoordinates ); + m_ReferenceImage->TransformIndexToPhysicalPoint( maxPointInIndexCoordinates, maxPointInWorldCoordinates ); +} + diff --git a/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h b/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h index 76acb4e829..4784f91a07 100644 --- a/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h +++ b/Modules/Segmentation/Algorithms/mitkCreateDistanceImageFromSurfaceFilter.h @@ -1,167 +1,194 @@ /*=================================================================== 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 mitkCreateDistanceImageFromSurfaceFilter_h_Included #define mitkCreateDistanceImageFromSurfaceFilter_h_Included #include "SegmentationExports.h" #include "mitkImageSource.h" #include "mitkSurface.h" #include "mitkProgressBar.h" #include "vtkSmartPointer.h" #include "vtkDoubleArray.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkPolyData.h" #include "vnl/vnl_matrix.h" #include "vnl/vnl_vector.h" #include "vnl/vnl_vector_fixed.h" #include "vnl/algo/vnl_qr.h" -#include "itkImage.h" +#include "itkImageBase.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNeighborhoodIterator.h" #include namespace mitk { /** \brief This filter interpolates the 3D surface for a segmented area. The basis for the interpolation are the edge-points of contours that are drawn into an image. The interpolation itself is performed via Radial Basis Function Interpolation. ATTENTION: This filter needs beside the edge points of the delineated contours additionally the normals for each edge point. \sa mitkSurfaceInterpolationController Based on the contour edge points and their normal this filter calculates a distance function with the following properties: - Putting a point into the distance function that lies inside the considered surface gives a negativ scalar value - Putting a point into the distance function that lies outside the considered surface gives a positive scalar value - Putting a point into the distance function that lies exactly on the considered surface gives the value zero With this interpolated distance function a distance image will be created. The desired surface can then be extract e.g. with the marching cubes algorithm. (Within the distance image the surface goes exactly where the pixelvalues are zero) Note that the obtained distance image has always an isotropig spacing. The size (in this case volume) of the image can be adjusted by calling SetDistanceImageVolume(unsigned int volume) which specifies the number ob pixels enclosed by the image. \ingroup Process $Author: fetzer$ */ class Segmentation_EXPORT CreateDistanceImageFromSurfaceFilter : public ImageSource { public: typedef vnl_vector_fixed PointType; + typedef itk::Image DistanceImageType; + typedef DistanceImageType::IndexType IndexType; + typedef itk::ImageRegionIteratorWithIndex ImageIterator; + typedef itk::NeighborhoodIterator NeighborhoodImageIterator; + typedef std::vector< PointType > NormalList; typedef std::vector< PointType > CenterList; typedef vnl_matrix SolutionMatrix; typedef vnl_vector FunctionValues; typedef vnl_vector InterpolationWeights; typedef std::vector SurfaceList; - typedef itk::Image DistanceImageType; - typedef itk::ImageRegionIteratorWithIndex ImageIterator; - typedef itk::NeighborhoodIterator NeighborhoodImageIterator; mitkClassMacro(CreateDistanceImageFromSurfaceFilter,ImageSource); itkNewMacro(Self); //Methods copied from mitkSurfaceToSurfaceFilter virtual void SetInput( const mitk::Surface* surface ); virtual void SetInput( unsigned int idx, const mitk::Surface* surface ); virtual const mitk::Surface* GetInput(); virtual const mitk::Surface* GetInput( unsigned int idx ); virtual void RemoveInputs(mitk::Surface* input); /** \brief Set the size of the output distance image. The size is specified by the image's volume (i.e. in this case how many pixels are enclosed by the image) If non is set, the volume will be 500000 pixels. */ itkSetMacro(DistanceImageVolume, unsigned int); void PrintEquationSystem(); //Resets the filter, i.e. removes all inputs and outputs void Reset(); /** \brief Set whether the mitkProgressBar should be used \a Parameter true for using the progress bar, false otherwise */ void SetUseProgressBar(bool); /** \brief Set the stepsize which the progress bar should proceed \a Parameter The stepsize for progressing */ void SetProgressStepSize(unsigned int stepSize); + void SetReferenceImage( itk::ImageBase<3>::Pointer referenceImage ); + + protected: CreateDistanceImageFromSurfaceFilter(); virtual ~CreateDistanceImageFromSurfaceFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: void CreateSolutionMatrixAndFunctionValues(); double CalculateDistanceValue(PointType p); void CreateDistanceImage (); + /** + * \brief This method fills the given variables with the minimum and + * maximum coordinates that contain all input-points in index- and + * world-coordinates. + * + * This method iterates over all input-points and transforms them from + * world-coordinates to index-coordinates using the transform of the + * reference-Image. + * Next, the minimal and maximal index-coordinates are determined that + * span an area that contains all given input-points. + * These index-coordinates are then transformed back to world-coordinates. + * + * These minimal and maximal points are then set to the given variables. + */ + void DetermineBounds( DistanceImageType::PointType &minPointInWorldCoordinates, + DistanceImageType::PointType &maxPointInWorldCoordinates, + DistanceImageType::IndexType &minPointInIndexCoordinates, + DistanceImageType::IndexType &maxPointInIndexCoordinates ); + + void FillImageRegion(DistanceImageType::RegionType reqRegion, DistanceImageType::PixelType pixelValue, DistanceImageType::Pointer image); //Datastructures for the interpolation CenterList m_Centers; NormalList m_Normals; FunctionValues m_FunctionValues; InterpolationWeights m_Weights; SolutionMatrix m_SolutionMatrix; double m_DistanceImageSpacing; + itk::ImageBase<3>::Pointer m_ReferenceImage; + unsigned int m_DistanceImageVolume; bool m_UseProgressBar; unsigned int m_ProgressStepSize; }; }//namespace #endif diff --git a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp index 4c0fa0f7a5..c44683b530 100644 --- a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp +++ b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp @@ -1,305 +1,317 @@ /*=================================================================== 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 "mitkSurfaceInterpolationController.h" #include "mitkMemoryUtilities.h" +#include "mitkImageAccessByItk.h" +#include "mitkImageCast.h" + +#include "mitkImageToSurfaceFilter.h" mitk::SurfaceInterpolationController::SurfaceInterpolationController() :m_SelectedSegmentation(0) { m_ReduceFilter = ReduceContourSetFilter::New(); m_NormalsFilter = ComputeContourSetNormalsFilter::New(); m_InterpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New(); m_ReduceFilter->SetUseProgressBar(false); m_NormalsFilter->SetUseProgressBar(false); m_InterpolateSurfaceFilter->SetUseProgressBar(false); m_Contours = Surface::New(); m_PolyData = vtkSmartPointer::New(); m_PolyData->SetPoints(vtkPoints::New()); m_InterpolationResult = 0; m_CurrentNumberOfReducedContours = 0; } mitk::SurfaceInterpolationController::~SurfaceInterpolationController() { ContourListMap::iterator it = m_MapOfContourLists.begin(); for (; it != m_MapOfContourLists.end(); it++) { for (unsigned int j = 0; j < m_MapOfContourLists[(*it).first].size(); ++j) { delete(m_MapOfContourLists[(*it).first].at(j).position); } m_MapOfContourLists.erase(it); } //Removing all observers std::map::iterator dataIter = m_SegmentationObserverTags.begin(); for (; dataIter != m_SegmentationObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("visible")->RemoveObserver( (*dataIter).second ); } m_SegmentationObserverTags.clear(); } mitk::SurfaceInterpolationController* mitk::SurfaceInterpolationController::GetInstance() { static mitk::SurfaceInterpolationController* m_Instance; if ( m_Instance == 0) { m_Instance = new SurfaceInterpolationController(); } return m_Instance; } void mitk::SurfaceInterpolationController::AddNewContour (mitk::Surface::Pointer newContour ,RestorePlanePositionOperation* op) { AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform = op->GetTransform(); mitk::Vector3D direction = op->GetDirectionVector(); int pos (-1); for (unsigned int i = 0; i < m_MapOfContourLists[m_SelectedSegmentation].size(); i++) { itk::Matrix diffM = transform->GetMatrix()-m_MapOfContourLists[m_SelectedSegmentation].at(i).position->GetTransform()->GetMatrix(); bool isSameMatrix(true); for (unsigned int j = 0; j < 3; j++) { if (fabs(diffM[j][0]) > 0.0001 && fabs(diffM[j][1]) > 0.0001 && fabs(diffM[j][2]) > 0.0001) { isSameMatrix = false; break; } } itk::Vector diffV = m_MapOfContourLists[m_SelectedSegmentation].at(i).position->GetTransform()->GetOffset()-transform->GetOffset(); if ( isSameMatrix && m_MapOfContourLists[m_SelectedSegmentation].at(i).position->GetPos() == op->GetPos() && (fabs(diffV[0]) < 0.0001 && fabs(diffV[1]) < 0.0001 && fabs(diffV[2]) < 0.0001) ) { pos = i; break; } } //Don't save a new empty contour if (pos == -1 && newContour->GetVtkPolyData()->GetNumberOfPoints() > 0) { mitk::RestorePlanePositionOperation* newOp = new mitk::RestorePlanePositionOperation (OpRESTOREPLANEPOSITION, op->GetWidth(), op->GetHeight(), op->GetSpacing(), op->GetPos(), direction, transform); ContourPositionPair newData; newData.contour = newContour; newData.position = newOp; m_ReduceFilter->SetInput(m_MapOfContourLists[m_SelectedSegmentation].size(), newContour); m_MapOfContourLists[m_SelectedSegmentation].push_back(newData); } //Edit a existing contour. If the contour is empty, edit it anyway so that the interpolation will always be consistent else if (pos != -1) { m_MapOfContourLists[m_SelectedSegmentation].at(pos).contour = newContour; m_ReduceFilter->SetInput(pos, newContour); } m_ReduceFilter->Update(); m_CurrentNumberOfReducedContours = m_ReduceFilter->GetNumberOfOutputs(); for (unsigned int i = 0; i < m_CurrentNumberOfReducedContours; i++) { m_NormalsFilter->SetInput(i, m_ReduceFilter->GetOutput(i)); m_InterpolateSurfaceFilter->SetInput(i, m_NormalsFilter->GetOutput(i)); } this->Modified(); } void mitk::SurfaceInterpolationController::Interpolate() { if (m_CurrentNumberOfReducedContours< 2) { //If no interpolation is possible reset the interpolation result m_InterpolationResult = 0; return; } //Setting up progress bar /* * Removed due to bug 12441. ProgressBar messes around with Qt event queue which is fatal for segmentation */ //mitk::ProgressBar::GetInstance()->AddStepsToDo(8); + // update the filter and get teh resulting distance-image m_InterpolateSurfaceFilter->Update(); - Image::Pointer distanceImage = m_InterpolateSurfaceFilter->GetOutput(); - vtkSmartPointer mcFilter = vtkSmartPointer::New(); - mcFilter->SetInput(distanceImage->GetVtkImageData()); - mcFilter->SetValue(0,0); - mcFilter->Update(); + // create a surface from the distance-image + mitk::ImageToSurfaceFilter::Pointer imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New(); + imageToSurfaceFilter->SetInput( distanceImage ); + imageToSurfaceFilter->SetThreshold( 0 ); + imageToSurfaceFilter->Update(); + m_InterpolationResult = imageToSurfaceFilter->GetOutput(); - m_InterpolationResult = 0; - m_InterpolationResult = mitk::Surface::New(); - m_InterpolationResult->SetVtkPolyData(mcFilter->GetOutput()); - m_InterpolationResult->GetGeometry()->SetOrigin(distanceImage->GetGeometry()->GetOrigin()); vtkSmartPointer polyDataAppender = vtkSmartPointer::New(); for (unsigned int i = 0; i < m_ReduceFilter->GetNumberOfOutputs(); i++) { polyDataAppender->AddInput(m_ReduceFilter->GetOutput(i)->GetVtkPolyData()); } polyDataAppender->Update(); m_Contours->SetVtkPolyData(polyDataAppender->GetOutput()); //Last progress step /* * Removed due to bug 12441. ProgressBar messes around with Qt event queue which is fatal for segmentation */ //mitk::ProgressBar::GetInstance()->Progress(8); m_InterpolationResult->DisconnectPipeline(); } mitk::Surface::Pointer mitk::SurfaceInterpolationController::GetInterpolationResult() { return m_InterpolationResult; } mitk::Surface* mitk::SurfaceInterpolationController::GetContoursAsSurface() { return m_Contours; } void mitk::SurfaceInterpolationController::SetDataStorage(DataStorage::Pointer ds) { m_DataStorage = ds; } void mitk::SurfaceInterpolationController::SetMinSpacing(double minSpacing) { m_ReduceFilter->SetMinSpacing(minSpacing); } void mitk::SurfaceInterpolationController::SetMaxSpacing(double maxSpacing) { m_ReduceFilter->SetMaxSpacing(maxSpacing); m_NormalsFilter->SetMaxSpacing(maxSpacing); } void mitk::SurfaceInterpolationController::SetDistanceImageVolume(unsigned int distImgVolume) { m_InterpolateSurfaceFilter->SetDistanceImageVolume(distImgVolume); } void mitk::SurfaceInterpolationController::SetSegmentationImage(Image* workingImage) { m_NormalsFilter->SetSegmentationBinaryImage(workingImage); } mitk::Image* mitk::SurfaceInterpolationController::GetImage() { return m_InterpolateSurfaceFilter->GetOutput(); } double mitk::SurfaceInterpolationController::EstimatePortionOfNeededMemory() { double numberOfPointsAfterReduction = m_ReduceFilter->GetNumberOfPointsAfterReduction()*3; double sizeOfPoints = pow(numberOfPointsAfterReduction,2)*sizeof(double); double totalMem = mitk::MemoryUtilities::GetTotalSizeOfPhysicalRam(); double percentage = sizeOfPoints/totalMem; return percentage; } +template +void mitk::SurfaceInterpolationController::GetImageBase(itk::Image* input, itk::ImageBase<3>::Pointer& result) +{ + result = input; +} + void mitk::SurfaceInterpolationController::SetCurrentSegmentationInterpolationList(mitk::Image* segmentation) { if (segmentation == m_SelectedSegmentation) return; m_ReduceFilter->Reset(); m_NormalsFilter->Reset(); m_InterpolateSurfaceFilter->Reset(); if (segmentation == 0) { m_SelectedSegmentation = 0; return; } ContourListMap::iterator it = m_MapOfContourLists.find(segmentation); m_SelectedSegmentation = segmentation; + itk::ImageBase<3>::Pointer itkImage; + AccessFixedDimensionByItk_1( m_SelectedSegmentation, GetImageBase, 3, itkImage ); + m_InterpolateSurfaceFilter->SetReferenceImage( itkImage.GetPointer() ); + if (it == m_MapOfContourLists.end()) { ContourPositionPairList newList; m_MapOfContourLists.insert(std::pair(segmentation, newList)); m_InterpolationResult = 0; m_CurrentNumberOfReducedContours = 0; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &SurfaceInterpolationController::OnSegmentationDeleted); m_SegmentationObserverTags.insert( std::pair( segmentation, segmentation->AddObserver( itk::DeleteEvent(), command ) ) ); } else { for (unsigned int i = 0; i < m_MapOfContourLists[m_SelectedSegmentation].size(); i++) { m_ReduceFilter->SetInput(i, m_MapOfContourLists[m_SelectedSegmentation].at(i).contour); } m_ReduceFilter->Update(); m_CurrentNumberOfReducedContours = m_ReduceFilter->GetNumberOfOutputs(); for (unsigned int i = 0; i < m_CurrentNumberOfReducedContours; i++) { m_NormalsFilter->SetInput(i, m_ReduceFilter->GetOutput(i)); m_InterpolateSurfaceFilter->SetInput(i, m_NormalsFilter->GetOutput(i)); } } Modified(); } void mitk::SurfaceInterpolationController::RemoveSegmentationFromContourList(mitk::Image *segmentation) { if (segmentation != 0) { m_MapOfContourLists.erase(segmentation); if (m_SelectedSegmentation == segmentation) { SetSegmentationImage(NULL); m_SelectedSegmentation = 0; } } } void mitk::SurfaceInterpolationController::OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &/*event*/) { mitk::Image* tempImage = dynamic_cast(const_cast(caller)); if (tempImage) { RemoveSegmentationFromContourList(tempImage); if (tempImage == m_SelectedSegmentation) { SetSegmentationImage(NULL); m_SelectedSegmentation = 0; } } } diff --git a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h index 457f4195f6..e25c85333e 100644 --- a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h +++ b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h @@ -1,171 +1,173 @@ /*=================================================================== 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 mitkSurfaceInterpolationController_h_Included #define mitkSurfaceInterpolationController_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkSurface.h" #include "mitkInteractionConst.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkCreateDistanceImageFromSurfaceFilter.h" #include "mitkReduceContourSetFilter.h" #include "mitkComputeContourSetNormalsFilter.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkWeakPointer.h" #include "vtkPolygon.h" #include "vtkPoints.h" #include "vtkCellArray.h" #include "vtkPolyData.h" #include "vtkSmartPointer.h" #include "vtkAppendPolyData.h" #include "vtkMarchingCubes.h" #include "vtkImageData.h" #include "mitkVtkRepresentationProperty.h" #include "vtkProperty.h" #include "mitkProgressBar.h" namespace mitk { class Segmentation_EXPORT SurfaceInterpolationController : public itk::Object { public: mitkClassMacro(SurfaceInterpolationController, itk::Object) itkNewMacro(Self) static SurfaceInterpolationController* GetInstance(); /** * Adds a new extracted contour to the list */ void AddNewContour(Surface::Pointer newContour, RestorePlanePositionOperation *op); /** * Interpolates the 3D surface from the given extracted contours */ void Interpolate (); mitk::Surface::Pointer GetInterpolationResult(); /** * Sets the minimum spacing of the current selected segmentation * This is needed since the contour points we reduced before they are used to interpolate the surface */ void SetMinSpacing(double minSpacing); /** * Sets the minimum spacing of the current selected segmentation * This is needed since the contour points we reduced before they are used to interpolate the surface */ void SetMaxSpacing(double maxSpacing); /** * Sets the volume i.e. the number of pixels that the distance image should have * By evaluation we found out that 50.000 pixel delivers a good result */ void SetDistanceImageVolume(unsigned int distImageVolume); /** * Sets the current segmentation which is used by the interpolation * This is needed because the calculation of the normals needs to now wheather a normal points inside a segmentation or not */ void SetSegmentationImage(Image* workingImage); Surface* GetContoursAsSurface(); void SetDataStorage(DataStorage::Pointer ds); /** * Sets the current list of contourpoints which is used for the surface interpolation * @param segmentation The current selected segmentation */ void SetCurrentSegmentationInterpolationList(mitk::Image* segmentation); /** * Removes the segmentation and all its contours from the list * @param segmentation The segmentation to be removed */ void RemoveSegmentationFromContourList(mitk::Image* segmentation); mitk::Image* GetImage(); /** * Estimates the memory which is needed to build up the equationsystem for the interpolation. * \returns The percentage of the real memory which will be used by the interpolation */ double EstimatePortionOfNeededMemory(); protected: SurfaceInterpolationController(); ~SurfaceInterpolationController(); + template void GetImageBase(itk::Image* input, itk::ImageBase<3>::Pointer& result); + private: void OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event); struct ContourPositionPair { Surface::Pointer contour; RestorePlanePositionOperation* position; }; typedef std::vector ContourPositionPairList; typedef std::map ContourListMap; ContourPositionPairList::iterator m_Iterator; ReduceContourSetFilter::Pointer m_ReduceFilter; ComputeContourSetNormalsFilter::Pointer m_NormalsFilter; CreateDistanceImageFromSurfaceFilter::Pointer m_InterpolateSurfaceFilter; double m_MinSpacing; double m_MaxSpacing; const Image* m_WorkingImage; Surface::Pointer m_Contours; vtkSmartPointer m_PolyData; unsigned int m_DistImageVolume; mitk::DataStorage::Pointer m_DataStorage; ContourListMap m_MapOfContourLists; mitk::Surface::Pointer m_InterpolationResult; unsigned int m_CurrentNumberOfReducedContours; mitk::Image* m_SelectedSegmentation; std::map m_SegmentationObserverTags; }; } #endif