From 39b307153ae5633a0a77a1ebc5184682b81b0d84 Mon Sep 17 00:00:00 2001 From: Will Schroeder <will.schroeder@kitware.com> Date: Mon, 19 Sep 2016 14:30:21 -0400 Subject: [PATCH] Move vtkPointCloud remote to Filters/Points Added a number of filters for processing point clouds. The files originally came from the vtkPointCloud remote module. --- Filters/Points/CMakeLists.txt | 18 +- .../TestEuclideanClusterExtraction.png.md5 | 1 + .../TestEuclideanClusterExtraction2.png.md5 | 1 + .../TestEuclideanClusterExtraction2_1.png.md5 | 1 + .../TestEuclideanClusterExtraction_1.png.md5 | 1 + .../TestEuclideanClusterExtraction_2.png.md5 | 1 + .../Data/Baseline/TestExtractPoints.png.md5 | 1 + .../Data/Baseline/TestExtractPoints_1.png.md5 | 1 + .../Baseline/TestFitImplicitFunction.png.md5 | 1 + .../TestFitImplicitFunction_1.png.md5 | 1 + .../TestHierarchicalBinningFilter.png.md5 | 1 + .../TestHierarchicalBinningFilter_1.png.md5 | 1 + .../TestHierarchicalBinningFilter_2.png.md5 | 1 + .../TestPCACurvatureEstimation.png.md5 | 1 + .../TestPCACurvatureEstimation2.png.md5 | 1 + .../TestPCACurvatureEstimation_1.png.md5 | 1 + .../Baseline/TestPCANormalEstimation.png.md5 | 1 + .../Baseline/TestPCANormalEstimation2.png.md5 | 1 + .../TestPCANormalEstimation2_1.png.md5 | 1 + .../TestPCANormalEstimation2_2.png.md5 | 1 + .../TestPCANormalEstimation_1.png.md5 | 1 + .../TestPCANormalEstimation_2.png.md5 | 1 + .../Baseline/TestRadiusOutlierRemoval.png.md5 | 1 + .../TestRadiusOutlierRemoval_1.png.md5 | 1 + .../TestRadiusOutlierRemoval_2.png.md5 | 1 + .../Baseline/TestSignedDistanceFilter.png.md5 | 1 + .../TestSignedDistanceFilter_1.png.md5 | 1 + .../TestStatisticalOutlierRemoval.png.md5 | 1 + .../TestStatisticalOutlierRemoval_1.png.md5 | 1 + .../TestStatisticalOutlierRemoval_2.png.md5 | 1 + .../Data/Baseline/TestVoxelGridFilter.png.md5 | 1 + .../Baseline/TestVoxelGridFilter_1.png.md5 | 1 + .../Baseline/TestVoxelGridFilter_2.png.md5 | 1 + .../Baseline/TestVoxelGridFilter_3.png.md5 | 1 + Filters/Points/Testing/Python/CMakeLists.txt | 18 + .../Python/TestEuclideanClusterExtraction.py | 96 ++ .../Python/TestEuclideanClusterExtraction2.py | 117 ++ .../Testing/Python/TestExtractPoints.py | 91 ++ .../Testing/Python/TestFitImplicitFunction.py | 92 ++ .../Python/TestHierarchicalBinningFilter.py | 115 ++ .../Python/TestPCACurvatureEstimation.py | 123 ++ .../Python/TestPCACurvatureEstimation2.py | 132 ++ .../Testing/Python/TestPCANormalEstimation.py | 116 ++ .../Python/TestPCANormalEstimation2.py | 115 ++ .../Python/TestRadiusOutlierRemoval.py | 120 ++ .../Python/TestSignedDistanceFilter.py | 128 ++ .../Python/TestStatisticalOutlierRemoval.py | 144 ++ .../Testing/Python/TestVoxelGridFilter.py | 112 ++ Filters/Points/vtkBoundedPointSource.cxx | 154 ++ Filters/Points/vtkBoundedPointSource.h | 98 ++ .../Points/vtkEuclideanClusterExtraction.cxx | 463 ++++++ .../Points/vtkEuclideanClusterExtraction.h | 233 +++ Filters/Points/vtkExtractHierarchicalBins.cxx | 102 ++ Filters/Points/vtkExtractHierarchicalBins.h | 102 ++ Filters/Points/vtkExtractPointCloudPiece.cxx | 137 ++ Filters/Points/vtkExtractPointCloudPiece.h | 61 + Filters/Points/vtkExtractPoints.cxx | 140 ++ Filters/Points/vtkExtractPoints.h | 101 ++ Filters/Points/vtkExtractSurface.cxx | 1423 +++++++++++++++++ Filters/Points/vtkExtractSurface.h | 158 ++ Filters/Points/vtkFitImplicitFunction.cxx | 141 ++ Filters/Points/vtkFitImplicitFunction.h | 103 ++ .../Points/vtkHierarchicalBinningFilter.cxx | 922 +++++++++++ Filters/Points/vtkHierarchicalBinningFilter.h | 189 +++ Filters/Points/vtkPCACurvatureEstimation.cxx | 244 +++ Filters/Points/vtkPCACurvatureEstimation.h | 100 ++ Filters/Points/vtkPCANormalEstimation.cxx | 359 +++++ Filters/Points/vtkPCANormalEstimation.h | 156 ++ Filters/Points/vtkPointCloudFilter.cxx | 325 ++++ Filters/Points/vtkPointCloudFilter.h | 118 ++ Filters/Points/vtkRadiusOutlierRemoval.cxx | 152 ++ Filters/Points/vtkRadiusOutlierRemoval.h | 102 ++ Filters/Points/vtkSignedDistance.cxx | 466 ++++++ Filters/Points/vtkSignedDistance.h | 156 ++ .../Points/vtkStatisticalOutlierRemoval.cxx | 347 ++++ Filters/Points/vtkStatisticalOutlierRemoval.h | 123 ++ Filters/Points/vtkVoxelGrid.cxx | 299 ++++ Filters/Points/vtkVoxelGrid.h | 141 ++ Remote/point-cloud.remote.cmake | 10 - 79 files changed, 9184 insertions(+), 11 deletions(-) create mode 100644 Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestExtractPoints.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestExtractPoints_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_1.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_2.png.md5 create mode 100644 Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_3.png.md5 create mode 100755 Filters/Points/Testing/Python/TestEuclideanClusterExtraction.py create mode 100755 Filters/Points/Testing/Python/TestEuclideanClusterExtraction2.py create mode 100755 Filters/Points/Testing/Python/TestExtractPoints.py create mode 100755 Filters/Points/Testing/Python/TestFitImplicitFunction.py create mode 100755 Filters/Points/Testing/Python/TestHierarchicalBinningFilter.py create mode 100755 Filters/Points/Testing/Python/TestPCACurvatureEstimation.py create mode 100755 Filters/Points/Testing/Python/TestPCACurvatureEstimation2.py create mode 100755 Filters/Points/Testing/Python/TestPCANormalEstimation.py create mode 100755 Filters/Points/Testing/Python/TestPCANormalEstimation2.py create mode 100755 Filters/Points/Testing/Python/TestRadiusOutlierRemoval.py create mode 100755 Filters/Points/Testing/Python/TestSignedDistanceFilter.py create mode 100755 Filters/Points/Testing/Python/TestStatisticalOutlierRemoval.py create mode 100755 Filters/Points/Testing/Python/TestVoxelGridFilter.py create mode 100644 Filters/Points/vtkBoundedPointSource.cxx create mode 100644 Filters/Points/vtkBoundedPointSource.h create mode 100644 Filters/Points/vtkEuclideanClusterExtraction.cxx create mode 100644 Filters/Points/vtkEuclideanClusterExtraction.h create mode 100644 Filters/Points/vtkExtractHierarchicalBins.cxx create mode 100644 Filters/Points/vtkExtractHierarchicalBins.h create mode 100644 Filters/Points/vtkExtractPointCloudPiece.cxx create mode 100644 Filters/Points/vtkExtractPointCloudPiece.h create mode 100644 Filters/Points/vtkExtractPoints.cxx create mode 100644 Filters/Points/vtkExtractPoints.h create mode 100644 Filters/Points/vtkExtractSurface.cxx create mode 100644 Filters/Points/vtkExtractSurface.h create mode 100644 Filters/Points/vtkFitImplicitFunction.cxx create mode 100644 Filters/Points/vtkFitImplicitFunction.h create mode 100644 Filters/Points/vtkHierarchicalBinningFilter.cxx create mode 100644 Filters/Points/vtkHierarchicalBinningFilter.h create mode 100644 Filters/Points/vtkPCACurvatureEstimation.cxx create mode 100644 Filters/Points/vtkPCACurvatureEstimation.h create mode 100644 Filters/Points/vtkPCANormalEstimation.cxx create mode 100644 Filters/Points/vtkPCANormalEstimation.h create mode 100644 Filters/Points/vtkPointCloudFilter.cxx create mode 100644 Filters/Points/vtkPointCloudFilter.h create mode 100644 Filters/Points/vtkRadiusOutlierRemoval.cxx create mode 100644 Filters/Points/vtkRadiusOutlierRemoval.h create mode 100644 Filters/Points/vtkSignedDistance.cxx create mode 100644 Filters/Points/vtkSignedDistance.h create mode 100644 Filters/Points/vtkStatisticalOutlierRemoval.cxx create mode 100644 Filters/Points/vtkStatisticalOutlierRemoval.h create mode 100644 Filters/Points/vtkVoxelGrid.cxx create mode 100644 Filters/Points/vtkVoxelGrid.h delete mode 100644 Remote/point-cloud.remote.cmake diff --git a/Filters/Points/CMakeLists.txt b/Filters/Points/CMakeLists.txt index d7ccbb702c6..5d21c4994e0 100644 --- a/Filters/Points/CMakeLists.txt +++ b/Filters/Points/CMakeLists.txt @@ -1,25 +1,41 @@ set(Module_SRCS + vtkBoundedPointSource.cxx vtkEllipsoidalGaussianKernel.cxx + vtkEuclideanClusterExtraction.cxx + vtkExtractHierarchicalBins.cxx + vtkExtractPointCloudPiece.cxx + vtkExtractPoints.cxx + vtkExtractSurface.cxx + vtkFitImplicitFunction.cxx vtkGaussianKernel.cxx vtkGeneralizedKernel.cxx + vtkHierarchicalBinningFilter.cxx vtkInterpolationKernel.cxx vtkLinearKernel.cxx + vtkPCACurvatureEstimation.cxx + vtkPCANormalEstimation.cxx + vtkPointCloudFilter.cxx vtkPointInterpolator.cxx vtkPointInterpolator2D.cxx vtkProbabilisticVoronoiKernel.cxx + vtkRadiusOutlierRemoval.cxx vtkSPHInterpolator.cxx vtkSPHCubicKernel.cxx vtkSPHKernel.cxx vtkSPHQuarticKernel.cxx vtkSPHQuinticKernel.cxx vtkShepardKernel.cxx - vtkWendlandQuinticKernel.cxx + vtkSignedDistance.cxx + vtkStatisticalOutlierRemoval.cxx + vtkVoxelGrid.cxx vtkVoronoiKernel.cxx + vtkWendlandQuinticKernel.cxx ) set_source_files_properties( vtkGeneralizedKernel.cxx vtkInterpolationKernel + vtkPointCloudFilter ABSTRACT ) diff --git a/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction.png.md5 b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction.png.md5 new file mode 100644 index 00000000000..43fc95762b5 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction.png.md5 @@ -0,0 +1 @@ +23961f1382fdf6df7918940bf0e73bbd diff --git a/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2.png.md5 new file mode 100644 index 00000000000..59e0e86c5a8 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2.png.md5 @@ -0,0 +1 @@ +3174c28fc3324676cbf5074c553b9bf6 diff --git a/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2_1.png.md5 new file mode 100644 index 00000000000..b3f4e4fde4e --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction2_1.png.md5 @@ -0,0 +1 @@ +a65f2bce5e7812d47bb7f00c39538d55 diff --git a/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_1.png.md5 new file mode 100644 index 00000000000..cad16606f05 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_1.png.md5 @@ -0,0 +1 @@ +43ba4fd2cd361108383cc986eea311f1 diff --git a/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_2.png.md5 new file mode 100644 index 00000000000..b292e4a1752 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestEuclideanClusterExtraction_2.png.md5 @@ -0,0 +1 @@ +3915fe2882fe6a9c68075166cd803cbd diff --git a/Filters/Points/Testing/Data/Baseline/TestExtractPoints.png.md5 b/Filters/Points/Testing/Data/Baseline/TestExtractPoints.png.md5 new file mode 100644 index 00000000000..5eebc86ad4b --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestExtractPoints.png.md5 @@ -0,0 +1 @@ +5d1c2af24bf493178bbc00a94d3bbb4c diff --git a/Filters/Points/Testing/Data/Baseline/TestExtractPoints_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestExtractPoints_1.png.md5 new file mode 100644 index 00000000000..d1b3dc60fe5 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestExtractPoints_1.png.md5 @@ -0,0 +1 @@ +655ccab43147a28b67712a2339ae7481 diff --git a/Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction.png.md5 b/Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction.png.md5 new file mode 100644 index 00000000000..05ce6201e22 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction.png.md5 @@ -0,0 +1 @@ +e904feb0254e876ac9e3f1a0af547e6a diff --git a/Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction_1.png.md5 new file mode 100644 index 00000000000..20412321453 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestFitImplicitFunction_1.png.md5 @@ -0,0 +1 @@ +a3f524867365311f687e8cd7550f0d08 diff --git a/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter.png.md5 b/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter.png.md5 new file mode 100644 index 00000000000..425b605747e --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter.png.md5 @@ -0,0 +1 @@ +4e97477d4d5ddc530415ee35a86c5282 diff --git a/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_1.png.md5 new file mode 100644 index 00000000000..48427fb187a --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_1.png.md5 @@ -0,0 +1 @@ +3d653c1926584ada2454e0ef484c484d diff --git a/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_2.png.md5 new file mode 100644 index 00000000000..8d5f2b13d3f --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestHierarchicalBinningFilter_2.png.md5 @@ -0,0 +1 @@ +8b054573ae82aa3a993a51655f4ac797 diff --git a/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation.png.md5 new file mode 100644 index 00000000000..6af265a005e --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation.png.md5 @@ -0,0 +1 @@ +dee171c1554600081adf8528d1dcbe8a diff --git a/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation2.png.md5 new file mode 100644 index 00000000000..a06cfe73713 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation2.png.md5 @@ -0,0 +1 @@ +df39e44bedd134a825b69b8ae0942d03 diff --git a/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation_1.png.md5 new file mode 100644 index 00000000000..740be5646d5 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCACurvatureEstimation_1.png.md5 @@ -0,0 +1 @@ +29e74ccf4fd4240c2d79fd26371db896 diff --git a/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation.png.md5 new file mode 100644 index 00000000000..366c2d7733c --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation.png.md5 @@ -0,0 +1 @@ +0207fa43dbcc7f5587ae15d1f21ec11e diff --git a/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2.png.md5 new file mode 100644 index 00000000000..062fa71c466 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2.png.md5 @@ -0,0 +1 @@ +1559fe90bfc8772c10e335f83e3e0467 diff --git a/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_1.png.md5 new file mode 100644 index 00000000000..54f3ca9ebf9 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_1.png.md5 @@ -0,0 +1 @@ +7fb2cab5ee8453c0b0315aeb36edb76b diff --git a/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_2.png.md5 new file mode 100644 index 00000000000..ab8d02ae1f2 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation2_2.png.md5 @@ -0,0 +1 @@ +9185bf2ccbe279bf4a3cdc989254a019 diff --git a/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_1.png.md5 new file mode 100644 index 00000000000..1199c5deefb --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_1.png.md5 @@ -0,0 +1 @@ +cedad82c1dad87a0de0949d55bbeb1e5 diff --git a/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_2.png.md5 new file mode 100644 index 00000000000..2d1236417db --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestPCANormalEstimation_2.png.md5 @@ -0,0 +1 @@ +152a15045f918455f93af5f00c2be206 diff --git a/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval.png.md5 b/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval.png.md5 new file mode 100644 index 00000000000..7796bab910e --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval.png.md5 @@ -0,0 +1 @@ +a72aa4ac4b715301840928e0d27fec67 diff --git a/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_1.png.md5 new file mode 100644 index 00000000000..394e43c8568 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_1.png.md5 @@ -0,0 +1 @@ +7e186d4187dae54b80ee4d14257bc1aa diff --git a/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_2.png.md5 new file mode 100644 index 00000000000..0653e87f9a1 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestRadiusOutlierRemoval_2.png.md5 @@ -0,0 +1 @@ +307f4526d74f10adb8767248ea9dadd6 diff --git a/Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter.png.md5 b/Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter.png.md5 new file mode 100644 index 00000000000..29e8545cddd --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter.png.md5 @@ -0,0 +1 @@ +565e56447abef5fda7b2da5f00c8fc85 diff --git a/Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter_1.png.md5 new file mode 100644 index 00000000000..28f00934908 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestSignedDistanceFilter_1.png.md5 @@ -0,0 +1 @@ +2feecf944487096b67f6b4160e907bdd diff --git a/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval.png.md5 b/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval.png.md5 new file mode 100644 index 00000000000..710b6bb0892 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval.png.md5 @@ -0,0 +1 @@ +29f9aac6f51eb73f765e51bc9dd94a18 diff --git a/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_1.png.md5 new file mode 100644 index 00000000000..c5c01f1ae09 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_1.png.md5 @@ -0,0 +1 @@ +a0d2231d5a6d5fab718a1b26975d86dd diff --git a/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_2.png.md5 new file mode 100644 index 00000000000..84aa2f92612 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestStatisticalOutlierRemoval_2.png.md5 @@ -0,0 +1 @@ +cb108893ba2e357f1c57a95063e92b0b diff --git a/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter.png.md5 b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter.png.md5 new file mode 100644 index 00000000000..166d7bb23ca --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter.png.md5 @@ -0,0 +1 @@ +47ed65bd9ec8a38c041a68f4c67b5818 diff --git a/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_1.png.md5 b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_1.png.md5 new file mode 100644 index 00000000000..65fe799fdc0 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_1.png.md5 @@ -0,0 +1 @@ +81c47e145a517a14caa1feb75d1137d5 diff --git a/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_2.png.md5 b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_2.png.md5 new file mode 100644 index 00000000000..c5fb98a2d49 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_2.png.md5 @@ -0,0 +1 @@ +9ed77dab18ab940a57be718f3f8dae66 diff --git a/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_3.png.md5 b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_3.png.md5 new file mode 100644 index 00000000000..31912b02fd4 --- /dev/null +++ b/Filters/Points/Testing/Data/Baseline/TestVoxelGridFilter_3.png.md5 @@ -0,0 +1 @@ +fc1399b4e6d61569132d877655c4c1dc diff --git a/Filters/Points/Testing/Python/CMakeLists.txt b/Filters/Points/Testing/Python/CMakeLists.txt index afaa380cdb0..72e64ffb450 100644 --- a/Filters/Points/Testing/Python/CMakeLists.txt +++ b/Filters/Points/Testing/Python/CMakeLists.txt @@ -7,3 +7,21 @@ vtk_add_test_python( TestSPHInterpolator.py TestSPHInterpolator2D.py ) + +if ("${VTK_RENDERING_BACKEND}" STREQUAL "OpenGL2") + vtk_add_test_python( + TestEuclideanClusterExtraction.py + TestEuclideanClusterExtraction2.py + TestExtractPoints.py + TestFitImplicitFunction.py + TestHierarchicalBinningFilter.py + TestPCACurvatureEstimation.py + TestPCACurvatureEstimation2.py + TestPCANormalEstimation.py + TestPCANormalEstimation2.py + TestRadiusOutlierRemoval.py + TestSignedDistanceFilter.py + TestStatisticalOutlierRemoval.py + TestVoxelGridFilter.py + ) +endif () diff --git a/Filters/Points/Testing/Python/TestEuclideanClusterExtraction.py b/Filters/Points/Testing/Python/TestEuclideanClusterExtraction.py new file mode 100755 index 00000000000..5a11baeb3aa --- /dev/null +++ b/Filters/Points/Testing/Python/TestEuclideanClusterExtraction.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# create pipeline +# + +# Create a cylinder +cyl = vtk.vtkCylinderSource() +cyl.SetCenter(-2,0,0) +cyl.SetRadius(0.02) +cyl.SetHeight(1.8) +cyl.SetResolution(24) + +# Create a (thin) box implicit function +plane = vtk.vtkPlaneSource() +plane.SetOrigin(-1, -0.5, 0) +plane.SetPoint1(0.5, -0.5, 0) +plane.SetPoint2(-1, 0.5, 0) + +# Create a sphere implicit function +sphere = vtk.vtkSphereSource() +sphere.SetCenter(2,0,0) +sphere.SetRadius(0.8) +sphere.SetThetaResolution(96) +sphere.SetPhiResolution(48) + +# Boolean (union) these together +append = vtk.vtkAppendPolyData() +append.AddInputConnection(cyl.GetOutputPort()) +append.AddInputConnection(plane.GetOutputPort()) +append.AddInputConnection(sphere.GetOutputPort()) + +# Extract points along sphere surface +pts = vtk.vtkPolyDataPointSampler() +pts.SetInputConnection(append.GetOutputPort()) +pts.SetDistance(0.025) +pts.Update() + +# Now see if we can extract the three objects as separate clusters. +extr = vtk.vtkEuclideanClusterExtraction() +extr.SetInputConnection(pts.GetOutputPort()) +extr.SetRadius(0.1) +extr.ColorClustersOn() +extr.SetExtractionModeToAllClusters() + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +extr.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(pts.GetOutput().GetNumberOfPoints())) +print(" Time to segment objects: {0}".format(time)) +print(" Number of clusters: {0}".format(extr.GetNumberOfExtractedClusters())) + +# Three different outputs for different curvatures +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(extr.GetOutputPort(0)) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) +subMapper.SetScalarRange(0,2) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) +subActor.AddPosition(0,2.25,0) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(0,0,-1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestEuclideanClusterExtraction2.py b/Filters/Points/Testing/Python/TestEuclideanClusterExtraction2.py new file mode 100755 index 00000000000..3a8e64735ac --- /dev/null +++ b/Filters/Points/Testing/Python/TestEuclideanClusterExtraction2.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Parameters for debugging +NPts = 100000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.SetBounds(-3,3, -1,1, -1,1) +points.ProduceRandomScalarsOff() +points.ProduceCellOutputOff() + +# create some scalars based on implicit function +# Create a cylinder +cyl = vtk.vtkCylinder() +cyl.SetCenter(-2,0,0) +cyl.SetRadius(0.02) + +# Create a (thin) box implicit function +box = vtk.vtkBox() +box.SetBounds(-1,0.5, -0.5,0.5, -0.0005, 0.0005) + +# Create a sphere implicit function +sphere = vtk.vtkSphere() +sphere.SetCenter(2,0,0) +sphere.SetRadius(0.8) + +# Boolean (union) these together +imp = vtk.vtkImplicitBoolean() +imp.SetOperationTypeToUnion() +imp.AddFunction(cyl) +imp.AddFunction(box) +imp.AddFunction(sphere) + +# Generate scalars and vector +sample = vtk.vtkSampleImplicitFunctionFilter() +sample.SetInputConnection(points.GetOutputPort()) +sample.SetImplicitFunction(imp) +sample.Update() +print(sample.GetOutput().GetScalarRange()) + +# Now see if we can extract the three objects as separate clusters. +extr = vtk.vtkEuclideanClusterExtraction() +extr.SetInputConnection(sample.GetOutputPort()) +extr.SetRadius(0.15) +#extr.ColorClustersOn() +#extr.SetExtractionModeToAllClusters() +extr.SetExtractionModeToLargestCluster() +extr.ScalarConnectivityOn() +extr.SetScalarRange(-0.64,-.3) + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +extr.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(points.GetOutput().GetNumberOfPoints())) +print(" Time to segment objects: {0}".format(time)) +print(" Number of clusters: {0}".format(extr.GetNumberOfExtractedClusters())) + +# Draw the points +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(extr.GetOutputPort(0)) +#subMapper.SetInputConnection(sample.GetOutputPort(0)) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) +subMapper.SetScalarRange(-0.64,2.25) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(sample.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(0,0,-1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestExtractPoints.py b/Filters/Points/Testing/Python/TestExtractPoints.py new file mode 100755 index 00000000000..37e8ebf153b --- /dev/null +++ b/Filters/Points/Testing/Python/TestExtractPoints.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Create a sphere implicit function +sphere = vtk.vtkSphere() +sphere.SetCenter(0.9,0.1,0.1) +sphere.SetRadius(0.33) + +# Extract points within sphere +extract = vtk.vtkExtractPoints() +extract.SetInputConnection(points.GetOutputPort()) +extract.SetImplicitFunction(sphere) + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +extract.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Time to remove points: {0}".format(time)) +print(" Number removed: {0}".format(extract.GetNumberOfPointsRemoved()), + " (out of: {}".format(NPts)) + +# First output are the non-outliers +extMapper = vtk.vtkPointGaussianMapper() +extMapper.SetInputConnection(extract.GetOutputPort()) +extMapper.EmissiveOff() +extMapper.SetScaleFactor(0.0) + +extActor = vtk.vtkActor() +extActor.SetMapper(extMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +ren1 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(extActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,1,1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +ren1.SetActiveCamera(cam) + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestFitImplicitFunction.py b/Filters/Points/Testing/Python/TestFitImplicitFunction.py new file mode 100755 index 00000000000..06cd9d7d964 --- /dev/null +++ b/Filters/Points/Testing/Python/TestFitImplicitFunction.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Create a sphere implicit function +sphere = vtk.vtkSphere() +sphere.SetCenter(0.9,0.1,0.1) +sphere.SetRadius(0.33) + +# Extract points within sphere +extract = vtk.vtkFitImplicitFunction() +extract.SetInputConnection(points.GetOutputPort()) +extract.SetImplicitFunction(sphere) +extract.SetThreshold(0.005) + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +extract.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Time to extract points: {0}".format(time)) +print(" Number removed: {0}".format(extract.GetNumberOfPointsRemoved()), + " (out of: {}".format(NPts)) + +# First output are the non-outliers +extMapper = vtk.vtkPointGaussianMapper() +extMapper.SetInputConnection(extract.GetOutputPort()) +extMapper.EmissiveOff() +extMapper.SetScaleFactor(0.0) + +extActor = vtk.vtkActor() +extActor.SetMapper(extMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +ren1 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(extActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,1,1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +ren1.SetActiveCamera(cam) + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestHierarchicalBinningFilter.py b/Filters/Points/Testing/Python/TestHierarchicalBinningFilter.py new file mode 100755 index 00000000000..11cf26c5869 --- /dev/null +++ b/Filters/Points/Testing/Python/TestHierarchicalBinningFilter.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +binNum = 16 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Bin the points +hBin = vtk.vtkHierarchicalBinningFilter() +hBin.SetInputConnection(points.GetOutputPort()) +#hBin.AutomaticOn() +hBin.AutomaticOff() +hBin.SetDivisions(2,2,2) +hBin.SetBounds(points.GetOutput().GetBounds()) + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +hBin.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(NPts)) +print(" Time to bin: {0}".format(time)) +#print(hBin) +#print(hBin.GetOutput()) + +# write stuff out +w = vtk.vtkXMLPolyDataWriter() +w.SetFileName("binPoints.vtp") +w.SetInputConnection(hBin.GetOutputPort()) +#w.SetDataModeToAscii() +#w.Write() + +# Output a selected bin of points +extBin = vtk.vtkExtractHierarchicalBins() +extBin.SetInputConnection(hBin.GetOutputPort()) +extBin.SetBinningFilter(hBin) +#extBin.SetLevel(0) +extBin.SetLevel(-1) +extBin.SetBin(binNum) + +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(extBin.GetOutputPort()) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Create another outline +bds = [0,0,0,0,0,0] +hBin.GetBinBounds(binNum,bds) +binOutline = vtk.vtkOutlineSource() +binOutline.SetBounds(bds) + +binOutlineMapper = vtk.vtkPolyDataMapper() +binOutlineMapper.SetInputConnection(binOutline.GetOutputPort()) + +binOutlineActor = vtk.vtkActor() +binOutlineActor.SetMapper(binOutlineMapper) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(outlineActor) +ren0.AddActor(binOutlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,1,1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestPCACurvatureEstimation.py b/Filters/Points/Testing/Python/TestPCACurvatureEstimation.py new file mode 100755 index 00000000000..ebf73b03d3c --- /dev/null +++ b/Filters/Points/Testing/Python/TestPCACurvatureEstimation.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# create pipeline +# + +# Create a cylinder +cyl = vtk.vtkCylinderSource() +cyl.SetCenter(-2,0,0) +cyl.SetRadius(0.02) +cyl.SetHeight(1.8) +cyl.SetResolution(24) + +# Create a (thin) box implicit function +plane = vtk.vtkPlaneSource() +plane.SetOrigin(-1, -0.5, 0) +plane.SetPoint1(0.5, -0.5, 0) +plane.SetPoint2(-1, 0.5, 0) + +# Create a sphere implicit function +sphere = vtk.vtkSphereSource() +sphere.SetCenter(2,0,0) +sphere.SetRadius(0.8) +sphere.SetThetaResolution(96) +sphere.SetPhiResolution(48) + +# Boolean (union) these together +append = vtk.vtkAppendPolyData() +append.AddInputConnection(cyl.GetOutputPort()) +append.AddInputConnection(plane.GetOutputPort()) +append.AddInputConnection(sphere.GetOutputPort()) + +# Extract points along sphere surface +pts = vtk.vtkPolyDataPointSampler() +pts.SetInputConnection(append.GetOutputPort()) +pts.SetDistance(0.01) +pts.Update() + +# Now generate normals from resulting points +curv = vtk.vtkPCACurvatureEstimation() +curv.SetInputConnection(pts.GetOutputPort()) +curv.SetSampleSize(20) + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +curv.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(pts.GetOutput().GetNumberOfPoints())) +print(" Time to generate curvature: {0}".format(time)) + +# Break out the curvature into three separate arrays +assign = vtk.vtkAssignAttribute() +assign.SetInputConnection(curv.GetOutputPort()) +assign.Assign("PCACurvature", "VECTORS", "POINT_DATA") + +extract = vtk.vtkExtractVectorComponents() +extract.SetInputConnection(assign.GetOutputPort()) +extract.Update() +print(extract.GetOutput(0).GetScalarRange()) +print(extract.GetOutput(1).GetScalarRange()) +print(extract.GetOutput(2).GetScalarRange()) + +# Three different outputs for different curvatures +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(extract.GetOutputPort(0)) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) +subActor.AddPosition(0,2.25,0) + +sub1Mapper = vtk.vtkPointGaussianMapper() +sub1Mapper.SetInputConnection(extract.GetOutputPort(1)) +sub1Mapper.EmissiveOff() +sub1Mapper.SetScaleFactor(0.0) + +sub1Actor = vtk.vtkActor() +sub1Actor.SetMapper(sub1Mapper) + +sub2Mapper = vtk.vtkPointGaussianMapper() +sub2Mapper.SetInputConnection(extract.GetOutputPort(2)) +sub2Mapper.EmissiveOff() +sub2Mapper.SetScaleFactor(0.0) + +sub2Actor = vtk.vtkActor() +sub2Actor.SetMapper(sub2Mapper) +sub2Actor.AddPosition(0,-2.25,0) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(sub1Actor) +ren0.AddActor(sub2Actor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(0,0,-1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestPCACurvatureEstimation2.py b/Filters/Points/Testing/Python/TestPCACurvatureEstimation2.py new file mode 100755 index 00000000000..568c08b95db --- /dev/null +++ b/Filters/Points/Testing/Python/TestPCACurvatureEstimation2.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.SetBounds(-3,3, -1,1, -1,1) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Create a cylinder +cyl = vtk.vtkCylinder() +cyl.SetCenter(-2,0,0) +cyl.SetRadius(0.02) + +# Create a (thin) box implicit function +box = vtk.vtkBox() +box.SetBounds(-1,0.5, -0.5,0.5, -0.0005, 0.0005) + +# Create a sphere implicit function +sphere = vtk.vtkSphere() +sphere.SetCenter(2,0,0) +sphere.SetRadius(0.8) + +# Boolean (union) these together +imp = vtk.vtkImplicitBoolean() +imp.SetOperationTypeToUnion() +imp.AddFunction(cyl) +imp.AddFunction(box) +imp.AddFunction(sphere) + +# Extract points along sphere surface +extract = vtk.vtkFitImplicitFunction() +extract.SetInputConnection(points.GetOutputPort()) +extract.SetImplicitFunction(imp) +extract.SetThreshold(0.0005) +extract.Update() + +# Now generate normals from resulting points +curv = vtk.vtkPCACurvatureEstimation() +curv.SetInputConnection(extract.GetOutputPort()) +curv.SetSampleSize(6) + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +curv.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(NPts)) +print(" Time to generate curvature: {0}".format(time)) + +# Break out the curvature into thress separate arrays +assign = vtk.vtkAssignAttribute() +assign.SetInputConnection(curv.GetOutputPort()) +assign.Assign("PCACurvature", "VECTORS", "POINT_DATA") + +extract = vtk.vtkExtractVectorComponents() +extract.SetInputConnection(assign.GetOutputPort()) +extract.Update() +print(extract.GetOutput(0).GetScalarRange()) +print(extract.GetOutput(1).GetScalarRange()) +print(extract.GetOutput(2).GetScalarRange()) + +# Three different outputs for different curvatures +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(extract.GetOutputPort(0)) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) +subActor.AddPosition(0,2.25,0) + +sub1Mapper = vtk.vtkPointGaussianMapper() +sub1Mapper.SetInputConnection(extract.GetOutputPort(1)) +sub1Mapper.EmissiveOff() +sub1Mapper.SetScaleFactor(0.0) + +sub1Actor = vtk.vtkActor() +sub1Actor.SetMapper(sub1Mapper) + +sub2Mapper = vtk.vtkPointGaussianMapper() +sub2Mapper.SetInputConnection(extract.GetOutputPort(2)) +sub2Mapper.EmissiveOff() +sub2Mapper.SetScaleFactor(0.0) + +sub2Actor = vtk.vtkActor() +sub2Actor.SetMapper(sub2Mapper) +sub2Actor.AddPosition(0,-2.25,0) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(sub1Actor) +ren0.AddActor(sub2Actor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(0,0,-1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestPCANormalEstimation.py b/Filters/Points/Testing/Python/TestPCANormalEstimation.py new file mode 100755 index 00000000000..e5a8a021bc6 --- /dev/null +++ b/Filters/Points/Testing/Python/TestPCANormalEstimation.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Create a sphere implicit function +sphere = vtk.vtkSphere() +sphere.SetCenter(0,0,0) +sphere.SetRadius(0.75) + +# Extract points along sphere surface +extract = vtk.vtkFitImplicitFunction() +extract.SetInputConnection(points.GetOutputPort()) +extract.SetImplicitFunction(sphere) +extract.SetThreshold(0.005) +extract.Update() + +# Now generate normals from resulting points +norms = vtk.vtkPCANormalEstimation() +norms.SetInputConnection(extract.GetOutputPort()) +norms.SetSampleSize(20) +norms.FlipNormalsOn() +norms.SetNormalOrientationToPoint() +norms.SetOrientationPoint(0,0,0) + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +norms.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(NPts)) +print(" Time to generate normals: {0}".format(time)) +#print(hBin) +#print(hBin.GetOutput()) + +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(norms.GetOutputPort()) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) + +# Draw the normals +mask = vtk.vtkMaskPoints() +mask.SetInputConnection(norms.GetOutputPort()) +mask.SetRandomModeType(1) +mask.SetMaximumNumberOfPoints(250) + +hhog = vtk.vtkHedgeHog() +hhog.SetInputConnection(mask.GetOutputPort()) +hhog.SetVectorModeToUseNormal() +hhog.SetScaleFactor(0.25) + +hogMapper = vtk.vtkPolyDataMapper() +hogMapper.SetInputConnection(hhog.GetOutputPort()) + +hogActor = vtk.vtkActor() +hogActor.SetMapper(hogMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(hogActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,1,1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestPCANormalEstimation2.py b/Filters/Points/Testing/Python/TestPCANormalEstimation2.py new file mode 100755 index 00000000000..6c77b4684e3 --- /dev/null +++ b/Filters/Points/Testing/Python/TestPCANormalEstimation2.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Create a sphere implicit function +sphere = vtk.vtkSphere() +sphere.SetCenter(0,0,0) +sphere.SetRadius(0.75) + +# Extract points along sphere surface +extract = vtk.vtkFitImplicitFunction() +extract.SetInputConnection(points.GetOutputPort()) +extract.SetImplicitFunction(sphere) +extract.SetThreshold(0.005) +extract.Update() + +# Now generate normals from resulting points +norms = vtk.vtkPCANormalEstimation() +norms.SetInputConnection(extract.GetOutputPort()) +norms.SetSampleSize(20) +norms.FlipNormalsOn() +norms.SetNormalOrientationToGraphTraversal() + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +norms.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(NPts)) +print(" Time to generate normals: {0}".format(time)) +#print(hBin) +#print(hBin.GetOutput()) + +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(norms.GetOutputPort()) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) + +# Draw the normals +mask = vtk.vtkMaskPoints() +mask.SetInputConnection(norms.GetOutputPort()) +mask.SetRandomModeType(1) +mask.SetMaximumNumberOfPoints(250) + +hhog = vtk.vtkHedgeHog() +hhog.SetInputConnection(mask.GetOutputPort()) +hhog.SetVectorModeToUseNormal() +hhog.SetScaleFactor(0.25) + +hogMapper = vtk.vtkPolyDataMapper() +hogMapper.SetInputConnection(hhog.GetOutputPort()) + +hogActor = vtk.vtkActor() +hogActor.SetMapper(hogMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(hogActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,1,1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestRadiusOutlierRemoval.py b/Filters/Points/Testing/Python/TestRadiusOutlierRemoval.py new file mode 100755 index 00000000000..100af2c3838 --- /dev/null +++ b/Filters/Points/Testing/Python/TestRadiusOutlierRemoval.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 10000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Reuse the locator +locator = vtk.vtkStaticPointLocator() +locator.SetDataSet(points.GetOutput()) +locator.BuildLocator() + +# Remove isolated points +removal = vtk.vtkRadiusOutlierRemoval() +removal.SetInputConnection(points.GetOutputPort()) +removal.SetLocator(locator) +removal.SetRadius(0.1) +removal.SetNumberOfNeighbors(2) +removal.GenerateOutliersOn() + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +removal.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Time to remove points: {0}".format(time)) +print(" Number removed: {0}".format(removal.GetNumberOfPointsRemoved()), + " (out of: {}".format(NPts)) + +# First output are the non-outliers +remMapper = vtk.vtkPointGaussianMapper() +remMapper.SetInputConnection(removal.GetOutputPort()) +remMapper.EmissiveOff() +remMapper.SetScaleFactor(0.0) + +remActor = vtk.vtkActor() +remActor.SetMapper(remMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Second output are the outliers +remMapper1 = vtk.vtkPointGaussianMapper() +remMapper1.SetInputConnection(removal.GetOutputPort(1)) +remMapper1.EmissiveOff() +remMapper1.SetScaleFactor(0.0) + +remActor1 = vtk.vtkActor() +remActor1.SetMapper(remMapper1) + +# Create an outline +outline1 = vtk.vtkOutlineFilter() +outline1.SetInputConnection(points.GetOutputPort()) + +outlineMapper1 = vtk.vtkPolyDataMapper() +outlineMapper1.SetInputConnection(outline1.GetOutputPort()) + +outlineActor1 = vtk.vtkActor() +outlineActor1.SetMapper(outlineMapper1) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +ren0.SetViewport(0,0,.5,1) +ren1 = vtk.vtkRenderer() +ren1.SetViewport(0.5,0,1,1) +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +renWin.AddRenderer(ren1) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(remActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +ren1.AddActor(remActor1) +ren1.AddActor(outlineActor1) +ren1.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(500,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,1,1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +ren1.SetActiveCamera(cam) + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestSignedDistanceFilter.py b/Filters/Points/Testing/Python/TestSignedDistanceFilter.py new file mode 100755 index 00000000000..b08dc3ddac3 --- /dev/null +++ b/Filters/Points/Testing/Python/TestSignedDistanceFilter.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Create a sphere implicit function +sphere = vtk.vtkSphere() +sphere.SetCenter(0,0,0) +sphere.SetRadius(0.75) + +# Cut the sphere in half with a plane +plane = vtk.vtkPlane() +plane.SetOrigin(0,0,0) +plane.SetNormal(1,1,1) + +# Boolean (intersect) these together to create a hemi-sphere +imp = vtk.vtkImplicitBoolean() +imp.SetOperationTypeToIntersection() +imp.AddFunction(sphere) +imp.AddFunction(plane) + +# Extract points along hemi-sphere surface +extract = vtk.vtkFitImplicitFunction() +extract.SetInputConnection(points.GetOutputPort()) +extract.SetImplicitFunction(imp) +extract.SetThreshold(0.005) +extract.Update() + +# Now generate normals from resulting points +norms = vtk.vtkPCANormalEstimation() +norms.SetInputConnection(extract.GetOutputPort()) +norms.SetSampleSize(20) +norms.FlipNormalsOff() +norms.SetNormalOrientationToGraphTraversal() +#norms.SetNormalOrientationToPoint() +#norms.SetOrientationPoint(0.3,0.3,0.3) +norms.Update() + +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(extract.GetOutputPort()) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) + +# Generate signed distance function and contour it +dist = vtk.vtkSignedDistance() +dist.SetInputConnection(norms.GetOutputPort()) +dist.SetRadius(0.1) #how far out to propagate distance calculation +dist.SetBounds(-1,1, -1,1, -1,1) +dist.SetDimensions(50,50,50) + +# Extract the surface with modified flying edges +fe = vtk.vtkExtractSurface() +fe.SetInputConnection(dist.GetOutputPort()) +fe.SetRadius(0.1) # this should match the signed distance radius + +# Time the execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +fe.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Points processed: {0}".format(NPts)) +print(" Time to generate and extract distance function: {0}".format(time)) + +feMapper = vtk.vtkPolyDataMapper() +feMapper.SetInputConnection(fe.GetOutputPort()) + +feActor = vtk.vtkActor() +feActor.SetMapper(feMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(feActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(250,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,-1,-1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestStatisticalOutlierRemoval.py b/Filters/Points/Testing/Python/TestStatisticalOutlierRemoval.py new file mode 100755 index 00000000000..f4f4e41b775 --- /dev/null +++ b/Filters/Points/Testing/Python/TestStatisticalOutlierRemoval.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 100000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create point cloud. A bunch of random points plus some outliers +# over the six faces of the bounding box +# +points = vtk.vtkPoints() +points.SetDataTypeToFloat() +points.SetNumberOfPoints(NPts+6) +scalars = vtk.vtkFloatArray() +scalars.SetNumberOfTuples(NPts+6) +scalars.SetName("scalars") +for i in range(0,NPts): + points.SetPoint(i,math.Random(-1,1),math.Random(-1,1),math.Random(-1,1)) + scalars.SetValue(i,math.Random(0,1)) + +points.SetPoint(NPts, -5,0,0) +scalars.SetValue(NPts, 0.5) +points.SetPoint(NPts+1, 5,0,0) +scalars.SetValue(NPts+1, 0.5) +points.SetPoint(NPts+2, 0,-5,0) +scalars.SetValue(NPts+2, 0.5) +points.SetPoint(NPts+3, 0, 5,0) +scalars.SetValue(NPts+3, 0.5) +points.SetPoint(NPts+4, 0,0,-5) +scalars.SetValue(NPts+4, 0.5) +points.SetPoint(NPts+5, 0,0, 5) +scalars.SetValue(NPts+5, 0.5) + +polydata = vtk.vtkPolyData() +polydata.SetPoints(points) +polydata.GetPointData().SetScalars(scalars) + +# Reuse the locator +locator = vtk.vtkStaticPointLocator() +locator.SetDataSet(polydata) +locator.BuildLocator() + +# Remove statistically isolated points +removal = vtk.vtkStatisticalOutlierRemoval() +removal.SetInputData(polydata) +removal.SetLocator(locator) +removal.SetSampleSize(20) +removal.SetStandardDeviationFactor(1.5) +removal.GenerateOutliersOn() + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +removal.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Number of points processed: {0}".format(NPts)) +print(" Time to remove outliers: {0}".format(time)) +print(" Number removed: {0}".format(removal.GetNumberOfPointsRemoved())) +print(" Computed mean: {0}".format(removal.GetComputedMean())) +print(" Computed standard deviation: {0}".format(removal.GetComputedStandardDeviation())) + +# First output are the non-outliers +remMapper = vtk.vtkPointGaussianMapper() +remMapper.SetInputConnection(removal.GetOutputPort()) +remMapper.EmissiveOff() +remMapper.SetScaleFactor(0.0) + +remActor = vtk.vtkActor() +remActor.SetMapper(remMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputData(polydata) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Second output are the outliers +remMapper1 = vtk.vtkPointGaussianMapper() +remMapper1.SetInputConnection(removal.GetOutputPort(1)) +remMapper1.EmissiveOff() +remMapper1.SetScaleFactor(0.0) + +remActor1 = vtk.vtkActor() +remActor1.SetMapper(remMapper1) + +# Create an outline +outline1 = vtk.vtkOutlineFilter() +outline1.SetInputData(polydata) + +outlineMapper1 = vtk.vtkPolyDataMapper() +outlineMapper1.SetInputConnection(outline1.GetOutputPort()) + +outlineActor1 = vtk.vtkActor() +outlineActor1.SetMapper(outlineMapper1) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +ren0.SetViewport(0,0,.5,1) +ren1 = vtk.vtkRenderer() +ren1.SetViewport(0.5,0,1,1) +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +renWin.AddRenderer(ren1) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(remActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +ren1.AddActor(remActor1) +ren1.AddActor(outlineActor1) +ren1.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(500,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(1,1,1) +cam.SetPosition(0,0,0) +ren0.ResetCamera() + +ren1.SetActiveCamera(cam) + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/Testing/Python/TestVoxelGridFilter.py b/Filters/Points/Testing/Python/TestVoxelGridFilter.py new file mode 100755 index 00000000000..8803ac7f565 --- /dev/null +++ b/Filters/Points/Testing/Python/TestVoxelGridFilter.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +import vtk +from vtk.test import Testing +from vtk.util.misc import vtkGetDataRoot +VTK_DATA_ROOT = vtkGetDataRoot() + +# Interpolate onto a volume + +# Parameters for debugging +NPts = 1000000 +math = vtk.vtkMath() +math.RandomSeed(31415) + +# create pipeline +# +points = vtk.vtkBoundedPointSource() +points.SetNumberOfPoints(NPts) +points.ProduceRandomScalarsOn() +points.ProduceCellOutputOff() +points.Update() + +# Subsample +subsample = vtk.vtkVoxelGrid() +subsample.SetInputConnection(points.GetOutputPort()) +subsample.SetConfigurationStyleToAutomatic() + +# Time execution +timer = vtk.vtkTimerLog() +timer.StartTimer() +subsample.Update() +timer.StopTimer() +time = timer.GetElapsedTime() +print("Time to subsample: {0}".format(time)) +print(" Original number of points: {0}".format(NPts)) +print(" Final number of points: {0}".format(subsample.GetOutput().GetNumberOfPoints())) + +# Output the original points +subMapper = vtk.vtkPointGaussianMapper() +subMapper.SetInputConnection(points.GetOutputPort()) +subMapper.EmissiveOff() +subMapper.SetScaleFactor(0.0) + +subActor = vtk.vtkActor() +subActor.SetMapper(subMapper) + +# Create an outline +outline = vtk.vtkOutlineFilter() +outline.SetInputConnection(points.GetOutputPort()) + +outlineMapper = vtk.vtkPolyDataMapper() +outlineMapper.SetInputConnection(outline.GetOutputPort()) + +outlineActor = vtk.vtkActor() +outlineActor.SetMapper(outlineMapper) + +# Output the subsampled points +subMapper1 = vtk.vtkPointGaussianMapper() +subMapper1.SetInputConnection(subsample.GetOutputPort()) +subMapper1.EmissiveOff() +subMapper1.SetScaleFactor(0.0) + +subActor1 = vtk.vtkActor() +subActor1.SetMapper(subMapper1) + +# Create an outline +outline1 = vtk.vtkOutlineFilter() +outline1.SetInputConnection(points.GetOutputPort()) + +outlineMapper1 = vtk.vtkPolyDataMapper() +outlineMapper1.SetInputConnection(outline1.GetOutputPort()) + +outlineActor1 = vtk.vtkActor() +outlineActor1.SetMapper(outlineMapper1) + +# Create the RenderWindow, Renderer and both Actors +# +ren0 = vtk.vtkRenderer() +ren0.SetViewport(0,0,.5,1) +ren1 = vtk.vtkRenderer() +ren1.SetViewport(0.5,0,1,1) +renWin = vtk.vtkRenderWindow() +renWin.AddRenderer(ren0) +renWin.AddRenderer(ren1) +iren = vtk.vtkRenderWindowInteractor() +iren.SetRenderWindow(renWin) + +# Add the actors to the renderer, set the background and size +# +ren0.AddActor(subActor) +ren0.AddActor(outlineActor) +ren0.SetBackground(0.1, 0.2, 0.4) + +ren1.AddActor(subActor1) +ren1.AddActor(outlineActor1) +ren1.SetBackground(0.1, 0.2, 0.4) + +renWin.SetSize(500,250) + +cam = ren0.GetActiveCamera() +cam.SetFocalPoint(0,0,0) +cam.SetPosition(1,1,1) +ren0.ResetCamera() + +ren1.SetActiveCamera(cam) + +iren.Initialize() + +# render the image +# +renWin.Render() + +#iren.Start() diff --git a/Filters/Points/vtkBoundedPointSource.cxx b/Filters/Points/vtkBoundedPointSource.cxx new file mode 100644 index 00000000000..d3ea7a1964f --- /dev/null +++ b/Filters/Points/vtkBoundedPointSource.cxx @@ -0,0 +1,154 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkBoundedPointSource.cxx + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkBoundedPointSource.h" + +#include "vtkCellArray.h" +#include "vtkMath.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkObjectFactory.h" +#include "vtkPoints.h" +#include "vtkPolyData.h" +#include "vtkFloatArray.h" +#include "vtkPointData.h" + +vtkStandardNewMacro(vtkBoundedPointSource); + +//---------------------------------------------------------------------------- +vtkBoundedPointSource::vtkBoundedPointSource() +{ + this->NumberOfPoints = 100; + + this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = -1.0; + this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = 1.0; + + this->OutputPointsPrecision = SINGLE_PRECISION; + + this->ProduceCellOutput = false; + + this->ProduceRandomScalars = false; + this->ScalarRange[0] = 0.0; + this->ScalarRange[1] = 1.0; + + this->SetNumberOfInputPorts(0); +} + +//---------------------------------------------------------------------------- +int vtkBoundedPointSource::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **vtkNotUsed(inputVector), + vtkInformationVector *outputVector) +{ + // get the info object + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the ouptut + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + vtkIdType ptId; + double x[3]; + + vtkPoints *newPoints = vtkPoints::New(); + // Set the desired precision for the points in the output. + if(this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION) + { + newPoints->SetDataType(VTK_DOUBLE); + } + else + { + newPoints->SetDataType(VTK_FLOAT); + } + + // Generate the points + newPoints->SetNumberOfPoints(this->NumberOfPoints); + double xmin = (this->Bounds[0] < this->Bounds[1] ? this->Bounds[0] : this->Bounds[1]); + double xmax = (this->Bounds[0] < this->Bounds[1] ? this->Bounds[1] : this->Bounds[0]); + double ymin = (this->Bounds[2] < this->Bounds[3] ? this->Bounds[2] : this->Bounds[3]); + double ymax = (this->Bounds[2] < this->Bounds[3] ? this->Bounds[3] : this->Bounds[2]); + double zmin = (this->Bounds[4] < this->Bounds[5] ? this->Bounds[4] : this->Bounds[5]); + double zmax = (this->Bounds[4] < this->Bounds[5] ? this->Bounds[5] : this->Bounds[4]); + + vtkMath *math = vtkMath::New(); + for (ptId=0; ptId<this->NumberOfPoints; ptId++) + { + x[0] = math->Random(xmin,xmax); + x[1] = math->Random(ymin,ymax); + x[2] = math->Random(zmin,zmax); + newPoints->SetPoint(ptId,x); + } + output->SetPoints(newPoints); + newPoints->Delete(); + + // Generate the scalars if requested + if ( this->ProduceRandomScalars ) + { + vtkFloatArray *scalars = vtkFloatArray::New(); + scalars->SetName("RandomScalars"); + scalars->SetNumberOfTuples(this->NumberOfPoints); + float *s = static_cast<float*>(scalars->GetVoidPointer(0)); + double sMin = (this->ScalarRange[0] < this->ScalarRange[1] ? + this->ScalarRange[0] : this->ScalarRange[1]); + double sMax = (this->ScalarRange[0] < this->ScalarRange[1] ? + this->ScalarRange[1] : this->ScalarRange[0]); + for (ptId=0; ptId<this->NumberOfPoints; ptId++) + { + *s++ = math->Random(sMin,sMax); + } + output->GetPointData()->SetScalars(scalars); + scalars->Delete(); + } + + // Generate the vertices if requested + if ( this->ProduceCellOutput ) + { + vtkCellArray *newVerts = vtkCellArray::New(); + newVerts->Allocate(newVerts->EstimateSize(1,this->NumberOfPoints)); + newVerts->InsertNextCell(this->NumberOfPoints); + for (ptId=0; ptId<this->NumberOfPoints; ptId++) + { + newVerts->InsertCellPoint(ptId); + } + output->SetVerts(newVerts); + newVerts->Delete(); + } + + math->Delete(); + return 1; +} + +//---------------------------------------------------------------------------- +void vtkBoundedPointSource::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Number Of Points: " << this->NumberOfPoints << "\n"; + + for(int i=0;i<6;i++) + { + os << indent << "Bounds[" << i << "]: " << this->Bounds[i] << "\n"; + } + + os << indent << "Output Points Precision: " << this->OutputPointsPrecision << "\n"; + + os << indent << "Produce Cell Output: " + << (this->ProduceCellOutput ? "On\n" : "Off\n"); + + os << indent << "Produce Random Scalars: " + << (this->ProduceRandomScalars ? "On\n" : "Off\n"); + os << indent << "Scalar Range (" << this->ScalarRange[0] << "," + << this->ScalarRange[1] << ")\n"; + +} diff --git a/Filters/Points/vtkBoundedPointSource.h b/Filters/Points/vtkBoundedPointSource.h new file mode 100644 index 00000000000..271533ff7b7 --- /dev/null +++ b/Filters/Points/vtkBoundedPointSource.h @@ -0,0 +1,98 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkBoundedPointSource.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkBoundedPointSource - create a random cloud of points within a +// specified bounding box + +// .SECTION Description +// vtkBoundedPointSource is a source object that creates a user-specified +// number of points within a specified bounding box. The points are scattered +// randomly throughout the box. Optionally, the user can produce a +// vtkPolyVertex cell as well as random scalar values within a specified +// range. The class is typically used for debugging and testing, as well as +// seeding streamlines. + +#ifndef vtkBoundedPointSource_h +#define vtkBoundedPointSource_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +class VTKFILTERSPOINTS_EXPORT vtkBoundedPointSource : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods for instantiation, type information and printing. + static vtkBoundedPointSource *New(); + vtkTypeMacro(vtkBoundedPointSource,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Set the number of points to generate. + vtkSetClampMacro(NumberOfPoints,vtkIdType,1,VTK_ID_MAX); + vtkGetMacro(NumberOfPoints,vtkIdType); + + // Description: + // Set the bounding box for the point distribution. By default the bounds is + // (-1,1,-1,1,-1,1). + vtkSetVector6Macro(Bounds,double); + vtkGetVectorMacro(Bounds,double,6); + + // Description: + // Set/get the desired precision for the output points. + // vtkAlgorithm::SINGLE_PRECISION - Output single-precision floating point. + // vtkAlgorithm::DOUBLE_PRECISION - Output double-precision floating point. + vtkSetMacro(OutputPointsPrecision,int); + vtkGetMacro(OutputPointsPrecision,int); + + // Description: + // Indicate whether to produce a vtkPolyVertex cell to go along with the + // output vtkPoints generated. By default a cell is NOT produced. Some filters + // do not need the vtkPolyVertex which just consumes a lot of memory. + vtkSetMacro(ProduceCellOutput, bool); + vtkGetMacro(ProduceCellOutput, bool); + vtkBooleanMacro(ProduceCellOutput, bool); + + // Description: + // Indicate whether to produce random point scalars in the output. By default + // this is off. + vtkSetMacro(ProduceRandomScalars, bool); + vtkGetMacro(ProduceRandomScalars, bool); + vtkBooleanMacro(ProduceRandomScalars, bool); + + // Description: + // Set the range in which the random scalars should be produced. By default the + // scalar range is (0,1). + vtkSetVector2Macro(ScalarRange,double); + vtkGetVectorMacro(ScalarRange,double,2); + +protected: + vtkBoundedPointSource(); + ~vtkBoundedPointSource() {} + + int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *); + + vtkIdType NumberOfPoints; + double Bounds[6]; + int OutputPointsPrecision; + bool ProduceCellOutput; + bool ProduceRandomScalars; + double ScalarRange[2]; + +private: + vtkBoundedPointSource(const vtkBoundedPointSource&) VTK_DELETE_FUNCTION; + void operator=(const vtkBoundedPointSource&) VTK_DELETE_FUNCTION; +}; + +#endif diff --git a/Filters/Points/vtkEuclideanClusterExtraction.cxx b/Filters/Points/vtkEuclideanClusterExtraction.cxx new file mode 100644 index 00000000000..4b3252287e6 --- /dev/null +++ b/Filters/Points/vtkEuclideanClusterExtraction.cxx @@ -0,0 +1,463 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkEuclideanClusterExtraction.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkEuclideanClusterExtraction.h" + +#include "vtkPointSet.h" +#include "vtkIdList.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkObjectFactory.h" +#include "vtkPointData.h" +#include "vtkPoints.h" +#include "vtkFloatArray.h" +#include "vtkAbstractPointLocator.h" +#include "vtkStaticPointLocator.h" +#include "vtkIdTypeArray.h" + +vtkStandardNewMacro(vtkEuclideanClusterExtraction); +vtkCxxSetObjectMacro(vtkEuclideanClusterExtraction,Locator,vtkAbstractPointLocator); + +//---------------------------------------------------------------------------- +// Construct with default extraction mode to extract largest cluster. +vtkEuclideanClusterExtraction::vtkEuclideanClusterExtraction() +{ + this->ClusterSizes = vtkIdTypeArray::New(); + this->ExtractionMode = VTK_EXTRACT_LARGEST_CLUSTER; + this->ColorClusters = false; + + this->ScalarConnectivity = false; + this->ScalarRange[0] = 0.0; + this->ScalarRange[1] = 1.0; + + this->ClosestPoint[0] = this->ClosestPoint[1] = this->ClosestPoint[2] = 0.0; + + this->Locator = vtkStaticPointLocator::New(); + + this->NeighborScalars = vtkFloatArray::New(); + this->NeighborScalars->Allocate(64); + + this->NeighborPointIds = vtkIdList::New(); + this->NeighborPointIds->Allocate(64); + + this->Seeds = vtkIdList::New(); + this->SpecifiedClusterIds = vtkIdList::New(); + + this->NewScalars = 0; + +} + +//---------------------------------------------------------------------------- +vtkEuclideanClusterExtraction::~vtkEuclideanClusterExtraction() +{ + this->SetLocator(NULL); + this->ClusterSizes->Delete(); + this->NeighborScalars->Delete(); + this->NeighborPointIds->Delete(); + this->Seeds->Delete(); + this->SpecifiedClusterIds->Delete(); +} + +//---------------------------------------------------------------------------- +int vtkEuclideanClusterExtraction::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkPointSet *input = vtkPointSet::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + vtkIdType numPts, i, ptId; + vtkPoints *newPts; + int maxPointsInCluster, clusterId, largestClusterId=0; + vtkPointData *pd=input->GetPointData(), *outputPD=output->GetPointData(); + + vtkDebugMacro(<<"Executing point clustering filter."); + + // Check input/allocate storage + // + if ( (numPts=input->GetNumberOfPoints()) < 1 ) + { + vtkDebugMacro(<<"No data to cluster!"); + return 1; + } + vtkPoints *inPts = input->GetPoints(); + + // Need to build a locator + if ( !this->Locator ) + { + vtkErrorMacro(<<"Point locator required\n"); + return 0; + } + this->Locator->SetDataSet(input); + this->Locator->BuildLocator(); + + // See whether to consider scalar connectivity + // + this->InScalars = input->GetPointData()->GetScalars(); + if ( !this->ScalarConnectivity ) + { + this->InScalars = NULL; + } + else + { + this->NeighborScalars->SetNumberOfComponents(this->InScalars->GetNumberOfComponents()); + if ( this->ScalarRange[1] < this->ScalarRange[0] ) + { + this->ScalarRange[1] = this->ScalarRange[0]; + } + } + + // Initialize. Keep track of the points visited. + // + this->Visited = new char [numPts]; + std::fill_n(this->Visited, numPts, static_cast<char>(0)); + + this->ClusterSizes->Reset(); + this->PointMap = new vtkIdType[numPts]; + std::fill_n(this->PointMap, numPts, static_cast<vtkIdType>(-1)); + + this->NewScalars = vtkIdTypeArray::New(); + this->NewScalars->SetName("ClusterId"); + this->NewScalars->SetNumberOfTuples(numPts); + + newPts = vtkPoints::New(); + newPts->SetDataType(input->GetPoints()->GetDataType()); + newPts->Allocate(numPts); + + // Traverse all points marking those visited. Each new search + // starts a new connected cluster. Connected clusters grow + // using a connected wave propagation. + // + this->Wave = vtkIdList::New(); + this->Wave->Allocate(numPts/4+1,numPts); + this->Wave2 = vtkIdList::New(); + this->Wave2->Allocate(numPts/4+1,numPts); + + this->PointNumber = 0; + this->ClusterNumber = 0; + maxPointsInCluster = 0; + + this->PointIds = vtkIdList::New(); + this->PointIds->Allocate(8, VTK_CELL_SIZE); + + if ( this->ExtractionMode != VTK_EXTRACT_POINT_SEEDED_CLUSTERS && + this->ExtractionMode != VTK_EXTRACT_CLOSEST_POINT_CLUSTER ) + { //visit all points assigning cluster number + for (ptId=0; ptId < numPts; ptId++) + { + if ( ptId && !(ptId % 10000) ) + { + this->UpdateProgress (0.1 + 0.8*ptId/numPts); + } + + if ( ! this->Visited[ptId] ) + { + this->NumPointsInCluster = 0; + this->InsertIntoWave(this->Wave,ptId); + this->TraverseAndMark (inPts); + + if ( this->NumPointsInCluster > maxPointsInCluster ) + { + maxPointsInCluster = this->NumPointsInCluster; + largestClusterId = this->ClusterNumber; + } + + if ( this->NumPointsInCluster > 0 ) + { + this->ClusterSizes->InsertValue(this->ClusterNumber++, + this->NumPointsInCluster); + } + this->Wave->Reset(); + this->Wave2->Reset(); + } + } + } + else // clusters have been seeded, everything considered in same cluster + { + this->NumPointsInCluster = 0; + + if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_CLUSTERS ) + { + this->NumPointsInCluster = 0; + for (i=0; i < this->Seeds->GetNumberOfIds(); i++) + { + ptId = this->Seeds->GetId(i); + if ( ptId >= 0 ) + { + this->InsertIntoWave(this->Wave,ptId); + } + } + } + else if ( this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_CLUSTER ) + {//loop over points, find closest one + ptId = this->Locator->FindClosestPoint(this->ClosestPoint); + this->InsertIntoWave(this->Wave,ptId); + } + this->UpdateProgress (0.5); + + //mark all seeded clusters + this->TraverseAndMark (inPts); + this->ClusterSizes->InsertValue(this->ClusterNumber,this->NumPointsInCluster); + this->UpdateProgress (0.9); + } + + vtkDebugMacro (<<"Extracted " << this->ClusterNumber << " cluster(s)"); + this->Wave->Delete(); + this->Wave2->Delete(); + delete [] this->Visited; + + // Now that points have been marked, traverse the PointMap pulling + // everything that has been visited and is selected for output. + outputPD->CopyAllocate(pd); + if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_CLUSTERS || + this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_CLUSTER || + this->ExtractionMode == VTK_EXTRACT_ALL_CLUSTERS) + { // extract any point that's been visited + for (ptId=0; ptId < numPts; ptId++) + { + if ( this->PointMap[ptId] >= 0 ) + { + newPts->InsertPoint(this->PointMap[ptId],inPts->GetPoint(ptId)); + outputPD->CopyData(pd,ptId,this->PointMap[ptId]); + } + } + } + else if ( this->ExtractionMode == VTK_EXTRACT_SPECIFIED_CLUSTERS ) + { + bool inCluster; + for (ptId=0; ptId < numPts; ptId++) + { + if ( this->PointMap[ptId] >= 0 ) + { + clusterId = this->NewScalars->GetValue(this->PointMap[ptId]); + for (inCluster=false,i=0; i<this->SpecifiedClusterIds->GetNumberOfIds(); i++) + { + if ( clusterId == this->SpecifiedClusterIds->GetId(i) ) + { + inCluster = true; + break; + } + } + if ( inCluster ) + { + newPts->InsertPoint(this->PointMap[ptId],inPts->GetPoint(ptId)); + outputPD->CopyData(pd,ptId,this->PointMap[ptId]); + } + } + } + } + else //extract largest cluster + { + for (ptId=0; ptId < numPts; ptId++) + { + if ( this->PointMap[ptId] >= 0 ) + { + clusterId = this->NewScalars->GetValue(this->PointMap[ptId]); + if ( clusterId == largestClusterId ) + { + newPts->InsertPoint(this->PointMap[ptId],inPts->GetPoint(ptId)); + outputPD->CopyData(pd,ptId,this->PointMap[ptId]); + } + } + } + } + + // if coloring clusters; send down new scalar data + if ( this->ColorClusters ) + { + int idx = outputPD->AddArray(this->NewScalars); + outputPD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS); + } + this->NewScalars->Delete(); + + newPts->Squeeze(); + output->SetPoints(newPts); + + delete [] this->PointMap; + this->PointIds->Delete(); + + // print out some debugging information + int num = this->GetNumberOfExtractedClusters(); + int count = 0; + + for (int ii = 0; ii < num; ii++) + { + count += this->ClusterSizes->GetValue (ii); + } + vtkDebugMacro (<< "Total # of points accounted for: " << count); + vtkDebugMacro (<< "Extracted " << newPts->GetNumberOfPoints() << " points"); + newPts->Delete(); + + return 1; +} + + +//---------------------------------------------------------------------------- +// Insert point into connected wave. Check to make sure it satisfies connectivity +// criterion (if enabled). +void vtkEuclideanClusterExtraction:: +InsertIntoWave(vtkIdList *wave, vtkIdType ptId) +{ + this->Visited[ptId] = 1; + if ( this->InScalars ) //is scalar connectivity enabled? + { + double s = this->InScalars->GetTuple1(ptId); + if ( s >= this->ScalarRange[0] && s <= this->ScalarRange[1] ) + { + wave->InsertNextId(ptId); + } + } + else + { + wave->InsertNextId(ptId); + } +} + + +//---------------------------------------------------------------------------- +// Update current point information including updating cluster number. Note: +// traversal occurs across proximally located points, possibly limited by +// scalar connectivty. +// +void vtkEuclideanClusterExtraction::TraverseAndMark (vtkPoints *inPts) +{ + vtkIdType i, j, numPts, numIds, ptId; + vtkIdList *tmpWave; + double x[3]; + + while ( (numIds=this->Wave->GetNumberOfIds()) > 0 ) + { + for ( i=0; i < numIds; i++ ) //for all points in this wave + { + ptId = this->Wave->GetId(i); + this->PointMap[ptId] = this->PointNumber++; + this->NewScalars->SetValue(this->PointMap[ptId],this->ClusterNumber); + this->NumPointsInCluster++; + + inPts->GetPoint(ptId,x); + this->Locator->FindPointsWithinRadius(this->Radius,x,this->NeighborPointIds); + + numPts = this->NeighborPointIds->GetNumberOfIds(); + for (j=0; j < numPts; ++j) + { + ptId = this->NeighborPointIds->GetId(j); + if ( ! this->Visited[ptId] ) + { + this->InsertIntoWave(this->Wave2,ptId); + }//if point not yet visited + }//for all neighbors + }//for all cells in this connected wave + + tmpWave = this->Wave; + this->Wave = this->Wave2; + this->Wave2 = tmpWave; + tmpWave->Reset(); + } //while wave is not empty + + return; +} + +//---------------------------------------------------------------------------- +// Obtain the number of connected clusters. +int vtkEuclideanClusterExtraction::GetNumberOfExtractedClusters() +{ + return this->ClusterSizes->GetMaxId() + 1; +} + +//---------------------------------------------------------------------------- +// Initialize list of point ids used to seed clusters. +void vtkEuclideanClusterExtraction::InitializeSeedList() +{ + this->Modified(); + this->Seeds->Reset(); +} + +//---------------------------------------------------------------------------- +// Add a seed id. Note: ids are 0-offset. +void vtkEuclideanClusterExtraction::AddSeed(vtkIdType id) +{ + this->Modified(); + this->Seeds->InsertNextId(id); +} + +//---------------------------------------------------------------------------- +// Delete a seed id. Note: ids are 0-offset. +void vtkEuclideanClusterExtraction::DeleteSeed(vtkIdType id) +{ + this->Modified(); + this->Seeds->DeleteId(id); +} + +//---------------------------------------------------------------------------- +// Initialize list of cluster ids to extract. +void vtkEuclideanClusterExtraction::InitializeSpecifiedClusterList() +{ + this->Modified(); + this->SpecifiedClusterIds->Reset(); +} + +//---------------------------------------------------------------------------- +// Add a cluster id to extract. Note: ids are 0-offset. +void vtkEuclideanClusterExtraction::AddSpecifiedCluster(int id) +{ + this->Modified(); + this->SpecifiedClusterIds->InsertNextId(id); +} + +//---------------------------------------------------------------------------- +// Delete a cluster id to extract. Note: ids are 0-offset. +void vtkEuclideanClusterExtraction::DeleteSpecifiedCluster(int id) +{ + this->Modified(); + this->SpecifiedClusterIds->DeleteId(id); +} + +//---------------------------------------------------------------------------- +int vtkEuclideanClusterExtraction:: +FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); + return 1; +} + +//---------------------------------------------------------------------------- +void vtkEuclideanClusterExtraction::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Extraction Mode: "; + os << this->GetExtractionModeAsString() << "\n"; + + os << indent << "Closest Point: (" << this->ClosestPoint[0] << ", " + << this->ClosestPoint[1] << ", " << this->ClosestPoint[2] << ")\n"; + + os << indent << "Color Clusters: " << (this->ColorClusters ? "On\n" : "Off\n"); + + os << indent << "Scalar Connectivity: " + << (this->ScalarConnectivity ? "On\n" : "Off\n"); + + double *range = this->GetScalarRange(); + os << indent << "Scalar Range: (" << range[0] << ", " << range[1] << ")\n"; + + os << indent << "Locator: " << this->Locator << "\n"; +} diff --git a/Filters/Points/vtkEuclideanClusterExtraction.h b/Filters/Points/vtkEuclideanClusterExtraction.h new file mode 100644 index 00000000000..35fe8ebd50c --- /dev/null +++ b/Filters/Points/vtkEuclideanClusterExtraction.h @@ -0,0 +1,233 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkEuclideanClusterExtraction.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkEuclideanClusterExtraction - perform segmentation based on geometric +// proximity and optional scalar threshold +// .SECTION Description +// vtkEuclideanClusterExtraction is a filter that extracts points that are in +// close geometric proximity, and optionally satisfies a scalar threshold +// criterion. (Points extracted in this way are referred to as clusters.) +// The filter works in one of five ways: 1) extract the largest cluster in the +// dataset; 2) extract specified cluster number(s); 3) extract all clusters +// containing specified point ids; 4) extract the cluster closest to a specified +// point; or 5) extract all clusters (which can be used for coloring the clusters). +// +// Note that geometric proximity is defined by setting the Radius instance +// variable. This variable defines a local sphere around each point; other +// points contained in this sphere are considered "connected" to the +// point. Setting this number too large will connect clusters that should not +// be; setting it too small will fragment the point cloud into myriad +// clusters. To accelerate the geometric proximity operations, a point +// locator may be specified. By default, a vtkStaticPointLocator is used, but +// any vtkAbstractPointLocator may be specified. +// +// The behavior of vtkEuclideanClusterExtraction can be modified by turning +// on the boolean ivar ScalarConnectivity. If this flag is on, the clustering +// algorithm is modified so that points are considered part of a cluster if +// they satisfy both the geometric proximity measure, and the points scalar +// values falls into the scalar range specified. This use of +// ScalarConnectivity is particularly useful for data with intensity or color +// information, serving as a simple "connected segmentation" algorithm. For +// example, by using a seed point in a known cluster, clustering will pull +// out all points "representing" the local structure. + +// .SECTION See Also +// vtkConnectivityFilter vtkPolyDataConnectivityFilter + +#ifndef vtkEuclideanClusterExtraction_h +#define vtkEuclideanClusterExtraction_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +#define VTK_EXTRACT_POINT_SEEDED_CLUSTERS 1 +#define VTK_EXTRACT_SPECIFIED_CLUSTERS 2 +#define VTK_EXTRACT_LARGEST_CLUSTER 3 +#define VTK_EXTRACT_ALL_CLUSTERS 4 +#define VTK_EXTRACT_CLOSEST_POINT_CLUSTER 5 + +class vtkDataArray; +class vtkFloatArray; +class vtkIdList; +class vtkIdTypeArray; +class vtkAbstractPointLocator; + + +class VTKFILTERSPOINTS_EXPORT vtkEuclideanClusterExtraction : public vtkPolyDataAlgorithm +{ +public: + vtkTypeMacro(vtkEuclideanClusterExtraction,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Construct with default extraction mode to extract largest clusters. + static vtkEuclideanClusterExtraction *New(); + + // Description: + // Specify the local search radius. + vtkSetClampMacro(Radius,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(Radius,double); + + // Description: + // Turn on/off connectivity based on scalar value. If on, points are + // connected only if the are proximal AND the scalar value of a candiate + // point falls in the scalar range specified. Of course input point scalar + // data must be provided. + vtkSetMacro(ScalarConnectivity,bool); + vtkGetMacro(ScalarConnectivity,bool); + vtkBooleanMacro(ScalarConnectivity,bool); + + // Description: + // Set the scalar range used to extract points based on scalar connectivity. + vtkSetVector2Macro(ScalarRange,double); + vtkGetVector2Macro(ScalarRange,double); + + // Description: + // Control the extraction of connected surfaces. + vtkSetClampMacro(ExtractionMode,int, + VTK_EXTRACT_POINT_SEEDED_CLUSTERS,VTK_EXTRACT_CLOSEST_POINT_CLUSTER); + vtkGetMacro(ExtractionMode,int); + void SetExtractionModeToPointSeededClusters() + {this->SetExtractionMode(VTK_EXTRACT_POINT_SEEDED_CLUSTERS);}; + void SetExtractionModeToLargestCluster() + {this->SetExtractionMode(VTK_EXTRACT_LARGEST_CLUSTER);}; + void SetExtractionModeToSpecifiedClusters() + {this->SetExtractionMode(VTK_EXTRACT_SPECIFIED_CLUSTERS);}; + void SetExtractionModeToClosestPointCluster() + {this->SetExtractionMode(VTK_EXTRACT_CLOSEST_POINT_CLUSTER);}; + void SetExtractionModeToAllClusters() + {this->SetExtractionMode(VTK_EXTRACT_ALL_CLUSTERS);}; + const char *GetExtractionModeAsString(); + + // Description: + // Initialize the list of point ids used to seed clusters. + void InitializeSeedList(); + + // Description: + // Add a seed id (point id). Note: ids are 0-offset. + void AddSeed(vtkIdType id); + + // Description: + // Delete a seed id.a + void DeleteSeed(vtkIdType id); + + // Description: + // Initialize the list of cluster ids to extract. + void InitializeSpecifiedClusterList(); + + // Description: + // Add a cluster id to extract. Note: ids are 0-offset. + void AddSpecifiedCluster(int id); + + // Description: + // Delete a cluster id to extract. + void DeleteSpecifiedCluster(int id); + + // Description: + // Used to specify the x-y-z point coordinates when extracting the cluster + // closest to a specified point. + vtkSetVector3Macro(ClosestPoint,double); + vtkGetVectorMacro(ClosestPoint,double,3); + + // Description: + // Obtain the number of connected clusters. This value is valid only after filter execution. + int GetNumberOfExtractedClusters(); + + // Description: + // Turn on/off the coloring of connected clusters. + vtkSetMacro(ColorClusters,bool); + vtkGetMacro(ColorClusters,bool); + vtkBooleanMacro(ColorClusters,bool); + + // Description: + // Specify a point locator. By default a vtkStaticPointLocator is + // used. The locator performs efficient proximity searches near a + // specified interpolation position. + void SetLocator(vtkAbstractPointLocator *locator); + vtkGetObjectMacro(Locator,vtkAbstractPointLocator); + +protected: + vtkEuclideanClusterExtraction(); + ~vtkEuclideanClusterExtraction(); + + double Radius; //connection radius + bool ColorClusters; //boolean turns on/off scalar gen for separate clusters + int ExtractionMode; //how to extract clusters + vtkIdList *Seeds; //id's of points or cells used to seed clusters + vtkIdList *SpecifiedClusterIds; //clusters specified for extraction + vtkIdTypeArray *ClusterSizes; //size (in cells) of each cluster extracted + + double ClosestPoint[3]; + + bool ScalarConnectivity; + double ScalarRange[2]; + + vtkAbstractPointLocator *Locator; + + // Configure the pipeline + virtual int RequestData(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int FillInputPortInformation(int port, vtkInformation *info); + + // Internal method for propagating connected waves. + void InsertIntoWave(vtkIdList *wave, vtkIdType ptId); + void TraverseAndMark(vtkPoints *pts); + +private: + vtkEuclideanClusterExtraction(const vtkEuclideanClusterExtraction&) VTK_DELETE_FUNCTION; + void operator=(const vtkEuclideanClusterExtraction&) VTK_DELETE_FUNCTION; + + // used to support algorithm execution + vtkFloatArray *NeighborScalars; + vtkIdList *NeighborPointIds; + char *Visited; + vtkIdType *PointMap; + vtkIdTypeArray *NewScalars; + vtkIdType ClusterNumber; + vtkIdType PointNumber; + vtkIdType NumPointsInCluster; + vtkDataArray *InScalars; + vtkIdList *Wave; + vtkIdList *Wave2; + vtkIdList *PointIds; + +}; + +// Description: +// Return the method of extraction as a string. +inline const char *vtkEuclideanClusterExtraction::GetExtractionModeAsString(void) +{ + if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_CLUSTERS ) + { + return "ExtractPointSeededClusters"; + } + else if ( this->ExtractionMode == VTK_EXTRACT_SPECIFIED_CLUSTERS ) + { + return "ExtractSpecifiedClusters"; + } + else if ( this->ExtractionMode == VTK_EXTRACT_ALL_CLUSTERS ) + { + return "ExtractAllClusters"; + } + else if ( this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_CLUSTER ) + { + return "ExtractClosestPointCluster"; + } + else + { + return "ExtractLargestCluster"; + } +} + +#endif diff --git a/Filters/Points/vtkExtractHierarchicalBins.cxx b/Filters/Points/vtkExtractHierarchicalBins.cxx new file mode 100644 index 00000000000..8344407ab11 --- /dev/null +++ b/Filters/Points/vtkExtractHierarchicalBins.cxx @@ -0,0 +1,102 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractHierarchicalBins.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkExtractHierarchicalBins.h" + +#include "vtkObjectFactory.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkDataArray.h" +#include "vtkHierarchicalBinningFilter.h" + +vtkStandardNewMacro(vtkExtractHierarchicalBins); +vtkCxxSetObjectMacro(vtkExtractHierarchicalBins,BinningFilter,vtkHierarchicalBinningFilter); + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//---------------------------------------------------------------------------- +// Mark points to be extracted +static void MaskPoints(vtkIdType numPts, vtkIdType *map, vtkIdType offset, + vtkIdType numFill) +{ + std::fill_n(map, offset, static_cast<vtkIdType>(-1)); + std::fill_n(map+offset, numFill, static_cast<vtkIdType>(1)); + std::fill_n(map+offset+numFill, numPts-(offset+numFill), static_cast<vtkIdType>(-1)); +} + +} //anonymous namespace + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +vtkExtractHierarchicalBins::vtkExtractHierarchicalBins() +{ + this->Level = 0; + this->Bin = -1; + this->BinningFilter = NULL; +} + +//---------------------------------------------------------------------------- +vtkExtractHierarchicalBins::~vtkExtractHierarchicalBins() +{ + this->SetBinningFilter(NULL); +} + +//---------------------------------------------------------------------------- +// Traverse all the input points and extract points that are contained within +// and implicit function. +int vtkExtractHierarchicalBins::FilterPoints(vtkPointSet *input) +{ + // Check the input. + if ( !this->BinningFilter ) + { + vtkErrorMacro(<<"vtkHierarchicalBinningFilter required\n"); + return 0; + } + + // Access the correct bin and determine how many points to extract. + vtkIdType offset; + vtkIdType numFill; + + if ( this->Level >= 0 ) + { + offset = this->BinningFilter->GetLevelOffset(this->Level, numFill); + } + else if ( this->Bin >= 0 ) + { + offset = this->BinningFilter->GetBinOffset(this->Bin, numFill); + } + else //pass everything through + { + return 1; + } + + vtkIdType numPts = input->GetNumberOfPoints(); + MaskPoints(numPts, this->PointMap, offset, numFill); + + return 1; +} + +//---------------------------------------------------------------------------- +void vtkExtractHierarchicalBins::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Level: " << this->Level << "\n"; + os << indent << "Bin: " << this->Bin << "\n"; + os << indent << "Binning Filter: " + << static_cast<void *>(this->BinningFilter) << "\n"; + +} diff --git a/Filters/Points/vtkExtractHierarchicalBins.h b/Filters/Points/vtkExtractHierarchicalBins.h new file mode 100644 index 00000000000..c73b1493ca6 --- /dev/null +++ b/Filters/Points/vtkExtractHierarchicalBins.h @@ -0,0 +1,102 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractHierarchicalBins.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkExtractHierarchicalBins - manipulate the output of +// vtkHierarchicalBinningFilter + +// .SECTION Description +// vtkExtractHierarchicalBins enables users to extract data from the output +// of vtkHierarchicalBinningFilter. Points at a particular level, or at a +// level and bin number, can be filtered to the output. To perform these +// operations, the output must contain points sorted into bins (the +// vtkPoints), with offsets pointing to the beginning of each bin (a +// vtkFieldData array named "BinOffsets"). +// + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkFiltersPointsFilter vtkRadiusOutlierRemoval vtkStatisticalOutlierRemoval +// vtkThresholdPoints vtkImplicitFunction vtkExtractGeoemtry +// vtkFitImplicitFunction + +#ifndef vtkExtractHierarchicalBins_h +#define vtkExtractHierarchicalBins_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPointCloudFilter.h" + +class vtkHierarchicalBinningFilter; +class vtkPointSet; + + +class VTKFILTERSPOINTS_EXPORT vtkExtractHierarchicalBins : public vtkPointCloudFilter +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkExtractHierarchicalBins *New(); + vtkTypeMacro(vtkExtractHierarchicalBins,vtkPointCloudFilter); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Specify the level to extract. If non-negative, with a negative bin + // number, then all points at this level are extracted and sent to the + // output. If negative, then the points from the specified bin are sent to + // the output. If both the level and bin number are negative values, then the + // input is sent to the output. By default the 0th level is extracted. + vtkSetMacro(Level,int); + vtkGetMacro(Level,int); + + // Description: + // Specify the bin number to extract. If a non-negative value, then the + // points from the bin number specified are extracted. If negative, then + // entire levels of points are extacted (assuming the Level is + // non-negative). Note that the bin tree is flattened, a particular + // bin number may refer to a bin on any level. + vtkSetMacro(Bin,int); + vtkGetMacro(Bin,int); + + // Description: + // Specify the vtkHierarchicalBinningFilter to query for relavant + // information. Make sure that this filter has executed prior to the execution of + // this filter. (This is generally a safe bet if connected in a pipeline.) + virtual void SetBinningFilter(vtkHierarchicalBinningFilter*); + vtkGetObjectMacro(BinningFilter,vtkHierarchicalBinningFilter); + + +protected: + vtkExtractHierarchicalBins(); + ~vtkExtractHierarchicalBins(); + + // Users can extract points from a particular level or bin. + int Level; + int Bin; + vtkHierarchicalBinningFilter *BinningFilter; + + // All derived classes must implement this method. Note that a side effect of + // the class is to populate the PointMap. Zero is returned if there is a failure. + virtual int FilterPoints(vtkPointSet *input); + +private: + vtkExtractHierarchicalBins(const vtkExtractHierarchicalBins&) VTK_DELETE_FUNCTION; + void operator=(const vtkExtractHierarchicalBins&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkExtractPointCloudPiece.cxx b/Filters/Points/vtkExtractPointCloudPiece.cxx new file mode 100644 index 00000000000..e1a4232bc88 --- /dev/null +++ b/Filters/Points/vtkExtractPointCloudPiece.cxx @@ -0,0 +1,137 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractPointCloudPiece.cxx + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkExtractPointCloudPiece.h" + +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkNew.h" +#include "vtkObjectFactory.h" +#include "vtkPointData.h" +// #include "vtkPolyData.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkDoubleArray.h" + +vtkStandardNewMacro(vtkExtractPointCloudPiece); + +//----------------------------------------------------------------------------- +vtkExtractPointCloudPiece::vtkExtractPointCloudPiece() +{ + this->ModuloOrdering = true; +} + +//----------------------------------------------------------------------------- +int vtkExtractPointCloudPiece::RequestUpdateExtent( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *vtkNotUsed(outputVector)) +{ + // get the info object + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + + inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), 0); + inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), 1); + inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), 0); + + return 1; +} + +//----------------------------------------------------------------------------- +int vtkExtractPointCloudPiece::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkPolyData *input = vtkPolyData::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // handle field data + vtkFieldData *fd = input->GetFieldData(); + vtkFieldData *outFD = output->GetFieldData(); + vtkDataArray *offsets = fd->GetArray("BinOffsets"); + // we wipe the field data as the early viesion of the binner + // was producing some huge field data that was killing file IO times + outFD->Initialize(); + + // Pipeline update piece will tell us what to generate. + int piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()); + + vtkIdType startIndex, endIndex; + if (vtkIntArray::SafeDownCast(offsets)) + { + vtkIntArray *ioffs = vtkIntArray::SafeDownCast(offsets); + startIndex = ioffs->GetValue(piece); + endIndex = ioffs->GetValue(piece+1); + } + else + { + vtkIdTypeArray *ioffs = vtkIdTypeArray::SafeDownCast(offsets); + startIndex = ioffs->GetValue(piece); + endIndex = ioffs->GetValue(piece+1); + } + + vtkIdType numPts = endIndex - startIndex; + vtkPointData *pd = input->GetPointData(); + vtkPointData *outPD = output->GetPointData(); + outPD->CopyAllocate(pd,numPts); + + vtkNew<vtkPoints> newPoints; + newPoints->Allocate(numPts); + + newPoints->SetNumberOfPoints(numPts); + + if (this->ModuloOrdering) + { + // loop over points copying them to the output + // we do it in an mod 11 approach to add some randomization to the order + vtkIdType inIdx = 0; + vtkIdType nextStart = 1; + for (vtkIdType i = 0; i < numPts; i++) + { + newPoints->SetPoint(i,input->GetPoint(inIdx+startIndex)); + outPD->CopyData(pd, inIdx + startIndex, i); + inIdx += 11; + if (inIdx >= numPts) + { + inIdx = nextStart; + nextStart++; + } + } + } + else // otherwise no reordering + { + // copy the points + newPoints->InsertPoints(0, numPts, startIndex, input->GetPoints()); + // copy point data + outPD->CopyData(pd, 0, numPts, startIndex); + } + + output->SetPoints(newPoints.Get()); + + return 1; +} + +//----------------------------------------------------------------------------- +void vtkExtractPointCloudPiece::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + os << indent << "ModuloOrdering: " << this->ModuloOrdering << "\n"; +} diff --git a/Filters/Points/vtkExtractPointCloudPiece.h b/Filters/Points/vtkExtractPointCloudPiece.h new file mode 100644 index 00000000000..225e2c02bc4 --- /dev/null +++ b/Filters/Points/vtkExtractPointCloudPiece.h @@ -0,0 +1,61 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractPointCloudPiece.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkExtractPointCloudPiece - Return a piece of a point cloud +// .SECTION Description +// This filter takes the output of a vtkHierarchicalBinningFilter and allows +// the pipeline to stream it. Pieces are detemined from an offset integral +// array is associated with the field data of the input. + +#ifndef vtkExtractPointCloudPiece_h +#define vtkExtractPointCloudPiece_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +class vtkIdList; +class vtkIntArray; + +class VTKFILTERSPOINTS_EXPORT vtkExtractPointCloudPiece : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods for instantiation, printing, and type information. + static vtkExtractPointCloudPiece *New(); + vtkTypeMacro(vtkExtractPointCloudPiece, vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Turn on or off modulo sampling of the points. By default this is on and the + // points in a given piece will be reordered in an attempt to reduce spatial + // coherency. + vtkSetMacro(ModuloOrdering,bool); + vtkGetMacro(ModuloOrdering,bool); + vtkBooleanMacro(ModuloOrdering,bool); + +protected: + vtkExtractPointCloudPiece(); + ~vtkExtractPointCloudPiece() {} + + // Usual data generation method + int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *); + int RequestUpdateExtent(vtkInformation *, vtkInformationVector **, vtkInformationVector *); + bool ModuloOrdering; + +private: + vtkExtractPointCloudPiece(const vtkExtractPointCloudPiece&) VTK_DELETE_FUNCTION; + void operator=(const vtkExtractPointCloudPiece&) VTK_DELETE_FUNCTION; +}; + +#endif diff --git a/Filters/Points/vtkExtractPoints.cxx b/Filters/Points/vtkExtractPoints.cxx new file mode 100644 index 00000000000..e9f43d372cf --- /dev/null +++ b/Filters/Points/vtkExtractPoints.cxx @@ -0,0 +1,140 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractPoints.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkExtractPoints.h" + +#include "vtkObjectFactory.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkImplicitFunction.h" +#include "vtkSMPTools.h" + +vtkStandardNewMacro(vtkExtractPoints); +vtkCxxSetObjectMacro(vtkExtractPoints,ImplicitFunction,vtkImplicitFunction); + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//---------------------------------------------------------------------------- +// The threaded core of the algorithm +template <typename T> +struct ExtractPoints +{ + const T *Points; + vtkImplicitFunction *Function; + bool ExtractInside; + vtkIdType *PointMap; + + ExtractPoints(T *points, vtkImplicitFunction *f, bool inside, vtkIdType *map) : + Points(points), Function(f), ExtractInside(inside), PointMap(map) + { + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + const T *p = this->Points + 3*ptId; + vtkIdType *map = this->PointMap + ptId; + vtkImplicitFunction *f = this->Function; + double x[3]; + double inside = (this->ExtractInside ? 1.0 : -1.0); + + for ( ; ptId < endPtId; ++ptId) + { + x[0] = static_cast<double>(*p++); + x[1] = static_cast<double>(*p++); + x[2] = static_cast<double>(*p++); + + *map++ = ((f->FunctionValue(x) * inside) <= 0.0 ? 1 : -1 ); + } + } + + static void Execute(vtkExtractPoints *self, vtkIdType numPts, + T *points, vtkIdType *map) + { + ExtractPoints extract(points, self->GetImplicitFunction(), + self->GetExtractInside(), map); + vtkSMPTools::For(0, numPts, extract); + } + +}; //ExtractPoints + +} //anonymous namespace + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +vtkExtractPoints::vtkExtractPoints() +{ + this->ImplicitFunction = NULL; + this->ExtractInside = true; +} + +//---------------------------------------------------------------------------- +vtkExtractPoints::~vtkExtractPoints() +{ + this->SetImplicitFunction(NULL); +} + +//---------------------------------------------------------------------------- +// Overload standard modified time function. If implicit function is modified, +// then this object is modified as well. +vtkMTimeType vtkExtractPoints::GetMTime() +{ + vtkMTimeType mTime=this->MTime.GetMTime(); + vtkMTimeType impFuncMTime; + + if ( this->ImplicitFunction != NULL ) + { + impFuncMTime = this->ImplicitFunction->GetMTime(); + mTime = ( impFuncMTime > mTime ? impFuncMTime : mTime ); + } + + return mTime; +} + +//---------------------------------------------------------------------------- +// Traverse all the input points and extract points that are contained within +// and implicit function. +int vtkExtractPoints::FilterPoints(vtkPointSet *input) +{ + // Check the input. + if ( !this->ImplicitFunction ) + { + vtkErrorMacro(<<"Implicit function required\n"); + return 0; + } + + // Determine which points, if any, should be removed. We create a map + // to keep track. The bulk of the algorithmic work is done in this pass. + vtkIdType numPts = input->GetNumberOfPoints(); + void *inPtr = input->GetPoints()->GetVoidPointer(0); + switch (input->GetPoints()->GetDataType()) + { + vtkTemplateMacro(ExtractPoints<VTK_TT>:: + Execute(this, numPts, (VTK_TT *)inPtr, this->PointMap)); + } + + return 1; +} + +//---------------------------------------------------------------------------- +void vtkExtractPoints::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Implicit Function: " + << static_cast<void *>(this->ImplicitFunction) << "\n"; + os << indent << "Extract Inside: " + << (this->ExtractInside ? "On\n" : "Off\n"); +} diff --git a/Filters/Points/vtkExtractPoints.h b/Filters/Points/vtkExtractPoints.h new file mode 100644 index 00000000000..c8e3a65471b --- /dev/null +++ b/Filters/Points/vtkExtractPoints.h @@ -0,0 +1,101 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractPoints.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkExtractPoints - extract points within an implicit function + +// .SECTION Description +// vtkExtractPoints removes points that are either inside or outside of a +// vtkImplicitFunction. Implicit functions in VTK defined as function of the +// form f(x,y,z)=c, where values c<=0 are interior values of the implicit +// function. Typical examples include planes, spheres, cylinders, cones, +// etc. plus boolean combinations of these functions. (This operation +// presumes closure on the set, so points on the boundary are also considered +// to be inside.) +// +// Note that while any vtkPointSet type can be provided as input, the output is +// represented by an explicit representation of points via a +// vtkPolyData. This output polydata will populate its instance of vtkPoints, +// but no cells will be defined (i.e., no vtkVertex or vtkPolyVertex are +// contained in the output). Also, after filter execution, the user can +// request a vtkIdType* map which indicates how the input points were mapped +// to the output. A value of map[i] (where i is the ith input point) less +// than 0 means that the ith input point was removed. (See also the +// superclass documentation for accessing the removed points through the +// filter's second output.) + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkPointCloudFilter vtkRadiusOutlierRemoval vtkStatisticalOutlierRemoval +// vtkThresholdPoints vtkImplicitFunction vtkExtractGeoemtry +// vtkFitImplicitFunction + +#ifndef vtkExtractPoints_h +#define vtkExtractPoints_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPointCloudFilter.h" + +class vtkImplicitFunction; +class vtkPointSet; + + +class VTKFILTERSPOINTS_EXPORT vtkExtractPoints : public vtkPointCloudFilter +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkExtractPoints *New(); + vtkTypeMacro(vtkExtractPoints,vtkPointCloudFilter); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Specify the implicit function for inside/outside checks. + virtual void SetImplicitFunction(vtkImplicitFunction*); + vtkGetObjectMacro(ImplicitFunction,vtkImplicitFunction); + + // Description: + // Boolean controls whether to extract points that are inside of implicit + // function (ExtractInside == true) or outside of implicit function + // (ExtractInside == false). By default, ExtractInside is true. + vtkSetMacro(ExtractInside,bool); + vtkGetMacro(ExtractInside,bool); + vtkBooleanMacro(ExtractInside,bool); + + // Description: + // Return the MTime taking into account changes to the implicit function + virtual vtkMTimeType GetMTime(); + +protected: + vtkExtractPoints(); + ~vtkExtractPoints(); + + vtkImplicitFunction *ImplicitFunction; + bool ExtractInside; + + // All derived classes must implement this method. Note that a side effect of + // the class is to populate the PointMap. Zero is returned if there is a failure. + virtual int FilterPoints(vtkPointSet *input); + +private: + vtkExtractPoints(const vtkExtractPoints&) VTK_DELETE_FUNCTION; + void operator=(const vtkExtractPoints&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkExtractSurface.cxx b/Filters/Points/vtkExtractSurface.cxx new file mode 100644 index 00000000000..4e32fa39855 --- /dev/null +++ b/Filters/Points/vtkExtractSurface.cxx @@ -0,0 +1,1423 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractSurface.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkExtractSurface.h" + +#include "vtkMath.h" +#include "vtkImageData.h" +#include "vtkCellArray.h" +#include "vtkInformation.h" +#include "vtkInformationIntegerVectorKey.h" +#include "vtkInformationVector.h" +#include "vtkObjectFactory.h" +#include "vtkPointData.h" +#include "vtkPolyData.h" +#include "vtkFloatArray.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkMarchingCubesTriangleCases.h" +#include "vtkSMPTools.h" + +#include <cmath> + +vtkStandardNewMacro(vtkExtractSurface); + + +//---------------------------------------------------------------------------- + +// This templated class implements the heart of the algorithm. +// vtkExtractSurface populates the information in this class and +// then invokes Contour() to actually initiate execution. +template <class T> +class vtkExtractSurfaceAlgorithm +{ +public: + // Edge case table values. + enum EdgeClass { + Below = 0, //below isovalue + Above = 1, //above isovalue + LeftAbove = 1, //left vertex is above isovalue + RightAbove = 2, //right vertex is above isovalue + BothAbove = 3, //entire edge is above isovalue + Empty = 4 //undefined edges should not be processed + }; + + // Dealing with boundary situations when processing volumes. + enum CellClass { + Interior = 0, + MinBoundary = 1, + MaxBoundary = 2 + }; + + // Edge-based case table to generate output triangle primitives. It is + // equivalent to the vertex-based Marching Cubes case table but provides + // several computational advantages (parallel separability, more efficient + // computation). This table is built from the MC case table when the class + // is instantiated. + unsigned char EdgeCases[256][16]; + + // A table to map old edge ids (as defined from vtkMarchingCubesCases) into + // the edge-based case table. This is so that the existing Marching Cubes + // case tables can be reused. + static const unsigned char EdgeMap[12]; + + // A table that lists voxel point ids as a function of edge ids (edge ids + // for edge-based case table). + static const unsigned char VertMap[12][2]; + + // A table describing vertex offsets (in index space) from the cube axes + // origin for each of the eight vertices of a voxel. + static const unsigned char VertOffsets[8][3]; + + // This table is used to accelerate the generation of output triangles and + // points. The EdgeUses array, a function of the voxel case number, + // indicates which voxel edges intersect with the contour (i.e., require + // interpolation). This array is filled in at instantiation during the case + // table generation process. + unsigned char EdgeUses[256][12]; + + // Flags indicate whether a particular case requires voxel axes to be + // processed. A cheap acceleration structure computed from the case + // tables at the point of instantiation. + unsigned char IncludesAxes[256]; + + // Algorithm-derived data. XCases tracks the x-row edge cases. The + // EdgeMetaData tracks information needed for parallel partitioning, + // and to enable generation of the output primitives without using + // a point locator. + unsigned char *XCases; + vtkIdType *EdgeMetaData; + + // Internal variables used by the various algorithm methods. Interfaces VTK + // image data in a form more convenient to the algorithm. + T *Scalars; + double Radius; + vtkIdType Dims[3]; + double Origin[3]; + double Spacing[3]; + vtkIdType NumberOfEdges; + vtkIdType SliceOffset; + int Min0; + int Max0; + int Inc0; + int Min1; + int Max1; + int Inc1; + int Min2; + int Max2; + int Inc2; + + // Output data. Threads write to partitioned memory. + vtkIdType *NewTris; + float *NewPoints; + float *NewGradients; + float *NewNormals; + bool NeedGradients; + + // Setup algorithm + vtkExtractSurfaceAlgorithm(); + + // Adjust the origin to the lower-left corner of the volume (if necessary) + void AdjustOrigin() + { + this->Origin[0] = this->Origin[0] + this->Spacing[0]*this->Min0; + this->Origin[1] = this->Origin[1] + this->Spacing[1]*this->Min1; + this->Origin[2] = this->Origin[2] + this->Spacing[2]*this->Min2;; + } + + // The three main passes of the algorithm. + void ProcessXEdge(double value, T const * const inPtr, vtkIdType row, vtkIdType slice); //PASS 1 + void ProcessYZEdges(vtkIdType row, vtkIdType slice); //PASS 2 + void GenerateOutput(double value, T* inPtr, vtkIdType row, vtkIdType slice);//PASS 3 + + // Used to extract the edge case separate from the state of the edge. The state is + // one of three values: NEAR, EMPTY, or UNSEEN. The state refers to the relationship + // of the edge to the signed distance function. + unsigned char EdgeCase(unsigned char eCase) + { + return (eCase & vtkExtractSurfaceAlgorithm::BothAbove); + } + + // Place holder for now in case fancy bit fiddling is needed later. + void SetXEdge(unsigned char *ePtr, unsigned char edgeCase) + {*ePtr = edgeCase;} + + // Given the four x-edge cases defining this voxel, return the voxel case + // number. + unsigned char GetEdgeCase(unsigned char *ePtr[4]) + { + return ( this->EdgeCase(*(ePtr[0])) | (this->EdgeCase(*(ePtr[1]))<<2) | + (this->EdgeCase(*(ePtr[2]))<<4) | (this->EdgeCase(*(ePtr[3]))<<6) ); + } + + // Given the four x-edge cases defining this voxel, indicate whether the voxel is + // valid and primitives are to be generated. This method needs to refer to the + // state of the edge. + bool GeneratePrimitives(unsigned char *ePtr[4]) + { + if ( *ePtr[0] >= vtkExtractSurfaceAlgorithm::Empty || + *ePtr[1] >= vtkExtractSurfaceAlgorithm::Empty || + *ePtr[2] >= vtkExtractSurfaceAlgorithm::Empty || + *ePtr[3] >= vtkExtractSurfaceAlgorithm::Empty ) + { + return false; + } + else + { + return true; + } + } + + // Return the number of contouring primitives for a particular edge case number. + unsigned char GetNumberOfPrimitives(unsigned char eCase) + { return this->EdgeCases[eCase][0]; } + + // Return an array indicating which voxel edges intersect the contour. + unsigned char *GetEdgeUses(unsigned char eCase) + { return this->EdgeUses[eCase]; } + + // Indicate whether voxel axes need processing for this case. + unsigned char CaseIncludesAxes(unsigned char eCase) + { return this->IncludesAxes[eCase]; } + + // Count edge intersections near volume boundaries. + void CountBoundaryYZInts(unsigned char loc, unsigned char *edgeCases, + vtkIdType *eMD[4]); + + // Produce the output triangles for this voxel cell. + void GenerateTris(unsigned char eCase, unsigned char numTris, vtkIdType *eIds, + vtkIdType &triId) + { + vtkIdType *tri; + const unsigned char *edges = this->EdgeCases[eCase] + 1; + for (int i=0; i < numTris; ++i, edges+=3) + { + tri = this->NewTris + 4*triId++; + tri[0] = 3; + tri[1] = eIds[edges[0]]; + tri[2] = eIds[edges[1]]; + tri[3] = eIds[edges[2]]; + } + } + + // Compute gradient on interior point. + void ComputeGradient(unsigned char loc, vtkIdType ijk[3], + T const * const s0_start, T const * const s0_end, + T const * const s1_start, T const * const s1_end, + T const * const s2_start, T const * const s2_end, + float g[3]) + { + if ( loc == Interior ) + { + g[0] = 0.5*( (*s0_start - *s0_end) / this->Spacing[0] ); + g[1] = 0.5*( (*s1_start - *s1_end) / this->Spacing[1] ); + g[2] = 0.5*( (*s2_start - *s2_end) / this->Spacing[2] ); + } + else + { + this->ComputeBoundaryGradient(ijk, + s0_start, s0_end, + s1_start, s1_end, + s2_start, s2_end, + g); + } + } + + + // Interpolate along a voxel axes edge. + void InterpolateAxesEdge(double t, unsigned char loc, + float x0[3], + T const * const s, + const int incs[3], + float x1[3], + vtkIdType vId, + vtkIdType ijk[3], + float g0[3]) + { + + float *x = this->NewPoints + 3*vId; + x[0] = x0[0] + t*(x1[0]-x0[0]); + x[1] = x0[1] + t*(x1[1]-x0[1]); + x[2] = x0[2] + t*(x1[2]-x0[2]); + + if ( this->NeedGradients ) + { + float gTmp[3], g1[3]; + this->ComputeGradient(loc,ijk, + s + incs[0], s - incs[0], + s + incs[1], s - incs[1], + s + incs[2], s - incs[2], + g1); + + float *g = ( this->NewGradients ? this->NewGradients + 3*vId : gTmp ); + g[0] = g0[0] + t*(g1[0]-g0[0]); + g[1] = g0[1] + t*(g1[1]-g0[1]); + g[2] = g0[2] + t*(g1[2]-g0[2]); + + if ( this->NewNormals ) + { + float *n = this->NewNormals + 3*vId; + n[0] = -g[0]; + n[1] = -g[1]; + n[2] = -g[2]; + vtkMath::Normalize(n); + } + }//if normals or gradients required + } + + // Compute the gradient on a point which may be on the boundary of the volume. + void ComputeBoundaryGradient(vtkIdType ijk[3], + T const * const s0_start, T const * const s0_end, + T const * const s1_start, T const * const s1_end, + T const * const s2_start, T const * const s2_end, + float g[3]); + + // Interpolate along an arbitrary edge, typically one that may be on the + // volume boundary. This means careful computation of stuff requiring + // neighborhood information (e.g., gradients). + void InterpolateEdge(double value, vtkIdType ijk[3], + T const * const s, const int incs[3], + float x[3], + unsigned char edgeNum, + unsigned char const* const edgeUses, + vtkIdType *eIds); + + // Produce the output points on the voxel axes for this voxel cell. + void GeneratePoints(double value, unsigned char loc, vtkIdType ijk[3], + T const * const sPtr, const int incs[3], + float x[3], unsigned char const * const edgeUses, + vtkIdType *eIds); + + // Helper function to set up the point ids on voxel edges. + unsigned char InitVoxelIds(unsigned char *ePtr[4], vtkIdType *eMD[4], + vtkIdType *eIds) + { + unsigned char eCase = this->GetEdgeCase(ePtr); + eIds[0] = eMD[0][0]; //x-edges + eIds[1] = eMD[1][0]; + eIds[2] = eMD[2][0]; + eIds[3] = eMD[3][0]; + eIds[4] = eMD[0][1]; //y-edges + eIds[5] = eIds[4] + this->EdgeUses[eCase][4]; + eIds[6] = eMD[2][1]; + eIds[7] = eIds[6] + this->EdgeUses[eCase][6]; + eIds[8] = eMD[0][2]; //z-edges + eIds[9] = eIds[8] + this->EdgeUses[eCase][8]; + eIds[10] = eMD[1][2]; + eIds[11] = eIds[10] + this->EdgeUses[eCase][10]; + return eCase; + } + + // Helper function to advance the point ids along voxel rows. + void AdvanceVoxelIds(unsigned char eCase, vtkIdType *eIds) + { + eIds[0] += this->EdgeUses[eCase][0]; //x-edges + eIds[1] += this->EdgeUses[eCase][1]; + eIds[2] += this->EdgeUses[eCase][2]; + eIds[3] += this->EdgeUses[eCase][3]; + eIds[4] += this->EdgeUses[eCase][4]; //y-edges + eIds[5] = eIds[4] + this->EdgeUses[eCase][5]; + eIds[6] += this->EdgeUses[eCase][6]; + eIds[7] = eIds[6] + this->EdgeUses[eCase][7]; + eIds[8] += this->EdgeUses[eCase][8]; //z-edges + eIds[9] = eIds[8] + this->EdgeUses[eCase][9]; + eIds[10] += this->EdgeUses[eCase][10]; + eIds[11] = eIds[10] + this->EdgeUses[eCase][11]; + } + + // Threading integration via SMPTools + template <class TT> class Pass1 + { + public: + vtkExtractSurfaceAlgorithm<TT> *Algo; + double Value; + Pass1(vtkExtractSurfaceAlgorithm<TT> *algo, double value) + {this->Algo = algo; this->Value = value;} + void operator()(vtkIdType slice, vtkIdType end) + { + vtkIdType row; + TT *rowPtr, *slicePtr = this->Algo->Scalars + slice*this->Algo->Inc2; + for ( ; slice < end; ++slice ) + { + for (row=0, rowPtr=slicePtr; row < this->Algo->Dims[1]; ++row) + { + this->Algo->ProcessXEdge(this->Value, rowPtr, row, slice); + rowPtr += this->Algo->Inc1; + }//for all rows in this slice + slicePtr += this->Algo->Inc2; + }//for all slices in this batch + } + }; + template <class TT> class Pass2 + { + public: + Pass2(vtkExtractSurfaceAlgorithm<TT> *algo) + {this->Algo = algo;} + vtkExtractSurfaceAlgorithm<TT> *Algo; + void operator()(vtkIdType slice, vtkIdType end) + { + for ( ; slice < end; ++slice) + { + for ( vtkIdType row=0; row < (this->Algo->Dims[1]-1); ++row) + { + this->Algo->ProcessYZEdges(row, slice); + }//for all rows in this slice + }//for all slices in this batch + } + }; + template <class TT> class Pass4 + { + public: + Pass4(vtkExtractSurfaceAlgorithm<TT> *algo, double value) + {this->Algo = algo; this->Value = value;} + vtkExtractSurfaceAlgorithm<TT> *Algo; + double Value; + void operator()(vtkIdType slice, vtkIdType end) + { + vtkIdType row; + vtkIdType *eMD0 = this->Algo->EdgeMetaData + slice*6*this->Algo->Dims[1]; + vtkIdType *eMD1 = eMD0 + 6*this->Algo->Dims[1]; + TT *rowPtr, *slicePtr = this->Algo->Scalars + slice*this->Algo->Inc2; + for ( ; slice < end; ++slice ) + { + // It's possible to skip entire slices if there is nothing to generate + if ( eMD1[3] > eMD0[3] ) //there are triangle primitives! + { + for (row=0, rowPtr=slicePtr; row < this->Algo->Dims[1]-1; ++row) + { + this->Algo->GenerateOutput(this->Value, rowPtr, row, slice); + rowPtr += this->Algo->Inc1; + }//for all rows in this slice + }//if there are triangles + slicePtr += this->Algo->Inc2; + eMD0 = eMD1; + eMD1 = eMD0 + 6*this->Algo->Dims[1]; + }//for all slices in this batch + } + }; + + // Interface between VTK and templated functions + static void Contour(vtkExtractSurface *self, vtkImageData *input, + int extent[6], vtkIdType *incs, T *scalars, + vtkPoints *newPts, vtkCellArray *newTris, + vtkFloatArray *newNormals, vtkFloatArray *newGradients); +}; + +//---------------------------------------------------------------------------- +// Map MC edges numbering to use the saner FlyingEdges edge numbering scheme. +template <class T> const unsigned char vtkExtractSurfaceAlgorithm<T>:: +EdgeMap[12] = {0,5,1,4,2,7,3,6,8,9,10,11}; + +//---------------------------------------------------------------------------- +// Map MC edges numbering to use the saner FlyingEdges edge numbering scheme. +template <class T> const unsigned char vtkExtractSurfaceAlgorithm<T>:: +VertMap[12][2] = {{0,1}, {2,3}, {4,5}, {6,7}, {0,2}, {1,3}, {4,6}, {5,7}, + {0,4}, {1,5}, {2,6}, {3,7}}; + +//---------------------------------------------------------------------------- +// The offsets of each vertex (in index space) from the voxel axes origin. +template <class T> const unsigned char vtkExtractSurfaceAlgorithm<T>:: +VertOffsets[8][3] = {{0,0,0}, {1,0,0}, {0,1,0}, {1,1,0}, + {0,0,1}, {1,0,1}, {0,1,1}, {1,1,1}}; + +//---------------------------------------------------------------------------- +// Instantiate and initialize key data members. Mostly we build the +// edge-based case table, and associated acceleration structures, from the +// marching cubes case table. Some of this code is borrowed shamelessly from +// vtkVoxel::Contour() method. +template <class T> vtkExtractSurfaceAlgorithm<T>:: +vtkExtractSurfaceAlgorithm():XCases(NULL),EdgeMetaData(NULL),NewTris(NULL), + NewPoints(NULL),NewGradients(NULL),NewNormals(NULL) +{ + int i, j, k, l, ii, eCase, index, numTris; + static int vertMap[8] = {0,1,3,2,4,5,7,6}; + static int CASE_MASK[8] = {1,2,4,8,16,32,64,128}; + EDGE_LIST *edge; + vtkMarchingCubesTriangleCases *triCase; + unsigned char *edgeCase; + + // Initialize cases, increments, and edge intersection flags + for (eCase=0; eCase<256; ++eCase) + { + for (j=0; j<16; ++j) + { + this->EdgeCases[eCase][j] = 0; + } + for (j=0; j<12; ++j) + { + this->EdgeUses[eCase][j] = 0; + } + this->IncludesAxes[eCase] = 0; + } + + // The voxel, edge-based case table is a function of the four x-edge cases + // that define the voxel. Here we convert the existing MC vertex-based case + // table into a x-edge case table. Note that the four x-edges are ordered + // (0->3): x, x+y, x+z, x+y+z; the four y-edges are ordered (4->7): y, y+x, + // y+z, y+x+z; and the four z-edges are ordered (8->11): z, z+x, z+y, + // z+x+y. + for (l=0; l<4; ++l) + { + for (k=0; k<4; ++k) + { + for (j=0; j<4; ++j) + { + for (i=0; i<4; ++i) + { + //yes we could just count to (0->255) but where's the fun in that? + eCase = i | (j<<2) | (k<<4) | (l<<6); + for ( ii=0, index = 0; ii < 8; ++ii) + { + if ( eCase & (1<<vertMap[ii]) ) //map into ancient MC table + { + index |= CASE_MASK[ii]; + } + } + //Now build case table + triCase = vtkMarchingCubesTriangleCases::GetCases() + index; + edge = triCase->edges; + for ( numTris=0, edge=triCase->edges; edge[0] > -1; edge += 3 ) + {//count the number of triangles + numTris++; + } + if ( numTris > 0 ) + { + edgeCase = this->EdgeCases[eCase]; + *edgeCase++ = numTris; + for ( edge = triCase->edges; edge[0] > -1; edge += 3, edgeCase+=3 ) + { + // Build new case table. + edgeCase[0] = this->EdgeMap[edge[0]]; + edgeCase[1] = this->EdgeMap[edge[1]]; + edgeCase[2] = this->EdgeMap[edge[2]]; + } + } + }//x-edges + }//x+y-edges + }//x+z-edges + }//x+y+z-edges + + // Okay now build the acceleration structure. This is used to generate + // output points and triangles when processing a voxel x-row as well as to + // perform other topological reasoning. This structure is a function of the + // particular case number. + for (eCase=0; eCase < 256; ++eCase) + { + edgeCase = this->EdgeCases[eCase]; + numTris = *edgeCase++; + + // Mark edges that are used by this case. + for (i=0; i < numTris*3; ++i) //just loop over all edges + { + this->EdgeUses[eCase][edgeCase[i]] = 1; + } + + this->IncludesAxes[eCase] = this->EdgeUses[eCase][0] | + this->EdgeUses[eCase][4] | this->EdgeUses[eCase][8]; + + }//for all cases +} + +//---------------------------------------------------------------------------- +// Count intersections along voxel axes. When traversing the volume across +// x-edges, the voxel axes on the boundary may be undefined near boundaries +// (because there are no fully-formed cells). Thus the voxel axes on the +// boundary are treated specially. +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +CountBoundaryYZInts(unsigned char loc, unsigned char *edgeUses, + vtkIdType *eMD[4]) +{ + switch (loc) + { + case 2: //+x boundary + eMD[0][1] += edgeUses[5]; + eMD[0][2] += edgeUses[9]; + break; + case 8: //+y + eMD[1][2] += edgeUses[10]; + break; + case 10://+x +y + eMD[0][1] += edgeUses[5]; + eMD[0][2] += edgeUses[9]; + eMD[1][2] += edgeUses[10]; + eMD[1][2] += edgeUses[11]; + break; + case 32://+z + eMD[2][1] += edgeUses[6]; + break; + case 34: //+x +z + eMD[0][1] += edgeUses[5]; + eMD[0][2] += edgeUses[9]; + eMD[2][1] += edgeUses[6]; + eMD[2][1] += edgeUses[7]; + break; + case 40: //+y +z + eMD[2][1] += edgeUses[6]; + eMD[1][2] += edgeUses[10]; + break; + case 42: //+x +y +z happens no more than once per volume + eMD[0][1] += edgeUses[5]; + eMD[0][2] += edgeUses[9]; + eMD[1][2] += edgeUses[10]; + eMD[1][2] += edgeUses[11]; + eMD[2][1] += edgeUses[6]; + eMD[2][1] += edgeUses[7]; + break; + default: //uh-oh shouldn't happen + break; + } +} + +//---------------------------------------------------------------------------- +// Compute the gradient when the point may be near the boundary of the +// volume. +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +ComputeBoundaryGradient(vtkIdType ijk[3], + T const * const s0_start, T const * const s0_end, + T const * const s1_start, T const * const s1_end, + T const * const s2_start, T const * const s2_end, + float g[3]) +{ + const T* s = s0_start - this->Inc0; + + if ( ijk[0] == 0 ) + { + g[0] = (*s0_start - *s) / this->Spacing[0]; + } + else if ( ijk[0] >= (this->Dims[0]-1) ) + { + g[0] = (*s - *s0_end) / this->Spacing[0]; + } + else + { + g[0] = 0.5 * ( (*s0_start - *s0_end) / this->Spacing[0] ); + } + + if ( ijk[1] == 0 ) + { + g[1] = (*s1_start - *s) / this->Spacing[1]; + } + else if ( ijk[1] >= (this->Dims[1]-1) ) + { + g[1] = (*s - *s1_end) / this->Spacing[1]; + } + else + { + g[1] = 0.5 * ( (*s1_start - *s1_end) / this->Spacing[1] ); + } + + if ( ijk[2] == 0 ) + { + g[2] = (*s2_start - *s) / this->Spacing[2]; + } + else if ( ijk[2] >= (this->Dims[2]-1) ) + { + g[2] = (*s - *s2_end) / this->Spacing[2]; + } + else + { + g[2] = 0.5 * ( (*s2_start - *s2_end) / this->Spacing[2] ); + } +} + +//---------------------------------------------------------------------------- +// Interpolate a new point along a boundary edge. Make sure to consider +// proximity to the boundary when computing gradients, etc. +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +InterpolateEdge(double value, vtkIdType ijk[3], + T const * const s, + const int incs[3], + float x[3], + unsigned char edgeNum, + unsigned char const * const edgeUses, + vtkIdType *eIds) +{ + // if this edge is not used then get out + if ( ! edgeUses[edgeNum] ) + { + return; + } + + // build the edge information + const unsigned char *vertMap = this->VertMap[edgeNum]; + + float x0[3], x1[3]; + vtkIdType ijk0[3], ijk1[3], vId=eIds[edgeNum]; + int i; + + const unsigned char *offsets = this->VertOffsets[vertMap[0]]; + T const * const s0 = s + offsets[0]*incs[0] + + offsets[1]*incs[1] + + offsets[2]*incs[2]; + for (i=0; i<3; ++i) + { + ijk0[i] = ijk[i] + offsets[i]; + x0[i] = x[i] + offsets[i]*this->Spacing[i]; + } + + offsets = this->VertOffsets[vertMap[1]]; + T const * const s1 = s + offsets[0]*incs[0] + + offsets[1]*incs[1] + + offsets[2]*incs[2]; + for (i=0; i<3; ++i) + { + ijk1[i] = ijk[i] + offsets[i]; + x1[i] = x[i] + offsets[i]*this->Spacing[i]; + } + + // Okay interpolate + double t = (value - *s0) / (*s1 - *s0); + float *xPtr = this->NewPoints + 3*vId; + xPtr[0] = x0[0] + t*(x1[0]-x0[0]); + xPtr[1] = x0[1] + t*(x1[1]-x0[1]); + xPtr[2] = x0[2] + t*(x1[2]-x0[2]); + + if ( this->NeedGradients ) + { + float gTmp[3], g0[3], g1[3]; + this->ComputeBoundaryGradient(ijk0, + s0+incs[0], s0-incs[0], + s0+incs[1], s0-incs[1], + s0+incs[2], s0-incs[2], + g0); + this->ComputeBoundaryGradient(ijk1, + s1+incs[0], s1-incs[0], + s1+incs[1], s1-incs[1], + s1+incs[2], s1-incs[2], + g1); + + float *g = ( this->NewGradients ? this->NewGradients + 3*vId : gTmp ); + g[0] = g0[0] + t*(g1[0]-g0[0]); + g[1] = g0[1] + t*(g1[1]-g0[1]); + g[2] = g0[2] + t*(g1[2]-g0[2]); + + if ( this->NewNormals ) + { + float *n = this->NewNormals + 3*vId; + n[0] = -g[0]; + n[1] = -g[1]; + n[2] = -g[2]; + vtkMath::Normalize(n); + } + }//if normals or gradients required +} + +//---------------------------------------------------------------------------- +// Generate the output points and optionally normals, gradients and +// interpolate attributes. +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +GeneratePoints(double value, unsigned char loc, vtkIdType ijk[3], + T const * const sPtr, const int incs[3], + float x[3], + unsigned char const * const edgeUses, + vtkIdType *eIds) +{ + // Create a slightly faster path for voxel axes interior to the volume. + float g0[3]; + if ( this->NeedGradients ) + { + this->ComputeGradient(loc,ijk, + sPtr + incs[0], sPtr - incs[0], + sPtr + incs[1], sPtr - incs[1], + sPtr + incs[2], sPtr - incs[2], + g0); + } + + // Interpolate the cell axes edges + for(int i=0; i < 3; ++i) + { + if(edgeUses[i*4]) + { + //edgesUses[0] == x axes edge + //edgesUses[4] == y axes edge + //edgesUses[8] == z axes edge + float x1[3] = {x[0], x[1], x[2] }; x1[i] += this->Spacing[i]; + vtkIdType ijk1[3] = { ijk[0], ijk[1], ijk[2] }; ++ijk1[i]; + + T const * const sPtr2 = (sPtr+incs[i]); + double t = (value - *sPtr) / (*sPtr2 - *sPtr); + this->InterpolateAxesEdge(t, loc, x, sPtr2, incs, x1, eIds[i*4], ijk1, g0); + } + } + + // On the boundary cells special work has to be done to cover the partial + // cell axes. These are boundary situations where the voxel axes is not + // fully formed. These situations occur on the +x,+y,+z volume + // boundaries. (The other cases fall through the default: case which is + // expected.) + // + // Note that loc is one of 27 regions in the volume, with (0,1,2) + // indicating (interior, min, max) along coordinate axes. + switch (loc) + { + case 2: case 6: case 18: case 22: //+x + this->InterpolateEdge(value, ijk, sPtr, incs, x, 5, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 9, edgeUses, eIds); + break; + case 8: case 9: case 24: case 25: //+y + this->InterpolateEdge(value, ijk, sPtr, incs, x, 1, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 10, edgeUses, eIds); + break; + case 32: case 33: case 36: case 37: //+z + this->InterpolateEdge(value, ijk, sPtr, incs, x, 2, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 6, edgeUses, eIds); + break; + case 10: case 26: //+x +y + this->InterpolateEdge(value, ijk, sPtr, incs, x, 1, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 5, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 9, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 10, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 11, edgeUses, eIds); + break; + case 34: case 38: //+x +z + this->InterpolateEdge(value, ijk, sPtr, incs, x, 2, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 5, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 9, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 6, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 7, edgeUses, eIds); + break; + case 40: case 41: //+y +z + this->InterpolateEdge(value, ijk, sPtr, incs, x, 1, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 2, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 3, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 6, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 10, edgeUses, eIds); + break; + case 42: //+x +y +z happens no more than once per volume + this->InterpolateEdge(value, ijk, sPtr, incs, x, 1, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 2, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 3, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 5, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 9, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 10, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 11, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 6, edgeUses, eIds); + this->InterpolateEdge(value, ijk, sPtr, incs, x, 7, edgeUses, eIds); + break; + default: //interior, or -x,-y,-z boundaries + return; + } +} + +//---------------------------------------------------------------------------- +// PASS 1: Process a single volume x-row (and all of the voxel edges that +// compose the row). Determine the x-edges case classification, count the +// number of x-edge intersections, and figure out where intersections along +// the x-row begins and ends (i.e., gather information for computational +// trimming). +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +ProcessXEdge(double value, T const* const inPtr, vtkIdType row, vtkIdType slice) +{ + vtkIdType nxcells=this->Dims[0]-1; + vtkIdType minInt=nxcells, maxInt = 0; + vtkIdType *edgeMetaData; + unsigned char *ePtr = this->XCases + slice*this->SliceOffset + row*nxcells; + double s0, s1 = static_cast<double>(*inPtr); + double radius=this->Radius; + + //run along the entire x-edge computing edge cases + edgeMetaData = this->EdgeMetaData + (slice*this->Dims[1] + row)*6; + std::fill_n(edgeMetaData, 6, 0); + + vtkIdType sum = 0; + + //pull this out help reduce false sharing + vtkIdType inc0 = this->Inc0; + + for (vtkIdType i=0; i < nxcells; ++i, ++ePtr) + { + s0 = s1; + s1 = static_cast<double>(*(inPtr + (i+1)*inc0)); + + unsigned char edgeCase = vtkExtractSurfaceAlgorithm::Below; + if (s0 >= value) + { + edgeCase = vtkExtractSurfaceAlgorithm::LeftAbove; + } + if( s1 >= value) + { + edgeCase |= vtkExtractSurfaceAlgorithm::RightAbove; + } + + // if edge intersects contour + if ( edgeCase == vtkExtractSurfaceAlgorithm::LeftAbove || + edgeCase == vtkExtractSurfaceAlgorithm::RightAbove ) + { + ++sum; //increment number of intersections along x-edge + minInt = ( i < minInt ? i : minInt); + maxInt = i + 1; + }//if contour interacts with this x-edge + + if ( s0 >= radius || s1 >= radius ) + { + edgeCase |= vtkExtractSurfaceAlgorithm::Empty; + } + this->SetXEdge(ePtr, edgeCase); + }//for all x-cell edges along this x-edge + + edgeMetaData[0] += sum; //write back the number of intersections along x-edge + + // The beginning and ending of intersections along the edge is used for + // computational trimming. + edgeMetaData[4] = minInt; //where intersections start along x edge + edgeMetaData[5] = maxInt; //where intersections end along x edge +} + +//---------------------------------------------------------------------------- +// PASS 2: Process a single x-row of voxels. Count the number of y- and +// z-intersections by topological reasoning from x-edge cases. Determine the +// number of primitives (i.e., triangles) generated from this row. Use +// computational trimming to reduce work. Note *ePtr[4] is four pointers to +// four x-edge rows that bound the voxel x-row and which contain edge case +// information. +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +ProcessYZEdges(vtkIdType row, vtkIdType slice) +{ + // Grab the four edge cases bounding this voxel x-row. + unsigned char *ePtr[4], ec0, ec1, ec2, ec3, xInts=1; + ePtr[0] = this->XCases + slice*this->SliceOffset + row*(this->Dims[0]-1); + ePtr[1] = ePtr[0] + this->Dims[0]-1; + ePtr[2] = ePtr[0] + this->SliceOffset; + ePtr[3] = ePtr[2] + this->Dims[0]-1; + + // Grab the edge meta data surrounding the voxel row. + vtkIdType *eMD[4]; + eMD[0] = this->EdgeMetaData + (slice*this->Dims[1] + row)*6; //this x-edge + eMD[1] = eMD[0] + 6; //x-edge in +y direction + eMD[2] = eMD[0] + this->Dims[1]*6; //x-edge in +z direction + eMD[3] = eMD[2] + 6; //x-edge in +y+z direction + + // Determine whether this row of x-cells needs processing. If there are no + // x-edge intersections, and the state of the four bounding x-edges is the + // same, then there is no need for processing. + if ( (eMD[0][0] | eMD[1][0] | eMD[2][0] | eMD[3][0]) == 0 ) //any x-ints? + { + unsigned char eCase0 = this->EdgeCase(*ePtr[0]); + unsigned char eCase1 = this->EdgeCase(*ePtr[1]); + unsigned char eCase2 = this->EdgeCase(*ePtr[2]); + unsigned char eCase3 = this->EdgeCase(*ePtr[3]); + if ( eCase0 == eCase1 && eCase1 == eCase2 && eCase2 == eCase3 ) + { + return; //there are no y- or z-ints, thus no contour, skip voxel row + } + else + { + xInts = 0; //there are y- or z- edge ints however + } + } + + // Determine proximity to the boundary of volume. This information is used + // to count edge intersections in boundary situations. + unsigned char loc, yLoc, zLoc, yzLoc; + yLoc = (row >= (this->Dims[1]-2) ? MaxBoundary : Interior); + zLoc = (slice >= (this->Dims[2]-2) ? MaxBoundary : Interior); + yzLoc = (yLoc << 2) | (zLoc << 4); + + // The trim edges may need adjustment if the contour travels between rows + // of x-edges (without intersecting these x-edges). This means checking + // whether the trim faces at (xL,xR) made up of the y-z edges intersect the + // contour. Basically just an intersection operation. Determine the voxel + // row trim edges, need to check all four x-edges. + vtkIdType xL=eMD[0][4], xR=eMD[0][5]; + vtkIdType i; + if ( xInts ) + { + for (i=1; i < 4; ++i) + { + xL = ( eMD[i][4] < xL ? eMD[i][4] : xL); + xR = ( eMD[i][5] > xR ? eMD[i][5] : xR); + } + + if ( xL > 0 ) //if trimmed in the -x direction + { + ec0 = *(ePtr[0]+xL); ec1 = *(ePtr[1]+xL); + ec2 = *(ePtr[2]+xL); ec3 = *(ePtr[3]+xL); + if ( (ec0 & 0x1) != (ec1 & 0x1) || (ec1 & 0x1) != (ec2 & 0x1) || + (ec2 & 0x1) != (ec3 & 0x1) ) + { + xL = eMD[0][4] = 0; //reset left trim + } + } + + if ( xR < (this->Dims[0]-1) ) //if trimmed in the +x direction + { + ec0 = *(ePtr[0]+xR); ec1 = *(ePtr[1]+xR); + ec2 = *(ePtr[2]+xR); ec3 = *(ePtr[3]+xR); + if ( (ec0 & 0x2) != (ec1 & 0x2) || (ec1 & 0x2) != (ec2 & 0x2) || + (ec2 & 0x2) != (ec3 & 0x2) ) + { + xR = eMD[0][5] = this->Dims[0]-1; //reset right trim + } + } + } + else //contour cuts through without intersecting x-edges, reset trim edges + { + xL = eMD[0][4] = 0; + xR = eMD[0][5] = this->Dims[0]-1; + } + + // Okay run along the x-voxels and count the number of y- and + // z-intersections. Here we are just checking y,z edges that make up the + // voxel axes. Also check the number of primitives generated. + unsigned char *edgeUses, eCase, numTris; + ePtr[0] += xL; ePtr[1] += xL; ePtr[2] += xL; ePtr[3] += xL; + for (i=xL; i < xR; ++i) //run along the trimmed x-voxels + { + eCase = this->GetEdgeCase(ePtr); + if ( (numTris=this->GetNumberOfPrimitives(eCase)) > 0 ) + { + // Okay let's increment the triangle count. But only if the voxel + // is valid and primitives are to be generated. + if ( this->GeneratePrimitives(ePtr) ) + { + eMD[0][3] += numTris; + } + + // Count the number of y- and z-points to be generated. Pass# 1 counted + // the number of x-intersections along the x-edges. Now we count all + // intersections on the y- and z-voxel axes. + edgeUses = this->GetEdgeUses(eCase); + eMD[0][1] += edgeUses[4]; //y-voxel axes edge always counted + eMD[0][2] += edgeUses[8]; //z-voxel axes edge always counted + loc = yzLoc | (i >= (this->Dims[0]-2) ? MaxBoundary : Interior); + if ( loc != 0 ) + { + this->CountBoundaryYZInts(loc,edgeUses,eMD); + } + }//if cell contains contour + + // advance the four pointers along voxel row + ePtr[0]++; ePtr[1]++; ePtr[2]++; ePtr[3]++; + }//for all voxels along this x-edge +} + +//---------------------------------------------------------------------------- +// PASS 4: Process the x-row cells to generate output primitives, including +// point coordinates and triangles. This is the fourth and final pass of the +// algorithm. +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +GenerateOutput(double value, T* rowPtr, vtkIdType row, vtkIdType slice) +{ + // Grab the edge meta data surrounding the voxel row. + vtkIdType *eMD[4]; + eMD[0] = this->EdgeMetaData + (slice*this->Dims[1] + row)*6; //this x-edge + eMD[1] = eMD[0] + 6; //x-edge in +y direction + eMD[2] = eMD[0] + this->Dims[1]*6; //x-edge in +z direction + eMD[3] = eMD[2] + 6; //x-edge in +y+z direction + + // Return if there is nothing to do (i.e., no triangles to generate) + if ( eMD[0][3] == eMD[1][3] ) + { + return; + } + + // Get the voxel row trim edges and prepare to generate. Find the voxel row + // trim edges, need to check all four x-edges to compute row trim edge. + vtkIdType xL=eMD[0][4], xR=eMD[0][5]; + vtkIdType i; + for (i=1; i < 4; ++i) + { + xL = ( eMD[i][4] < xL ? eMD[i][4] : xL); + xR = ( eMD[i][5] > xR ? eMD[i][5] : xR); + } + + // Grab the four edge cases bounding this voxel x-row. Begin at left trim edge. + unsigned char *ePtr[4]; + ePtr[0] = this->XCases + slice*this->SliceOffset + row*(this->Dims[0]-1) + xL; + ePtr[1] = ePtr[0] + this->Dims[0]-1; + ePtr[2] = ePtr[0] + this->SliceOffset; + ePtr[3] = ePtr[2] + this->Dims[0]-1; + + // Traverse all voxels in this row, those containing the contour are + // further identified for processing, meaning generating points and + // triangles. Begin by setting up point ids on voxel edges. + vtkIdType triId = eMD[0][3]; + vtkIdType eIds[12]; //the ids of generated points + + unsigned char eCase = this->InitVoxelIds(ePtr,eMD,eIds); + + // Determine the proximity to the boundary of volume. This information is + // used to generate edge intersections. + unsigned char loc, yLoc, zLoc, yzLoc; + yLoc = (row < 1 ? MinBoundary : + (row >= (this->Dims[1]-2) ? MaxBoundary : Interior)); + zLoc = (slice < 1 ? MinBoundary : + (slice >= (this->Dims[2]-2) ? MaxBoundary : Interior)); + yzLoc = (yLoc << 2) | (zLoc << 4); + + // Run along voxels in x-row direction and generate output primitives. Note + // that active voxel axes edges are interpolated to produce points and + // possibly interpolate attribute data. + float x[3]; + x[0] = this->Origin[0] + xL*this->Spacing[0]; + x[1] = this->Origin[1] + row*this->Spacing[1]; + x[2] = this->Origin[2] + slice*this->Spacing[2]; + + //compute the ijk for this section + vtkIdType ijk[3] = { xL, row, slice}; + + //load the inc0/inc1/inc2 into local memory + const int incs[3] = { this->Inc0, this->Inc1, this->Inc2 }; + const T* sPtr = rowPtr + xL*incs[0]; + + for (i=xL; i < xR; ++i) + { + const unsigned char numTris = this->GetNumberOfPrimitives(eCase); + if ( numTris > 0 ) + { + // Start by generating triangles for this case + if ( this->GeneratePrimitives(ePtr) ) + { + this->GenerateTris(eCase,numTris,eIds,triId); + } + + // Now generate point(s) along voxel axes if needed. Remember to take + // boundary into account. + loc = yzLoc | (i < 1 ? MinBoundary : + (i >= (this->Dims[0]-2) ? MaxBoundary : Interior)); + if ( this->CaseIncludesAxes(eCase) || loc != Interior ) + { + unsigned char const * const edgeUses = this->GetEdgeUses(eCase); + this->GeneratePoints(value, loc, ijk, + sPtr, incs, + x, edgeUses, eIds); + } + this->AdvanceVoxelIds(eCase,eIds); + } + + // advance along voxel row + ePtr[0]++; ePtr[1]++; ePtr[2]++; ePtr[3]++; + eCase = this->GetEdgeCase(ePtr); + + ++ijk[0]; + sPtr += incs[0]; + x[0]+= this->Spacing[0]; + } //for all non-trimmed cells along this x-edge +} + +//---------------------------------------------------------------------------- +// Contouring filter specialized for 3D volumes. This templated function +// interfaces the vtkExtractSurface class with the templated algorithm +// class. It also invokes the three passes of the Flying Edges algorithm. +template <class T> void vtkExtractSurfaceAlgorithm<T>:: +Contour(vtkExtractSurface *self, vtkImageData *input, int extent[6], + vtkIdType *incs, T *scalars, vtkPoints *newPts, vtkCellArray *newTris, + vtkFloatArray *newNormals, vtkFloatArray *newGradients) +{ + double value; + int numContours = 1; + vtkIdType vidx, row, slice, *eMD, zInc; + vtkIdType numOutXPts, numOutYPts, numOutZPts, numOutTris; + vtkIdType numXPts=0, numYPts=0, numZPts=0, numTris=0; + vtkIdType startXPts, startYPts, startZPts, startTris; + startXPts = startYPts = startZPts = startTris = 0; + + // This may be subvolume of the total 3D image. Capture information for + // subsequent processing. + vtkExtractSurfaceAlgorithm<T> algo; + algo.Scalars = scalars; + algo.Radius = self->GetRadius(); + input->GetOrigin(algo.Origin); + input->GetSpacing(algo.Spacing); + algo.Min0 = extent[0]; + algo.Max0 = extent[1]; + algo.Inc0 = incs[0]; + algo.Min1 = extent[2]; + algo.Max1 = extent[3]; + algo.Inc1 = incs[1]; + algo.Min2 = extent[4]; + algo.Max2 = extent[5]; + algo.Inc2 = incs[2]; + algo.AdjustOrigin(); + + // Now allocate working arrays. The XCases array tracks x-edge cases. + algo.Dims[0] = algo.Max0 - algo.Min0 + 1; + algo.Dims[1] = algo.Max1 - algo.Min1 + 1; + algo.Dims[2] = algo.Max2 - algo.Min2 + 1; + algo.NumberOfEdges = algo.Dims[1]*algo.Dims[2]; + algo.SliceOffset = (algo.Dims[0]-1) * algo.Dims[1]; + algo.XCases = new unsigned char [(algo.Dims[0]-1)*algo.NumberOfEdges]; + + // Also allocate the characterization (metadata) array for the x edges. + // This array tracks the number of x-, y- and z- intersections on the voxel + // axes along an x-edge; as well as the number of the output triangles, and + // the xMin_i and xMax_i (minimum index of first intersection, maximum + // index of intersection for the ith x-row, the so-called trim edges used + // for computational trimming). + algo.EdgeMetaData = new vtkIdType [algo.NumberOfEdges*6]; + + // Loop across each contour value. This encompasses all three passes. + for (vidx = 0; vidx < numContours; vidx++) + { + value = 0.0; //single pass, zero-crossing isosurface + + // PASS 1: Traverse all x-rows building edge cases and counting number of + // intersections (i.e., accumulate information necessary for later output + // memory allocation, e.g., the number of output points along the x-rows + // are counted). + Pass1<T> pass1(&algo,value); + vtkSMPTools::For(0,algo.Dims[2], pass1); + + // PASS 2: Traverse all voxel x-rows and process voxel y&z edges. The + // result is a count of the number of y- and z-intersections, as well as + // the number of triangles generated along these voxel rows. + Pass2<T> pass2(&algo); + vtkSMPTools::For(0,algo.Dims[2]-1, pass2); + + // PASS 3: Now allocate and generate output. First we have to update the + // edge meta data to partition the output into separate pieces so + // independent threads can write without collisions. Once allocation is + // complete, the volume is processed on a voxel row by row basis to + // produce output points and triangles, and interpolate point attribute + // data (as necessary). NOTE: This implementation is serial. It is + // possible to use a threaded prefix sum to make it even faster. Since + // this pass usually takes a small amount of time, we choose simplicity + // over performance. + numOutXPts = startXPts; + numOutYPts = startYPts; + numOutZPts = startZPts; + numOutTris = startTris; + + // Count number of points and tris generate along each cell row + for (slice=0; slice < algo.Dims[2]; ++slice) + { + zInc = slice * algo.Dims[1]; + for (row=0; row < algo.Dims[1]; ++row) + { + eMD = algo.EdgeMetaData + (zInc+row)*6; + numXPts = eMD[0]; + numYPts = eMD[1]; + numZPts = eMD[2]; + numTris = eMD[3]; + eMD[0] = numOutXPts + numOutYPts + numOutZPts; + eMD[1] = eMD[0] + numXPts; + eMD[2] = eMD[1] + numYPts; + eMD[3] = numOutTris; + numOutXPts += numXPts; + numOutYPts += numYPts; + numOutZPts += numZPts; + numOutTris += numTris; + } + } + + // Output can now be allocated. + vtkIdType totalPts = numOutXPts + numOutYPts + numOutZPts; + if ( totalPts > 0 ) + { + newPts->GetData()->WriteVoidPointer(0,3*totalPts); + algo.NewPoints = static_cast<float*>(newPts->GetVoidPointer(0)); + newTris->WritePointer(numOutTris,4*numOutTris); + algo.NewTris = static_cast<vtkIdType*>(newTris->GetPointer()); + + if (newGradients) + { + newGradients->WriteVoidPointer(0,3*totalPts); + algo.NewGradients = static_cast<float*>(newGradients->GetVoidPointer(0)); + } + if (newNormals) + { + newNormals->WriteVoidPointer(0,3*totalPts); + algo.NewNormals = static_cast<float*>(newNormals->GetVoidPointer(0)); + } + algo.NeedGradients = (algo.NewGradients || algo.NewNormals); + + // PASS 4: Fourth and final pass: Process voxel rows and generate output. + // Note that we are simultaneously generating triangles and interpolating + // points. These could be split into separate, parallel operations for + // maximum performance. + Pass4<T> pass4(&algo,value); + vtkSMPTools::For(0,algo.Dims[2]-1, pass4); + }//if anything generated + + // Handle multiple contours + startXPts = numOutXPts; + startYPts = numOutYPts; + startZPts = numOutZPts; + startTris = numOutTris; + }// for all contour values + + // Clean up and return + delete [] algo.XCases; + delete [] algo.EdgeMetaData; +} + +//---------------------------------------------------------------------------- +// Here is the VTK class proper. +// Construct object with a single contour value of 0.0. +vtkExtractSurface::vtkExtractSurface() +{ + this->Radius = 0.1; + this->HoleFilling = false; + this->ComputeNormals = 1; + this->ComputeGradients = 0; + + // by default process active point scalars + this->SetInputArrayToProcess(0,0,0,vtkDataObject::FIELD_ASSOCIATION_POINTS, + vtkDataSetAttributes::SCALARS); +} + +//---------------------------------------------------------------------------- +vtkExtractSurface::~vtkExtractSurface() +{ +} + +//---------------------------------------------------------------------------- +int vtkExtractSurface::RequestUpdateExtent( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // These require extra ghost levels + if (this->ComputeGradients || this->ComputeNormals) + { + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + int ghostLevels; + ghostLevels = + outInfo->Get( + vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()); + inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), + ghostLevels + 1); + } + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkExtractSurface::RequestData( + vtkInformation *request, + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + vtkDebugMacro(<< "Executing 3D structured contour"); + + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkImageData *input = vtkImageData::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // to be safe recompute the update extent + this->RequestUpdateExtent(request,inputVector,outputVector); + vtkDataArray *inScalars = this->GetInputArrayToProcess(0,inputVector); + + // Determine extent + int* inExt = input->GetExtent(); + int exExt[6]; + inInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), exExt); + for (int i=0; i<3; i++) + { + if (inExt[2*i] > exExt[2*i]) + { + exExt[2*i] = inExt[2*i]; + } + if (inExt[2*i+1] < exExt[2*i+1]) + { + exExt[2*i+1] = inExt[2*i+1]; + } + } + if ( exExt[0] >= exExt[1] || exExt[2] >= exExt[3] || exExt[4] >= exExt[5] ) + { + vtkDebugMacro(<<"3D structured contours requires 3D data"); + return 0; + } + + // Check data type and execute appropriate function + // + if (inScalars == NULL) + { + vtkDebugMacro("No scalars for contouring."); + return 0; + } + + // Create necessary objects to hold output. We will defer the + // actual allocation to a later point. + vtkCellArray *newTris = vtkCellArray::New(); + vtkPoints *newPts = vtkPoints::New(); + newPts->SetDataTypeToFloat(); + vtkFloatArray *newNormals = NULL; + vtkFloatArray *newGradients = NULL; + + if (this->ComputeNormals) + { + newNormals = vtkFloatArray::New(); + newNormals->SetNumberOfComponents(3); + newNormals->SetName("Normals"); + } + if (this->ComputeGradients) + { + newGradients = vtkFloatArray::New(); + newGradients->SetNumberOfComponents(3); + newGradients->SetName("Gradients"); + } + + void *ptr = input->GetArrayPointerForExtent(inScalars, exExt); + vtkIdType *incs = input->GetIncrements(); + switch (inScalars->GetDataType()) + { + vtkTemplateMacro(vtkExtractSurfaceAlgorithm<VTK_TT>:: + Contour(this, input, exExt, incs, (VTK_TT *)ptr, + newPts, newTris, newNormals, newGradients)); + } + + vtkDebugMacro(<<"Created: " + << newPts->GetNumberOfPoints() << " points, " + << newTris->GetNumberOfCells() << " triangles"); + + // Update ourselves. Because we don't know up front how many lines + // we've created, take care to reclaim memory. + output->SetPoints(newPts); + newPts->Delete(); + + output->SetPolys(newTris); + newTris->Delete(); + + if (newNormals) + { + int idx = output->GetPointData()->AddArray(newNormals); + output->GetPointData()->SetActiveAttribute(idx, vtkDataSetAttributes::NORMALS); + newNormals->Delete(); + } + + if (newGradients) + { + int idx = output->GetPointData()->AddArray(newGradients); + output->GetPointData()->SetActiveAttribute(idx, vtkDataSetAttributes::VECTORS); + newGradients->Delete(); + } + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkExtractSurface::FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData"); + return 1; +} + +//---------------------------------------------------------------------------- +void vtkExtractSurface::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Radius: " << this->Radius << "\n"; + os << indent << "Hole Filling: " << (this->HoleFilling ? "On\n" : "Off\n"); + os << indent << "Compute Normals: " << (this->ComputeNormals ? "On\n" : "Off\n"); + os << indent << "Compute Gradients: " << (this->ComputeGradients ? "On\n" : "Off\n"); +} diff --git a/Filters/Points/vtkExtractSurface.h b/Filters/Points/vtkExtractSurface.h new file mode 100644 index 00000000000..cd9749983b3 --- /dev/null +++ b/Filters/Points/vtkExtractSurface.h @@ -0,0 +1,158 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkExtractSurface.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkExtractSurface - generate zero-crossing isosurface from +// truncated signed distance volume + +// .SECTION Description +// This filter extracts the zero-crossing isosurface from a truncated signed +// distance function TSDF. The TSDF is sampled across a volume, and is +// extracted using a modified version of the Flying Edges algorithm for +// increased speed, and to support multithreading. To use the filter, an +// input volume should be assigned, which may have special values indicating +// empty and/or unseen portions of the volume. These values are equal to +/- +// radius value of the signed distance function, and should be consistent +// with any filters used to generate the input volume (e.g., +// vtkSignedDistance). +// +// The Flying Edges algorithm is modified to deal with the nature of the +// truncated, signed distance function. Being truncated, the distance +// function typically is not computed throughout the volume, rather the +// special data values "unseen" and/or "empty" maybe assigned to distant or +// bordering voxels. The implications of this are that this implementation +// may produce non-closed, non-manifold surfaces, which is what is needed +// to extract surfaces. +// +// More specifically, voxels may exist in one of three states: 1) within the +// TSDF, which extends +/-Radius from a generating geometry (typically a +// point cloud); 2) in the empty state, in which it is known that the surface +// does not exist; and 3) the unseen state, where a surface may exist but not +// enough information is known to be certain. Such situations arise, for +// example, when laser scanners generate point clouds, and the propagation of +// the laser beam "carves" out regions where no geometry exists (thereby +// defining empty space). Furthermore, areas in which the beam are occluded +// by geometry are known as "unseen" and the boundary between empty and +// unseen can be processed to produced a portion of the output isosurface +// (this is called hole filling). + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. +// +// <pre> +// Notes on the implementation: +// 1. This is a lightly modified version of vtkFlyingEdges3D. Some design goals +// included minimizing the impact on the FE algorithm, and not adding extra +// memory requirements. +// 2. It presumes an isocontour value=0.0 (the zero crossing of a signed +// distance function). +// 3. The major modifications are to the edge cases. In Flying Edges, a single +// byte represents the case of an edge, and within that byte only 2 bits +// are needed (the extra six bytes are not used). Here, these unused bytes +// are repurposed to represent the "state" of the edge, whether it is +// 1) near to the TSDF; 2) in an empty state; or 3) unseen state. +// 4. Since these now-used bits encode extra state information, masking and +// related methods are used to tease apart the edge cases from the edge +// state. +// 5. Voxels with edges marked "empty" are not processed, i.e., no output +// triangle primitives are generated. Depending on whether hole filling is +// enabled, voxels with edges marked "unseen" may not be processed either. +// 6. As a result of #1 and #5, and the desire to keep the implementation simple, +// it is possible to produce output points which are not attached to any output +// triangle. +//</pre> +// +// This algorithm loosely follows the most excellent paper by Curless and +// Levoy: "A Volumetric Method for Building Complex Models from Range +// Images." + +// .SECTION See Also +// vtkSignedDistance vtkFlyingEdges3D + +#ifndef vtkExtractSurface_h +#define vtkExtractSurface_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" +#include "vtkContourValues.h" // Passes calls through + +class vtkImageData; + +class VTKFILTERSPOINTS_EXPORT vtkExtractSurface : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods for instantiating the class, providing type information, + // and printing. + static vtkExtractSurface *New(); + vtkTypeMacro(vtkExtractSurface,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Specify the radius of influence of the signed distance function. Data + // values (which are distances) that are greater than or equal to the + // radius (i.e., d >= Radius) are considered unseen voxels; those voxel + // data values d <= -Radius are considered empty voxels. + vtkSetClampMacro(Radius,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(Radius,double); + + // Description: + // Enable hole filling. This generates separating surfaces between the + // empty and unseen portions of the volume. + vtkSetMacro(HoleFilling,bool); + vtkGetMacro(HoleFilling,bool); + vtkBooleanMacro(HoleFilling,bool); + + // Description: + // Set/Get the computation of normals. Normal computation is fairly + // expensive in both time and storage. If the output data will be processed + // by filters that modify topology or geometry, it may be wise to turn + // Normals and Gradients off. + vtkSetMacro(ComputeNormals,int); + vtkGetMacro(ComputeNormals,int); + vtkBooleanMacro(ComputeNormals,int); + + // Description: + // Set/Get the computation of gradients. Gradient computation is fairly + // expensive in both time and storage. Note that if ComputeNormals is on, + // gradients will have to be calculated, but will not be stored in the + // output dataset. If the output data will be processed by filters that + // modify topology or geometry, it may be wise to turn Normals and + // Gradients off. + vtkSetMacro(ComputeGradients,int); + vtkGetMacro(ComputeGradients,int); + vtkBooleanMacro(ComputeGradients,int); + +protected: + vtkExtractSurface(); + ~vtkExtractSurface(); + + double Radius; + bool HoleFilling; + int ComputeNormals; + int ComputeGradients; + + virtual int RequestData(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int RequestUpdateExtent(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int FillInputPortInformation(int port, vtkInformation *info); + +private: + vtkExtractSurface(const vtkExtractSurface&) VTK_DELETE_FUNCTION; + void operator=(const vtkExtractSurface&) VTK_DELETE_FUNCTION; +}; + +#endif diff --git a/Filters/Points/vtkFitImplicitFunction.cxx b/Filters/Points/vtkFitImplicitFunction.cxx new file mode 100644 index 00000000000..f6fc04110f8 --- /dev/null +++ b/Filters/Points/vtkFitImplicitFunction.cxx @@ -0,0 +1,141 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkFitImplicitFunction.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkFitImplicitFunction.h" + +#include "vtkObjectFactory.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkImplicitFunction.h" +#include "vtkSMPTools.h" + +vtkStandardNewMacro(vtkFitImplicitFunction); +vtkCxxSetObjectMacro(vtkFitImplicitFunction,ImplicitFunction,vtkImplicitFunction); + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//---------------------------------------------------------------------------- +// The threaded core of the algorithm +template <typename T> +struct ExtractPoints +{ + const T *Points; + vtkImplicitFunction *Function; + double Threshold; + vtkIdType *PointMap; + + ExtractPoints(T *points, vtkImplicitFunction *f, double thresh, vtkIdType *map) : + Points(points), Function(f), Threshold(thresh), PointMap(map) + { + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + const T *p = this->Points + 3*ptId; + vtkIdType *map = this->PointMap + ptId; + vtkImplicitFunction *f = this->Function; + double x[3], val; + double tMin = (-this->Threshold); + double tMax = this->Threshold; + + for ( ; ptId < endPtId; ++ptId) + { + x[0] = static_cast<double>(*p++); + x[1] = static_cast<double>(*p++); + x[2] = static_cast<double>(*p++); + + val = f->FunctionValue(x); + *map++ = ( (val >= tMin && val < tMax) ? 1 : -1 ); + } + } + + static void Execute(vtkFitImplicitFunction *self, vtkIdType numPts, + T *points, vtkIdType *map) + { + ExtractPoints extract(points, self->GetImplicitFunction(), + self->GetThreshold(), map); + vtkSMPTools::For(0, numPts, extract); + } + +}; //ExtractPoints + +} //anonymous namespace + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +vtkFitImplicitFunction::vtkFitImplicitFunction() +{ + this->ImplicitFunction = NULL; + this->Threshold = 0.01; +} + +//---------------------------------------------------------------------------- +vtkFitImplicitFunction::~vtkFitImplicitFunction() +{ + this->SetImplicitFunction(NULL); +} + +//---------------------------------------------------------------------------- +// Overload standard modified time function. If implicit function is modified, +// then this object is modified as well. +vtkMTimeType vtkFitImplicitFunction::GetMTime() +{ + vtkMTimeType mTime=this->MTime.GetMTime(); + vtkMTimeType impFuncMTime; + + if ( this->ImplicitFunction != NULL ) + { + impFuncMTime = this->ImplicitFunction->GetMTime(); + mTime = ( impFuncMTime > mTime ? impFuncMTime : mTime ); + } + + return mTime; +} + +//---------------------------------------------------------------------------- +// Traverse all the input points and extract those that lie near the surface +// of an implicit function. +int vtkFitImplicitFunction::FilterPoints(vtkPointSet *input) +{ + // Check the input. + if ( !this->ImplicitFunction ) + { + vtkErrorMacro(<<"Implicit function required\n"); + return 0; + } + + // Determine which points, if any, should be removed. We create a map + // to keep track. The bulk of the algorithmic work is done in this pass. + vtkIdType numPts = input->GetNumberOfPoints(); + void *inPtr = input->GetPoints()->GetVoidPointer(0); + switch (input->GetPoints()->GetDataType()) + { + vtkTemplateMacro(ExtractPoints<VTK_TT>:: + Execute(this, numPts, (VTK_TT *)inPtr, this->PointMap)); + } + + return 1; +} + +//---------------------------------------------------------------------------- +void vtkFitImplicitFunction::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Implicit Function: " + << static_cast<void *>(this->ImplicitFunction) << "\n"; + os << indent << "Threshold: " << this->Threshold << "\n"; +} diff --git a/Filters/Points/vtkFitImplicitFunction.h b/Filters/Points/vtkFitImplicitFunction.h new file mode 100644 index 00000000000..aa3045e45c9 --- /dev/null +++ b/Filters/Points/vtkFitImplicitFunction.h @@ -0,0 +1,103 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkFitImplicitFunction.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkFitImplicitFunction - extract points on the surface of an implicit function + +// .SECTION Description +// vtkFitImplicitFunction extract points that are on the surface of an +// implicit function (within some threshold). Implicit functions in VTK are +// any function of the form f(x,y,z)=c, where values c==0 are considered the +// surface of the implicit function. Typical examples of implicit functions +// include planes, spheres, cylinders, cones, etc. plus boolean combinations +// of these functions. In this implementation, a threshold is used to create +// a fuzzy region considered "on" the surface. In essence, this is a very +// poor man's RANSAC algorithm, where the user picks a function on which to +// fit some points. Thus it is possible to use this filter to define a +// proposed model and place it into an optimization loop to best fit it to a +// set of points. +// +// Note that while any vtkPointSet type can be provided as input, the output is +// represented by an explicit representation of points via a +// vtkPolyData. This output polydata will populate its instance of vtkPoints, +// but no cells will be defined (i.e., no vtkVertex or vtkPolyVertex are +// contained in the output). Also, after filter execution, the user can +// request a vtkIdType* map which indicates how the input points were mapped +// to the output. A value of map[i] (where i is the ith input point) less +// than 0 means that the ith input point was removed. (See also the +// superclass documentation for accessing the removed points through the +// filter's second output.) + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkPointCloudFilter vtkExtractPoints vtkImplicitFunction + +#ifndef vtkFitImplicitFunction_h +#define vtkFitImplicitFunction_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPointCloudFilter.h" + +class vtkImplicitFunction; +class vtkPointSet; + + +class VTKFILTERSPOINTS_EXPORT vtkFitImplicitFunction : public vtkPointCloudFilter +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkFitImplicitFunction *New(); + vtkTypeMacro(vtkFitImplicitFunction,vtkPointCloudFilter); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Specify the implicit function defining a surface on which points + // are to be extracted. + virtual void SetImplicitFunction(vtkImplicitFunction*); + vtkGetObjectMacro(ImplicitFunction,vtkImplicitFunction); + + // Description: + // Specify a threshold value which defines a fuzzy extraction surface. + // Since in this filter the implicit surface is defined as f(x,y,z)=0; + // the extracted points are (-Threshold <= f(x,y,z) < Threshold). + vtkSetClampMacro(Threshold,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(Threshold,double); + + // Description: + // Return the MTime taking into account changes to the implicit function. + virtual vtkMTimeType GetMTime(); + +protected: + vtkFitImplicitFunction(); + ~vtkFitImplicitFunction(); + + vtkImplicitFunction *ImplicitFunction; + double Threshold; + + // All derived classes must implement this method. Note that a side effect of + // the class is to populate the PointMap. Zero is returned if there is a failure. + virtual int FilterPoints(vtkPointSet *input); + +private: + vtkFitImplicitFunction(const vtkFitImplicitFunction&) VTK_DELETE_FUNCTION; + void operator=(const vtkFitImplicitFunction&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkHierarchicalBinningFilter.cxx b/Filters/Points/vtkHierarchicalBinningFilter.cxx new file mode 100644 index 00000000000..dd413d74da1 --- /dev/null +++ b/Filters/Points/vtkHierarchicalBinningFilter.cxx @@ -0,0 +1,922 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkHierarchicalBinningFilter.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkHierarchicalBinningFilter.h" + +#include "vtkObjectFactory.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkIntArray.h" +#include "vtkIdTypeArray.h" +#include "vtkDoubleArray.h" +#include "vtkPointData.h" +#include "vtkFieldData.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkSMPTools.h" + +vtkStandardNewMacro(vtkHierarchicalBinningFilter); + +namespace { + +//----------------------------------------------------------------------------- +// Number ^ index: power function for integers. +static int power(int number, int level) +{ + if (level == 0) + { + return 1; + } + int num = number; + for (int i = 1; i < level; i++) + { + number = number * num; + } + return number; +} + +//---------------------------------------------------------------------------- +static int GetLevelOffset(int level, int divs[3]) +{ + int block = divs[0] * divs[1] * divs[2]; + int offset = 0; + for (int i=0; i<level; ++i) + { + offset += power( block, i); + } + return offset; +} + +//----------------------------------------------------------------------------- +// The hierarchy of uniform subdivided binning grids. +struct UniformBinning +{ + int Level; + int Divs[3]; + double Bounds[6]; + int NumBins; //number of bins in this level of the tree + int LevelOffset; //offset from root bin + + // These are internal data members used for performance reasons + double H[3]; + double hX, hY, hZ; + double fX, fY, fZ, bX, bY, bZ; + vtkIdType xD, yD, zD, xyD; + + // Construction. Provide the current level, and the global binning + // divisions, and the global bounds. + UniformBinning(int level, int divs[3], double bounds[6]) + { + this->Level = level; + + this->Divs[0] = power( divs[0], level); + this->Divs[1] = power( divs[1], level); + this->Divs[2] = power( divs[2], level); + this->NumBins = this->Divs[0] * this->Divs[1] * this->Divs[2]; + + this->Bounds[0] = bounds[0]; + this->Bounds[1] = bounds[1]; + this->Bounds[2] = bounds[2]; + this->Bounds[3] = bounds[3]; + this->Bounds[4] = bounds[4]; + this->Bounds[5] = bounds[5]; + + this->H[0] = (this->Bounds[1] - this->Bounds[0]) / static_cast<double>(this->Divs[0]); + this->H[1] = (this->Bounds[3] - this->Bounds[2]) / static_cast<double>(this->Divs[1]); + this->H[2] = (this->Bounds[5] - this->Bounds[4]) / static_cast<double>(this->Divs[2]); + + this->LevelOffset = GetLevelOffset(level,divs); + + // Setup internal data members for more efficient processing. + this->hX = this->H[0]; + this->hY = this->H[1]; + this->hZ = this->H[2]; + this->fX = 1.0 / this->H[0]; + this->fY = 1.0 / this->H[1]; + this->fZ = 1.0 / this->H[2]; + this->bX = this->Bounds[0]; + this->bY = this->Bounds[2]; + this->bZ = this->Bounds[4]; + this->xD = this->Divs[0]; + this->yD = this->Divs[1]; + this->zD = this->Divs[2]; + this->xyD = this->Divs[0] * this->Divs[1]; + } + + + //----------------------------------------------------------------------------- + // Inlined for performance. These function invocations must be called after + // BuildLocator() is invoked, otherwise the output is indeterminate. + void GetBinIndices(const double *x, int ijk[3]) const + { + // Compute point index. Make sure it lies within range of locator. + ijk[0] = static_cast<int>(((x[0] - bX) * fX)); + ijk[1] = static_cast<int>(((x[1] - bY) * fY)); + ijk[2] = static_cast<int>(((x[2] - bZ) * fZ)); + + ijk[0] = (ijk[0] < 0 ? 0 : (ijk[0] >= xD ? xD-1 : ijk[0])); + ijk[1] = (ijk[1] < 0 ? 0 : (ijk[1] >= yD ? yD-1 : ijk[1])); + ijk[2] = (ijk[2] < 0 ? 0 : (ijk[2] >= zD ? zD-1 : ijk[2])); + } + + //----------------------------------------------------------------------------- + // The bin offset is used to uniquefy the id across the hierarchy of binning grids + vtkIdType GetBinIndex(const double *x) const + { + int ijk[3]; + this->GetBinIndices(x, ijk); + return (this->LevelOffset + ijk[0] + ijk[1]*xD + ijk[2]*xyD); + } + + //----------------------------------------------------------------------------- + // Get the bounds for a particular bin at this level + void GetBinBounds(int localBin, double bounds[6]) + { + int i = localBin % this->xD; + int j = (localBin / this->xD) % this->yD; + int k = localBin / this->xyD; + bounds[0] = this->Bounds[0] + i * hX; + bounds[1] = bounds[0] + this->hX; + bounds[2] = this->Bounds[2] + j * hY; + bounds[3] = bounds[2] + this->hY; + bounds[4] = this->Bounds[4] + k * hZ; + bounds[5] = bounds[4] + this->hZ; + } +}; + +} //anonymous namespace + +//----------------------------------------------------------------------------- +// This non-templated class provides virtual functions to simplify the access +// to the templated subclass. Note this is not in anonymous namespace because the +// VTK class refers to it in the header file (PIMPLd). +struct vtkBinTree +{ + vtkPoints *InPts; // the points to be binned + vtkIdType NumPts; + + int NumLevels; + int Divs[3]; + double Bounds[6]; + UniformBinning *Tree[VTK_MAX_LEVEL+1]; // a uniform binning for each level + int NumBins; // the total number of bins (from all levels) in the tree + int BatchSize; //build the offsets in parallel + + int OffsetsType; + vtkDataArray *OffsetsArray; // container for offset array + + // Construction + vtkBinTree(vtkIdType npts, vtkPoints *pts, int numLevels, int divs[3], + double bounds[6], int offsetsType) : + InPts(pts), NumPts(npts), NumLevels(numLevels), OffsetsType(offsetsType) + { + this->Divs[0] = divs[0]; + this->Divs[1] = divs[1]; + this->Divs[2] = divs[2]; + + this->Bounds[0] = bounds[0]; + this->Bounds[1] = bounds[1]; + this->Bounds[2] = bounds[2]; + this->Bounds[3] = bounds[3]; + this->Bounds[4] = bounds[4]; + this->Bounds[5] = bounds[5]; + + // Build the levels. We create an extra one; it simplifies things later. + this->NumBins = 0; + for (int level=0; level < this->NumLevels; ++level) + { + this->Tree[level] = new UniformBinning(level, divs, bounds); + this->NumBins += this->Tree[level]->NumBins; + } + this->Tree[this->NumLevels] = new UniformBinning(this->NumLevels, divs, bounds); + + this->BatchSize = 0; + + this->OffsetsType = offsetsType; + this->OffsetsArray = NULL; + } + + // Virtual functions supporting convenience methods in templated subclass. + virtual ~vtkBinTree() + { + for (int i=0; i <= this->NumLevels; ++i) + { + delete this->Tree[i]; + } + if ( this->OffsetsArray ) + { + this->OffsetsArray->Delete(); + this->OffsetsArray = NULL; + } + } + virtual void Execute(vtkPointSet *input, vtkPolyData *output) = 0; + int GetNumberOfGlobalBins() + { + return this->NumBins; + } + int GetNumberOfBins(int level) + { + return this->Tree[level]->NumBins; + } + virtual vtkIdType GetLevelOffset(int level, vtkIdType& npts) = 0; + virtual vtkIdType GetBinOffset(int globalBin, vtkIdType& npts) = 0; + virtual vtkIdType GetLocalBinOffset(int level, int localBin, vtkIdType& npts) = 0; + // Sometimes the global bin needs to be expressed as a local bin number + + // tree level. + void TranslateGlobalBinToLocalBin(int globalBin, int& level, int& localBin) + { + for ( level=this->NumLevels-1; + globalBin < this->Tree[level]->LevelOffset; --level ) + { + ; + } + localBin = globalBin - this->Tree[level]->LevelOffset; + } + void GetBinBounds(int globalBin, double bounds[6]) + { + int level, localBin; + this->TranslateGlobalBinToLocalBin(globalBin, level, localBin); + return this->Tree[level]->GetBinBounds(localBin, bounds); + } + void GetLocalBinBounds(int level, int localBin, double bounds[6]) + { + return this->Tree[level]->GetBinBounds(localBin, bounds); + } + + void ExportMetaData(vtkPolyData *output) + { + this->OffsetsArray->SetName("BinOffsets"); + output->GetFieldData()->AddArray(this->OffsetsArray); + + // Bounding box + vtkDoubleArray *da = vtkDoubleArray::New(); + da->SetName("BinBounds"); + da->SetNumberOfTuples(6); + for (int i=0; i<6; ++i) + { + da->SetValue(i,this->Bounds[i]); + } + output->GetFieldData()->AddArray(da); + da->Delete(); + + // Divisions + vtkIntArray *ia = vtkIntArray::New(); + ia->SetName("BinDivisions"); + ia->SetNumberOfTuples(3); + for (int i=0; i<3; ++i) + { + ia->SetValue(i,this->Divs[i]); + } + output->GetFieldData()->AddArray(ia); + ia->Delete(); + + } +}; + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//----------------------------------------------------------------------------- +// The following tuple is what is sorted in the map. Note that it is templated +// because depending on the number of points / bins to process we may want +// to use vtkIdType. Otherwise for performance reasons it's best to use an int +// (or other integral type). Typically sort() is 25-30% faster on smaller +// integral types, plus it takes a heck less memory (when vtkIdType is 64-bit +// and int is 32-bit). +template <typename TTuple> +class BinTuple +{ +public: + TTuple PtId; //originating point id + TTuple Bin; //i-j-k index into bin space + + //Operator< used to support the subsequent sort operation. + bool operator< (const BinTuple& tuple) const + {return Bin < tuple.Bin;} +}; + +//----------------------------------------------------------------------------- +// This templated class manages the creation of the binning tree. It also +// implements the operator() functors which are supplied to vtkSMPTools for +// threaded processesing. +template <typename TIds> +struct BinTree : public vtkBinTree +{ + BinTuple<TIds> *Map; //the map to be sorted + TIds *Offsets; //offsets for each bin into the map + + // Construction + BinTree(vtkIdType npts, vtkPoints *pts, int numLevels, int divs[3], + double bounds[6], int offsetsType) : + vtkBinTree(npts, pts, numLevels, divs, bounds, offsetsType) + { + //one extra allocation to simplify traversal + this->Map = new BinTuple<TIds>[this->NumPts+1]; + this->Map[this->NumPts].Bin = this->NumBins; + if ( offsetsType == VTK_INT ) + { + this->OffsetsArray = vtkIntArray::New(); + } + else + { + this->OffsetsArray = vtkIdTypeArray::New(); + } + this->OffsetsArray->SetNumberOfTuples(this->NumBins+1); + this->Offsets = static_cast<TIds*>(this->OffsetsArray->GetVoidPointer(0)); + this->Offsets[this->NumBins] = this->NumPts; + } + + // Release allocated memory + virtual ~BinTree() + { + delete [] this->Map; + //Offsets data array deleted by superclass + } + + // The number of point ids in a bin is determined by computing the + // difference between the offsets into the sorted points array. + vtkIdType GetNumberOfIds(vtkIdType binNum) + { + return (this->Offsets[binNum+1] - this->Offsets[binNum]); + } + + // Given a bin number, return the point ids in that bin. + const BinTuple<TIds> *GetIds(vtkIdType binNum) + { + return this->Map + this->Offsets[binNum]; + } + + // Explicit point representation (e.g., vtkPointSet), faster path + template <typename T, typename TPts> + class MapPoints + { + public: + BinTree<T> *Tree; + const TPts *Points; + int Thresh[VTK_MAX_LEVEL]; + + MapPoints(BinTree<T> *tree, const TPts *pts) : + Tree(tree), Points(pts) + { + for (int i=0; i < this->Tree->NumLevels; ++i) + { + this->Thresh[i] = this->Tree->Tree[i]->LevelOffset; + } + } + + void operator()(vtkIdType ptId, vtkIdType end) + { + double p[3]; + const TPts *x = this->Points + 3*ptId; + BinTuple<T> *t = this->Tree->Map + ptId; + int numLevels = this->Tree->NumLevels; + int level, idx, numBins = this->Tree->NumBins; + for ( ; ptId < end; ++ptId, x+=3, ++t ) + { + t->PtId = ptId; + p[0] = static_cast<double>(x[0]); + p[1] = static_cast<double>(x[1]); + p[2] = static_cast<double>(x[2]); + idx = ptId % numBins; + + for ( level=numLevels-1; idx < this->Thresh[level]; --level ) + { + ; + } + t->Bin = this->Tree->Tree[level]->GetBinIndex(p); + }//for all points in this batch + } + }; + + // A clever way to build offsets in parallel. Basically each thread builds + // offsets across a range of the sorted map. Recall that offsets are an + // integral value referring to the locations of the sorted points that + // reside in each bin. + template <typename T> + class MapOffsets + { + public: + BinTree<T> *Tree; + vtkIdType NumPts; + int NumBins; + int BatchSize; + + MapOffsets(BinTree<T> *tree, int numBatches) : Tree(tree) + { + this->NumPts = this->Tree->NumPts; + this->NumBins = this->Tree->NumBins; + this->BatchSize = ceil( static_cast<double>(this->NumPts) / numBatches); + } + + // Traverse sorted points (i.e., tuples) and update bin offsets. + void operator()(vtkIdType batch, vtkIdType batchEnd) + { + T *offsets = this->Tree->Offsets; + const BinTuple<T> *curPt = + this->Tree->Map + batch*this->BatchSize; + const BinTuple<T> *endBatchPt = + this->Tree->Map + batchEnd*this->BatchSize; + const BinTuple<T> *endPt = + this->Tree->Map + this->NumPts; + const BinTuple<T> *prevPt; + endBatchPt = ( endBatchPt > endPt ? endPt : endBatchPt ); + + // Special case at the very beginning of the mapped points array. If + // the first point is in bin# N, then all bins up and including + // N must refer to the first point. + if ( curPt == this->Tree->Map ) + { + prevPt = this->Tree->Map; + std::fill_n(offsets, curPt->Bin+1, 0); //point to the first points + }//at the very beginning of the map (sorted points array) + + // We are entering this functor somewhere in the interior of the + // mapped points array. All we need to do is point to the entry + // position because we are interested only in prevPt->Bin. + else + { + prevPt = curPt; + }//else in the middle of a batch + + // Okay we have a starting point for a bin run. Now we can begin + // filling in the offsets in this batch. A previous thread should + // have/will have completed the previous and subsequent runs outside + // of the [batch,batchEnd) range + for ( curPt=prevPt; curPt < endBatchPt; ) + { + for ( ; curPt->Bin == prevPt->Bin && curPt <= endBatchPt; + ++curPt ) + { + ; //advance + } + // Fill in any gaps in the offset array + std::fill_n(offsets + prevPt->Bin + 1, + curPt->Bin - prevPt->Bin, + curPt - this->Tree->Map); + prevPt = curPt; + }//for all batches in this range + }//operator() + }; + + //---------------------------------------------------------------------------- + // Copy points to output + template <typename T, typename TPts> + struct ShufflePoints + { + BinTree<T> *Tree; + vtkIdType NumPts; + TPts *InPoints; + TPts *OutPoints; + + ShufflePoints(BinTree<T> *tree, vtkIdType numPts, TPts *inPts, TPts *outPts) : + Tree(tree), NumPts(numPts), InPoints(inPts), OutPoints(outPts) + { + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + BinTuple<TIds> *map = this->Tree->Map + ptId; + TPts *py = this->OutPoints + 3*ptId; + TPts *px; + + for ( ; ptId < endPtId; ++ptId, ++map) + { + px = this->InPoints + 3*map->PtId; + *py++ = *px++; + *py++ = *px++; + *py++ = *px; + } + } + }; //ShufflePoints + + //---------------------------------------------------------------------------- + // Copy data arrays to output + template <typename T, typename TA> + struct ShuffleArray + { + BinTree<T> *Tree; + vtkIdType NumPts; + int NumComp; + TA *InArray; + TA *OutArray; + + ShuffleArray(BinTree<T> *tree, vtkIdType numPts, int numComp, TA *in, TA *out) : + Tree(tree), NumPts(numPts), NumComp(numComp), InArray(in), OutArray(out) + { + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + BinTuple<TIds> *map = this->Tree->Map + ptId; + TA *y = this->OutArray + this->NumComp*ptId; + TA *x; + int i; + + for ( ; ptId < endPtId; ++ptId, ++map) + { + x = this->InArray + this->NumComp*map->PtId; + for (i=0; i<this->NumComp; ++i) + { + *y++ = *x++; + } + } + } + + static void Execute(BinTree<TIds> *tree, vtkIdType numPts, int numComp, + TA *in, TA *out) + { + ShuffleArray<TIds,TA> shuffle(tree,numPts,numComp,in,out); + vtkSMPTools::For(0,numPts, shuffle); + } + + }; //ShuffleArray + + // Bin the points, produce output + void Execute(vtkPointSet *input, vtkPolyData *output) + { + vtkPoints *inPts = input->GetPoints(); + void *pts = inPts->GetVoidPointer(0); + vtkPoints *outPts = output->GetPoints(); + int dataType = inPts->GetDataType(); + + if ( dataType == VTK_FLOAT ) + { + MapPoints<TIds,float> mapper(this,static_cast<float*>(pts)); + vtkSMPTools::For(0,this->NumPts, mapper); + } + else if ( dataType == VTK_DOUBLE ) + { + MapPoints<TIds,double> mapper(this,static_cast<double*>(pts)); + vtkSMPTools::For(0,this->NumPts, mapper); + } + else + { + vtkGenericWarningMacro("Type not supported\n"); + return; + } + + // Now gather the points into contiguous runs in bins + // + vtkSMPTools::Sort(this->Map, this->Map + this->NumPts); + + // Build the offsets into the Map. The offsets are the positions of + // each bin into the sorted list. They mark the beginning of the + // list of points in each bin. Amazingly, this can be done in + // parallel. + // + int numBatches = static_cast<int>( + ceil(static_cast<double>(this->NumPts) / (5*this->NumBins))); + MapOffsets<TIds> offMapper(this,numBatches); + vtkSMPTools::For(0,numBatches, offMapper); + + // Put the offset into the output for downstream filters + this->ExportMetaData(output); + + // Shuffle the points around + if ( dataType == VTK_FLOAT ) + { + ShufflePoints<TIds,float> + shuffle(this, this->NumPts, static_cast<float*>(inPts->GetVoidPointer(0)), + static_cast<float*>(outPts->GetVoidPointer(0))); + vtkSMPTools::For(0,this->NumPts, shuffle); + } + else if ( dataType == VTK_DOUBLE ) + { + ShufflePoints<TIds,double> + shuffle(this, this->NumPts, static_cast<double*>(inPts->GetVoidPointer(0)), + static_cast<double*>(outPts->GetVoidPointer(0))); + vtkSMPTools::For(0,this->NumPts, shuffle); + } + + // Now shuffle the data arrays + vtkPointData *inPD = input->GetPointData(); + vtkPointData *outPD = output->GetPointData(); + outPD->CopyAllocate(inPD,this->NumPts); + + char *name; + vtkDataArray *iArray, *oArray; + void *iD, *oD; + int i, numComp, numArrays = inPD->GetNumberOfArrays(); + for (i=0; i < numArrays; ++i) + { + iArray = inPD->GetArray(i); + if ( iArray ) + { + name = iArray->GetName(); + numComp = iArray->GetNumberOfComponents(); + oArray = outPD->GetArray(name); + if ( !oArray ) + { + continue; + } + oArray->SetNumberOfTuples(this->NumPts); + iD = iArray->GetVoidPointer(0); + oD = oArray->GetVoidPointer(0); + switch (iArray->GetDataType()) //template macro burps on multiple template parameters + { + case VTK_FLOAT: + ShuffleArray<TIds,float>:: + Execute(this,this->NumPts,numComp,(float *)iD,(float *)oD); break; + case VTK_DOUBLE: + ShuffleArray<TIds,double>:: + Execute(this,this->NumPts,numComp,(double *)iD,(double *)oD); break; + case VTK_INT: + ShuffleArray<TIds,int>:: + Execute(this,this->NumPts,numComp,(int *)iD,(int *)oD); break; + case VTK_UNSIGNED_INT: + ShuffleArray<TIds,unsigned int>:: + Execute(this,this->NumPts,numComp,(unsigned int *)iD,(unsigned int *)oD); break; + case VTK_CHAR: + ShuffleArray<TIds,char>:: + Execute(this,this->NumPts,numComp,(char *)iD,(char *)oD); break; + case VTK_UNSIGNED_CHAR: + ShuffleArray<TIds,unsigned char>:: + Execute(this,this->NumPts,numComp,(unsigned char *)iD,(unsigned char *)oD); break; + case VTK_SHORT: + ShuffleArray<TIds,short>:: + Execute(this,this->NumPts,numComp,(short *)iD,(short *)oD); break; + case VTK_UNSIGNED_SHORT: + ShuffleArray<TIds,unsigned short>:: + Execute(this,this->NumPts,numComp,(unsigned short *)iD,(unsigned short *)oD); break; + default: + vtkGenericWarningMacro("Unsupported attribute type"); + }//over all VTK types + }//have valid array + }//for each candidate array + } + + virtual vtkIdType GetLevelOffset(int level, vtkIdType& npts) + { + vtkIdType offset = this->Offsets[this->Tree[level]->LevelOffset]; + vtkIdType offset2 = this->Offsets[this->Tree[level+1]->LevelOffset]; + + npts = offset2 - offset; + return offset; + } + + virtual vtkIdType GetBinOffset(int globalBin, vtkIdType& npts) + { + vtkIdType offset = this->Offsets[globalBin]; + vtkIdType offset2 = this->Offsets[globalBin+1]; + + npts = offset2 - offset; + return offset; + } + + virtual vtkIdType GetLocalBinOffset(int level, int localBin, vtkIdType& npts) + { + vtkIdType offset = this->Offsets[this->Tree[level]->LevelOffset] + localBin; + vtkIdType offset2 = this->Offsets[this->Tree[level]->LevelOffset] + localBin + 1; + + npts = offset2 - offset; + return offset; + } + +}; //BinTree + + +} //anonymous namespace + + +//================= Begin VTK class proper ======================================= +//---------------------------------------------------------------------------- +vtkHierarchicalBinningFilter::vtkHierarchicalBinningFilter() +{ + + this->NumberOfLevels = 3; + this->Automatic = true; + + this->Divisions[0] = this->Divisions[1] = this->Divisions[2] = 2; + this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = 0.0; + this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = 1.0; + + this->Tree = NULL; +} + +//---------------------------------------------------------------------------- +vtkHierarchicalBinningFilter::~vtkHierarchicalBinningFilter() +{ + if ( this->Tree ) + { + delete this->Tree; + this->Tree = NULL; + } +} + +//---------------------------------------------------------------------------- +// Produce the output data +int vtkHierarchicalBinningFilter::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkPointSet *input = vtkPointSet::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // Check the input + if ( !input || !output ) + { + return 1; + } + vtkIdType numPts = input->GetNumberOfPoints(); + if ( numPts < 1 ) + { + return 1; + } + + // Set up the binning operation + vtkPoints *inPts = input->GetPoints(); + int dataType = inPts->GetDataType(); + vtkPoints *outPts = inPts->NewInstance(); + outPts->SetDataType(dataType); + outPts->SetNumberOfPoints(numPts); + output->SetPoints(outPts); + outPts->UnRegister(this); + + int numLevels = this->NumberOfLevels; + int *divs = this->Divisions; + double *bounds = this->Bounds; + + // If automatic, try and create uniform-sized bins; cubes are ideal. + if ( this->Automatic ) + { + inPts->GetBounds(this->Bounds); + double h[3]; + h[0] = this->Bounds[1] - this->Bounds[0]; + h[1] = this->Bounds[3] - this->Bounds[2]; + h[2] = this->Bounds[5] - this->Bounds[4]; + int min = (h[0] < h[1] ? (h[0] < h[2] ? 0 : 2) : (h[1] < h[2] ? 1 : 2)); + divs[min] = ( h[min] > 0.0 ? 2 : 1); + h[min] = ( divs[min] == 1 ? 1.0 : h[min] ); + for (int i=0; i<3; ++i) + { + if ( i != min ) + { + divs[i] = vtkMath::Round( divs[min]*h[i]/h[min] ) ; + divs[i] = ( divs[i] <= 0 ? 1 : divs[i] ); + } + } + } + + // Bin the points, produce output + if ( numPts >= VTK_INT_MAX ) + { + this->Tree = new BinTree<vtkIdType>(numPts,inPts,numLevels,divs,bounds,VTK_ID_TYPE); + this->Tree->Execute(input,output); + } + else + { + this->Tree = new BinTree<int>(numPts,inPts,numLevels,divs,bounds,VTK_INT); + this->Tree->Execute(input,output); + } + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkHierarchicalBinningFilter:: +GetNumberOfGlobalBins() +{ + if ( this->Tree ) + { + return this->Tree->GetNumberOfGlobalBins(); + } + else + { + return 0; + } +} + +//---------------------------------------------------------------------------- +int vtkHierarchicalBinningFilter:: +GetNumberOfBins(int level) +{ + if ( this->Tree ) + { + return this->Tree->GetNumberOfBins(level); + } + else + { + return 0; + } +} + +//---------------------------------------------------------------------------- +vtkIdType vtkHierarchicalBinningFilter:: +GetLevelOffset(int level, vtkIdType& npts) +{ + if ( this->Tree ) + { + return this->Tree->GetLevelOffset(level,npts); + } + else + { + return -1; + } +} + +//---------------------------------------------------------------------------- +vtkIdType vtkHierarchicalBinningFilter:: +GetBinOffset(int globalBin, vtkIdType& npts) +{ + if ( this->Tree ) + { + return this->Tree->GetBinOffset(globalBin,npts); + } + else + { + return -1; + } +} + +//---------------------------------------------------------------------------- +vtkIdType vtkHierarchicalBinningFilter:: +GetLocalBinOffset(int level, int localBin, vtkIdType& npts) +{ + if ( this->Tree ) + { + return this->Tree->GetLocalBinOffset(level,localBin,npts); + } + else + { + return -1; + } +} + +//---------------------------------------------------------------------------- +void vtkHierarchicalBinningFilter:: +GetBinBounds(int globalBin, double bounds[6]) +{ + if ( this->Tree ) + { + return this->Tree->GetBinBounds(globalBin,bounds); + } + else + { + return; + } +} + +//---------------------------------------------------------------------------- +void vtkHierarchicalBinningFilter:: +GetLocalBinBounds(int level, int localBin, double bounds[6]) +{ + if ( this->Tree ) + { + return this->Tree->GetLocalBinBounds(level,localBin,bounds); + } + else + { + return; + } +} + +//---------------------------------------------------------------------------- +int vtkHierarchicalBinningFilter:: +FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); + return 1; +} + +//---------------------------------------------------------------------------- +void vtkHierarchicalBinningFilter::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Number of Levels: " + << this->NumberOfLevels << endl; + + os << indent << "Automatic: " + << (this->Automatic ? "On\n" : "Off\n"); + + for(int i=0;i<6;i++) + { + os << indent << "Bounds[" << i << "]: " << this->Bounds[i] << "\n"; + } + + os << indent << "Divisions: (" + << this->Divisions[0] << "," + << this->Divisions[1] << "," + << this->Divisions[2] << ")\n"; +} diff --git a/Filters/Points/vtkHierarchicalBinningFilter.h b/Filters/Points/vtkHierarchicalBinningFilter.h new file mode 100644 index 00000000000..fde84fa6446 --- /dev/null +++ b/Filters/Points/vtkHierarchicalBinningFilter.h @@ -0,0 +1,189 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkHierarchicalBinningFilter.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkHierarchicalBinningFilter - uniform binning of points into +// a hierarchical structure + +// .SECTION Description +// vtkHierarchicalBinningFilter creates a spatial, hierarchical ordering of +// input points. This hierarchy is suitable for level-of-detail rendering, or +// multiresolution processing. Each level of the hierarchy is based on +// uniform binning of space, where deeper levels (and its bins) are +// repeatedly subdivided by a given branching factor. Points are associated +// with bins at different levels, with the number of points in each level +// proportional to the number of bins in that level. The output points are +// sorted according to a bin number, where the bin number is unique, +// monotonically increasing number representing the breadth first ordering of +// all of the levels and their bins. Thus all points in a bin (or even a level) +// are segmented into contiguous runs. +// +// Note that points are associated with different bins using a pseudo random +// process. No points are repeated, and no new points are created, thus the +// effect of executing this filter is simply to reorder the input points. +// +// The algorithm proceeds as follows: Given an initial bounding box, the +// space is uniformally subdivided into bins of (M x N x O) dimensions; in +// turn each subsequent level in the tree is further divided into (M x N x O) +// bins (note that level 0 is a single, root bin). Thus the number of bins at +// level L of the hierachical tree is: Nbins=(M^L x N^L x O^L). Once the +// binning is created to a specified depth, then points are placed in the +// bins using a pseudo-random sampling proportional to the number of bins in each +// level. All input points are sorted in the order described above, with no +// points repeated. +// +// The output of this filter are sorted points and associated point +// attributes represented by a vtkPolyData. In addition, an offest integral +// array is associated with the field data of the output, providing offsets +// into the points list via a breadth-first traversal order. Metadata +// describing the output is provided in the field data. Convenience functions +// are also provided here to access the data in a particular bin or across a +// level. (Using the offset array directly may result in higher performance.) +// +// While any vtkPointSet type can be provided as input, the output is +// represented by an explicit representation of points via a +// vtkPolyData. This output polydata will populate its instance of vtkPoints, +// but no cells will be defined (i.e., no vtkVertex or vtkPolyVertex are +// contained in the output). + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkPointCloudFilter vtkQuadricClustering vtkStaticPointLocator + +#ifndef vtkHierarchicalBinningFilter_h +#define vtkHierarchicalBinningFilter_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +#define VTK_MAX_LEVEL 12 + +struct vtkBinTree; + +class VTKFILTERSPOINTS_EXPORT vtkHierarchicalBinningFilter : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkHierarchicalBinningFilter *New(); + vtkTypeMacro(vtkHierarchicalBinningFilter,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Specify the number of levels in the spatial hierachy. By default, the + // number of levels is three. + vtkSetClampMacro(NumberOfLevels,int,1,VTK_MAX_LEVEL); + vtkGetMacro(NumberOfLevels,int); + + // Description: + // Specify whether to determine the determine the level divisions, and the bounding + // box automatically (by default this is on). If off, then the user must specify both + // the bounding box and bin divisions. (Computing the bounding box can be slow for + // large point clouds, manual specification can save time.) + vtkSetMacro(Automatic,bool); + vtkGetMacro(Automatic,bool); + vtkBooleanMacro(Automatic,bool); + + // Description: + // Set the number of branching divisions in each binning direction. Each + // level of the tree is subdivided by this factor. The Divisions[i] must be + // >= 1. Note: if Automatic subdivision is specified, the the Divisions are + // set by the filter. + vtkSetVector3Macro(Divisions,int); + vtkGetVectorMacro(Divisions,int,3); + + // Description: + // Set the bounding box of the point cloud. If Automatic is enabled, then + // this is computed during filter execution. If manually specified + // (Automatic is off) then make sure the bounds is represented as + // (xmin,xmax, ymin,ymax, zmin,zmax). If the bounds specified is does not + // enclose the points, then points are clamped to lie in the bounding box. + vtkSetVector6Macro(Bounds,double); + vtkGetVectorMacro(Bounds,double,6); + + // Description: + // Convenience methods for extracting useful information about this bin + // tree. Return the number of total bins across all levels (i.e., the + // total global bins). Invoke this method after the bin tree has been + // built. + int GetNumberOfGlobalBins(); + + // Description: + // Convenience methods for extracting useful information about this bin + // tree. Return the number of bins in a particular level of the + // tree. Invoke this method after the bin tree has been built. + int GetNumberOfBins(int level); + + // Description: + // Convenience methods for extracting useful information about this bin + // tree. Given a level, return the beginning point id and number of points + // that level. Invoke this method after the bin tree has been built. + vtkIdType GetLevelOffset(int level, vtkIdType& npts); + + // Description: + // Convenience methods for extracting useful information about this bin + // tree. Given a global bin number, return the point id and number of + // points for that bin. Invoke this method after the bin tree has been + // built. + vtkIdType GetBinOffset(int globalBin, vtkIdType& npts); + + // Description: + // Convenience methods for extracting useful information about this bin + // tree. Given a level, and the bin number in that level, return the + // offset point id and number of points for that bin. Invoke this method + // after the bin tree has been built. + vtkIdType GetLocalBinOffset(int level, int localBin, vtkIdType& npts); + + // Description: + // Convenience methods for extracting useful information about a bin tree. + // Given a global bin number, return the bounds (xmin,xmax,ymin,ymax,zmin,zmax) + // for that bin. Invoke this method after the bin tree has been built. + void GetBinBounds(int globalBin, double bounds[6]); + + // Description: + // Convenience methods for extracting useful information about a bin tree. + // Given a level, and a local bin number, return the bounds + // (xmin,xmax,ymin,ymax,zmin,zmax) for that bin. Invoke this method after + // the bin tree has been built. + void GetLocalBinBounds(int level, int localBin, double bounds[6]); + +protected: + vtkHierarchicalBinningFilter(); + ~vtkHierarchicalBinningFilter(); + + // IVars + int NumberOfLevels; + bool Automatic; + int Divisions[3]; + double Bounds[6]; + + // Handle to the underlying implementation. The representation is maintained so + // that the convenience functions can be invoked on the bin tree. + vtkBinTree *Tree; + + virtual int RequestData(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int FillInputPortInformation(int port, vtkInformation *info); + +private: + vtkHierarchicalBinningFilter(const vtkHierarchicalBinningFilter&) VTK_DELETE_FUNCTION; + void operator=(const vtkHierarchicalBinningFilter&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkPCACurvatureEstimation.cxx b/Filters/Points/vtkPCACurvatureEstimation.cxx new file mode 100644 index 00000000000..1d23334e3a8 --- /dev/null +++ b/Filters/Points/vtkPCACurvatureEstimation.cxx @@ -0,0 +1,244 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPCACurvatureEstimation.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkPCACurvatureEstimation.h" + +#include "vtkObjectFactory.h" +#include "vtkAbstractPointLocator.h" +#include "vtkStaticPointLocator.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkPointData.h" +#include "vtkFloatArray.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkSMPTools.h" +#include "vtkSMPThreadLocalObject.h" + +vtkStandardNewMacro(vtkPCACurvatureEstimation); +vtkCxxSetObjectMacro(vtkPCACurvatureEstimation,Locator,vtkAbstractPointLocator); + + +namespace { + +//---------------------------------------------------------------------------- +// The threaded core of the algorithm. +template <typename T> +struct GenerateCurvature +{ + const T *Points; + vtkAbstractPointLocator *Locator; + int SampleSize; + float *Curvature; + + // Don't want to allocate working arrays on every thread invocation. Thread local + // storage lots of new/delete. + vtkSMPThreadLocalObject<vtkIdList> PIds; + + GenerateCurvature(T *points, vtkAbstractPointLocator *loc, int sample, float *curve) : + Points(points), Locator(loc), SampleSize(sample), Curvature(curve) + { + } + + // Just allocate a little bit of memory to get started. + void Initialize() + { + vtkIdList*& pIds = this->PIds.Local(); + pIds->Allocate(128); //allocate some memory + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + const T *px = this->Points + 3*ptId; + const T *py; + float *c = this->Curvature + 3*ptId; + double x[3], mean[3], den; + vtkIdList*& pIds = this->PIds.Local(); + vtkIdType numPts, nei; + int sample, i; + double *a[3], a0[3], a1[3], a2[3], xp[3]; + a[0] = a0; a[1] = a1; a[2] = a2; + double *v[3], v0[3], v1[3], v2[3]; + v[0] = v0; v[1] = v1; v[2] = v2; + double eVal[3]; + + for ( ; ptId < endPtId; ++ptId ) + { + x[0] = static_cast<double>(*px++); + x[1] = static_cast<double>(*px++); + x[2] = static_cast<double>(*px++); + + // Retrieve the local neighborhood + this->Locator->FindClosestNPoints(this->SampleSize, x, pIds); + numPts = pIds->GetNumberOfIds(); + + // First step: compute the mean position of the neighborhood. + mean[0] = mean[1] = mean[2] = 0.0; + for (sample=0; sample<numPts; ++sample) + { + nei = pIds->GetId(sample); + py = this->Points + 3*nei; + mean[0] += static_cast<double>(*py++); + mean[1] += static_cast<double>(*py++); + mean[2] += static_cast<double>(*py); + } + mean[0] /= static_cast<double>(numPts); + mean[1] /= static_cast<double>(numPts); + mean[2] /= static_cast<double>(numPts); + + // Now compute the covariance matrix + a0[0] = a1[0] = a2[0] = 0.0; + a0[1] = a1[1] = a2[1] = 0.0; + a0[2] = a1[2] = a2[2] = 0.0; + for (sample=0; sample < numPts; ++sample ) + { + nei = pIds->GetId(sample); + py = this->Points + 3*nei; + xp[0] = static_cast<double>(*py++) - mean[0]; + xp[1] = static_cast<double>(*py++) - mean[1]; + xp[2] = static_cast<double>(*py) - mean[2]; + for (i=0; i < 3; i++) + { + a0[i] += xp[0] * xp[i]; + a1[i] += xp[1] * xp[i]; + a2[i] += xp[2] * xp[i]; + } + } + for (i=0; i < 3; i++) + { + a0[i] /= static_cast<double>(numPts); + a1[i] /= static_cast<double>(numPts); + a2[i] /= static_cast<double>(numPts); + } + + // Next extract the eigenvectors and values + vtkMath::Jacobi(a,eVal,v); + + // Finally compute the curvatures + den = eVal[0] + eVal[1] + eVal[2]; + *c++ = (eVal[0] - eVal[1]) / den; + *c++ = 2.0*(eVal[1] - eVal[2]) / den; + *c++ = 3.0*eVal[2] / den; + + }//for all points + } + + void Reduce() + { + } + + static void Execute(vtkPCACurvatureEstimation *self, vtkIdType numPts, T *points, + float *curvature) + { + GenerateCurvature gen(points, self->GetLocator(), self->GetSampleSize(), + curvature); + vtkSMPTools::For(0, numPts, gen); + } +}; //GenerateCurvature + +} //anonymous namespace + + +//================= Begin VTK class proper ======================================= +//---------------------------------------------------------------------------- +vtkPCACurvatureEstimation::vtkPCACurvatureEstimation() +{ + + this->SampleSize = 25; + this->Locator = vtkStaticPointLocator::New(); +} + +//---------------------------------------------------------------------------- +vtkPCACurvatureEstimation::~vtkPCACurvatureEstimation() +{ + this->SetLocator(NULL); +} + +//---------------------------------------------------------------------------- +// Produce the output data +int vtkPCACurvatureEstimation::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkPointSet *input = vtkPointSet::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // Check the input + if ( !input || !output ) + { + return 1; + } + vtkIdType numPts = input->GetNumberOfPoints(); + if ( numPts < 1 ) + { + return 1; + } + + // Start by building the locator. + if ( !this->Locator ) + { + vtkErrorMacro(<<"Point locator required\n"); + return 0; + } + this->Locator->SetDataSet(input); + this->Locator->BuildLocator(); + + // Generate the point curvature. + vtkFloatArray *curvature = vtkFloatArray::New(); + curvature->SetNumberOfComponents(3); + curvature->SetNumberOfTuples(numPts); + curvature->SetName("PCACurvature"); + float *c = static_cast<float*>(curvature->GetVoidPointer(0)); + + void *inPtr = input->GetPoints()->GetVoidPointer(0); + switch (input->GetPoints()->GetDataType()) + { + vtkTemplateMacro(GenerateCurvature<VTK_TT>:: + Execute(this, numPts, (VTK_TT *)inPtr, c)); + } + + // Now send the curvatures to the output and clean up + output->SetPoints(input->GetPoints()); + output->GetPointData()->PassData(input->GetPointData()); + output->GetPointData()->AddArray(curvature); + curvature->Delete(); + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkPCACurvatureEstimation:: +FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); + return 1; +} + +//---------------------------------------------------------------------------- +void vtkPCACurvatureEstimation::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Sample Size: " << this->SampleSize << "\n"; + +} diff --git a/Filters/Points/vtkPCACurvatureEstimation.h b/Filters/Points/vtkPCACurvatureEstimation.h new file mode 100644 index 00000000000..beda00a77d3 --- /dev/null +++ b/Filters/Points/vtkPCACurvatureEstimation.h @@ -0,0 +1,100 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPCACurvatureEstimation.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkPCACurvatureEstimation - generate curvature estimates using +// principal component analysis + +// .SECTION Description +// vtkPCACurvatureEstimation generates point normals using PCA (principal +// component analysis). Basically this estimates a local tangent plane +// around sample point p by considering a small neighborhood of points +// around p, and fitting a plane to the neighborhood (via PCA). A good +// introductory reference is Hoppe's "Surface reconstruction from +// unorganized points." +// +// To use this filter, sepcify a neighborhood size. This may have to be set +// via experimentation. Optionally a point locator can be specified (instead +// of the default locator), which is used to accelerate searches around a +// sample point. Finally, the user should specify how to generate +// consistently-oriented normals. As computed by PCA, normals may point in +// +/- orientation, which may not be consistent with neighboring normals. +// +// The output of this filter is the same as the input except that a normal +// per point is produced. (Note that these are unit normals.) While any +// vtkPointSet type can be provided as input, the output is represented by an +// explicit representation of points via a vtkPolyData. This output polydata +// will populate its instance of vtkPoints, but no cells will be defined +// (i.e., no vtkVertex or vtkPolyVertex are contained in the output). + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkPCANormalEstimation + +#ifndef vtkPCACurvatureEstimation_h +#define vtkPCACurvatureEstimation_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +class vtkAbstractPointLocator; + + +class VTKFILTERSPOINTS_EXPORT vtkPCACurvatureEstimation : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkPCACurvatureEstimation *New(); + vtkTypeMacro(vtkPCACurvatureEstimation,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // For each sampled point, specify the number of the closest, surrounding + // points used to estimate the normal (the so called k-neighborhood). By + // default 25 points are used. Smaller numbers may speed performance at the + // cost of accuracy. + vtkSetClampMacro(SampleSize,int,1,VTK_INT_MAX); + vtkGetMacro(SampleSize,int); + + // Description: + // Specify a point locator. By default a vtkStaticPointLocator is + // used. The locator performs efficient searches to locate points + // around a sample point. + void SetLocator(vtkAbstractPointLocator *locator); + vtkGetObjectMacro(Locator,vtkAbstractPointLocator); + +protected: + vtkPCACurvatureEstimation(); + ~vtkPCACurvatureEstimation(); + + // IVars + int SampleSize; + vtkAbstractPointLocator *Locator; + + virtual int RequestData(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int FillInputPortInformation(int port, vtkInformation *info); + +private: + vtkPCACurvatureEstimation(const vtkPCACurvatureEstimation&) VTK_DELETE_FUNCTION; + void operator=(const vtkPCACurvatureEstimation&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkPCANormalEstimation.cxx b/Filters/Points/vtkPCANormalEstimation.cxx new file mode 100644 index 00000000000..1e9d68797a9 --- /dev/null +++ b/Filters/Points/vtkPCANormalEstimation.cxx @@ -0,0 +1,359 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPCANormalEstimation.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkPCANormalEstimation.h" + +#include "vtkObjectFactory.h" +#include "vtkAbstractPointLocator.h" +#include "vtkStaticPointLocator.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkPointData.h" +#include "vtkFloatArray.h" +#include "vtkIdList.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkSMPTools.h" +#include "vtkSMPThreadLocalObject.h" + +vtkStandardNewMacro(vtkPCANormalEstimation); +vtkCxxSetObjectMacro(vtkPCANormalEstimation,Locator,vtkAbstractPointLocator); + + +namespace { + +//---------------------------------------------------------------------------- +// The threaded core of the algorithm. +template <typename T> +struct GenerateNormals +{ + const T *Points; + vtkAbstractPointLocator *Locator; + int SampleSize; + float *Normals; + int Orient; + double OPoint[3]; + bool Flip; + + // Don't want to allocate working arrays on every thread invocation. Thread local + // storage lots of new/delete. + vtkSMPThreadLocalObject<vtkIdList> PIds; + + GenerateNormals(T *points, vtkAbstractPointLocator *loc, int sample, float *normals, + int orient, double opoint[3], bool flip) : + Points(points), Locator(loc), SampleSize(sample), Normals(normals), + Orient(orient), Flip(flip) + { + this->OPoint[0] = opoint[0]; + this->OPoint[1] = opoint[1]; + this->OPoint[2] = opoint[2]; + } + + // Just allocate a little bit of memory to get started. + void Initialize() + { + vtkIdList*& pIds = this->PIds.Local(); + pIds->Allocate(128); //allocate some memory + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + const T *px = this->Points + 3*ptId; + const T *py; + float *n = this->Normals + 3*ptId; + double x[3], mean[3], o[3]; + vtkIdList*& pIds = this->PIds.Local(); + vtkIdType numPts, nei; + int sample, i; + double *a[3], a0[3], a1[3], a2[3], xp[3]; + a[0] = a0; a[1] = a1; a[2] = a2; + double *v[3], v0[3], v1[3], v2[3]; + v[0] = v0; v[1] = v1; v[2] = v2; + double eVecMin[3], eVal[3]; + float flipVal = (this->Flip ? -1.0 : 1.0); + + for ( ; ptId < endPtId; ++ptId ) + { + x[0] = static_cast<double>(*px++); + x[1] = static_cast<double>(*px++); + x[2] = static_cast<double>(*px++); + + // Retrieve the local neighborhood + this->Locator->FindClosestNPoints(this->SampleSize, x, pIds); + numPts = pIds->GetNumberOfIds(); + + // First step: compute the mean position of the neighborhood. + mean[0] = mean[1] = mean[2] = 0.0; + for (sample=0; sample<numPts; ++sample) + { + nei = pIds->GetId(sample); + py = this->Points + 3*nei; + mean[0] += static_cast<double>(*py++); + mean[1] += static_cast<double>(*py++); + mean[2] += static_cast<double>(*py); + } + mean[0] /= static_cast<double>(numPts); + mean[1] /= static_cast<double>(numPts); + mean[2] /= static_cast<double>(numPts); + + // Now compute the covariance matrix + a0[0] = a1[0] = a2[0] = 0.0; + a0[1] = a1[1] = a2[1] = 0.0; + a0[2] = a1[2] = a2[2] = 0.0; + for (sample=0; sample < numPts; ++sample ) + { + nei = pIds->GetId(sample); + py = this->Points + 3*nei; + xp[0] = static_cast<double>(*py++) - mean[0]; + xp[1] = static_cast<double>(*py++) - mean[1]; + xp[2] = static_cast<double>(*py) - mean[2]; + for (i=0; i < 3; i++) + { + a0[i] += xp[0] * xp[i]; + a1[i] += xp[1] * xp[i]; + a2[i] += xp[2] * xp[i]; + } + } + for (i=0; i < 3; i++) + { + a0[i] /= static_cast<double>(numPts); + a1[i] /= static_cast<double>(numPts); + a2[i] /= static_cast<double>(numPts); + } + + // Next extract the eigenvectors and values + vtkMath::Jacobi(a,eVal,v); + //eVecMax[0] = v[0][0]; eVecMax[1] = v[1][0]; eVecMax[2] = v[2][0]; + //eVecMid[0] = v[0][1]; eVecMid[1] = v[1][1]; eVecMid[2] = v[2][1]; + eVecMin[0] = v[0][2]; eVecMin[1] = v[1][2]; eVecMin[2] = v[2][2]; + + // Orient properly + if ( this->Orient == vtkPCANormalEstimation::POINT ) + { + o[0] = this->OPoint[0] - x[0]; + o[1] = this->OPoint[1] - x[1]; + o[2] = this->OPoint[2] - x[2]; + if ( vtkMath::Dot(o,eVecMin) < 0.0 ) + { + eVecMin[0] *= -1; + eVecMin[1] *= -1; + eVecMin[2] *= -1; + } + } + + // Finally compute the point normal (which is the smallest eigenvector) + *n++ = flipVal * eVecMin[0]; + *n++ = flipVal * eVecMin[1]; + *n++ = flipVal * eVecMin[2]; + + }//for all points + } + + void Reduce() + { + } + + static void Execute(vtkPCANormalEstimation *self, vtkIdType numPts, T *points, + float *normals, int orient, double opoint[3], bool flip) + { + GenerateNormals gen(points, self->GetLocator(), self->GetSampleSize(), + normals, orient, opoint, flip); + vtkSMPTools::For(0, numPts, gen); + } +}; //GenerateNormals + +} //anonymous namespace + + +//================= Begin VTK class proper ======================================= +//---------------------------------------------------------------------------- +vtkPCANormalEstimation::vtkPCANormalEstimation() +{ + + this->SampleSize = 25; + this->Locator = vtkStaticPointLocator::New(); + this->NormalOrientation = vtkPCANormalEstimation::POINT; + this->OrientationPoint[0] = this->OrientationPoint[1] = + this->OrientationPoint[2] = 0.0; + this->FlipNormals = false; + +} + +//---------------------------------------------------------------------------- +vtkPCANormalEstimation::~vtkPCANormalEstimation() +{ + this->SetLocator(NULL); +} + +//---------------------------------------------------------------------------- +// Produce the output data +int vtkPCANormalEstimation::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkPointSet *input = vtkPointSet::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // Check the input + if ( !input || !output ) + { + return 1; + } + vtkIdType numPts = input->GetNumberOfPoints(); + if ( numPts < 1 ) + { + return 1; + } + + // Start by building the locator. + if ( !this->Locator ) + { + vtkErrorMacro(<<"Point locator required\n"); + return 0; + } + this->Locator->SetDataSet(input); + this->Locator->BuildLocator(); + + // Generate the point normals. + vtkFloatArray *normals = vtkFloatArray::New(); + normals->SetNumberOfComponents(3); + normals->SetNumberOfTuples(numPts); + float *n = static_cast<float*>(normals->GetVoidPointer(0)); + + void *inPtr = input->GetPoints()->GetVoidPointer(0); + switch (input->GetPoints()->GetDataType()) + { + vtkTemplateMacro(GenerateNormals<VTK_TT>::Execute(this, numPts, (VTK_TT *)inPtr, n, + this->NormalOrientation, this->OrientationPoint, this->FlipNormals)); + } + + // Orient the normals in a consistent fashion (if requested). This requires a traveral + // across the point cloud, traversing neighbors that are in close proximity. + if ( this->NormalOrientation == vtkPCANormalEstimation::GRAPH_TRAVERSAL ) + { + vtkIdType ptId; + char *pointMap = new char [numPts]; + std::fill_n(pointMap, numPts, static_cast<char>(0)); + vtkIdList *wave = vtkIdList::New(); + wave->Allocate(numPts/4+1,numPts); + vtkIdList *wave2 = vtkIdList::New(); + wave2->Allocate(numPts/4+1,numPts); + + for (ptId=0; ptId < numPts; ptId++) + { + if ( pointMap[ptId] == 0 ) + { + wave->InsertNextId(ptId); //begin next connected wave + pointMap[ptId] = 1; + this->TraverseAndFlip (input->GetPoints(), n, pointMap, wave, wave2); + wave->Reset(); + wave2->Reset(); + } + }//for all points + delete [] pointMap; + wave->Delete(); + wave2->Delete(); + }//if graph traversal required + + // Now send the normals to the output and clean up + output->SetPoints(input->GetPoints()); + output->GetPointData()->PassData(input->GetPointData()); + output->GetPointData()->SetNormals(normals); + normals->Delete(); + + return 1; +} + +//---------------------------------------------------------------------------- +// Mark current point as visited and assign cluster number. Note: +// traversal occurs across proximally located points. +// +void vtkPCANormalEstimation:: +TraverseAndFlip (vtkPoints *inPts, float *normals, char *pointMap, + vtkIdList *wave, vtkIdList *wave2) +{ + vtkIdType i, j, numPts, numIds, ptId; + vtkIdList *tmpWave; + double x[3]; + float *n, *n2; + vtkIdList *neighborPointIds = vtkIdList::New(); + + while ( (numIds=wave->GetNumberOfIds()) > 0 ) + { + for ( i=0; i < numIds; i++ ) //for all points in this wave + { + ptId = wave->GetId(i); + inPts->GetPoint(ptId,x); + n = normals + 3*ptId; + this->Locator->FindClosestNPoints(this->SampleSize,x,neighborPointIds); + + numPts = neighborPointIds->GetNumberOfIds(); + for (j=0; j < numPts; ++j) + { + ptId = neighborPointIds->GetId(j); + if ( pointMap[ptId] == 0 ) + { + pointMap[ptId] = 1; + n2 = normals + 3*ptId; + if ( vtkMath::Dot(n,n2) < 0.0 ) + { + *n2++ *= -1; + *n2++ *= -1; + *n2 *= -1; + } + wave2->InsertNextId(ptId); + }//if point not yet visited + }//for all neighbors + }//for all cells in this wave + + tmpWave = wave; + wave = wave2; + wave2 = tmpWave; + tmpWave->Reset(); + } //while wave is not empty + + neighborPointIds->Delete(); + + return; +} + +//---------------------------------------------------------------------------- +int vtkPCANormalEstimation:: +FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); + return 1; +} + +//---------------------------------------------------------------------------- +void vtkPCANormalEstimation::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Sample Size: " << this->SampleSize << "\n"; + os << indent << "Normal Orientation: " << this->NormalOrientation << endl; + os << indent << "Orientation Point: (" << this->OrientationPoint[0] << "," + << this->OrientationPoint[1] << "," << this->OrientationPoint[2] << ")\n"; + os << indent << "Flip Normals: " << (this->FlipNormals ? "On\n" : "Off\n"); + +} diff --git a/Filters/Points/vtkPCANormalEstimation.h b/Filters/Points/vtkPCANormalEstimation.h new file mode 100644 index 00000000000..62a3656c2e1 --- /dev/null +++ b/Filters/Points/vtkPCANormalEstimation.h @@ -0,0 +1,156 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPCANormalEstimation.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkPCANormalEstimation - generate point normals using local tangent planes + +// .SECTION Description +// vtkPCANormalEstimation generates point normals using PCA (principal +// component analysis). Basically this estimates a local tangent plane +// around each sample point p by considering a small neighborhood of points +// around p, and fitting a plane to the neighborhood (via PCA). A good +// introductory reference is Hoppe's "Surface reconstruction from +// unorganized points." +// +// To use this filter, specify a neighborhood size. This may have to be set +// via experimentation. In addition, the user may optionally specify a point +// locator (instead of the default locator), which is used to accelerate +// searches around the sample point. Finally, the user should specify how to +// generate consistently-oriented normals. As computed by PCA, normals may +// point in arbitrary +/- orientation, which may not be consistent with +// neighboring normals. There are three methods to address normal +// consistency: 1) leave the normals as computed, 2) adjust the +/- sign of +// the normals so that the normals all point towards a specified point, and +// 3) perform a traversal of the point cloud and flip neighboring normals so +// that they are mutually consistent. +// +// The output of this filter is the same as the input except that a normal +// per point is produced. (Note that these are unit normals.) While any +// vtkPointSet type can be provided as input, the output is represented by an +// explicit representation of points via a vtkPolyData. This output polydata +// will populate its instance of vtkPoints, but no cells will be defined +// (i.e., no vtkVertex or vtkPolyVertex are contained in the output). + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkPCACurvatureEstimation + +#ifndef vtkPCANormalEstimation_h +#define vtkPCANormalEstimation_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +class vtkAbstractPointLocator; +class vtkIdList; + + +class VTKFILTERSPOINTS_EXPORT vtkPCANormalEstimation : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkPCANormalEstimation *New(); + vtkTypeMacro(vtkPCANormalEstimation,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // For each sampled point, specify the number of the closest, surrounding + // points used to estimate the normal (the so called k-neighborhood). By + // default 25 points are used. Smaller numbers may speed performance at the + // cost of accuracy. + vtkSetClampMacro(SampleSize,int,1,VTK_INT_MAX); + vtkGetMacro(SampleSize,int); + + // Description: + // This enum is used to control how normals oriented is controlled. + enum Style + { + AS_COMPUTED=0, + POINT=1, + GRAPH_TRAVERSAL=3 + }; + + // Description: + // Configure how the filter addresses consistency in normal + // oreientation. When initially computed using PCA, a point normal may + // point in the + or - direction, which may not be consistent with + // neighboring points. To address this, various strategies have been used + // to create consistent normals. The simplest approach is to do nothing + // (AsComputed). Another simple approach is to flip the normal based on its + // direction with respect to a specified point (i.e., point normals will + // point towrads the specified point). Finally, a full traversal of points + // across the graph of neighboring, connected points produces the best + // results but is computationally expensive. + vtkSetMacro(NormalOrientation,int); + vtkGetMacro(NormalOrientation,int); + void SetNormalOrientationToAsComputed() + { this->SetNormalOrientation(AS_COMPUTED); } + void SetNormalOrientationToPoint() + { this->SetNormalOrientation(POINT); } + void SetNormalOrientationToGraphTraversal() + { this->SetNormalOrientation(GRAPH_TRAVERSAL); } + + // Description: + // If the normal orientation is to be consistent with a specified + // direction, then an orientation point should be set. The sign of the + // normals will be modified so that they point towards this point. By + // default, the specified orientation point is (0,0,0). + vtkSetVector3Macro(OrientationPoint,double); + vtkGetVectorMacro(OrientationPoint,double,3); + + // Description: + // The normal orientation can be flipped by enabling this flag. + vtkSetMacro(FlipNormals,bool); + vtkGetMacro(FlipNormals,bool); + vtkBooleanMacro(FlipNormals,bool); + + // Description: + // Specify a point locator. By default a vtkStaticPointLocator is + // used. The locator performs efficient searches to locate points + // around a sample point. + void SetLocator(vtkAbstractPointLocator *locator); + vtkGetObjectMacro(Locator,vtkAbstractPointLocator); + +protected: + vtkPCANormalEstimation(); + ~vtkPCANormalEstimation(); + + // IVars + int SampleSize; + vtkAbstractPointLocator *Locator; + int NormalOrientation; + double OrientationPoint[3]; + bool FlipNormals; + + // Methods used to produce consistent normal orientations + void TraverseAndFlip (vtkPoints *inPts, float *normals, char *pointMap, + vtkIdList *wave, vtkIdList *wave2); + + // Pipeline management + virtual int RequestData(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int FillInputPortInformation(int port, vtkInformation *info); + +private: + vtkPCANormalEstimation(const vtkPCANormalEstimation&) VTK_DELETE_FUNCTION; + void operator=(const vtkPCANormalEstimation&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkPointCloudFilter.cxx b/Filters/Points/vtkPointCloudFilter.cxx new file mode 100644 index 00000000000..a3d2a5e7a3d --- /dev/null +++ b/Filters/Points/vtkPointCloudFilter.cxx @@ -0,0 +1,325 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPointCloudFilter.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkPointCloudFilter.h" + +#include "vtkObjectFactory.h" +#include "vtkAbstractPointLocator.h" +#include "vtkStaticPointLocator.h" +#include "vtkPointSet.h" +#include "vtkDataArray.h" +#include "vtkPoints.h" +#include "vtkPointData.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkMath.h" +#include "vtkSMPTools.h" +#include "vtkSMPThreadLocalObject.h" +#include "vtkArrayListTemplate.h" // For processing attribute data + + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//---------------------------------------------------------------------------- +// Map input points to output. Basically the third pass of the algorithm. +template <typename T> +struct MapPoints +{ + T *InPoints; + T *OutPoints; + const vtkIdType *PointMap; + ArrayList Arrays; + + MapPoints(vtkIdType, T *inPts, vtkIdType numOutPts, T *outPts, + vtkIdType *map, vtkPointData *inPD, vtkPointData *outPD) : + InPoints(inPts), OutPoints(outPts), PointMap(map) + { + this->Arrays.AddArrays(numOutPts, inPD, outPD); + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + T *inP, *outP; + const vtkIdType *map=this->PointMap; + vtkIdType outPtId; + + for ( ; ptId < endPtId; ++ptId) + { + outPtId = map[ptId]; + if ( outPtId != -1 ) + { + inP = this->InPoints + 3*ptId; + outP = this->OutPoints + 3*outPtId; + *outP++ = *inP++; + *outP++ = *inP++; + *outP = *inP; + this->Arrays.Copy(ptId,outPtId); + } + } + } + + static void Execute(vtkIdType numInPts, T *inPts, + vtkIdType numOutPts, T *outPts, vtkIdType *map, + vtkPointData *inPD, vtkPointData *outPD) + { + MapPoints copy(numInPts, inPts, numOutPts, outPts, map, inPD, outPD); + vtkSMPTools::For(0, numInPts, copy); + } + +}; //MapPoints + +//---------------------------------------------------------------------------- +// Map outlier points to second output. This is an optional pass of the +// algorithm. +template <typename T> +struct MapOutliers +{ + T *InPoints; + T *OutPoints; + const vtkIdType *PointMap; + ArrayList Arrays; + + MapOutliers(vtkIdType, T *inPts, vtkIdType numOutPts, T *outPts, + vtkIdType *map, vtkPointData *inPD, vtkPointData *outPD2) : + InPoints(inPts), OutPoints(outPts), PointMap(map) + { + this->Arrays.AddArrays(numOutPts, inPD, outPD2); + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + T *inP, *outP; + const vtkIdType *map=this->PointMap; + vtkIdType outPtId; + + for ( ; ptId < endPtId; ++ptId) + { + outPtId = map[ptId]; + if ( outPtId < 0 ) + { + outPtId = (-outPtId) - 1; + inP = this->InPoints + 3*ptId; + outP = this->OutPoints + 3*outPtId; + *outP++ = *inP++; + *outP++ = *inP++; + *outP = *inP; + this->Arrays.Copy(ptId,outPtId); + } + } + } + + static void Execute(vtkIdType numInPts, T *inPts, + vtkIdType numOutPts, T *outPts, vtkIdType *map, + vtkPointData *inPD, vtkPointData *outPD2) + { + MapOutliers copy(numInPts, inPts, numOutPts, outPts, map, inPD, outPD2); + vtkSMPTools::For(0, numInPts, copy); + } + +}; //MapOutliers + + +} //anonymous namespace + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +vtkPointCloudFilter::vtkPointCloudFilter() +{ + this->PointMap = NULL; + this->NumberOfPointsRemoved = 0; + this->GenerateOutliers = false; + + // Optional second output of outliers + this->SetNumberOfOutputPorts(2); +} + +//---------------------------------------------------------------------------- +vtkPointCloudFilter::~vtkPointCloudFilter() +{ + if ( this->PointMap ) + { + delete [] this->PointMap; + } +} + +//---------------------------------------------------------------------------- +const vtkIdType* vtkPointCloudFilter::GetPointMap() +{ + return this->PointMap; +} + +//---------------------------------------------------------------------------- +vtkIdType vtkPointCloudFilter::GetNumberOfPointsRemoved() +{ + return this->NumberOfPointsRemoved; +} + +//---------------------------------------------------------------------------- +// There are three high level passes. First we traverse all the input points +// to see how many neighbors each point has within a specified radius, and a +// map is created indicating whether an input point is to be copied to the +// output. Next a prefix sum is used to count the output points, and to +// update the mapping between the input and the output. Finally, non-removed +// input points (and associated attributes) are copied to the output. +int vtkPointCloudFilter::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkPointSet *input = vtkPointSet::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // Reset the filter + this->NumberOfPointsRemoved = 0; + if ( this->PointMap ) + { + delete [] this->PointMap; //might have executed previously + } + + // Check input + if ( !input || !output ) + { + return 1; + } + vtkIdType numPts = input->GetNumberOfPoints(); + if ( numPts < 1 ) + { + return 1; + } + + // Okay invoke filtering operation. This is always the initial pass. + this->PointMap = new vtkIdType[numPts]; + if ( ! this->FilterPoints(input) ) + { + return 1; + } + + // Count the resulting points (prefix sum). The second pass of the algorithm; it + // could be threaded but prefix sum does not benefit very much from threading. + vtkIdType ptId; + vtkIdType count=0; + vtkIdType *map = this->PointMap; + for (ptId=0; ptId < numPts; ++ptId) + { + if ( map[ptId] != -1 ) + { + map[ptId] = count; + count++; + } + } + this->NumberOfPointsRemoved = numPts - count; + + // If the number of input and output points is the same we short circuit + // the process. Otherwise, copy the masked input points to the output. + vtkPointData *inPD = input->GetPointData(); + vtkPointData *outPD = output->GetPointData(); + if ( this->NumberOfPointsRemoved == 0 ) + { + output->SetPoints(input->GetPoints()); + outPD->PassData(inPD); + return 1; + } + + // Okay copy the points from the input to the output. We use a threaded + // operation that provides a minor benefit (since it's mostly data + // movement with almost no computation). + outPD->CopyAllocate(inPD,count); + vtkPoints *points = input->GetPoints()->NewInstance(); + points->SetDataType(input->GetPoints()->GetDataType()); + points->SetNumberOfPoints(count); + output->SetPoints(points); + + void *inPtr = input->GetPoints()->GetVoidPointer(0); + void *outPtr = output->GetPoints()->GetVoidPointer(0); + switch (output->GetPoints()->GetDataType()) + { + vtkTemplateMacro(MapPoints<VTK_TT>::Execute(numPts, (VTK_TT *)inPtr, count, + (VTK_TT *)outPtr, this->PointMap, inPD, outPD)); + } + + // Clean up. We leave the map in case the user wants to use it. + points->Delete(); + + // Create the second output if requested. Note that we are using a negative + // count in the map (offset by -1) which indicates the final position of + // the output point in the second output. + if ( this->GenerateOutliers && this->NumberOfPointsRemoved > 0 ) + { + vtkInformation *outInfo2 = outputVector->GetInformationObject(1); + // get the second output + vtkPolyData *output2 = vtkPolyData::SafeDownCast( + outInfo2->Get(vtkDataObject::DATA_OBJECT())); + vtkPointData *outPD2 = output2->GetPointData(); + outPD2->CopyAllocate(inPD,(count-1)); + + // Update map + count = 1; //offset by one + map = this->PointMap; + for (ptId=0; ptId < numPts; ++ptId) + { + if ( map[ptId] == -1 ) + { + map[ptId] = (-count); + count++; + } + } + + // Copy to second output + vtkPoints *points2 = input->GetPoints()->NewInstance(); + points2->SetDataType(input->GetPoints()->GetDataType()); + points2->SetNumberOfPoints(count-1); + output2->SetPoints(points2); + void *outPtr2 = output2->GetPoints()->GetVoidPointer(0); + switch (output->GetPoints()->GetDataType()) + { + vtkTemplateMacro(MapOutliers<VTK_TT>::Execute(numPts, (VTK_TT *)inPtr, (count-1), + (VTK_TT *)outPtr2, this->PointMap, inPD, outPD2)); + } + points2->Delete(); + } + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkPointCloudFilter:: +FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); + return 1; +} + + +//---------------------------------------------------------------------------- +void vtkPointCloudFilter::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Number of Points Removed: " + << this->NumberOfPointsRemoved << "\n"; + + os << indent << "Generate Outliers: " + << (this->GenerateOutliers ? "On\n" : "Off\n"); + +} diff --git a/Filters/Points/vtkPointCloudFilter.h b/Filters/Points/vtkPointCloudFilter.h new file mode 100644 index 00000000000..951e9793867 --- /dev/null +++ b/Filters/Points/vtkPointCloudFilter.h @@ -0,0 +1,118 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPointCloudFilter.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkPointCloudFilter - abstract class for filtering a point cloud + +// .SECTION Description +// vtkPointCloudFilter serves as a base for classes that filter point clouds. +// It takes as input any vtkPointSet (which represents points explicitly +// using vtkPoints) and produces as output an explicit representation of +// filtered points via a vtkPolyData. This output vtkPolyData will populate +// its instance of vtkPoints, but no cells will be defined (i.e., no +// vtkVertex or vtkPolyVertex are contained in the output). Also, after +// filter execution, the user can request a vtkIdType* point map which +// indicates how the input points were mapped to the output. A value of +// PointMap[i] < 0 (where i is the ith input point) means that the ith input +// point was removed. Otherwise PointMap[i] indicates the position in the +// output vtkPoints array (point cloud). +// +// Optionally the filter may produce a second output. This second output is +// another vtkPolyData with a vtkPoints that contains the points that were +// removed during processing. To produce this second output, you must enable +// GenerateOutliers. If this optional, second output is created, then the +// contents of the PointMap are modified as well. In this case, a PointMap[i] +// < 0 means that the ith input point has been mapped to the (-PointMap[i])-1 +// position in the second output's vtkPoints. + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. +// +// The filter copies point attributes from input to output consistent +// with the filtering operation. +// +// It is convenient to use vtkPointGaussianMapper to render the points (since +// this mapper does not require cells to be defined, and it is quite fast). + +// .SECTION See Also +// vtkRadiusOutlierRemoval vtkPointGaussianMapper vtkThresholdPoints + +#ifndef vtkPointCloudFilter_h +#define vtkPointCloudFilter_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +class vtkPointSet; + + +class VTKFILTERSPOINTS_EXPORT vtkPointCloudFilter : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods to obtain type information, and print information. + vtkTypeMacro(vtkPointCloudFilter,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Retrieve a map which indicates, on a point-by-point basis, where each + // input point was placed into the output. In other words, map[i] indicates + // where the ith input point is located in the output array of points. If + // map[i] < 0, then the ith input point was removed during filter + // execution. This method returns valid information only after the filter + // executes. + const vtkIdType* GetPointMap(); + + // Description: + // Return the number of points removed after filter execution. The + // information retuned is valid only after the filter executes. + vtkIdType GetNumberOfPointsRemoved(); + + // Description: + // If this method is enabled (true), then a second output will be created + // that contains the outlier points. By default this is off (false). Note + // that if enabled, the PointMap is modified as well: the outlier points + // are listed as well, with similar meaning, except their value is negated + // and shifted by -1. + vtkSetMacro(GenerateOutliers,bool); + vtkGetMacro(GenerateOutliers,bool); + vtkBooleanMacro(GenerateOutliers,bool); + +protected: + vtkPointCloudFilter(); + ~vtkPointCloudFilter(); + + // All derived classes must implement this method. Note that a side effect of + // the class is to populate the PointMap. Zero is returned on error. + virtual int FilterPoints(vtkPointSet *input) = 0; + + // Keep track of which points are removed through the point map + vtkIdType *PointMap; + vtkIdType NumberOfPointsRemoved; + + // Does a second output need to be created? + bool GenerateOutliers; + + virtual int RequestData(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int FillInputPortInformation(int port, vtkInformation *info); + +private: + vtkPointCloudFilter(const vtkPointCloudFilter&) VTK_DELETE_FUNCTION; + void operator=(const vtkPointCloudFilter&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkRadiusOutlierRemoval.cxx b/Filters/Points/vtkRadiusOutlierRemoval.cxx new file mode 100644 index 00000000000..9878457d2e2 --- /dev/null +++ b/Filters/Points/vtkRadiusOutlierRemoval.cxx @@ -0,0 +1,152 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkRadiusOutlierRemoval.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkRadiusOutlierRemoval.h" + +#include "vtkObjectFactory.h" +#include "vtkAbstractPointLocator.h" +#include "vtkStaticPointLocator.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkIdList.h" +#include "vtkSMPTools.h" +#include "vtkSMPThreadLocalObject.h" + + +vtkStandardNewMacro(vtkRadiusOutlierRemoval); +vtkCxxSetObjectMacro(vtkRadiusOutlierRemoval,Locator,vtkAbstractPointLocator); + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//---------------------------------------------------------------------------- +// The threaded core of the algorithm (first pass) +template <typename T> +struct RemoveOutliers +{ + const T *Points; + vtkAbstractPointLocator *Locator; + double Radius; + int NumNeighbors; + vtkIdType *PointMap; + + // Don't want to allocate working arrays on every thread invocation. Thread local + // storage lots of new/delete. + vtkSMPThreadLocalObject<vtkIdList> PIds; + + RemoveOutliers(T *points, vtkAbstractPointLocator *loc, double radius, int numNei, + vtkIdType *map) : Points(points), Locator(loc), Radius(radius), + NumNeighbors(numNei), PointMap(map) + { + } + + // Just allocate a little bit of memory to get started. + void Initialize() + { + vtkIdList*& pIds = this->PIds.Local(); + pIds->Allocate(128); //allocate some memory + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + const T *p = this->Points + 3*ptId; + vtkIdType *map = this->PointMap + ptId; + double x[3]; + vtkIdList*& pIds = this->PIds.Local(); + + for ( ; ptId < endPtId; ++ptId) + { + x[0] = static_cast<double>(*p++); + x[1] = static_cast<double>(*p++); + x[2] = static_cast<double>(*p++); + + this->Locator->FindPointsWithinRadius(this->Radius, x, pIds); + vtkIdType numPts = pIds->GetNumberOfIds(); + + // Keep in mind that The FindPoints method will always return at + // least one point (itself). + *map++ = ( numPts > this->NumNeighbors ? 1 : -1 ); + } + } + + void Reduce() + { + } + + static void Execute(vtkRadiusOutlierRemoval *self, vtkIdType numPts, + T *points, vtkIdType *map) + { + RemoveOutliers remove(points, self->GetLocator(), self->GetRadius(), + self->GetNumberOfNeighbors(), map); + vtkSMPTools::For(0, numPts, remove); + } + +}; //RemoveOutliers + +} //anonymous namespace + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +vtkRadiusOutlierRemoval::vtkRadiusOutlierRemoval() +{ + this->Radius = 1.0; + this->NumberOfNeighbors = 2; + this->Locator = vtkStaticPointLocator::New(); +} + +//---------------------------------------------------------------------------- +vtkRadiusOutlierRemoval::~vtkRadiusOutlierRemoval() +{ + this->SetLocator(NULL); +} + +//---------------------------------------------------------------------------- +// Traverse all the input points to see how many neighbors each point has +// within a specified radius, and populate the map which indicates how points +// are to be copied to the output. +int vtkRadiusOutlierRemoval::FilterPoints(vtkPointSet *input) +{ + // Perform the point removal + // Start by building the locator + if ( !this->Locator ) + { + vtkErrorMacro(<<"Point locator required\n"); + return 0; + } + this->Locator->SetDataSet(input); + this->Locator->BuildLocator(); + + // Determine which points, if any, should be removed. We create a map + // to keep track. The bulk of the algorithmic work is done in this pass. + vtkIdType numPts = input->GetNumberOfPoints(); + void *inPtr = input->GetPoints()->GetVoidPointer(0); + switch (input->GetPoints()->GetDataType()) + { + vtkTemplateMacro(RemoveOutliers<VTK_TT>:: + Execute(this, numPts, (VTK_TT *)inPtr, this->PointMap)); + } + + return 1; +} + +//---------------------------------------------------------------------------- +void vtkRadiusOutlierRemoval::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Radius: " << this->Radius << "\n"; + os << indent << "Number of Neighbors: " << this->NumberOfNeighbors << "\n"; + os << indent << "Locator: " << this->Locator << "\n"; +} diff --git a/Filters/Points/vtkRadiusOutlierRemoval.h b/Filters/Points/vtkRadiusOutlierRemoval.h new file mode 100644 index 00000000000..8a4de26a122 --- /dev/null +++ b/Filters/Points/vtkRadiusOutlierRemoval.h @@ -0,0 +1,102 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkRadiusOutlierRemoval.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkRadiusOutlierRemoval - remove isolated points + +// .SECTION Description +// vtkRadiusOutlierRemoval removes isolated points; i.e., those points that +// have few neighbors within a specified radius. The user must specify the +// radius defining the local region, as well as the isolation threshold +// (i.e., number of neighboring points required for the point to be +// considered isolated). Optionally, users can specify a point locator to +// accelerate local neighborhood search operations. (By default a +// vtkStaticPointLocator will be created.) +// +// Note that while any vtkPointSet type can be provided as input, the output +// is represented by an explicit representation of points via a +// vtkPolyData. This output polydata will populate its instance of vtkPoints, +// but no cells will be defined (i.e., no vtkVertex or vtkPolyVertex are +// contained in the output). Also, after filter execution, the user can +// request a vtkIdType* map which indicates how the input points were mapped +// to the output. A value of map[i] (where i is the ith input point) less +// than 0 means that the ith input point was removed. (See also the +// superclass documentation for accessing the removed points through the +// filter's second output.) + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkPointCloudFilter vtkStatisticalOutlierRemoval vtkExtractPoints +// vtkThresholdPoints vtkImplicitFunction + +#ifndef vtkRadiusOutlierRemoval_h +#define vtkRadiusOutlierRemoval_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPointCloudFilter.h" + +class vtkAbstractPointLocator; +class vtkPointSet; + + +class VTKFILTERSPOINTS_EXPORT vtkRadiusOutlierRemoval : public vtkPointCloudFilter +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkRadiusOutlierRemoval *New(); + vtkTypeMacro(vtkRadiusOutlierRemoval,vtkPointCloudFilter); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Specify the local search radius. + vtkSetClampMacro(Radius,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(Radius,double); + + // Description: + // Specify the number of neighbors that a point must have, within + // the specified radius, for the point to not be considered isolated. + vtkSetClampMacro(NumberOfNeighbors,int,1,VTK_INT_MAX); + vtkGetMacro(NumberOfNeighbors,int); + + // Description: + // Specify a point locator. By default a vtkStaticPointLocator is + // used. The locator performs efficient searches to locate near a + // specified interpolation position. + void SetLocator(vtkAbstractPointLocator *locator); + vtkGetObjectMacro(Locator,vtkAbstractPointLocator); + +protected: + vtkRadiusOutlierRemoval(); + ~vtkRadiusOutlierRemoval(); + + double Radius; + int NumberOfNeighbors; + vtkAbstractPointLocator *Locator; + + // All derived classes must implement this method. Note that a side effect of + // the class is to populate the PointMap. Zero is returned if there is a failure. + virtual int FilterPoints(vtkPointSet *input); + +private: + vtkRadiusOutlierRemoval(const vtkRadiusOutlierRemoval&) VTK_DELETE_FUNCTION; + void operator=(const vtkRadiusOutlierRemoval&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkSignedDistance.cxx b/Filters/Points/vtkSignedDistance.cxx new file mode 100644 index 00000000000..4d21f6975b5 --- /dev/null +++ b/Filters/Points/vtkSignedDistance.cxx @@ -0,0 +1,466 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkSignedDistance.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkSignedDistance.h" + +#include "vtkArrayListTemplate.h" // For processing attribute data +#include "vtkImageData.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkObjectFactory.h" +#include "vtkPointData.h" +#include "vtkPolyData.h" +#include "vtkAbstractPointLocator.h" +#include "vtkStaticPointLocator.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkDoubleArray.h" +#include "vtkSMPTools.h" +#include "vtkSMPThreadLocalObject.h" + +vtkStandardNewMacro(vtkSignedDistance); +vtkCxxSetObjectMacro(vtkSignedDistance,Locator,vtkAbstractPointLocator); + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +// The threaded core of the algorithm +template <typename T> +struct SignedDistance +{ + T *Pts; + float *Normals; + int Dims[3]; + double Origin[3]; + double Spacing[3]; + double Radius; + vtkAbstractPointLocator *Locator; + float *Scalars; + + // Don't want to allocate these working arrays on every thread invocation, + // so make them thread local. + vtkSMPThreadLocalObject<vtkIdList> PIds; + + SignedDistance(T *pts, float *normals, int dims[3], double origin[3], double spacing[3], + double radius, vtkAbstractPointLocator *loc, float *scalars) : + Pts(pts), Normals(normals), Radius(radius), Locator(loc), Scalars(scalars) + { + for (int i=0; i < 3; ++i) + { + this->Dims[i] = dims[i]; + this->Origin[i] = origin[i]; + this->Spacing[i] = spacing[i]; + } + } + + // Just allocate a little bit of memory to get started. + void Initialize() + { + vtkIdList*& pIds = this->PIds.Local(); + pIds->Allocate(128); //allocate some memory + } + + // Threaded interpolation method + void operator() (vtkIdType slice, vtkIdType sliceEnd) + { + T *p; + float *n; + double x[3], dist; + vtkIdType numPts; + double *origin=this->Origin; + double *spacing=this->Spacing; + int ii, *dims=this->Dims; + vtkIdType ptId, jOffset, kOffset, sliceSize=dims[0]*dims[1]; + vtkIdList*& pIds = this->PIds.Local(); + + for ( ; slice < sliceEnd; ++slice) + { + x[2] = origin[2] + slice*spacing[2]; + kOffset = slice*sliceSize; + + for ( int j=0; j < dims[1]; ++j) + { + x[1] = origin[1] + j*spacing[1]; + jOffset = j*dims[0]; + + for ( int i=0; i < dims[0]; ++i) + { + x[0] = origin[0] + i*spacing[0]; + ptId = i + jOffset + kOffset; + + // Compute signed distance from surrounding points + this->Locator->FindPointsWithinRadius(this->Radius, x, pIds); + numPts = pIds->GetNumberOfIds(); + if ( numPts > 0 ) + { + for (dist=0.0, ii=0; ii < numPts; ++ii) + { + p = this->Pts + 3*pIds->GetId(ii); + n = this->Normals + 3*pIds->GetId(ii); + dist += n[0]*(x[0]-p[0]) + n[1]*(x[1]-p[1]) + n[2]*(x[2]-p[2]); + } + this->Scalars[ptId] = dist / static_cast<double>(numPts); + }//if nearby points + }//over i + }//over j + }//over slices + } + + void Reduce() + { + } + + static void Execute(vtkSignedDistance *self, T *pts, float *normals, int dims[3], + double origin[3], double spacing[3], float *scalars) + { + SignedDistance dist(pts, normals, dims, origin, spacing, self->GetRadius(), + self->GetLocator(), scalars); + vtkSMPTools::For(0, dims[2], dist); + } + +}; //SignedDistance + +} //anonymous namespace + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +// Construct with sample dimensions=(256,256,256), and so that model bounds are +// automatically computed from the input. +vtkSignedDistance::vtkSignedDistance() +{ + this->Dimensions[0] = 256; + this->Dimensions[1] = 256; + this->Dimensions[2] = 256; + + this->Bounds[0] = 0.0; + this->Bounds[1] = 0.0; + this->Bounds[2] = 0.0; + this->Bounds[3] = 0.0; + this->Bounds[4] = 0.0; + this->Bounds[5] = 0.0; + + this->Radius = 0.1; + + this->Locator = vtkStaticPointLocator::New(); + + this->Initialized = 0; +} + +//---------------------------------------------------------------------------- +vtkSignedDistance::~vtkSignedDistance() +{ + this->SetLocator(NULL); +} + + +//---------------------------------------------------------------------------- +// Initialize the filter for appending data. You must invoke the +// StartAppend() method before doing successive Appends(). It's also a +// good idea to manually specify the model bounds; otherwise the input +// bounds for the data will be used. +void vtkSignedDistance::StartAppend() +{ + vtkInformation* outInfo = this->GetOutputInformation(0); + outInfo->Set( + vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), + vtkStreamingDemandDrivenPipeline::GetWholeExtent(outInfo), + 6); + + vtkDebugMacro(<< "Initializing data"); + this->AllocateOutputData(this->GetOutput(), this->GetOutputInformation(0)); + vtkIdType numPts = this->Dimensions[0] * this->Dimensions[1] * this->Dimensions[2]; + + // initialize output to initial unseen value at each location + float *newScalars = static_cast<float*>( + this->GetOutput()->GetPointData()->GetScalars()->GetVoidPointer(0)); + std::fill_n(newScalars, numPts, this->Radius); + + // Compute the initial bounds + double bounds[6]; + vtkImageData *output=this->GetOutput(); + double tempd[3]; + int i; + + // compute model bounds if not set previously + if ( this->Bounds[0] >= this->Bounds[1] || + this->Bounds[2] >= this->Bounds[3] || + this->Bounds[4] >= this->Bounds[5] ) + { + vtkPolyData *input = vtkPolyData::SafeDownCast(this->GetInput()); + input->GetBounds(bounds); + for (i=0; i<3; i++) + { + this->Bounds[2*i] = bounds[2*i]; + this->Bounds[2*i+1] = bounds[2*i+1]; + } + } + + // Set volume origin and data spacing + output->SetOrigin(this->Bounds[0], this->Bounds[2], this->Bounds[4]); + + for (i=0; i<3; i++) + { + tempd[i] = (this->Bounds[2*i+1] - this->Bounds[2*i]) / + (this->Dimensions[i] - 1); + } + output->SetSpacing(tempd); + + outInfo->Set(vtkDataObject::ORIGIN(),this->Bounds[0], + this->Bounds[2], this->Bounds[4]); + outInfo->Set(vtkDataObject::SPACING(),tempd,3); + + this->Initialized = 1; +} + + +//---------------------------------------------------------------------------- +// Append a data set to the existing output. To use this function, +// you'll have to invoke the StartAppend() method before doing +// successive appends. It's also a good idea to specify the model +// bounds; otherwise the input model bounds is used. When you've +// finished appending, use the EndAppend() method. +void vtkSignedDistance::Append(vtkPolyData *input) +{ + vtkDebugMacro(<< "Appending data"); + + // There better be data + if ( !input || input->GetNumberOfPoints() < 1 ) + { + return; + } + + if ( !this->Initialized ) + { + this->StartAppend(); + } + + // Make sure that there are normals and output scalars + vtkPoints *pts = input->GetPoints(); + float *scalars = static_cast<float*>( + this->GetOutput()->GetPointData()->GetScalars()->GetVoidPointer(0)); + vtkDataArray *normalArray = input->GetPointData()->GetNormals(); + if ( ! normalArray || normalArray->GetDataType() != VTK_FLOAT ) + { + vtkErrorMacro(<< "Float normals required!"); + return; + } + float *normals = static_cast<float*>(normalArray->GetVoidPointer(0)); + + // Build the locator + if ( !this->Locator ) + { + vtkErrorMacro(<<"Point locator required\n"); + return; + } + this->Locator->SetDataSet(input); + this->Locator->BuildLocator(); + + // Finally: compute the signed distance function + vtkImageData *output=this->GetOutput(); + void *inPtr = pts->GetVoidPointer(0); + switch (pts->GetDataType()) + { + vtkTemplateMacro(SignedDistance<VTK_TT>::Execute(this, (VTK_TT *)inPtr, normals, + this->Dimensions, output->GetOrigin(), output->GetSpacing(), scalars)); + } + +} + +//---------------------------------------------------------------------------- +// Method completes the append process (does the capping if requested). +void vtkSignedDistance::EndAppend() +{ + vtkDataArray *newScalars; + vtkDebugMacro(<< "End append"); + + if (!(newScalars = this->GetOutput()->GetPointData()->GetScalars())) + { + vtkErrorMacro("No output produced."); + return; + } +} + +//---------------------------------------------------------------------------- +int vtkSignedDistance::RequestInformation ( + vtkInformation * vtkNotUsed(request), + vtkInformationVector ** vtkNotUsed( inputVector ), + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + int i; + double ar[3], origin[3]; + + vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_FLOAT, 1); + + outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), + 0, this->Dimensions[0]-1, + 0, this->Dimensions[1]-1, + 0, this->Dimensions[2]-1); + + for (i=0; i < 3; i++) + { + origin[i] = this->Bounds[2*i]; + if ( this->Dimensions[i] <= 1 ) + { + ar[i] = 1; + } + else + { + ar[i] = (this->Bounds[2*i+1] - this->Bounds[2*i]) + / (this->Dimensions[i] - 1); + } + } + outInfo->Set(vtkDataObject::ORIGIN(),origin,3); + outInfo->Set(vtkDataObject::SPACING(),ar,3); + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkSignedDistance::RequestData( + vtkInformation* vtkNotUsed( request ), + vtkInformationVector** inputVector, + vtkInformationVector* vtkNotUsed( outputVector )) +{ + // get the input + vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); + vtkPolyData *input = vtkPolyData::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + + vtkDebugMacro(<< "Executing space carver"); + + if (input == NULL) + { + // we do not want to release the data because user might + // have called Append ... + return 0; + } + + this->StartAppend(); + this->Append(input); + this->EndAppend(); + + return 1; +} + +//---------------------------------------------------------------------------- +// Set the i-j-k dimensions on which to sample the distance function. +void vtkSignedDistance::SetDimensions(int i, int j, int k) +{ + int dim[3]; + + dim[0] = i; + dim[1] = j; + dim[2] = k; + + this->SetDimensions(dim); +} + +//---------------------------------------------------------------------------- +void vtkSignedDistance::SetDimensions(int dim[3]) +{ + int dataDim, i; + + vtkDebugMacro(<< " setting Dimensions to (" << dim[0] << "," << dim[1] << "," << dim[2] << ")"); + + if ( dim[0] != this->Dimensions[0] || + dim[1] != this->Dimensions[1] || + dim[2] != this->Dimensions[2] ) + { + if ( dim[0]<1 || dim[1]<1 || dim[2]<1 ) + { + vtkErrorMacro (<< "Bad Sample Dimensions, retaining previous values"); + return; + } + + for (dataDim=0, i=0; i<3 ; i++) + { + if (dim[i] > 1) + { + dataDim++; + } + } + + if ( dataDim < 3 ) + { + vtkErrorMacro(<<"Sample dimensions must define a volume!"); + return; + } + + for ( i=0; i<3; i++) + { + this->Dimensions[i] = dim[i]; + } + + this->Modified(); + } +} + +//---------------------------------------------------------------------------- +int vtkSignedDistance::FillInputPortInformation( + int vtkNotUsed( port ), vtkInformation* info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1); + return 1; +} + +//---------------------------------------------------------------------------- +int vtkSignedDistance::ProcessRequest(vtkInformation* request, + vtkInformationVector** inputVector, + vtkInformationVector* outputVector) +{ + // If we have no input then we will not generate the output because + // the user already called StartAppend/Append/EndAppend. + if(request->Has(vtkDemandDrivenPipeline::REQUEST_DATA_NOT_GENERATED())) + { + if(inputVector[0]->GetNumberOfInformationObjects() == 0) + { + vtkInformation* outInfo = outputVector->GetInformationObject(0); + outInfo->Set(vtkDemandDrivenPipeline::DATA_NOT_GENERATED(), 1); + } + return 1; + } + else if(request->Has(vtkDemandDrivenPipeline::REQUEST_DATA())) + { + if(inputVector[0]->GetNumberOfInformationObjects() == 0) + { + return 1; + } + } + return this->Superclass::ProcessRequest(request, inputVector, outputVector); +} + +//---------------------------------------------------------------------------- +void vtkSignedDistance::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Radius: " << this->Radius << "\n"; + os << indent << "Dimensions: (" << this->Dimensions[0] << ", " + << this->Dimensions[1] << ", " + << this->Dimensions[2] << ")\n"; + os << indent << "Bounds: \n"; + os << indent << " Xmin,Xmax: (" << this->Bounds[0] << ", " + << this->Bounds[1] << ")\n"; + os << indent << " Ymin,Ymax: (" << this->Bounds[2] << ", " + << this->Bounds[3] << ")\n"; + os << indent << " Zmin,Zmax: (" << this->Bounds[4] << ", " + << this->Bounds[5] << ")\n"; + + os << indent << "Locator: " << this->Locator << "\n"; +} diff --git a/Filters/Points/vtkSignedDistance.h b/Filters/Points/vtkSignedDistance.h new file mode 100644 index 00000000000..dc293d11f71 --- /dev/null +++ b/Filters/Points/vtkSignedDistance.h @@ -0,0 +1,156 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkSignedDistance.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkSignedDistance - compute signed distances from an input point cloud +// .SECTION Description +// vtkSignedDistance is a filter that computes signed distances over a volume +// from an input point cloud. The input point cloud must have point normals +// defined, as well as an optional weighting function (e.g., probabilities +// that the point measurements are accurate). Once the signed distance +// function is computed, then the output volume may be isocontoured to +// extract a approximating surface to the point cloud. +// +// To use this filter, specify the input vtkPolyData (which represents the +// point cloud); define the sampling volume; specify a radius (which limits +// the radius of influence of each point); and set an optional point locator +// (to accelerate proximity operations, a vtkStaticPointLocator is used by +// default). Note that large radius values may have significant impact on +// performance. The volume is defined by specifying dimensions in the x-y-z +// directions, as well as a domain bounds. By default the model bounds are +// defined from the input points, but the user can also manually specify +// them. +// +// This filter has one other unusual capability: it is possible to append +// data in a sequence of operations to generate a single output. This is +// useful when you have multiple point clouds (e.g., possibly from multiple +// acqusition scans) and want to incrementally accumulate all the data. +// However, the user must be careful to either specify the Bounds or +// order the input such that the bounds of the first input completely +// contains all other input data. This is because the geometry and topology +// of the output sampling volume cannot be changed after the initial Append +// operation. +// +// This algorithm loosely follows the most excellent paper by Curless and +// Levoy: "A Volumetric Method for Building Complex Models from Range +// Images." As described in this paper it may produce a signed distance +// volume that may contain the three data states for each voxel: near +// surface, empty, or unseen (see vtkExtractSurface for additional +// information). However, this algorithm has been extended to support +// different interpolation kernels as follows. +// +// (Kernel description TODO including supplied weights.) +// +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkExtractSurface vtkImplicitModeller + +#ifndef vtkSignedDistance_h +#define vtkSignedDistance_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkImageAlgorithm.h" + +class vtkPolyData; +class vtkAbstractPointLocator; + + +class VTKFILTERSPOINTS_EXPORT vtkSignedDistance : public vtkImageAlgorithm +{ +public: + // Description: + // Standard methods for instantiating the class, providing type information, + // and printing. + static vtkSignedDistance *New(); + vtkTypeMacro(vtkSignedDistance,vtkImageAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Set/Get the i-j-k dimensions on which to computer the distance function. + vtkGetVectorMacro(Dimensions,int,3); + void SetDimensions(int i, int j, int k); + void SetDimensions(int dim[3]); + + // Description: + // Set / get the region in space in which to perform the sampling. If + // not specified, it will be computed automatically. + vtkSetVector6Macro(Bounds,double); + vtkGetVectorMacro(Bounds,double,6); + + // Description: + // Set / get the radius of influence of each point. Smaller values + // generally improve performance markedly. + vtkSetClampMacro(Radius,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(Radius,double); + + // Description: + // Specify a point locator. By default a vtkStaticPointLocator is + // used. The locator performs efficient searches to locate points + // surrounding a voxel (within the specified radius). + void SetLocator(vtkAbstractPointLocator *locator); + vtkGetObjectMacro(Locator,vtkAbstractPointLocator); + + // Description: + // Initialize the filter for appending data. You must invoke the + // StartAppend() method before doing successive Appends(). It's also a + // good idea to manually specify the model bounds; otherwise the input + // bounds for the data will be used. + void StartAppend(); + + // Description: + // Append a data set to the existing output. To use this function, + // you'll have to invoke the StartAppend() method before doing + // successive appends. It's also a good idea to specify the model + // bounds; otherwise the input model bounds is used. When you've + // finished appending, use the EndAppend() method. + void Append(vtkPolyData *input); + + // Description: + // Method completes the append process. + void EndAppend(); + + // See the vtkAlgorithm for a desciption of what these do + int ProcessRequest(vtkInformation*, + vtkInformationVector**, + vtkInformationVector*); + +protected: + vtkSignedDistance(); + ~vtkSignedDistance(); + + int Dimensions[3]; + double Bounds[6]; + double Radius; + vtkAbstractPointLocator *Locator; + + // Flag tracks whether process needs initialization + int Initialized; + + virtual int RequestInformation (vtkInformation *, + vtkInformationVector **, + vtkInformationVector *); + virtual int RequestData (vtkInformation *, + vtkInformationVector **, vtkInformationVector *); + virtual int FillInputPortInformation(int, vtkInformation*); + +private: + vtkSignedDistance(const vtkSignedDistance&) VTK_DELETE_FUNCTION; + void operator=(const vtkSignedDistance&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkStatisticalOutlierRemoval.cxx b/Filters/Points/vtkStatisticalOutlierRemoval.cxx new file mode 100644 index 00000000000..02a90ab7e62 --- /dev/null +++ b/Filters/Points/vtkStatisticalOutlierRemoval.cxx @@ -0,0 +1,347 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkStatisticalOutlierRemoval.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkStatisticalOutlierRemoval.h" + +#include "vtkObjectFactory.h" +#include "vtkAbstractPointLocator.h" +#include "vtkStaticPointLocator.h" +#include "vtkPointSet.h" +#include "vtkPoints.h" +#include "vtkIdList.h" +#include "vtkSMPTools.h" +#include "vtkSMPThreadLocalObject.h" +#include "vtkMath.h" + +vtkStandardNewMacro(vtkStatisticalOutlierRemoval); +vtkCxxSetObjectMacro(vtkStatisticalOutlierRemoval,Locator,vtkAbstractPointLocator); + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//---------------------------------------------------------------------------- +// The threaded core of the algorithm (first pass) +template <typename T> +struct ComputeMeanDistance +{ + const T *Points; + vtkAbstractPointLocator *Locator; + int SampleSize; + float *Distance; + double Mean; + + // Don't want to allocate working arrays on every thread invocation. Thread local + // storage lots of new/delete. + vtkSMPThreadLocalObject<vtkIdList> PIds; + vtkSMPThreadLocal<double> ThreadMean; + vtkSMPThreadLocal<vtkIdType> ThreadCount; + + ComputeMeanDistance(T *points, vtkAbstractPointLocator *loc, int size, float *d) : + Points(points), Locator(loc), SampleSize(size), Distance(d), Mean(0.0) + { + } + + // Just allocate a little bit of memory to get started. + void Initialize() + { + vtkIdList*& pIds = this->PIds.Local(); + pIds->Allocate(128); //allocate some memory + + double &threadMean = this->ThreadMean.Local(); + threadMean = 0.0; + + vtkIdType &threadCount = this->ThreadCount.Local(); + threadCount = 0; + } + + // Compute average distance for each point, plus accumlate summation of + // mean distances and count (for averaging in the Reduce() method). + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + const T *px = this->Points + 3*ptId; + const T *py; + double x[3], y[3]; + vtkIdList*& pIds = this->PIds.Local(); + double &threadMean = this->ThreadMean.Local(); + vtkIdType &threadCount = this->ThreadCount.Local(); + + for ( ; ptId < endPtId; ++ptId) + { + x[0] = static_cast<double>(*px++); + x[1] = static_cast<double>(*px++); + x[2] = static_cast<double>(*px++); + + // The method FindClosestNPoints will include the current point, so + // we increase the sample size by one. + this->Locator->FindClosestNPoints(this->SampleSize+1, x, pIds); + vtkIdType numPts = pIds->GetNumberOfIds(); + + double sum = 0.0; + vtkIdType nei; + for (int sample=0; sample < numPts; ++sample) + { + nei = pIds->GetId(sample); + if ( nei != ptId ) //exclude ourselves + { + py = this->Points + 3*nei; + y[0] = static_cast<double>(*py++); + y[1] = static_cast<double>(*py++); + y[2] = static_cast<double>(*py); + sum += sqrt( vtkMath::Distance2BetweenPoints(x,y) ); + } + }//sum the lengths of all samples exclusing current point + + // Average the lengths; again exclude ourselves + if ( numPts > 0 ) + { + this->Distance[ptId] = sum / static_cast<double>(numPts-1); + threadMean += this->Distance[ptId]; + threadCount++; + } + else //ignore if no points are found, something bad has happened + { + this->Distance[ptId] = VTK_FLOAT_MAX; //the effect is to eliminate it + } + } + } + + // Compute the mean by compositing all threads + void Reduce() + { + double mean=0.0; + vtkIdType count=0; + + vtkSMPThreadLocal<double>::iterator mItr; + vtkSMPThreadLocal<double>::iterator mEnd = this->ThreadMean.end(); + for ( mItr=this->ThreadMean.begin(); mItr != mEnd; ++mItr ) + { + mean += *mItr; + } + + vtkSMPThreadLocal<vtkIdType>::iterator cItr; + vtkSMPThreadLocal<vtkIdType>::iterator cEnd = this->ThreadCount.end(); + for ( cItr=this->ThreadCount.begin(); cItr != cEnd; ++cItr ) + { + count += *cItr; + } + + this->Mean = mean / static_cast<double>(count); + } + + static void Execute(vtkStatisticalOutlierRemoval *self, vtkIdType numPts, + T *points, float *distances, double& mean) + { + ComputeMeanDistance compute(points, self->GetLocator(), + self->GetSampleSize(), distances); + vtkSMPTools::For(0, numPts, compute); + mean = compute.Mean; + } + +}; //ComputeMeanDistance + +//---------------------------------------------------------------------------- +// Now that the mean is known, compute the standard deviation +struct ComputeStdDev +{ + float *Distances; + double Mean; + double StdDev; + vtkSMPThreadLocal<double> ThreadSigma; + vtkSMPThreadLocal<vtkIdType> ThreadCount; + + ComputeStdDev(float *d, double mean) : Distances(d), Mean(mean), StdDev(0.0) + { + } + + void Initialize() + { + double &threadSigma = this->ThreadSigma.Local(); + threadSigma = 0.0; + + vtkIdType &threadCount = this->ThreadCount.Local(); + threadCount = 0; + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + double &threadSigma = this->ThreadSigma.Local(); + vtkIdType &threadCount = this->ThreadCount.Local(); + float d; + + for ( ; ptId < endPtId; ++ptId) + { + d = this->Distances[ptId]; + if ( d < VTK_FLOAT_MAX ) + { + threadSigma += (this->Mean - d) * (this->Mean - d); + threadCount++; + } + else + { + continue; //skip bad point + } + } + } + + void Reduce() + { + double sigma=0.0; + vtkIdType count=0; + + vtkSMPThreadLocal<double>::iterator sItr; + vtkSMPThreadLocal<double>::iterator sEnd = this->ThreadSigma.end(); + for ( sItr=this->ThreadSigma.begin(); sItr != sEnd; ++sItr ) + { + sigma += *sItr; + } + + vtkSMPThreadLocal<vtkIdType>::iterator cItr; + vtkSMPThreadLocal<vtkIdType>::iterator cEnd = this->ThreadCount.end(); + for ( cItr=this->ThreadCount.begin(); cItr != cEnd; ++cItr ) + { + count += *cItr; + } + + this->StdDev = sqrt( sigma / static_cast<double>(count) ); + } + + static void Execute(vtkIdType numPts, float *distances, + double mean, double& sigma) + { + ComputeStdDev stdDev(distances, mean); + vtkSMPTools::For(0, numPts, stdDev); + sigma = stdDev.StdDev; + } + +}; //ComputeStdDev + +//---------------------------------------------------------------------------- +// Statistics are computed, now filter the points +struct RemoveOutliers +{ + double Mean; + double Sigma; + float *Distances; + vtkIdType *PointMap; + + RemoveOutliers(double mean, double sigma, float *distances, vtkIdType *map) : + Mean(mean), Sigma(sigma), Distances(distances), PointMap(map) + { + } + + void operator() (vtkIdType ptId, vtkIdType endPtId) + { + vtkIdType *map = this->PointMap + ptId; + float *d = this->Distances + ptId; + double mean=this->Mean, sigma=this->Sigma; + + for ( ; ptId < endPtId; ++ptId) + { + *map++ = ( fabs(*d++ - mean) <= sigma ? 1 : -1 ); + } + } + + static void Execute(vtkIdType numPts, float *distances, double mean, + double sigma, vtkIdType *map) + { + RemoveOutliers remove(mean, sigma, distances, map); + vtkSMPTools::For(0, numPts, remove); + } + +}; //RemoveOutliers + + +} //anonymous namespace + + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +vtkStatisticalOutlierRemoval::vtkStatisticalOutlierRemoval() +{ + this->SampleSize = 25; + this->StandardDeviationFactor = 1.0; + this->Locator = vtkStaticPointLocator::New(); + + this->ComputedMean = 0.0; + this->ComputedStandardDeviation = 0.0; + +} + +//---------------------------------------------------------------------------- +vtkStatisticalOutlierRemoval::~vtkStatisticalOutlierRemoval() +{ + this->SetLocator(NULL); +} + +//---------------------------------------------------------------------------- +// Traverse all the input points and gather statistics about average distance +// between them, and the standard deviation of variation. Then filter points +// within a specified deviation from the mean. +int vtkStatisticalOutlierRemoval::FilterPoints(vtkPointSet *input) +{ + // Perform the point removal + // Start by building the locator + if ( !this->Locator ) + { + vtkErrorMacro(<<"Point locator required\n"); + return 0; + } + this->Locator->SetDataSet(input); + this->Locator->BuildLocator(); + + // Compute statistics across the point cloud. Start my computing + // mean distance to N closest neighbors. + vtkIdType numPts = input->GetNumberOfPoints(); + float *dist = new float [numPts]; + void *inPtr = input->GetPoints()->GetVoidPointer(0); + double mean=0.0, sigma=0.0; + switch (input->GetPoints()->GetDataType()) + { + vtkTemplateMacro(ComputeMeanDistance<VTK_TT>:: + Execute(this, numPts, (VTK_TT *)inPtr, dist, mean)); + } + + // At this point the mean distance for each point, and across the point + // cloud is known. Now compute global standard deviation. + ComputeStdDev::Execute(numPts, dist, mean, sigma); + + // Finally filter the points based on specified deviation range. + RemoveOutliers::Execute(numPts, dist, mean, + this->StandardDeviationFactor*sigma, this->PointMap); + + // Assign derived variable + this->ComputedMean = mean; + this->ComputedStandardDeviation = sigma; + + // Clean up + delete [] dist; + + return 1; +} + +//---------------------------------------------------------------------------- +void vtkStatisticalOutlierRemoval::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Sample Size: " << this->SampleSize << "\n"; + os << indent << "Standard Deviation Factor: " + << this->StandardDeviationFactor << "\n"; + os << indent << "Locator: " << this->Locator << "\n"; + + os << indent << "Computed Mean: " << this->ComputedMean << "\n"; + os << indent << "Computed Standard Deviation: " + << this->ComputedStandardDeviation << "\n"; +} diff --git a/Filters/Points/vtkStatisticalOutlierRemoval.h b/Filters/Points/vtkStatisticalOutlierRemoval.h new file mode 100644 index 00000000000..bdcc3cc7cdf --- /dev/null +++ b/Filters/Points/vtkStatisticalOutlierRemoval.h @@ -0,0 +1,123 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkStatisticalOutlierRemoval.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkStatisticalOutlierRemoval - remove sparse outlier points + +// .SECTION Description +// The vtkStatisticalOutlierRemoval filter removes sparse outlier points +// through statistical analysis. The average (mean) distance between points +// in the point cloud is computed (taking a local sample size around each +// point); followed by computation of the global standard deviation of +// distances between points. This global, statistical information is compared +// against the mean separation distance for each point; those points whose +// average separation is greater than the user-specified variation in +// a multiple of standard deviation are removed. +// +// Note that while any vtkPointSet type can be provided as input, the output is +// represented by an explicit representation of points via a +// vtkPolyData. This output polydata will populate its instance of vtkPoints, +// but no cells will be defined (i.e., no vtkVertex or vtkPolyVertex are +// contained in the output). Also, after filter execution, the user can +// request a vtkIdType* map which indicates how the input points were mapped +// to the output. A value of map[i] (where i is the ith input point) less +// than 0 means that the ith input point was removed. (See also the +// superclass documentation for accessing the removed points through the +// filter's second output.) + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkPointCloudFilter vtkRadiusOutlierRemoval vtkExtractPoints +// vtkThresholdPoints + +#ifndef vtkStatisticalOutlierRemoval_h +#define vtkStatisticalOutlierRemoval_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPointCloudFilter.h" + +class vtkAbstractPointLocator; +class vtkPointSet; + + +class VTKFILTERSPOINTS_EXPORT vtkStatisticalOutlierRemoval : public vtkPointCloudFilter +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkStatisticalOutlierRemoval *New(); + vtkTypeMacro(vtkStatisticalOutlierRemoval,vtkPointCloudFilter); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // For each point sampled, specify the number of the closest, surrounding + // points used to compute statistics. By default 25 points are used. Smaller + // numbers may speed performance. + vtkSetClampMacro(SampleSize,int,1,VTK_INT_MAX); + vtkGetMacro(SampleSize,int); + + // Description: + // The filter uses this specified standard deviation factor to extract + // points. By default, points within 1.0 standard deviations (i.e., a + // StandardDeviationFactor=1.0) of the mean distance to neighboring + // points are retained. + vtkSetClampMacro(StandardDeviationFactor,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(StandardDeviationFactor,double); + + // Description: + // Specify a point locator. By default a vtkStaticPointLocator is + // used. The locator performs efficient searches to locate points + // surroinding a sample point. + void SetLocator(vtkAbstractPointLocator *locator); + vtkGetObjectMacro(Locator,vtkAbstractPointLocator); + + // Description: + // After execution, return the value of the computed mean. Before execution + // the value returned is invalid. + vtkSetClampMacro(ComputedMean,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(ComputedMean,double); + + // Description: + // After execution, return the value of the computed sigma (standard + // deviation). Before execution the value returned is invalid. + vtkSetClampMacro(ComputedStandardDeviation,double,0.0,VTK_FLOAT_MAX); + vtkGetMacro(ComputedStandardDeviation,double); + +protected: + vtkStatisticalOutlierRemoval(); + ~vtkStatisticalOutlierRemoval(); + + int SampleSize; + double StandardDeviationFactor; + vtkAbstractPointLocator *Locator; + + // Derived quantities + double ComputedMean; + double ComputedStandardDeviation; + + // All derived classes must implement this method. Note that a side effect of + // the class is to populate the PointMap. Zero is returned if there is a failure. + virtual int FilterPoints(vtkPointSet *input); + +private: + vtkStatisticalOutlierRemoval(const vtkStatisticalOutlierRemoval&) VTK_DELETE_FUNCTION; + void operator=(const vtkStatisticalOutlierRemoval&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Filters/Points/vtkVoxelGrid.cxx b/Filters/Points/vtkVoxelGrid.cxx new file mode 100644 index 00000000000..5951bc7ea74 --- /dev/null +++ b/Filters/Points/vtkVoxelGrid.cxx @@ -0,0 +1,299 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkVoxelGrid.cxx + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkVoxelGrid.h" + +#include "vtkObjectFactory.h" +#include "vtkDoubleArray.h" +#include "vtkPointSet.h" +#include "vtkPointData.h" +#include "vtkPoints.h" +#include "vtkStaticPointLocator.h" +#include "vtkLinearKernel.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkSMPTools.h" +#include "vtkSMPThreadLocalObject.h" +#include "vtkArrayListTemplate.h" // For processing attribute data + + +vtkStandardNewMacro(vtkVoxelGrid); +vtkCxxSetObjectMacro(vtkVoxelGrid,Kernel,vtkInterpolationKernel); + +//---------------------------------------------------------------------------- +// Helper classes to support efficient computing, and threaded execution. +namespace { + +//---------------------------------------------------------------------------- +// The threaded core of the algorithm (first pass) +template <typename T> +struct Subsample +{ + T *InPoints; + vtkStaticPointLocator *Locator; + vtkInterpolationKernel *Kernel; + const vtkIdType *BinMap; + ArrayList Arrays; + T *OutPoints; + + // Don't want to allocate working arrays on every thread invocation. Thread local + // storage prevents lots of new/delete. + vtkSMPThreadLocalObject<vtkIdList> PIds; + vtkSMPThreadLocalObject<vtkDoubleArray> Weights; + + Subsample(T* inPts, vtkPointData *inPD, vtkPointData *outPD, + vtkStaticPointLocator *loc, vtkInterpolationKernel *k, + vtkIdType nBins, vtkIdType *binMap, T *outPts) : + InPoints(inPts), Locator(loc), Kernel(k), BinMap(binMap), OutPoints(outPts) + { + this->Arrays.AddArrays(nBins, inPD, outPD); + } + + // Just allocate a little bit of memory to get started. + void Initialize() + { + vtkIdList*& pIds = this->PIds.Local(); + pIds->Allocate(128); //allocate some memory + vtkDoubleArray*& weights = this->Weights.Local(); + weights->Allocate(128); + } + + void operator() (vtkIdType binId, vtkIdType endBinId) + { + T *px; + T *py = this->OutPoints + 3*binId; + const vtkIdType *map = this->BinMap; + vtkIdList*& pIds = this->PIds.Local(); + vtkIdType numWeights; + vtkDoubleArray*& weights = this->Weights.Local(); + double y[3], count; + vtkIdType numIds, id; + vtkStaticPointLocator *loc = this->Locator; + + for ( ; binId < endBinId; ++binId ) + { + if ( map[binId] >= 0 ) + { + y[0] = y[1] = y[2] = 0.0; + loc->GetBucketIds(binId,pIds); + numIds = pIds->GetNumberOfIds(); + for (id=0; id < numIds; ++id) + { + px = this->InPoints + 3*pIds->GetId(id); + y[0] += *px++; + y[1] += *px++; + y[2] += *px; + } + count = static_cast<double>(numIds); + y[0] /= count; + y[1] /= count; + y[2] /= count; + *py++ = y[0]; + *py++ = y[1]; + *py++ = y[2]; + + // Now interpolate attributes + numWeights = this->Kernel->ComputeWeights(y, pIds, weights); + this->Arrays.Interpolate(numWeights, pIds->GetPointer(0), + weights->GetPointer(0), binId); + }// if occupied bin + }//for all bins in this batch + } + + void Reduce() + { + } + + static void Execute(T *inPts, vtkPointData *inPD, vtkPointData *outPD, + vtkStaticPointLocator *loc, vtkInterpolationKernel *k, + vtkIdType numBins, vtkIdType *binMap, T *outPts) + { + Subsample subsample(inPts, inPD, outPD, loc, k, numBins, binMap, outPts); + vtkSMPTools::For(0, numBins, subsample); + } + +}; //Subsample + +} //anonymous namespace + + +//================= Begin class proper ======================================= +//---------------------------------------------------------------------------- +vtkVoxelGrid::vtkVoxelGrid() +{ + this->Locator = vtkStaticPointLocator::New(); + this->ConfigurationStyle = vtkVoxelGrid::AUTOMATIC; + + this->Divisions[0] = this->Divisions[1] = this->Divisions[2] = 50; + this->LeafSize[0] = this->LeafSize[1] = this->LeafSize[2] = 1.0; + this->NumberOfPointsPerBin = 10; + + this->Kernel = vtkLinearKernel::New(); +} + +//---------------------------------------------------------------------------- +vtkVoxelGrid::~vtkVoxelGrid() +{ + this->Locator->Delete(); + this->SetKernel(NULL); +} + +//---------------------------------------------------------------------------- +// Produce the output data +int vtkVoxelGrid::RequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + // get the info objects + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + // get the input and output + vtkPointSet *input = vtkPointSet::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData *output = vtkPolyData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // Check the input + if ( !input || !output ) + { + return 1; + } + vtkIdType numPts = input->GetNumberOfPoints(); + if ( numPts < 1 ) + { + return 1; + } + + // Make sure there is a kernel + if ( !this->Kernel ) + { + vtkErrorMacro(<<"Interpolation kernel required\n"); + return 1; + } + + bool valid = 1; + if ( this->LeafSize[0] <= 0.0 || this->LeafSize[1] <= 0.0 || this->LeafSize[2] <= 0.0 || + this->Divisions[0] < 1 || this->Divisions[1] < 1 || this->Divisions[2] < 1 ) + { + valid = false; + } + + // Configure and build the locator + if ( valid && this->ConfigurationStyle == vtkVoxelGrid::MANUAL ) + { + this->Locator->AutomaticOff(); + this->Locator->SetDivisions(this->Divisions); + } + else if ( valid && this->ConfigurationStyle == vtkVoxelGrid::SPECIFY_LEAF_SIZE ) + { + double bounds[6]; + int divs[3]; + this->Locator->AutomaticOff(); + input->GetBounds(bounds); + divs[0] = (bounds[1]-bounds[0]) / this->LeafSize[0]; + divs[1] = (bounds[3]-bounds[2]) / this->LeafSize[1]; + divs[2] = (bounds[5]-bounds[4]) / this->LeafSize[2]; + this->Locator->SetDivisions(divs); + } + else // this->ConfigurationStyle == vtkVoxelGrid::AUTOMATIC + { + this->Locator->AutomaticOn(); + this->Locator->SetNumberOfPointsPerBucket(this->NumberOfPointsPerBin); + } + this->Locator->SetDataSet(input); + this->Locator->BuildLocator(); + + // Run through the locator and compute the number of output points, + // and build a map of the bin number to output point. This is a prefix sum. + vtkIdType numOutPts=0; + vtkIdType binNum, numBins = this->Locator->GetNumberOfBuckets(); + vtkIdType *binMap = new vtkIdType [numBins]; + for ( binNum=0; binNum < numBins; ++binNum ) + { + if ( this->Locator->GetNumberOfPointsInBucket(binNum) > 0 ) + { + binMap[binNum] = numOutPts++; + } + else + { + binMap[binNum] = -1; + } + } + + // Grab the point data for interpolation + vtkPointData *inPD = input->GetPointData(); + vtkPointData *outPD = output->GetPointData(); + outPD->InterpolateAllocate(inPD,numBins); + + // Finally run over all of the bins, and those that are not emoty are + // processed. The processing consists of averaging all of the points found + // in the bin, and setting the average point position in the output points. + vtkPoints *points = input->GetPoints()->NewInstance(); + points->SetDataType(input->GetPoints()->GetDataType()); + points->SetNumberOfPoints(numOutPts); + output->SetPoints(points); + + void *inPtr = input->GetPoints()->GetVoidPointer(0); + void *outPtr = output->GetPoints()->GetVoidPointer(0); + switch (output->GetPoints()->GetDataType()) + { + vtkTemplateMacro(Subsample<VTK_TT>::Execute((VTK_TT *)inPtr, inPD, outPD, + this->Locator, this->Kernel, numBins, binMap, (VTK_TT *)outPtr)); + } + + // Send attributes to output + int numPtArrays = input->GetPointData()->GetNumberOfArrays(); + for (int i=0; i<numPtArrays; ++i) + { + output->GetPointData()->AddArray(input->GetPointData()->GetArray(i)); + } + + // Clean up + points->Delete(); + delete [] binMap; + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkVoxelGrid:: +FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); + return 1; +} + +//---------------------------------------------------------------------------- +void vtkVoxelGrid::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Configuration Style: " << this->ConfigurationStyle << endl; + + os << indent << "Divisions: (" + << this->Divisions[0] << "," + << this->Divisions[1] << "," + << this->Divisions[2] << ")\n"; + + os << indent << "Leaf Size: (" + << this->LeafSize[0] << "," + << this->LeafSize[1] << "," + << this->LeafSize[2] << ")\n"; + + os << indent << "Number of Points Per Bin: " + << this->NumberOfPointsPerBin << endl; +} diff --git a/Filters/Points/vtkVoxelGrid.h b/Filters/Points/vtkVoxelGrid.h new file mode 100644 index 00000000000..c0356504b1d --- /dev/null +++ b/Filters/Points/vtkVoxelGrid.h @@ -0,0 +1,141 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkVoxelGrid.h + + Copyright (c) Kitware, Inc. + All rights reserved. + See LICENSE file for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkVoxelGrid - subsample points using uniform binning + +// .SECTION Description +// vtkVoxelGrid is a filter that subsamples a point cloud based on a regular +// binning of space. Basically the algorithm operates by dividing space into +// a volume of M x N x O bins, and then for each bin averaging all of the +// points positions into a single representive point. Several strategies for +// computing the binning can be used: 1) manual configuration of a requiring +// specifying bin dimensions (the bounds are calculated from the data); 2) by +// explicit specification of the bin size in world coordinates (x-y-z +// lengths); and 3) an automatic process in which the user specifies an +// approximate, average number of points per bin and dimensions and bin size +// are computed automatically. (Note that under the hood a +// vtkStaticPointLocator is used.) +// +// While any vtkPointSet type can be provided as input, the output is +// represented by an explicit representation of points via a +// vtkPolyData. This output polydata will populate its instance of vtkPoints, +// but no cells will be defined (i.e., no vtkVertex or vtkPolyVertex are +// contained in the output). + +// .SECTION Caveats +// This class has been threaded with vtkSMPTools. Using TBB or other +// non-sequential type (set in the CMake variable +// VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly. + +// .SECTION See Also +// vtkStaticPointLocator vtkPointCloudFilter vtkQuadricClustering + +#ifndef vtkVoxelGrid_h +#define vtkVoxelGrid_h + +#include "vtkFiltersPointsModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +class vtkStaticPointLocator; +class vtkInterpolationKernel; + + +class VTKFILTERSPOINTS_EXPORT vtkVoxelGrid : public vtkPolyDataAlgorithm +{ +public: + // Description: + // Standard methods for instantiating, obtaining type information, and + // printing information. + static vtkVoxelGrid *New(); + vtkTypeMacro(vtkVoxelGrid,vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // This enum is used to configure the operation of the filter. + enum Style + { + MANUAL=0, + SPECIFY_LEAF_SIZE=1, + AUTOMATIC=2 + }; + + // Description: + // Configure how the filter is to operate. The user can choose to manually + // specify the binning volume (by setting its dimensions via MANUAL style); or + // specify a leaf bin size in the x-y-z directions (SPECIFY_LEAF_SIZE); or + // in AUTOMATIC style, use a rough average number of points in each bin + // guide the bin size and binning volume dimensions. By default, AUTOMATIC + // configuration style is used. + vtkSetMacro(ConfigurationStyle,int); + vtkGetMacro(ConfigurationStyle,int); + void SetConfigurationStyleToManual() + { this->SetConfigurationStyle(MANUAL); } + void SetConfigurationStyleToLeafSize() + { this->SetConfigurationStyle(SPECIFY_LEAF_SIZE); } + void SetConfigurationStyleToAutomatic() + { this->SetConfigurationStyle(AUTOMATIC); } + + // Description: + // Set the number of divisions in x-y-z directions (the binning volume + // dimensions). This data member is used when the configuration style is + // set to MANUAL. + vtkSetVector3Macro(Divisions,int); + vtkGetVectorMacro(Divisions,int,3); + + // Description: + // Set the bin size in the x-y-z directions. This data member is + // used when the configuration style is set to SPECIFY_LEAF_SIZE. The class will + // use these x-y-z lengths, within the bounding box of the point cloud, + // to determine the binning dimensions. + vtkSetVector3Macro(LeafSize,double); + vtkGetVectorMacro(LeafSize,double,3); + + // Description: + // Specify the average number of points in each bin. Larger values + // result in higher rates of subsampling. This data member is used when the + // configuration style is set to AUTOMATIC. The class will automatically + // determine the binning dimensions in the x-y-z directions. + vtkSetClampMacro(NumberOfPointsPerBin,int,1,VTK_INT_MAX); + vtkGetMacro(NumberOfPointsPerBin,int); + + // Description: + // Specify an interpolation kernel to combine the point attributes. By + // default a vtkLinearKernel is used (i.e., average values). The + // interpolation kernel changes the basis of the interpolation. + void SetKernel(vtkInterpolationKernel *kernel); + vtkGetObjectMacro(Kernel,vtkInterpolationKernel); + +protected: + vtkVoxelGrid(); + ~vtkVoxelGrid(); + + vtkStaticPointLocator *Locator; + int ConfigurationStyle; + + int Divisions[3]; + double LeafSize[3]; + int NumberOfPointsPerBin; + vtkInterpolationKernel *Kernel; + + virtual int RequestData(vtkInformation *, vtkInformationVector **, + vtkInformationVector *); + virtual int FillInputPortInformation(int port, vtkInformation *info); + +private: + vtkVoxelGrid(const vtkVoxelGrid&) VTK_DELETE_FUNCTION; + void operator=(const vtkVoxelGrid&) VTK_DELETE_FUNCTION; + +}; + +#endif diff --git a/Remote/point-cloud.remote.cmake b/Remote/point-cloud.remote.cmake deleted file mode 100644 index 502b6a50e39..00000000000 --- a/Remote/point-cloud.remote.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# -# vtkPointCloud - specialized filters for processing point clouds -# GIT_REPOSITORY git@gitlab.kitware.com:vtk/point-cloud.git - - -vtk_fetch_module(vtkPointCloud - "Point Cloud processing in VTK" - GIT_REPOSITORY https://gitlab.kitware.com/vtk/point-cloud.git - GIT_TAG master - ) -- GitLab