Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkACVD.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include "mitkACVD.h"
18 #include <mitkExceptionMacro.h>
19 #include <vtkIdList.h>
20 #include <vtkIntArray.h>
21 #include <vtkIsotropicDiscreteRemeshing.h>
22 #include <vtkMultiThreader.h>
23 #include <vtkPolyData.h>
24 #include <vtkPolyDataNormals.h>
25 #include <vtkSmartPointer.h>
26 #include <vtkSurface.h>
27 
28 struct ClustersQuadrics
29 {
30  explicit ClustersQuadrics(int size) : Elements(new double *[size]), Size(size)
31  {
32  for (int i = 0; i < size; ++i)
33  {
34  Elements[i] = new double[9];
35 
36  for (int j = 0; j < 9; ++j)
37  Elements[i][j] = 0.0;
38  }
39  }
40 
41  ~ClustersQuadrics()
42  {
43  for (int i = 0; i < Size; ++i)
44  delete[] Elements[i];
45 
46  delete Elements;
47  }
48 
49  double **Elements;
50  int Size;
51 
52 private:
53  ClustersQuadrics(const ClustersQuadrics &);
54  ClustersQuadrics &operator=(const ClustersQuadrics &);
55 };
56 
57 static void ValidateSurface(mitk::Surface::ConstPointer surface, unsigned int t)
58 {
59  if (surface.IsNull())
60  mitkThrow() << "Input surface is NULL!";
61 
62  if (t >= surface->GetSizeOfPolyDataSeries())
63  mitkThrow() << "Input surface doesn't have data at time step " << t << "!";
64 
65  vtkPolyData *polyData = const_cast<mitk::Surface *>(surface.GetPointer())->GetVtkPolyData(t);
66 
67  if (polyData == nullptr)
68  mitkThrow() << "PolyData of input surface at time step " << t << " is NULL!";
69 
70  if (polyData->GetNumberOfPolys() == 0)
71  mitkThrow() << "Input surface has no polygons at time step " << t << "!";
72 }
73 
75  unsigned int t,
76  int numVertices,
77  double gradation,
78  int subsampling,
79  double edgeSplitting,
80  int optimizationLevel,
81  bool forceManifold,
82  bool boundaryFixing)
83 {
84  ValidateSurface(surface, t);
85 
86  MITK_INFO << "Start remeshing...";
87 
88  vtkSmartPointer<vtkPolyData> surfacePolyData = vtkSmartPointer<vtkPolyData>::New();
89  surfacePolyData->DeepCopy(const_cast<Surface *>(surface.GetPointer())->GetVtkPolyData(t));
90 
91  vtkSmartPointer<vtkSurface> mesh = vtkSmartPointer<vtkSurface>::New();
92 
93  mesh->CreateFromPolyData(surfacePolyData);
94  mesh->GetCellData()->Initialize();
95  mesh->GetPointData()->Initialize();
96 
97  mesh->DisplayMeshProperties();
98 
99  if (numVertices == 0)
100  numVertices = surfacePolyData->GetNumberOfPoints();
101 
102  if (edgeSplitting != 0.0)
103  mesh->SplitLongEdges(edgeSplitting);
104 
105  vtkSmartPointer<vtkIsotropicDiscreteRemeshing> remesher = vtkSmartPointer<vtkIsotropicDiscreteRemeshing>::New();
106 
107  remesher->GetMetric()->SetGradation(gradation);
108  remesher->SetBoundaryFixing(boundaryFixing);
109  remesher->SetConsoleOutput(1);
110  remesher->SetForceManifold(forceManifold);
111  remesher->SetInput(mesh);
112  remesher->SetNumberOfClusters(numVertices);
113  remesher->SetNumberOfThreads(vtkMultiThreader::GetGlobalDefaultNumberOfThreads());
114  remesher->SetSubsamplingThreshold(subsampling);
115 
116  remesher->Remesh();
117 
118  // Optimization: Minimize distance between input surface and remeshed surface
119  if (optimizationLevel != 0)
120  {
121  ClustersQuadrics clustersQuadrics(numVertices);
122 
123  vtkSmartPointer<vtkIdList> faceList = vtkSmartPointer<vtkIdList>::New();
124  vtkSmartPointer<vtkIntArray> clustering = remesher->GetClustering();
125  vtkSmartPointer<vtkSurface> remesherInput = remesher->GetInput();
126  int clusteringType = remesher->GetClusteringType();
127  int numItems = remesher->GetNumberOfItems();
128  int numMisclassifiedItems = 0;
129 
130  for (int i = 0; i < numItems; ++i)
131  {
132  int cluster = clustering->GetValue(i);
133 
134  if (cluster >= 0 && cluster < numVertices)
135  {
136  if (clusteringType != 0)
137  {
138  remesherInput->GetVertexNeighbourFaces(i, faceList);
139  int numIds = static_cast<int>(faceList->GetNumberOfIds());
140 
141  for (int j = 0; j < numIds; ++j)
142  vtkQuadricTools::AddTriangleQuadric(
143  clustersQuadrics.Elements[cluster], remesherInput, faceList->GetId(j), false);
144  }
145  else
146  {
147  vtkQuadricTools::AddTriangleQuadric(clustersQuadrics.Elements[cluster], remesherInput, i, false);
148  }
149  }
150  else
151  {
152  ++numMisclassifiedItems;
153  }
154  }
155 
156  if (numMisclassifiedItems != 0)
157  std::cout << numMisclassifiedItems << " items with wrong cluster association" << std::endl;
158 
159  vtkSmartPointer<vtkSurface> remesherOutput = remesher->GetOutput();
160  double point[3];
161 
162  for (int i = 0; i < numVertices; ++i)
163  {
164  remesherOutput->GetPoint(i, point);
165  vtkQuadricTools::ComputeRepresentativePoint(clustersQuadrics.Elements[i], point, optimizationLevel);
166  remesherOutput->SetPointCoordinates(i, point);
167  }
168 
169  std::cout << "After quadrics post-processing:" << std::endl;
170  remesherOutput->DisplayMeshProperties();
171  }
172 
173  vtkSmartPointer<vtkPolyDataNormals> normals = vtkSmartPointer<vtkPolyDataNormals>::New();
174 
175  normals->SetInputData(remesher->GetOutput());
176  normals->AutoOrientNormalsOn();
177  normals->ComputeCellNormalsOff();
178  normals->ComputePointNormalsOn();
179  normals->ConsistencyOff();
180  normals->FlipNormalsOff();
181  normals->NonManifoldTraversalOff();
182  normals->SplittingOff();
183 
184  normals->Update();
185 
186  Surface::Pointer remeshedSurface = Surface::New();
187  remeshedSurface->SetVtkPolyData(normals->GetOutput());
188 
189  MITK_INFO << "Finished remeshing";
190 
191  return remeshedSurface;
192 }
193 
194 mitk::ACVD::RemeshFilter::RemeshFilter()
195  : m_TimeStep(0),
196  m_NumVertices(0),
197  m_Gradation(1.0),
198  m_Subsampling(10),
199  m_EdgeSplitting(0.0),
200  m_OptimizationLevel(1),
201  m_ForceManifold(false),
202  m_BoundaryFixing(false)
203 {
204  Surface::Pointer output = Surface::New();
205  this->SetNthOutput(0, output);
206 }
207 
208 mitk::ACVD::RemeshFilter::~RemeshFilter()
209 {
210 }
211 
213 {
214  Surface::Pointer output = Remesh(this->GetInput(),
215  m_TimeStep,
216  m_NumVertices,
217  m_Gradation,
218  m_Subsampling,
219  m_EdgeSplitting,
220  m_OptimizationLevel,
221  m_ForceManifold,
222  m_BoundaryFixing);
223  this->SetNthOutput(0, output);
224 }
MITKREMESHING_EXPORT Surface::Pointer Remesh(Surface::ConstPointer surface, unsigned int t, int numVertices, double gradation, int subsampling=10, double edgeSplitting=0.0, int optimizationLevel=1, bool forceManifold=false, bool boundaryFixing=false)
Remesh a surface and store the result in a new surface.
Definition: mitkACVD.cpp:74
Class for storing surfaces (vtkPolyData).
Definition: mitkSurface.h:32
#define MITK_INFO
Definition: mitkLogMacros.h:22
static void ValidateSurface(mitk::Surface::ConstPointer surface, unsigned int t)
Definition: mitkACVD.cpp:57
#define mitkThrow()
void GenerateData() override
Definition: mitkACVD.cpp:212
static Pointer New()
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.