Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,47 @@ Important details:
See `development/mesh/check_marching_cubes.py` for reference comparisons and
`development/mesh/benchmark_marching_cubes.py` for reproducible timings.

### Mesh Smoothing

Laplacian mesh smoothing is available as `bic.mesh.smooth_mesh`. It was moved
from `bic.utils.smooth_mesh`; the old location is no longer available. The C++
API likewise moved from `#include "bioimage_cpp/mesh_smoothing.hxx"` and
`bioimage_cpp::smooth_mesh` to `#include "bioimage_cpp/mesh/smoothing.hxx"`
and `bioimage_cpp::mesh::smooth_mesh`.

### Mesh Simplification

Topology-preserving triangle-mesh simplification is available as
`bic.mesh.simplify_mesh`. It uses quadric-error edge collapses, preserves
manifold topology and open boundaries, and applies configurable soft penalties
to sharp features:

vertices, faces, _, values = bic.mesh.marching_cubes(
volume, level=0.5, allow_degenerate=False
)
vertices, faces, normals, values = bic.mesh.simplify_mesh(
vertices,
faces,
reduction=0.8,
values=values,
feature_angle=45.0,
feature_weight=10.0,
)

`reduction` is the approximate fraction of input faces to remove. A mesh can
stop above its target if no topology- and boundary-safe collapse remains.
Normals are recomputed from the final face geometry. Optional scalar values are
interpolated along collapsed edges; the fourth return item is `None` when
values are omitted. Vertices and values preserve float32/float64 input dtypes,
while returned faces are int64.

The input must be a non-empty, consistently oriented triangle 2-manifold with
no duplicate, zero-area, or unreferenced geometry. Use
`marching_cubes(..., allow_degenerate=False)` before simplification; invalid
meshes are rejected rather than silently repaired.



### Anti-Aliased Resampling

`affine_transform` itself never pre-smooths the input; downsampling without
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Image processing and segmentation functionality in C++ with light-weight python bindings through nanobind and minimal dependencies to enable distribution via pip.

The package includes dependency-free triangle-mesh extraction from 3D volumes
and segmentation masks under `bioimage_cpp.mesh`.
and segmentation masks, plus Laplacian mesh smoothing, under `bioimage_cpp.mesh`.

The `bioimage_cpp` python library can be installed via pip:
```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

Run::

python development/utils/benchmark_mesh_smoothing.py --n-vertices 5000 --iterations 5 --repeats 3
python development/utils/benchmark_mesh_smoothing.py --n-vertices 20000 --threads 1,2,4,0
python development/mesh/benchmark_mesh_smoothing.py --n-vertices 5000 --iterations 5 --repeats 3
python development/mesh/benchmark_mesh_smoothing.py --n-vertices 20000 --threads 1,2,4,0

Notes
-----
Expand Down Expand Up @@ -117,7 +117,7 @@ def main() -> int:
label = f"bioimage_cpp[n_threads={n_threads}]"

def run(verts=mesh.verts, normals=mesh.normals, faces=mesh.faces, n=n_threads):
bic.utils.smooth_mesh(verts, normals, faces, iterations=args.iterations, n_threads=n)
bic.mesh.smooth_mesh(verts, normals, faces, iterations=args.iterations, n_threads=n)

times = time_call(run, args.repeats, args.warmup)
rows.append(
Expand All @@ -141,7 +141,7 @@ def run_ref():
# exact agreement; see module docstring).
if reference is not None:
ref_v, ref_n = reference(mesh.verts, mesh.normals, mesh.faces, 1)
ours_v, ours_n = bic.utils.smooth_mesh(mesh.verts, mesh.normals, mesh.faces, iterations=1)
ours_v, ours_n = bic.mesh.smooth_mesh(mesh.verts, mesh.normals, mesh.faces, iterations=1)
v_max_diff = float(np.max(np.abs(ours_v - ref_v)))
n_max_diff = float(np.max(np.abs(ours_n - ref_n)))
print(f"Correctness vs reference (iterations=1): "
Expand Down
Loading
Loading