OpenVDB  9.0.1
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 
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
Definition: Morphology.h:80
Base class for interrupters.
Definition: NullInterrupter.h:25
Definition: FiniteDifference.h:167
Definition: Exceptions.h:65
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:644
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
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
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
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
Performs various types of level set deformations with interface tracking. These unrestricted deformat...
#define OPENVDB_ALL_TREE_INSTANTIATE(Function)
Definition: version.h.in:151
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
Implementation of morphological dilation and erosion.
Definition: Exceptions.h:13
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
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
Container class that associates a tree with a transform and metadata.
Definition: Grid.h:28
GridType
List of types that are currently supported by NanoVDB.
Definition: NanoVDB.h:216
Definition: Types.h:416
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
Definition: Morphology.h:58
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202