OpenVDB  9.0.0
TopologyToLevelSet.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @file TopologyToLevelSet.h
5 ///
6 /// @brief This tool generates a narrow-band signed distance field / level set
7 /// from the interface between active and inactive voxels in a vdb grid.
8 ///
9 /// @par Example:
10 /// Combine with @c tools::PointsToVolume for fast point cloud to level set conversion.
11 
12 #ifndef OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED
13 #define OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED
14 
15 #include "LevelSetFilter.h"
16 #include "Morphology.h" // for erodeActiveValues and dilateActiveValues
17 #include "SignedFloodFill.h"
18 
19 #include <openvdb/Grid.h>
20 #include <openvdb/Types.h>
21 #include <openvdb/math/FiniteDifference.h> // for math::BiasedGradientScheme
23 #include <openvdb/openvdb.h>
25 #include <tbb/task_group.h>
26 #include <algorithm> // for std::min(), std::max()
27 #include <vector>
28 
29 
30 namespace openvdb {
32 namespace OPENVDB_VERSION_NAME {
33 namespace tools {
34 
35 
36 /// @brief Compute the narrow-band signed distance to the interface between
37 /// active and inactive voxels in the input grid.
38 ///
39 /// @return A shared pointer to a new sdf / level set grid of type @c float
40 ///
41 /// @param grid Input grid of arbitrary type whose active voxels are used
42 /// in constructing the level set.
43 /// @param halfWidth Half the width of the narrow band in voxel units.
44 /// @param closingSteps Number of morphological closing steps used to fill gaps
45 /// in the active voxel region.
46 /// @param dilation Number of voxels to expand the active voxel region.
47 /// @param smoothingSteps Number of smoothing interations.
48 template<typename GridT>
49 typename GridT::template ValueConverter<float>::Type::Ptr
50 topologyToLevelSet(const GridT& grid, int halfWidth = 3, int closingSteps = 1, int dilation = 0,
51  int smoothingSteps = 0);
52 
53 
54 /// @brief Compute the narrow-band signed distance to the interface between
55 /// active and inactive voxels in the input grid.
56 ///
57 /// @return A shared pointer to a new sdf / level set grid of type @c float
58 ///
59 /// @param grid Input grid of arbitrary type whose active voxels are used
60 /// in constructing the level set.
61 /// @param halfWidth Half the width of the narrow band in voxel units.
62 /// @param closingSteps Number of morphological closing steps used to fill gaps
63 /// in the active voxel region.
64 /// @param dilation Number of voxels to expand the active voxel region.
65 /// @param smoothingSteps Number of smoothing interations.
66 /// @param interrupt Optional object adhering to the util::NullInterrupter interface.
67 template<typename GridT, typename InterrupterT>
68 typename GridT::template ValueConverter<float>::Type::Ptr
69 topologyToLevelSet(const GridT& grid, int halfWidth = 3, int closingSteps = 1, int dilation = 0,
70  int smoothingSteps = 0, InterrupterT* interrupt = nullptr);
71 
72 
73 ////////////////////////////////////////
74 
75 /// @cond OPENVDB_DOCS_INTERNAL
76 
77 namespace ttls_internal {
78 
79 
80 template<typename TreeT>
81 struct DilateOp
82 {
83  DilateOp(TreeT& t, int n) : tree(&t), size(n) {}
84  void operator()() const {
86  }
87  TreeT* tree;
88  const int size;
89 };
90 
91 
92 template<typename TreeT>
93 struct ErodeOp
94 {
95  ErodeOp(TreeT& t, int n) : tree(&t), size(n) {}
96  void operator()() const {
97  tools::erodeActiveValues(*tree, /*iterations=*/size, tools::NN_FACE, tools::IGNORE_TILES);
98  tools::pruneInactive(*tree);
99  }
100  TreeT* tree;
101  const int size;
102 };
103 
104 
105 template<typename TreeType>
106 struct OffsetAndMinComp
107 {
108  using LeafNodeType = typename TreeType::LeafNodeType;
109  using ValueType = typename TreeType::ValueType;
110 
111  OffsetAndMinComp(std::vector<LeafNodeType*>& lhsNodes,
112  const TreeType& rhsTree, ValueType offset)
113  : mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes[0]), mRhsTree(&rhsTree), mOffset(offset)
114  {
115  }
116 
117  void operator()(const tbb::blocked_range<size_t>& range) const
118  {
119  using Iterator = typename LeafNodeType::ValueOnIter;
120 
121  tree::ValueAccessor<const TreeType> rhsAcc(*mRhsTree);
122  const ValueType offset = mOffset;
123 
124  for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
125 
126  LeafNodeType& lhsNode = *mLhsNodes[n];
127  const LeafNodeType * rhsNodePt = rhsAcc.probeConstLeaf(lhsNode.origin());
128  if (!rhsNodePt) continue;
129 
130  for (Iterator it = lhsNode.beginValueOn(); it; ++it) {
131  ValueType& val = const_cast<ValueType&>(it.getValue());
132  val = std::min(val, offset + rhsNodePt->getValue(it.pos()));
133  }
134  }
135  }
136 
137 private:
138  LeafNodeType * * const mLhsNodes;
139  TreeType const * const mRhsTree;
140  ValueType const mOffset;
141 }; // struct OffsetAndMinComp
142 
143 
144 template<typename GridType, typename InterrupterType>
145 inline void
146 normalizeLevelSet(GridType& grid, const int halfWidthInVoxels, InterrupterType* interrupt = nullptr)
147 {
148  LevelSetFilter<GridType, GridType, InterrupterType> filter(grid, interrupt);
149  filter.setSpatialScheme(math::FIRST_BIAS);
150  filter.setNormCount(halfWidthInVoxels);
151  filter.normalize();
152  filter.prune();
153 }
154 
155 
156 template<typename GridType, typename InterrupterType>
157 inline void
158 smoothLevelSet(GridType& grid, int iterations, int halfBandWidthInVoxels,
159  InterrupterType* interrupt = nullptr)
160 {
161  using ValueType = typename GridType::ValueType;
162  using TreeType = typename GridType::TreeType;
163  using LeafNodeType = typename TreeType::LeafNodeType;
164 
165  GridType filterGrid(grid);
166 
167  LevelSetFilter<GridType, GridType, InterrupterType> filter(filterGrid, interrupt);
168  filter.setSpatialScheme(math::FIRST_BIAS);
169 
170  for (int n = 0; n < iterations; ++n) {
171  if (interrupt && interrupt->wasInterrupted()) break;
172  filter.mean(1);
173  }
174 
175  std::vector<LeafNodeType*> nodes;
176  grid.tree().getNodes(nodes);
177 
178  const ValueType offset = ValueType(double(0.5) * grid.transform().voxelSize()[0]);
179 
180  tbb::parallel_for(tbb::blocked_range<size_t>(0, nodes.size()),
181  OffsetAndMinComp<TreeType>(nodes, filterGrid.tree(), -offset));
182 
183  // Clean up any damanage that was done by the min operation
184  normalizeLevelSet(grid, halfBandWidthInVoxels, interrupt);
185 }
186 
187 
188 } // namespace ttls_internal
189 
190 /// @endcond
191 
192 template<typename GridT, typename InterrupterT>
193 typename GridT::template ValueConverter<float>::Type::Ptr
194 topologyToLevelSet(const GridT& grid, int halfWidth, int closingSteps, int dilation,
195  int smoothingSteps, InterrupterT* interrupt)
196 {
197  using MaskTreeT = typename GridT::TreeType::template ValueConverter<ValueMask>::Type;
198  using FloatTreeT = typename GridT::TreeType::template ValueConverter<float>::Type;
199  using FloatGridT = Grid<FloatTreeT>;
200 
201  // Check inputs
202 
203  halfWidth = std::max(halfWidth, 1);
204  closingSteps = std::max(closingSteps, 0);
205  dilation = std::max(dilation, 0);
206 
207  if (!grid.hasUniformVoxels()) {
208  OPENVDB_THROW(ValueError, "Non-uniform voxels are not supported!");
209  }
210 
211  // Copy the topology into a MaskGrid.
212  MaskTreeT maskTree( grid.tree(), false/*background*/, openvdb::TopologyCopy() );
213 
214  // Morphological closing operation.
215  tools::dilateActiveValues(maskTree, closingSteps + dilation, tools::NN_FACE, tools::IGNORE_TILES);
216  tools::erodeActiveValues(maskTree, /*iterations=*/closingSteps, tools::NN_FACE, tools::IGNORE_TILES);
217  tools::pruneInactive(maskTree);
218 
219  // Generate a volume with an implicit zero crossing at the boundary
220  // between active and inactive values in the input grid.
221  const float background = float(grid.voxelSize()[0]) * float(halfWidth);
222  typename FloatTreeT::Ptr lsTree(
223  new FloatTreeT( maskTree, /*out=*/background, /*in=*/-background, openvdb::TopologyCopy() ) );
224 
225  tbb::task_group pool;
226  pool.run( ttls_internal::ErodeOp< MaskTreeT >( maskTree, halfWidth ) );
227  pool.run( ttls_internal::DilateOp<FloatTreeT>( *lsTree , halfWidth ) );
228  pool.wait();// wait for both tasks to complete
229 
230  lsTree->topologyDifference( maskTree );
231  tools::pruneLevelSet( *lsTree, /*threading=*/true);
232 
233  // Create a level set grid from the tree
234  typename FloatGridT::Ptr lsGrid = FloatGridT::create( lsTree );
235  lsGrid->setTransform( grid.transform().copy() );
236  lsGrid->setGridClass( openvdb::GRID_LEVEL_SET );
237 
238  // Use a PDE based scheme to propagate distance values from the
239  // implicit zero crossing.
240  ttls_internal::normalizeLevelSet(*lsGrid, 3*halfWidth, interrupt);
241 
242  // Additional filtering
243  if (smoothingSteps > 0) {
244  ttls_internal::smoothLevelSet(*lsGrid, smoothingSteps, halfWidth, interrupt);
245  }
246 
247  return lsGrid;
248 }
249 
250 
251 template<typename GridT>
252 typename GridT::template ValueConverter<float>::Type::Ptr
253 topologyToLevelSet(const GridT& grid, int halfWidth, int closingSteps, int dilation, int smoothingSteps)
254 {
255  util::NullInterrupter interrupt;
256  return topologyToLevelSet(grid, halfWidth, closingSteps, dilation, smoothingSteps, &interrupt);
257 }
258 
259 
260 ////////////////////////////////////////
261 
262 
263 // Explicit Template Instantiation
264 
265 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
266 
267 #ifdef OPENVDB_INSTANTIATE_TOPOLOGYTOLEVELSET
269 #endif
270 
271 #define _FUNCTION(TreeT) \
272  Grid<TreeT>::ValueConverter<float>::Type::Ptr topologyToLevelSet(const Grid<TreeT>&, int, int, int, int, \
273  util::NullInterrupter*)
275 #undef _FUNCTION
276 
277 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
278 
279 
280 } // namespace tools
281 } // namespace OPENVDB_VERSION_NAME
282 } // namespace openvdb
283 
284 #endif // OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED
285 
Performs various types of level set deformations with interface tracking. These unrestricted deformat...
Implementation of morphological dilation and erosion.
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
Container class that associates a tree with a transform and metadata.
Definition: Grid.h:577
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:564
Definition: Exceptions.h:65
GridType
List of types that are currently supported by NanoVDB.
Definition: NanoVDB.h:216
@ FIRST_BIAS
Definition: FiniteDifference.h:167
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
GridT::template ValueConverter< float >::Type::Ptr topologyToLevelSet(const GridT &grid, int halfWidth=3, int closingSteps=1, int dilation=0, int smoothingSteps=0, InterrupterT *interrupt=nullptr)
Compute the narrow-band signed distance to the interface between active and inactive voxels in the in...
Definition: TopologyToLevelSet.h:194
@ NN_FACE
Definition: Morphology.h:58
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition: Prune.h:390
void erodeActiveValues(TreeOrLeafManagerT &tree, const int iterations=1, const NearestNeighbors nn=NN_FACE, const TilePolicy mode=PRESERVE_TILES, const bool threaded=true)
Topologically erode all active values (i.e. both voxels and tiles) in a tree using one of three neare...
Definition: Morphology.h:1132
void dilateActiveValues(TreeOrLeafManagerT &tree, const int iterations=1, const NearestNeighbors nn=NN_FACE, const TilePolicy mode=PRESERVE_TILES, const bool threaded=true)
Topologically dilate all active values (i.e. both voxels and tiles) in a tree using one of three near...
Definition: Morphology.h:1055
@ IGNORE_TILES
Definition: Morphology.h:80
void pruneInactive(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with background tiles any nodes whose values are a...
Definition: Prune.h:355
@ GRID_LEVEL_SET
Definition: Types.h:337
Definition: Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
Base class for interrupters.
Definition: NullInterrupter.h:26
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202
#define OPENVDB_ALL_TREE_INSTANTIATE(Function)
Definition: version.h.in:151