Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkReduceContourSetFilter.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 (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
14 
16 {
17  m_MaxSegmentLenght = 0;
18  m_StepSize = 10;
19  m_Tolerance = -1;
20  m_ReductionType = DOUGLAS_PEUCKER;
21  m_MaxSpacing = -1;
22  m_MinSpacing = -1;
23  this->m_UseProgressBar = false;
24  this->m_ProgressStepSize = 1;
25  m_NumberOfPointsAfterReduction = 0;
26 
28  this->SetNthOutput(0, output.GetPointer());
29 }
30 
32 {
33 }
34 
35 void mitk::ReduceContourSetFilter::SetInput(unsigned int idx, const mitk::Surface *surface)
36 {
37  this->SetNthInput(idx, const_cast<mitk::Surface *>(surface));
38  this->Modified();
39 }
40 
42 {
43  this->SetInput(0, surface);
44 }
45 
47 {
48  unsigned int numberOfInputs = this->GetNumberOfIndexedInputs();
49  unsigned int numberOfOutputs(0);
50 
51  vtkSmartPointer<vtkPolyData> newPolyData;
52  vtkSmartPointer<vtkCellArray> newPolygons;
53  vtkSmartPointer<vtkPoints> newPoints;
54 
55  // For the purpose of evaluation
56  // unsigned int numberOfPointsBefore (0);
57  m_NumberOfPointsAfterReduction = 0;
58 
59  for (unsigned int i = 0; i < numberOfInputs; i++)
60  {
61  auto *currentSurface = this->GetInput(i);
62  vtkSmartPointer<vtkPolyData> polyData = currentSurface->GetVtkPolyData();
63 
64  newPolyData = vtkSmartPointer<vtkPolyData>::New();
65  newPolygons = vtkSmartPointer<vtkCellArray>::New();
66  newPoints = vtkSmartPointer<vtkPoints>::New();
67 
68  vtkSmartPointer<vtkCellArray> existingPolys = polyData->GetPolys();
69 
70  vtkSmartPointer<vtkPoints> existingPoints = polyData->GetPoints();
71 
72  existingPolys->InitTraversal();
73 
74  vtkIdType *cell(nullptr);
75  vtkIdType cellSize(0);
76 
77  for (existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);)
78  {
79  bool incorporatePolygon =
80  this->CheckForIntersection(cell, cellSize, existingPoints, /*numberOfIntersections, intersectionPoints, */ i);
81  if (!incorporatePolygon)
82  continue;
83 
84  vtkSmartPointer<vtkPolygon> newPolygon = vtkSmartPointer<vtkPolygon>::New();
85 
86  if (m_ReductionType == NTH_POINT)
87  {
88  this->ReduceNumberOfPointsByNthPoint(cellSize, cell, existingPoints, newPolygon, newPoints);
89  if (newPolygon->GetPointIds()->GetNumberOfIds() != 0)
90  {
91  newPolygons->InsertNextCell(newPolygon);
92  }
93  }
94  else if (m_ReductionType == DOUGLAS_PEUCKER)
95  {
96  this->ReduceNumberOfPointsByDouglasPeucker(cellSize, cell, existingPoints, newPolygon, newPoints);
97  if (newPolygon->GetPointIds()->GetNumberOfIds() > 3)
98  {
99  newPolygons->InsertNextCell(newPolygon);
100  }
101  }
102 
103  // Again for evaluation
104  // numberOfPointsBefore += cellSize;
105  m_NumberOfPointsAfterReduction += newPolygon->GetPointIds()->GetNumberOfIds();
106  }
107 
108  if (newPolygons->GetNumberOfCells() != 0)
109  {
110  newPolyData->SetPolys(newPolygons);
111  newPolyData->SetPoints(newPoints);
112  newPolyData->BuildLinks();
113 
114  this->SetNumberOfIndexedOutputs(numberOfOutputs + 1);
116  this->SetNthOutput(numberOfOutputs, surface.GetPointer());
117  surface->SetVtkPolyData(newPolyData);
118  numberOfOutputs++;
119  }
120  }
121 
122  // MITK_INFO<<"Points before: "<<numberOfPointsBefore<<" ##### Points after: "<<numberOfPointsAfter;
123  this->SetNumberOfIndexedOutputs(numberOfOutputs);
124 
125  if (numberOfOutputs == 0)
126  {
128  tmp_output->SetVtkPolyData(vtkPolyData::New());
129  this->SetNthOutput(0, tmp_output.GetPointer());
130  }
131  // Setting progressbar
132  if (this->m_UseProgressBar)
133  mitk::ProgressBar::GetInstance()->Progress(this->m_ProgressStepSize);
134 }
135 
136 void mitk::ReduceContourSetFilter::ReduceNumberOfPointsByNthPoint(
137  vtkIdType cellSize, vtkIdType *cell, vtkPoints *points, vtkPolygon *reducedPolygon, vtkPoints *reducedPoints)
138 {
139  unsigned int newNumberOfPoints(0);
140  unsigned int mod = cellSize % m_StepSize;
141 
142  if (mod == 0)
143  {
144  newNumberOfPoints = cellSize / m_StepSize;
145  }
146  else
147  {
148  newNumberOfPoints = ((cellSize - mod) / m_StepSize) + 1;
149  }
150 
151  if (newNumberOfPoints <= 3)
152  {
153  return;
154  }
155  reducedPolygon->GetPointIds()->SetNumberOfIds(newNumberOfPoints);
156  reducedPolygon->GetPoints()->SetNumberOfPoints(newNumberOfPoints);
157 
158  for (vtkIdType i = 0; i < cellSize; i++)
159  {
160  if (i % m_StepSize == 0)
161  {
162  double point[3];
163  points->GetPoint(cell[i], point);
164  vtkIdType id = reducedPoints->InsertNextPoint(point);
165  reducedPolygon->GetPointIds()->SetId(i / m_StepSize, id);
166  }
167  }
168  vtkIdType id = cell[0];
169  double point[3];
170  points->GetPoint(id, point);
171  id = reducedPoints->InsertNextPoint(point);
172  reducedPolygon->GetPointIds()->SetId(newNumberOfPoints - 1, id);
173 }
174 
175 void mitk::ReduceContourSetFilter::ReduceNumberOfPointsByDouglasPeucker(
176  vtkIdType cellSize, vtkIdType *cell, vtkPoints *points, vtkPolygon *reducedPolygon, vtkPoints *reducedPoints)
177 {
178  // If the cell is too small to obtain a reduced polygon with the given stepsize return
179  if (cellSize <= static_cast<vtkIdType>(m_StepSize * 3))
180  return;
181 
182  /*
183  What we do now is (see the Douglas Peucker Algorithm):
184 
185  1. Divide the current contour in two line segments (start - middle; middle - end), put them into the stack
186 
187  2. Fetch first line segment and create the following vectors:
188 
189  - v1 = (start;end)
190  - v2 = (start;currentPoint) -> for each point of the current line segment!
191 
192  3. Calculate the distance from the currentPoint to v1:
193 
194  a. Determine the length of the orthogonal projection of v2 to v1 by:
195 
196  l = v2 * (normalized v1)
197 
198  b. There a three possibilities for the distance then:
199 
200  d = sqrt(lenght(v2)^2 - l^2) if l > 0 and l < length(v1)
201  d = lenght(v2-v1) if l > 0 and l > lenght(v1)
202  d = length(v2) if l < 0 because v2 is then pointing in a different direction than v1
203 
204  4. Memorize the point with the biggest distance and create two new line segments with it at the end of the iteration
205  and put it into the stack
206 
207  5. If the distance value D <= m_Tolerance, then add the start and end index and the corresponding points to the
208  reduced ones
209  */
210 
211  // First of all set tolerance if none is specified
212  if (m_Tolerance < 0)
213  {
214  if (m_MaxSpacing > 0)
215  {
216  m_Tolerance = m_MinSpacing;
217  }
218  else
219  {
220  m_Tolerance = 1.5;
221  }
222  }
223 
224  std::stack<LineSegment> lineSegments;
225 
226  // 1. Divide in line segments
227 
228  LineSegment ls2;
229  ls2.StartIndex = cell[cellSize / 2];
230  ls2.EndIndex = cell[cellSize - 1];
231  lineSegments.push(ls2);
232 
233  LineSegment ls1;
234  ls1.StartIndex = cell[0];
235  ls1.EndIndex = cell[cellSize / 2];
236  lineSegments.push(ls1);
237 
238  LineSegment currentSegment;
239  double v1[3];
240  double v2[3];
241  double tempV[3];
242  double lenghtV1;
243 
244  double currentMaxDistance(0);
245  vtkIdType currentMaxDistanceIndex(0);
246 
247  double l;
248  double d;
249 
250  vtkIdType pointId(0);
251  // Add the start index to the reduced points. From now on just the end indices will be added
252  pointId = reducedPoints->InsertNextPoint(points->GetPoint(cell[0]));
253  reducedPolygon->GetPointIds()->InsertNextId(pointId);
254 
255  while (!lineSegments.empty())
256  {
257  currentSegment = lineSegments.top();
258  lineSegments.pop();
259 
260  // 2. Create vectors
261 
262  points->GetPoint(currentSegment.EndIndex, tempV);
263  points->GetPoint(currentSegment.StartIndex, v1);
264 
265  v1[0] = tempV[0] - v1[0];
266  v1[1] = tempV[1] - v1[1];
267  v1[2] = tempV[2] - v1[2];
268 
269  lenghtV1 = vtkMath::Norm(v1);
270 
271  vtkMath::Normalize(v1);
272  int range = currentSegment.EndIndex - currentSegment.StartIndex;
273  for (int i = 1; i < abs(range); ++i)
274  {
275  points->GetPoint(currentSegment.StartIndex + i, tempV);
276  points->GetPoint(currentSegment.StartIndex, v2);
277 
278  v2[0] = tempV[0] - v2[0];
279  v2[1] = tempV[1] - v2[1];
280  v2[2] = tempV[2] - v2[2];
281 
282  // 3. Calculate the distance
283 
284  l = vtkMath::Dot(v2, v1);
285 
286  d = vtkMath::Norm(v2);
287 
288  if (l > 0 && l < lenghtV1)
289  {
290  d = sqrt((d * d - l * l));
291  }
292  else if (l > 0 && l > lenghtV1)
293  {
294  tempV[0] = lenghtV1 * v1[0] - v2[0];
295  tempV[1] = lenghtV1 * v1[1] - v2[1];
296  tempV[2] = lenghtV1 * v1[2] - v2[2];
297  d = vtkMath::Norm(tempV);
298  }
299 
300  // 4. Memorize maximum distance
301  if (d > currentMaxDistance)
302  {
303  currentMaxDistance = d;
304  currentMaxDistanceIndex = currentSegment.StartIndex + i;
305  }
306  }
307 
308  // 4. & 5.
309  if (currentMaxDistance <= m_Tolerance)
310  {
311  // double temp[3];
312  int segmentLenght = currentSegment.EndIndex - currentSegment.StartIndex;
313  if (segmentLenght > (int)m_MaxSegmentLenght)
314  {
315  m_MaxSegmentLenght = (unsigned int)segmentLenght;
316  }
317 
318  // MITK_INFO<<"Lenght: "<<abs(segmentLenght);
319  if (abs(segmentLenght) > 25)
320  {
321  unsigned int newLenght(segmentLenght);
322  while (newLenght > 25)
323  {
324  newLenght = newLenght * 0.5;
325  }
326  unsigned int divisions = abs(segmentLenght) / newLenght;
327  // MITK_INFO<<"Divisions: "<<divisions;
328 
329  for (unsigned int i = 1; i <= divisions; ++i)
330  {
331  // MITK_INFO<<"Inserting MIDDLE: "<<(currentSegment.StartIndex + newLenght*i);
332  pointId = reducedPoints->InsertNextPoint(points->GetPoint(currentSegment.StartIndex + newLenght * i));
333  reducedPolygon->GetPointIds()->InsertNextId(pointId);
334  }
335  }
336  // MITK_INFO<<"Inserting END: "<<currentSegment.EndIndex;
337  pointId = reducedPoints->InsertNextPoint(points->GetPoint(currentSegment.EndIndex));
338  reducedPolygon->GetPointIds()->InsertNextId(pointId);
339  }
340  else
341  {
342  ls2.StartIndex = currentMaxDistanceIndex;
343  ls2.EndIndex = currentSegment.EndIndex;
344  lineSegments.push(ls2);
345 
346  ls1.StartIndex = currentSegment.StartIndex;
347  ls1.EndIndex = currentMaxDistanceIndex;
348  lineSegments.push(ls1);
349  }
350  currentMaxDistance = 0;
351  }
352 }
353 
354 bool mitk::ReduceContourSetFilter::CheckForIntersection(
355  vtkIdType *currentCell,
356  vtkIdType currentCellSize,
357  vtkPoints *currentPoints,
358  /* vtkIdType numberOfIntersections, vtkIdType* intersectionPoints,*/ unsigned int currentInputIndex)
359 {
360  /*
361  If we check the current cell for intersections then we have to consider three possibilies:
362  1. There is another cell among all the other input surfaces which intersects the current polygon:
363  - That means we have to save the intersection points because these points should not be eliminated
364  2. There current polygon exists just because of an intersection of another polygon with the current plane defined by
365  the current polygon
366  - That means the current polygon should not be incorporated and all of its points should be eliminated
367  3. There is no intersection
368  - That mean we can just reduce the current polygons points without considering any intersections
369  */
370 
371  for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++)
372  {
373  // Don't check for intersection with the polygon itself
374  if (i == currentInputIndex)
375  continue;
376 
377  // Get the next polydata to check for intersection
378  vtkSmartPointer<vtkPolyData> poly = this->GetInput(i)->GetVtkPolyData();
379  vtkSmartPointer<vtkCellArray> polygonArray = poly->GetPolys();
380  polygonArray->InitTraversal();
381  vtkIdType anotherInputPolygonSize(0);
382  vtkIdType *anotherInputPolygonIDs(nullptr);
383 
384  /*
385  The procedure is:
386  - Create the equation of the plane, defined by the points of next input
387  - Calculate the distance of each point of the current polygon to the plane
388  - If the maximum distance is not bigger than 1.5 of the maximum spacing AND the minimal distance is not bigger
389  than 0.5 of the minimum spacing then the current contour is an intersection contour
390  */
391 
392  for (polygonArray->InitTraversal(); polygonArray->GetNextCell(anotherInputPolygonSize, anotherInputPolygonIDs);)
393  {
394  // Choosing three plane points to calculate the plane vectors
395  double p1[3];
396  double p2[3];
397  double p3[3];
398 
399  // The plane vectors
400  double v1[3];
401  double v2[3] = {0};
402  // The plane normal
403  double normal[3];
404 
405  // Create first Vector
406  poly->GetPoint(anotherInputPolygonIDs[0], p1);
407  poly->GetPoint(anotherInputPolygonIDs[1], p2);
408 
409  v1[0] = p2[0] - p1[0];
410  v1[1] = p2[1] - p1[1];
411  v1[2] = p2[2] - p1[2];
412 
413  // Find 3rd point for 2nd vector (The angle between the two plane vectors should be bigger than 30 degrees)
414 
415  double maxDistance(0);
416  double minDistance(10000);
417 
418  for (vtkIdType j = 2; j < anotherInputPolygonSize; j++)
419  {
420  poly->GetPoint(anotherInputPolygonIDs[j], p3);
421 
422  v2[0] = p3[0] - p1[0];
423  v2[1] = p3[1] - p1[1];
424  v2[2] = p3[2] - p1[2];
425 
426  // Calculate the angle between the two vector for the current point
427  double dotV1V2 = vtkMath::Dot(v1, v2);
428  double absV1 = sqrt(vtkMath::Dot(v1, v1));
429  double absV2 = sqrt(vtkMath::Dot(v2, v2));
430  double cosV1V2 = dotV1V2 / (absV1 * absV2);
431 
432  double arccos = acos(cosV1V2);
433  double degree = vtkMath::DegreesFromRadians(arccos);
434 
435  // If angle is bigger than 30 degrees break
436  if (degree > 30)
437  break;
438 
439  } // for (to find 3rd point)
440 
441  // Calculate normal of the plane by taking the cross product of the two vectors
442  vtkMath::Cross(v1, v2, normal);
443  vtkMath::Normalize(normal);
444 
445  // Determine position of the plane
446  double lambda = vtkMath::Dot(normal, p1);
447 
448  /*
449  Calculate the distance to the plane for each point of the current polygon
450  If the distance is zero then save the currentPoint as intersection point
451  */
452  for (vtkIdType k = 0; k < currentCellSize; k++)
453  {
454  double currentPoint[3];
455  currentPoints->GetPoint(currentCell[k], currentPoint);
456 
457  double tempPoint[3];
458  tempPoint[0] = normal[0] * currentPoint[0];
459  tempPoint[1] = normal[1] * currentPoint[1];
460  tempPoint[2] = normal[2] * currentPoint[2];
461 
462  double temp = tempPoint[0] + tempPoint[1] + tempPoint[2] - lambda;
463  double distance = fabs(temp);
464 
465  if (distance > maxDistance)
466  {
467  maxDistance = distance;
468  }
469  if (distance < minDistance)
470  {
471  minDistance = distance;
472  }
473  } // for (to calculate distance and intersections with currentPolygon)
474 
475  if (maxDistance < 1.5 * m_MaxSpacing && minDistance < 0.5 * m_MinSpacing)
476  {
477  return false;
478  }
479 
480  // Because we are considering the plane defined by the acual input polygon only one iteration is sufficient
481  // We do not need to consider each cell of the plane
482  break;
483  } // for (to traverse through all cells of actualInputPolyData)
484 
485  } // for (to iterate through all inputs)
486 
487  return true;
488 }
489 
491 {
492  Superclass::GenerateOutputInformation();
493 }
494 
496 {
497  for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++)
498  {
499  this->PopBackInput();
500  }
501  this->SetNumberOfIndexedInputs(0);
502  this->SetNumberOfIndexedOutputs(0);
503 
504  // BUG XXXXX Fix
506  this->SetNthOutput(0, output.GetPointer());
507 
508  m_NumberOfPointsAfterReduction = 0;
509 }
510 
512 {
513  this->m_UseProgressBar = status;
514 }
515 
517 {
518  this->m_ProgressStepSize = stepSize;
519 }
void Progress(unsigned int steps=1)
Sets the current amount of progress to current progress + steps.
Class for storing surfaces (vtkPolyData).
Definition: mitkSurface.h:28
float k(1.0)
virtual vtkPolyData * GetVtkPolyData(unsigned int t=0) const
void SetUseProgressBar(bool)
Set whether the mitkProgressBar should be used.
void SetInput(const mitk::Surface *surface) override
virtual const mitk::Surface * GetInput()
void SetProgressStepSize(unsigned int stepSize)
Set the stepsize which the progress bar should proceed.
static ProgressBar * GetInstance()
static method to get the GUI dependent ProgressBar-instance so the methods for steps to do and progre...
void Normalize(itk::Image< TPixel, VImageDimension > *itkImage, mitk::Image::Pointer im2, mitk::Image::Pointer mask1, std::string output)
Definition: CLBrainMask.cpp:36
static Pointer New()