Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.