diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index 9005b35b9d..6b2972b4f4 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -1,59 +1,60 @@ set(LIBPOSTFIX "Ext") # Modules must be listed according to their dependencies set(module_dirs SceneSerializationBase PlanarFigure ImageExtraction ImageStatistics LegacyAdaptors IpPicSupport MitkExt SceneSerialization Segmentation + PlanarFigureSegmentation Qmitk QmitkExt GraphAlgorithms DiffusionImaging GPGPU IGT CameraCalibration IGTUI RigidRegistration RigidRegistrationUI DeformableRegistration DeformableRegistrationUI OpenCL OpenCVVideoSupport Overlays InputDevices ToFHardware ToFProcessing ToFUI US ClippingTools USUI DicomUI Simulation 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/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..7ee46d046d --- /dev/null +++ b/Modules/PlanarFigureSegmentation/mitkPlanarFigureSegmentationController.cpp @@ -0,0 +1,330 @@ +/*=================================================================== + +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 +#include "mitkImageWriter.h" +#include "mitkSurfaceVtkWriter.h" +#include "mitkImageToSurfaceFilter.h" + +#include "itkTimeProbe.h" +#include "itkResampleImageFilter.h" +#include "itkInvertIntensityImageFilter.h" +#include "itkBinaryThresholdImageFilter.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->RemoveInputs( m_ReduceFilter->GetOutput( indexOfFigure ) ); + m_ReduceFilter->RemoveInputs( const_cast(m_ReduceFilter->GetInput(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 ); + } + 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; + } + +// for( int i=0; iGetNumberOfControlPoints(); i++ ) +// { +// // ... determine the world-coordinates +// mitk::Point2D polyLinePoint = figure->GetControlPoint(i); +// 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 + +