| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | // | ||
| 4 | /// @file Morphology.h | ||
| 5 | /// | ||
| 6 | /// @authors Ken Museth, Nick Avramoussis | ||
| 7 | /// | ||
| 8 | /// @brief Implementation of morphological dilation and erosion. | ||
| 9 | /// | ||
| 10 | /// @note By design the morphological operations only change the | ||
| 11 | /// state of voxels, not their values. If one desires to | ||
| 12 | /// change the values of voxels that change state an efficient | ||
| 13 | /// technique is to construct a boolean mask by performing a | ||
| 14 | /// topology difference between the original and final grids. | ||
| 15 | |||
| 16 | #ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED | ||
| 17 | #define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED | ||
| 18 | |||
| 19 | #include "Activate.h" // backwards compatibility | ||
| 20 | #include "Prune.h" | ||
| 21 | #include "ValueTransformer.h" | ||
| 22 | #include "openvdb/Types.h" | ||
| 23 | #include "openvdb/Grid.h" | ||
| 24 | #include "openvdb/tree/ValueAccessor.h" | ||
| 25 | #include "openvdb/tree/LeafManager.h" | ||
| 26 | #include <openvdb/openvdb.h> | ||
| 27 | #include <openvdb/points/PointDataGrid.h> | ||
| 28 | |||
| 29 | #include <tbb/task_arena.h> | ||
| 30 | #include <tbb/enumerable_thread_specific.h> | ||
| 31 | #include <tbb/parallel_for.h> | ||
| 32 | |||
| 33 | #include <type_traits> | ||
| 34 | #include <vector> | ||
| 35 | |||
| 36 | |||
| 37 | namespace openvdb { | ||
| 38 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 39 | namespace OPENVDB_VERSION_NAME { | ||
| 40 | namespace tools { | ||
| 41 | |||
| 42 | /// @brief Voxel topology of nearest neighbors | ||
| 43 | /// @details | ||
| 44 | /// <dl> | ||
| 45 | /// <dt><b>NN_FACE</b> | ||
| 46 | /// <dd>face adjacency (6 nearest neighbors, defined as all neighbor | ||
| 47 | /// voxels connected along one of the primary axes) | ||
| 48 | /// | ||
| 49 | /// <dt><b>NN_FACE_EDGE</b> | ||
| 50 | /// <dd>face and edge adjacency (18 nearest neighbors, defined as all | ||
| 51 | /// neighbor voxels connected along either one or two of the primary axes) | ||
| 52 | /// | ||
| 53 | /// <dt><b>NN_FACE_EDGE_VERTEX</b> | ||
| 54 | /// <dd>face, edge and vertex adjacency (26 nearest neighbors, defined | ||
| 55 | /// as all neighbor voxels connected along either one, two or all | ||
| 56 | /// three of the primary axes) | ||
| 57 | /// </dl> | ||
| 58 | enum NearestNeighbors { NN_FACE = 6, NN_FACE_EDGE = 18, NN_FACE_EDGE_VERTEX = 26 }; | ||
| 59 | |||
| 60 | /// @brief Different policies when dilating trees with active tiles | ||
| 61 | /// @details | ||
| 62 | /// <dl> | ||
| 63 | /// <dt><b>IGNORE_TILES</b> | ||
| 64 | /// <dd>Active tiles are ignores. For dilation, only active voxels are | ||
| 65 | /// dilated. For erosion, active tiles still appear as neighboring | ||
| 66 | /// activity however will themselves not be eroded. | ||
| 67 | /// | ||
| 68 | /// <dt><b>EXPAND_TILES</b> | ||
| 69 | /// <dd>For dilation and erosion, active tiles are voxelized (expanded), | ||
| 70 | /// dilated or eroded and left in their voxelized state irrespective of | ||
| 71 | /// their final state. | ||
| 72 | /// | ||
| 73 | /// <dt><b>PRESERVE_TILES</b> | ||
| 74 | /// <dd>For dilation, active tiles remain unchanged but they still | ||
| 75 | /// contribute to the dilation as if they were active voxels. For | ||
| 76 | /// erosion, active tiles are only eroded should the erosion wavefront | ||
| 77 | /// reach them, otherwise they are left unchanged. Additionally, dense | ||
| 78 | /// or empty nodes with constant values are pruned. | ||
| 79 | /// </dl> | ||
| 80 | enum TilePolicy { IGNORE_TILES, EXPAND_TILES, PRESERVE_TILES }; | ||
| 81 | |||
| 82 | /// @brief Topologically dilate all active values (i.e. both voxels | ||
| 83 | /// and tiles) in a tree using one of three nearest neighbor | ||
| 84 | /// connectivity patterns. | ||
| 85 | /// @details If the input is *not* a MaskTree OR if tiles are being | ||
| 86 | /// preserved, this algorithm will copy the input tree topology onto a | ||
| 87 | /// MaskTree, performs the dilation on the mask and copies the resulting | ||
| 88 | /// topology back. This algorithm guarantees topology preservation | ||
| 89 | /// (non-pruned leaf nodes will persists) EXCEPT for direct MaskTree | ||
| 90 | /// dilation. MaskTree dilation is optimised for performance and may | ||
| 91 | /// replace existing leaf nodes i.e. any held leaf node pointers may | ||
| 92 | /// become invalid. See the Morphology class for more granular control. | ||
| 93 | /// @note This method is fully multi-threaded and support active tiles, | ||
| 94 | /// however only the PRESERVE_TILES policy ensures a pruned topology. | ||
| 95 | /// The values of any voxels are unchanged. | ||
| 96 | /// | ||
| 97 | /// @param tree tree or leaf manager to be dilated. The leaf | ||
| 98 | /// manager will be synchronized with the result. | ||
| 99 | /// @param iterations number of iterations to apply the dilation | ||
| 100 | /// @param nn connectivity pattern of the dilation: either | ||
| 101 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
| 102 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
| 103 | /// nearest neighbors). | ||
| 104 | /// @param mode Defined the policy for handling active tiles | ||
| 105 | /// (see above for details) | ||
| 106 | /// @param threaded Whether to multi-thread execution | ||
| 107 | template<typename TreeOrLeafManagerT> | ||
| 108 | void dilateActiveValues(TreeOrLeafManagerT& tree, | ||
| 109 | const int iterations = 1, | ||
| 110 | const NearestNeighbors nn = NN_FACE, | ||
| 111 | const TilePolicy mode = PRESERVE_TILES, | ||
| 112 | const bool threaded = true); | ||
| 113 | |||
| 114 | /// @brief Topologically erode all active values (i.e. both voxels | ||
| 115 | /// and tiles) in a tree using one of three nearest neighbor | ||
| 116 | /// connectivity patterns. | ||
| 117 | /// @details If tiles are being preserve, this algorithm will copy the input | ||
| 118 | /// tree topology onto a MaskTree, performs the erosion on the mask and | ||
| 119 | /// intersects the resulting topology back. This algorithm guarantees | ||
| 120 | /// topology preservation (non-pruned leaf nodes will persists). See the | ||
| 121 | /// Morphology class for more granular control. | ||
| 122 | /// @note This method is fully multi-threaded and support active tiles, | ||
| 123 | /// however only the PRESERVE_TILES policy ensures a pruned topology. | ||
| 124 | /// The values of any voxels are unchanged. Erosion by NN_FACE neighbors | ||
| 125 | /// is usually faster than other neighbor schemes. NN_FACE_EDGE and | ||
| 126 | /// NN_FACE_EDGE_VERTEX operate at comparable dilation speeds. | ||
| 127 | /// | ||
| 128 | /// @param tree tree or leaf manager to be eroded. The leaf | ||
| 129 | /// manager will be synchronized with the result. | ||
| 130 | /// @param iterations number of iterations to apply the erosion | ||
| 131 | /// @param nn connectivity pattern of the erosion: either | ||
| 132 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
| 133 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
| 134 | /// nearest neighbors). | ||
| 135 | /// @param mode Defined the policy for handling active tiles | ||
| 136 | /// (see above for details) | ||
| 137 | /// @param threaded Whether to multi-thread execution | ||
| 138 | template<typename TreeOrLeafManagerT> | ||
| 139 | void erodeActiveValues(TreeOrLeafManagerT& tree, | ||
| 140 | const int iterations = 1, | ||
| 141 | const NearestNeighbors nn = NN_FACE, | ||
| 142 | const TilePolicy mode = PRESERVE_TILES, | ||
| 143 | const bool threaded = true); | ||
| 144 | |||
| 145 | |||
| 146 | //////////////////////////////////////// | ||
| 147 | |||
| 148 | |||
| 149 | namespace morphology { | ||
| 150 | |||
| 151 | /// @brief Dilation/Erosion operations over a Trees leaf level voxel topology. | ||
| 152 | template<typename TreeType> | ||
| 153 |
1/2✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
|
28210 | class Morphology |
| 154 | { | ||
| 155 | public: | ||
| 156 | using LeafType = typename TreeType::LeafNodeType; | ||
| 157 | using MaskType = typename LeafType::NodeMaskType; | ||
| 158 | using ValueType = typename TreeType::ValueType; | ||
| 159 | using MaskTreeT = typename TreeType::template ValueConverter<ValueMask>::Type; | ||
| 160 | using MaskLeafT = typename MaskTreeT::LeafNodeType; | ||
| 161 | using AccessorType = tree::ValueAccessor<TreeType>; | ||
| 162 | |||
| 163 | 15018 | Morphology(TreeType& tree) | |
| 164 |
1/2✓ Branch 2 taken 7509 times.
✗ Branch 3 not taken.
|
15018 | : mManagerPtr(new tree::LeafManager<TreeType>(tree)) |
| 165 | , mManager(*mManagerPtr) | ||
| 166 | 15018 | , mThreaded(true) {} | |
| 167 | |||
| 168 |
5/80✓ Branch 1 taken 1104 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 28 taken 1116 times.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✓ Branch 81 taken 35 times.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
|
2257 | Morphology(tree::LeafManager<TreeType>& tree) |
| 169 | : mManagerPtr(nullptr) | ||
| 170 | , mManager(tree) | ||
| 171 | 20701 | , mThreaded(true) {} | |
| 172 | |||
| 173 | /// @brief Return whether this class is using multi-threading. | ||
| 174 | 38744 | bool getThreaded() const { return mThreaded; } | |
| 175 | /// @brief Set whether to use multi-threading. | ||
| 176 | /// @note The grain size is not exposed | ||
| 177 |
7/38✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 13820 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
18445 | inline void setThreaded(const bool threaded) { mThreaded = threaded; } |
| 178 | |||
| 179 | /// @brief Return a const reference to the leaf manager | ||
| 180 | inline const tree::LeafManager<TreeType>& leafManager() const { return mManager; } | ||
| 181 | |||
| 182 | /// @brief Topologically erode all voxels by the provided nearest neighbor | ||
| 183 | /// scheme and optionally collapse constant leaf nodes | ||
| 184 | /// @details Inactive Tiles contribute to the erosion but active tiles are | ||
| 185 | /// not modified. | ||
| 186 | /// @param iter Number of erosion iterations | ||
| 187 | /// @param nn Connectivity pattern of the erosion | ||
| 188 | /// @param prune Whether to collapse constant leaf nodes after the erosion | ||
| 189 | void erodeVoxels(const size_t iter, | ||
| 190 | const NearestNeighbors nn, | ||
| 191 | const bool prune = false); | ||
| 192 | |||
| 193 | /// @brief Topologically dilate all voxels by the provided nearest neighbor | ||
| 194 | /// scheme and optionally collapse constant leaf nodes | ||
| 195 | /// @details Voxel values are unchanged and only leaf nodes are used to | ||
| 196 | /// propagate the dilation. | ||
| 197 | /// @param iter Number of dilation iterations | ||
| 198 | /// @param nn Connectivity pattern of the dilation | ||
| 199 | /// @param prune Whether to collapse constant leaf nodes after the dilation | ||
| 200 | /// @param preserveMaskLeafNodes When dilating mask trees, the default behaviour | ||
| 201 | /// chooses to steal the mask nodes rather than copy them. Although faster, | ||
| 202 | /// this means that leaf nodes may be re-allocated. Set this to true if you | ||
| 203 | /// need the original topology pointers to be preserved. | ||
| 204 | void dilateVoxels(const size_t iter, | ||
| 205 | const NearestNeighbors nn, | ||
| 206 | const bool prune = false, | ||
| 207 | const bool preserveMaskLeafNodes = false); | ||
| 208 | |||
| 209 | |||
| 210 | /// @brief Copy the current node masks onto the provided vector. The vector | ||
| 211 | /// is resized if necessary. | ||
| 212 | /// @param masks The vector of NodeMasks to copy onto | ||
| 213 |
2/2✓ Branch 0 taken 20980 times.
✓ Branch 1 taken 1347 times.
|
44654 | void copyMasks(std::vector<MaskType>& masks) const |
| 214 | { | ||
| 215 |
2/2✓ Branch 0 taken 20980 times.
✓ Branch 1 taken 1347 times.
|
44654 | if (masks.size() < mManager.leafCount()) { |
| 216 | 41960 | masks.resize(mManager.leafCount()); | |
| 217 | } | ||
| 218 | |||
| 219 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22327 times.
|
44654 | if (this->getThreaded()) { |
| 220 | // @note this is marginally faster than using leafRange or foreach | ||
| 221 | 44654 | tbb::parallel_for(mManager.getRange(), | |
| 222 | 363768 | [&](const tbb::blocked_range<size_t>& r){ | |
| 223 |
6/20✓ Branch 0 taken 1518 times.
✓ Branch 1 taken 1485 times.
✓ Branch 2 taken 213694 times.
✓ Branch 3 taken 133977 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 11692 times.
✓ Branch 15 taken 1402 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
363768 | for (size_t idx = r.begin(); idx < r.end(); ++idx) |
| 224 |
3/20✗ Branch 0 not taken.
✓ Branch 1 taken 1518 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 213694 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 11692 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
226904 | masks[idx] = mManager.leaf(idx).getValueMask(); |
| 225 | }); | ||
| 226 | } | ||
| 227 | else { | ||
| 228 | ✗ | for (size_t idx = 0; idx < mManager.leafCount(); ++idx) { | |
| 229 | masks[idx] = mManager.leaf(idx).getValueMask(); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | 44654 | } | |
| 233 | |||
| 234 | public: | ||
| 235 | /// @brief Node Mask dilation/erosion operations for individual leaf nodes on | ||
| 236 | /// a given tree. The leaf node may optionally belong to a different tree | ||
| 237 | /// than the provided accessor, which will have the effect of dilating the | ||
| 238 | /// leaf node mask into a different tree, or eroding the node mask based | ||
| 239 | /// on corresponding neighbors in a different tree. | ||
| 240 |
12/116✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 2422 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4572 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 167 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 13646 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 1670 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 3141 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✓ Branch 50 taken 7 times.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 58 taken 3 times.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✓ Branch 66 taken 1 times.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✓ Branch 98 taken 2 times.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 105 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
|
25677 | struct NodeMaskOp |
| 241 | { | ||
| 242 | static const Int32 DIM = static_cast<Int32>(LeafType::DIM); | ||
| 243 | static const Int32 LOG2DIM = static_cast<Int32>(LeafType::LOG2DIM); | ||
| 244 | |||
| 245 | // Select the storage size based off the dimensions of the leaf node | ||
| 246 | using Word = typename std::conditional<LOG2DIM == 3, uint8_t, | ||
| 247 | typename std::conditional<LOG2DIM == 4, uint16_t, | ||
| 248 | typename std::conditional<LOG2DIM == 5, uint32_t, | ||
| 249 | typename std::conditional<LOG2DIM == 6, uint64_t, | ||
| 250 | void>::type>::type>::type>::type; | ||
| 251 | |||
| 252 | static_assert(!std::is_same<Word, void>::value, | ||
| 253 | "Unsupported Node Dimension for node mask dilation/erosion"); | ||
| 254 | |||
| 255 | 51354 | NodeMaskOp(AccessorType& accessor, | |
| 256 | const NearestNeighbors op) | ||
| 257 | : mOrigin(nullptr) | ||
| 258 | , mNeighbors(NodeMaskOp::ksize(op), nullptr) | ||
| 259 | , mAccessor(&accessor) | ||
| 260 | , mOnTile(true) | ||
| 261 | , mOffTile(false) | ||
| 262 | 51354 | , mOp(op) {} | |
| 263 | |||
| 264 | /// @brief Dilate a single leaf node by the current spatial scheme | ||
| 265 | /// stored on the instance of this NodeMaskOp. Neighbor leaf | ||
| 266 | /// nodes are also updated. | ||
| 267 | /// @details Unlike erode, dilate is expected to be called in a | ||
| 268 | /// single threaded context as it will update the node masks | ||
| 269 | /// of neighboring leaf nodes as well as the provided leaf. | ||
| 270 | /// @param leaf The leaf to dilate. The leaf's origin and value mask | ||
| 271 | /// are used to calculate the result of the dilation. | ||
| 272 | inline void dilate(LeafType& leaf) | ||
| 273 | { | ||
| 274 | // copy the mask | ||
| 275 | const MaskType mask = leaf.getValueMask(); | ||
| 276 | this->dilate(leaf, mask); | ||
| 277 | } | ||
| 278 | |||
| 279 | /// @brief Dilate a single leaf node by the current spatial scheme | ||
| 280 | /// stored on the instance of this NodeMaskOp. The provided | ||
| 281 | /// mask is used in place of the actual leaf's node mask and | ||
| 282 | /// applied to the leaf afterwards. Neighbor leaf nodes are | ||
| 283 | /// also updated. | ||
| 284 | /// @details Unlike erode, dilate is expected to be called in a | ||
| 285 | /// single threaded context as it will update the node masks | ||
| 286 | /// of neighboring leaf nodes as well as the provided leaf. | ||
| 287 | /// @param leaf The leaf to dilate. The leaf's origin is used to | ||
| 288 | /// calculate the result of the dilation. | ||
| 289 | /// @param mask The node mask to use in place of the current leaf | ||
| 290 | /// node mask. | ||
| 291 | 403940 | inline void dilate(LeafType& leaf, const MaskType& mask) | |
| 292 | { | ||
| 293 | 403940 | this->clear(); | |
| 294 |
3/4✓ Branch 0 taken 117411 times.
✓ Branch 1 taken 54257 times.
✓ Branch 2 taken 30302 times.
✗ Branch 3 not taken.
|
403940 | mNeighbors[0] = &(leaf.getValueMask()); |
| 295 | this->setOrigin(leaf.origin()); | ||
| 296 | 403940 | switch (mOp) { | |
| 297 | 234822 | case NN_FACE_EDGE : { this->dilate18(mask); return; } | |
| 298 | 108514 | case NN_FACE_EDGE_VERTEX : { this->dilate26(mask); return; } | |
| 299 | 60604 | case NN_FACE : { this->dilate6(mask); return; } | |
| 300 | ✗ | default : { | |
| 301 | ✗ | assert(false && "Unknown op during dilation."); return; | |
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | /// @brief Erode a single leaf node by the current spatial scheme | ||
| 307 | /// stored on the instance of this NodeMaskOp. | ||
| 308 | /// @details Unlike dilate, this method updates the provided mask | ||
| 309 | /// and does not apply the result to the leaf node. The | ||
| 310 | /// leaf node is simply used to infer the position in the | ||
| 311 | /// tree to find it's neighbors. This allows erode to be | ||
| 312 | /// called from multiple threads | ||
| 313 | /// @param leaf The leaf to erode. The leaf's origin is used to | ||
| 314 | /// calculate the result of the erosion. | ||
| 315 | /// @return The eroded mask | ||
| 316 | inline MaskType erode(const LeafType& leaf) | ||
| 317 | { | ||
| 318 | // copy the mask | ||
| 319 | MaskType mask = leaf.getValueMask(); | ||
| 320 | this->erode(leaf, mask); | ||
| 321 | return mask; | ||
| 322 | } | ||
| 323 | |||
| 324 | /// @brief Erode a single leaf node by the current spatial scheme | ||
| 325 | /// stored on the instance of this NodeMaskOp. The provided | ||
| 326 | /// mask is used in place of the actual leaf's node mask and | ||
| 327 | /// stores the erosion result. | ||
| 328 | /// @details Unlike dilate, this method updates the provided mask | ||
| 329 | /// and does not apply the result to the leaf node. The | ||
| 330 | /// leaf node is simply used to infer the position in the | ||
| 331 | /// tree to find it's neighbors. | ||
| 332 | /// @param leaf The leaf to erode. The leaf's origin is used to | ||
| 333 | /// calculate the result of the erosion. | ||
| 334 | /// @param mask The node mask to use in place of the current leaf | ||
| 335 | /// node mask. | ||
| 336 | 54278 | inline void erode(const LeafType& leaf, MaskType& mask) | |
| 337 | { | ||
| 338 | 54278 | this->clear(); | |
| 339 | // @note leaf mask will not be modified through gather methods | ||
| 340 |
1/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 28086 times.
✗ Branch 3 not taken.
|
54278 | mNeighbors[0] = const_cast<MaskType*>(&leaf.getValueMask()); |
| 341 | this->setOrigin(leaf.origin()); | ||
| 342 | 54278 | switch (mOp) { | |
| 343 | ✗ | case NN_FACE_EDGE : { this->erode18(mask); return; } | |
| 344 | ✗ | case NN_FACE_EDGE_VERTEX : { this->erode26(mask); return; } | |
| 345 | 54278 | case NN_FACE : { this->erode6(mask); return; } | |
| 346 | ✗ | default : { | |
| 347 | ✗ | assert(false && "Unknown op during erosion."); return; | |
| 348 | } | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | private: | ||
| 353 | static size_t ksize(const NearestNeighbors op) { | ||
| 354 |
7/40✗ Branch 0 not taken.
✓ Branch 1 taken 2424 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7059 times.
✓ Branch 5 taken 4036 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7334 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 1670 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✓ Branch 36 taken 11 times.
✓ Branch 37 taken 3143 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
25677 | switch (op) { |
| 355 | case NN_FACE_EDGE : return 19; | ||
| 356 | 7070 | case NN_FACE_EDGE_VERTEX : return 27; | |
| 357 | 11273 | case NN_FACE : return 7; | |
| 358 | ✗ | default : return 7; | |
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | void dilate6(const MaskType& mask); | ||
| 363 | void dilate18(const MaskType& mask); | ||
| 364 | void dilate26(const MaskType& mask); | ||
| 365 | void erode6(MaskType& mask); | ||
| 366 | |||
| 367 | /// @note Forward API for erosion of 18/26 trees is to use erodeActiveValues | ||
| 368 | /// which falls back to an inverse dilation | ||
| 369 | /// @todo It may still be worth investigating more optimal gathering | ||
| 370 | /// techniques | ||
| 371 | ✗ | inline void erode18(MaskType&) { OPENVDB_THROW(NotImplementedError, "erode18 is not implemented yet!"); } | |
| 372 | ✗ | inline void erode26(MaskType&) { OPENVDB_THROW(NotImplementedError, "erode26 is not implemented yet!"); } | |
| 373 | |||
| 374 |
6/80✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1894 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 117411 times.
✓ Branch 9 taken 54257 times.
✓ Branch 10 taken 30302 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 14088 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 12104 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
|
230056 | inline void setOrigin(const Coord& origin) { mOrigin = &origin; } |
| 375 | 171668 | inline const Coord& getOrigin() const { return *mOrigin; } | |
| 376 | 460112 | inline void clear() { std::fill(mNeighbors.begin(), mNeighbors.end(), nullptr); } | |
| 377 | |||
| 378 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | inline void scatter(size_t n, int indx) |
| 379 | { | ||
| 380 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | assert(n < mNeighbors.size()); |
| 381 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | assert(mNeighbors[n]); |
| 382 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | mNeighbors[n]->template getWord<Word>(indx) |= mWord; |
| 383 | |||
| 384 | } | ||
| 385 | template<int DX, int DY, int DZ> | ||
| 386 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23686879 times.
|
47373758 | inline void scatter(size_t n, int indx) |
| 387 | { | ||
| 388 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23686879 times.
|
47373758 | assert(n < mNeighbors.size()); |
| 389 |
2/2✓ Branch 0 taken 2078239 times.
✓ Branch 1 taken 21608640 times.
|
47373758 | if (!mNeighbors[n]) { |
| 390 | 4156478 | mNeighbors[n] = this->getNeighbor<DX,DY,DZ,true>(); | |
| 391 | } | ||
| 392 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23686879 times.
|
47373758 | assert(mNeighbors[n]); |
| 393 | 47373758 | this->scatter(n, indx - (DIM - 1)*(DY + DX*DIM)); | |
| 394 | } | ||
| 395 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8592216 times.
|
17030478 | inline Word gather(size_t n, int indx) |
| 396 | { | ||
| 397 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8592216 times.
|
17030478 | assert(n < mNeighbors.size()); |
| 398 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8592216 times.
|
17030478 | return mNeighbors[n]->template getWord<Word>(indx); |
| 399 | } | ||
| 400 | template<int DX, int DY, int DZ> | ||
| 401 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3573406 times.
|
7146812 | inline Word gather(size_t n, int indx) |
| 402 | { | ||
| 403 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3573406 times.
|
7146812 | assert(n < mNeighbors.size()); |
| 404 |
2/2✓ Branch 0 taken 153042 times.
✓ Branch 1 taken 3420364 times.
|
7146812 | if (!mNeighbors[n]) { |
| 405 | 306084 | mNeighbors[n] = this->getNeighbor<DX,DY,DZ,false>(); | |
| 406 | } | ||
| 407 | 7146812 | return this->gather(n, indx - (DIM -1)*(DY + DX*DIM)); | |
| 408 | } | ||
| 409 | |||
| 410 | void scatterFacesXY(int x, int y, int i1, int n, int i2); | ||
| 411 | void scatterEdgesXY(int x, int y, int i1, int n, int i2); | ||
| 412 | Word gatherFacesXY(int x, int y, int i1, int n, int i2); | ||
| 413 | /// @note Currently unused | ||
| 414 | Word gatherEdgesXY(int x, int y, int i1, int n, int i2); | ||
| 415 | |||
| 416 | template<int DX, int DY, int DZ, bool Create> | ||
| 417 | 4462562 | inline MaskType* getNeighbor() | |
| 418 | { | ||
| 419 | 4462562 | const Coord xyz = mOrigin->offsetBy(DX*DIM, DY*DIM, DZ*DIM); | |
| 420 | 4462562 | auto* leaf = mAccessor->probeLeaf(xyz); | |
| 421 |
2/2✓ Branch 0 taken 1800408 times.
✓ Branch 1 taken 430873 times.
|
4462562 | if (leaf) return &(leaf->getValueMask()); |
| 422 |
2/2✓ Branch 1 taken 299472 times.
✓ Branch 2 taken 131401 times.
|
861746 | if (mAccessor->isValueOn(xyz)) return &mOnTile; |
| 423 | 23000 | if (!Create) return &mOffTile; | |
| 424 | 239802 | leaf = mAccessor->touchLeaf(xyz); | |
| 425 | 239802 | return &(leaf->getValueMask()); | |
| 426 | } | ||
| 427 | |||
| 428 | private: | ||
| 429 | const Coord* mOrigin; | ||
| 430 | std::vector<MaskType*> mNeighbors; | ||
| 431 | AccessorType* const mAccessor; | ||
| 432 | Word mWord; | ||
| 433 | MaskType mOnTile, mOffTile; | ||
| 434 | const NearestNeighbors mOp; | ||
| 435 | };// NodeMaskOp | ||
| 436 | |||
| 437 | private: | ||
| 438 | std::unique_ptr<tree::LeafManager<TreeType>> mManagerPtr; | ||
| 439 | tree::LeafManager<TreeType>& mManager; | ||
| 440 | bool mThreaded; | ||
| 441 | };// Morphology | ||
| 442 | |||
| 443 | |||
| 444 | template <typename TreeT> | ||
| 445 | typename std::enable_if<std::is_same<TreeT, typename TreeT::template ValueConverter<ValueMask>::Type>::value, | ||
| 446 | typename TreeT::template ValueConverter<ValueMask>::Type*>::type | ||
| 447 | 6166 | getMaskTree(TreeT& tree) { return &tree; } | |
| 448 | |||
| 449 | template <typename TreeT> | ||
| 450 | typename std::enable_if<!std::is_same<TreeT, typename TreeT::template ValueConverter<ValueMask>::Type>::value, | ||
| 451 | typename TreeT::template ValueConverter<ValueMask>::Type*>::type | ||
| 452 | 2464 | getMaskTree(TreeT&) { return nullptr; } | |
| 453 | |||
| 454 | |||
| 455 | template <typename TreeType> | ||
| 456 | 5067 | void Morphology<TreeType>::erodeVoxels(const size_t iter, | |
| 457 | const NearestNeighbors nn, | ||
| 458 | const bool prune) | ||
| 459 | { | ||
| 460 |
1/2✓ Branch 0 taken 3364 times.
✗ Branch 1 not taken.
|
5067 | if (iter == 0) return; |
| 461 |
2/2✓ Branch 0 taken 3355 times.
✓ Branch 1 taken 9 times.
|
5067 | const size_t leafCount = mManager.leafCount(); |
| 462 |
2/2✓ Branch 0 taken 3355 times.
✓ Branch 1 taken 9 times.
|
5067 | if (leafCount == 0) return; |
| 463 | auto& tree = mManager.tree(); | ||
| 464 | |||
| 465 | // If the nearest neighbor mode is not FACE, fall back to an | ||
| 466 | // inverse dilation scheme which executes over a mask topology | ||
| 467 |
2/2✓ Branch 0 taken 2220 times.
✓ Branch 1 taken 1135 times.
|
5054 | if (nn != NN_FACE) { |
| 468 | // This method 1) dilates the input topology, 2) reverse the node masks, | ||
| 469 | // 3) performs a final dilation and 4) subtracts the result from the original | ||
| 470 | // topology. A cache of the original leaf pointers is required which tracks | ||
| 471 | // the original leaf nodes in a mask topology. These will need their | ||
| 472 | // masks updated in the original tree. The first dilation may create new leaf | ||
| 473 | // nodes in two instances. The first is where no topology existed before. The | ||
| 474 | // second is where an active tile overlaps with dilated topology. These | ||
| 475 | // tiles will be expanded to a dense leaf nodes by topologyUnion. We need | ||
| 476 | // to make sure these tiles are properly turned off. | ||
| 477 | |||
| 478 | 6672 | MaskTreeT mask(tree, false, TopologyCopy()); | |
| 479 | |||
| 480 | // Create a new morphology class to perform dilation over the mask | ||
| 481 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
6672 | tree::LeafManager<MaskTreeT> manager(mask); |
| 482 | Morphology<MaskTreeT> m(manager); | ||
| 483 | m.setThreaded(this->getThreaded()); | ||
| 484 | |||
| 485 | // perform a single dilation using the current scheme. Necessary to | ||
| 486 | // create edge leaf nodes and compute the active wavefront. Note that | ||
| 487 | // the cached array pointers will continue to be valid | ||
| 488 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | m.dilateVoxels(1, nn, /*prune=*/false); |
| 489 | |||
| 490 | // compute the wavefront. If the leaf previously existed, compute the | ||
| 491 | // xor activity result which is guaranteed to be equal to but slightly | ||
| 492 | // faster than a subtraction | ||
| 493 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5394 times.
|
13544 | auto computeWavefront = [&](const size_t idx) { |
| 494 | auto& leaf = manager.leaf(idx); | ||
| 495 | auto& nodemask = leaf.getValueMask(); | ||
| 496 |
4/20✓ Branch 0 taken 3756 times.
✓ Branch 1 taken 1638 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 4072 times.
✓ Branch 19 taken 1846 times.
|
11312 | if (const auto* original = tree.probeConstLeaf(leaf.origin())) { |
| 497 | nodemask ^= original->getValueMask(); | ||
| 498 | } | ||
| 499 | else { | ||
| 500 | // should never have a dense leaf if it didn't exist in the | ||
| 501 | // original tree (it was previous possible when dilateVoxels() | ||
| 502 | // called topologyUnion without the preservation of active | ||
| 503 | // tiles) | ||
| 504 |
2/20✗ Branch 0 not taken.
✓ Branch 1 taken 1638 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 1846 times.
|
3484 | assert(!nodemask.isOn()); |
| 505 | } | ||
| 506 | }; | ||
| 507 | |||
| 508 |
1/2✓ Branch 0 taken 2220 times.
✗ Branch 1 not taken.
|
3336 | if (this->getThreaded()) { |
| 509 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | tbb::parallel_for(manager.getRange(), |
| 510 | 11312 | [&](const tbb::blocked_range<size_t>& r){ | |
| 511 |
4/40✓ Branch 0 taken 8271 times.
✓ Branch 1 taken 8244 times.
✓ Branch 2 taken 3041 times.
✓ Branch 3 taken 2774 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
22330 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
| 512 | 11312 | computeWavefront(idx); | |
| 513 | } | ||
| 514 | }); | ||
| 515 | } | ||
| 516 | else { | ||
| 517 | ✗ | for (size_t idx = 0; idx < manager.leafCount(); ++idx) { | |
| 518 | ✗ | computeWavefront(idx); | |
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | // perform the inverse dilation | ||
| 523 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | m.dilateVoxels(iter, nn, /*prune=*/false); |
| 524 | |||
| 525 | // subtract the inverse dilation from the original node masks | ||
| 526 | 10060 | auto subtractTopology = [&](const size_t idx) { | |
| 527 |
2/20✗ Branch 0 not taken.
✓ Branch 1 taken 3756 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 4072 times.
|
7828 | auto& leaf = mManager.leaf(idx); |
| 528 | const auto* maskleaf = mask.probeConstLeaf(leaf.origin()); | ||
| 529 |
2/20✗ Branch 0 not taken.
✓ Branch 1 taken 3756 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 4072 times.
|
7828 | assert(maskleaf); |
| 530 | leaf.getValueMask() -= maskleaf->getValueMask(); | ||
| 531 | }; | ||
| 532 | |||
| 533 |
1/2✓ Branch 0 taken 2220 times.
✗ Branch 1 not taken.
|
3336 | if (this->getThreaded()) { |
| 534 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | tbb::parallel_for(mManager.getRange(), |
| 535 | 7828 | [&](const tbb::blocked_range<size_t>& r){ | |
| 536 |
4/40✓ Branch 0 taken 5281 times.
✓ Branch 1 taken 5256 times.
✓ Branch 2 taken 2547 times.
✓ Branch 3 taken 2444 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
15528 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
| 537 | 7828 | subtractTopology(idx); | |
| 538 | } | ||
| 539 | }); | ||
| 540 | } | ||
| 541 | else { | ||
| 542 | ✗ | for (size_t idx = 0; idx < leafCount; ++idx) { | |
| 543 | ✗ | subtractTopology(idx); | |
| 544 | } | ||
| 545 | } | ||
| 546 | } | ||
| 547 | else { | ||
| 548 | // NN_FACE erosion scheme | ||
| 549 | |||
| 550 | // Save the value masks of all leaf nodes. | ||
| 551 | std::vector<MaskType> nodeMasks; | ||
| 552 |
1/2✓ Branch 1 taken 1135 times.
✗ Branch 2 not taken.
|
1718 | this->copyMasks(nodeMasks); |
| 553 | |||
| 554 |
1/2✓ Branch 0 taken 1135 times.
✗ Branch 1 not taken.
|
1718 | if (this->getThreaded()) { |
| 555 | 1718 | const auto range = mManager.getRange(); | |
| 556 |
2/2✓ Branch 0 taken 1256 times.
✓ Branch 1 taken 1135 times.
|
3620 | for (size_t i = 0; i < iter; ++i) { |
| 557 | // For each leaf, in parallel, gather neighboring off values | ||
| 558 | // and update the cached value mask | ||
| 559 |
1/2✓ Branch 1 taken 1256 times.
✗ Branch 2 not taken.
|
1902 | tbb::parallel_for(range, |
| 560 |
1/2✓ Branch 2 taken 1894 times.
✗ Branch 3 not taken.
|
42552 | [&](const tbb::blocked_range<size_t>& r) { |
| 561 | AccessorType accessor(tree); | ||
| 562 |
3/20✓ Branch 1 taken 2422 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 1670 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 28 taken 3141 times.
✗ Branch 29 not taken.
|
7233 | NodeMaskOp cache(accessor, nn); |
| 563 |
6/20✓ Branch 0 taken 2455 times.
✓ Branch 1 taken 2422 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 14156 times.
✓ Branch 15 taken 1670 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 12665 times.
✓ Branch 19 taken 3141 times.
|
36509 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
| 564 |
3/20✗ Branch 0 not taken.
✓ Branch 1 taken 2455 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 14156 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 12665 times.
|
29276 | const auto& leaf = mManager.leaf(idx); |
| 565 |
6/20✓ Branch 0 taken 561 times.
✓ Branch 1 taken 1894 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 68 times.
✓ Branch 15 taken 14088 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 561 times.
✓ Branch 19 taken 12104 times.
|
29276 | if (leaf.isEmpty()) continue; |
| 566 | // original bit-mask of current leaf node | ||
| 567 | MaskType& newMask = nodeMasks[idx]; | ||
| 568 |
3/20✓ Branch 1 taken 1894 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 14088 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 28 taken 12104 times.
✗ Branch 29 not taken.
|
28086 | cache.erode(leaf, newMask); |
| 569 | } | ||
| 570 | }); | ||
| 571 | |||
| 572 | // update the masks after all nodes have been eroded | ||
| 573 |
1/2✓ Branch 1 taken 1256 times.
✗ Branch 2 not taken.
|
1902 | tbb::parallel_for(range, |
| 574 | 36684 | [&](const tbb::blocked_range<size_t>& r){ | |
| 575 |
6/20✓ Branch 0 taken 2455 times.
✓ Branch 1 taken 2422 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 14156 times.
✓ Branch 15 taken 1826 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 12665 times.
✓ Branch 19 taken 3160 times.
|
36684 | for (size_t idx = r.begin(); idx < r.end(); ++idx) |
| 576 |
3/20✗ Branch 0 not taken.
✓ Branch 1 taken 2455 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 14156 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 12665 times.
|
29276 | mManager.leaf(idx).setValueMask(nodeMasks[idx]); |
| 577 | }); | ||
| 578 | } | ||
| 579 | } | ||
| 580 | else { | ||
| 581 | AccessorType accessor(tree); | ||
| 582 | ✗ | NodeMaskOp cache(accessor, nn); | |
| 583 | ✗ | for (size_t i = 0; i < iter; ++i) { | |
| 584 | // For each leaf, in parallel, gather neighboring off values | ||
| 585 | // and update the cached value mask | ||
| 586 | ✗ | for (size_t idx = 0; idx < leafCount; ++idx) { | |
| 587 | ✗ | const auto& leaf = mManager.leaf(idx); | |
| 588 | ✗ | if (leaf.isEmpty()) continue; | |
| 589 | // original bit-mask of current leaf node | ||
| 590 | MaskType& newMask = nodeMasks[idx]; | ||
| 591 | ✗ | cache.erode(leaf, newMask); | |
| 592 | } | ||
| 593 | |||
| 594 | ✗ | for (size_t idx = 0; idx < leafCount; ++idx) { | |
| 595 | ✗ | mManager.leaf(idx).setValueMask(nodeMasks[idx]); | |
| 596 | } | ||
| 597 | } | ||
| 598 | } | ||
| 599 | } | ||
| 600 | |||
| 601 | // if prune, replace any inactive nodes | ||
| 602 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3355 times.
|
5054 | if (prune) { |
| 603 | ✗ | tools::prune(mManager.tree(), | |
| 604 | zeroVal<typename TreeType::ValueType>(), | ||
| 605 | this->getThreaded()); | ||
| 606 | ✗ | mManager.rebuild(!this->getThreaded()); | |
| 607 | } | ||
| 608 | } | ||
| 609 | |||
| 610 | template <typename TreeType> | ||
| 611 | 17244 | void Morphology<TreeType>::dilateVoxels(const size_t iter, | |
| 612 | const NearestNeighbors nn, | ||
| 613 | const bool prune, | ||
| 614 | const bool preserveMaskLeafNodes) | ||
| 615 | { | ||
| 616 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8622 times.
|
17244 | if (iter == 0) return; |
| 617 | |||
| 618 | const bool threaded = this->getThreaded(); | ||
| 619 | |||
| 620 | // Actual dilation op. main implementation is single threaded. Note that this | ||
| 621 | // is templated (auto-ed) as the threaded implemenation may call this with a | ||
| 622 | // different value type to the source morphology class | ||
| 623 | // @note GCC 6.4.0 crashes trying to compile this lambda with [&] capture | ||
| 624 | 35688 | auto dilate = [iter, nn, threaded](auto& manager, const bool collapse) { | |
| 625 | |||
| 626 | using LeafManagerT = typename std::decay<decltype(manager)>::type; | ||
| 627 | using TreeT = typename LeafManagerT::TreeType; | ||
| 628 | using ValueT = typename TreeT::ValueType; | ||
| 629 | using LeafT = typename TreeT::LeafNodeType; | ||
| 630 | |||
| 631 | // this is only used for the impl of copyMasks | ||
| 632 | Morphology<TreeT> m(manager); | ||
| 633 | m.setThreaded(threaded); | ||
| 634 | |||
| 635 | TreeT& tree = manager.tree(); | ||
| 636 | tree::ValueAccessor<TreeT> accessor(tree); | ||
| 637 | |||
| 638 | // build cache objects | ||
| 639 |
6/38✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 13820 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
18444 | typename Morphology<TreeT>::NodeMaskOp cache(accessor, nn); |
| 640 | std::vector<MaskType> nodeMasks; | ||
| 641 | 211 | std::vector<std::unique_ptr<LeafT>> nodes; | |
| 642 | const ValueT& bg = tree.background(); | ||
| 643 | 18444 | const bool steal = iter > 1; | |
| 644 | |||
| 645 |
11/38✓ Branch 0 taken 5097 times.
✓ Branch 1 taken 4616 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16089 times.
✓ Branch 5 taken 13820 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 2 times.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
39636 | for (size_t i = 0; i < iter; ++i) { |
| 646 |
10/76✓ Branch 0 taken 481 times.
✓ Branch 1 taken 4616 times.
✓ Branch 3 taken 481 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2269 times.
✓ Branch 11 taken 13820 times.
✓ Branch 13 taken 2269 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 3 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✓ Branch 71 taken 2 times.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
|
21194 | if (i > 0) manager.rebuild(!threaded); |
| 647 | // If the leaf count is zero, we can stop dilation | ||
| 648 | const size_t leafCount = manager.leafCount(); | ||
| 649 |
6/38✓ Branch 0 taken 5097 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 16089 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✓ Branch 42 taken 2 times.
✗ Branch 43 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
|
39425 | if (leafCount == 0) return; |
| 650 | |||
| 651 | // Copy the masks. This only resizes if necessary. As we're stealing/replacing | ||
| 652 | // dense nodes, it's possible we don't need to re-allocate the cache. | ||
| 653 |
5/38✓ Branch 1 taken 5097 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 7 taken 16089 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
21192 | m.copyMasks(nodeMasks); |
| 654 | |||
| 655 | // For each node, dilate the mask into itself and neighboring leaf nodes. | ||
| 656 | // If the node was originally dense (all active), steal/replace it so | ||
| 657 | // subsequent iterations are faster | ||
| 658 |
5/38✓ Branch 1 taken 5097 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 9 taken 16089 times.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 57 taken 2 times.
✗ Branch 58 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
|
223162 | manager.foreach([&](LeafT& leaf, const size_t idx) { |
| 659 | // original bit-mask of current leaf node | ||
| 660 | const MaskType& oldMask = nodeMasks[idx]; | ||
| 661 | const bool dense = oldMask.isOn(); | ||
| 662 | 201970 | cache.dilate(leaf, oldMask); | |
| 663 |
7/38✓ Branch 0 taken 1109 times.
✓ Branch 1 taken 32811 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8167 times.
✓ Branch 5 taken 159877 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
201970 | if (!dense) return; |
| 664 | // This node does not need to be visited again - replace or steal | ||
| 665 |
3/38✗ Branch 0 not taken.
✓ Branch 1 taken 1109 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5736 times.
✓ Branch 5 taken 2431 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
9276 | if (collapse) { |
| 666 | // if collapse, replace this dense leaf with an active background tile | ||
| 667 | 5736 | accessor.addTile(1, leaf.origin(), bg, true); | |
| 668 | } | ||
| 669 |
4/38✓ Branch 0 taken 1021 times.
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2153 times.
✓ Branch 5 taken 278 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
3540 | else if (steal) { |
| 670 | // otherwise, temporarily steal this node | ||
| 671 | 3174 | nodes.emplace_back( | |
| 672 | tree.template stealNode<LeafT>(leaf.origin(), | ||
| 673 | 6348 | zeroVal<ValueT>(), true)); | |
| 674 | } | ||
| 675 | }, false); | ||
| 676 | } | ||
| 677 | |||
| 678 |
7/38✓ Branch 0 taken 44 times.
✓ Branch 1 taken 4572 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 167 times.
✓ Branch 5 taken 13653 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
18442 | if (nodes.empty()) return; |
| 679 | // Add back all dense nodes | ||
| 680 |
4/38✓ Branch 0 taken 1021 times.
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2153 times.
✓ Branch 5 taken 167 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
3385 | for (auto& node : nodes) { |
| 681 |
2/38✓ Branch 1 taken 1021 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 7 taken 2153 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
3174 | accessor.addLeaf(node.release()); |
| 682 | } | ||
| 683 | }; | ||
| 684 | |||
| 685 | // | ||
| 686 | |||
| 687 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8620 times.
|
17244 | if (!threaded) { |
| 688 | // single threaded dilation. If it's a mask tree we can collapse | ||
| 689 | // nodes during the dilation, otherwise we must call prune afterwards | ||
| 690 | constexpr bool isMask = std::is_same<TreeType, MaskTreeT>::value; | ||
| 691 | 4 | dilate(mManager, isMask && prune); | |
| 692 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
4 | if (!isMask && prune) { |
| 693 | ✗ | tools::prune(mManager.tree(), | |
| 694 | zeroVal<typename TreeType::ValueType>(), | ||
| 695 | threaded); | ||
| 696 | } | ||
| 697 | } | ||
| 698 | else { | ||
| 699 | // multi-threaded dilation | ||
| 700 | |||
| 701 | // Steal or create mask nodes that represent the current leaf nodes. | ||
| 702 | // If the input is a mask tree, optionally re-allocate the nodes if | ||
| 703 | // preserveMaskLeafNodes is true. This ensures that leaf node | ||
| 704 | // pointers are not changed in the source tree. Stealing the mask | ||
| 705 | // nodes is significantly faster as it also avoids a post union. | ||
| 706 | std::vector<MaskLeafT*> array; | ||
| 707 |
2/2✓ Branch 0 taken 2454 times.
✓ Branch 1 taken 6166 times.
|
17240 | MaskTreeT* mask = getMaskTree(mManager.tree()); |
| 708 | |||
| 709 |
2/2✓ Branch 0 taken 2454 times.
✓ Branch 1 taken 6166 times.
|
17240 | if (!mask) { |
| 710 | 9816 | MaskTreeT topology; | |
| 711 |
1/2✓ Branch 1 taken 2454 times.
✗ Branch 2 not taken.
|
4908 | topology.topologyUnion(mManager.tree()); |
| 712 |
1/2✓ Branch 1 taken 2454 times.
✗ Branch 2 not taken.
|
4908 | array.reserve(mManager.leafCount()); |
| 713 | topology.stealNodes(array); | ||
| 714 | } | ||
| 715 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6165 times.
|
12332 | else if (preserveMaskLeafNodes) { |
| 716 | mask = nullptr; // act as if theres no mask tree | ||
| 717 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | array.resize(mManager.leafCount()); |
| 718 |
1/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
2 | tbb::parallel_for(mManager.getRange(), |
| 719 | 40 | [&](const tbb::blocked_range<size_t>& r){ | |
| 720 |
2/20✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
40 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
| 721 |
1/20✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
|
40 | array[idx] = new MaskLeafT(mManager.leaf(idx)); |
| 722 | } | ||
| 723 | }); | ||
| 724 | } | ||
| 725 | else { | ||
| 726 |
1/2✓ Branch 1 taken 6165 times.
✗ Branch 2 not taken.
|
12330 | array.reserve(mManager.leafCount()); |
| 727 | mask->stealNodes(array); | ||
| 728 | } | ||
| 729 | |||
| 730 | // @note this grain size is used for optimal threading | ||
| 731 |
2/2✓ Branch 0 taken 550 times.
✓ Branch 1 taken 8070 times.
|
17240 | const size_t numThreads = size_t(tbb::this_task_arena::max_concurrency()); |
| 732 |
2/2✓ Branch 0 taken 550 times.
✓ Branch 1 taken 8070 times.
|
17240 | const size_t subTreeSize = math::Max(size_t(1), array.size()/(2*numThreads)); |
| 733 | |||
| 734 | // perform recursive dilation to sub trees | ||
| 735 |
1/2✓ Branch 1 taken 8620 times.
✗ Branch 2 not taken.
|
34480 | tbb::enumerable_thread_specific<std::unique_ptr<MaskTreeT>> pool; |
| 736 | MaskLeafT** start = array.data(); | ||
| 737 |
1/2✓ Branch 1 taken 8620 times.
✗ Branch 2 not taken.
|
17240 | tbb::parallel_for(tbb::blocked_range<MaskLeafT**>(start, start + array.size(), subTreeSize), |
| 738 | 36884 | [&](const tbb::blocked_range<MaskLeafT**>& range) { | |
| 739 | 18442 | std::unique_ptr<MaskTreeT> mask(new MaskTreeT); | |
| 740 |
15/40✓ Branch 0 taken 10888 times.
✓ Branch 1 taken 4616 times.
✓ Branch 3 taken 10888 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 27368 times.
✓ Branch 6 taken 13820 times.
✓ Branch 8 taken 27368 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 3 times.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 1 times.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✓ Branch 35 taken 2 times.
✓ Branch 36 taken 2 times.
✓ Branch 38 taken 2 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
|
56704 | for (MaskLeafT** it = range.begin(); it != range.end(); ++it) mask->addLeaf(*it); |
| 741 |
5/20✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 13820 times.
✗ Branch 7 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
|
36884 | tree::LeafManager<MaskTreeT> manager(*mask, range.begin(), range.end()); |
| 742 |
5/20✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13820 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
|
18442 | dilate(manager, prune); |
| 743 | auto& subtree = pool.local(); | ||
| 744 |
7/20✓ Branch 0 taken 3189 times.
✓ Branch 1 taken 1427 times.
✓ Branch 2 taken 8924 times.
✓ Branch 3 taken 4896 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 2 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
18442 | if (!subtree) subtree = std::move(mask); |
| 745 |
2/20✓ Branch 1 taken 1427 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4896 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
|
6323 | else subtree->merge(*mask, MERGE_ACTIVE_STATES); |
| 746 | }); | ||
| 747 | |||
| 748 |
2/2✓ Branch 0 taken 8614 times.
✓ Branch 1 taken 6 times.
|
17240 | if (!pool.empty()) { |
| 749 | auto piter = pool.begin(); | ||
| 750 |
2/2✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 6162 times.
|
17228 | MaskTreeT& subtree = mask ? *mask : **piter++; |
| 751 |
3/4✓ Branch 0 taken 9667 times.
✓ Branch 1 taken 8614 times.
✓ Branch 3 taken 9667 times.
✗ Branch 4 not taken.
|
55896 | for (; piter != pool.end(); ++piter) subtree.merge(**piter); |
| 752 | // prune, ensures partially merged nodes that may have become | ||
| 753 | // dense are converted to tiles | ||
| 754 |
3/4✓ Branch 0 taken 36 times.
✓ Branch 1 taken 8578 times.
✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
|
17228 | if (prune) tools::prune(subtree, zeroVal<typename MaskTreeT::ValueType>(), threaded); |
| 755 | // copy final topology onto dest. If mask exists, then this | ||
| 756 | // has already been handled by the above subtree merges | ||
| 757 |
3/4✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 6162 times.
✓ Branch 3 taken 2452 times.
✗ Branch 4 not taken.
|
17228 | if (!mask) mManager.tree().topologyUnion(subtree, /*preserve-active-tiles*/true); |
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | // sync | ||
| 762 | 17244 | mManager.rebuild(!threaded); | |
| 763 | } | ||
| 764 | |||
| 765 | |||
| 766 | template <typename TreeType> | ||
| 767 | inline void | ||
| 768 | 54278 | Morphology<TreeType>::NodeMaskOp::erode6(MaskType& mask) | |
| 769 | { | ||
| 770 |
2/2✓ Branch 0 taken 224688 times.
✓ Branch 1 taken 28086 times.
|
488502 | for (int x = 0; x < DIM; ++x) { |
| 771 |
2/2✓ Branch 0 taken 1797504 times.
✓ Branch 1 taken 224688 times.
|
3908016 | for (int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) { |
| 772 | // Extract the portion of the original mask that corresponds to a row in z. | ||
| 773 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 1797504 times.
✓ Branch 2 taken 1432036 times.
✓ Branch 3 taken 365468 times.
|
3473792 | if (Word& w = mask.template getWord<Word>(n)) { |
| 774 | // erode in two z directions (this is first since it uses the original w) | ||
| 775 | 2838413 | w = Word(w & | |
| 776 | 2838413 | (Word(w<<1 | (this->template gather<0,0,-1>(1, n)>>(DIM-1))) & | |
| 777 | 2838413 | Word(w>>1 | (this->template gather<0,0, 1>(2, n)<<(DIM-1))))); | |
| 778 | 2838413 | w = Word(w & this->gatherFacesXY(x, y, 0, n, 3)); | |
| 779 | } | ||
| 780 | }// loop over y | ||
| 781 | }//loop over x | ||
| 782 | 54278 | } | |
| 783 | |||
| 784 | template <typename TreeType> | ||
| 785 | inline void | ||
| 786 | 60604 | Morphology<TreeType>::NodeMaskOp::dilate6(const MaskType& mask) | |
| 787 | { | ||
| 788 |
2/2✓ Branch 0 taken 242416 times.
✓ Branch 1 taken 30302 times.
|
545436 | for (int x = 0; x < DIM; ++x ) { |
| 789 | 4363488 | for (int y = 0, n = (x << LOG2DIM); | |
| 790 |
2/2✓ Branch 0 taken 1939328 times.
✓ Branch 1 taken 242416 times.
|
4363488 | y < DIM; ++y, ++n) { |
| 791 | // Extract the portion of the original mask that corresponds to a row in z. | ||
| 792 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 1939328 times.
✓ Branch 2 taken 1040830 times.
✓ Branch 3 taken 898498 times.
|
3878656 | if (const Word w = mask.template getWord<Word>(n)) { |
| 793 | // Dilate the current leaf in the +z and -z direction | ||
| 794 | 2081660 | this->mWord = Word(w | (w>>1) | (w<<1)); | |
| 795 | 2081660 | this->scatter(0, n); | |
| 796 | // Dilate into neighbor leaf in the -z direction | ||
| 797 |
2/2✓ Branch 0 taken 783723 times.
✓ Branch 1 taken 257107 times.
|
2081660 | if ( (this->mWord = Word(w<<(DIM-1))) ) { |
| 798 | 1567446 | this->template scatter< 0, 0,-1>(1, n); | |
| 799 | } | ||
| 800 | // Dilate into neighbor leaf in the +z direction | ||
| 801 |
2/2✓ Branch 0 taken 663917 times.
✓ Branch 1 taken 376913 times.
|
2081660 | if ( (this->mWord = Word(w>>(DIM-1))) ) { |
| 802 | 1327834 | this->template scatter< 0, 0, 1>(2, n); | |
| 803 | } | ||
| 804 | // Dilate in the xy-face directions relative to the center leaf | ||
| 805 | 2081660 | this->mWord = w; | |
| 806 | 2081660 | this->scatterFacesXY(x, y, 0, n, 3); | |
| 807 | } | ||
| 808 | }// loop over y | ||
| 809 | }//loop over x | ||
| 810 | } | ||
| 811 | |||
| 812 | template <typename TreeType> | ||
| 813 | inline void | ||
| 814 | 234822 | Morphology<TreeType>::NodeMaskOp::dilate18(const MaskType& mask) | |
| 815 | { | ||
| 816 | //origins of neighbor leaf nodes in the -z and +z directions | ||
| 817 | 234822 | const Coord origin = this->getOrigin(); | |
| 818 | 234822 | const Coord orig_mz = origin.offsetBy(0, 0, -DIM); | |
| 819 | 234822 | const Coord orig_pz = origin.offsetBy(0, 0, DIM); | |
| 820 |
2/2✓ Branch 0 taken 939288 times.
✓ Branch 1 taken 117411 times.
|
2113398 | for (int x = 0; x < DIM; ++x ) { |
| 821 |
2/2✓ Branch 0 taken 7514304 times.
✓ Branch 1 taken 939288 times.
|
16907184 | for (int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) { |
| 822 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 7514304 times.
✓ Branch 2 taken 4096229 times.
✓ Branch 3 taken 3418075 times.
|
15028608 | if (const Word w = mask.template getWord<Word>(n)) { |
| 823 | { | ||
| 824 | 8192458 | this->mWord = Word(w | (w>>1) | (w<<1)); | |
| 825 | this->setOrigin(origin); | ||
| 826 | 8192458 | this->scatter(0, n); | |
| 827 | 8192458 | this->scatterFacesXY(x, y, 0, n, 3); | |
| 828 | 8192458 | this->mWord = w; | |
| 829 | 8192458 | this->scatterEdgesXY(x, y, 0, n, 3); | |
| 830 | } | ||
| 831 |
2/2✓ Branch 0 taken 3011004 times.
✓ Branch 1 taken 1085225 times.
|
8192458 | if ( (this->mWord = Word(w<<(DIM-1))) ) { |
| 832 | this->setOrigin(origin); | ||
| 833 | 6022008 | this->template scatter< 0, 0,-1>(1, n); | |
| 834 | this->setOrigin(orig_mz); | ||
| 835 | 6022008 | this->scatterFacesXY(x, y, 1, n, 11); | |
| 836 | } | ||
| 837 |
2/2✓ Branch 0 taken 3011682 times.
✓ Branch 1 taken 1084547 times.
|
8192458 | if ( (this->mWord = Word(w>>(DIM-1))) ) { |
| 838 | this->setOrigin(origin); | ||
| 839 | 6023364 | this->template scatter< 0, 0, 1>(2, n); | |
| 840 | this->setOrigin(orig_pz); | ||
| 841 | 6023364 | this->scatterFacesXY(x, y, 2, n, 15); | |
| 842 | } | ||
| 843 | } | ||
| 844 | }// loop over y | ||
| 845 | }//loop over x | ||
| 846 | } | ||
| 847 | |||
| 848 | |||
| 849 | template <typename TreeType> | ||
| 850 | inline void | ||
| 851 | 108514 | Morphology<TreeType>::NodeMaskOp::dilate26(const MaskType& mask) | |
| 852 | { | ||
| 853 | //origins of neighbor leaf nodes in the -z and +z directions | ||
| 854 | 108514 | const Coord origin = this->getOrigin(); | |
| 855 | 108514 | const Coord orig_mz = origin.offsetBy(0, 0, -DIM); | |
| 856 | 108514 | const Coord orig_pz = origin.offsetBy(0, 0, DIM); | |
| 857 |
2/2✓ Branch 0 taken 434056 times.
✓ Branch 1 taken 54257 times.
|
976626 | for (int x = 0; x < DIM; ++x) { |
| 858 |
2/2✓ Branch 0 taken 3472448 times.
✓ Branch 1 taken 434056 times.
|
7813008 | for (int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) { |
| 859 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 3472448 times.
✓ Branch 2 taken 1437729 times.
✓ Branch 3 taken 2034719 times.
|
6944896 | if (const Word w = mask.template getWord<Word>(n)) { |
| 860 | { | ||
| 861 | 2875458 | this->mWord = Word(w | (w>>1) | (w<<1)); | |
| 862 | this->setOrigin(origin); | ||
| 863 | 2875458 | this->scatter(0, n); | |
| 864 | 2875458 | this->scatterFacesXY(x, y, 0, n, 3); | |
| 865 | 2875458 | this->scatterEdgesXY(x, y, 0, n, 3); | |
| 866 | } | ||
| 867 |
2/2✓ Branch 0 taken 946918 times.
✓ Branch 1 taken 490811 times.
|
2875458 | if ( (this->mWord = Word(w<<(DIM-1))) ) { |
| 868 | this->setOrigin(origin); | ||
| 869 | 1893836 | this->template scatter< 0, 0,-1>(1, n); | |
| 870 | this->setOrigin(orig_mz); | ||
| 871 | 1893836 | this->scatterFacesXY(x, y, 1, n, 11); | |
| 872 | 1893836 | this->scatterEdgesXY(x, y, 1, n, 11); | |
| 873 | } | ||
| 874 |
2/2✓ Branch 0 taken 949447 times.
✓ Branch 1 taken 488282 times.
|
2875458 | if ( (this->mWord = Word(w>>(DIM-1))) ) { |
| 875 | this->setOrigin(origin); | ||
| 876 | 1898894 | this->template scatter< 0, 0, 1>(2, n); | |
| 877 | this->setOrigin(orig_pz); | ||
| 878 | 1898894 | this->scatterFacesXY(x, y, 2, n, 19); | |
| 879 | 1898894 | this->scatterEdgesXY(x, y, 2, n, 19); | |
| 880 | } | ||
| 881 | } | ||
| 882 | }// loop over y | ||
| 883 | }//loop over x | ||
| 884 | } | ||
| 885 | |||
| 886 | template<typename TreeType> | ||
| 887 | inline void | ||
| 888 | 28987678 | Morphology<TreeType>::NodeMaskOp::scatterFacesXY(int x, int y, int i1, int n, int i2) | |
| 889 | { | ||
| 890 | // dilate current leaf or neighbor in the -x direction | ||
| 891 |
2/2✓ Branch 0 taken 12663324 times.
✓ Branch 1 taken 1830515 times.
|
28987678 | if (x > 0) { |
| 892 | 25326648 | this->scatter(i1, n-DIM); | |
| 893 | } else { | ||
| 894 | 3661030 | this->template scatter<-1, 0, 0>(i2, n); | |
| 895 | } | ||
| 896 | // dilate current leaf or neighbor in the +x direction | ||
| 897 |
2/2✓ Branch 0 taken 12667677 times.
✓ Branch 1 taken 1826162 times.
|
28987678 | if (x < DIM-1) { |
| 898 | 25335354 | this->scatter(i1, n+DIM); | |
| 899 | } else { | ||
| 900 | 3652324 | this->template scatter< 1, 0, 0>(i2+1, n); | |
| 901 | } | ||
| 902 | // dilate current leaf or neighbor in the -y direction | ||
| 903 |
2/2✓ Branch 0 taken 12668253 times.
✓ Branch 1 taken 1825586 times.
|
28987678 | if (y > 0) { |
| 904 | 25336506 | this->scatter(i1, n-1); | |
| 905 | } else { | ||
| 906 | 3651172 | this->template scatter< 0,-1, 0>(i2+2, n); | |
| 907 | } | ||
| 908 | // dilate current leaf or neighbor in the +y direction | ||
| 909 |
2/2✓ Branch 0 taken 12686608 times.
✓ Branch 1 taken 1807231 times.
|
28987678 | if (y < DIM-1) { |
| 910 | 25373216 | this->scatter(i1, n+1); | |
| 911 | } else { | ||
| 912 | 3614462 | this->template scatter< 0, 1, 0>(i2+3, n); | |
| 913 | } | ||
| 914 | } | ||
| 915 | |||
| 916 | |||
| 917 | template<typename TreeType> | ||
| 918 | inline void | ||
| 919 | 14860646 | Morphology<TreeType>::NodeMaskOp::scatterEdgesXY(int x, int y, int i1, int n, int i2) | |
| 920 | { | ||
| 921 |
2/2✓ Branch 0 taken 6488095 times.
✓ Branch 1 taken 942228 times.
|
14860646 | if (x > 0) { |
| 922 |
2/2✓ Branch 0 taken 5671482 times.
✓ Branch 1 taken 816613 times.
|
12976190 | if (y > 0) { |
| 923 | 11342964 | this->scatter(i1, n-DIM-1); | |
| 924 | } else { | ||
| 925 | 1633226 | this->template scatter< 0,-1, 0>(i2+2, n-DIM); | |
| 926 | } | ||
| 927 |
2/2✓ Branch 0 taken 5672157 times.
✓ Branch 1 taken 815938 times.
|
12976190 | if (y < DIM-1) { |
| 928 | 11344314 | this->scatter(i1, n-DIM+1); | |
| 929 | } else { | ||
| 930 | 1631876 | this->template scatter< 0, 1, 0>(i2+3, n-DIM); | |
| 931 | } | ||
| 932 | } else { | ||
| 933 |
2/2✓ Branch 0 taken 821783 times.
✓ Branch 1 taken 120445 times.
|
1884456 | if (y < DIM-1) { |
| 934 | 1643566 | this->template scatter<-1, 0, 0>(i2 , n+1); | |
| 935 | } else { | ||
| 936 | 240890 | this->template scatter<-1, 1, 0>(i2+7, n ); | |
| 937 | } | ||
| 938 |
2/2✓ Branch 0 taken 820484 times.
✓ Branch 1 taken 121744 times.
|
1884456 | if (y > 0) { |
| 939 | 1640968 | this->template scatter<-1, 0, 0>(i2 , n-1); | |
| 940 | } else { | ||
| 941 | 243488 | this->template scatter<-1,-1, 0>(i2+4, n ); | |
| 942 | } | ||
| 943 | } | ||
| 944 |
2/2✓ Branch 0 taken 6490437 times.
✓ Branch 1 taken 939886 times.
|
14860646 | if (x < DIM-1) { |
| 945 |
2/2✓ Branch 0 taken 5672719 times.
✓ Branch 1 taken 817718 times.
|
12980874 | if (y > 0) { |
| 946 | 11345438 | this->scatter(i1, n+DIM-1); | |
| 947 | } else { | ||
| 948 | 1635436 | this->template scatter< 0,-1, 0>(i2+2, n+DIM); | |
| 949 | } | ||
| 950 |
2/2✓ Branch 0 taken 5674240 times.
✓ Branch 1 taken 816197 times.
|
12980874 | if (y < DIM-1) { |
| 951 | 11348480 | this->scatter(i1, n+DIM+1); | |
| 952 | } else { | ||
| 953 | 1632394 | this->template scatter< 0, 1, 0>(i2+3, n+DIM); | |
| 954 | } | ||
| 955 | } else { | ||
| 956 |
2/2✓ Branch 0 taken 819247 times.
✓ Branch 1 taken 120639 times.
|
1879772 | if (y > 0) { |
| 957 | 1638494 | this->template scatter< 1, 0, 0>(i2+1, n-1); | |
| 958 | } else { | ||
| 959 | 241278 | this->template scatter< 1,-1, 0>(i2+6, n ); | |
| 960 | } | ||
| 961 |
2/2✓ Branch 0 taken 819700 times.
✓ Branch 1 taken 120186 times.
|
1879772 | if (y < DIM-1) { |
| 962 | 1639400 | this->template scatter< 1, 0, 0>(i2+1, n+1); | |
| 963 | } else { | ||
| 964 | 240372 | this->template scatter< 1, 1, 0>(i2+5, n ); | |
| 965 | } | ||
| 966 | } | ||
| 967 | } | ||
| 968 | |||
| 969 | |||
| 970 | template<typename TreeType> | ||
| 971 | inline typename Morphology<TreeType>::NodeMaskOp::Word | ||
| 972 | 2838413 | Morphology<TreeType>::NodeMaskOp::gatherFacesXY(int x, int y, int i1, int n, int i2) | |
| 973 | { | ||
| 974 | // erode current leaf or neighbor in negative x-direction | ||
| 975 |
2/2✓ Branch 0 taken 1257278 times.
✓ Branch 1 taken 174758 times.
|
2838413 | Word w = x > 0 ? |
| 976 | this->gather(i1, n - DIM) : | ||
| 977 | this->template gather<-1,0,0>(i2, n); | ||
| 978 | |||
| 979 | // erode current leaf or neighbor in positive x-direction | ||
| 980 |
2/2✓ Branch 0 taken 1252039 times.
✓ Branch 1 taken 179997 times.
|
2838413 | w = Word(w & (x < DIM - 1 ? |
| 981 | this->gather(i1, n + DIM) : | ||
| 982 | 356982 | this->template gather<1,0,0>(i2 + 1, n))); | |
| 983 | |||
| 984 | // erode current leaf or neighbor in negative y-direction | ||
| 985 |
2/2✓ Branch 0 taken 1257441 times.
✓ Branch 1 taken 174595 times.
|
2838413 | w = Word(w & (y > 0 ? |
| 986 | this->gather(i1, n - 1) : | ||
| 987 | 346207 | this->template gather<0,-1,0>(i2 + 2, n))); | |
| 988 | |||
| 989 | // erode current leaf or neighbor in positive y-direction | ||
| 990 |
2/2✓ Branch 0 taken 1252052 times.
✓ Branch 1 taken 179984 times.
|
2838413 | w = Word(w & (y < DIM - 1 ? |
| 991 | this->gather(i1, n + 1) : | ||
| 992 | 356953 | this->template gather<0,1,0>(i2+3, n))); | |
| 993 | |||
| 994 | 2838413 | return w; | |
| 995 | } | ||
| 996 | |||
| 997 | |||
| 998 | template<typename TreeType> | ||
| 999 | inline typename Morphology<TreeType>::NodeMaskOp::Word | ||
| 1000 | Morphology<TreeType>::NodeMaskOp::gatherEdgesXY(int x, int y, int i1, int n, int i2) | ||
| 1001 | { | ||
| 1002 | Word w = ~Word(0); | ||
| 1003 | |||
| 1004 | if (x > 0) { | ||
| 1005 | w &= y > 0 ? this->gather(i1, n-DIM-1) : | ||
| 1006 | this->template gather< 0,-1, 0>(i2+2, n-DIM); | ||
| 1007 | w &= y < DIM-1 ? this->gather(i1, n-DIM+1) : | ||
| 1008 | this->template gather< 0, 1, 0>(i2+3, n-DIM); | ||
| 1009 | } else { | ||
| 1010 | w &= y < DIM-1 ? this->template gather<-1, 0, 0>(i2 , n+1): | ||
| 1011 | this->template gather<-1, 1, 0>(i2+7, n ); | ||
| 1012 | w &= y > 0 ? this->template gather<-1, 0, 0>(i2 , n-1): | ||
| 1013 | this->template gather<-1,-1, 0>(i2+4, n ); | ||
| 1014 | } | ||
| 1015 | if (x < DIM-1) { | ||
| 1016 | w &= y > 0 ? this->gather(i1, n+DIM-1) : | ||
| 1017 | this->template gather< 0,-1, 0>(i2+2, n+DIM); | ||
| 1018 | w &= y < DIM-1 ? this->gather(i1, n+DIM+1) : | ||
| 1019 | this->template gather< 0, 1, 0>(i2+3, n+DIM); | ||
| 1020 | } else { | ||
| 1021 | w &= y > 0 ? this->template gather< 1, 0, 0>(i2+1, n-1): | ||
| 1022 | this->template gather< 1,-1, 0>(i2+6, n ); | ||
| 1023 | w &= y < DIM-1 ? this->template gather< 1, 0, 0>(i2+1, n+1): | ||
| 1024 | this->template gather< 1, 1, 0>(i2+5, n ); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | return w; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | } // namespace morphology | ||
| 1031 | |||
| 1032 | |||
| 1033 | ///////////////////////////////////////////////////////////////////// | ||
| 1034 | ///////////////////////////////////////////////////////////////////// | ||
| 1035 | |||
| 1036 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 1037 | |||
| 1038 | namespace morph_internal { | ||
| 1039 | template <typename T> struct Adapter { | ||
| 1040 | using TreeType = T; | ||
| 1041 | static TreeType& get(T& tree) { return tree; } | ||
| 1042 | static void sync(T&) {} // no-op | ||
| 1043 | }; | ||
| 1044 | template <typename T> | ||
| 1045 | struct Adapter<openvdb::tree::LeafManager<T>> { | ||
| 1046 | using TreeType = T; | ||
| 1047 | static TreeType& get(openvdb::tree::LeafManager<T>& M) { return M.tree(); } | ||
| 1048 | ✗ | static void sync(openvdb::tree::LeafManager<T>& M) { M.rebuild(); } | |
| 1049 | }; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | /// @endcond | ||
| 1053 | |||
| 1054 | template<typename TreeOrLeafManagerT> | ||
| 1055 | 8396 | void dilateActiveValues(TreeOrLeafManagerT& treeOrLeafM, | |
| 1056 | const int iterations, | ||
| 1057 | const NearestNeighbors nn, | ||
| 1058 | const TilePolicy mode, | ||
| 1059 | const bool threaded) | ||
| 1060 | { | ||
| 1061 | using AdapterT = morph_internal::Adapter<TreeOrLeafManagerT>; | ||
| 1062 | using TreeT = typename AdapterT::TreeType; | ||
| 1063 | using MaskT = typename TreeT::template ValueConverter<ValueMask>::Type; | ||
| 1064 | |||
| 1065 |
2/2✓ Branch 0 taken 4179 times.
✓ Branch 1 taken 19 times.
|
13304 | if (iterations <= 0) return; |
| 1066 | |||
| 1067 |
2/2✓ Branch 0 taken 3406 times.
✓ Branch 1 taken 773 times.
|
8358 | if (mode == IGNORE_TILES) { |
| 1068 | 6742 | morphology::Morphology<TreeT> morph(treeOrLeafM); | |
| 1069 | morph.setThreaded(threaded); | ||
| 1070 | // This will also sync the leaf manager | ||
| 1071 |
1/2✓ Branch 1 taken 3406 times.
✗ Branch 2 not taken.
|
6812 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
| 1072 | return; | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | // The following branching optimises from the different tree types | ||
| 1076 | // and TilePolicy combinations | ||
| 1077 | |||
| 1078 | auto& tree = AdapterT::get(treeOrLeafM); | ||
| 1079 | |||
| 1080 | // If the input is a mask tree, don't copy the topology - voxelize | ||
| 1081 | // it directly and let the morphology class directly steal/prune | ||
| 1082 | // its nodes | ||
| 1083 | constexpr bool isMask = std::is_same<TreeT, MaskT>::value; | ||
| 1084 | |||
| 1085 |
2/2✓ Branch 0 taken 731 times.
✓ Branch 1 taken 16 times.
|
1494 | if (isMask || mode == EXPAND_TILES) { |
| 1086 | tree.voxelizeActiveTiles(); | ||
| 1087 | AdapterT::sync(treeOrLeafM); | ||
| 1088 | 1514 | morphology::Morphology<TreeT> morph(treeOrLeafM); | |
| 1089 | morph.setThreaded(threaded); | ||
| 1090 | |||
| 1091 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 737 times.
|
1514 | if (mode == PRESERVE_TILES) { |
| 1092 |
1/2✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
|
40 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/true); |
| 1093 | } | ||
| 1094 | else { | ||
| 1095 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
12 | assert(mode == EXPAND_TILES); |
| 1096 |
1/2✓ Branch 1 taken 737 times.
✗ Branch 2 not taken.
|
1474 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
| 1097 | } | ||
| 1098 | return; | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | // If the tree TreeType being dilated is not a MaskTree, always copy | ||
| 1102 | // the topology over onto a MaskTree, perform the required dilation | ||
| 1103 | // and copy the final topology back. This technique avoids unnecessary | ||
| 1104 | // allocation with tile expansion and correctly preserves the tree | ||
| 1105 | // topology. | ||
| 1106 | // | ||
| 1107 | // Note that we also always use a mask if the tile policy is PRESERVE_TILES | ||
| 1108 | // due to the way the underlying dilation only works on voxels. | ||
| 1109 | // @todo Investigate tile based dilation | ||
| 1110 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
32 | assert(mode == PRESERVE_TILES); |
| 1111 | |||
| 1112 | 64 | MaskT topology; | |
| 1113 | topology.topologyUnion(tree); | ||
| 1114 | topology.voxelizeActiveTiles(); | ||
| 1115 | |||
| 1116 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | morphology::Morphology<MaskT> morph(topology); |
| 1117 | 32 | morph.setThreaded(threaded); | |
| 1118 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/true); |
| 1119 | |||
| 1120 | tree.topologyUnion(topology, /*preserve-tiles*/true); | ||
| 1121 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | topology.clear(); |
| 1122 | |||
| 1123 | // @note this is necessary to match the behaviour of mask tree dilation | ||
| 1124 | // where source partial leaf nodes that become dense are also | ||
| 1125 | // converted into tiles, not simply newly created dense nodes | ||
| 1126 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | tools::prune(tree, zeroVal<typename TreeT::ValueType>(), threaded); |
| 1127 | AdapterT::sync(treeOrLeafM); | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | |||
| 1131 | template<typename TreeOrLeafManagerT> | ||
| 1132 | 3368 | void erodeActiveValues(TreeOrLeafManagerT& treeOrLeafM, | |
| 1133 | const int iterations, | ||
| 1134 | const NearestNeighbors nn, | ||
| 1135 | const TilePolicy mode, | ||
| 1136 | const bool threaded) | ||
| 1137 | { | ||
| 1138 | using AdapterT = morph_internal::Adapter<TreeOrLeafManagerT>; | ||
| 1139 | using TreeT = typename AdapterT::TreeType; | ||
| 1140 | using MaskT = typename TreeT::template ValueConverter<ValueMask>::Type; | ||
| 1141 | |||
| 1142 | 3390 | if (iterations <= 0) return; | |
| 1143 | |||
| 1144 | // If the tile policiy is PRESERVE_TILES, peform the erosion on a | ||
| 1145 | // voxelized mask grid followed by a topology intersection such that | ||
| 1146 | // the original uneroded topology is preserved. | ||
| 1147 | 3362 | if (mode == PRESERVE_TILES) { | |
| 1148 | auto& tree = AdapterT::get(treeOrLeafM); | ||
| 1149 | 44 | MaskT topology; | |
| 1150 | topology.topologyUnion(tree); | ||
| 1151 | topology.voxelizeActiveTiles(); | ||
| 1152 | |||
| 1153 | { | ||
| 1154 | 22 | morphology::Morphology<MaskT> morph(topology); | |
| 1155 | 22 | morph.setThreaded(threaded); | |
| 1156 | 22 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); | |
| 1157 | } | ||
| 1158 | |||
| 1159 | // prune to ensure topologyIntersection does not expand tiles | ||
| 1160 | // which have not been changed | ||
| 1161 | 22 | tools::prune(topology, zeroVal<typename MaskT::ValueType>(), threaded); | |
| 1162 | tree.topologyIntersection(topology); | ||
| 1163 | AdapterT::sync(treeOrLeafM); | ||
| 1164 | return; | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | 3340 | if (mode == EXPAND_TILES) { | |
| 1168 | // if expanding, voxelize everything first if there are active tiles | ||
| 1169 | // @note check first to avoid any unnecessary rebuilds | ||
| 1170 | auto& tree = AdapterT::get(treeOrLeafM); | ||
| 1171 | 6 | if (tree.hasActiveTiles()) { | |
| 1172 | tree.voxelizeActiveTiles(); | ||
| 1173 | AdapterT::sync(treeOrLeafM); | ||
| 1174 | } | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | // ignoring tiles. They won't be eroded | ||
| 1178 | 3340 | morphology::Morphology<TreeT> morph(treeOrLeafM); | |
| 1179 | morph.setThreaded(threaded); | ||
| 1180 | 3340 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); | |
| 1181 | } | ||
| 1182 | |||
| 1183 | |||
| 1184 | ///////////////////////////////////////////////////////////////////// | ||
| 1185 | ///////////////////////////////////////////////////////////////////// | ||
| 1186 | |||
| 1187 | |||
| 1188 | /// @brief Topologically dilate all leaf-level active voxels in a tree | ||
| 1189 | /// using one of three nearest neighbor connectivity patterns. | ||
| 1190 | /// @warning This method is NOT multi-threaded and ignores active tiles! | ||
| 1191 | /// | ||
| 1192 | /// @param tree tree to be dilated | ||
| 1193 | /// @param iterations number of iterations to apply the dilation | ||
| 1194 | /// @param nn connectivity pattern of the dilation: either | ||
| 1195 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
| 1196 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
| 1197 | /// nearest neighbors). | ||
| 1198 | /// | ||
| 1199 | /// @note The values of any voxels are unchanged. | ||
| 1200 | template<typename TreeType> | ||
| 1201 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::dilateActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
| 1202 | 1 | inline void dilateVoxels(TreeType& tree, | |
| 1203 | int iterations = 1, | ||
| 1204 | NearestNeighbors nn = NN_FACE) | ||
| 1205 | { | ||
| 1206 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (iterations <= 0) return; |
| 1207 | 1 | morphology::Morphology<TreeType> morph(tree); | |
| 1208 | morph.setThreaded(false); // backwards compatible | ||
| 1209 | // This will also sync the leaf manager | ||
| 1210 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
| 1211 | } | ||
| 1212 | |||
| 1213 | /// @brief Topologically dilate all leaf-level active voxels in a tree | ||
| 1214 | /// using one of three nearest neighbor connectivity patterns. | ||
| 1215 | /// @warning This method is NOT multi-threaded and ignores active tiles! | ||
| 1216 | /// | ||
| 1217 | /// @param manager LeafManager containing the tree to be dilated. | ||
| 1218 | /// On exit it is updated to include all the leaf | ||
| 1219 | /// nodes of the dilated tree. | ||
| 1220 | /// @param iterations number of iterations to apply the dilation | ||
| 1221 | /// @param nn connectivity pattern of the dilation: either | ||
| 1222 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
| 1223 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
| 1224 | /// nearest neighbors). | ||
| 1225 | /// | ||
| 1226 | /// @note The values of any voxels are unchanged. | ||
| 1227 | template<typename TreeType> | ||
| 1228 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::dilateActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
| 1229 | 1 | inline void dilateVoxels(tree::LeafManager<TreeType>& manager, | |
| 1230 | int iterations = 1, | ||
| 1231 | NearestNeighbors nn = NN_FACE) | ||
| 1232 | { | ||
| 1233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (iterations <= 0) return; |
| 1234 | morphology::Morphology<TreeType> morph(manager); | ||
| 1235 | morph.setThreaded(false); | ||
| 1236 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
| 1237 | } | ||
| 1238 | |||
| 1239 | //@{ | ||
| 1240 | /// @brief Topologically erode all leaf-level active voxels in the given tree. | ||
| 1241 | /// @details That is, shrink the set of active voxels by @a iterations voxels | ||
| 1242 | /// in the +x, -x, +y, -y, +z and -z directions, but don't change the values | ||
| 1243 | /// of any voxels, only their active states. | ||
| 1244 | /// @todo Currently operates only on leaf voxels; need to extend to tiles. | ||
| 1245 | template<typename TreeType> | ||
| 1246 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::erodeActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
| 1247 | 1 | inline void erodeVoxels(TreeType& tree, | |
| 1248 | int iterations=1, | ||
| 1249 | NearestNeighbors nn = NN_FACE) | ||
| 1250 | { | ||
| 1251 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (iterations > 0) { |
| 1252 | 1 | morphology::Morphology<TreeType> morph(tree); | |
| 1253 | morph.setThreaded(true); | ||
| 1254 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
| 1255 | } | ||
| 1256 | |||
| 1257 | 1 | tools::pruneLevelSet(tree); // matches old behaviour | |
| 1258 | 1 | } | |
| 1259 | |||
| 1260 | template<typename TreeType> | ||
| 1261 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::erodeActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
| 1262 | 1 | inline void erodeVoxels(tree::LeafManager<TreeType>& manager, | |
| 1263 | int iterations = 1, | ||
| 1264 | NearestNeighbors nn = NN_FACE) | ||
| 1265 | { | ||
| 1266 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (iterations <= 0) return; |
| 1267 | morphology::Morphology<TreeType> morph(manager); | ||
| 1268 | morph.setThreaded(true); | ||
| 1269 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
| 1270 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | tools::pruneLevelSet(manager.tree()); // matches old behaviour |
| 1271 | } | ||
| 1272 | //@} | ||
| 1273 | |||
| 1274 | |||
| 1275 | //////////////////////////////////////// | ||
| 1276 | |||
| 1277 | |||
| 1278 | // Explicit Template Instantiation | ||
| 1279 | |||
| 1280 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 1281 | |||
| 1282 | #ifdef OPENVDB_INSTANTIATE_MORPHOLOGY | ||
| 1283 | #include <openvdb/util/ExplicitInstantiation.h> | ||
| 1284 | #endif | ||
| 1285 | |||
| 1286 | #define _FUNCTION(TreeT) \ | ||
| 1287 | void dilateActiveValues(TreeT&, \ | ||
| 1288 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
| 1289 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
| 1290 | #undef _FUNCTION | ||
| 1291 | |||
| 1292 | #define _FUNCTION(TreeT) \ | ||
| 1293 | void dilateActiveValues(tree::LeafManager<TreeT>&, \ | ||
| 1294 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
| 1295 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
| 1296 | #undef _FUNCTION | ||
| 1297 | |||
| 1298 | #define _FUNCTION(TreeT) \ | ||
| 1299 | void erodeActiveValues(TreeT&, \ | ||
| 1300 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
| 1301 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
| 1302 | #undef _FUNCTION | ||
| 1303 | |||
| 1304 | #define _FUNCTION(TreeT) \ | ||
| 1305 | void erodeActiveValues(tree::LeafManager<TreeT>&, \ | ||
| 1306 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
| 1307 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
| 1308 | #undef _FUNCTION | ||
| 1309 | |||
| 1310 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 1311 | |||
| 1312 | |||
| 1313 | } // namespace tools | ||
| 1314 | } // namespace OPENVDB_VERSION_NAME | ||
| 1315 | } // namespace openvdb | ||
| 1316 | |||
| 1317 | #endif // OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED | ||
| 1318 |