diff --git a/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h b/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h index 4985045249..504db804c1 100644 --- a/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h +++ b/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h @@ -1,112 +1,140 @@ /*=================================================================== 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 _MITKUNSTRUCTUREDGRIDCLUSTERINGFILTER_h__ #define _MITKUNSTRUCTUREDGRIDCLUSTERINGFILTER_h__ #include #include #include #include #include #include #include namespace mitk { /** - * @brief The UnstructuredGridClusteringFilter class + * @brief This filter uses the DBSCAN algorithm for clustering an + * mitk::UnstructuredGrid. "MinPts" defines the number of neighbours which are + * required to be a kernel point if a point is in range of a kernel point + * but hasnt enough neighbours this point is added to the cluster but is a + * density reachable point and the cluster ends at this point. "eps" is the + * range in which the neighbours are searched. If "Meshing" is set the + * clusteres UnstructuredGrid is meshed and visible in 2D renderwindows. * * DBSCAN algorithm: * * DBSCAN(D, eps, MinPts) * C = 0 * for each unvisited point P in dataset D * mark P as visited * N = D.regionQuery(P, eps) * if sizeof(N) < MinPts * mark P as NOISE * else * C = next cluster * expandCluster(P, N, C, eps, MinPts) * * expandCluster(P, N, C, eps, MinPts) * add P to cluster C * for each point P' in N * if P' is not visited * mark P' as visited * N' = D.regionQuery(P', eps) * if sizeof(N') >= MinPts * N = N joined with N' * if P' is not yet member of any cluster * add P' to cluster C */ class MITKALGORITHMSEXT_EXPORT UnstructuredGridClusteringFilter : public UnstructuredGridToUnstructuredGridFilter { public: mitkClassMacro(UnstructuredGridClusteringFilter, UnstructuredGridToUnstructuredGridFilter) itkFactorylessNewMacro(Self) itkCloneMacro(Self) + /** Sets the distance for the neighbour search */ itkSetMacro(eps, double) itkGetMacro(eps, double) + /** Sets the number of required neighbours */ itkSetMacro(MinPts, int) itkGetMacro(MinPts, int) + /** If activated the clusteres UnstructuredGrid is meshed */ itkSetMacro(Meshing, bool) + /** Returns all clusters as UnstructuredGrids which were found */ virtual std::vector< mitk::UnstructuredGrid::Pointer> GetAllClusters(); + /** Returns the number of the clusters which were found */ virtual int GetNumberOfFoundClusters(); - virtual void GenerateOutputInformation() override; - - virtual void GenerateData() override; - protected: + /** Constructor */ UnstructuredGridClusteringFilter(); + /** Destructor */ virtual ~UnstructuredGridClusteringFilter(); + /** Defines the output of the filter */ + virtual void GenerateOutputInformation(); + + /** Is called by the Update() method */ + virtual void GenerateData(); + private: + /** Used for the DBSCAN algorithm to expand a cluster and add more points to it */ void ExpandCluster(int id, vtkIdList* pointIDs, vtkPoints* cluster, vtkPoints *inpPoints); + /** The result main Cluster */ mitk::UnstructuredGrid::Pointer m_UnstructGrid; + /** All clusters which were found */ std::vector< vtkSmartPointer > m_Clusters; + /** The distances of the points from the input UnstructuredGrid*/ + std::vector< vtkSmartPointer > m_DistanceArrays; + + /** The range for the neighbout search */ double m_eps; + /** The number of the required neighbours */ int m_MinPts; + /** Activates the meshing for the UnstructuredGrid clusters*/ bool m_Meshing; + /** If its activated the distance of the clusters is used instead of the + * size */ + bool m_DistCalc; + }; } // namespace mitk #endif //_MITKUNSTRUCTUREDGRIDCLUSTERINGFILTER_h__ diff --git a/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp b/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp index 9b1f089927..2a3ad2844d 100644 --- a/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp @@ -1,218 +1,271 @@ /*=================================================================== 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 +#include + #include #include #include #include #include #include +#include +#include +#include +#include -mitk::UnstructuredGridClusteringFilter::UnstructuredGridClusteringFilter() : m_eps(0.0), m_MinPts(0), m_Meshing(true) +mitk::UnstructuredGridClusteringFilter::UnstructuredGridClusteringFilter() : m_eps(5.0), m_MinPts(4), m_Meshing(false), m_DistCalc(false) { this->m_UnstructGrid = mitk::UnstructuredGrid::New(); } mitk::UnstructuredGridClusteringFilter::~UnstructuredGridClusteringFilter(){} std::map visited; std::map isNoise; std::map clusterMember; vtkSmartPointer pLocator; +std::vector< vtkSmartPointer > clusterVector; + +std::vector< std::vector > clustersPointsIDs; void mitk::UnstructuredGridClusteringFilter::GenerateOutputInformation() { m_UnstructGrid = this->GetOutput(); } void mitk::UnstructuredGridClusteringFilter::GenerateData() { mitk::UnstructuredGrid::Pointer inputGrid = const_cast(this->GetInput()); if(inputGrid.IsNull()) return; vtkSmartPointer vtkInpGrid = inputGrid->GetVtkUnstructuredGrid(); vtkSmartPointer inpPoints = vtkInpGrid->GetPoints(); pLocator =vtkSmartPointer::New(); - std::vector< vtkSmartPointer > clusterVector; + + vtkSmartPointer distances = vtkSmartPointer::New(); + if(inputGrid->GetVtkUnstructuredGrid()->GetPointData()->GetNumberOfArrays() > 0) + { + m_DistCalc = true; + distances = dynamic_cast(vtkInpGrid->GetPointData()->GetArray(0)); + } pLocator->SetDataSet(vtkInpGrid); pLocator->AutomaticOn(); pLocator->SetNumberOfPointsPerBucket(2); pLocator->BuildLocator(); //fill the visited map with false for checking for(int i=0; iGetNumberOfPoints();i++) { visited[i] = false; isNoise[i] = false; clusterMember[i] = false; } for(int i=0; iGetNumberOfPoints();i++) { if(!visited[i]) { visited[i] = true; //mark P as visited vtkSmartPointer idList = vtkSmartPointer::New(); //represent N pLocator->FindPointsWithinRadius(m_eps, inpPoints->GetPoint(i), idList); //N = D.regionQuery(P, eps) if(idList->GetNumberOfIds() < m_MinPts) //if sizeof(N) < MinPts { isNoise[i] = true; //mark P as NOISE } else { vtkSmartPointer cluster = vtkSmartPointer::New(); //represent a cluster clusterVector.push_back(cluster); //C = next cluster this->ExpandCluster(i,idList,cluster,inpPoints); //expandCluster(P, N, C, eps, MinPts) mod. the parameter list } } } //OUTPUT LOGIC m_Clusters = clusterVector; int numberOfClusterPoints = 0; int IdOfBiggestCluster = 0; for(unsigned int i=0; i array = vtkSmartPointer::New(); vtkSmartPointer points = m_Clusters.at(i); - for(int j=0; jGetNumberOfPoints();j++) + if(m_DistCalc) { - double point[3]; - points->GetPoint(j,point); + array->SetNumberOfComponents(1); + array->SetNumberOfTuples(points->GetNumberOfPoints()); + for(int j=0; jGetNumberOfPoints();j++) + { + double point[3]; + points->GetPoint(j,point); + if(clustersPointsIDs.at(i).at(j)GetNumberOfPoints()) + { + if(distances->GetValue(clustersPointsIDs.at(i).at(j)) > 0.001) + { + double dist[1] = {distances->GetValue(clustersPointsIDs.at(i).at(j))}; + array->SetTuple(j, dist); + } + else + { + double dist[1] = {0.0}; + array->SetTuple(j, dist); + } + } + } + m_DistanceArrays.push_back(array); } if(points->GetNumberOfPoints() > numberOfClusterPoints) { numberOfClusterPoints = points->GetNumberOfPoints(); IdOfBiggestCluster = i; } } vtkSmartPointer biggestCluster = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); points = m_Clusters.at(IdOfBiggestCluster); vtkSmartPointer verts = vtkSmartPointer::New(); verts->GetPointIds()->SetNumberOfIds(m_Clusters.at(IdOfBiggestCluster)->GetNumberOfPoints()); for(int i=0; iGetNumberOfPoints(); i++) { verts->GetPointIds()->SetId(i,i); } biggestCluster->Allocate(1); biggestCluster->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); biggestCluster->SetPoints(m_Clusters.at(IdOfBiggestCluster)); if(m_Meshing) { vtkSmartPointer mesher = vtkSmartPointer::New(); mesher->SetInputData(biggestCluster); mesher->SetAlpha(0.9); mesher->Update(); vtkSmartPointer output = mesher->GetOutput(); m_UnstructGrid->SetVtkUnstructuredGrid(output); } else { m_UnstructGrid->SetVtkUnstructuredGrid(biggestCluster); } + + clusterVector.clear(); + clustersPointsIDs.clear(); } void mitk::UnstructuredGridClusteringFilter::ExpandCluster(int id, vtkIdList *pointIDs, vtkPoints* cluster, vtkPoints* inpPoints) { + std::vector x; + x.push_back(id); cluster->InsertNextPoint(inpPoints->GetPoint(id)); //add P to cluster C - clusterMember[id] = true; //right? + clusterMember[id] = true; vtkSmartPointer neighbours = vtkSmartPointer::New(); //same N as in other function inpPoints->GetPoints(pointIDs,neighbours); for(int i=0; iGetNumberOfIds();i++) //for each point P' in N { if(!visited[pointIDs->GetId(i)]) //if P' is not visited { visited[pointIDs->GetId(i)] = true; //mark P' as visited vtkSmartPointer idList = vtkSmartPointer::New(); //represent N' pLocator->FindPointsWithinRadius(m_eps, inpPoints->GetPoint(pointIDs->GetId(i)), idList); //N' = D.regionQuery(P', eps) + if(idList->GetNumberOfIds() >= m_MinPts) //if sizeof(N') >= MinPts { for(int j=0; jGetNumberOfIds();j++) //N = N joined with N' { - pointIDs->InsertNextId(idList->GetId(j)); + if(idList->GetId(j)GetNumberOfPoints())//a litte bit hacked ?! + { + pointIDs->InsertNextId(idList->GetId(j)); + } } } } if(!clusterMember[pointIDs->GetId(i)]) //if P' is not yet member of any cluster { - clusterMember[pointIDs->GetId(i)] = true; - cluster->InsertNextPoint(inpPoints->GetPoint(pointIDs->GetId(i))); //add P' to cluster C + if(pointIDs->GetId(i)GetNumberOfPoints()){ + clusterMember[pointIDs->GetId(i)] = true; + x.push_back(pointIDs->GetId(i)); + cluster->InsertNextPoint(inpPoints->GetPoint(pointIDs->GetId(i))); //add P' to cluster C + } } } + + clustersPointsIDs.push_back(x); } std::vector mitk::UnstructuredGridClusteringFilter::GetAllClusters() { std::vector< mitk::UnstructuredGrid::Pointer > mitkUGridVector; for(unsigned int i=0; i cluster = vtkSmartPointer::New(); vtkSmartPointer points = m_Clusters.at(i); vtkSmartPointer verts = vtkSmartPointer::New(); verts->GetPointIds()->SetNumberOfIds(points->GetNumberOfPoints()); - for(int i=0; iGetNumberOfPoints(); i++) + for(int j=0; jGetNumberOfPoints(); j++) { - verts->GetPointIds()->SetId(i,i); + verts->GetPointIds()->SetId(j,j); } cluster->Allocate(1); cluster->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); cluster->SetPoints(points); + if(m_DistCalc) + { + cluster->GetPointData()->AddArray(m_DistanceArrays.at(i)); + } mitk::UnstructuredGrid::Pointer mitkGrid = mitk::UnstructuredGrid::New(); if(m_Meshing) { vtkSmartPointer mesher = vtkSmartPointer::New(); mesher->SetInputData(cluster); mesher->SetAlpha(0.9); mesher->Update(); vtkSmartPointer output = mesher->GetOutput(); mitkGrid->SetVtkUnstructuredGrid(output); } else { mitkGrid->SetVtkUnstructuredGrid(cluster); } mitkUGridVector.push_back(mitkGrid); } return mitkUGridVector; } int mitk::UnstructuredGridClusteringFilter::GetNumberOfFoundClusters() { return m_Clusters.size(); } diff --git a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp b/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp index 4d5ed0bb7e..dde681a2ae 100644 --- a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp +++ b/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp @@ -1,89 +1,89 @@ /*=================================================================== 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 "mitkTestingMacros.h" #include #include #include class mitkImageToUnstructuredGridFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageToUnstructuredGridFilterTestSuite); MITK_TEST(testImageToUnstructuredGridFilterInitialization); MITK_TEST(testInput); MITK_TEST(testUnstructuredGridGeneration); MITK_TEST(testThreshold); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Image::Pointer m_BallImage; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_BallImage = mitk::IOUtil::LoadImage(GetTestDataFilePath("BallBinary30x30x30.nrrd")); } void testImageToUnstructuredGridFilterInitialization() { mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New(); CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing initialization of threshold member variable",testFilter->GetThreshold() == -0.1); } void testInput() { mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New(); testFilter->SetInput(m_BallImage); CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testFilter->GetInput() == m_BallImage); } void testUnstructuredGridGeneration() { mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New(); testFilter->SetInput(m_BallImage); testFilter->Update(); - CPPUNIT_ASSERT_MESSAGE("Testing surface generation!", testFilter->GetOutput() != NULL); + CPPUNIT_ASSERT_MESSAGE("Testing UnstructuredGrid generation!", testFilter->GetOutput() != NULL); } void testThreshold() { mitk::ImageToUnstructuredGridFilter::Pointer testFilter1 = mitk::ImageToUnstructuredGridFilter::New(); testFilter1->SetInput(m_BallImage); testFilter1->Update(); int numberOfPoints1 = testFilter1->GetNumberOfExtractedPoints(); mitk::ImageToUnstructuredGridFilter::Pointer testFilter2 = mitk::ImageToUnstructuredGridFilter::New(); testFilter2->SetInput(m_BallImage); testFilter2->SetThreshold(1.0); testFilter2->Update(); int numberOfPoints2 = testFilter2->GetNumberOfExtractedPoints(); CPPUNIT_ASSERT_MESSAGE("Testing Threshold", numberOfPoints1 > numberOfPoints2); } }; MITK_TEST_SUITE_REGISTRATION(mitkImageToUnstructuredGridFilter) diff --git a/Modules/AlgorithmsExt/test/mitkUnstructuredGridClusteringFilterTest.cpp b/Modules/AlgorithmsExt/test/mitkUnstructuredGridClusteringFilterTest.cpp index e4bc6b88d4..75c502ca21 100644 --- a/Modules/AlgorithmsExt/test/mitkUnstructuredGridClusteringFilterTest.cpp +++ b/Modules/AlgorithmsExt/test/mitkUnstructuredGridClusteringFilterTest.cpp @@ -1,129 +1,126 @@ /*=================================================================== 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 "mitkTestingMacros.h" #include #include #include #include #include #include #include #include -#include class mitkUnstructuredGridClusteringFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkUnstructuredGridClusteringFilterTestSuite); - vtkDebugLeaks::SetExitError(0); - MITK_TEST(testUnstructuredGridClusteringFilterInitialization); MITK_TEST(testInput); MITK_TEST(testUnstructuredGridGeneration); MITK_TEST(testReturnedCluster); MITK_TEST(testClusterVector); MITK_TEST(testGetNumberOfFoundClusters); CPPUNIT_TEST_SUITE_END(); private: mitk::UnstructuredGrid::Pointer m_UnstructuredGrid; public: void setUp() override { m_UnstructuredGrid = mitk::UnstructuredGrid::New(); //Loading the test data std::vector< mitk::BaseData::Pointer > vector = mitk::IOUtil::Load(GetTestDataFilePath("UnstructuredGrid/scoredGrid.vtu")); mitk::BaseData::Pointer base = vector.at(0); m_UnstructuredGrid = dynamic_cast(base.GetPointer()); } void testUnstructuredGridClusteringFilterInitialization() { mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); CPPUNIT_ASSERT_MESSAGE("Testing instantiation of filter object", clusterFilter.IsNotNull()); } void testInput() { mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); clusterFilter->SetInput(m_UnstructuredGrid); CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", clusterFilter->GetInput() == m_UnstructuredGrid); } void testUnstructuredGridGeneration() { mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); clusterFilter->SetInput(m_UnstructuredGrid); clusterFilter->SetMeshing(false); clusterFilter->SetMinPts(4); clusterFilter->Seteps(1.2); clusterFilter->Update(); CPPUNIT_ASSERT_MESSAGE("Testing output generation!", clusterFilter->GetOutput() != NULL); } void testReturnedCluster() { mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); clusterFilter->SetInput(m_UnstructuredGrid); clusterFilter->SetMeshing(false); clusterFilter->SetMinPts(4); clusterFilter->Seteps(1.2); clusterFilter->Update(); mitk::UnstructuredGrid::Pointer cluster = clusterFilter->GetOutput(); CPPUNIT_ASSERT_MESSAGE("Testing the output cluster!", cluster->GetVtkUnstructuredGrid()->GetPoints()->GetNumberOfPoints() == 620); } void testClusterVector() { mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); clusterFilter->SetInput(m_UnstructuredGrid); clusterFilter->SetMeshing(false); clusterFilter->SetMinPts(4); clusterFilter->Seteps(1.2); clusterFilter->Update(); std::vector< mitk::UnstructuredGrid::Pointer > clustervector = clusterFilter->GetAllClusters(); //test that all clusters have points: bool havePoints = true; for(unsigned int i=0; iGetVtkUnstructuredGrid()->GetPoints()->GetNumberOfPoints()<1) havePoints = false; } CPPUNIT_ASSERT_MESSAGE("Testing number of found clusters!", havePoints && clustervector.size() == 17); } void testGetNumberOfFoundClusters() { mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); clusterFilter->SetInput(m_UnstructuredGrid); clusterFilter->SetMeshing(false); clusterFilter->SetMinPts(4); clusterFilter->Seteps(1.2); clusterFilter->Update(); CPPUNIT_ASSERT_MESSAGE("Testing number of found clusters!", clusterFilter->GetNumberOfFoundClusters() == 17); } }; MITK_TEST_SUITE_REGISTRATION(mitkUnstructuredGridClusteringFilter) diff --git a/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp b/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp new file mode 100644 index 0000000000..c7a83549d3 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp @@ -0,0 +1,146 @@ +/*=================================================================== + +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 "mitkFeatureBasedEdgeDetectionFilter.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +mitk::FeatureBasedEdgeDetectionFilter::FeatureBasedEdgeDetectionFilter() +{ + m_PointGrid = mitk::UnstructuredGrid::New(); + m_SegmentationMask = mitk::Image::New(); + m_thresholdImage = mitk::Image::New(); + + this->SetNumberOfRequiredInputs(1); + + this->SetNumberOfIndexedOutputs(1); +} + +mitk::FeatureBasedEdgeDetectionFilter::~FeatureBasedEdgeDetectionFilter(){} + +void mitk::FeatureBasedEdgeDetectionFilter::GenerateData() +{ + mitk::Image::ConstPointer image = ImageToUnstructuredGridFilter::GetInput(); + mitk::Image::Pointer ncImage = const_cast(image.GetPointer()); + + //statistics + mitk::ImageStatisticsCalculator::Pointer statCalc = mitk::ImageStatisticsCalculator::New(); + statCalc->SetImage(image); + statCalc->SetMaskingModeToImage(); + if(m_SegmentationMask->IsEmpty()) + { + MITK_WARN << "Please set a segmentation mask first" << std::endl; + return; + } + statCalc->SetImageMask(m_SegmentationMask); + statCalc->ComputeStatistics(); + + mitk::ImageStatisticsCalculator::Statistics stats = statCalc->GetStatistics(); + double mean = stats.GetMean(); + double stdDev = stats.GetSigma(); + + double upperThreshold = mean + stdDev; + double lowerThreshold = mean - stdDev; + + mitk::ProgressBar::GetInstance()->Progress(); + + //thresholding + AccessByItk_2(ncImage.GetPointer(), ITKThresholding, lowerThreshold, upperThreshold) + + mitk::ProgressBar::GetInstance()->Progress(); + + //fill holes + mitk::MorphologicalOperations::FillHoles(m_thresholdImage); + + mitk::ProgressBar::GetInstance()->Progress(); + +// mitk::MorphologicalOperations::Closing(m_morphThreshold,1,mitk::MorphologicalOperations::Ball); +// mitk::MorphologicalOperations::Opening(m_morphThreshold,1,mitk::MorphologicalOperations::Ball); + + //masking + mitk::MaskImageFilter::Pointer maskFilter = mitk::MaskImageFilter::New(); + maskFilter->SetInput(image); + maskFilter->SetMask(m_thresholdImage); + maskFilter->OverrideOutsideValueOn(); + maskFilter->SetOutsideValue(0); + try + { + maskFilter->Update(); + } + catch(itk::ExceptionObject& excpt) + { + MITK_ERROR << excpt.GetDescription(); + return; + } + + mitk::Image::Pointer resultImage = maskFilter->GetOutput(); + + mitk::ProgressBar::GetInstance()->Progress(); + + //imagetopointcloudfilter + mitk::ImageToPointCloudFilter::Pointer pclFilter = mitk::ImageToPointCloudFilter::New(); + pclFilter->SetInput(resultImage); + pclFilter->Update(); + + mitk::ProgressBar::GetInstance()->Progress(); + + mitk::UnstructuredGrid::Pointer outp = mitk::UnstructuredGrid::New(); + outp->SetVtkUnstructuredGrid( pclFilter->GetOutput()->GetVtkUnstructuredGrid() ); + this->SetNthOutput(0, outp); + +// m_PointGrid->SetVtkUnstructuredGrid( pclFilter->GetOutput()->GetVtkUnstructuredGrid() ); +// m_PointGrid = this->GetOutput(); +} + +template +void mitk::FeatureBasedEdgeDetectionFilter::ITKThresholding( itk::Image* originalImage, double lower, double upper) +{ + typedef itk::Image ImageType; + typedef itk::Image SegmentationType; + typedef itk::BinaryThresholdImageFilter ThresholdFilterType; + + typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); + filter->SetInput(originalImage); + filter->SetLowerThreshold(lower); + filter->SetUpperThreshold(upper); + filter->SetInsideValue(1); + filter->SetOutsideValue(0); + filter->Update(); + + m_thresholdImage = mitk::ImportItkImage(filter->GetOutput()); +} + +void mitk::FeatureBasedEdgeDetectionFilter::SetSegmentationMask(mitk::Image::Pointer segmentation) +{ + this->m_SegmentationMask = segmentation; +} + +void mitk::FeatureBasedEdgeDetectionFilter::GenerateOutputInformation() +{ + Superclass::GenerateOutputInformation(); +} diff --git a/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.h b/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.h new file mode 100644 index 0000000000..24162698b7 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.h @@ -0,0 +1,80 @@ +/*=================================================================== + +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 mitkFeatureBasedEdgeDetectionFilter_h_Included +#define mitkFeatureBasedEdgeDetectionFilter_h_Included + +#include +#include + +namespace mitk +{ + +/** + * @brief Calculates edges and extracts them as an UnstructuredGrid with respect + * to the given segmentation. + * + * At first the statistic of the grey values within the segmentation is + * calculated. Based on this statistic a thresholding is executed. The + * thresholded image will be processed by morphological filters. The resulting + * image will be used for masking the input image. The masked image is used as + * input for the ImageToPointCloudFilter, which output is an UnstructuredGrid. + */ +class MITKSEGMENTATION_EXPORT FeatureBasedEdgeDetectionFilter: + public ImageToUnstructuredGridFilter +{ + +public: + + mitkClassMacro( FeatureBasedEdgeDetectionFilter, ImageToUnstructuredGridFilter) + + itkFactorylessNewMacro(Self) + + /** Sets the segmentation for calculating the statistics within that */ + void SetSegmentationMask(mitk::Image::Pointer); + +protected: + + /** This method is called by Update(). */ + virtual void GenerateData(); + + /** Initializes the output information */ + virtual void GenerateOutputInformation(); + + /** Constructor */ + FeatureBasedEdgeDetectionFilter(); + + /** Destructor */ + virtual ~FeatureBasedEdgeDetectionFilter(); + + /** Execute a thresholding filter with the given lower and upper bound */ + template + void ITKThresholding( itk::Image* originalImage, double lower, double upper); + +private: + + mitk::UnstructuredGrid::Pointer m_PointGrid; + + /** The used mask given by the segmentation*/ + mitk::Image::Pointer m_SegmentationMask; + + /** The thesholded image */ + mitk::Image::Pointer m_thresholdImage; + +}; + +} +#endif diff --git a/Modules/Segmentation/Testing/files.cmake b/Modules/Segmentation/Testing/files.cmake index b7d0da917d..c9b707401d 100644 --- a/Modules/Segmentation/Testing/files.cmake +++ b/Modules/Segmentation/Testing/files.cmake @@ -1,34 +1,35 @@ set(MODULE_TESTS mitkContourMapper2DTest.cpp mitkContourTest.cpp mitkContourModelSetToImageFilterTest.cpp mitkDataNodeSegmentationTest.cpp + mitkFeatureBasedEdgeDetectionFilterTest.cpp mitkImageToContourFilterTest.cpp # mitkSegmentationInterpolationTest.cpp mitkOverwriteSliceFilterTest.cpp mitkOverwriteSliceFilterObliquePlaneTest.cpp # mitkToolManagerTest.cpp mitkToolManagerProviderTest.cpp mitkManualSegmentationToSurfaceFilterTest.cpp #new cpp unit style ) if(MITK_ENABLE_RENDERING_TESTING) #since mitkInteractionTestHelper is currently creating a vtkRenderWindow set(MODULE_TESTS ${MODULE_TESTS} mitkToolInteractionTest.cpp ) endif() set(MODULE_IMAGE_TESTS mitkOverwriteSliceImageFilterTest.cpp #only runs on images ) set(MODULE_CUSTOM_TESTS ) set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) diff --git a/Modules/Segmentation/Testing/mitkFeatureBasedEdgeDetectionFilterTest.cpp b/Modules/Segmentation/Testing/mitkFeatureBasedEdgeDetectionFilterTest.cpp new file mode 100644 index 0000000000..25a1580ffe --- /dev/null +++ b/Modules/Segmentation/Testing/mitkFeatureBasedEdgeDetectionFilterTest.cpp @@ -0,0 +1,73 @@ +/*=================================================================== + +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 "mitkTestingMacros.h" +#include +#include + +#include + +class mitkFeatureBasedEdgeDetectionFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkFeatureBasedEdgeDetectionFilterTestSuite); + MITK_TEST(testFeatureBasedEdgeDetectionFilterInitialization); + MITK_TEST(testInput); + MITK_TEST(testUnstructuredGridGeneration); + CPPUNIT_TEST_SUITE_END(); + +private: + + /** Members used inside the different test methods. All members are initialized via setUp().*/ + mitk::Image::Pointer m_Pic3D; + mitk::Image::Pointer m_Segmentation; + +public: + + /** + * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). + */ + void setUp() + { + m_Pic3D = mitk::IOUtil::LoadImage(GetTestDataFilePath("Pic3D.nrrd")); + m_Segmentation = mitk::IOUtil::LoadImage(GetTestDataFilePath("PlaneSuggestion/pic3D_segmentation.nrrd")); + } + + void testFeatureBasedEdgeDetectionFilterInitialization() + { + mitk::FeatureBasedEdgeDetectionFilter::Pointer testFilter = mitk::FeatureBasedEdgeDetectionFilter::New(); + CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull()); + } + + void testInput() + { + mitk::FeatureBasedEdgeDetectionFilter::Pointer testFilter = mitk::FeatureBasedEdgeDetectionFilter::New(); + testFilter->SetInput(m_Pic3D); + CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testFilter->GetInput() == m_Pic3D); + } + + void testUnstructuredGridGeneration() + { + mitk::FeatureBasedEdgeDetectionFilter::Pointer testFilter = mitk::FeatureBasedEdgeDetectionFilter::New(); + testFilter->SetInput(m_Pic3D); + testFilter->SetSegmentationMask(m_Segmentation); + testFilter->Update(); + + CPPUNIT_ASSERT_MESSAGE("Testing surface generation!", testFilter->GetOutput()->GetVtkUnstructuredGrid() != NULL); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkFeatureBasedEdgeDetectionFilter) diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 981f749ddf..7d69d6df21 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,118 +1,119 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkContourModelSetToImageFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkDiffSliceOperation.cpp Algorithms/mitkDiffSliceOperationApplier.cpp + Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp Algorithms/mitkImageLiveWireContourModelFilter.cpp Algorithms/mitkImageToContourFilter.cpp #Algorithms/mitkImageToContourModelFilter.cpp Algorithms/mitkImageToLiveWireContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOtsuSegmentationFilter.cpp Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp Algorithms/mitkOverwriteSliceImageFilter.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp Algorithms/mitkVtkImageOverwrite.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkToolManager.cpp Controllers/mitkSegmentationModuleActivator.cpp Controllers/mitkToolManagerProvider.cpp DataManagement/mitkContour.cpp #DataManagement/mitkContourElement.cpp #DataManagement/mitkContourModel.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAdaptiveRegionGrowingTool.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkAutoSegmentationTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCalculateGrayValueStatisticsTool.cpp Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourInteractor.cpp Interactions/mitkContourModelInteractor.cpp Interactions/mitkContourModelLiveWireInteractor.cpp Interactions/mitkContourTool.cpp Interactions/mitkCorrectorTool2D.cpp Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp Interactions/mitkExtrudedContourInteractor.cpp Interactions/mitkFastMarchingTool.cpp Interactions/mitkFastMarchingTool3D.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkLiveWireTool2D.cpp Interactions/mitkOtsuTool3D.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkPixelManipulationTool.cpp Interactions/mitkRegionGrow3DTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Interactions/mitkTool.cpp Interactions/mitkToolCommand.cpp Interactions/mitkWatershedTool.cpp Interactions/mitkPickingTool.cpp #IO/mitkContourModelIOFactory.cpp #IO/mitkContourModelReader.cpp #IO/mitkContourModelWriter.cpp #IO/mitkContourModelWriterFactory.cpp Rendering/mitkContourMapper2D.cpp #Rendering/mitkContourModelGLMapper2D.cpp #Rendering/mitkContourModelMapper2D.cpp #Rendering/mitkContourModelMapper3D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp ) set(RESOURCE_FILES Add_48x48.png Add_Cursor_32x32.png Correction_48x48.png Correction_Cursor_32x32.png Erase_48x48.png Erase_Cursor_32x32.png FastMarching_48x48.png FastMarching_Cursor_32x32.png Fill_48x48.png Fill_Cursor_32x32.png LiveWire_48x48.png LiveWire_Cursor_32x32.png Otsu_48x48.png Paint_48x48.png Paint_Cursor_32x32.png Pick_48x48.png RegionGrowing_48x48.png RegionGrowing_Cursor_32x32.png Subtract_48x48.png Subtract_Cursor_32x32.png Threshold_48x48.png TwoThresholds_48x48.png Watershed_48x48.png Watershed_Cursor_32x32.png Wipe_48x48.png Wipe_Cursor_32x32.png Interactions/dummy.xml Interactions/LiveWireTool.xml Interactions/PressMoveRelease.xml Interactions/PressMoveReleaseAndPointSetting.xml Interactions/PressMoveReleaseWithCTRLInversion.xml Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml Interactions/SegmentationToolsConfig.xml ) diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp index 7fd26252cc..a1363ff641 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp @@ -1,1178 +1,1269 @@ /*=================================================================== 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 "QmitkSlicesInterpolator.h" #include "QmitkStdMultiWidget.h" #include "QmitkSelectableGLWidget.h" #include "mitkToolManager.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkProgressBar.h" #include "mitkGlobalInteraction.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkInteractionConst.h" #include "mitkApplyDiffImageOperation.h" #include "mitkDiffImageApplier.h" #include "mitkSegTool2D.h" #include "mitkCoreObjectFactory.h" #include "mitkSurfaceToImageFilter.h" #include "mitkSliceNavigationController.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include + //#define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) float SURFACE_COLOR_RGB [3] = {0.49f, 1.0f, 0.16f}; const std::map QmitkSlicesInterpolator::createActionToSliceDimension() { std::map actionToSliceDimension; foreach(mitk::SliceNavigationController* slicer, m_ControllerToDeleteObserverTag.keys()) { actionToSliceDimension[new QAction(QString::fromStdString(slicer->GetViewDirectionAsString()),0)] = slicer; } return actionToSliceDimension; } QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget* parent, const char* /*name*/) :QWidget(parent), // ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ), m_Interpolator( mitk::SegmentationInterpolationController::New() ), m_SurfaceInterpolator(mitk::SurfaceInterpolationController::GetInstance()), m_ToolManager(NULL), m_Initialized(false), m_LastSNC(0), m_LastSliceIndex(0), m_2DInterpolationEnabled(false), m_3DInterpolationEnabled(false) { m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this); QVBoxLayout* vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode); + m_EdgeDetector = mitk::FeatureBasedEdgeDetectionFilter::New(); + m_PointScorer = mitk::PointCloudScoringFilter::New(); + m_PlaneSuggester = mitk::ClusteredPlaneSuggestionFilter::New(); + m_CmbInterpolation = new QComboBox(m_GroupBoxEnableExclusiveInterpolationMode); m_CmbInterpolation->addItem("Disabled"); m_CmbInterpolation->addItem("2-Dimensional"); m_CmbInterpolation->addItem("3-Dimensional"); vboxLayout->addWidget(m_CmbInterpolation); m_BtnApply2D = new QPushButton("Confirm for single slice", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply2D); m_BtnApplyForAllSlices2D = new QPushButton("Confirm for all slices", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApplyForAllSlices2D); m_BtnApply3D = new QPushButton("Confirm", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply3D); + m_BtnSuggestPlane = new QPushButton("Suggest a plane", m_GroupBoxEnableExclusiveInterpolationMode); + vboxLayout->addWidget(m_BtnSuggestPlane); + m_BtnReinit3DInterpolation = new QPushButton("Reinit Interpolation", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnReinit3DInterpolation); m_ChkShowPositionNodes = new QCheckBox("Show Position Nodes", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_ChkShowPositionNodes); this->HideAllInterpolationControls(); connect(m_CmbInterpolation, SIGNAL(currentIndexChanged(int)), this, SLOT(OnInterpolationMethodChanged(int))); connect(m_BtnApply2D, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); connect(m_BtnApplyForAllSlices2D, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked())); connect(m_BtnApply3D, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked())); + + connect(m_BtnSuggestPlane, SIGNAL(clicked()), this, SLOT(OnSuggestPlaneClicked())); + connect(m_BtnReinit3DInterpolation, SIGNAL(clicked()), this, SLOT(OnReinit3DInterpolation())); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool))); QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode); this->setLayout(layout); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged ); InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver( itk::ModifiedEvent(), command ); itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged ); SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver( itk::ModifiedEvent(), command2 ); // feedback node and its visualization properties m_FeedbackNode = mitk::DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_FeedbackNode ); m_FeedbackNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "color", mitk::ColorProperty::New(255.0, 255.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", mitk::BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", mitk::IntProperty::New( 20 ) ); m_FeedbackNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_FeedbackNode->SetProperty( "name", mitk::StringProperty::New("Interpolation feedback") ); m_FeedbackNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_FeedbackNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetProperty( "color", mitk::ColorProperty::New(SURFACE_COLOR_RGB) ); m_InterpolatedSurfaceNode->SetProperty( "name", mitk::StringProperty::New("Surface Interpolation feedback") ); m_InterpolatedSurfaceNode->SetProperty( "opacity", mitk::FloatProperty::New(0.5) ); m_InterpolatedSurfaceNode->SetProperty( "line width", mitk::FloatProperty::New(4.0f) ); m_InterpolatedSurfaceNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetProperty( "color", mitk::ColorProperty::New(0.0, 0.0, 0.0) ); m_3DContourNode->SetProperty("hidden object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "name", mitk::StringProperty::New("Drawn Contours") ); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); QWidget::setContentsMargins(0, 0, 0, 0); if ( QWidget::layout() != NULL ) { QWidget::layout()->setContentsMargins(0, 0, 0, 0); } //For running 3D Interpolation in background // create a QFuture and a QFutureWatcher connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); } void QmitkSlicesInterpolator::SetDataStorage( mitk::DataStorage::Pointer storage ) { m_DataStorage = storage; m_SurfaceInterpolator->SetDataStorage(storage); } mitk::DataStorage* QmitkSlicesInterpolator::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void QmitkSlicesInterpolator::Initialize(mitk::ToolManager* toolManager, const QList &controllers) { Q_ASSERT(!controllers.empty()); if (m_Initialized) { // remove old observers Uninitialize(); } m_ToolManager = toolManager; if (m_ToolManager) { // set enabled only if a segmentation is selected mitk::DataNode* node = m_ToolManager->GetWorkingData(0); QWidget::setEnabled( node != NULL ); // react whenever the set of selected segmentation changes m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); // connect to the slice navigation controller. after each change, call the interpolator foreach(mitk::SliceNavigationController* slicer, controllers) { //Has to be initialized m_LastSNC = slicer; m_TimeStep.insert(slicer, slicer->GetTime()->GetPos()); itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted); m_ControllerToDeleteObserverTag.insert(slicer, slicer->AddObserver(itk::DeleteEvent(), deleteCommand)); itk::MemberCommand::Pointer timeChangedCommand = itk::MemberCommand::New(); timeChangedCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTimeChanged); m_ControllerToTimeObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::TimeGeometryEvent(NULL,0), timeChangedCommand)); itk::MemberCommand::Pointer sliceChangedCommand = itk::MemberCommand::New(); sliceChangedCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSliceChanged); m_ControllerToSliceObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(NULL,0), sliceChangedCommand)); } ACTION_TO_SLICEDIMENSION = createActionToSliceDimension(); } m_Initialized = true; } void QmitkSlicesInterpolator::Uninitialize() { if (m_ToolManager.IsNotNull()) { m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified); m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified); } foreach(mitk::SliceNavigationController* slicer, m_ControllerToSliceObserverTag.keys()) { slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer)); } ACTION_TO_SLICEDIMENSION.clear(); m_ToolManager = NULL; m_Initialized = false; } QmitkSlicesInterpolator::~QmitkSlicesInterpolator() { if (m_Initialized) { // remove old observers Uninitialize(); } if(m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if(m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); // remove observer m_Interpolator->RemoveObserver( InterpolationInfoChangedObserverTag ); m_SurfaceInterpolator->RemoveObserver( SurfaceInterpolationInfoChangedObserverTag ); delete m_Timer; } /** External enableization... */ void QmitkSlicesInterpolator::setEnabled( bool enable ) { QWidget::setEnabled(enable); //Set the gui elements of the different interpolation modi enabled if (enable) { if (m_2DInterpolationEnabled) { this->Show2DInterpolationControls(true); m_Interpolator->Activate2DInterpolation(true); } else if (m_3DInterpolationEnabled) { this->Show3DInterpolationControls(true); this->Show3DInterpolationResult(true); } } //Set all gui elements of the interpolation disabled else { this->HideAllInterpolationControls(); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status) { OnInterpolationActivated(status); m_Interpolator->Activate2DInterpolation(status); } void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status) { On3DInterpolationActivated(status); } void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status) { if (status) { OnInterpolationActivated(!status); On3DInterpolationActivated(!status); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::HideAllInterpolationControls() { this->Show2DInterpolationControls(false); this->Show3DInterpolationControls(false); } void QmitkSlicesInterpolator::Show2DInterpolationControls(bool show) { m_BtnApply2D->setVisible(show); m_BtnApplyForAllSlices2D->setVisible(show); } void QmitkSlicesInterpolator::Show3DInterpolationControls(bool show) { m_BtnApply3D->setVisible(show); + m_BtnSuggestPlane->setVisible(show); m_ChkShowPositionNodes->setVisible(show); m_BtnReinit3DInterpolation->setVisible(show); } void QmitkSlicesInterpolator::OnInterpolationMethodChanged(int index) { switch(index) { case 0: // Disabled m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation"); this->HideAllInterpolationControls(); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(false); this->Show3DInterpolationResult(false); m_Interpolator->Activate2DInterpolation(false); break; case 1: // 2D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show2DInterpolationControls(true); this->OnInterpolationActivated(true); this->On3DInterpolationActivated(false); m_Interpolator->Activate2DInterpolation(true); break; case 2: // 3D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show3DInterpolationControls(true); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(true); m_Interpolator->Activate2DInterpolation(false); break; default: MITK_ERROR << "Unknown interpolation method!"; m_CmbInterpolation->setCurrentIndex(0); break; } } void QmitkSlicesInterpolator::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } } void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified() { if (m_ToolManager->GetWorkingData(0) != 0) { m_Segmentation = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); m_BtnReinit3DInterpolation->setEnabled(true); } else { //If no workingdata is set, remove the interpolation feedback this->GetDataStorage()->Remove(m_FeedbackNode); m_FeedbackNode->SetData(NULL); this->GetDataStorage()->Remove(m_3DContourNode); m_3DContourNode->SetData(NULL); this->GetDataStorage()->Remove(m_InterpolatedSurfaceNode); m_InterpolatedSurfaceNode->SetData(NULL); m_BtnReinit3DInterpolation->setEnabled(false); return; } //Updating the current selected segmentation for the 3D interpolation SetCurrentContourListID(); if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } this->CheckSupportedImageDimension(); } void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified() { } void QmitkSlicesInterpolator::OnTimeChanged(itk::Object* sender, const itk::EventObject& e) { //Check if we really have a GeometryTimeEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController* slicer = dynamic_cast(sender); Q_ASSERT(slicer); m_TimeStep[slicer] = slicer->GetTime()->GetPos(); m_SurfaceInterpolator->SetCurrentTimeStep( slicer->GetTime()->GetPos() ); if (m_LastSNC == slicer) { slicer->SendSlice();//will trigger a new interpolation } } void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e) { //Check whether we really have a GeometrySliceEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController* slicer = dynamic_cast(sender); if (TranslateAndInterpolateChangedSlice(e, slicer)) { slicer->GetRenderer()->RequestUpdate(); } } bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e, mitk::SliceNavigationController* slicer) { if (!m_2DInterpolationEnabled) return false; try { const mitk::SliceNavigationController::GeometrySliceEvent& event = dynamic_cast(e); mitk::TimeGeometry* tsg = event.GetTimeGeometry(); if (tsg && m_TimeStep.contains(slicer)) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(tsg->GetGeometryForTimeStep(m_TimeStep[slicer]).GetPointer()); if (slicedGeometry) { m_LastSNC = slicer; mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetPlaneGeometry( event.GetPos() )); if (plane) Interpolate( plane, m_TimeStep[slicer], slicer ); return true; } } } catch(std::bad_cast) { return false; // so what } return false; } void QmitkSlicesInterpolator::Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep, mitk::SliceNavigationController* slicer ) { if (m_ToolManager) { mitk::DataNode* node = m_ToolManager->GetWorkingData(0); if (node) { m_Segmentation = dynamic_cast(node->GetData()); if (m_Segmentation) { int clickedSliceDimension(-1); int clickedSliceIndex(-1); // calculate real slice position, i.e. slice of the image and not slice of the TimeSlicedGeometry mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex ); mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( clickedSliceDimension, clickedSliceIndex, plane, timeStep ); m_FeedbackNode->SetData( interpolation ); m_LastSNC = slicer; m_LastSliceIndex = clickedSliceIndex; } } } } void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if(interpolatedSurface.IsNotNull() && workingNode && workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) { m_BtnApply3D->setEnabled(true); + m_BtnSuggestPlane->setEnabled(true); m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface()); this->Show3DInterpolationResult(true); if( !m_DataStorage->Exists(m_InterpolatedSurfaceNode) ) { m_DataStorage->Add(m_InterpolatedSurfaceNode); } if (!m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode, workingNode); } } else if (interpolatedSurface.IsNull()) { m_BtnApply3D->setEnabled(false); + m_BtnSuggestPlane->setEnabled(false); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { this->Show3DInterpolationResult(false); } } m_BtnReinit3DInterpolation->setEnabled(true); foreach (mitk::SliceNavigationController* slicer, m_ControllerToTimeObserverTag.keys()) { slicer->GetRenderer()->RequestUpdate(); } } void QmitkSlicesInterpolator::OnAcceptInterpolationClicked() { if (m_Segmentation && m_FeedbackNode->GetData()) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set slice as input mitk::Image::Pointer slice = dynamic_cast(m_FeedbackNode->GetData()); reslice->SetInputSlice(slice->GetSliceData()->GetVtkImageAccessor(slice)->GetVtkImageData()); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( m_Segmentation ); unsigned int timestep = m_LastSNC->GetTime()->GetPos(); extractor->SetTimeStep( timestep ); extractor->SetWorldGeometry( m_LastSNC->GetCurrentPlaneGeometry() ); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry( m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep( timestep ) ); extractor->Modified(); extractor->Update(); //the image was modified within the pipeline, but not marked so m_Segmentation->Modified(); m_Segmentation->GetVtkImageData()->Modified(); m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::AcceptAllInterpolations(mitk::SliceNavigationController* slicer) { /* * What exactly is done here: * 1. We create an empty diff image for the current segmentation * 2. All interpolated slices are written into the diff image * 3. Then the diffimage is applied to the original segmentation */ if (m_Segmentation) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); mitk::Image::Pointer image3D = m_Segmentation; unsigned int timeStep( slicer->GetTime()->GetPos() ); if (m_Segmentation->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( m_Segmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->Update(); image3D = timeSelector->GetOutput(); } // create a empty diff image for the undo operation mitk::Image::Pointer diffImage = mitk::Image::New(); diffImage->Initialize( image3D ); // Create scope for ImageWriteAccessor so that the accessor is destroyed // after the image is initialized. Otherwise later image access will lead to an error { mitk::ImageWriteAccessor imAccess(diffImage); // Set all pixels to zero mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); memset( imAccess.GetData(), 0, (pixelType.GetBpe() >> 3) * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2) ); } // Since we need to shift the plane it must be clone so that the original plane isn't altered mitk::PlaneGeometry::Pointer reslicePlane = slicer->GetCurrentPlaneGeometry()->Clone(); int sliceDimension(-1); int sliceIndex(-1); mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, reslicePlane, sliceDimension, sliceIndex ); unsigned int zslices = m_Segmentation->GetDimension( sliceDimension ); mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); mitk::Point3D origin = reslicePlane->GetOrigin(); unsigned int totalChangedSlices(0); for (unsigned int sliceIndex = 0; sliceIndex < zslices; ++sliceIndex) { // Transforming the current origin of the reslice plane // so that it matches the one of the next slice m_Segmentation->GetSlicedGeometry()->WorldToIndex(origin, origin); origin[sliceDimension] = sliceIndex; m_Segmentation->GetSlicedGeometry()->IndexToWorld(origin, origin); reslicePlane->SetOrigin(origin); //Set the slice as 'input' mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( sliceDimension, sliceIndex, reslicePlane, timeStep ); if (interpolation.IsNotNull()) // we don't check if interpolation is necessary/sensible - but m_Interpolator does { //Setting up the reslicing pipeline which allows us to write the interpolation results back into //the image volume vtkSmartPointer reslice = vtkSmartPointer::New(); //set overwrite mode to true to write back to the image volume reslice->SetInputSlice(interpolation->GetSliceData()->GetVtkImageAccessor(interpolation)->GetVtkImageData()); reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer diffslicewriter = mitk::ExtractSliceFilter::New(reslice); diffslicewriter->SetInput( diffImage ); diffslicewriter->SetTimeStep( 0 ); diffslicewriter->SetWorldGeometry(reslicePlane); diffslicewriter->SetVtkOutputRequest(true); diffslicewriter->SetResliceTransformByGeometry( diffImage->GetTimeGeometry()->GetGeometryForTimeStep( 0 ) ); diffslicewriter->Modified(); diffslicewriter->Update(); ++totalChangedSlices; } mitk::ProgressBar::GetInstance()->Progress(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); if (totalChangedSlices > 0) { // store undo stack items if ( true ) { // create do/undo operations mitk::ApplyDiffImageOperation* doOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); mitk::ApplyDiffImageOperation* undoOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); undoOp->SetFactor( -1.0 ); std::stringstream comment; comment << "Confirm all interpolations (" << totalChangedSlices << ")"; mitk::OperationEvent* undoStackItem = new mitk::OperationEvent( mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment.str() ); mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); // acutally apply the changes here to the original image mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation( doOp ); } } m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::FinishInterpolation(mitk::SliceNavigationController* slicer) { //this redirect is for calling from outside if (slicer == NULL) OnAcceptAllInterpolationsClicked(); else AcceptAllInterpolations( slicer ); } void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked() { QMenu orientationPopup(this); std::map::const_iterator it; for(it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++) orientationPopup.addAction(it->first); connect( &orientationPopup, SIGNAL(triggered(QAction*)), this, SLOT(OnAcceptAllPopupActivated(QAction*)) ); orientationPopup.exec( QCursor::pos() ); } void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked() { if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) { mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(dynamic_cast(m_InterpolatedSurfaceNode->GetData())); // check if ToolManager holds valid ReferenceData if (m_ToolManager->GetReferenceData(0) == NULL || m_ToolManager->GetWorkingData(0) == NULL) { return; } s2iFilter->SetImage(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); s2iFilter->Update(); mitk::DataNode* segmentationNode = m_ToolManager->GetWorkingData(0); mitk::Image::Pointer newSeg = s2iFilter->GetOutput(); mitk::Image* currSeg = dynamic_cast(segmentationNode->GetData()); unsigned int timestep = m_LastSNC->GetTime()->GetPos(); mitk::ImageReadAccessor readAccess(newSeg, newSeg->GetVolumeData(timestep)); const void* cPointer = readAccess.GetData(); if (currSeg && cPointer) { currSeg->SetVolume( cPointer, timestep, 0 ); } else { return; } m_CmbInterpolation->setCurrentIndex(0); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); mitk::DataNode::Pointer segSurface = mitk::DataNode::New(); float rgb[3]; segmentationNode->GetColor(rgb); segSurface->SetColor(rgb); segSurface->SetData(m_InterpolatedSurfaceNode->GetData()); std::stringstream stream; stream << segmentationNode->GetName(); stream << "_"; stream << "3D-interpolation"; segSurface->SetName(stream.str()); segSurface->SetProperty( "opacity", mitk::FloatProperty::New(0.7) ); segSurface->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(true)); segSurface->SetProperty( "3DInterpolationResult", mitk::BoolProperty::New(true)); segSurface->SetVisibility(false); m_DataStorage->Add(segSurface, segmentationNode); this->Show3DInterpolationResult(false); } } +void::QmitkSlicesInterpolator::OnSuggestPlaneClicked() +{ + if (m_PlaneWatcher.isRunning()) + m_PlaneWatcher.waitForFinished(); + m_PlaneFuture = QtConcurrent::run(this, &QmitkSlicesInterpolator::RunPlaneSuggestion); + m_PlaneWatcher.setFuture(m_PlaneFuture); +} + +void::QmitkSlicesInterpolator::RunPlaneSuggestion() +{ + if(m_FirstRun) + mitk::ProgressBar::GetInstance()->AddStepsToDo(8); + else + mitk::ProgressBar::GetInstance()->AddStepsToDo(3); + + m_EdgeDetector->SetSegmentationMask(m_Segmentation); + m_EdgeDetector->SetInput(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); + m_EdgeDetector->Update(); + + mitk::UnstructuredGrid::Pointer uGrid = mitk::UnstructuredGrid::New(); + uGrid->SetVtkUnstructuredGrid(m_EdgeDetector->GetOutput()->GetVtkUnstructuredGrid()); + + mitk::ProgressBar::GetInstance()->Progress(); + + mitk::Surface::Pointer surface = dynamic_cast(m_InterpolatedSurfaceNode->GetData()); + + vtkSmartPointer< vtkPolyData > vtkpoly = surface->GetVtkPolyData(); + vtkSmartPointer< vtkPoints> vtkpoints = vtkpoly->GetPoints(); + + vtkSmartPointer vGrid = vtkSmartPointer::New(); + vtkSmartPointer verts = vtkSmartPointer::New(); + + verts->GetPointIds()->SetNumberOfIds(vtkpoints->GetNumberOfPoints()); + for(int i=0; iGetNumberOfPoints(); i++) + { + verts->GetPointIds()->SetId(i,i); + } + + vGrid->Allocate(1); + vGrid->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); + vGrid->SetPoints(vtkpoints); + + mitk::UnstructuredGrid::Pointer interpolationGrid = mitk::UnstructuredGrid::New(); + interpolationGrid->SetVtkUnstructuredGrid(vGrid); + + m_PointScorer->SetInput(0, uGrid); + m_PointScorer->SetInput(1, interpolationGrid); + m_PointScorer->Update(); + + mitk::UnstructuredGrid::Pointer scoredGrid = mitk::UnstructuredGrid::New(); + scoredGrid = m_PointScorer->GetOutput(); + + mitk::ProgressBar::GetInstance()->Progress(); + + double spacing = mitk::SurfaceInterpolationController::GetInstance()->GetDistanceImageSpacing(); + + m_PlaneSuggester->SetInput(scoredGrid); + m_PlaneSuggester->SetMinPts(4); + m_PlaneSuggester->SetEps(spacing); + m_PlaneSuggester->Update(); + + mitk::GeometryData::Pointer geoData = m_PlaneSuggester->GetGeoData(); + mitk::PlaneGeometry::Pointer plane = dynamic_cast(geoData->GetGeometry()); + + mitk::ProgressBar::GetInstance()->Progress(); + + mitk::BaseRenderer::Pointer br = mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")); + br->GetSliceNavigationController()->ReorientSlices(plane->GetOrigin(),plane->GetNormal()); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + m_FirstRun = false; +} + void QmitkSlicesInterpolator::OnReinit3DInterpolation() { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("3DContourContainer", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer contourNodes = m_DataStorage->GetDerivations( m_ToolManager->GetWorkingData(0), pred); if (contourNodes->Size() != 0) { m_3DContourNode = contourNodes->at(0); } else { QMessageBox errorInfo; errorInfo.setWindowTitle("Reinitialize surface interpolation"); errorInfo.setIcon(QMessageBox::Information); errorInfo.setText("No contours available for the selected segmentation!"); errorInfo.exec(); } mitk::Surface::Pointer contours = dynamic_cast(m_3DContourNode->GetData()); if (contours) mitk::SurfaceInterpolationController::GetInstance()->ReinitializeInterpolation(contours); m_BtnReinit3DInterpolation->setEnabled(false); } void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction* action) { try { std::map::const_iterator iter = ACTION_TO_SLICEDIMENSION.find( action ); if (iter != ACTION_TO_SLICEDIMENSION.end()) { mitk::SliceNavigationController* slicer = iter->second; AcceptAllInterpolations( slicer ); } } catch(...) { /* Showing message box with possible memory error */ QMessageBox errorInfo; errorInfo.setWindowTitle("Interpolation Process"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!"); errorInfo.exec(); //additional error message on std::cerr std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl; } } void QmitkSlicesInterpolator::OnInterpolationActivated(bool on) { m_2DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() ) { if (on && !m_DataStorage->Exists(m_FeedbackNode)) { m_DataStorage->Add( m_FeedbackNode ); } } } catch(...) { // don't care (double add/remove) } if (m_ToolManager) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); QWidget::setEnabled( workingNode != NULL ); m_BtnApply2D->setEnabled( on ); m_FeedbackNode->SetVisibility( on ); if (!on) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } if (workingNode) { mitk::Image* segmentation = dynamic_cast(workingNode->GetData()); if (segmentation) { m_Interpolator->SetSegmentationVolume( segmentation ); if (referenceNode) { mitk::Image* referenceImage = dynamic_cast(referenceNode->GetData()); m_Interpolator->SetReferenceVolume( referenceImage ); // may be NULL } } } } UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::Run3DInterpolation() { m_SurfaceInterpolator->Interpolate(); } void QmitkSlicesInterpolator::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSlicesInterpolator::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB)); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); if( currentColor[2] == SURFACE_COLOR_RGB[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(1.0f,1.0f,1.0f)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on) { m_3DInterpolationEnabled = on; this->CheckSupportedImageDimension(); try { if ( m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); mitk::NodePredicateAnd::Pointer pred = mitk::NodePredicateAnd::New(mitk::NodePredicateProperty::New("3DInterpolationResult", mitk::BoolProperty::New(true)), mitk::NodePredicateDataType::New("Surface")); mitk::DataStorage::SetOfObjects::ConstPointer interpolationResults = m_DataStorage->GetDerivations(workingNode, pred); for (unsigned int i = 0; i < interpolationResults->Size(); ++i) { mitk::DataNode::Pointer currNode = interpolationResults->at(i); if (currNode.IsNotNull()) m_DataStorage->Remove(currNode); } if ((workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) && !isInterpolationResult && m_3DInterpolationEnabled) { int ret = QMessageBox::Yes; if (m_SurfaceInterpolator->EstimatePortionOfNeededMemory() > 0.5) { QMessageBox msgBox; msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!"); msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?"); msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); ret = msgBox.exec(); } if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); if (ret == QMessageBox::Yes) { m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } else { m_CmbInterpolation->setCurrentIndex(0); } } else if (!m_3DInterpolationEnabled) { this->Show3DInterpolationResult(false); m_BtnApply3D->setEnabled(m_3DInterpolationEnabled); + m_BtnSuggestPlane->setEnabled(m_3DInterpolationEnabled); } } else { QWidget::setEnabled( false ); m_ChkShowPositionNodes->setEnabled(m_3DInterpolationEnabled); } } if (!m_3DInterpolationEnabled) { this->Show3DInterpolationResult(false); m_BtnApply3D->setEnabled(m_3DInterpolationEnabled); + m_BtnSuggestPlane->setEnabled(m_3DInterpolationEnabled); } } catch(...) { MITK_ERROR<<"Error with 3D surface interpolation!"; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::EnableInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated OnInterpolationActivated(on); } void QmitkSlicesInterpolator::Enable3DInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated On3DInterpolationActivated(on); } void QmitkSlicesInterpolator::UpdateVisibleSuggestion() { if (m_2DInterpolationEnabled && m_LastSNC) { // determine which one is the current view, try to do an initial interpolation mitk::BaseRenderer* renderer = m_LastSNC->GetRenderer(); if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { //TODO 18735: This cast always returns NULL, cuase GetWorldGeometry returns a Base Geometry?!?!?! const mitk::TimeGeometry* timeGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeGeometry) { mitk::SliceNavigationController::GeometrySliceEvent event( const_cast(timeGeometry), renderer->GetSlice() ); TranslateAndInterpolateChangedSlice(event, m_LastSNC); } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject& /*e*/) { // something (e.g. undo) changed the interpolation info, we should refresh our display UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject& /*e*/) { if(m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } void QmitkSlicesInterpolator:: SetCurrentContourListID() { // New ContourList = hide current interpolation Show3DInterpolationResult(false); if ( m_DataStorage.IsNotNull() && m_ToolManager && m_LastSNC ) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); if (!isInterpolationResult) { QWidget::setEnabled( true ); // In case the time is not valid use 0 to access the time geometry of the working node unsigned int time_position = 0; if( m_LastSNC->GetTime() != NULL ) time_position = m_LastSNC->GetTime()->GetPos(); mitk::Vector3D spacing = workingNode->GetData()->GetGeometry( time_position )->GetSpacing(); double minSpacing (100); double maxSpacing (0); for (int i =0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceInterpolator->SetMaxSpacing(maxSpacing); m_SurfaceInterpolator->SetMinSpacing(minSpacing); m_SurfaceInterpolator->SetDistanceImageVolume(50000); mitk::Image* segmentationImage = dynamic_cast(workingNode->GetData()); /*if (segmentationImage->GetDimension() == 3) {*/ m_SurfaceInterpolator->SetCurrentInterpolationSession(segmentationImage); m_SurfaceInterpolator->SetCurrentTimeStep( time_position ); //} /*else MITK_INFO<<"3D Interpolation is only supported for 3D images at the moment!";*/ if (m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } } else { QWidget::setEnabled(false); } } } void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status) { if (m_InterpolatedSurfaceNode.IsNotNull()) m_InterpolatedSurfaceNode->SetVisibility(status); if (m_3DContourNode.IsNotNull()) m_3DContourNode->SetVisibility(status, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::CheckSupportedImageDimension() { if (m_ToolManager->GetWorkingData(0)) m_Segmentation = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); /*if (m_3DInterpolationEnabled && m_Segmentation && m_Segmentation->GetDimension() != 3) { QMessageBox info; info.setWindowTitle("3D Interpolation Process"); info.setIcon(QMessageBox::Information); info.setText("3D Interpolation is only supported for 3D images at the moment!"); info.exec(); m_CmbInterpolation->setCurrentIndex(0); }*/ } void QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject& /*e*/) { //Don't know how to avoid const_cast here?! mitk::SliceNavigationController* slicer = dynamic_cast(const_cast(sender)); if (slicer) { m_ControllerToTimeObserverTag.remove(slicer); m_ControllerToSliceObserverTag.remove(slicer); m_ControllerToDeleteObserverTag.remove(slicer); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h index cd847486cf..fa14d66415 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h +++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.h @@ -1,282 +1,302 @@ /*=================================================================== 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 QmitkSlicesInterpolator_h_Included #define QmitkSlicesInterpolator_h_Included #include "mitkSliceNavigationController.h" #include #include "mitkSegmentationInterpolationController.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkWeakPointer.h" #include "mitkSurfaceInterpolationController.h" #include "mitkToolManager.h" +#include "mitkFeatureBasedEdgeDetectionFilter.h" +#include "mitkPointCloudScoringFilter.h" +#include "mitkClusteredPlaneSuggestionFilter.h" + #include #include #include #include #include #include #include #include "mitkVtkRepresentationProperty.h" #include "vtkProperty.h" //For running 3D interpolation in background #include #include #include #include namespace mitk { class PlaneGeometry; class SliceNavigationController; } class QPushButton; /** \brief GUI for slices interpolation. \ingroup ToolManagerEtAl \ingroup Widgets \sa QmitkInteractiveSegmentation \sa mitk::SegmentationInterpolation There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage While mitk::SegmentationInterpolation does the bookkeeping of interpolation (keeping track of which slices contain how much segmentation) and the algorithmic work, QmitkSlicesInterpolator is responsible to watch the GUI, to notice, which slice is currently visible. It triggers generation of interpolation suggestions and also triggers acception of suggestions. \todo show/hide feedback on demand Last contributor: $Author: maleike $ */ class MITKSEGMENTATIONUI_EXPORT QmitkSlicesInterpolator : public QWidget { Q_OBJECT public: QmitkSlicesInterpolator(QWidget* parent = 0, const char* name = 0); /** To be called once before real use. */ void Initialize(mitk::ToolManager* toolManager, const QList &controllers); void Uninitialize(); virtual ~QmitkSlicesInterpolator(); void SetDataStorage( mitk::DataStorage::Pointer storage ); mitk::DataStorage* GetDataStorage(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnToolManagerWorkingDataModified(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnToolManagerReferenceDataModified(); void OnTimeChanged(itk::Object* sender, const itk::EventObject&); void OnSliceChanged(itk::Object* sender, const itk::EventObject&); void OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject& ); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnInterpolationInfoChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSurfaceInterpolationInfoChanged(const itk::EventObject&); /** * @brief Set the visibility of the 3d interpolation */ void Show3DInterpolationResult(bool); signals: void SignalRememberContourPositions(bool); void SignalShowMarkerNodes(bool); public slots: virtual void setEnabled( bool ); /** Call this from the outside to enable/disable interpolation */ void EnableInterpolation(bool); void Enable3DInterpolation(bool); /** Call this from the outside to accept all interpolations */ void FinishInterpolation(mitk::SliceNavigationController* slicer = NULL); protected slots: /** Reaction to button clicks. */ void OnAcceptInterpolationClicked(); /* Opens popup to ask about which orientation should be interpolated */ void OnAcceptAllInterpolationsClicked(); /* Reaction to button clicks */ void OnAccept3DInterpolationClicked(); void OnReinit3DInterpolation(); + void OnSuggestPlaneClicked(); + /* * Will trigger interpolation for all slices in given orientation (called from popup menu of OnAcceptAllInterpolationsClicked) */ void OnAcceptAllPopupActivated(QAction* action); /** Called on activation/deactivation */ void OnInterpolationActivated(bool); void On3DInterpolationActivated(bool); void OnInterpolationMethodChanged(int index); //Enhancement for 3D interpolation void On2DInterpolationEnabled(bool); void On3DInterpolationEnabled(bool); void OnInterpolationDisabled(bool); void OnShowMarkers(bool); void Run3DInterpolation(); + void RunPlaneSuggestion(); + void OnSurfaceInterpolationFinished(); void StartUpdateInterpolationTimer(); void StopUpdateInterpolationTimer(); void ChangeSurfaceColor(); protected: const std::map createActionToSliceDimension(); std::map ACTION_TO_SLICEDIMENSION; void AcceptAllInterpolations(mitk::SliceNavigationController* slicer); /** Retrieves the currently selected PlaneGeometry from a SlicedGeometry3D that is generated by a SliceNavigationController and calls Interpolate to further process this PlaneGeometry into an interpolation. \param e is a actually a mitk::SliceNavigationController::GeometrySliceEvent, sent by a SliceNavigationController \param slice the SliceNavigationController */ bool TranslateAndInterpolateChangedSlice(const itk::EventObject& e, mitk::SliceNavigationController* slicer); /** Given a PlaneGeometry, this method figures out which slice of the first working image (of the associated ToolManager) should be interpolated. The actual work is then done by our SegmentationInterpolation object. */ void Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep, mitk::SliceNavigationController *slicer ); //void InterpolateSurface(); /** Called internally to update the interpolation suggestion. Finds out about the focused render window and requests an interpolation. */ void UpdateVisibleSuggestion(); void SetCurrentContourListID(); private: void HideAllInterpolationControls(); void Show2DInterpolationControls(bool show); void Show3DInterpolationControls(bool show); void CheckSupportedImageDimension(); mitk::SegmentationInterpolationController::Pointer m_Interpolator; mitk::SurfaceInterpolationController::Pointer m_SurfaceInterpolator; + mitk::FeatureBasedEdgeDetectionFilter::Pointer m_EdgeDetector; + mitk::PointCloudScoringFilter::Pointer m_PointScorer; + mitk::ClusteredPlaneSuggestionFilter::Pointer m_PlaneSuggester; + mitk::ToolManager::Pointer m_ToolManager; bool m_Initialized; QHash m_ControllerToTimeObserverTag; QHash m_ControllerToSliceObserverTag; QHash m_ControllerToDeleteObserverTag; unsigned int InterpolationInfoChangedObserverTag; unsigned int SurfaceInterpolationInfoChangedObserverTag; QGroupBox* m_GroupBoxEnableExclusiveInterpolationMode; QComboBox* m_CmbInterpolation; QPushButton* m_BtnApply2D; QPushButton* m_BtnApplyForAllSlices2D; QPushButton* m_BtnApply3D; + + QPushButton* m_BtnSuggestPlane; + QCheckBox* m_ChkShowPositionNodes; QPushButton* m_BtnReinit3DInterpolation; mitk::DataNode::Pointer m_FeedbackNode; mitk::DataNode::Pointer m_InterpolatedSurfaceNode; mitk::DataNode::Pointer m_3DContourNode; mitk::Image* m_Segmentation; mitk::SliceNavigationController* m_LastSNC; unsigned int m_LastSliceIndex; QHash m_TimeStep; bool m_2DInterpolationEnabled; bool m_3DInterpolationEnabled; //unsigned int m_CurrentListID; mitk::DataStorage::Pointer m_DataStorage; QFuture m_Future; QFutureWatcher m_Watcher; QTimer* m_Timer; + + QFuture m_PlaneFuture; + QFutureWatcher m_PlaneWatcher; + + bool m_FirstRun = true; }; #endif diff --git a/Modules/SurfaceInterpolation/CMakeLists.txt b/Modules/SurfaceInterpolation/CMakeLists.txt index 33cd8a94ec..cab2c46a95 100644 --- a/Modules/SurfaceInterpolation/CMakeLists.txt +++ b/Modules/SurfaceInterpolation/CMakeLists.txt @@ -1,7 +1,7 @@ MITK_CREATE_MODULE( - DEPENDS MitkAlgorithmsExt + DEPENDS MitkImageExtraction MitkImageStatistics PACKAGE_DEPENDS PUBLIC Eigen WARNINGS_AS_ERRORS ) add_subdirectory(Testing) diff --git a/Modules/SurfaceInterpolation/Testing/files.cmake b/Modules/SurfaceInterpolation/Testing/files.cmake index 193f4912a8..be4eb82fbf 100644 --- a/Modules/SurfaceInterpolation/Testing/files.cmake +++ b/Modules/SurfaceInterpolation/Testing/files.cmake @@ -1,7 +1,8 @@ set(MODULE_TESTS mitkComputeContourSetNormalsFilterTest.cpp mitkCreateDistanceImageFromSurfaceFilterTest.cpp - mitkPointCloudScoringFilterTest.cpp + mitkImageToPointCloudFilterTest.cpp + mitkPointCloudScoringFilterTest mitkReduceContourSetFilterTest.cpp mitkSurfaceInterpolationControllerTest.cpp ) diff --git a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp b/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp similarity index 66% copy from Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp copy to Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp index 4d5ed0bb7e..e5d1c30fd7 100644 --- a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp +++ b/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp @@ -1,89 +1,88 @@ /*=================================================================== 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 "mitkTestingMacros.h" -#include +#include #include #include -class mitkImageToUnstructuredGridFilterTestSuite : public mitk::TestFixture +class mitkImageToPointCloudFilterTestSuite : public mitk::TestFixture { - CPPUNIT_TEST_SUITE(mitkImageToUnstructuredGridFilterTestSuite); - MITK_TEST(testImageToUnstructuredGridFilterInitialization); + CPPUNIT_TEST_SUITE(mitkImageToPointCloudFilterTestSuite); + MITK_TEST(testImageToPointCloudFilterInitialization); MITK_TEST(testInput); MITK_TEST(testUnstructuredGridGeneration); MITK_TEST(testThreshold); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Image::Pointer m_BallImage; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). */ - void setUp() override + void setUp() { m_BallImage = mitk::IOUtil::LoadImage(GetTestDataFilePath("BallBinary30x30x30.nrrd")); } - void testImageToUnstructuredGridFilterInitialization() + void testImageToPointCloudFilterInitialization() { mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New(); CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull()); - CPPUNIT_ASSERT_MESSAGE("Testing initialization of threshold member variable",testFilter->GetThreshold() == -0.1); } void testInput() { - mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New(); + mitk::ImageToPointCloudFilter::Pointer testFilter = mitk::ImageToPointCloudFilter::New(); testFilter->SetInput(m_BallImage); CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testFilter->GetInput() == m_BallImage); } void testUnstructuredGridGeneration() { - mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New(); + mitk::ImageToPointCloudFilter::Pointer testFilter = mitk::ImageToPointCloudFilter::New(); testFilter->SetInput(m_BallImage); testFilter->Update(); - CPPUNIT_ASSERT_MESSAGE("Testing surface generation!", testFilter->GetOutput() != NULL); + CPPUNIT_ASSERT_MESSAGE("Testing UnstructuredGrid generation!", testFilter->GetOutput() != NULL); } void testThreshold() { - mitk::ImageToUnstructuredGridFilter::Pointer testFilter1 = mitk::ImageToUnstructuredGridFilter::New(); + mitk::ImageToPointCloudFilter::Pointer testFilter1 = mitk::ImageToPointCloudFilter::New(); testFilter1->SetInput(m_BallImage); testFilter1->Update(); int numberOfPoints1 = testFilter1->GetNumberOfExtractedPoints(); - mitk::ImageToUnstructuredGridFilter::Pointer testFilter2 = mitk::ImageToUnstructuredGridFilter::New(); + mitk::ImageToPointCloudFilter::Pointer testFilter2 = mitk::ImageToPointCloudFilter::New(); testFilter2->SetInput(m_BallImage); - testFilter2->SetThreshold(1.0); + testFilter2->SetMethod(mitk::ImageToPointCloudFilter::LAPLACIAN_STD_DEV3); testFilter2->Update(); int numberOfPoints2 = testFilter2->GetNumberOfExtractedPoints(); CPPUNIT_ASSERT_MESSAGE("Testing Threshold", numberOfPoints1 > numberOfPoints2); } }; -MITK_TEST_SUITE_REGISTRATION(mitkImageToUnstructuredGridFilter) +MITK_TEST_SUITE_REGISTRATION(mitkImageToPointCloudFilter) diff --git a/Modules/SurfaceInterpolation/Testing/mitkPointCloudScoringFilterTest.cpp b/Modules/SurfaceInterpolation/Testing/mitkPointCloudScoringFilterTest.cpp index c8493aea01..3086c004fe 100644 --- a/Modules/SurfaceInterpolation/Testing/mitkPointCloudScoringFilterTest.cpp +++ b/Modules/SurfaceInterpolation/Testing/mitkPointCloudScoringFilterTest.cpp @@ -1,122 +1,129 @@ /*=================================================================== 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 "mitkTestingMacros.h" #include #include #include #include #include #include #include class mitkPointCloudScoringFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPointCloudScoringFilterTestSuite); MITK_TEST(testPointCloudScoringFilterInitialization); MITK_TEST(testInputs); MITK_TEST(testOutput); MITK_TEST(testScoring); CPPUNIT_TEST_SUITE_END(); private: mitk::UnstructuredGrid::Pointer m_UnstructuredGrid1; mitk::UnstructuredGrid::Pointer m_UnstructuredGrid2; public: void setUp() override { m_UnstructuredGrid1 = mitk::UnstructuredGrid::New(); m_UnstructuredGrid2 = mitk::UnstructuredGrid::New(); vtkSmartPointer vtkGrid1 = vtkSmartPointer::New(); vtkSmartPointer vtkGrid2 = vtkSmartPointer::New(); vtkSmartPointer points1 = vtkSmartPointer::New(); vtkSmartPointer points2 = vtkSmartPointer::New(); for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { for(int k=0; k<3; k++) { mitk::Point3D point; point[0]=i; point[1]=j; point[2]=k; points1->InsertNextPoint(point[0],point[1],point[2]); points2->InsertNextPoint(point[0]+i,point[1]+i,point[2]+i); } } } vtkGrid1->SetPoints(points1); vtkGrid2->SetPoints(points2); m_UnstructuredGrid1->SetVtkUnstructuredGrid(vtkGrid1); m_UnstructuredGrid2->SetVtkUnstructuredGrid(vtkGrid2); } void testPointCloudScoringFilterInitialization() { mitk::PointCloudScoringFilter::Pointer testFilter = mitk::PointCloudScoringFilter::New(); CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull()); } void testInputs() { mitk::PointCloudScoringFilter::Pointer testFilter = mitk::PointCloudScoringFilter::New(); testFilter->SetInput(0, m_UnstructuredGrid1); testFilter->SetInput(1, m_UnstructuredGrid2); mitk::UnstructuredGrid::Pointer uGrid1 = dynamic_cast(testFilter->GetInputs().at(0).GetPointer()); mitk::UnstructuredGrid::Pointer uGrid2 = dynamic_cast(testFilter->GetInputs().at(1).GetPointer()); CPPUNIT_ASSERT_MESSAGE("Testing the first input", uGrid1 == m_UnstructuredGrid1); CPPUNIT_ASSERT_MESSAGE("Testing the second input", uGrid2 == m_UnstructuredGrid2); } void testOutput() { mitk::PointCloudScoringFilter::Pointer testFilter = mitk::PointCloudScoringFilter::New(); testFilter->SetInput(0, m_UnstructuredGrid1); testFilter->SetInput(1, m_UnstructuredGrid2); testFilter->Update(); - CPPUNIT_ASSERT_MESSAGE("Testing mitkUnstructuredGrid generation!", testFilter->GetOutput() != NULL); + + if(!testFilter->GetOutput()->GetVtkUnstructuredGrid()) + std::cout << "ITS EMPTY1!" << std::endl; + + CPPUNIT_ASSERT_MESSAGE("Testing mitkUnstructuredGrid generation!", testFilter->GetOutput()->GetVtkUnstructuredGrid() != NULL); } void testScoring() { mitk::PointCloudScoringFilter::Pointer testFilter = mitk::PointCloudScoringFilter::New(); - testFilter->SetInput(0, m_UnstructuredGrid2); - testFilter->SetInput(1, m_UnstructuredGrid1); + testFilter->SetInput(0, m_UnstructuredGrid1); + testFilter->SetInput(1, m_UnstructuredGrid2); testFilter->Update(); + if(!testFilter->GetOutput()->GetVtkUnstructuredGrid()) + std::cout << "ITS EMPTY2!" << std::endl; + mitk::UnstructuredGrid::Pointer outpGrid = testFilter->GetOutput(); int numBefore = m_UnstructuredGrid1->GetVtkUnstructuredGrid()->GetNumberOfPoints(); int numAfter = outpGrid->GetVtkUnstructuredGrid()->GetNumberOfPoints(); CPPUNIT_ASSERT_MESSAGE("Testing grid scoring", numBefore > numAfter); } }; MITK_TEST_SUITE_REGISTRATION(mitkPointCloudScoringFilter) diff --git a/Modules/SurfaceInterpolation/files.cmake b/Modules/SurfaceInterpolation/files.cmake index f3339c5be6..40b96eee8e 100644 --- a/Modules/SurfaceInterpolation/files.cmake +++ b/Modules/SurfaceInterpolation/files.cmake @@ -1,7 +1,9 @@ set(CPP_FILES + mitkClusteredPlaneSuggestionFilter.cpp mitkComputeContourSetNormalsFilter.cpp mitkCreateDistanceImageFromSurfaceFilter.cpp mitkPointCloudScoringFilter.cpp + mitkImageToPointCloudFilter.cpp mitkReduceContourSetFilter.cpp mitkSurfaceInterpolationController.cpp ) diff --git a/Modules/SurfaceInterpolation/mitkClusteredPlaneSuggestionFilter.cpp b/Modules/SurfaceInterpolation/mitkClusteredPlaneSuggestionFilter.cpp new file mode 100644 index 0000000000..4239061337 --- /dev/null +++ b/Modules/SurfaceInterpolation/mitkClusteredPlaneSuggestionFilter.cpp @@ -0,0 +1,164 @@ +/*=================================================================== + +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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +mitk::ClusteredPlaneSuggestionFilter::ClusteredPlaneSuggestionFilter(): m_Meshing(false), m_MinPts(4), m_Eps(1.2), m_UseDistances(false), m_NumberOfUsedClusters(3) +{ + this->m_MainCluster = mitk::UnstructuredGrid::New(); + this->m_GeoData = mitk::GeometryData::New(); +} + +mitk::ClusteredPlaneSuggestionFilter::~ClusteredPlaneSuggestionFilter(){} + +bool myComparison(std::pair i, std::pair j){ return (i.first>j.first); } + +void mitk::ClusteredPlaneSuggestionFilter::GenerateData() +{ + mitk::UnstructuredGrid::Pointer inpGrid = const_cast(this->GetInput()); + + if(inpGrid.IsNull()) + { + MITK_ERROR << "Input or cast to UnstructuredGrid is null"; + return; + } + + mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); + clusterFilter->SetInput(inpGrid); + clusterFilter->SetMeshing(false); + clusterFilter->SetMinPts(m_MinPts); + clusterFilter->Seteps(m_Eps); + clusterFilter->Update(); + + vtkSmartPointer< vtkUnstructuredGrid > vtkGrid = clusterFilter->GetOutput()->GetVtkUnstructuredGrid(); + + if(!vtkGrid) + { + MITK_ERROR << "vtkUnstructuredGrid output from clustering is null"; + return; + } + + m_MainCluster->SetVtkUnstructuredGrid(vtkGrid); + m_Clusters = clusterFilter->GetAllClusters(); + + //find the avg distances of every cluster maybe a square mean for better looking at high values? + std::vector< std::pair > avgDistances; + + //get the number of points and the id of every cluster + std::vector< std::pair > sizeIDs; + + for(unsigned int i=0; i data = dynamic_cast(cluster->GetVtkUnstructuredGrid()->GetPointData()->GetArray(0)); + double avg = 0.0; + for(int j=0; jGetSize();j++) + { + avg += data->GetValue(j); + } + avgDistances.push_back(std::make_pair(avg/static_cast(data->GetSize()),i)); + } + //now sort the clusters with their distances + + for(unsigned int i=0; iGetVtkUnstructuredGrid()->GetNumberOfPoints(), i)); + } + + //sort the point IDs for finding the biggest clusters and clusters with the + //highest distance + //sizeIDs is sorted by number of points in each cluster + //avgDistances is sorted by avg distance to next edge + std::sort(sizeIDs.begin(), sizeIDs.end(), myComparison); + std::sort(avgDistances.begin(), avgDistances.end(), myComparison); + + int number = 0; //max number of biggest clusters which are used for the plane + + //if less than m_NumberOfUsedClusters clusters are found + if(m_Clusters.size() < m_NumberOfUsedClusters) + number = m_Clusters.size(); + else + number = m_NumberOfUsedClusters; + + //Generate a pointset from UnstructuredGrid for the PlaneFitFilter: + mitk::PointSet::Pointer pointset = mitk::PointSet::New(); + + int pointId = 0; + + for(int j=0; j tmpGrid; + + if(m_UseDistances) + tmpGrid = m_Clusters.at(avgDistances.at(j).second)->GetVtkUnstructuredGrid(); //highest distance + else + tmpGrid = m_Clusters.at(sizeIDs.at(j).second)->GetVtkUnstructuredGrid(); //biggest cluster + + for(int i=0; iGetNumberOfPoints();i++) + { + mitk::Point3D point; + point[0] = tmpGrid->GetPoint(i)[0]; + point[1] = tmpGrid->GetPoint(i)[1]; + point[2] = tmpGrid->GetPoint(i)[2]; + + pointset->InsertPoint(pointId,point); + pointId++; + } + } + + mitk::PlaneFit::Pointer planeFilter = mitk::PlaneFit::New(); + planeFilter->SetInput(pointset); + planeFilter->Update(); + + m_GeoData = planeFilter->GetOutput(); + + if(m_GeoData.IsNull()) + { + MITK_ERROR << "GeometryData output from PlaneFit filter is null"; + return; + } + + avgDistances.clear(); +} + +void mitk::ClusteredPlaneSuggestionFilter::GenerateOutputInformation() +{ + mitk::UnstructuredGrid::ConstPointer inputImage = this->GetInput(); + + m_MainCluster = this->GetOutput(); + + itkDebugMacro(<<"GenerateOutputInformation()"); + + if(inputImage.IsNull()) return; +} diff --git a/Modules/SurfaceInterpolation/mitkClusteredPlaneSuggestionFilter.h b/Modules/SurfaceInterpolation/mitkClusteredPlaneSuggestionFilter.h new file mode 100644 index 0000000000..5f157d1ce3 --- /dev/null +++ b/Modules/SurfaceInterpolation/mitkClusteredPlaneSuggestionFilter.h @@ -0,0 +1,127 @@ +/*=================================================================== + +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 _MITKCLUSTEREDPLANESUGGESTIONFILTER_h__ +#define _MITKCLUSTEREDPLANESUGGESTIONFILTER_h__ + +#include + +#include +#include + + +namespace mitk { + + /** + * @brief Clustering an UnstructuredGrid and calculating a Plane through it. + * + * The output is the biggest found cluster but you can get all clusters in a + * std::vector represented by vtkPoints. Use GetClusters() the get the vector. + * With GetGeoData() you get the calculated geometry as a mitk::GeometryData. + * Internally the mitk::UnstructuredGridClusteringFilter is used for + * clustering and after the mitk::PlaneFit for calculating the plane. + * The parameters m_Meshing (Set/GetMeshing()), m_MinPts (Set/GetMinPts()) and + * m_Eps (Set/GetEps()) are used for the UnstructuredGridClusteringFilter. + */ + class MITKSURFACEINTERPOLATION_EXPORT ClusteredPlaneSuggestionFilter + : public UnstructuredGridToUnstructuredGridFilter + { + public: + + mitkClassMacro(ClusteredPlaneSuggestionFilter, + UnstructuredGridToUnstructuredGridFilter) + + itkFactorylessNewMacro(Self) + + itkCloneMacro(Self) + + /** Returns the geometry of the calculated plane from mitk::PlaneFit */ + itkGetMacro(GeoData, mitk::GeometryData::Pointer) + + /** Returns all clusters which were found by the clustering filter */ + itkGetMacro(Clusters, std::vector< mitk::UnstructuredGrid::Pointer >) + + /** Activate the meshing function for the returned clusters. The meshing + * is needed to see the result in the 2D-renderwindows */ + itkGetMacro(Meshing, bool) + itkSetMacro(Meshing, bool) + + /** Minimal points which have to be located in the neighbourhood to define + * the point as a core point. For more information see DBSCAN algorithm */ + itkGetMacro(MinPts, int) + itkSetMacro(MinPts, int) + + /** The range/neighbourhood for clustering the points. For more + * information see DBSCAN algorithm */ + itkGetMacro(Eps, double) + itkSetMacro(Eps, double) + + /** Setting to true, uses the distances of the clusters otherwise the + * the size of the clusters is used */ + itkSetMacro(UseDistances, bool) + + /** Sets the number of clusters which are used for the plane suggestion + * if the number of found clusters is lower than the "NumberOfUsedClusters" + * all found clusters are used */ + itkSetMacro(NumberOfUsedClusters, unsigned int) + + + protected: + + /** Constructor */ + ClusteredPlaneSuggestionFilter(); + + /** Destructor */ + virtual ~ClusteredPlaneSuggestionFilter(); + + /** Is called by the Update() method of the filter */ + virtual void GenerateData(); + + /** Defines the output of the filter */ + virtual void GenerateOutputInformation(); + + + private: + + /** The geometry of the calculated plane */ + mitk::GeometryData::Pointer m_GeoData; + + /** The vector which holds all found clusters */ + std::vector< mitk::UnstructuredGrid::Pointer > m_Clusters; + + /** The biggest found cluster - the output */ + mitk::UnstructuredGrid::Pointer m_MainCluster; + + /** Connect the points to meshes. Required for 2D rendering */ + bool m_Meshing; + + /** Number of points required to define a core point */ + int m_MinPts; + + /** The distance/neighbourhood for clustering */ + double m_Eps; + + /** Decides to use the distances or the size */ + bool m_UseDistances; + + /** The number of clusters which are used for the plane suggestion */ + unsigned int m_NumberOfUsedClusters; + + }; + +} // namespace mitk + +#endif //_MITKCLUSTEREDPLANESUGGESTIONFILTER_h__ diff --git a/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h b/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h index b2914e9a01..f668260f96 100644 --- a/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h +++ b/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h @@ -1,184 +1,186 @@ /*=================================================================== 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 #include "mitkImageSource.h" #include "mitkSurface.h" #include "mitkProgressBar.h" #include "vnl/vnl_vector_fixed.h" #include "itkImageBase.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 MITKSURFACEINTERPOLATION_EXPORT CreateDistanceImageFromSurfaceFilter : public ImageSource { public: typedef vnl_vector_fixed PointType; typedef itk::Image DistanceImageType; typedef DistanceImageType::IndexType IndexType; typedef std::vector< PointType > NormalList; typedef std::vector< PointType > CenterList; typedef std::vector SurfaceList; mitkClassMacro(CreateDistanceImageFromSurfaceFilter,ImageSource); itkFactorylessNewMacro(Self) itkCloneMacro(Self) + itkGetMacro(DistanceImageSpacing, double) + using Superclass::SetInput; //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() override; virtual void GenerateOutputInformation() override; private: void CreateSolutionMatrixAndFunctionValues(); double CalculateDistanceValue(PointType p); void FillDistanceImage (); /** * \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 PreprocessContourPoints(); void CreateEmptyDistanceImage(); //Datastructures for the interpolation CenterList m_Centers; NormalList m_Normals; Eigen::MatrixXd m_SolutionMatrix; Eigen::VectorXd m_FunctionValues; Eigen::VectorXd m_Weights; DistanceImageType::Pointer m_DistanceImageITK; itk::ImageBase<3>::Pointer m_ReferenceImage; double m_DistanceImageSpacing; double m_DistanceImageDefaultBufferValue; unsigned int m_DistanceImageVolume; bool m_UseProgressBar; unsigned int m_ProgressStepSize; }; }//namespace #endif diff --git a/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.cpp b/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.cpp new file mode 100644 index 0000000000..12981a7dba --- /dev/null +++ b/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.cpp @@ -0,0 +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. + +===================================================================*/ + +#include "mitkImageToPointCloudFilter.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +mitk::ImageToPointCloudFilter::ImageToPointCloudFilter(): + m_NumberOfExtractedPoints(0) +{ + m_Method = DetectionMethod(0); + + this->SetNumberOfRequiredInputs(1); + + this->SetNumberOfIndexedOutputs(1); +} + +mitk::ImageToPointCloudFilter::~ImageToPointCloudFilter(){} + +void mitk::ImageToPointCloudFilter::GenerateData() +{ + mitk::Image::ConstPointer image = ImageToUnstructuredGridFilter::GetInput(); + m_Geometry = image->GetGeometry(); + + if ( image.IsNull() ) + { + MITK_ERROR << "mitk::ImageToContourFilter: No input available. " + "Please set the input!" << std::endl; + return; + } + + mitk::Image::Pointer notConstImage = const_cast(image.GetPointer()); + + switch(m_Method) + { + case 0: + AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 2) + break; + + case 1: + AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 3) + break; + + case 2: + AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 4) + break; + + default: + AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 2) + break; + } +} + +template +void mitk::ImageToPointCloudFilter:: + StdDeviations(itk::Image* image, int amount) +{ + typedef itk::Image InputImageType; + typedef itk::CastImageFilter< InputImageType, FloatImageType > + ImagePTypeToFloatPTypeCasterType; + typedef itk::LaplacianImageFilter< FloatImageType, FloatImageType > + LaplacianFilterType; + typename LaplacianFilterType::Pointer lapFilter = LaplacianFilterType::New(); + + typename ImagePTypeToFloatPTypeCasterType::Pointer caster = + ImagePTypeToFloatPTypeCasterType::New(); + caster->SetInput( image ); + caster->Update(); + FloatImageType::Pointer fImage = caster->GetOutput(); + + lapFilter->SetInput(fImage); + lapFilter->UpdateLargestPossibleRegion(); + mitk::Image::Pointer edgeImage = mitk::ImportItkImage(lapFilter->GetOutput()); + + mitk::ImageStatisticsCalculator::Pointer statCalc = + mitk::ImageStatisticsCalculator::New(); + statCalc->SetImage(edgeImage); + statCalc->ComputeStatistics(); + mitk::ImageStatisticsCalculator::Statistics stats = statCalc->GetStatistics(); + double mean = stats.GetMean(); + double stdDev = stats.GetSigma(); + + double upperThreshold = mean + stdDev * amount; + double lowerThreshold = mean - stdDev * amount; + + typename itk::ImageRegionIterator it(lapFilter->GetOutput(), + lapFilter->GetOutput()->GetRequestedRegion()); + + vtkSmartPointer points = vtkSmartPointer::New(); + + double greatX=0, greatY=0, greatZ=0; + + it.GoToBegin(); + while( !it.IsAtEnd() ) + { + if(it.Get() > lowerThreshold && it.Get() < upperThreshold) + { + it.Set(0); + } + else + { + it.Set(1); + + mitk::Point3D imagePoint; + mitk::Point3D worldPoint; + + imagePoint[0] = it.GetIndex()[0]; + imagePoint[1] = it.GetIndex()[1]; + imagePoint[2] = it.GetIndex()[2]; + + m_Geometry->IndexToWorld(imagePoint, worldPoint); + + if(worldPoint[0]>greatX) + greatX=worldPoint[0]; + if(worldPoint[1]>greatY) + greatY=worldPoint[1]; + if(worldPoint[2]>greatZ) + greatZ=worldPoint[2]; + + points->InsertNextPoint(worldPoint[0],worldPoint[1],worldPoint[2]); + m_NumberOfExtractedPoints++; + } + ++it; + } + + /*need to build the UnstructuredGrid with at least one vertex otherwise its + not visible*/ + vtkSmartPointer verts = vtkSmartPointer::New(); + + verts->GetPointIds()->SetNumberOfIds(m_NumberOfExtractedPoints); + for(int i=0; iGetPointIds()->SetId(i,i); + } + + vtkSmartPointer uGrid = vtkSmartPointer::New(); + uGrid->Allocate(1); + + uGrid->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); + uGrid->SetPoints(points); + + mitk::UnstructuredGrid::Pointer outputGrid = mitk::UnstructuredGrid::New(); + outputGrid->SetVtkUnstructuredGrid(uGrid); + this->SetNthOutput(0, outputGrid); +} + + +void mitk::ImageToPointCloudFilter::GenerateOutputInformation() +{ + Superclass::GenerateOutputInformation(); +} diff --git a/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.h b/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.h new file mode 100644 index 0000000000..e2e74638f4 --- /dev/null +++ b/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.h @@ -0,0 +1,102 @@ +/*=================================================================== + +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 mitkImageToPointCloudFilter_h_Included +#define mitkImageToPointCloudFilter_h_Included + +#include +#include + +#include + +namespace mitk +{ + +/** +* @brief The filter extracts the edge pixels of an image as points and stores +* them in an UnstructuredGrid. Every pixel which grey value is between the +* mean +- standard deviation * (2 or 3), will be extracted as point. The +* DetectionMethod can be set to choose if the doubled or tripled standard +* deviation is used. +*/ +class MITKSURFACEINTERPOLATION_EXPORT ImageToPointCloudFilter: + public ImageToUnstructuredGridFilter +{ + +public: + + /** + * @brief The method which calculates and extracts the edge pixels/points. + * For the edge detection the laplacian filter is used and for extraction + * the standard deviation multiplied with 2, 3 or 4 (depending on selected + * method) is used. + */ + enum DetectionMethod + { + LAPLACIAN_STD_DEV2 = 0, + LAPLACIAN_STD_DEV3 = 1, + LAPLACIAN_STD_DEV4 = 2 + }; + + mitkClassMacro( ImageToPointCloudFilter, ImageToUnstructuredGridFilter) + itkFactorylessNewMacro(Self) + + typedef itk::Image FloatImageType; + + /** Returns the selected DetectionMethod */ + itkGetMacro(Method,DetectionMethod) + + /** Returns the number of extracted points after edge detection */ + itkGetMacro(NumberOfExtractedPoints,int) + + /** Sets the DetectionMethod for edge detection and extraction */ + itkSetMacro(Method,DetectionMethod) + +protected: + + /** This method is called by Update(). */ + virtual void GenerateData(); + + /** Initializes the output information */ + virtual void GenerateOutputInformation(); + + /** Constructor */ + ImageToPointCloudFilter(); + + /** Destructor */ + virtual ~ImageToPointCloudFilter(); + +private: + + /** Uses the laplace filter to create an image and extracts a pixel as point + * if the grey value is between the mean +- standard deviation * (2 or 3) */ + template + void StdDeviations(itk::Image* image, int amount); + + /** The geometry of the input image for transforming the pixel coordinates to + * world coordinates */ + mitk::BaseGeometry* m_Geometry; + + /** The number of extracted points */ + int m_NumberOfExtractedPoints; + + /** The selected detection method */ + DetectionMethod m_Method; + +}; + +} +#endif diff --git a/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.cpp b/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.cpp index ba07f1ff6e..9379d6242a 100644 --- a/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.cpp +++ b/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.cpp @@ -1,134 +1,186 @@ /*=================================================================== 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 "mitkPointCloudScoringFilter.h" +#include + #include #include #include #include #include +#include +#include mitk::PointCloudScoringFilter::PointCloudScoringFilter(): m_NumberOfOutpPoints(0) { - m_OutpGrid = mitk::UnstructuredGrid::New(); - - this->SetNthOutput(0, m_OutpGrid); + this->SetNumberOfIndexedOutputs(1); } mitk::PointCloudScoringFilter::~PointCloudScoringFilter(){} void mitk::PointCloudScoringFilter::GenerateData() { if(UnstructuredGridToUnstructuredGridFilter::GetNumberOfInputs()!=2) { MITK_ERROR << "Not enough input arguments for PointCloudScoringFilter" << std::endl; return; } DataObjectPointerArray inputs = UnstructuredGridToUnstructuredGridFilter::GetInputs(); mitk::UnstructuredGrid::Pointer edgeGrid = dynamic_cast(inputs.at(0).GetPointer()); mitk::UnstructuredGrid::Pointer segmGrid = dynamic_cast(inputs.at(1).GetPointer()); if(edgeGrid->IsEmpty() || segmGrid->IsEmpty()) { if(edgeGrid->IsEmpty()) - MITK_ERROR << "Cannot convert edgeGrid into Surfaces" << std::endl; + MITK_ERROR << "edgeGrid is empty" << std::endl; if(segmGrid->IsEmpty()) - MITK_ERROR << "Cannot convert segmGrid into Surfaces" << std::endl; + MITK_ERROR << "segmGrid is empty" << std::endl; } + if(m_FilteredScores.size()>0) + m_FilteredScores.clear(); + vtkSmartPointer edgevtkGrid = edgeGrid->GetVtkUnstructuredGrid(); vtkSmartPointer segmvtkGrid = segmGrid->GetVtkUnstructuredGrid(); // KdTree from here vtkSmartPointer kdPoints = vtkSmartPointer::New(); vtkSmartPointer kdTree = vtkSmartPointer::New(); for(int i=0; iGetNumberOfPoints(); i++) { kdPoints->InsertNextPoint(edgevtkGrid->GetPoint(i)); } kdTree->BuildLocatorFromPoints(kdPoints); // KdTree until here vtkSmartPointer points = vtkSmartPointer::New(); for(int i=0; iGetNumberOfPoints(); i++) { points->InsertNextPoint(segmvtkGrid->GetPoint(i)); } std::vector< ScorePair > score; + std::vector< double > distances; double dist_glob = 0.0; double dist = 0.0; for(int i=0; iGetNumberOfPoints(); i++) { double point[3]; points->GetPoint(i,point); kdTree->FindClosestPoint(point[0],point[1],point[2],dist); dist_glob+=dist; + distances.push_back(dist); score.push_back(std::make_pair(i,dist)); } double avg = dist_glob / points->GetNumberOfPoints(); - for(unsigned int i=0; i avg) + tmpVar = tmpVar + ((distances.at(i) - avg) * (distances.at(i) - avg)); + if(distances.at(i)>highest) + highest = distances.at(i); + } + + //CUBIC MEAN + double cubicAll = 0.0; + for(unsigned i=0; i(score.size()); + double cubic = pow(root2,1.0/3.0); + //CUBIC END + + double metricValue = cubic; + + for(unsigned int i=0; i metricValue) { m_FilteredScores.push_back(std::make_pair(score.at(i).first,score.at(i).second)); } } m_NumberOfOutpPoints = m_FilteredScores.size(); vtkSmartPointer filteredPoints = vtkSmartPointer::New(); + //storing the distances in the uGrid PointData + vtkSmartPointer pointDataDistances = + vtkSmartPointer::New(); + pointDataDistances->SetNumberOfComponents(1); + pointDataDistances->SetNumberOfTuples(m_FilteredScores.size()); + pointDataDistances->SetName("Distances"); + for(unsigned int i=0; iGetPoint(m_FilteredScores.at(i).first); filteredPoints->InsertNextPoint(point[0],point[1],point[2]); + if(score.at(i).second > 0.001) + { + double dist[1] = {score.at(i).second}; + pointDataDistances->InsertTuple(i,dist); + } + else + { + double dist[1] = {0.0}; + pointDataDistances->InsertTuple(i,dist); + } } unsigned int numPoints = filteredPoints->GetNumberOfPoints(); vtkSmartPointer verts = vtkSmartPointer::New(); verts->GetPointIds()->SetNumberOfIds(numPoints); for(unsigned int i=0; iGetPointIds()->SetId(i,i); } vtkSmartPointer uGrid = vtkSmartPointer::New(); uGrid->Allocate(1); uGrid->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); uGrid->SetPoints(filteredPoints); + uGrid->GetPointData()->AddArray(pointDataDistances); + + mitk::UnstructuredGrid::Pointer outputGrid = mitk::UnstructuredGrid::New(); + outputGrid->SetVtkUnstructuredGrid(uGrid); + this->SetNthOutput(0, outputGrid); - m_OutpGrid->SetVtkUnstructuredGrid(uGrid); + score.clear(); + distances.clear(); } void mitk::PointCloudScoringFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } diff --git a/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.h b/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.h index bac9e79f62..8393a8ed19 100644 --- a/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.h +++ b/Modules/SurfaceInterpolation/mitkPointCloudScoringFilter.h @@ -1,89 +1,84 @@ /*=================================================================== 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 mitkPointCloudScoringFilter_h_Included #define mitkPointCloudScoringFilter_h_Included #include #include namespace mitk { class UnstructuredGrid; /** * @brief Scores an UnstructuredGrid as good as one matches to the other. * * The result UnstructureGrid of the filter are the points where the distance to * the closest point of the other UnstructuredGrid is higher than the average * distance from all points to their closest neighbours of the other * UnstructuredGrid. * The second input is the UnstructuredGrid, which you want to score. All Points * of the output UnstructuredGrid are from the second input. */ class MITKSURFACEINTERPOLATION_EXPORT PointCloudScoringFilter: public UnstructuredGridToUnstructuredGridFilter { public: typedef std::pair ScorePair; mitkClassMacro( PointCloudScoringFilter, UnstructuredGridToUnstructuredGridFilter) itkFactorylessNewMacro(Self) - itkCloneMacro(Self) /** Number of Points of the scored UnstructuredGrid. These points are far away * from their neighbours */ itkGetMacro(NumberOfOutpPoints, int) /** A vector in which the point IDs and their distance to their neighbours * is stored */ itkGetMacro(FilteredScores, std::vector< ScorePair >) protected: /** is called by the Update() method */ virtual void GenerateData() override; /** Defines the output */ virtual void GenerateOutputInformation() override; /** Constructor */ PointCloudScoringFilter(); /** Destructor */ virtual ~PointCloudScoringFilter(); private: - /** The output UnstructuredGrid containing the scored points, which are far - * aways from their neighbours */ - mitk::UnstructuredGrid::Pointer m_OutpGrid; - /** The Point IDs and their distance to their neighbours */ std::vector< ScorePair > m_FilteredScores; /** The number of points which are far aways from their neighbours */ int m_NumberOfOutpPoints; }; } #endif diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp index b8ecd5a08e..347f1ff468 100644 --- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp +++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.cpp @@ -1,666 +1,669 @@ /*=================================================================== 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" //#include "vtkXMLPolyDataWriter.h" #include "vtkPolyDataWriter.h" // Check whether the given contours are coplanar bool ContoursCoplanar(mitk::SurfaceInterpolationController::ContourPositionInformation leftHandSide, mitk::SurfaceInterpolationController::ContourPositionInformation rightHandSide) { // Here we check two things: // 1. Whether the normals of both contours are at least parallel // 2. Whether both contours lie in the same plane // Check for coplanarity: // a. Span a vector between two points one from each contour // b. Calculate dot product for the vector and one of the normals // c. If the dot is zero the two vectors are orthogonal and the contours are coplanar double vec[3]; vec[0] = leftHandSide.contourPoint[0] - rightHandSide.contourPoint[0]; vec[1] = leftHandSide.contourPoint[1] - rightHandSide.contourPoint[1]; vec[2] = leftHandSide.contourPoint[2] - rightHandSide.contourPoint[2]; double n[3]; n[0] = rightHandSide.contourNormal[0]; n[1] = rightHandSide.contourNormal[1]; n[2] = rightHandSide.contourNormal[2]; double dot = vtkMath::Dot(n, vec); double n2[3]; n2[0] = leftHandSide.contourNormal[0]; n2[1] = leftHandSide.contourNormal[1]; n2[2] = leftHandSide.contourNormal[2]; // The normals of both contours have to be parallel but not of the same orientation double lengthLHS = leftHandSide.contourNormal.GetNorm(); double lengthRHS = rightHandSide.contourNormal.GetNorm(); double dot2 = vtkMath::Dot(n, n2); bool contoursParallel = mitk::Equal(fabs(lengthLHS*lengthRHS), fabs(dot2), 0.001); if (mitk::Equal(dot, 0.0, 0.001) && contoursParallel) return true; else return false; } mitk::SurfaceInterpolationController::ContourPositionInformation CreateContourPositionInformation(mitk::Surface::Pointer contour) { mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo; contourInfo.contour = contour; double n[3]; double p[3]; contour->GetVtkPolyData()->GetPoints()->GetPoint(0, p); vtkPolygon::ComputeNormal(contour->GetVtkPolyData()->GetPoints(), n); contourInfo.contourNormal = n; contourInfo.contourPoint = p; return contourInfo; } mitk::SurfaceInterpolationController::SurfaceInterpolationController() :m_SelectedSegmentation(nullptr), m_CurrentTimeStep(0) { + m_DistanceImageSpacing = 0.0; m_ReduceFilter = ReduceContourSetFilter::New(); m_NormalsFilter = ComputeContourSetNormalsFilter::New(); m_InterpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New(); //m_TimeSelector = ImageTimeSelector::New(); m_ReduceFilter->SetUseProgressBar(false); // m_ReduceFilter->SetProgressStepSize(1); m_NormalsFilter->SetUseProgressBar(true); m_NormalsFilter->SetProgressStepSize(1); m_InterpolateSurfaceFilter->SetUseProgressBar(true); m_InterpolateSurfaceFilter->SetProgressStepSize(7); m_Contours = Surface::New(); m_PolyData = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); m_PolyData->SetPoints(points); m_InterpolationResult = nullptr; m_CurrentNumberOfReducedContours = 0; } mitk::SurfaceInterpolationController::~SurfaceInterpolationController() { //Removing all observers auto dataIter = m_SegmentationObserverTags.begin(); for (; dataIter != m_SegmentationObserverTags.end(); ++dataIter ) { (*dataIter).first->RemoveObserver( (*dataIter).second ); } m_SegmentationObserverTags.clear(); } mitk::SurfaceInterpolationController* mitk::SurfaceInterpolationController::GetInstance() { static mitk::SurfaceInterpolationController::Pointer m_Instance; if ( m_Instance.IsNull() ) { m_Instance = SurfaceInterpolationController::New(); } return m_Instance; } void mitk::SurfaceInterpolationController::AddNewContour (mitk::Surface::Pointer newContour) { if( newContour->GetVtkPolyData()->GetNumberOfPoints() > 0) { ContourPositionInformation contourInfo = CreateContourPositionInformation(newContour); this->AddToInterpolationPipeline(contourInfo); this->Modified(); } } void mitk::SurfaceInterpolationController::AddNewContours(std::vector newContours) { for (unsigned int i = 0; i < newContours.size(); ++i) { if( newContours.at(i)->GetVtkPolyData()->GetNumberOfPoints() > 0) { ContourPositionInformation contourInfo = CreateContourPositionInformation(newContours.at(i)); this->AddToInterpolationPipeline(contourInfo); } } this->Modified(); } void mitk::SurfaceInterpolationController::AddToInterpolationPipeline(ContourPositionInformation contourInfo ) { if(!m_SelectedSegmentation) { return; } int pos (-1); unsigned int numTimeSteps = m_SelectedSegmentation->GetTimeSteps(); if ( m_CurrentTimeStep >= numTimeSteps ) { MITK_ERROR << "Invalid time step requested for interpolation pipeline."; return; } ContourPositionInformationVec2D currentContours = m_ListOfInterpolationSessions[m_SelectedSegmentation]; ContourPositionInformationList currentContourList = currentContours[m_CurrentTimeStep]; mitk::Surface* newContour = contourInfo.contour; for (unsigned int i = 0; i < currentContourList.size(); i++) { ContourPositionInformation contourFromList = currentContourList.at(i); if (ContoursCoplanar(contourInfo, contourFromList)) { pos = i; break; } } //Don't save a new empty contour if (pos == -1 && newContour->GetVtkPolyData()->GetNumberOfPoints() > 0) { m_ReduceFilter->SetInput(m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].size(), newContour); m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].push_back(contourInfo); } else if (pos != -1 && newContour->GetVtkPolyData()->GetNumberOfPoints() > 0) { m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].at(pos) = contourInfo; m_ReduceFilter->SetInput(pos, newContour); } else if (newContour->GetVtkPolyData()->GetNumberOfPoints() == 0) { this->RemoveContour(contourInfo); } } bool mitk::SurfaceInterpolationController::RemoveContour(ContourPositionInformation contourInfo ) { if(!m_SelectedSegmentation) { return false; } unsigned int numTimeSteps = m_SelectedSegmentation->GetTimeSteps(); if ( m_CurrentTimeStep >= numTimeSteps ) { return false; } auto it = m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].begin(); while (it != m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].end()) { ContourPositionInformation currentContour = (*it); if (ContoursCoplanar(currentContour, contourInfo)) { m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].erase(it); this->ReinitializeInterpolation(); return true; } ++it; } return false; } const mitk::Surface* mitk::SurfaceInterpolationController::GetContour(ContourPositionInformation contourInfo ) { if(!m_SelectedSegmentation) { return nullptr; } unsigned int numTimeSteps = m_SelectedSegmentation->GetTimeSteps(); if ( m_CurrentTimeStep >= numTimeSteps ) { return nullptr; } ContourPositionInformationList contourList = m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep]; for (unsigned int i = 0; i < contourList.size(); ++i) { ContourPositionInformation currentContour = contourList.at(i); if (ContoursCoplanar(contourInfo, currentContour)) return currentContour.contour; } return nullptr; } unsigned int mitk::SurfaceInterpolationController::GetNumberOfContours() { if(!m_SelectedSegmentation) { return -1; } unsigned int numTimeSteps = m_SelectedSegmentation->GetTimeSteps(); if ( m_CurrentTimeStep >= numTimeSteps ) { return -1; } return m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].size(); } void mitk::SurfaceInterpolationController::Interpolate() { m_ReduceFilter->Update(); m_CurrentNumberOfReducedContours = m_ReduceFilter->GetNumberOfOutputs(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( m_SelectedSegmentation ); timeSelector->SetTimeNr( m_CurrentTimeStep ); timeSelector->SetChannelNr( 0 ); timeSelector->Update(); mitk::Image::Pointer refSegImage = timeSelector->GetOutput(); m_NormalsFilter->SetSegmentationBinaryImage(refSegImage); for (unsigned int i = 0; i < m_CurrentNumberOfReducedContours; i++) { mitk::Surface::Pointer reducedContour = m_ReduceFilter->GetOutput(i); reducedContour->DisconnectPipeline(); m_NormalsFilter->SetInput(i, reducedContour); m_InterpolateSurfaceFilter->SetInput(i, m_NormalsFilter->GetOutput(i)); } if (m_CurrentNumberOfReducedContours< 2) { //If no interpolation is possible reset the interpolation result m_InterpolationResult = nullptr; return; } //Setting up progress bar mitk::ProgressBar::GetInstance()->AddStepsToDo(10); // create a surface from the distance-image mitk::ImageToSurfaceFilter::Pointer imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New(); imageToSurfaceFilter->SetInput( m_InterpolateSurfaceFilter->GetOutput() ); imageToSurfaceFilter->SetThreshold( 0 ); imageToSurfaceFilter->SetSmooth(true); imageToSurfaceFilter->SetSmoothIteration(20); imageToSurfaceFilter->Update(); mitk::Surface::Pointer interpolationResult = mitk::Surface::New(); interpolationResult->SetVtkPolyData( imageToSurfaceFilter->GetOutput()->GetVtkPolyData(), m_CurrentTimeStep ); m_InterpolationResult = interpolationResult; + m_DistanceImageSpacing = m_InterpolateSurfaceFilter->GetDistanceImageSpacing(); + vtkSmartPointer polyDataAppender = vtkSmartPointer::New(); for (unsigned int i = 0; i < m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].size(); i++) { polyDataAppender->AddInputData(m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].at(i).contour->GetVtkPolyData()); } polyDataAppender->Update(); m_Contours->SetVtkPolyData(polyDataAppender->GetOutput()); //Last progress step mitk::ProgressBar::GetInstance()->Progress(20); 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); } mitk::Image::Pointer mitk::SurfaceInterpolationController::GetCurrentSegmentation() { return m_SelectedSegmentation; } 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; } unsigned int mitk::SurfaceInterpolationController::GetNumberOfInterpolationSessions() { return m_ListOfInterpolationSessions.size(); } template void mitk::SurfaceInterpolationController::GetImageBase(itk::Image* input, itk::ImageBase<3>::Pointer& result) { result->Graft(input); } void mitk::SurfaceInterpolationController::SetCurrentSegmentationInterpolationList(mitk::Image::Pointer segmentation) { this->SetCurrentInterpolationSession(segmentation); } void mitk::SurfaceInterpolationController::SetCurrentInterpolationSession(mitk::Image::Pointer currentSegmentationImage) { if (currentSegmentationImage.GetPointer() == m_SelectedSegmentation) return; if (currentSegmentationImage.IsNull()) { m_SelectedSegmentation = nullptr; return; } m_SelectedSegmentation = currentSegmentationImage.GetPointer(); auto it = m_ListOfInterpolationSessions.find(currentSegmentationImage.GetPointer()); // If the session does not exist yet create a new ContourPositionPairList otherwise reinitialize the interpolation pipeline if (it == m_ListOfInterpolationSessions.end()) { ContourPositionInformationVec2D newList; m_ListOfInterpolationSessions.insert(std::pair(m_SelectedSegmentation, newList)); m_InterpolationResult = nullptr; m_CurrentNumberOfReducedContours = 0; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &SurfaceInterpolationController::OnSegmentationDeleted); m_SegmentationObserverTags.insert( std::pair( m_SelectedSegmentation, m_SelectedSegmentation->AddObserver( itk::DeleteEvent(), command ) ) ); } this->ReinitializeInterpolation(); } bool mitk::SurfaceInterpolationController::ReplaceInterpolationSession(mitk::Image::Pointer oldSession, mitk::Image::Pointer newSession) { if (oldSession.IsNull() || newSession.IsNull()) return false; if (oldSession.GetPointer() == newSession.GetPointer()) return false; if (!mitk::Equal(*(oldSession->GetGeometry()), *(newSession->GetGeometry()), mitk::eps, false)) return false; auto it = m_ListOfInterpolationSessions.find(oldSession.GetPointer()); if (it == m_ListOfInterpolationSessions.end()) return false; ContourPositionInformationVec2D oldList = (*it).second; m_ListOfInterpolationSessions.insert(std::pair(newSession.GetPointer(), oldList)); itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &SurfaceInterpolationController::OnSegmentationDeleted); m_SegmentationObserverTags.insert( std::pair( newSession, newSession->AddObserver( itk::DeleteEvent(), command ) ) ); if (m_SelectedSegmentation == oldSession) m_SelectedSegmentation = newSession; mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( m_SelectedSegmentation ); timeSelector->SetTimeNr( m_CurrentTimeStep ); timeSelector->SetChannelNr( 0 ); timeSelector->Update(); mitk::Image::Pointer refSegImage = timeSelector->GetOutput(); m_NormalsFilter->SetSegmentationBinaryImage(refSegImage); this->RemoveInterpolationSession(oldSession); return true; } void mitk::SurfaceInterpolationController::RemoveSegmentationFromContourList(mitk::Image *segmentation) { this->RemoveInterpolationSession(segmentation); } void mitk::SurfaceInterpolationController::RemoveInterpolationSession(mitk::Image::Pointer segmentationImage) { if (segmentationImage) { if (m_SelectedSegmentation == segmentationImage) { m_NormalsFilter->SetSegmentationBinaryImage(nullptr); m_SelectedSegmentation = nullptr; } m_ListOfInterpolationSessions.erase(segmentationImage); // Remove observer auto pos = m_SegmentationObserverTags.find(segmentationImage); if (pos != m_SegmentationObserverTags.end()) { segmentationImage->RemoveObserver((*pos).second); m_SegmentationObserverTags.erase(pos); } } } void mitk::SurfaceInterpolationController::RemoveAllInterpolationSessions() { //Removing all observers auto dataIter = m_SegmentationObserverTags.begin(); while (dataIter != m_SegmentationObserverTags.end()) { mitk::Image* image = (*dataIter).first; image->RemoveObserver((*dataIter).second); ++dataIter; } m_SegmentationObserverTags.clear(); m_SelectedSegmentation = nullptr; m_ListOfInterpolationSessions.clear(); } void mitk::SurfaceInterpolationController::ReinitializeInterpolation(mitk::Surface::Pointer contours) { // 1. detect coplanar contours // 2. merge coplanar contours into a single surface // 4. add contour to pipeline // Split the surface into separate polygons vtkSmartPointer existingPolys; vtkSmartPointer existingPoints; existingPolys = contours->GetVtkPolyData()->GetPolys(); existingPoints = contours->GetVtkPolyData()->GetPoints(); existingPolys->InitTraversal(); vtkSmartPointer ids = vtkSmartPointer::New(); typedef std::pair PointNormalPair; std::vector list; std::vector > pointsList; int count (0); for( existingPolys->InitTraversal(); existingPolys->GetNextCell(ids);) { // Get the points vtkSmartPointer points = vtkSmartPointer::New(); existingPoints->GetPoints(ids, points); ++count; pointsList.push_back(points); PointNormalPair p_n; double n[3]; vtkPolygon::ComputeNormal(points, n); p_n.first = n; double p[3]; existingPoints->GetPoint(ids->GetId(0), p); p_n.second = p; ContourPositionInformation p_info; p_info.contourNormal = n; p_info.contourPoint = p; list.push_back(p_info); continue; } // Detect and sort coplanar polygons auto outer = list.begin(); std::vector< std::vector< vtkSmartPointer > > relatedPoints; while (outer != list.end()) { auto inner = outer; ++inner; std::vector< vtkSmartPointer > rel; auto pointsIter = pointsList.begin(); rel.push_back((*pointsIter)); pointsIter = pointsList.erase(pointsIter); while (inner != list.end()) { if(ContoursCoplanar((*outer),(*inner))) { inner = list.erase(inner); rel.push_back((*pointsIter)); pointsIter = pointsList.erase(pointsIter); } else { ++inner; ++pointsIter; } } relatedPoints.push_back(rel); ++outer; } // Build the separate surfaces again std::vector finalSurfaces; for (unsigned int i = 0; i < relatedPoints.size(); ++i) { vtkSmartPointer contourSurface = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer polygons = vtkSmartPointer::New(); unsigned int pointId (0); for (unsigned int j = 0; j < relatedPoints.at(i).size(); ++j) { unsigned int numPoints = relatedPoints.at(i).at(j)->GetNumberOfPoints(); vtkSmartPointer polygon = vtkSmartPointer::New(); polygon->GetPointIds()->SetNumberOfIds(numPoints); polygon->GetPoints()->SetNumberOfPoints(numPoints); vtkSmartPointer currentPoints = relatedPoints.at(i).at(j); for (unsigned k = 0; k < numPoints; ++k) { points->InsertPoint(pointId, currentPoints->GetPoint(k)); polygon->GetPointIds()->SetId(k, pointId); ++pointId; } polygons->InsertNextCell(polygon); } contourSurface->SetPoints(points); contourSurface->SetPolys(polygons); contourSurface->BuildLinks(); mitk::Surface::Pointer surface = mitk::Surface::New(); surface->SetVtkPolyData(contourSurface); finalSurfaces.push_back(surface); } // Add detected contours to interpolation pipeline this->AddNewContours(finalSurfaces); } void mitk::SurfaceInterpolationController::OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &/*event*/) { mitk::Image* tempImage = dynamic_cast(const_cast(caller)); if (tempImage) { if (m_SelectedSegmentation == tempImage) { m_NormalsFilter->SetSegmentationBinaryImage(nullptr); m_SelectedSegmentation = nullptr; } m_SegmentationObserverTags.erase(tempImage); m_ListOfInterpolationSessions.erase(tempImage); } } void mitk::SurfaceInterpolationController::ReinitializeInterpolation() { // If session has changed reset the pipeline m_ReduceFilter->Reset(); m_NormalsFilter->Reset(); m_InterpolateSurfaceFilter->Reset(); itk::ImageBase<3>::Pointer itkImage = itk::ImageBase<3>::New(); if ( m_SelectedSegmentation ) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( m_SelectedSegmentation ); timeSelector->SetTimeNr( m_CurrentTimeStep ); timeSelector->SetChannelNr( 0 ); timeSelector->Update(); mitk::Image::Pointer refSegImage = timeSelector->GetOutput(); AccessFixedDimensionByItk_1( refSegImage, GetImageBase, 3, itkImage ); m_InterpolateSurfaceFilter->SetReferenceImage(itkImage.GetPointer()); unsigned int numTimeSteps = m_SelectedSegmentation->GetTimeSteps(); unsigned int size = m_ListOfInterpolationSessions[m_SelectedSegmentation].size(); if ( size != numTimeSteps ) { m_ListOfInterpolationSessions[m_SelectedSegmentation].resize( numTimeSteps ); } if ( m_CurrentTimeStep < numTimeSteps ) { unsigned int numContours = m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep].size(); for ( unsigned int c = 0; c < numContours; ++c ) { m_ReduceFilter->SetInput(c, m_ListOfInterpolationSessions[m_SelectedSegmentation][m_CurrentTimeStep][c].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(); } } diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h index 1acb2344ff..43f301fef3 100644 --- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h +++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h @@ -1,259 +1,263 @@ /*=================================================================== 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 #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 "mitkImageTimeSelector.h" #include "mitkProgressBar.h" namespace mitk { class MITKSURFACEINTERPOLATION_EXPORT SurfaceInterpolationController : public itk::Object { public: mitkClassMacroItkParent(SurfaceInterpolationController, itk::Object) itkFactorylessNewMacro(Self) itkCloneMacro(Self) + itkGetMacro(DistanceImageSpacing, double) + struct ContourPositionInformation { Surface::Pointer contour; Vector3D contourNormal; Point3D contourPoint; }; typedef std::vector ContourPositionInformationList; typedef std::vector ContourPositionInformationVec2D; //typedef std::map ContourListMap; typedef std::map ContourListMap; static SurfaceInterpolationController* GetInstance(); void SetCurrentTimeStep( unsigned int ts ) { if ( m_CurrentTimeStep != ts ) { m_CurrentTimeStep = ts; if ( m_SelectedSegmentation ) { this->ReinitializeInterpolation(); } } }; unsigned int GetCurrentTimeStep() { return m_CurrentTimeStep; }; /** * @brief Adds a new extracted contour to the list * @param newContour the contour to be added. If a contour at that position * already exists the related contour will be updated */ void AddNewContour (Surface::Pointer newContour); /** * @brief Removes the contour for a given plane for the current selected segmenation * @param contourInfo the contour which should be removed * @return true if a contour was found and removed, false if no contour was found */ bool RemoveContour (ContourPositionInformation contourInfo ); /** * @brief Adds new extracted contours to the list. If one or more contours at a given position * already exist they will be updated respectively * @param newContours the list of the contours */ void AddNewContours (std::vector newContours); /** * @brief Returns the contour for a given plane for the current selected segmenation * @param ontourInfo the contour which should be returned * @return the contour as an mitk::Surface. If no contour is available at the give position NULL is returned */ const mitk::Surface* GetContour (ContourPositionInformation contourInfo ); /** * @brief Returns the number of available contours for the current selected segmentation * @return the number of contours */ unsigned int GetNumberOfContours(); /** * 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); /** * @brief Get the current selected segmentation for which the interpolation is performed * @return the current segmentation image */ mitk::Image::Pointer GetCurrentSegmentation(); 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 * \deprecatedSince{2014_03} */ DEPRECATED (void SetCurrentSegmentationInterpolationList(mitk::Image::Pointer segmentation)); /** * Sets the current list of contourpoints which is used for the surface interpolation * @param segmentation The current selected segmentation */ void SetCurrentInterpolationSession(mitk::Image::Pointer currentSegmentationImage); /** * Removes the segmentation and all its contours from the list * @param segmentation The segmentation to be removed * \deprecatedSince{2014_03} */ DEPRECATED (void RemoveSegmentationFromContourList(mitk::Image* segmentation)); /** * @brief Remove interpolation session * @param segmentationImage the session to be removed */ void RemoveInterpolationSession(mitk::Image::Pointer segmentationImage); /** * Replaces the current interpolation session with a new one. All contours form the old * session will be applied to the new session. This only works if the two images have the * geometry * @param oldSession the session which should be replaced * @param newSession the new session which replaces the old one * @return true it the the replacement was successful, false if not (e.g. the image's geometry differs) */ bool ReplaceInterpolationSession(mitk::Image::Pointer oldSession, mitk::Image::Pointer newSession); /** * @brief Removes all sessions */ void RemoveAllInterpolationSessions(); /** * @brief Reinitializes the interpolation using the provided contour data * @param contours a mitk::Surface which contains the contours as polys in the vtkPolyData */ void ReinitializeInterpolation(mitk::Surface::Pointer contours); 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(); unsigned int GetNumberOfInterpolationSessions(); protected: SurfaceInterpolationController(); ~SurfaceInterpolationController(); template void GetImageBase(itk::Image* input, itk::ImageBase<3>::Pointer& result); private: void ReinitializeInterpolation(); void OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event); void AddToInterpolationPipeline(ContourPositionInformation contourInfo ); ReduceContourSetFilter::Pointer m_ReduceFilter; ComputeContourSetNormalsFilter::Pointer m_NormalsFilter; CreateDistanceImageFromSurfaceFilter::Pointer m_InterpolateSurfaceFilter; Surface::Pointer m_Contours; + double m_DistanceImageSpacing; + vtkSmartPointer m_PolyData; mitk::DataStorage::Pointer m_DataStorage; ContourListMap m_ListOfInterpolationSessions; mitk::Surface::Pointer m_InterpolationResult; unsigned int m_CurrentNumberOfReducedContours; mitk::Image* m_SelectedSegmentation; std::map m_SegmentationObserverTags; unsigned int m_CurrentTimeStep; }; } #endif