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={}) |
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.
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.
\[ v_i \leftarrow v_i + S_{smooth}\left( v_i \right) \]
and proceed to the next interation.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 \).
The algorithm can be used in different cases related to the CT-scanning workflows:
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.