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