PhenoMesh¶
The PhenoMesh class extends PyVista’s PolyData with phenotyping-specific
operations for 3D plant analysis.
PhenoMesh Class¶
- class PhenoMesh[source]¶
Bases:
PolyDataExtension of PyVista PolyData with phenotyping-specific operations.
PhenoMesh inherits from pv.PolyData, so it can be used anywhere a PolyData is expected. It adds convenience methods for 3D plant phenotyping workflows including smoothing, repair, curvature analysis, and domain segmentation.
- Parameters:
var_inp (
PolyData|str|Path|ndarray[tuple[Any,...],dtype[floating[Any]]] |None, default:None) – Input data - can be a PolyData, file path, or vertex array*args (
Any) – Additional positional arguments passed to pv.PolyDatacontour (
ndarray[tuple[Any,...],dtype[bool]] |None, default:None) – Optional binary contour array the mesh was generated fromresolution (
list[float] |None, default:None) – Optional spatial resolution [z, y, x]**kwargs (
Any) – Additional keyword arguments passed to pv.PolyData
Example
>>> mesh = PhenoMesh(pv.Sphere()) >>> smoothed = mesh.smooth(iterations=100) >>> isinstance(mesh, pv.PolyData) # True - PhenoMesh is a PolyData True
- __init__(var_inp=None, *args, contour=None, resolution=None, **kwargs)[source]¶
Initialize PhenoMesh with optional PolyData, file, or vertex array.
- property contour: ndarray[tuple[Any, ...], dtype[bool]] | None¶
Binary contour array the mesh was generated from.
- classmethod from_polydata(polydata, contour=None, resolution=None)[source]¶
Create PhenoMesh from PyVista PolyData.
- to_polydata()[source]¶
Return a plain PolyData copy (without PhenoMesh attributes).
- Return type:
PolyData- Returns:
A new pv.PolyData copy of this mesh
- smooth(iterations=100, relaxation_factor=0.01, feature_smoothing=False, boundary_smoothing=True, edge_angle=15.0, feature_angle=45.0)[source]¶
Smooth the mesh using Laplacian smoothing.
- Parameters:
iterations (
int, default:100) – Number of smoothing iterationsrelaxation_factor (
float, default:0.01) – Relaxation factor for smoothing (0-1)feature_smoothing (
bool, default:False) – Smooth along featuresboundary_smoothing (
bool, default:True) – Smooth boundary edgesedge_angle (
float, default:15.0) – Angle for edge detectionfeature_angle (
float, default:45.0) – Angle for feature detection
- Return type:
- Returns:
New PhenoMesh with smoothed surface
- smooth_taubin(iterations=100, pass_band=0.1, edge_angle=15.0, feature_angle=45.0, feature_smoothing=False, boundary_smoothing=True, non_manifold_smoothing=True, normalize_coordinates=True)[source]¶
Smooth the mesh using Taubin smoothing (low-pass filter).
Taubin smoothing is less prone to shrinkage than Laplacian smoothing.
- Parameters:
iterations (
int, default:100) – Number of smoothing iterationspass_band (
float, default:0.1) – Pass band for the filter (0-2)edge_angle (
float, default:15.0) – Angle for edge detectionfeature_angle (
float, default:45.0) – Angle for feature detectionfeature_smoothing (
bool, default:False) – Smooth along featuresboundary_smoothing (
bool, default:True) – Smooth boundary edgesnon_manifold_smoothing (
bool, default:True) – Smooth non-manifold edgesnormalize_coordinates (
bool, default:True) – Normalize coordinates before smoothing
- Return type:
- Returns:
New PhenoMesh with smoothed surface
- smooth_boundary(iterations=20, sigma=0.1)[source]¶
Smooth the boundary of the mesh using Laplacian smoothing.
- decimate(target_reduction=0.5, volume_preservation=True)[source]¶
Reduce mesh complexity by decimating faces.
- subdivide(n_subdivisions=1, subfilter='linear')[source]¶
Subdivide mesh faces to increase resolution.
- remesh_decimate(iterations, upsample_factor=2, downsample_factor=0.5, verbose=True)[source]¶
Iterative remeshing/decimation.
- triangulate()[source]¶
Convert all faces to triangles.
- Return type:
- Returns:
New PhenoMesh with triangulated faces
- clean(point_merging=True, tolerance=None, lines_to_points=True, polys_to_lines=True, strips_to_polys=True, absolute=True)[source]¶
Clean mesh by removing degenerate cells and merging duplicate points.
- Parameters:
point_merging (
bool, default:True) – Merge coincident pointstolerance (
float|None, default:None) – Tolerance for point merginglines_to_points (
bool, default:True) – Convert degenerate lines to pointspolys_to_lines (
bool, default:True) – Convert degenerate polygons to linesstrips_to_polys (
bool, default:True) – Convert strips to polygonsabsolute (
bool, default:True) – Use absolute tolerance
- Return type:
- Returns:
Cleaned PhenoMesh
- extract_largest()[source]¶
Extract the largest connected component.
- Return type:
- Returns:
New PhenoMesh containing only the largest connected region
- repair_small_holes(max_hole_edges=100, refine=True)[source]¶
Repair small holes in the mesh based on the number of edges.
- extract_clean_fill_triangulate(hole_edges=300)[source]¶
Perform ExtractLargest, Clean, FillHoles, and TriFilter operations.
- ecft(hole_edges=300)¶
Perform ExtractLargest, Clean, FillHoles, and TriFilter operations.
- compute_normals(cell_normals=True, point_normals=True, flip_normals=False, consistent_normals=True, auto_orient_normals=False, non_manifold_traversal=True, feature_angle=30.0)[source]¶
Compute surface normals.
- Parameters:
cell_normals (
bool, default:True) – Compute cell normalspoint_normals (
bool, default:True) – Compute point normalsflip_normals (
bool, default:False) – Flip all normalsconsistent_normals (
bool, default:True) – Make normals consistentauto_orient_normals (
bool, default:False) – Orient normals outwardnon_manifold_traversal (
bool, default:True) – Allow traversal across non-manifold edgesfeature_angle (
float, default:30.0) – Feature angle for splitting
- Return type:
- Returns:
New PhenoMesh with computed normals
- flip_normals()[source]¶
Flip all surface normals.
- Return type:
- Returns:
New PhenoMesh with flipped normals
- correct_normal_orientation(relative='x', inplace=False)[source]¶
Correct the orientation of the normals.
- filter_by_curvature(curvature_threshold, curvatures=None)[source]¶
Remove mesh vertices outside curvature threshold range.
- Parameters:
- Return type:
- Returns:
Filtered PhenoMesh
- remove_points(mask, keep_scalars=True)[source]¶
Remove points from the mesh.
- Parameters:
- Return type:
- Returns:
Tuple of (new PhenoMesh with points removed, indices of removed points)
- remove_by_normals(threshold_angle=0, flip=False, angle_type='polar')[source]¶
Remove points based on the point normal angle.
- remove_bridges(verbose=True)[source]¶
Remove triangles where all vertices are part of the mesh boundary.
- remove_tongues(radius, threshold=6, hole_edges=100, verbose=True)[source]¶
Remove “tongues” in mesh.
- Parameters:
- Return type:
- Returns:
PhenoMesh with tongues removed
- remove_inland_under(contour, threshold, resolution=None, invert=False)[source]¶
Remove the part of the mesh that is under the contour.
- Parameters:
- Return type:
- Returns:
PhenoMesh with the inland part removed
- clip(normal='x', origin=None, invert=True)[source]¶
Clip mesh with a plane.
- Parameters:
- Return type:
- Returns:
Clipped PhenoMesh
- drop_skirt(max_distance, flip=False)[source]¶
Downprojects the boundary to the lowest point in the z-direction.
- compute_geodesic_distance(start_vertex, end_vertex)[source]¶
Compute geodesic distance between two vertices.
- get_boundary_edges()[source]¶
Get boundary edges.
- Return type:
- Returns:
PhenoMesh containing boundary edges
- get_non_manifold_edges()[source]¶
Get non-manifold edges.
- Return type:
- Returns:
PhenoMesh containing non-manifold edges
- get_manifold_edges()[source]¶
Get manifold edges.
- Return type:
- Returns:
PhenoMesh containing manifold edges
- get_vertex_neighbors(index, include_self=True)[source]¶
Get the indices of the vertices connected to a given vertex.
- label_from_image(segmented_image, resolution=None, background=0, mode='point')[source]¶
Label mesh vertices or faces using nearest voxel in segmented image.
- Parameters:
segmented_image (
ndarray[tuple[Any,...],dtype[integer[Any]]]) – 3D segmented image with integer labelsresolution (
list[float] |None, default:None) – Spatial resolution of segmented imagebackground (
int, default:0) – Background label value to ignoremode (
str, default:'point') – Labeling mode (‘point’ for vertices, ‘face’ for cell centers)
- Return type:
- Returns:
Label array
- project_to_surface(intensity_image, distance_threshold, mask=None, resolution=None, aggregation_function=<function sum>, background=0)[source]¶
Project image intensity values onto mesh surface.
- Parameters:
intensity_image (
ndarray[tuple[Any,...],dtype[Any]]) – 3D intensity image arraydistance_threshold (
float) – Maximum distance from surface for projectionmask (
ndarray[tuple[Any,...],dtype[bool]] |None, default:None) – Optional mask array for imageresolution (
list[float] |None, default:None) – Spatial resolution of intensity imageaggregation_function (
Callable[...,Any], default:<function sum at 0x7fdc25a994b0>) – Aggregation functionbackground (
float, default:0) – Background value to ignore in image
- Return type:
- Returns:
Array of projected values, one per mesh vertex
- define_meristem(method='central_mass', return_coordinates=False)[source]¶
Determine which domain corresponds to the meristem.
- fit_paraboloid(return_coord=False)[source]¶
Fit a paraboloid to the mesh.
- Parameters:
return_coord (
bool, default:False) – If True, return apex coordinates as well- Return type:
ndarray[tuple[Any,...],dtype[floating[Any]]] |tuple[ndarray[tuple[Any,...],dtype[floating[Any]]],ndarray[tuple[Any,...],dtype[floating[Any]]]]- Returns:
Parameters for the paraboloid, and optionally the apex coordinates
- process(hole_repair_threshold=100, downscaling=0.01, upscaling=2, threshold_angle=60, top_cut='center', tongues_radius=None, tongues_ratio=4, smooth_iterations=200, smooth_relaxation=0.01, curvature_threshold=0.4, inland_threshold=None, contour=None)[source]¶
Convenience function for postprocessing the mesh.
- Parameters:
hole_repair_threshold (
int, default:100) – Threshold for the hole repair algorithmdownscaling (
float, default:0.01) – Downscaling factor for the meshupscaling (
float, default:2) – Upscaling factor for the meshthreshold_angle (
float, default:60) – Threshold for the polar angletop_cut (
str|tuple[float,float,float], default:'center') – Top cut locationtongues_radius (
float|None, default:None) – Radius of the tonguestongues_ratio (
float, default:4) – Ratio of the tonguessmooth_iterations (
int, default:200) – Number of smoothing iterationssmooth_relaxation (
float, default:0.01) – Smoothing relaxation factorcurvature_threshold (
float, default:0.4) – Threshold for the curvatureinland_threshold (
float|None, default:None) – Threshold for the inland removalcontour (
ndarray[tuple[Any,...],dtype[bool]] |None, default:None) – Contour to use for the inland removal
- Return type:
- Returns:
Processed PhenoMesh
- smoothen(iterations=100, relaxation_factor=0.01, feature_smoothing=False, boundary_smoothing=True, edge_angle=15.0, feature_angle=45.0)¶
Smooth the mesh using Laplacian smoothing.
- Parameters:
iterations (
int, default:100) – Number of smoothing iterationsrelaxation_factor (
float, default:0.01) – Relaxation factor for smoothing (0-1)feature_smoothing (
bool, default:False) – Smooth along featuresboundary_smoothing (
bool, default:True) – Smooth boundary edgesedge_angle (
float, default:15.0) – Angle for edge detectionfeature_angle (
float, default:45.0) – Angle for feature detection
- Return type:
- Returns:
New PhenoMesh with smoothed surface
- filter_curvature(curvature_threshold, curvatures=None)¶
Remove mesh vertices outside curvature threshold range.
- Parameters:
- Return type:
- Returns:
Filtered PhenoMesh
- remove_normals(threshold_angle=0, flip=False, angle_type='polar')¶
Remove points based on the point normal angle.
- repair_small(max_hole_edges=100, refine=True)¶
Repair small holes in the mesh based on the number of edges.
- label_mesh(segmented_image, resolution=None, background=0, mode='point')¶
Label mesh vertices or faces using nearest voxel in segmented image.
- Parameters:
segmented_image (
ndarray[tuple[Any,...],dtype[integer[Any]]]) – 3D segmented image with integer labelsresolution (
list[float] |None, default:None) – Spatial resolution of segmented imagebackground (
int, default:0) – Background label value to ignoremode (
str, default:'point') – Labeling mode (‘point’ for vertices, ‘face’ for cell centers)
- Return type:
- Returns:
Label array
- project2surface(intensity_image, distance_threshold, mask=None, resolution=None, aggregation_function=<function sum>, background=0)¶
Project image intensity values onto mesh surface.
- Parameters:
intensity_image (
ndarray[tuple[Any,...],dtype[Any]]) – 3D intensity image arraydistance_threshold (
float) – Maximum distance from surface for projectionmask (
ndarray[tuple[Any,...],dtype[bool]] |None, default:None) – Optional mask array for imageresolution (
list[float] |None, default:None) – Spatial resolution of intensity imageaggregation_function (
Callable[...,Any], default:<function sum at 0x7fdc25a994b0>) – Aggregation functionbackground (
float, default:0) – Background value to ignore in image
- Return type:
- Returns:
Array of projected values, one per mesh vertex
- process_mesh(hole_repair_threshold=100, downscaling=0.01, upscaling=2, threshold_angle=60, top_cut='center', tongues_radius=None, tongues_ratio=4, smooth_iterations=200, smooth_relaxation=0.01, curvature_threshold=0.4, inland_threshold=None, contour=None)¶
Convenience function for postprocessing the mesh.
- Parameters:
hole_repair_threshold (
int, default:100) – Threshold for the hole repair algorithmdownscaling (
float, default:0.01) – Downscaling factor for the meshupscaling (
float, default:2) – Upscaling factor for the meshthreshold_angle (
float, default:60) – Threshold for the polar angletop_cut (
str|tuple[float,float,float], default:'center') – Top cut locationtongues_radius (
float|None, default:None) – Radius of the tonguestongues_ratio (
float, default:4) – Ratio of the tonguessmooth_iterations (
int, default:200) – Number of smoothing iterationssmooth_relaxation (
float, default:0.01) – Smoothing relaxation factorcurvature_threshold (
float, default:0.4) – Threshold for the curvatureinland_threshold (
float|None, default:None) – Threshold for the inland removalcontour (
ndarray[tuple[Any,...],dtype[bool]] |None, default:None) – Contour to use for the inland removal
- Return type:
- Returns:
Processed PhenoMesh
- boundary_points()¶
Get vertex indices of points in the boundary.
- boundary_edges()¶
Get boundary edges.
- Return type:
- Returns:
PhenoMesh containing boundary edges
- non_manifold_edges()¶
Get non-manifold edges.
- Return type:
- Returns:
PhenoMesh containing non-manifold edges
- manifold_edges()¶
Get manifold edges.
- Return type:
- Returns:
PhenoMesh containing manifold edges
- feature_edges(angle=30)¶
Get feature edges defined by given angle.
- vertex_neighbors(index, include_self=True)¶
Get the indices of the vertices connected to a given vertex.
- vertex_neighbors_all(include_self=True)¶
Get all vertex neighbors.
- vertex_cycles()¶
Find cycles (holes/boundaries) in the mesh.
Usage Examples¶
Creating a PhenoMesh¶
from phenotastic import PhenoMesh
import pyvista as pv
# From a PyVista mesh
mesh = PhenoMesh(pv.Sphere())
# From a file
mesh = PhenoMesh(pv.read("mesh.vtk"))
# With custom attributes
mesh = PhenoMesh(
pv.Sphere(),
contour=binary_contour_array,
resolution=[1.0, 1.0, 1.0]
)
Mesh Processing¶
# Smoothing
mesh = mesh.smooth(iterations=100)
mesh = mesh.smooth_taubin(iterations=50, pass_band=0.1)
# Remeshing
mesh = mesh.remesh(n_clusters=10000)
# Repair
mesh = mesh.repair()
mesh = mesh.repair_small(nbe=100)
Curvature Analysis¶
# Compute curvature
curvature = mesh.compute_curvature(curvature_type="mean")
# Filter by curvature
filtered = mesh.filter_curvature(curvature_threshold=(-0.5, 0.5))
Interoperability¶
# PhenoMesh is a PolyData
isinstance(mesh, pv.PolyData) # True
# Use anywhere PolyData is expected
plotter = pv.Plotter()
plotter.add_mesh(mesh)
# Convert to plain PolyData
polydata = mesh.to_polydata()
# Create from PolyData
mesh = PhenoMesh.from_polydata(polydata)