1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkEuclideanClusterExtraction.cxx
5 
6   Copyright (c) Kitware, Inc.
7   All rights reserved.
8   See LICENSE file for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkEuclideanClusterExtraction.h"
16 
17 #include "vtkPointSet.h"
18 #include "vtkIdList.h"
19 #include "vtkInformation.h"
20 #include "vtkInformationVector.h"
21 #include "vtkMath.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkPointData.h"
24 #include "vtkPoints.h"
25 #include "vtkFloatArray.h"
26 #include "vtkAbstractPointLocator.h"
27 #include "vtkStaticPointLocator.h"
28 #include "vtkIdTypeArray.h"
29 
30 vtkStandardNewMacro(vtkEuclideanClusterExtraction);
31 vtkCxxSetObjectMacro(vtkEuclideanClusterExtraction,Locator,vtkAbstractPointLocator);
32 
33 //----------------------------------------------------------------------------
34 // Construct with default extraction mode to extract largest cluster.
vtkEuclideanClusterExtraction()35 vtkEuclideanClusterExtraction::vtkEuclideanClusterExtraction()
36 {
37   this->ClusterSizes = vtkIdTypeArray::New();
38   this->ExtractionMode = VTK_EXTRACT_LARGEST_CLUSTER;
39   this->ColorClusters = false;
40 
41   this->ScalarConnectivity = false;
42   this->ScalarRange[0] = 0.0;
43   this->ScalarRange[1] = 1.0;
44 
45   this->ClosestPoint[0] = this->ClosestPoint[1] = this->ClosestPoint[2] = 0.0;
46 
47   this->Locator = vtkStaticPointLocator::New();
48 
49   this->NeighborScalars = vtkFloatArray::New();
50   this->NeighborScalars->Allocate(64);
51 
52   this->NeighborPointIds = vtkIdList::New();
53   this->NeighborPointIds->Allocate(64);
54 
55   this->Seeds = vtkIdList::New();
56   this->SpecifiedClusterIds = vtkIdList::New();
57 
58   this->NewScalars = nullptr;
59 
60 }
61 
62 //----------------------------------------------------------------------------
~vtkEuclideanClusterExtraction()63 vtkEuclideanClusterExtraction::~vtkEuclideanClusterExtraction()
64 {
65   this->SetLocator(nullptr);
66   this->ClusterSizes->Delete();
67   this->NeighborScalars->Delete();
68   this->NeighborPointIds->Delete();
69   this->Seeds->Delete();
70   this->SpecifiedClusterIds->Delete();
71 }
72 
73 //----------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)74 int vtkEuclideanClusterExtraction::RequestData(
75   vtkInformation *vtkNotUsed(request),
76   vtkInformationVector **inputVector,
77   vtkInformationVector *outputVector)
78 {
79   // get the info objects
80   vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
81   vtkInformation *outInfo = outputVector->GetInformationObject(0);
82 
83   // get the input and output
84   vtkPointSet *input = vtkPointSet::SafeDownCast(
85     inInfo->Get(vtkDataObject::DATA_OBJECT()));
86   vtkPolyData *output = vtkPolyData::SafeDownCast(
87     outInfo->Get(vtkDataObject::DATA_OBJECT()));
88 
89   vtkIdType numPts, i, ptId;
90   vtkPoints *newPts;
91   int maxPointsInCluster, clusterId, largestClusterId=0;
92   vtkPointData *pd=input->GetPointData(), *outputPD=output->GetPointData();
93 
94   vtkDebugMacro(<<"Executing point clustering filter.");
95 
96   //  Check input/allocate storage
97   //
98   if ( (numPts=input->GetNumberOfPoints()) < 1 )
99   {
100     vtkDebugMacro(<<"No data to cluster!");
101     return 1;
102   }
103   vtkPoints *inPts = input->GetPoints();
104 
105   // Need to build a locator
106   if ( !this->Locator )
107   {
108     vtkErrorMacro(<<"Point locator required\n");
109     return 0;
110   }
111   this->Locator->SetDataSet(input);
112   this->Locator->BuildLocator();
113 
114   // See whether to consider scalar connectivity
115   //
116   this->InScalars = input->GetPointData()->GetScalars();
117   if ( !this->ScalarConnectivity )
118   {
119     this->InScalars = nullptr;
120   }
121   else
122   {
123     this->NeighborScalars->SetNumberOfComponents(this->InScalars->GetNumberOfComponents());
124     if ( this->ScalarRange[1] < this->ScalarRange[0] )
125     {
126       this->ScalarRange[1] = this->ScalarRange[0];
127     }
128   }
129 
130   // Initialize.  Keep track of the points visited.
131   //
132   this->Visited = new char [numPts];
133   std::fill_n(this->Visited, numPts, static_cast<char>(0));
134 
135   this->ClusterSizes->Reset();
136   this->PointMap = new vtkIdType[numPts];
137   std::fill_n(this->PointMap, numPts, static_cast<vtkIdType>(-1));
138 
139   this->NewScalars = vtkIdTypeArray::New();
140   this->NewScalars->SetName("ClusterId");
141   this->NewScalars->SetNumberOfTuples(numPts);
142 
143   newPts = vtkPoints::New();
144   newPts->SetDataType(input->GetPoints()->GetDataType());
145   newPts->Allocate(numPts);
146 
147   // Traverse all points marking those visited.  Each new search
148   // starts a new connected cluster. Connected clusters grow
149   // using a connected wave propagation.
150   //
151   this->Wave = vtkIdList::New();
152   this->Wave->Allocate(numPts/4+1,numPts);
153   this->Wave2 = vtkIdList::New();
154   this->Wave2->Allocate(numPts/4+1,numPts);
155 
156   this->PointNumber = 0;
157   this->ClusterNumber = 0;
158   maxPointsInCluster = 0;
159 
160   this->PointIds = vtkIdList::New();
161   this->PointIds->Allocate(8, VTK_CELL_SIZE);
162 
163   if ( this->ExtractionMode != VTK_EXTRACT_POINT_SEEDED_CLUSTERS &&
164   this->ExtractionMode != VTK_EXTRACT_CLOSEST_POINT_CLUSTER )
165   { //visit all points assigning cluster number
166     for (ptId=0; ptId < numPts; ptId++)
167     {
168       if ( ptId && !(ptId % 10000) )
169       {
170         this->UpdateProgress (0.1 + 0.8*ptId/numPts);
171       }
172 
173       if ( ! this->Visited[ptId] )
174       {
175         this->NumPointsInCluster = 0;
176         this->InsertIntoWave(this->Wave,ptId);
177         this->TraverseAndMark (inPts);
178 
179         if ( this->NumPointsInCluster > maxPointsInCluster )
180         {
181           maxPointsInCluster = this->NumPointsInCluster;
182           largestClusterId = this->ClusterNumber;
183         }
184 
185         if ( this->NumPointsInCluster > 0 )
186         {
187           this->ClusterSizes->InsertValue(this->ClusterNumber++,
188                                           this->NumPointsInCluster);
189         }
190         this->Wave->Reset();
191         this->Wave2->Reset();
192       }
193     }
194   }
195   else // clusters have been seeded, everything considered in same cluster
196   {
197     this->NumPointsInCluster = 0;
198 
199     if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_CLUSTERS )
200     {
201       this->NumPointsInCluster = 0;
202       for (i=0; i < this->Seeds->GetNumberOfIds(); i++)
203       {
204         ptId = this->Seeds->GetId(i);
205         if ( ptId >= 0 )
206         {
207           this->InsertIntoWave(this->Wave,ptId);
208         }
209       }
210     }
211     else if ( this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_CLUSTER )
212     {//loop over points, find closest one
213       ptId = this->Locator->FindClosestPoint(this->ClosestPoint);
214       this->InsertIntoWave(this->Wave,ptId);
215     }
216     this->UpdateProgress (0.5);
217 
218     //mark all seeded clusters
219     this->TraverseAndMark (inPts);
220     this->ClusterSizes->InsertValue(this->ClusterNumber,this->NumPointsInCluster);
221     this->UpdateProgress (0.9);
222   }
223 
224   vtkDebugMacro (<<"Extracted " << this->ClusterNumber << " cluster(s)");
225   this->Wave->Delete();
226   this->Wave2->Delete();
227   delete [] this->Visited;
228 
229   // Now that points have been marked, traverse the PointMap pulling
230   // everything that has been visited and is selected for output.
231   outputPD->CopyAllocate(pd);
232   if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_CLUSTERS ||
233   this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_CLUSTER ||
234   this->ExtractionMode == VTK_EXTRACT_ALL_CLUSTERS)
235   { // extract any point that's been visited
236     for (ptId=0; ptId < numPts; ptId++)
237     {
238       if ( this->PointMap[ptId] >= 0 )
239       {
240         newPts->InsertPoint(this->PointMap[ptId],inPts->GetPoint(ptId));
241         outputPD->CopyData(pd,ptId,this->PointMap[ptId]);
242       }
243     }
244   }
245   else if ( this->ExtractionMode == VTK_EXTRACT_SPECIFIED_CLUSTERS )
246   {
247     bool inCluster;
248     for (ptId=0; ptId < numPts; ptId++)
249     {
250       if ( this->PointMap[ptId] >= 0 )
251       {
252         clusterId = this->NewScalars->GetValue(this->PointMap[ptId]);
253         for (inCluster=false,i=0; i<this->SpecifiedClusterIds->GetNumberOfIds(); i++)
254         {
255           if ( clusterId == this->SpecifiedClusterIds->GetId(i) )
256           {
257             inCluster = true;
258             break;
259           }
260         }
261         if ( inCluster )
262         {
263           newPts->InsertPoint(this->PointMap[ptId],inPts->GetPoint(ptId));
264           outputPD->CopyData(pd,ptId,this->PointMap[ptId]);
265         }
266       }
267     }
268   }
269   else //extract largest cluster
270   {
271     for (ptId=0; ptId < numPts; ptId++)
272     {
273       if ( this->PointMap[ptId] >= 0 )
274       {
275         clusterId = this->NewScalars->GetValue(this->PointMap[ptId]);
276         if ( clusterId == largestClusterId )
277         {
278           newPts->InsertPoint(this->PointMap[ptId],inPts->GetPoint(ptId));
279           outputPD->CopyData(pd,ptId,this->PointMap[ptId]);
280         }
281       }
282     }
283   }
284 
285   // if coloring clusters; send down new scalar data
286   if ( this->ColorClusters )
287   {
288     int idx = outputPD->AddArray(this->NewScalars);
289     outputPD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS);
290   }
291   this->NewScalars->Delete();
292 
293   newPts->Squeeze();
294   output->SetPoints(newPts);
295 
296   delete [] this->PointMap;
297   this->PointIds->Delete();
298 
299   // print out some debugging information
300   int num = this->GetNumberOfExtractedClusters();
301   int count = 0;
302 
303   for (int ii = 0; ii < num; ii++)
304   {
305     count += this->ClusterSizes->GetValue (ii);
306   }
307   vtkDebugMacro (<< "Total # of points accounted for: " << count);
308   vtkDebugMacro (<< "Extracted " << newPts->GetNumberOfPoints() << " points");
309   newPts->Delete();
310 
311   return 1;
312 }
313 
314 
315 //----------------------------------------------------------------------------
316 // Insert point into connected wave. Check to make sure it satisfies connectivity
317 // criterion (if enabled).
318 void vtkEuclideanClusterExtraction::
InsertIntoWave(vtkIdList * wave,vtkIdType ptId)319 InsertIntoWave(vtkIdList *wave, vtkIdType ptId)
320 {
321   this->Visited[ptId] = 1;
322   if ( this->InScalars ) //is scalar connectivity enabled?
323   {
324     double s = this->InScalars->GetTuple1(ptId);
325     if ( s >= this->ScalarRange[0] && s <= this->ScalarRange[1] )
326     {
327       wave->InsertNextId(ptId);
328     }
329   }
330   else
331   {
332     wave->InsertNextId(ptId);
333   }
334 }
335 
336 
337 //----------------------------------------------------------------------------
338 // Update current point information including updating cluster number.  Note:
339 // traversal occurs across proximally located points, possibly limited by
340 // scalar connectivty.
341 //
TraverseAndMark(vtkPoints * inPts)342 void vtkEuclideanClusterExtraction::TraverseAndMark (vtkPoints *inPts)
343 {
344   vtkIdType i, j, numPts, numIds, ptId;
345   vtkIdList *tmpWave;
346   double x[3];
347 
348   while ( (numIds=this->Wave->GetNumberOfIds()) > 0 )
349   {
350     for ( i=0; i < numIds; i++ ) //for all points in this wave
351     {
352       ptId = this->Wave->GetId(i);
353       this->PointMap[ptId] = this->PointNumber++;
354       this->NewScalars->SetValue(this->PointMap[ptId],this->ClusterNumber);
355       this->NumPointsInCluster++;
356 
357       inPts->GetPoint(ptId,x);
358       this->Locator->FindPointsWithinRadius(this->Radius,x,this->NeighborPointIds);
359 
360       numPts = this->NeighborPointIds->GetNumberOfIds();
361       for (j=0; j < numPts; ++j)
362       {
363         ptId = this->NeighborPointIds->GetId(j);
364         if ( ! this->Visited[ptId] )
365         {
366           this->InsertIntoWave(this->Wave2,ptId);
367         }//if point not yet visited
368       }//for all neighbors
369     }//for all cells in this connected wave
370 
371     tmpWave = this->Wave;
372     this->Wave = this->Wave2;
373     this->Wave2 = tmpWave;
374     tmpWave->Reset();
375   } //while wave is not empty
376 }
377 
378 //----------------------------------------------------------------------------
379 // Obtain the number of connected clusters.
GetNumberOfExtractedClusters()380 int vtkEuclideanClusterExtraction::GetNumberOfExtractedClusters()
381 {
382   return this->ClusterSizes->GetMaxId() + 1;
383 }
384 
385 //----------------------------------------------------------------------------
386 // Initialize list of point ids used to seed clusters.
InitializeSeedList()387 void vtkEuclideanClusterExtraction::InitializeSeedList()
388 {
389   this->Modified();
390   this->Seeds->Reset();
391 }
392 
393 //----------------------------------------------------------------------------
394 // Add a seed id. Note: ids are 0-offset.
AddSeed(vtkIdType id)395 void vtkEuclideanClusterExtraction::AddSeed(vtkIdType id)
396 {
397   this->Modified();
398   this->Seeds->InsertNextId(id);
399 }
400 
401 //----------------------------------------------------------------------------
402 // Delete a seed id. Note: ids are 0-offset.
DeleteSeed(vtkIdType id)403 void vtkEuclideanClusterExtraction::DeleteSeed(vtkIdType id)
404 {
405   this->Modified();
406   this->Seeds->DeleteId(id);
407 }
408 
409 //----------------------------------------------------------------------------
410 // Initialize list of cluster ids to extract.
InitializeSpecifiedClusterList()411 void vtkEuclideanClusterExtraction::InitializeSpecifiedClusterList()
412 {
413   this->Modified();
414   this->SpecifiedClusterIds->Reset();
415 }
416 
417 //----------------------------------------------------------------------------
418 // Add a cluster id to extract. Note: ids are 0-offset.
AddSpecifiedCluster(int id)419 void vtkEuclideanClusterExtraction::AddSpecifiedCluster(int id)
420 {
421   this->Modified();
422   this->SpecifiedClusterIds->InsertNextId(id);
423 }
424 
425 //----------------------------------------------------------------------------
426 // Delete a cluster id to extract. Note: ids are 0-offset.
DeleteSpecifiedCluster(int id)427 void vtkEuclideanClusterExtraction::DeleteSpecifiedCluster(int id)
428 {
429   this->Modified();
430   this->SpecifiedClusterIds->DeleteId(id);
431 }
432 
433 //----------------------------------------------------------------------------
434 int vtkEuclideanClusterExtraction::
FillInputPortInformation(int,vtkInformation * info)435 FillInputPortInformation(int, vtkInformation *info)
436 {
437   info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet");
438   return 1;
439 }
440 
441 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)442 void vtkEuclideanClusterExtraction::PrintSelf(ostream& os, vtkIndent indent)
443 {
444   this->Superclass::PrintSelf(os,indent);
445 
446   os << indent << "Extraction Mode: ";
447   os << this->GetExtractionModeAsString() << "\n";
448 
449   os << indent << "Closest Point: (" << this->ClosestPoint[0] << ", "
450      << this->ClosestPoint[1] << ", " << this->ClosestPoint[2] << ")\n";
451 
452   os << indent << "Color Clusters: " << (this->ColorClusters ? "On\n" : "Off\n");
453 
454   os << indent << "Scalar Connectivity: "
455      << (this->ScalarConnectivity ? "On\n" : "Off\n");
456 
457   double *range = this->GetScalarRange();
458   os << indent << "Scalar Range: (" << range[0] << ", " << range[1] << ")\n";
459 
460   os << indent << "Locator: " << this->Locator << "\n";
461 }
462