MeshLib C++ Docs
Loading...
Searching...
No Matches
Subvoxel Mesh Correction

Precise automatic mesh correction or/and smoothing based on reference voxel (volume) data. More...

Classes

struct  MR::MoveMeshToVoxelMaxDerivSettings
 
class  MR::MeshOnVoxelsT< MeshType >
 

Functions

MRVOXELS_API Expected< VertBitSet > MR::moveMeshToVoxelMaxDeriv (Mesh &mesh, const AffineXf3f &meshXf, const VdbVolume &volume, const AffineXf3f &volumeXf, const MoveMeshToVoxelMaxDerivSettings &settings, ProgressCallback callback={})
 

Detailed Description

Precise automatic mesh correction or/and smoothing based on reference voxel (volume) data.

This group provides highlevel interface of the algorithm: moveMeshToVoxelMaxDeriv configurable through MoveMeshToVoxelMaxDerivSettings and the low-level structure MeshOnVoxelsT that allows creation of custom correction strategy.

Description of the algorithm.

  1. Input: Mesh and VdbVolume with corresponding transforms. The objects must be aligned.
  2. First step is to apply preparation smoothing with force specified by MoveMeshToVoxelMaxDerivSettings::preparationSmoothForce. This step might be needed if the initial surface is very noisy.
  3. Then the algorithm performs correction iteratively for the total number of iterations specified by MoveMeshToVoxelMaxDerivSettings::iters.
    1. Each iteration consist of moving each vertex of the mesh towards its optimal position. More formally, let's denote, \( V = \left\{ ( v_i, n_i ) \right\} \) – set of vertices and corresponding normals of the mesh, and \( F: \mathbb{R}^3 \rightarrow \mathbb{R} \) – scalar field of volume – for each position in the scene, it gives the density of the voxels in this position. For points with non-integer coordinates, the density is interpolated linearly (for details see MeshOnVoxelsT).

      The optimal position \( p_i \) of the vertex \( v_i \) is given by fitting the polynomial \( Q_i \) of degree MoveMeshToVoxelMaxDerivSettings::degree to the set of points \( \left\{ F\left( v_i + n_i \cdot k \cdot \text{voxel size} \right) \mid k \in -l \dots l \right\} \) where \( l = \frac{\text{sample points}}{2} \) (see MoveMeshToVoxelMaxDerivSettings::samplePoints); and finding the minimum of the derivative of this polynomial.

      Since the MoveMeshToVoxelMaxDerivSettings::degree is limited to \( 6 \), we know that the degree of \( \frac{d^2 Q_i}{dx^2} \) is limited to \( 4 \) and thus the equation \( \frac{d^2 Q_i}{dx^2} \left( x \right) = 0 \) has analytical solution and it gives us the extrema of the derivative. Comparing the evaluations of the polynomial at extrema and on the border of the interval we obtain the optimal position \( p_i \). To sum up:

      \begin{align*} &l = \frac{\text{sample points}}{2} \\ &N_i \leftarrow \left\{ v_i + n_i \cdot k \cdot \text{voxel size} \mid k = -l \dots l \right\} \\ &\overline{N_i} \leftarrow \left\{ v_i + n_i \cdot x \cdot \text{voxel size} \mid x \in \left[-l; l\right] \right\} \\ &Q_i \leftarrow \text{fit polynomial from } \left\{ \left( p, F(p) \right) \mid p \in N_i \right\} \\ &p_i \leftarrow \min_{x \in \overline{N_i}} \frac{dQ_i}{dx} \end{align*}

      Note that we are finding the minimum of \( \frac{dQ_i}{dx} \) analytically over the real interval \( \overline{N_i} \) and not over \( N_i \). This is exactly where the subvoxel precision comes from.

    2. However, directly using the optimal position has proven to be error-prone, as both voxels and mesh could contain noise. Therefore, we use the following heuristic: instead of moving the vertex \( v_i \) to position \( p_i \), we construct a field of shifts with domain on the mesh vertices \( S : v_i \mapsto \text{clamp}\left( p_i - v_i \right) \) and smooth it with the force specified by MoveMeshToVoxelMaxDerivSettings::intermediateSmoothForce for 15 iterations. Then we update the vertices according to the smoothed field:

      \[ v_i \leftarrow v_i + S_{smooth}\left( v_i \right) \]

      and proceed to the next interation.
  4. The algorithm modifies the mesh inplace and returns the set of vertices that were moved during the correction.

A nice visualization of why it works could be found in MeshInspector's plugin "Voxels Inspector". Just click on any vertex and you will see the values of \( F, Q_i \) on the interval \( \overline{N_i} \), as well as points of \( N_i \).

Usage

The algorithm can be used in different cases related to the CT-scanning workflows:

Function Documentation

◆ moveMeshToVoxelMaxDeriv()

MRVOXELS_API Expected< VertBitSet > MR::moveMeshToVoxelMaxDeriv ( Mesh & mesh,
const AffineXf3f & meshXf,
const VdbVolume & volume,
const AffineXf3f & volumeXf,
const MoveMeshToVoxelMaxDerivSettings & settings,
ProgressCallback callback = {} )

Moves each vertex along its normal to the minimize (with sign, i.e. maximize the absolute value with negative sign) the derivative of voxels.

Returns
Vertices that were moved by the algorithm