# let's look at an example first. let's load the example segmentation
= tcio.read_h5(f"datasets/reconstruction_example/ilastik_probabilities.h5")[0]
segmentation
# now let's select all the points where the segmentation probability exceeds some threshold to get our point cloud
= 0.95
threshold = segmentation>threshold
segmentation_binary = ndimage.binary_erosion(segmentation_binary, iterations=1)
segmentation_binary = np.stack(np.where(segmentation_binary), axis=-1) points
Mesh creation and remeshing with MeshLab
pymeshlab
as backend
This notebook contains functions for (a) creating a mesh from a point cloud, (b) subdividing meshes while preserving UV information, and (c) remeshing (which destroys UV information), using pymeshlab.
Compared to the igl
-based remesh
module, there are some more powerful remeshing algorithms: isotropic remeshing, which creates a new mesh with (close-to) equal-sized, equilateral triangles (great for improving a marching cubes mesh), and adaptive subdivision (subdivide long edges only).
Constructing a mesh from a point cloud (rather than from a volumetric using marching cubes) is sometimes useful when the surface you are interested in is not the boundary of a volume (e.g. a membrane floating in free space).
Subdivision is useful since it allows one to increase or decrease the mesh “resolution” as needed when the mesh is deformed. The advantage of pymeshlab-based subdivision is that it allows subdividing “long” edges only, while igl-based subdivision subdivides all edges, which (exponentially) increases the number of your vertices.
Poisson reconstruction
Poisson reconstruction is an algorithm to reconstruct a mesh from a point cloud. It requires the points to be equipped with normal vectors, which are computed in the first step.
This is useful for surfaces that are not the boundary of a solid volume, like a membrane.
= 80
zslice plt.imshow(segmentation_binary[zslice,:,:])
# A point cloud is simply a mesh with no faces
= tcmesh.ObjMesh(vertices=points, faces=[])
point_cloud = intmsl.convert_to_pymeshlab(point_cloud) point_cloud_pymeshlab
# let's create a pymeshlab instance and add out point cloud to it
# There are three relevant filters we will use:
# generate_simplified_point_cloud - reduce number of points in point cloud
# compute_normal_for_point_clouds - estimate normals for point cloid. This is required for the next step
# generate_surface_reconstruction_screened_poisson - Surface reconstruction by Poisson reconstruction
= pymeshlab.MeshSet()
ms
ms.add_mesh(point_cloud_pymeshlab)
=1000)
ms.generate_simplified_point_cloud(samplenum=20, smoothiter=2)
ms.compute_normal_for_point_clouds(k=8, fulldepth=5,)
ms.generate_surface_reconstruction_screened_poisson(depth
=10, targetlen=pymeshlab.PercentageValue(1))
ms.meshing_isotropic_explicit_remeshing(iterations
= intmsl.convert_from_pymeshlab(ms.current_mesh()) mesh_reconstructed
mesh_reconstructed.faces.shape
(22156, 3)
reconstruct_poisson
reconstruct_poisson (points, samplenum=1000, k_neighbor_normals=10, reconstruc_args=None)
*Reconstruct triangular mesh from a point cloud.
Recommended to use isotropic remeshing after to improve mesh quality (remesh_pymeshlab). Wrapper of pymeshlab filter generate_surface_reconstruction_screened_poisson See pymeshlab.readthedocs.io/en/latest/filter_list.html for its arguments.*
Type | Default | Details | |
---|---|---|---|
points | np.array of shape (n_points, 3) | Point cloud. | |
samplenum | int | 1000 | Sample number for point cloud simplification. Smaller values lead to more simplification of the point cloud and faster processing |
k_neighbor_normals | int | 10 | Nearest neighbors for constructing normal vectors to point cloud |
reconstruc_args | NoneType | None | Arguments to surface reconstruction algorithm/ |
Returns | tcmehs.ObjMesh | Reconstructed surface. |
reconstruct_poisson(points)
<blender_tissue_cartography.mesh.ObjMesh>
Subdivision / simplification
subdivide_pymeshlab
subdivide_pymeshlab (mesh, threshold=1, iterations=3, reglue=True, decimals=None)
*Refine mesh by edge subdivision using pymeshlab.
Subdivides all edges by placing new vertices at edge midpoints. Preserves UV information, by cutting the mesh along seams and (optionally) gluing back after. New texture vertices are also placed at texture-edge midpoints.
In contrast to remeshing_igl.subdivide_igl, you can choose to subdivide long edges only, which is very useful to increase mesh resolution only where necessary.*
Type | Default | Details | |
---|---|---|---|
mesh | ObjMesh | Initial mesh. | |
threshold | int | 1 | All the edges longer than this threshold will be refined. In percent? Lower values mean more subdivision. Setting this value to zero will force an uniform refinement. |
iterations | int | 3 | Filter iterations |
reglue | bool | True | Glue back after cutting |
decimals | NoneType | None | Decimal precision for merging vertices when regluing. If None, estimate from the average edge mesh length as -4*log_10(avg length) |
Returns | ObjMesh | Subdivided mesh. |
= tcmesh.ObjMesh.read_obj("datasets/movie_example/initial_uv.obj")
mesh_test = subdivide_pymeshlab(mesh_test, reglue=True, threshold=1) mesh_subdiv
Warning: readOBJ() ignored non-comment line 4:
o mesh_01_cylinder_seams_uv
mesh_test.vertices.shape, mesh_subdiv.vertices.shape
((20212, 3), (12625, 3))
*mesh_subdiv.texture_vertices.T, mesh_subdiv.texture_tris, lw=0.2) plt.triplot(
Isotropic remeshing
Will dstroy UV information. Only use it before you create your UV maps.
remesh_pymeshlab
remesh_pymeshlab (mesh, targetlen=1, iterations=10)
*Remesh mesh using pymeshlab.
This creates a triangulation where triangles are close to equilateral and everywhere the same size. Very useful to improve the output of marching cubes. Uses the meshing_isotropic_explicit_remeshing filter.
This function erases UV information!*
Type | Default | Details | |
---|---|---|---|
mesh | ObjMesh | Initial mesh. | |
targetlen | int | 1 | Percent value for target edge length. |
iterations | int | 10 | Number of iterations. |
Returns | ObjMesh | Simplified mesh. |
= tcmesh.ObjMesh.read_obj("datasets/movie_example/uv_sphere.obj") mesh
= remesh_pymeshlab(mesh, iterations=10, targetlen=5) mesh_remeshed
mesh_remeshed.only_vertices
True
*mesh_remeshed.vertices[:,:2].T, mesh_remeshed.tris, lw=0.2) plt.triplot(
"datasets/movie_example/isotropic_remeshed.obj") mesh_remeshed.write_obj(