mesh_tools package
Submodules
mesh_tools.compartment_utils module
- mesh_tools.compartment_utils.filter_face_coloring_to_connected_components(curr_limb_mesh, face_coloring, connectivity='vertices', must_keep_labels={})[source]
Purpose: To eliminate all but the largest connected component of a label on the mesh face coloring
Reason for need: when cancelling out conflict pieces it can split up a mesh into disconnected components and we want only one component so that it can later be expanded during the waterfilling process
- mesh_tools.compartment_utils.get_skeletal_distance(main_mesh, edges, buffer=0.01, bbox_ratio=1.2, distance_threshold=3000, distance_by_mesh_center=True, print_flag=False, edge_loop_print=False, stitch_patches=0, connectivity='edges', fast_mesh_split=True, significant_sub_components=20)[source]
Purpose: To return the histogram of distances along a mesh subtraction process so that we could evenutally find an adaptive distance threshold
- mesh_tools.compartment_utils.get_skeletal_distance_no_skipping(main_mesh, edges, buffer=0.01, bbox_ratio=1.2, distance_threshold=3000, distance_by_mesh_center=False, print_flag=False, edge_loop_print=False, stitch_patches=0, significant_sub_components=20, connectivity='edges', fast_mesh_split=True)[source]
Purpose: To return the histogram of distances along a mesh subtraction process so that we could evenutally find an adaptive distance threshold
- mesh_tools.compartment_utils.groups_of_labels_to_resolved_labels(current_mesh, face_correspondence_lists)[source]
Purpose: To take a list of face correspondences of different parts and turn them into an array mapping every face (on the mesh) to a label
- mesh_tools.compartment_utils.mesh_correspondence_adaptive_distance(curr_branch_skeleton, curr_branch_mesh, skeleton_segment_width=1000, distance_by_mesh_center=True, print_flag=False, return_mesh_perc_drop=False, stitch_patches=0, buffer=100, bbox_ratio=1.2, distance_threshold=3000, return_closest_face_on_empty=False, connectivity='vertices')[source]
- mesh_tools.compartment_utils.resolve_empty_conflicting_face_labels(curr_limb_mesh, face_lookup, no_missing_labels=[], max_submesh_threshold=50000, max_color_filling_iterations=4, debug=False, must_keep_labels={}, connectivity='vertices', branch_skeletons=None)[source]
Input: - full mesh - current face coloring of mesh (could be incomplete) corresponding to skeletal pieces (but doesn’t need the skeletal pieces to do it’s jobs, those are just represented in the labels)
Output: - better face coloring which has labels that:
cover entire mesh
the labels exist as only 1 connected component on the mesh
Pseudocode of what doing: - clearing out the branch_mesh correspondence stored in limb_correspondence[limb_idx][k][“branch_mesh”] - gets a list of how many subdivided branches there were (becuase this should be the number of labels) and the mesh of whole limb - Builds a face to skeleeton branch correspondence bassed on the current branch_piece[“correspondence_face_idx”] that already exists
This may have overlaps or faces mapped to zero branches that we need to resolve
computes the percentage of empty and conflicting faces
makes sure that at least one face that corresponds to each branch piece (and throws error if so)
#Doing the resolution of the empty and conflicting faces: - clears out all conflicting faces and leaves them just like the empty ones - uses the filter_face_coloring_to_connected_components which only keeps the largest connected component of a label
(because the zeroing out of conflicting labels could have eliminated or split up some of the labels)
if a face was totally eliminated then add it back to the face coloring
(only does this once so a face could still be missing if one face totally overwrites another face)
**At this point: there is at one-to-one correspondence of mesh face to skeletal piece label OR empty label (-1)
# Using the waterfilling algorithm: designed at fixing the correspondence to empty label (-1) - get a submesh of the original mesh but only for those empty faces and divide into disconnecteed mesh pieces - run through waterfilling algorithm to color each empty piece - check that there are no more empty faces - gets the one connected mesh component that corresponds to that label (get both the actual mesh and the mesh indexes)
#the output of all of the algorithm: - save the result back in limb_correspondence[limb_idx][k][“branch_mesh”] so it is accurately updated
- mesh_tools.compartment_utils.waterfill_labeling(total_mesh_correspondence, submesh_indices, total_mesh=None, total_mesh_graph=None, propagation_type='random', max_iterations=1000, max_submesh_threshold=1000, connectivity='vertices')[source]
Pseudocode: 1) check if the submesh you are propagating labels to is too large 2) for each unmarked face get the neighbors of all of the faces, and for all these neighbors get all the labels 3) if the neighbors label is not empty. depending on the type of progation type then pick the winning label a. random: just randomly choose from list b. …. not yet implemented 4) revise the faces that are still empty and repeat process until all faces are empty (have a max iterations number)
mesh_tools.correspondence_utils module
- mesh_tools.correspondence_utils.correspondence_1_to_1(mesh, local_correspondence, curr_limb_endpoints_must_keep=None, must_keep_labels={}, plot=False)[source]
Will Fix the 1-to-1 Correspondence of the mesh correspondence for the limbs and make sure that the endpoints that are designated as touching the soma then make sure the mesh correspondnece reaches the soma limb border
has an optional argument must_keep_labels that will allow you to specify some labels that are a must keep
- mesh_tools.correspondence_utils.mesh_correspondence_first_pass(mesh, skeleton=None, skeleton_branches=None, distance_by_mesh_center=True, remove_inside_pieces_threshold=0, skeleton_segment_width=1000, initial_distance_threshold=3000, skeletal_buffer=100, backup_distance_threshold=6000, backup_skeletal_buffer=300, connectivity='edges', plot=False)[source]
Will come up with the mesh correspondences for all of the skeleton branches: where there can be overlaps and empty faces
mesh_tools.meshlab module
- class mesh_tools.meshlab.Decimator(decimation_ratio, temp_folder, overwrite=False, **kwargs)[source]
Bases:
Meshlab- Use like so:
```python # Preprocessing temp_folder = ‘decimation_temp’ mls_func = Decimator(0.05, temp_folder, overwrite=True)
# Processing decimated_mesh = mls_func(mesh.vertices, mesh.faces, mesh.segment_id)
# Alternative way so don’t have to read in and out files
temp_folder = “new_temp” my_Dec = Decimator(0.35,temp_folder,overwrite=True) #how to run an actual decimation input_mesh_path = “./107738877133006848/107738877133006848_soma_3.off”
- new_mesh_decimated = my_Dec(input_mesh_path=input_mesh_path,
delete_temp_files=False, return_mesh=False)
- class mesh_tools.meshlab.FillHoles(temp_folder='./temp', max_hole_size=2000, self_itersect_faces=True, overwrite=False, **kwargs)[source]
Bases:
FilterBaseClass that will apply hole filling filters to meshes
- class mesh_tools.meshlab.FilterBase(temp_folder, script_filters, name='filter', overwrite=False, **kwargs)[source]
Bases:
MeshlabBase Class that can be extended to implement different meshlabserver filters
- Use like so:
```python # Preprocessing temp_folder = ‘decimation_temp’ mls_func = Decimator(0.05, temp_folder, overwrite=True)
# Processing decimated_mesh = mls_func(mesh.vertices, mesh.faces, mesh.segment_id)
#Alternative way where don’t have to read in and out files temp_folder = “poisson_temp” my_Poisson = Poisson(temp_folder,overwrite=True) #how to run an actual decimation input_mesh_path = “./107738877133006848/107738877133006848_soma_3.off” new_mesh_decimated = my_Poisson(input_mesh_path=input_mesh_path,
delete_temp_files=False, return_mesh=True)
- class mesh_tools.meshlab.Interior(temp_folder='./temp', overwrite=False, return_interior=True, quality_max=0.1, **kwargs)[source]
Bases:
FilterBaseClass that will apply hole filling filters to meshes
- class mesh_tools.meshlab.Poisson(temp_folder='./temp', overwrite=False, **kwargs)[source]
Bases:
Meshlab- Use like so:
```python # Preprocessing temp_folder = ‘decimation_temp’ mls_func = Decimator(0.05, temp_folder, overwrite=True)
# Processing decimated_mesh = mls_func(mesh.vertices, mesh.faces, mesh.segment_id)
#Alternative way where don’t have to read in and out files temp_folder = “poisson_temp” my_Poisson = Poisson(temp_folder,overwrite=True) #how to run an actual decimation input_mesh_path = “./107738877133006848/107738877133006848_soma_3.off” new_mesh_decimated = my_Poisson(input_mesh_path=input_mesh_path,
delete_temp_files=False, return_mesh=True)
mesh_tools.meshparty_skeletonize module
- mesh_tools.meshparty_skeletonize.branches_from_mesh(mesh, root=None, filter_mesh=False, invalidation_d=12000, smooth_neighborhood=1, verbose=True, meshparty_segment_size=100, combine_close_skeleton_nodes_threshold=700, filter_end_nodes=False, filter_end_node_length=4000)[source]
Purpose: To create a skeleton and the mesh correspondence from a mesh and the starting root
- mesh_tools.meshparty_skeletonize.calculate_skeleton_paths_on_mesh(mesh, soma_pt=None, soma_thresh=7500, invalidation_d=10000, smooth_neighborhood=5, large_skel_path_threshold=5000, cc_vertex_thresh=50, return_map=False)[source]
function to turn a trimesh object of a neuron into a skeleton, without running soma collapse, or recasting result into a Skeleton. Used by
meshparty.skeletonize.skeletonize_mesh()and makes use ofmeshparty.skeletonize.skeletonize_components()- Parameters:
mesh (meshparty.trimesh_io.Mesh) – the mesh to skeletonize, defaults assume vertices in nm
soma_pt (np.array) – a length 3 array specifying to soma location to make the root default=None, in which case a heuristic root will be chosen in units of mesh vertices
soma_thresh (float) – distance in mesh vertex units over which to consider mesh vertices close to soma_pt to belong to soma these vertices will automatically be invalidated and no skeleton branches will attempt to reach them. This distance will also be used to collapse all skeleton points within this distance to the soma_pt root if collpase_soma is true. (default=7500 (nm))
invalidation_d (float) – the distance along the mesh to invalidate when applying TEASAR like algorithm. Controls how detailed a structure the skeleton algorithm reaches. default (10000 (nm))
smooth_neighborhood (int) – the neighborhood in edge hopes over which to smooth skeleton locations. This controls the smoothing of the skeleton (default 5)
large_skel_path_threshold (int) – the threshold in terms of skeleton vertices that skeletons will be nominated for tip merging. Smaller skeleton fragments will not be merged at their tips (default 5000)
cc_vertex_thresh (int) – the threshold in terms of vertex numbers that connected components of the mesh will be considered for skeletonization. mesh connected components with fewer than these number of vertices will be ignored by skeletonization algorithm. (default 100)
return_map (bool) – whether to return a map of how each mesh vertex maps onto each skeleton vertex based upon how it was invalidated.
- Returns:
skel_verts (np.array) – a Nx3 matrix of skeleton vertex positions
skel_edges (np.array) – a Kx2 matrix of skeleton edge indices into skel_verts
smooth_verts (np.array) – a Nx3 matrix of vertex positions after smoothing
skel_verts_orig (np.array) – a N long index of skeleton vertices in the original mesh vertex index
(mesh_to_skeleton_map) (np.array) – a Mx2 map of mesh vertex indices to skeleton vertex indices
- mesh_tools.meshparty_skeletonize.collapse_soma_skeleton(soma_pt, verts, edges, soma_d_thresh=12000, mesh_to_skeleton_map=None, soma_mesh_indices=None, return_filter=False, only_soma_component=True, return_soma_ind=False)[source]
function to adjust skeleton result to move root to soma_pt
- Parameters:
soma_pt (numpy.array) – a 3 long vector of xyz locations of the soma (None to just remove duplicate )
verts (numpy.array) – a Nx3 array of xyz vertex locations
edges (numpy.array) – a Kx2 array of edges of the skeleton
soma_d_thresh (float) – distance from soma_pt to collapse skeleton nodes
mesh_to_skeleton_map (np.array) – a M long array of how each mesh index maps to a skeleton vertex (default None). The function will update this as it collapses vertices to root.
soma_mesh_indices (np.array) – a K long array of indices in the mesh that should be considered soma Any skeleton vertex on these vertices will all be collapsed to root.
return_filter (bool) – whether to return a list of which skeleton vertices were used in the end for the reduced set of skeleton vertices
only_soma_component (bool) – whether to collapse only the skeleton connected component which is closest to the soma_pt (default True)
return_soma_ind (bool) – whether to return which skeleton index that is the soma_pt
- Returns:
np.array – verts, Px3 array of xyz skeleton vertices
np.array – edges, Qx2 array of skeleton edges
(np.array) – new_mesh_to_skeleton_map, returned if mesh_to_skeleton_map and soma_pt passed
(np.array) – used_vertices, if return_filter this contains the indices into the passed verts which the return verts is using
int – an index into the returned verts that is the root of the skeleton node, only returned if return_soma_ind is True
- mesh_tools.meshparty_skeletonize.compute_segments(sk)[source]
Precompute segments between branches and end points
- mesh_tools.meshparty_skeletonize.mesh_teasar(mesh, root=None, valid=None, root_ds=None, root_pred=None, soma_pt=None, soma_thresh=7500, invalidation_d=10000, return_timing=False, return_map=False)[source]
core skeletonization function used to skeletonize a single component of a mesh
- mesh_tools.meshparty_skeletonize.ray_trace_distance(vertex_inds, mesh, max_iter=10, rand_jitter=0.001, verbose=False, ray_inter=None)[source]
Compute distance to opposite side of the mesh for specified vertex indices on the mesh.
- Parameters:
vertex_inds (np.array) – a K long set of indices into the mesh.vertices that you want to perform ray tracing on
mesh (
meshparty.trimesh_io.Mesh) – mesh to perform ray tracing onmax_iter (int) – maximum retries to attempt in order to get a proper sdf measure (default 10)
rand_jitter (float) – the amplitude of gaussian jitter on the vertex normal to add on each iteration (default .001)
verbose (bool) – whether to print debug statements (default False)
ray_inter (ray_pyembree.RayMeshIntersector) – a ray intercept object pre-initialized with a mesh, in case y ou are doing this many times and want to avoid paying initialization costs. (default None) will initialize it for you
- Returns:
rs, a K long array of sdf values. rays with no result after max_iters will contain zeros.
- Return type:
np.array
- mesh_tools.meshparty_skeletonize.reduce_verts(verts, faces)[source]
removes unused vertices from a graph or mesh
- Parameters:
verts (numpy.array) – NxD numpy array of vertex locations
faces (numpy.array) – MxK numpy array of connected shapes (i.e. edges or tris) (entries are indices into verts)
- Returns:
np.array – new_verts, a filtered set of vertices
np.array – new_face, a reindexed set of faces (or edges)
np.array – used_verts, the index of the new_verts in the old verts
- mesh_tools.meshparty_skeletonize.setup_root(mesh, is_soma_pt=None, soma_d=None, is_valid=None)[source]
function to find the root index to use for this mesh
- mesh_tools.meshparty_skeletonize.skeleton_obj_to_branches(sk_meshparty_obj, mesh, meshparty_n_surface_downsampling=0, meshparty_segment_size=100, verbose=False, filter_end_nodes=False, filter_end_node_length=4500, combine_close_skeleton_nodes_threshold=700, return_skeleton_only=False, **kwargs)[source]
- mesh_tools.meshparty_skeletonize.skeletonize(mesh, root=None, filter_mesh=False, invalidation_d=12000, smooth_neighborhood=1, verbose=True, meshparty_segment_size=100, combine_close_skeleton_nodes_threshold=700, filter_end_nodes=False, filter_end_node_length=4000, plot_skeleton=False)[source]
Purpose: Will return a meshParty skeleton with all of the filtering
- mesh_tools.meshparty_skeletonize.skeletonize_components(mesh, soma_pt=None, soma_thresh=10000, invalidation_d=10000, cc_vertex_thresh=100, return_map=False)[source]
core skeletonization routine, used by
meshparty.skeletonize.calculate_skeleton_paths_on_mesh()to calculcate skeleton on all components of mesh, with no post processing
- mesh_tools.meshparty_skeletonize.skeletonize_mesh(mesh, soma_pt=None, soma_radius=7500, collapse_soma=True, invalidation_d=12000, smooth_vertices=False, smooth_neighborhood=5, compute_radius=True, compute_original_index=True, cc_vertex_thresh=100, verbose=True)[source]
Build skeleton object from mesh skeletonization
- Parameters:
mesh (meshparty.trimesh_io.Mesh) – the mesh to skeletonize, defaults assume vertices in nm
soma_pt (np.array) – a length 3 array specifying to soma location to make the root default=None, in which case a heuristic root will be chosen in units of mesh vertices.
soma_radius (float) – distance in mesh vertex units over which to consider mesh vertices close to soma_pt to belong to soma these vertices will automatically be invalidated and no skeleton branches will attempt to reach them. This distance will also be used to collapse all skeleton points within this distance to the soma_pt root if collpase_soma is true. (default=7500 (nm))
collapse_soma (bool) – whether to collapse the skeleton around the soma point (default True)
invalidation_d (float) – the distance along the mesh to invalidate when applying TEASAR like algorithm. Controls how detailed a structure the skeleton algorithm reaches. default (12000 (nm))
smooth_vertices (bool) – whether to smooth the vertices of the skeleton
compute_radius (bool) – whether to calculate the radius of the skeleton at each point on the skeleton (default True)
compute_original_index (bool) – whether to calculate how each of the mesh nodes maps onto the skeleton (default True)
cc_vertex_thresh (int, optional) – Smallest number of vertices in a connected component to skeletonize.
verbose (bool) – whether to print verbose logging
- Returns:
a Skeleton object for this mesh
- Return type:
meshparty.skeleton.Skeleton
- mesh_tools.meshparty_skeletonize.skeletonize_mesh_largest_component(mesh, root=None, verbose=False, filter_mesh=True, invalidation_d=12000, smooth_neighborhood=1, **kwargs)[source]
To run the skeletonization on the largest connected component of one mesh
Example 1: #How to get the skeleton from the skeleton object sk_meshparty = sk_meshparty_obj.vertices[sk_meshparty_obj.edges]
- mesh_tools.meshparty_skeletonize.smooth_graph(values, edges, mask=None, neighborhood=2, iterations=100, r=0.1)[source]
smooths a spatial graph via iterative local averaging calculates the average value of neighboring values and relaxes the values toward that average
- Parameters:
values (numpy.array) – a NxK numpy array of values, for example xyz positions
edges (numpy.array) – a Mx2 numpy array of indices into values that are edges
mask (numpy.array) – NOT yet implemented optional N boolean vector of values to mask the vert locations. the result will return a result at every vert but the values that are false in this mask will be ignored and not factored into the smoothing.
neighborhood (int) – an integer of how far in the graph to relax over as being local to any vertex (default = 2)
iterations (int) – number of relaxation iterations (default = 100)
r (float) – relaxation factor at each iteration new_vertex = (1-r)*old_vertex*mask + (r+(1-r)*(1-mask))*(local_avg) default = .1
- Returns:
new_verts, a Nx3 list of new smoothed vertex positions
- Return type:
np.array
mesh_tools.skeleton_utils module
These functions will help with skeletonization
- mesh_tools.skeleton_utils.accuracy_stats_from_true_predicted_skeleton(true_skeleton, predicted_skeleton, search_radius=5000, include_skeletons=True, plot_subskeleton=False, plot_skeleton_categories=False, mesh=None, verbose=False)[source]
Purpose: To compute all TP/FP… skeletons and skeletal lengths and all other stats: precision, recall between 2 skeletons
What to do with an exclude skeleton? It should not be included in the false negative
- mesh_tools.skeleton_utils.add_and_smooth_segment_to_branch(skeleton, skeleton_stitch_point=None, new_stitch_point=None, new_seg=None, resize_mult=0.2, n_resized_cutoff=3, smooth_branch_at_end=True, n_resized_cutoff_to_smooth=None, smooth_width=100, max_stitch_distance_for_smoothing=300, verbose=False, **kwargs)[source]
Purpose: To add on a new skeletal segment to a branch that will prevent the characteristic hooking when stitching a new point
Pseudocode: 1) Get the distance of the stitch point = A 2) Resize the skeleton to B*A (where B < 1) 3) Find all nodes that are CA away from the MP stitch point 4) Delete those nodes (except the last one and set that as the new stitch point) 5) Make the new stitch
Ex: When using a new segment orig_sk_func_smoothed = add_and_smooth_segment_to_branch(orig_sk,
new_seg = np.array([stitch_point_MAP,stitch_point_MP]).reshape(-1,2,3))
- mesh_tools.skeleton_utils.align_skeletons_at_connectivity(sk_1, sk_2)[source]
To align 2 skeletons where both starts with the endpoint that they share
Ex:
- mesh_tools.skeleton_utils.angle_between_skeleton_vectors_and_ref_vector(reference_vector, skeleton_vectors=None, skeleton=None, start_endpoint_coordinate=None, segment_width=0, n_segments=0, plot_skeleton=False)[source]
Purpose: To calculate the angles between a reference vector and certain skeleton vectors created by sections of a skeleton that has been ordered and reshaped
- mesh_tools.skeleton_utils.bounding_box_corners(skeleton)[source]
Purpose: To find the minimum and maximum coordinates of the skeleton bounding box
- mesh_tools.skeleton_utils.calcification(location_with_filename, max_triangle_angle=1.91986, quality_speed_tradeoff=0.2, medially_centered_speed_tradeoff=0.2, area_variation_factor=0.0001, max_iterations=500, is_medially_centered=True, min_edge_length=75, edge_length_multiplier=0.002, print_parameters=True)[source]
- mesh_tools.skeleton_utils.calculate_skeletal_distance(my_skeleton)
- mesh_tools.skeleton_utils.calculate_skeletal_length(my_skeleton)
- mesh_tools.skeleton_utils.change_basis_matrix(v)[source]
This just gives change of basis matrix for a basis that has the vector v as its 3rd basis vector and the other 2 vectors orthogonal to v (but not necessarily orthogonal to each other) * not make an orthonormal basis *
– changed so now pass the non-orthogonal components to the QR decomposition to get them as orthogonal
- mesh_tools.skeleton_utils.check_correspondence_branches_have_2_endpoints(correspondence, verbose=True, raise_error=True)[source]
Purpose: check that all branches have 2 endpoints
- mesh_tools.skeleton_utils.clean_skeleton(G, distance_func=None, min_distance_to_junction=3, return_skeleton=True, endpoints_must_keep=None, print_flag=False, return_removed_skeletons=False, error_if_endpoints_must_keep_not_endnode=True, verbose=False)[source]
Example of how to use:
Simple Example: def distance_measure_func(path,G=None): #print(“new thing”) return len(path)
new_G = clean_skeleton(G,distance_measure_func,return_skeleton=False) nx.draw(new_G,with_labels=True)
More complicated example:
from mesh_tools import skeleton_utils as sk from importlib import reload sk = reload(sk)
from pathlib import Path test_skeleton = Path(“./Dustin_vp6/Dustin_soma_0_branch_0_0_skeleton.cgal”) if not test_skeleton.exists():
print(str(test_skeleton)[:-14]) file_of_skeleton = sk.calcification(str(test_skeleton.absolute())[:-14])
- else:
file_of_skeleton = test_skeleton
# import the skeleton test_sk = sk.read_skeleton_edges_coordinates(test_skeleton) import trimesh test_mesh = trimesh.load_mesh(str(str(test_skeleton.absolute())[:-14] + “.off”)) sk.graph_skeleton_and_mesh(test_mesh.vertices,
test_mesh.faces, edge_coordinates=test_sk)
# clean the skeleton and then visualize import time clean_time = time.time() cleaned_skeleton = clean_skeleton(test_sk,
distance_func=skeletal_distance,
min_distance_to_junction=10000, return_skeleton=True, print_flag=True)
print(f”Total time for skeleton clean {time.time() - clean_time}”)
# see what skeleton looks like now test_mesh = trimesh.load_mesh(str(str(test_skeleton.absolute())[:-14] + “.off”)) sk.graph_skeleton_and_mesh(test_mesh.vertices,
test_mesh.faces, edge_coordinates=cleaned_skeleton)
————— end of example —————–
- mesh_tools.skeleton_utils.clean_skeleton_with_decompose(distance_cleaned_skeleton)[source]
Purpose: to eliminate the loops that are cleaned in the decompose process from the skeleton and then reconstruct Pseudocode: 1) decompose skeleton 2) recompose skeleton (was checked that no nodes to recombine)
- mesh_tools.skeleton_utils.clean_skeleton_with_soma_verts(G, distance_func, min_distance_to_junction=3, return_skeleton=True, soma_border_vertices=None, distance_to_ignore_end_nodes_close_to_soma_border=5000, skeleton_mesh=None, endpoints_must_keep=None, print_flag=False)[source]
Example of how to use:
Simple Example: def distance_measure_func(path,G=None): #print(“new thing”) return len(path)
new_G = clean_skeleton(G,distance_measure_func,return_skeleton=False) nx.draw(new_G,with_labels=True)
More complicated example:
from mesh_tools import skeleton_utils as sk from importlib import reload sk = reload(sk)
from pathlib import Path test_skeleton = Path(“./Dustin_vp6/Dustin_soma_0_branch_0_0_skeleton.cgal”) if not test_skeleton.exists():
print(str(test_skeleton)[:-14]) file_of_skeleton = sk.calcification(str(test_skeleton.absolute())[:-14])
- else:
file_of_skeleton = test_skeleton
# import the skeleton test_sk = sk.read_skeleton_edges_coordinates(test_skeleton) import trimesh test_mesh = trimesh.load_mesh(str(str(test_skeleton.absolute())[:-14] + “.off”)) sk.graph_skeleton_and_mesh(test_mesh.vertices,
test_mesh.faces, edge_coordinates=test_sk)
# clean the skeleton and then visualize import time clean_time = time.time() cleaned_skeleton = clean_skeleton(test_sk,
distance_func=skeletal_distance,
min_distance_to_junction=10000, return_skeleton=True, print_flag=True)
print(f”Total time for skeleton clean {time.time() - clean_time}”)
# see what skeleton looks like now test_mesh = trimesh.load_mesh(str(str(test_skeleton.absolute())[:-14] + “.off”)) sk.graph_skeleton_and_mesh(test_mesh.vertices,
test_mesh.faces, edge_coordinates=cleaned_skeleton)
————— end of example —————–
- mesh_tools.skeleton_utils.closest_distances_from_skeleton_vertices_to_base_skeleton(skeleton, base_skeleton, verbose=False, plot_min_pair=False)[source]
Purposes: To measure the distance from one skeleton’s vertices to the closest vertices on a base skeleton
Application: To help determine if a merge error has occured where the skeletons pass too close to each other
Ex: sk.closest_distances_from_skeleton_vertices_to_base_skeleton(new_sks[0],
new_sks[1], verbose= True, plot_min_pair=True)
- mesh_tools.skeleton_utils.closest_skeleton_coordinate(skeleton, coordinate)[source]
Purpose: will map a coordinate to the closest skeleton coordinate
Pseudocode: 1) Turn the skeleton into a KDTree of coordinates 2) Find the closest coordinate
- mesh_tools.skeleton_utils.closest_skeleton_coordinates(coordinates, skeleton=None, radius=None, verbose=False, plot=False, skeleton_coordinates=None, return_idx=False, return_distances=False)[source]
Purpose: will map a coordinate to the closest skeleton coordinate
Pseudocode: 1) Turn the skeleton into a KDTree of coordinates 2) Find the closest coordinate
Ex: sk.closest_skeleton_coordinates(
restr_skeleton, coordinates = soma_center, plot = True, verbose = True, radius = 50_000,
)
- mesh_tools.skeleton_utils.combine_close_branch_points(skeleton=None, combine_threshold=700, print_flag=False, skeleton_branches=None)[source]
Purpose: To take a skeleton or graph and return a skelton/graph where close branch points are combined
Example Code of how could get the orders: # How could potentially get the edge order we wanted endpoint_neighbors_to_order_map = dict() for k in endpoint_neighbors:
total_orders = [] total_orders_neighbors = [] for j in p:
- try:
total_orders.append(curr_sk_graph[j][k][“order”]) total_orders_neighbors.append(j)
- except:
pass
order_index = np.argmin(total_orders) endpoint_neighbors_to_order_map[(k,total_orders_neighbors[order_index])] = total_orders[order_index]
endpoint_neighbors_to_order_map
Ex: sk = reload(sk) from datasci_tools import numpy_utils as nu nu = reload(nu) branch_skeleton_data_cleaned = [] for i,curr_sk in enumerate(branch_skeleton_data):
print(f”
- —– Working on skeleton {i} ———“)
new_sk = sk.combine_close_branch_points(curr_sk,print_flag=True) print(f”Original Sk = {curr_sk.shape}, Cleaned Sk = {new_sk.shape}”) branch_skeleton_data_cleaned.append(new_sk)
- mesh_tools.skeleton_utils.compare_endpoints(endpioints_1, endpoints_2, **kwargs)[source]
comparing the endpoints of a graph:
Ex: from datasci_tools import networkx_utils as xu xu = reload(xu)mess end_1 = np.array([[2,3,4],[1,4,5]]) end_2 = np.array([[1,4,5],[2,3,4]])
xu.compare_endpoints(end_1,end_2)
- mesh_tools.skeleton_utils.compare_skeletons_ordered(skeleton_1, skeleton_2, edge_threshold=0.01, node_threshold=0.01, print_flag=False)[source]
Purpose: To compare skeletons where the edges are ordered (not comparing overall skeletons) Those would be isomorphic graphs (not yet developed)
Example of how to use: skeletons_idx_to_stack = [0,1,2,3] total_skeleton = sk.stack_skeletons([double_soma_obj.concept_network.nodes[“L1”][“data”].concept_network.nodes[k][“data”].skeleton for k in skeletons_idx_to_stack]) #sk.graph_skeleton_and_mesh(other_skeletons=[total_skeleton])
skeleton_1 = copy.copy(total_skeleton) skeleton_2 = copy.copy(total_skeleton) skeleton_1[0][0] = np.array([558916.8, 1122107. , 842972.8]) #change so there will be error
- sk.compare_skeletons_ordered(skeleton_1,
- skeleton_2,
- edge_threshold=0.01, #how much the edge distances can vary by
node_threshold = 0.01, #how much the nodes can vary by print_flag = True
)
- mesh_tools.skeleton_utils.convert_branch_graph_to_skeleton(skeleton_graph)[source]
Want an ordered skeleton that is only a line Pseudocode: 1) Get the ordered node coordinates 2) Create an edge array like [(0,1),(1,2)…. (n_nodes-1,n_nodes)] 3) index the edges intot he node coordinates and return
- mesh_tools.skeleton_utils.convert_skeleton_to_coordinates(skeleton)
- mesh_tools.skeleton_utils.convert_skeleton_to_graph(staring_edges=None, stitch_print=False, combine_node_dist=0.0001, node_matching_size_threshold=10000, vertices=None, edges=None)[source]
Purpose: To automatically convert a skeleton to a graph
7/9 adjustments: make so slight differences in coordinates not affect the graph
Pseudocode for how you could apply the closeness to skeletons 1) Get the unique rows 2) See if there are any rows that are the same (that gives you what to change them to) 3) put those into a graph and find the connected components 4) Pick the first node in the component to be the dominant one a. for each non-dominant node, replace every occurance of the non-dominant one with the dominant one in indices - add the non-dominant ones to a list to delete
** this will result in an indices that doesn’t have any of the repeat nodes, but the repeat nodes are still taking up the numbers that they were originally in order with ***
np.delete(x,[1,3],axis=0)) # to delete the rows
remap the indices and delete the unique rows that were not used
Do everything like normal
- mesh_tools.skeleton_utils.convert_skeleton_to_nodes_edges_optimized(skeleton, verbose=False)
- mesh_tools.skeleton_utils.convert_skeleton_to_nodes_edges_optimized_old(array)[source]
Purpose: will return the nodes and edges but without making the nodes unique (so will have some repeats)
- mesh_tools.skeleton_utils.coordinate_restriction_to_skeleton(skeleton, coordinates, distance_threshold=15000, keep_skeleton_within_threshold=False, plot=False, verbose=False, return_graph=False)[source]
Purpose: To keep or eliminate all skeletal points within a certain distance of a coordinate:
convert skeleton to graph
find all node coordinates and names
Find the closest distance from coordinates to cancel points
Either take the subgraph of nodes with range or outside of range
Ex: sk.coordinate_restriction_to_skeleton(
skeleton = skeleton_proof_stitch, coordinates = hdju.nucleus_center_from_segment_id(segment_id_raw), distance_threshold = 40_000, keep_skeleton_within_threshold = False, plot = True, verbose = True, )
- mesh_tools.skeleton_utils.coordinates_along_skeleton_offset_from_start(skeleton, starting_coordinate, n_points, offset, verbose=False, plot_starting_skeleton=False, plot_restricted_skeleton=False, plot_points=False)[source]
Purpose: To generate a certain number of points from the start of a skeleton. Ideally the points are a certain distance away from the start , but if there is not enough distance to do this then just space the points out as much as possible
Pseudocode: 1) Restrict the skeleton to n_points*offset from the start For the number of points going to take 2) Resize the skeleton
Ex: from mesh_tools import skeleton_utils as sk from datasci_tools import numpy_dep as np
skeleton = neuron_obj[0][42].skeleton starting_coordinate = neuron_obj[0].current_starting_coordinate
sk.coordinates_along_skeleton_offset_from_start( skeleton, starting_coordinate,
offset = 3000, n_points = 10, plot_points=True
)
- mesh_tools.skeleton_utils.coordinates_from_downstream_dist(skeleton, donwstream_dists, start_endpoint_coordinate=None, verbose=False, segment_width=20, plot=False)[source]
Purpose: Get skeleton coordinates as dictated by the skeletal lengths downstream - would be nice to chop up a skeleton by lengths from upstream
Psueodocode: 1) Order skeleton 2) Compute the skeletal distances of each coordinate 3) Get the coordinates that are closest in downstream dist to those sent
- mesh_tools.skeleton_utils.create_soma_extending_branches(current_skeleton, skeleton_mesh, soma_to_piece_touching_vertices, return_endpoints_must_keep=True, return_created_branch_info=False, try_moving_to_closest_sk_to_endpoint=True, distance_to_move_point_threshold=1500, check_connected_skeleton=True)[source]
Purpose: To make sure there is one singular branch extending towards the soma
Return value: endpoints_must_keep: dict mapping soma to array of the vertex points that must be kept because they are the soma extending branches of the skeleton
Pseudocode: Iterating through all of the somas and all of the groups of touching vertices a) project the skeleton and the soma boundary vertices on to the vertices of the mesh b) Find the closest skeleton point to the soma boundary vetices c) check the degree of the closest skeleton point: - if it is a degree one then leave alone - if it is not a degree one then create a new skeleton branch from the closest skeleton point and the average fo the border vertices and add to
Extension: (this is the same method that would be used for adding on a floating skeletal piece) If we made a new skeleton branch then could pass back the closest skeleton point coordinates and the new skeleton segment so we could: 1) Find the branch that it was added to 2) Divide up the mesh correspondnece between the new resultant branches – would then still reuse the old mesh correspondence
- mesh_tools.skeleton_utils.cut_skeleton_at_coordinate(skeleton, cut_coordinate, tolerance=0.001, verbose=False)[source]
Purpose: To cut a skeleton into 2 pieces at a certain cut coordinate
Application: Used when the MP skeleton pieces connect to the middle of a MAP branch and have to split it
Example: ex_sk = neuron_obj[1][0].skeleton cut_coordinate = np.array([ 560252., 1121040., 842599.])
- new_sk_cuts = sk.cut_skeleton_at_coordinate(skeleton=ex_sk,
cut_coordinate=cut_coordinate)
- ipvu.plot_objects(skeletons=new_sk_cuts,
skeletons_colors=”random”, scatters=[cut_coordinate])
- mesh_tools.skeleton_utils.decompose_skeleton_to_branches(current_skeleton, max_branch_distance=-1, skip_branch_threshold=20000, return_indices=False, remove_cycles=True)[source]
Example of how to run: elephant_skeleton = sk.read_skeleton_edges_coordinates(“../test_neurons/elephant_skeleton.cgal”) elephant_skeleton_branches = sk.decompose_skeleton_to_branches(elephant_skeleton) sk.graph_skeleton_and_mesh(other_skeletons=[sk.stack_skeletons(elephant_skeleton_branches)])
* Future error possibly: there could be issues in the future where had triangles of degree > 2 in your skeleton******
- mesh_tools.skeleton_utils.divide_branch(curr_branch_skeleton, segment_width=1000, equal_width=True, n_segments=0)[source]
When wanting to divide up one branch into multiple branches that don’t exceed a certain size
Example of how to use:
sk = reload(sk)
curr_index = 1 ex_branch = total_branch_skeletons[curr_index] ex_mesh = total_branch_meshes[curr_index] # sk.graph_skeleton_and_mesh(other_skeletons=[ex_branch], # other_meshes=[ex_mesh])
#there were empty arrays which is causing the error! returned_branches = sk.divide_branch(curr_branch_skeleton=ex_branch,
segment_width = 1000, equal_width=False, n_segments = 0)
print(len(returned_branches)) lengths = [sk.calculate_skeleton_distance(k) for k in returned_branches] print(f”lengths = {lengths}”)
- sk.graph_skeleton_and_mesh(
other_skeletons=returned_branches[:10],
- other_skeletons_colors=[“black”],
#other_skeletons=[ex_branch], other_meshes=[ex_mesh])
- mesh_tools.skeleton_utils.downsample_skeleton(current_skeleton)[source]
Downsamples the skeleton by 50% number of edges
- mesh_tools.skeleton_utils.endpoint_connectivity(endpoints_1, endpoints_2, exceptions_flag=True, return_coordinate=False, print_flag=False)[source]
Pupose: To determine where the endpoints of two branches are connected
Example: end_1 = np.array([[759621., 936916., 872083.],
[790891., 913598., 806043.]])
- end_2 = np.array([[790891., 913598., 806043.],
[794967., 913603., 797825.]])
endpoint_connectivity(end_1,end_2) >> {0: 1, 1: 0}
- mesh_tools.skeleton_utils.example_cgal_skeletonization_original_params(filename='./elephant.off', plot=True)[source]
- mesh_tools.skeleton_utils.filter_away_leaf_skeletons(skeleton, min_distance_to_junction=4001, endpoints_must_keep=None, verbose=False, return_removed_skeletons=False, plot=False, **kwargs)
- mesh_tools.skeleton_utils.filter_away_small_skeleton_offshoots(skeleton, min_distance_to_junction=4001, endpoints_must_keep=None, verbose=False, return_removed_skeletons=False, plot=False, **kwargs)[source]
- mesh_tools.skeleton_utils.find_branch_skeleton_with_specific_coordinate(divded_skeleton, current_coordinate)[source]
Purpose: From list of skeletons find the ones that have a certain coordinate
Example: curr_limb = current_neuron[0] branch_names = curr_limb.get_branch_names(return_int=True) curr_limb_divided_skeletons = [curr_limb[k].skeleton for k in branch_names] ideal_starting_endpoint = curr_limb.current_starting_coordinate
sk = reload(sk) sk.find_branch_skeleton_with_specific_coordinate(curr_limb_divided_skeletons,ideal_starting_endpoint)
- mesh_tools.skeleton_utils.find_end_nodes_with_significant_mesh_correspondence(skeleton, mesh, mesh_threshold=275, skeleton_considered_min=1600, skeleton_considered_max=6000, plot_viable_endpoint_correspondences=False, plot_keep_endpoints=False, verbose=False)[source]
- mesh_tools.skeleton_utils.find_skeleton_endpoint_coordinates(skeleton, coordinates_to_exclude=None, plot=False)[source]
Purpose: To find the endpoint coordinates of a skeleton
Application: 1) Can get the endpoints of a skeleton and then check that none of the spines contain an endpoint coordinate to help guard against false spines at the endpoints
Pseudocode: 1) convert the skeleton to a graph 2) Find the endpoint nodes of the graph (with degree 1) 3) return the coordinates of the graph nodes
- mesh_tools.skeleton_utils.flip_skeleton(current_skeleton)[source]
Will flip the absolute order of a skeleton
- mesh_tools.skeleton_utils.generate_surface_skeleton(vertices, faces, surface_samples=1000, n_surface_downsampling=0, print_flag=False)[source]
Purpose: To generate a surface skeleton
Specifics: New implementation that uses meshparty method of finding root that optimally finds longest shortest path
- mesh_tools.skeleton_utils.generate_surface_skeleton_slower(vertices, faces=None, surface_samples=1000, n_surface_downsampling=0, print_flag=False)[source]
Purpose: Generates a surface skeleton without using the root method and instead just samples points
- mesh_tools.skeleton_utils.get_ordered_branch_nodes_coordinates(skeleton_graph, nodes=False, coordinates=True)[source]
Purpose: want to get ordered skeleton coordinates: 1) get both end nodes 2) count shortest path between them (to get order) 3) then use the get node attributes function
- mesh_tools.skeleton_utils.graph_skeleton_and_mesh(main_mesh_verts=[], main_mesh_faces=[], unique_skeleton_verts_final=[], edges_final=[], edge_coordinates=[], other_meshes=[], other_meshes_colors=[], mesh_alpha=0.2, other_meshes_face_components=[], other_skeletons=[], other_skeletons_colors=[], return_other_colors=False, main_mesh_color=[0.0, 1.0, 0.0, 0.2], main_skeleton_color=[0, 0.0, 1, 1], main_mesh_face_coloring=[], other_scatter=[], scatter_size=0.3, other_scatter_colors=[], main_scatter_color='red', scatter_with_widgets=False, buffer=1000, axis_box_off=True, html_path='', show_at_end=True, append_figure=False, set_zoom=True, flip_y=True, adaptive_min_max_limits=True)[source]
Graph the final result of skeleton and mesh
Pseudocode on how to do face colorings : could get a dictionary mapping faces to colors or groups - if mapped to groups then do random colors (and generate them) - if mapped to colors then just do submeshes and send the colors
- mesh_tools.skeleton_utils.high_degree_coordinates_on_path(limb_obj, curr_path_to_cut, degree_to_check=4)[source]
Purpose: Find coordinates on a skeleton of the path speciifed (in terms of node ids) that are above the degree_to_check (in reference to the skeleton)
- mesh_tools.skeleton_utils.high_degree_coordinates_on_skeleton(skeleton, min_degree_to_find=4, exactly_equal=False, verbose=False, plot_high_degree_points=False)[source]
Purpose: To find the high degree coordinates on a skeleton
- mesh_tools.skeleton_utils.intersect_skeleton(skeleton_1, skeleton_2, distance_threshold=5000, **kwargs)[source]
- mesh_tools.skeleton_utils.jitter_skeleton_from_coordinate(coordinate, jitter=None, random_noise=True, verbose=True)[source]
Purpose: generate a skeleton segment to slightly shift and endpoint and return the skeleton
Pseudocode: 1) Compute the new jittered endpoint 2) Create the new skeleton segment
- mesh_tools.skeleton_utils.map_between_branches_lists(branches_1, branches_2, check_all_matched=True, min_to_match=2)[source]
Purpose: Will create a unique mapping of a branch in the first list to the best fitting branch in the second in terms of the most matching coordinates with a distance of 0
min_to_match is the number of vertices that must match in order to be considered for the matching Ex: cleaned_branches = sk.decompose_skeleton_to_branches(curr_limb_sk_cleaned) original_branches = [k.skeleton for k in curr_limb] map_between_branches_lists(original_branches,cleaned_branches)
- mesh_tools.skeleton_utils.map_between_branches_lists_old(branches_1, branches_2, check_all_matched=True, min_to_match=2)[source]
Purpose: Will create a unique mapping of a branch in the first list to the best fitting branch in the second in terms of the most matching coordinates with a distance of 0
min_to_match is the number of vertices that must match in order to be considered for the matching Ex: cleaned_branches = sk.decompose_skeleton_to_branches(curr_limb_sk_cleaned) original_branches = [k.skeleton for k in curr_limb] map_between_branches_lists(original_branches,cleaned_branches)
- mesh_tools.skeleton_utils.matching_endpoint_singular(array_1, array_2, return_indices=False, verbose=False)[source]
purpose: To find the one matching coordinate between two arrays and can return the index of each
- mesh_tools.skeleton_utils.mesh_restriction_to_skeleton(skeleton, mesh, distance_threshold=6000, keep_skeleton_within_threshold=True, plot=False, verbose=False, return_graph=False)[source]
- mesh_tools.skeleton_utils.mesh_subtraction_by_skeleton(mesh, edges, buffer=1000, bbox_ratio=None, distance_threshold=8000, resize_length=2000, verbose=False, initial_plotting=False, edge_idx_to_process=None, edge_loop_print=False, edge_loop_plotting_slice=False, edge_loop_plotting=False, edge_length_to_process=0.001, submesh_face_min=200, edge_loop_plot_winning_faces=False, return_subtracted_mesh=False, plot_final_mesh=False, final_split_n_faces_min=500)[source]
- Purpose: Will return significant mesh pieces that are
not already accounteed for by the skeleton
Psuedocode: 1) Restrict the mesh with a bounding box if requested 2) Optionally downsample the skeleton Iterate through all of the edges in skeleton and find matching faces 3) Concatenate into one mesh
——Example 1:—– segment_id = 31504318800
- neuron_obj = hdju.neuron_obj_from_table(
segment_id=segment_id, table = h01auto.Decomposition.Object(), verbose = True, return_one=True,
)
from neurd import neuron_visualizations as nviz nviz.visualize_neuron(neuron_obj,limb_branch_dict=”all”)
ret_mesh = sk.mesh_subtraction_by_skeleton(
mesh = neuron_obj[0].mesh, edges = neuron_obj[0][3].skeleton,
buffer=2000, bbox_ratio=None,#,1.2 distance_threshold=8_000, resize_length = 2000, verbose =True, initial_plotting = False,
#edge_idx_to_process = [0,3], edge_loop_print=False, edge_loop_plotting_slice = False, edge_loop_plotting = False,
edge_length_to_process = 0.001, submesh_face_min = 200,
edge_loop_plot_winning_faces= False, return_subtracted_mesh = True,
plot_final_mesh = True, final_split_n_faces_min = 500, )
- mesh_tools.skeleton_utils.mesh_subtraction_by_skeleton_old(main_mesh, edges, buffer=0.01, bbox_ratio=1.2, distance_threshold=2000, significance_threshold=500, print_flag=False)[source]
Purpose: Will return significant mesh pieces that are not already accounteed for by the skeleton
Example of how to run
main_mesh_path = Path(“./Dustin/Dustin.off”) main_mesh = trimesh.load_mesh(str(main_mesh_path.absolute())) skeleton_path = main_mesh_path.parents[0] / Path(main_mesh_path.stem + “_skeleton.cgal”) edges = sk.read_skeleton_edges_coordinates(str(skeleton_path.absolute()))
# turn this into nodes and edges main_mesh_nodes, main_mesh_edges = sk.read_skeleton_verts_edges(str(skeleton_path.absolute())) sk.graph_skeleton_and_mesh(
main_mesh_verts=main_mesh.vertices, main_mesh_faces=main_mesh.faces, unique_skeleton_verts_final = main_mesh_nodes, edges_final=main_mesh_edges, buffer = 0
)
- leftover_pieces = mesh_subtraction_by_skeleton(main_mesh,edges,
buffer=0.01,
- bbox_ratio=1.2,
distance_threshold=500,
- significance_threshold=500,
print_flag=False)
# Visualize the results: pieces_mesh = trimesh.Trimesh(vertices=np.array([]),
faces=np.array([]))
- for l in leftover_pieces:
pieces_mesh += l
- sk.graph_skeleton_and_mesh(
main_mesh_verts=pieces_mesh.vertices, main_mesh_faces=pieces_mesh.faces, unique_skeleton_verts_final = main_mesh_nodes, edges_final=main_mesh_edges, buffer = 0
)
- mesh_tools.skeleton_utils.move_point_to_nearest_branch_end_point_within_threshold(skeleton, coordinate, distance_to_move_point_threshold=1000, return_coordinate=True, return_change_status=True, verbose=False, consider_high_degree_nodes=True, possible_node_coordinates=None, excluded_node_coordinates=None)[source]
Purpose: To pick a branch or endpoint node that is within a certain a certain distance of the original node (if none in certain distance then return original)
Arguments: possible_node_coordinates: this allows you to specify nodes that you want to select
- mesh_tools.skeleton_utils.number_connected_components(skeleton)[source]
Will find the number of connected components in a whole skeleton
- mesh_tools.skeleton_utils.number_connected_components_branches(skeleton_branches)[source]
Will find the number of connected components in a list of skeleton branches
Pseudocode:
Get the shared endpoint of the branches
Reorder the branches so both start with the endpoint and then resize
For each edge skeleton (in order to get the final edge skeletons): 3) Use the restrict skeelton function to subtract the offset - if not then add whole skeleton to final skeleton 4) if it was a sucess, see if the distance is greater than comparison distance - if not then add current skeleton to final 5) Use the subtract skeleton to only keep the comparison distance of skeleton 6) Add to final skeleton
offset_skeletons_aligned_at_shared_endpoint()
Ex: vis_branches_idx = [7,9] vis_branches = [curr_limb[k] for k in vis_branches_idx] vis_branches
curr_skeletons = [k.skeleton for k in vis_branches] stripped_skeletons = sk.offset_skeletons_aligned_at_shared_endpoint(curr_skeletons)
curr_colors = [“red”,”black”] ipvu.plot_objects(meshes=[k.mesh for k in vis_branches],
meshes_colors=curr_colors, skeletons=stripped_skeletons, skeletons_colors=curr_colors, scatters=[np.array([stripped_skeletons[0][-1][-1],stripped_skeletons[1][-1][-1]])], scatter_size=1
)
sk.parent_child_skeletal_angle(stripped_skeletons[1],stripped_skeletons[0])
- mesh_tools.skeleton_utils.offset_skeletons_aligned_parent_child_skeletal_angle(skeleton_1, skeleton_2, offset=1000, comparison_distance=2000, min_comparison_distance=1000)[source]
Purpose: To determine the parent child skeletal angle of 2 skeletons while using the offset and comparison distance
- mesh_tools.skeleton_utils.old_combine_close_branch_points(skeleton, combine_threshold=700, print_flag=False)[source]
Purpose: To take a skeleton or graph and return a skelton/graph where close branch points are combined
Example Code of how could get the orders: # How could potentially get the edge order we wanted endpoint_neighbors_to_order_map = dict() for k in endpoint_neighbors:
total_orders = [] total_orders_neighbors = [] for j in p:
- try:
total_orders.append(curr_sk_graph[j][k][“order”]) total_orders_neighbors.append(j)
- except:
pass
order_index = np.argmin(total_orders) endpoint_neighbors_to_order_map[(k,total_orders_neighbors[order_index])] = total_orders[order_index]
endpoint_neighbors_to_order_map
Ex: sk = reload(sk) from datasci_tools import numpy_utils as nu nu = reload(nu) branch_skeleton_data_cleaned = [] for i,curr_sk in enumerate(branch_skeleton_data):
print(f”
- —– Working on skeleton {i} ———“)
new_sk = sk.combine_close_branch_points(curr_sk,print_flag=True) print(f”Original Sk = {curr_sk.shape}, Cleaned Sk = {new_sk.shape}”) branch_skeleton_data_cleaned.append(new_sk)
- mesh_tools.skeleton_utils.order_resize_skeleton(skeleton, start_endpoint_coordinate, segment_width=0, n_segments=0, plot_skeleton=False, **kwargs)[source]
Purpose: To order a skeleton and then resize it
Ex: sk.order_resize_skeleton(branch_obj.skeleton,
nru.upstream_endpoint(limb_obj,branch_idx), skeleton_resize_distance, plot_skeleton=True
)
- mesh_tools.skeleton_utils.order_skeleton(skeleton, start_endpoint_coordinate=None, verbose=False, return_indexes=False, error_on_non_start_coordinate=False)[source]
Purpose: to get the skeleton in ordered vertices 1) Convert to graph 2) Find the endpoint nodes 3) Find the shortest path between endpoints 4) Get the coordinates of all of the nodes 5) Create the skeleton by indexing into the coordinates by the order of the path
- mesh_tools.skeleton_utils.order_skeletons_connecting_endpoints(skeletons, starting_endpoint_coordinate)[source]
Purpose: To get a list of the endpoints in the order that the list of skeletons are connected
Ex: order_skeletons_connecting_endpoints([neuron_obj[0][k].skeleton for
k in ex_path],
starting_endpoint_coordinate=st_coord)
- mesh_tools.skeleton_utils.parent_child_skeletal_angle(parent_skeleton, child_skeleton)[source]
To find the angle from continuation that the second skeleton deviates from the parent angle
angles are just computed from the vectors of the endpoints
- mesh_tools.skeleton_utils.path_ordered_skeleton(skeleton)[source]
Purpose: To order the edges in sequential order in a skeleton so skeleton[0] is one end edge and skeleton[-1] is the other end edge
Pseudocode: How to order a skeleton: 1) turn the skeleton into a graph 2) start at an endpoint node 3) output the skeleton edges for the edges of the graph until hit the other end node
Ex: skeleton = big_neuron[0][30].skeleton new_skeleton_ordered = path_ordered_skeleton(skeleton)
- mesh_tools.skeleton_utils.percentage_skeleton_match_to_ref_vector(skeleton, reference_vector, max_angle, min_angle=None, start_endpoint_coordinate=None, segment_width=0, n_segments=0, plot_skeleton=False, verbose=False, return_match_length=False)[source]
Find the percentage (and distance) that match angle requirements to a given vector
Ex: sk.percentage_skeleton_match_to_ref_vector(branch_obj.skeleton,
- reference_vector=reference_vector,
max_angle=angle_threshold,
start_endpoint_coordinate=nru.upstream_endpoint(limb_obj,branch_idx),
- segment_width=skeleton_resize_distance,
- plot_skeleton=True,
verbose=True, return_match_length = True)
- mesh_tools.skeleton_utils.plot_ipv_scatter(scatter_points, scatter_color=[1.0, 0.0, 0.0, 0.5], scatter_size=0.4, flip_y=True)[source]
- mesh_tools.skeleton_utils.plot_ipv_skeleton(edge_coordinates, color=[0, 0.0, 1, 1], flip_y=True)[source]
- mesh_tools.skeleton_utils.recompose_skeleton_from_branches(decomposed_branches)[source]
Takes skeleton branches and stitches them back together without any repeating nodes
- mesh_tools.skeleton_utils.remove_cycles_from_skeleton(skeleton, max_cycle_distance=5000, verbose=False, check_cycles_at_end=True, return_original_if_error=False, error_on_more_than_two_paths_between_high_degree_nodes=False)[source]
Purpose: To remove small cycles from a skeleton
Pseudocode: How to resolve a cycle A) Convert the skeleton into a graph B) Find all cycles in the graph
For each cycle
1) Get the length of the cycle –> if length if too big then skip 2) If only 1 high degree node, then just delete the other non high degree nodes 3) Else, there should only be 2 high degree nodes in the vertices of the cycle –> if more or less then skip 3) Get the 2 paths between the high degree nodes 4) Delete nodes on the path for the longer distance one ————
convert the graph back into a skeleton
Ex: remove_cycles_from_skeleton(skeleton = significant_poisson_skeleton)
- mesh_tools.skeleton_utils.resize_and_center_skeleton(skeleton, resize_skeleton=True, segment_width=5000, plot_resized_skeleton=False, center_coordinates=True, center=None, verbose=False)[source]
- mesh_tools.skeleton_utils.resize_center_and_coordinates_from_skeleton(skeleton, resize_skeleton=True, segment_width=5000, plot_resized_skeleton=False, center_coordinates=True, center=None, verbose=False, return_coordinates=True)[source]
Purpose: To resize, center export as coordinates
- mesh_tools.skeleton_utils.resize_skeleton_branch(curr_branch_skeleton, segment_width=0, n_segments=0, print_flag=False, try_order_skeleton_from_original=True)[source]
sk = reload(sk) cleaned_skeleton = sk.resize_skeleton_branch(curr_branch_skeleton,segment_width=1000)
- sk.graph_skeleton_and_mesh(other_meshes=[curr_branch_mesh],
other_skeletons=[cleaned_skeleton])
- mesh_tools.skeleton_utils.resize_skeleton_with_branching(skeleton, segment_width, optimal_speed=False, verbose=False, plot=False)[source]
Purpose: To resize a skeleton that may have some branching points
Pseudocode: 1) Decompose the skeleton into segments 2) Resize all of the skeleton segments 3) Recompose the skeleton
- mesh_tools.skeleton_utils.restrict_skeleton_from_start(skeleton, cutoff_distance, subtract_cutoff=False, return_indexes=True, return_success=True, resize_skeleton_to_help_success=False, tolerance=10, starting_coordinate=None)[source]
To restrict a skeleton to a certain cutoff distance from the start which keeps that distance or subtracts it (and does not resize or reorder the skeleton but keeps the existing segment lengths)
Ex: restrict_skeleton_from_start(skeleton = base_skeleton_ordered, cutoff_distance = offset)
**** warning only works if skeletal segment *
- mesh_tools.skeleton_utils.restrict_skeleton_from_start_plus_offset(skeleton, offset=1000, comparison_distance=2000, min_comparison_distance=1000, verbose=True, start_coordinate=None, skeleton_resolution=100, plot=False)[source]
Purpose: Will get a portion of the skeleton relative the start that 1) subtracts off the offset 2) keeps the next comparison distance length
Pseudocode:
3) Use the restrict skeelton function to subtract the offset - if not then add whole skeleton to final skeleton 4) if it was a sucess, see if the distance is greater than comparison distance - if not then add current skeleton to final 5) Use the subtract skeleton to only keep the comparison distance of skeleton 6) Add to final skeleton
offset_skeletons_aligned_at_shared_endpoint()
Ex: vis_branches_idx = [7,9] vis_branches = [curr_limb[k] for k in vis_branches_idx] vis_branches
curr_skeletons = [k.skeleton for k in vis_branches] stripped_skeletons = sk.offset_skeletons_aligned_at_shared_endpoint(curr_skeletons)
curr_colors = [“red”,”black”] ipvu.plot_objects(meshes=[k.mesh for k in vis_branches],
meshes_colors=curr_colors, skeletons=stripped_skeletons, skeletons_colors=curr_colors, scatters=[np.array([stripped_skeletons[0][-1][-1],stripped_skeletons[1][-1][-1]])], scatter_size=1
)
sk.parent_child_skeletal_angle(stripped_skeletons[1],stripped_skeletons[0])
- mesh_tools.skeleton_utils.restrict_skeleton_to_distance_from_coordinate(skeleton, coordinate, distance_threshold)[source]
Purpose: To restrict a skeleton to only those points within a certain distance of a starting coordinate
Ex: retr_skeleton = sk.restrict_skeleton_to_distance_from_coordinate(neuron_obj[0].skeleton,
neuron_obj[0].current_starting_coordinate, 10000,)
- ipvu.plot_objects(main_skeleton=neuron_obj[0].skeleton,
skeletons=[restr_skeleton], skeletons_colors=”red”)
- mesh_tools.skeleton_utils.save_skeleton_cgal(surface_with_poisson_skeleton, largest_mesh_path)[source]
surface_with_poisson_skeleton (np.array) : nx2 matrix with the nodes
- mesh_tools.skeleton_utils.setdiff_skeleton(skeleton_1, skeleton_2, distance_threshold=5000, **kwargs)[source]
- mesh_tools.skeleton_utils.setup_root(mesh, is_soma_pt=None, soma_d=None, is_valid=None)[source]
function to find the root index to use for this mesh
Purpose: The output will be used to find the path for a surface skeletonization (aka: longest shortest path)
The output: 1) root: One of the end points 2) target: The other endpoint: 3) root_ds: (N,) matrix of distances from root to all other vertices 4) : predecessor matrix for root to shortest paths of all other vertices –> used to find surface path 5) valid: boolean mask (NOT USED)
Purpose: To return the shared coordinates by skeletons
Will return the endpoint that joins two branches
- mesh_tools.skeleton_utils.shortest_path_between_two_sets_of_skeleton_coordiantes(skeleton, coordinates_list_1, coordinates_list_2, return_closest_coordinates=False, return_path_distance=False, plot_closest_coordinates=False, G=None)[source]
Purpose: to find the shortest skeleton path between 2 sets of skeleton coordinates
Pseudocode: 1) convert the skeleton into a graph 2) Find the nodes of the skeleton coordinates in both groups 3) Find the shortest path between the nodes (along with the winning nodes) 4) Find the coordinates of these end nodes (if just asked for this return here) 5) Get a subgraph of the shortest path and convert back into a skeleton
Ex: shortest_path_between_two_sets_of_skeleton_coordiantes(
skeleton = limb_skeleton, coordinates_list_1 = [starting_coordinate], coordinates_list_2 = closest_branch_endpoints, return_closest_coordinates = True, plot_closest_coordinates = True)
- mesh_tools.skeleton_utils.skeleton_cgal(mesh=None, mesh_path=None, path_to_write=None, filepath='./temp.off', remove_mesh_temp_file=True, remove_skeleton_temp_file=False, manifold_fix=True, verbose=False, return_skeleton_file_path_and_status=False, cgal_original_parameters=False, plot=False, **kwargs)[source]
Pseudocode: 1) Write the mesh to a file 2) Pass the file to the calcification 3) Delete the temporary file
– will now check and fix mesh manifoldness
Polyhedron mesh; if (!input) {
std::cerr << “Cannot open file “ << std::endl; return 2;
} std::vector<K::Point_3> points; std::vector< std::vector<std::size_t> > polygons; if (!CGAL::read_OFF(input, points, polygons)) {
std::cerr << “Error parsing the OFF file “ << std::endl; return 3;
} CGAL::Polygon_mesh_processing::orient_polygon_soup(points, polygons); CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, polygons, mesh); if(!CGAL::is_closed(mesh)){
std::cerr << “Not closed mesh”<<std::endl; return 4;
}
- mesh_tools.skeleton_utils.skeleton_cgal_original_parameters(mesh=None, cgal_original_parameters=True, plot=False, **kwargs)[source]
- mesh_tools.skeleton_utils.skeleton_coordinate_offset_from_endpoint(skeleton, endpoint_coordinate, offset_distance, plot_coordinate=False, verbose=False, subtract_cutoff=True, return_success=False)[source]
Purpose: Will return a skeleton coordinate that is a certain distance offset from an endpoint coordinate
Ex: new_point = skeleton_coordinate_offset_from_endpoint(v_skeleton,
coord, #offset_distance=offset_distance_for_points, offset_distance=50000, verbose=True,
plot_coordinate=True)
- mesh_tools.skeleton_utils.skeleton_coordinate_path_from_start(skeleton, start_endpoint_coordinate=None)[source]
Purpose: will just give vertex path of skeleton from the first
- mesh_tools.skeleton_utils.skeleton_endpoint_vector(skeleton, normalize_vector=True, starting_coordinate=None)[source]
Purpose: To get the vector made by the endpoints of a skeleton
Pseudocode: 1) if the starting coordinate is specified then order the skeleton according to that starting coordinate 2) Get the endpoints of the skeleton 3) Subtract the endpoints from each other (Normalize if requested)
- mesh_tools.skeleton_utils.skeleton_graph_nodes_to_group(skeleton_grpah)[source]
Checks that no nodes in graph are in the same coordinates and need to be combined
Example Use Case:
example_skeleton = current_mesh_data[0][“branch_skeletons”][0] skeleton_grpah = sk.convert_skeleton_to_graph(example_skeleton) limb_nodes_to_group = sk.skeleton_graph_nodes_to_group(skeleton_grpah) limb_nodes_to_group
#decompose the skeleton and then recompose and see if any nodes to group decomposed_branches = sk.decompose_skeleton_to_branches(example_skeleton) decomposed_branches_stacked = sk.stack_skeletons(example_skeleton) restitched_decomposed_skeleton = sk.convert_graph_to_skeleton(sk.convert_skeleton_to_graph(decomposed_branches_stacked)) sk.skeleton_graph_nodes_to_group(restitched_decomposed_skeleton)
#shows that the restitched skeleton is still just one connected componet connected_components = nx.connected_components(sk.convert_skeleton_to_graph(decomposed_branches_stacked)) len(list(connected_components))
sk.graph_skeleton_and_mesh(other_skeletons = [restitched_decomposed_skeleton])
- mesh_tools.skeleton_utils.skeleton_list_connectivity(skeletons, print_flag=False)[source]
Will find the edge list for the connectivity of branches in a list of skeleton branches
- mesh_tools.skeleton_utils.skeleton_list_connectivity_slow(skeletons, print_flag=False)[source]
Purpose: To determine which skeletons branches are connected to which and to record an edge list
Pseudocode: For all branches i:
a. get the endpoints For all branches j:
get the endpoints
compare the endpoints to the first
if matching then add an edge
- mesh_tools.skeleton_utils.skeleton_n_components(curr_skeleton)[source]
Purpose: To get the number of connected components represented by the current skeleton
- mesh_tools.skeleton_utils.skeleton_path_between_skeleton_coordinates(starting_coordinate, destination_coordinate=None, skeleton=None, skeleton_graph=None, destination_node=None, only_skeleton_distance=False, plot_skeleton_path=False, return_singular_node_path_if_no_path=False, verbose=False)[source]
Purpose: To find the skeleton_path_between two coordinates (that lie on a skeleton) (can just request the distances and not the path)
Pseudocode: 0) Convert the skeleton into a graph 1) Find the graph node of the destination coordinate 2) For each coordinate: - find the node in the skeleton graph
a. if skeleton path is requested - find the shortest path between the start and destiation node (and convert ) - find the shortest path length between start node and destination node
b. if only skeleton distance is requested - find shortest path distance between 2 nodes
return list of skeleton paths or skeleton path distances
Ex: skeleton_path_between_skeleton_coordinates(
#skeleton = filtered_neuron[0].skeleton,
- skeleton = None,
starting_coordinate = skeleton[[10000]].reshape(-1,3),
destination_coordinate = None, skeleton_graph = limb_graphs[0], destination_node = 15310, only_skeleton_distance = False, plot_skeleton_path = True, verbose = True,)
- mesh_tools.skeleton_utils.skeleton_vectors(skeleton, start_endpoint_coordinate, segment_width=0, n_segments=0, plot_skeleton=False)[source]
Purpose: To get skeleton vectors of a reshaped and ordered skeleton
- mesh_tools.skeleton_utils.skeletonize_and_clean_connected_branch_CGAL(mesh, curr_soma_to_piece_touching_vertices=None, total_border_vertices=None, filter_end_node_length=4001, perform_cleaning_checks=False, combine_close_skeleton_nodes=True, combine_close_skeleton_nodes_threshold=700, verbose=False, remove_cycles_at_end=True, remove_mesh_interior_face_threshold=0, error_on_bad_cgal_return=False, max_stitch_distance=10000, restrict_end_nodes_filtered_by_corr=False, **kwargs)[source]
Purpose: To create a clean skeleton from a mesh (used in the neuron preprocessing package)
- mesh_tools.skeleton_utils.skeletonize_connected_branch(current_mesh, output_folder='./temp', delete_temp_files=True, name='None', surface_reconstruction_size=50, surface_reconstruction_width=None, poisson_stitch_size=4000, n_surface_downsampling=1, n_surface_samples=1000, skeleton_print=False, mesh_subtraction_distance_threshold=8000, mesh_subtraction_buffer=1000, max_stitch_distance=10000, current_min_edge=75, close_holes=True, limb_name=None, use_surface_after_CGAL=True, remove_cycles=True, connectivity='vertices', verbose=False, remove_mesh_interior_face_threshold=0, error_on_bad_cgal_return=False)[source]
Purpose: To take a mesh and construct a full skeleton of it (Assuming the Soma is already extracted)
Poisson Surface Reconstruction
- CGAL skeletonization of all signfiicant pieces
- (if above certain size ! threshold)
–> if not skip straight to surface skeletonization
Using CGAL skeleton, find the leftover mesh not skeletonized
4) Do surface reconstruction on the parts that are left over - with some downsampling 5) Stitch the skeleton
- mesh_tools.skeleton_utils.skeletonize_connected_branch_meshparty(mesh, segment_size=100, invalidation_d=1200, combine_close_skeleton_nodes_threshold=0, filter_end_nodes=True, filter_end_node_length=4000, root=None, verbose=False, only_skeleton=False, plot=False, **kwargs)[source]
Purpose: To do the meshparty skeletonization and skeleton procedure
Example: Applying skeletonization to an axon mesh
axon_mesh = neuron_obj.axon_mesh meshparty_sk,sk_meshparty_obj = sk.skeletonize_connected_branch_meshparty(axon_mesh,
root=neuron_obj.axon_starting_coordinate, invalidation_d=1200, #combine_close_skeleton_nodes_threshold=20000,
filter_end_node_length=3000,
)
ipvu.plot_objects(axon_mesh,meshparty_sk)
- mesh_tools.skeleton_utils.smooth_skeleton_branch(skeleton, neighborhood=2, iterations=100, coordinates_to_keep=None, keep_endpoints=True)[source]
Purpose: To smooth skeleton of branch while keeping the same endpoints
Pseudocode: 1) get the endpoint coordinates of the skeleton 2) turn the skeleton into nodes and edges - if number of nodes is less than 3 then return 3) Find the indexes that are the end coordinates 4) Send the coordinates and edges off to get smoothed 5) Replace the end coordinate smooth vertices with original 6) Convert nodes and edges back to a skeleton
Ex: orig_smoothed_sk = smooth_skeleton(neuron_obj[limb_idx][branch_idx].skeleton,
neighborhood=5)
- mesh_tools.skeleton_utils.soma_skeleton_stitching(total_soma_skeletons, soma_mesh)[source]
Purpose: Will stitch together the meshes that are touching the soma
Pseudocode: 1) Compute the soma mesh center point 2) For meshes that were originally connected to soma a. Find the closest skeletal point to soma center b. Add an edge from closest point to soma center 3) Then do stitching algorithm on all of remaining disconnected
skeletons
- mesh_tools.skeleton_utils.split_skeleton_into_edges(current_skeleton)[source]
Purpose: Will split a skeleton into a list of skeletons where each skeleton is just one previous edge of the skeleton before
Example of how to use:
returned_split = split_skeleton_into_edges(downsampled_skeleton) print(len(returned_split), downsampled_skeleton.shape) returned_split
- mesh_tools.skeleton_utils.stitch_skeleton(staring_edges, max_stitch_distance=10000, stitch_print=False, main_mesh=[])[source]
- mesh_tools.skeleton_utils.subskeleton_from_vertices_idx(vertices_idx, skeleton=None, vertices=None, edges=None, plot=False)[source]
Purpose: To get a skeleton from a list of indices referencing a subset of the original vertices
Ex: sk.subskeleton_from_vertices_idx(
vertices_idx = np.arange(0,1000), skeleton = truth_skeleton, plot=True
)
- mesh_tools.skeleton_utils.subtract_exact_coordinates_from_skeleton(skeleton, coordinates, return_graph=False, verbose=False)[source]
- mesh_tools.skeleton_utils.vector_away_from_endpoint(skeleton, endpoint, offset=500, comparison_distance=3000, skeleton_resolution=100, min_comparison_distance=1000, plot_restricted_skeleton=False, normalize_vector=True, verbose=False)[source]
Purpose: To get the upstream or downstream vector from the offest start of a skeleton
Result: The vector naturally points away from the start coordinate
Ex: sk.vector_away_from_endpoint(
skeleton = branch_obj.skeleton, endpoint = np.array([2504610. , 480431. , 33741.2]), verbose = True, plot_restricted_skeleton = True
)
mesh_tools.trimesh_utils module
These functions just help with generically helping with trimesh mesh manipulation
- mesh_tools.trimesh_utils.area_of_vertex_boundary(mesh, vertices, plot=False)[source]
Purpose: To find the area of a vertex boundary
- mesh_tools.trimesh_utils.bbox_intersect_test(mesh_1, mesh_2, plot=True, mesh_1_color='green', mesh_2_color='black', mesh_alpha=1, mesh_1_bbox_color='red', mesh_2_bbox_color='blue', bbox_alpha=0.2, verbose=False)[source]
Purpose: To determine if a mesh bounding box intersects another bounding box
Pseducode: 1) Plot the bounding boxes of the corner and the mesh 2) Determine whether the two bounding boxes intersect
Ex: bbox_intersect_test( mesh_1=segment_mesh, mesh_2=ease_bounding_box, plot = True, verbose = True, )
- mesh_tools.trimesh_utils.bbox_mesh(arr=None, bbox_corners=None, bbox_coords=None, verbose=False, debug=False, plot=False)[source]
compute the faces of the bouding box mesh: it is the size 3 sliding window across a path (for all paths) - but there is some redundancy paths then
How to efficiently determine all paths? 1) node can only move from point with one element greater - could get edge matrix easily
- mesh_tools.trimesh_utils.bbox_mesh_restriction(curr_mesh, bbox_upper_corners, mult_ratio=1)[source]
Purpose: Can send multiple bounding box corners to the function and it will restrict your mesh to only the faces that are within those bounding boxs ** currently doing bounding boxes that are axis aligned
– Future work – could get an oriented bounding box by doing
elephant_skeleton_verts_mesh = trimesh.Trimesh(vertices=el_verts,faces=np.array([])) elephant_skeleton_verts_mesh.bounding_box_oriented
but would then have to do a projection into the oriented bounding box plane to get all of the points contained within
- mesh_tools.trimesh_utils.bbox_side_length_ratios(current_mesh)[source]
Will compute the ratios of the bounding box sides To be later used to see if there is skewness
- mesh_tools.trimesh_utils.border_euclidean_length(border)[source]
The border does have to be specified as ordered coordinates
- mesh_tools.trimesh_utils.bounding_box(mesh, oriented=False)[source]
Returns the mesh of the bounding box
Input: Can take in the corners of a bounding box as well
- mesh_tools.trimesh_utils.bounding_box_center(mesh, oriented=False)[source]
Computed the center of the bounding box
Ex: ex_mesh = neuron_obj_with_web[axon_limb_name][9].mesh ipvu.plot_objects(ex_mesh,
scatters=[tu.bounding_box_center(ex_mesh)], scatter_size=1)
- mesh_tools.trimesh_utils.bounding_box_oriented_side_lengths(mesh, return_largest_side=False)[source]
- mesh_tools.trimesh_utils.center_at_origin(mesh)
To move a mesh to where the mesh center is at the origina
- mesh_tools.trimesh_utils.centered_at_origin(mesh)
To move a mesh to where the mesh center is at the origina
- mesh_tools.trimesh_utils.check_coordinates_inside_bounding_box(mesh, coordinates, bbox_coordinate_divisor=None, return_inside_indices=True, verbose=False)[source]
Purpose: To return the indices of points inside of the bounding box of amesh
Ex: soma_mesh = neuron_obj.get_soma_meshes()[0] ex_verts = np.vstack([neuron_objs[0][1].mesh.vertices[:5],soma_mesh.vertices[:5]]) tu.check_coordinates_inside_bounding_box(soma_mesh,ex_verts,return_inside_indices=False)
- mesh_tools.trimesh_utils.check_meshes_inside_mesh_bbox(main_mesh, test_meshes, return_indices=False, return_inside=True, bbox_multiply_ratio=1)[source]
Purpose: Will check to see if any of the vertices of the test meshes are inside the bounding box of the main mesh
Pseudocode: 1) Get the bounding box corners of the main mesh 2) For each test mesh - send the vertices to see if inside bounding box - if any are then add indices to the running list
3) Return either the meshes/indices of the inside/outside pieces based on the parameters set
- mesh_tools.trimesh_utils.check_meshes_inside_multiple_mesh_bbox(main_meshes, test_meshes, return_indices=False, return_inside=True)[source]
Purpose: will return all of the pieces inside or outside of multiple seperate main mesh bounding boxes
Pseudocode: For each main mesh 1) Run the check_meshes_inside_mesh_bbox and collect the resulting indexes 2) Combine the results based on the following: - If outside, then do intersetion of results (becuase need to be outside of all) - if inside, then return union of results (because if inside at least one then should be considered inside) 3) Return either the meshes or indices
Ex: from mesh_tools import trimesh_utils as tu tu = reload(tu) tu.check_meshes_inside_multiple_mesh_bbox([soma_mesh,soma_mesh,soma_mesh],neuron_obj.non_soma_touching_meshes,
return_indices=False)
- mesh_tools.trimesh_utils.check_meshes_outside_mesh_bbox(main_mesh, test_meshes, return_indices=False)[source]
- mesh_tools.trimesh_utils.check_meshes_outside_multiple_mesh_bbox(main_meshes, test_meshes, return_indices=False)[source]
- mesh_tools.trimesh_utils.close_hole_area_max(mesh, verbose=False, return_mesh=False, plot=False, **kwargs)[source]
- mesh_tools.trimesh_utils.close_hole_area_top_2_mean(mesh, verbose=False, return_mesh=False, plot=False, **kwargs)[source]
- mesh_tools.trimesh_utils.close_hole_area_top_k_extrema(mesh, k, extrema='max', aggr_func=None, verbose=False, return_mesh=False, plot=False, default_value=0, fill_to_meet_length=True)[source]
- mesh_tools.trimesh_utils.close_hole_areas(mesh, return_meshes=False, sort_type='max', verbose=False)[source]
Purpose: To find the aresa of all conn components needed for closing holes
- mesh_tools.trimesh_utils.close_mesh_holes(mesh, faces=None, insert_vertices=False, calculate_normals=False, vertices_to_stitch=None, return_mesh=True, return_mesh_with_holes_stitched=False, plot=False, verbose=False)
Create a fan stitch over the boundary of the specified faces. If the boundary is non-convex a triangle fan is going to be extremely wonky. :param vertices: Vertices in space. :type vertices: (n, 3) float :param faces: Face indexes to stitch with triangle fans. :type faces: (n,) int :param insert_vertices: Allow stitching to insert new vertices? :type insert_vertices: bool
- Returns:
fan ((m, 3) int) – New triangles referencing mesh.vertices.
vertices ((p, 3) float) – Inserted vertices (only returned if insert_vertices)
- mesh_tools.trimesh_utils.close_mesh_holes_at_vertices(mesh, vertices, verbose=False, plot=False)
Purpose: To get the mesh that would stitch up a certain boundary defined by vertices
Process: 1) Run stitching with certain vertices
- mesh_tools.trimesh_utils.closest_connected_component_mesh_to_coordinates(mesh, coordinates, plot=False)[source]
- mesh_tools.trimesh_utils.closest_coordinate_to_mesh_faces(mesh, coordinates, return_closest_distance=False, verbose=False)
Given a list of coordinates will find the closest face on a mesh
- mesh_tools.trimesh_utils.closest_distance_between_meshes(original_mesh, submesh, print_flag=False, attribute_name='triangles_center')[source]
- mesh_tools.trimesh_utils.closest_face_to_coordinate(mesh, coordinate, return_face_coordinate=False)[source]
To find the closest face midpoint to a coordiante
- mesh_tools.trimesh_utils.closest_face_to_coordinates(mesh, coordinates, return_closest_distance=False, verbose=False)
Given a list of coordinates will find the closest face on a mesh
- mesh_tools.trimesh_utils.closest_mesh_attribute_to_coordinates_fast(mesh, coordinates, attribute='vertices', return_distance=False, return_attribute=False, stop_after_0_dist=True, verbose=False, verbose_time=False)[source]
- mesh_tools.trimesh_utils.closest_mesh_coordinate_to_other_mesh(mesh, other_mesh, coordinate_type='vertices', return_closest_distance=False, verbose=False, plot=False)[source]
Purpose: To find the mesh coordinate to coordinates from aother mehs
- mesh_tools.trimesh_utils.closest_mesh_distance_to_coordinates_fast(mesh, coordinates, attribute='vertices', return_attribute=False, stop_after_0_dist=True, verbose=False, verbose_time=False)[source]
- mesh_tools.trimesh_utils.closest_mesh_to_coordinate(mesh_list, coordinate, return_mesh=True, return_distance=False, distance_method='min_distance', return_idx=False, verbose=False)[source]
Purpose: Will pick the closest mesh from a list of meshes to a certain coordinate point
Pseudocode: Iterate through all of the meshes 1) Build KDTree of mesh 2) query the coordinate against mesh 3) Save the distance in array
Find the index with the smallest distance
Return the mesh or the index
** distance methods are: 1) min_distance: will measure the distance between the closest mesh face and coordinate 2) bbox_center: measure the bounding box center of the mesh to the coordinate
Ex: closest_mesh_to_coordinate(mesh_list = new_meshes, coordinate = current_endpoints[1], verbose=False, return_mesh=False, return_distance=True)
- mesh_tools.trimesh_utils.closest_mesh_to_coordinates(mesh_list, coordinates, return_mesh=True, return_distance=False, distance_method='min_distance', verbose=False)[source]
- mesh_tools.trimesh_utils.closest_mesh_to_mesh(mesh, meshes, return_closest_distance=False, return_closest_vertex_on_mesh=False, verbose=True)[source]
Purpose: To find the index of the closest mesh out of a list of meshes and the closest distance and vertex
- mesh_tools.trimesh_utils.closest_mesh_vertex_to_other_mesh(mesh, other_mesh, return_closest_distance=False, verbose=False, plot=False)[source]
- mesh_tools.trimesh_utils.closest_n_attributes_to_coordinate(mesh, coordinate, n, attribute, return_idx=False, plot=False)[source]
Ex: tu.closest_n_attributes_to_coordinate(
branch_obj.mesh, coordinate = branch_obj.mesh_center, attribute = “vertices”, n = 5, plot = True)
- mesh_tools.trimesh_utils.closest_segmentation_to_coordinate(mesh, coordinate, clusters=3, smoothness=0.2, plot_segmentation=False, plot_closest_mesh=False, return_cgal=False, verbose=False, mesh_segmentation=None, mesh_segmentation_cdfs=None)[source]
Purpose: To run a mesh segmentation and then choose the segment that is closest to a coordinate
- mesh_tools.trimesh_utils.closest_split_to_coordinate(mesh, coordinate, plot_split=False, plot_closest_mesh=False, significance_threshold=0, connectivity='faces', verbose=False)[source]
To run the mesh splitting and then to choose the closest split mesh to a coordinate
- mesh_tools.trimesh_utils.compare_meshes_by_face_midpoints(mesh1, mesh2, match_threshold=0.001, print_flag=False)[source]
- mesh_tools.trimesh_utils.compare_meshes_by_face_midpoints_list(mesh1_list, mesh2_list, **kwargs)[source]
- mesh_tools.trimesh_utils.components_to_submeshes(mesh, components, return_components=True, only_watertight=False, **kwargs)[source]
- mesh_tools.trimesh_utils.connected_components_from_face_idx(mesh, face_idx, return_meshes=True, verbose=False)[source]
Purpose: To return the face_idxs of the connected components for mesh defined by face idx
- mesh_tools.trimesh_utils.connected_components_from_mesh(mesh, return_face_idx_map=False, plot=False, **kwargs)[source]
- mesh_tools.trimesh_utils.connected_nondegenerate_mesh(mesh, return_kept_faces_idx=False, return_removed_faces_idx=False, connectivity='edges', plot=False, verbose=False)[source]
Purpose: To convert a mesh to a connected non-degenerate mesh
Pseuducode: 1) Find all the non-degnerate faces indices 2) Split the mesh with the connectivity and returns the largest mesh 3) Return a submesh of all non degenerate faces and the split meshes
- mesh_tools.trimesh_utils.convert_meshes_to_face_idxes(mesh_list, main_mesh, exact_match=True, original_mesh_kd=None)[source]
Purpose: Will convert a list of
- mesh_tools.trimesh_utils.coordinate_on_mesh_mesh_border(mesh, mesh_border=None, overlapping_vertices=None, mesh_border_minus_meshes=None, meshes_to_minus=None, coordinate_method='first_coordinate', verbose=False, verbose_time=False, return_winning_coordinate_group=False, plot=True)[source]
Purpose: to find a coordinate on the border between 2 meshes
New method: 1) Finds the overlapping vertices, if none then find the closest one 2) Find the border vertices groups 3a) If no border vertices then just picks overlapping vertices as winning group 3b) Picks the border vertices groups which has a closest overall distance to any of border vertices
- mesh_tools.trimesh_utils.coordinates_to_enclosing_sphere_center_radius(coordinates, verbose=False)[source]
Purpose: to get the volume that would be needed to encapsulate a set of points
Pseudocode: 1) get the mean of the points 2) Return the max distance from the mean
- mesh_tools.trimesh_utils.coordinates_to_enclosing_sphere_volume(coordinates, verbose=False)[source]
Purpose: to get the volume that would be needed to encapsulate a set of points
Pseudocode: 1) get the mean of the points 2) Return the max distance from the mean
- mesh_tools.trimesh_utils.decimate(mesh, decimation_ratio=0.25, output_folder='./temp', delete_temp_files=True, name=None, verbose=False)[source]
- mesh_tools.trimesh_utils.default_mesh_stats_to_run(ray_trace_perc_options=(30, 50, 70, 90, 95), interpercentile_options=(30, 50, 70, 90, 95), center_to_width_ratio_options=(30, 50, 70, 90, 95))[source]
- mesh_tools.trimesh_utils.divide_mesh_into_connected_components(mesh, return_face_idx_map=False, plot=False, **kwargs)
- mesh_tools.trimesh_utils.export_mesh(current_mesh, main_mesh_path)
- mesh_tools.trimesh_utils.face_idx_map_from_face_idx_list(face_idx_list, mesh=None, n_faces=None, default_value=-1)[source]
Purpose: To turn a list of face idx into an overall face idx mapping to each component
- mesh_tools.trimesh_utils.face_midpoints_x_z(mesh)[source]
Application: Can just analyze the data that is not going top to bottom in microns volume
- mesh_tools.trimesh_utils.face_neighbors_by_vertices(mesh, faces_list, concatenate_unique_list=True)[source]
Find the neighbors of face where neighbors are faces that touch the same vertices
Pseudocode: 1) Change the faces to vertices 2) Find all the faces associated with the vertices
- mesh_tools.trimesh_utils.faces_closer_than_distance_of_coordinates(mesh, coordinate, distance_threshold, verbose=False, closer_than=True, return_mesh=False, plot=False)[source]
Purpose: To get the faces of a mesh that are within a certain distance of a coordinate/coordinates
Pseudocode: 1) Build KDTree from coordinates 2) Query the mesh triangles against KDTree 3) Find the meshes that are closer/farther than threshold 4) Return face ids (or submesh)
Ex:
rest_mesh = tu.faces_closer_than_distance_of_coordinates( mesh = branch.mesh, coordinate = curr_point, distance_threshold = 5_000, verbose = True, return_mesh=True )
ipvu.plot_objects(rest_mesh)
- mesh_tools.trimesh_utils.faces_defined_by_vertices_idx_list_to_mesh(mesh, faces, vertices=None, plot=False, verbose=False)[source]
Purpose: To create a mesh from an (n,3) array representing new faces
- mesh_tools.trimesh_utils.faces_farther_than_distance_of_coordinates(mesh, coordinate, distance_threshold, verbose=False, return_mesh=False, plot=False)[source]
- mesh_tools.trimesh_utils.farthest_coordinate_to_faces(mesh, coordinates, return_distance=False, verbose=False, plot=False)[source]
To find the coordinate who has the farthest closest distance to a mesh
Ex: import neurd
branch_obj = neuron_obj[0][0] neurd.plot_branch_spines(branch_obj) farthest_coord = farthest_coordinate_to_faces(
branch_obj.mesh, branch_obj.skeleton, return_distance = False, verbose = True
)
ipvu.plot_objects( branch_obj.mesh,
branch_obj.skeleton, scatters=[farthest_coord], scatter_size=2
)
- mesh_tools.trimesh_utils.farthest_face_to_coordinate(mesh, coordinate, return_face_coordinate=False)[source]
To find the closest face midpoint to a coordiante
- mesh_tools.trimesh_utils.fill_mesh_holes(mesh, plot=False, verbose=False, in_place=False, try_with_fill_single_hole_fill_for_backup=True, **kwargs)
- mesh_tools.trimesh_utils.fill_mesh_holes_with_fan(mesh, plot=False, verbose=False, in_place=False, try_with_fill_single_hole_fill_for_backup=True, **kwargs)[source]
- mesh_tools.trimesh_utils.filter_away_border_touching_submeshes_by_group(mesh, submesh_list, border_percentage_threshold=0.3, inverse_border_percentage_threshold=0.9, verbose=False, return_meshes=True)[source]
Purpose: Will return submeshes or indices that do not touch a border edge of the parenet mesh
Pseudocode: 1) Get the border vertices of mesh grouped 2) For each submesh
Find which border group the vertices overlap with (0 distances)
For each group that it is touching i) Find the number of overlap ii) if the percentage is greater than threshold then nullify
Ex:
- return_value = filter_away_border_touching_submeshes(
mesh = eraser_branch.mesh, submesh_list = eraser_branch.spines, verbose = True, return_meshes=True)
- sk.graph_skeleton_and_mesh(main_mesh_verts=mesh.vertices,
- main_mesh_faces=mesh.faces,
- other_meshes=eraser_branch.spines,
other_meshes_colors=”red”)
- sk.graph_skeleton_and_mesh(main_mesh_verts=mesh.vertices,
- main_mesh_faces=mesh.faces,
- other_meshes=return_value,
other_meshes_colors=”red”)
Ex 2: tu = reload(tu) tu.filter_away_border_touching_submeshes_by_group(
mesh=curr_branch.mesh, submesh_list=curr_branch.spines
)
- mesh_tools.trimesh_utils.filter_away_connected_comp_in_face_idx_with_minimum_vertex_distance_to_coordinates(mesh, face_idx, coordinates, min_distance_threshold=0.0001, verbose=False, return_meshes=False, plot=True)[source]
Purpose: To filter away any connected components that have a vertices touching at least one coordinate
- mesh_tools.trimesh_utils.filter_away_inside_meshes(mesh_list, distance_type='shortest_vertex_distance', distance_threshold=2000, inside_percentage_threshold=0.15, verbose=False, return_meshes=False, max_mesh_sized_filtered_away=inf)[source]
Purpose: To filter out any meshes that are inside of another mesh in the list
Get all the pairings of meshes to check and the distances between
Find the order of pairs to check 1st by distance
Create a viable meshes index list with initially all indexes present
For each pair (in the order pre-determined in 2) a) If both indexes are not in the viable meshes list –> continue b) check the percentage that each is inside of the other c) Get the ones that are above a threshold d1) If none are above threshold then continue d2) If one is above threshold then that is the losing index d3) If both are above the threshold then pick the smallest one as the losing index e) remove the losing index from the viable meshes index list
Return either the viables meshes indexes or the meshes themselves
- mesh_tools.trimesh_utils.filter_meshes_by_bounding_box_longest_side(meshes, side_length_threshold)[source]
- mesh_tools.trimesh_utils.filter_meshes_by_containing_coordinates(mesh_list, nullifying_points, filter_away=True, method='distance', distance_threshold=2000, verbose=False, return_indices=False)[source]
Purpose: Will either filter away or keep meshes from a list of meshes based on points based to the function
Application: Can filter away spines that are too close to the endpoints of skeletons
Ex: import trimesh from datasci_tools import numpy_dep as np tu = reload(tu)
curr_limb = recovered_neuron[2] curr_limb_end_coords = find_skeleton_endpoint_coordinates(curr_limb.skeleton)
kept_spines = []
- for curr_branch in curr_limb:
#a) get the spines curr_spines = curr_branch.spines
#For each spine: if not curr_spines is None:
- curr_kept_spines = tu.filter_meshes_by_bbox_containing_coordinates(curr_spines,
curr_limb_end_coords)
print(f”curr_kept_spines = {curr_kept_spines}”) kept_spines += curr_kept_spines
ipvu.plot_objects(meshes=kept_spines)
- mesh_tools.trimesh_utils.filter_meshes_by_size(mesh_list, size_threshold, size_type='faces', above_threshold=True, return_indices=False, verbose=False, **kwargs)[source]
Purpose: Will return the meshes or indices of the meshes that are above (or below if abvoe threshold set to False) the vertices or faces threshold
Pseudocode: 1) Calculate the sizes of the meshes based on the threshold set 2) Find the indices of the meshes that are above or below the threshold 3) Return either the meshes or indices
Ex: tu.filter_meshes_by_size(mesh_list=new_meshes,
faces_threshold = None,
- vertices_threshold=100,
above_threshold=True, return_indices = True,
verbose=False)
- mesh_tools.trimesh_utils.filter_meshes_by_size_min_max(mesh_list, min_size_threshold, max_size_threshold, size_type='faces', return_indices=False, verbose=False)[source]
- mesh_tools.trimesh_utils.filter_vertices_by_mesh(mesh, vertices)[source]
Purpose: To restrict the vertices to those that only lie on a mesh
- mesh_tools.trimesh_utils.find_border_face_groups(mesh, return_sizes=False)[source]
Will return all borders as faces and grouped together
- mesh_tools.trimesh_utils.find_border_vertex_groups(mesh, return_coordinates=False, return_cycles=False, return_sizes=False, verbose=False, plot=False)[source]
Will return all borders as faces and grouped together
- mesh_tools.trimesh_utils.find_closest_coordinate_to_mesh_faces(mesh, coordinates, return_closest_distance=False, verbose=False)[source]
Given a list of coordinates will find the closest face on a mesh
- mesh_tools.trimesh_utils.find_closest_face_to_coordinates(mesh, coordinates, return_closest_distance=False, verbose=False)[source]
Given a list of coordinates will find the closest face on a mesh
- mesh_tools.trimesh_utils.find_large_dense_submesh(mesh, glia_pieces=None, verbose=True, large_dense_size_threshold=400000, large_mesh_cancellation_distance=3000, filter_away_floating_pieces=True, bbox_filter_away_ratio=1.7, connectivity='vertices', floating_piece_size_threshold=130000, remove_large_dense_submesh=True)[source]
Purpose: Getting all of the points close to glia and removing them
Get the inner glia mesh
Build a KDTree fo the inner glia
Query the faces of the remainingn mesh
Get all the faces beyond a certain distance and get the submesh
Filter away all floating pieces in a certain region of the bounding box
- mesh_tools.trimesh_utils.interpercentile_range_face_midpoints(mesh, percentage=70, verbose=False)[source]
purpose: To find the
- mesh_tools.trimesh_utils.interpercentile_range_face_midpoints_volume(mesh, percentage=70, verbose=False)[source]
- mesh_tools.trimesh_utils.interpercentile_range_face_midpoints_x(mesh, percentage=70, verbose=False)[source]
- mesh_tools.trimesh_utils.interpercentile_range_face_midpoints_y(mesh, percentage=70, verbose=False)[source]
- mesh_tools.trimesh_utils.interpercentile_range_face_midpoints_z(mesh, percentage=70, verbose=False)[source]
- mesh_tools.trimesh_utils.ipr_first_second_largest_eigenvector_xz_diff(mesh, percentage=70, verbose=False)[source]
- mesh_tools.trimesh_utils.ipr_second_largest_eigenvector_xz(mesh, percentage=70, verbose=False)[source]
- mesh_tools.trimesh_utils.largest_border_to_coordinate(mesh, coordinate, distance_threshold=10000, plot_border_vertices=False, error_on_no_border=True, plot_winning_border=False, verbose=False)[source]
Purpose: To find the biggest border within a certain radius
Pseudocode: 1) Find all of the vertex border groups 2) Find the average vertex of these groups 3) Find the distance of these groups from the coordinate 4) Filter for those within a certain radius 5) Return the border group that is the largest
- mesh_tools.trimesh_utils.largest_conn_comp(mesh, connectivity='vertices', return_face_indices=False)[source]
Purpose: To return the largest connected component of the mesh
- mesh_tools.trimesh_utils.largest_hole_length(mesh, euclidean_length=True)[source]
Will find either the vertex count or the euclidean distance of the largest hole in a mesh
- mesh_tools.trimesh_utils.load_mesh_no_processing(current_mesh_file)[source]
will load a mesh from .off file format
- mesh_tools.trimesh_utils.load_mesh_no_processing_h5(current_mesh_file)[source]
Will load a mesh from h5py file format
- mesh_tools.trimesh_utils.max_distance_betwee_mesh_vertices(mesh_1, mesh_2, verbose=False, max_distance_threshold=None)[source]
Purpose: Will calculate the maximum distance between vertices of two meshes
Application: Can be used to see how well a poisson reconstruction estimate of a soma and the actual soma that was backtracked to the mesh are in order to identify true somas and not get fooled by the glia / neural error checks
Pseudocode: 1) Make a KDTree from the new backtracked soma 2) Do a query of the poisson soma vertices 3) If a certain distance is too far then fail
- mesh_tools.trimesh_utils.mesh_bbox_contains_skeleton(mesh, skeleton, perc_contained_threshold=1, verbose=False)[source]
Purpose: To determine if a mesh bounding box contains all the points of a skeleton
- mesh_tools.trimesh_utils.mesh_center_weighted_face_midpoints(mesh)[source]
Purpose: calculate a mesh center point
Pseudocode: a) get the face midpoints b) get the surface area of all of the faces and total surface area c) multiply the surface area percentage by the midpoints d) sum up the products
- mesh_tools.trimesh_utils.mesh_centered_at_origin(mesh)[source]
To move a mesh to where the mesh center is at the origina
- mesh_tools.trimesh_utils.mesh_face_graph_by_vertex(mesh)[source]
Create a connectivity graph based on the faces that touch the same vertex have a connection edge
- mesh_tools.trimesh_utils.mesh_interior(mesh, return_interior=True, quality_max=0.1, try_hole_close=True, max_hole_size=10000, self_itersect_faces=False, verbose=True, **kwargs)[source]
- mesh_tools.trimesh_utils.mesh_list_connectivity(meshes, main_mesh, connectivity='edges', pairs_to_test=None, min_common_vertices=1, weighted_edges=False, return_vertex_connection_groups=False, return_largest_vertex_connection_group=False, return_connected_components=False, print_flag=False, verbose=False)[source]
Pseudocode: 1) Build an edge list 2) Use the edgelist to find connected components
Arguments: - meshes (list of trimesh.Trimesh) # - retrun_vertex_connection_groups (bool): whether to return the touching vertices
- mesh_tools.trimesh_utils.mesh_list_distance_connectivity(meshes, pairs_to_test=None, max_distance=inf, return_G=False, verbose=False)[source]
Purpose: To find the distances between all meshes in a list and represent them as edges (can passback as a graph if need be)
Pseudocode: For all possile combinations of meshes (or those prescribed) 1) Find the distance between the meshes 2)
- mesh_tools.trimesh_utils.mesh_overlap_with_restriction_mesh(mesh, restriction_mesh, size_measure='faces', match_threshold=0.001, verbose=False, restriction_mesh_kd=None)[source]
Purpose: To find what percentage of a mesh matches a mesh that is restriction mesh
- mesh_tools.trimesh_utils.mesh_pieces_connectivity(main_mesh, central_piece, periphery_pieces, connectivity='edges', return_vertices=False, return_central_faces=False, return_vertices_idx=False, print_flag=False, merge_vertices=False)[source]
purpose: function that will determine if certain pieces of mesh are touching in reference to a central mesh
Pseudocde: 1) Get the original faces of the central_piece and the periphery_pieces 2) For each periphery piece, find if touching the central piece at all
get the vertices belonging to central mesh
get vertices belonging to current periphery
see if there is any overlap
2a) If yes then add to the list to return 2b) if no, don’t add to list
Example of How to use it:
- connected_mesh_pieces = mesh_pieces_connectivity(
main_mesh=current_mesh, central_piece=seperate_soma_meshes[0], periphery_pieces = sig_non_soma_pieces)
print(f”connected_mesh_pieces = {connected_mesh_pieces}”)
Application: For finding connectivity to the somas
Example: How to use merge vertices option import time
start_time = time.time()
#0) Getting the Soma border
tu = reload(tu) new_mesh = tu.combine_meshes(touching_limbs_meshes + [curr_soma_mesh])
soma_idx = 1 curr_soma_mesh = current_neuron[nru.soma_label(soma_idx)].mesh touching_limbs = current_neuron.get_limbs_touching_soma(soma_idx) touching_limb_objs = [current_neuron[k] for k in touching_limbs]
touching_limbs_meshes = [k.mesh for k in touching_limb_objs] touching_pieces,touching_vertices = tu.mesh_pieces_connectivity(main_mesh=new_mesh,
central_piece = curr_soma_mesh, periphery_pieces = touching_limbs_meshes,
return_vertices=True,
- return_central_faces=False,
print_flag=False, merge_vertices=True,
)
limb_to_soma_border = dict([(k,v) for k,v in zip(np.array(touching_limbs)[touching_pieces],touching_vertices)]) limb_to_soma_border
print(time.time() - start_time)
- mesh_tools.trimesh_utils.mesh_segmentation(mesh, filepath=None, clusters=2, smoothness=0.2, cgal_folder=PosixPath('.'), return_sdf=True, delete_temp_files=True, return_meshes=True, check_connect_comp=True, return_ordered_by_size=True, verbose=False, connectivity='vertices', min_n_faces_conn_comp=40, plot_segmentation=False, plot_buffer=0, return_mesh_idx=False)[source]
Function tha segments the mesh and then either returns: 1) Face indexes of different mesh segments 2) The cut up mesh into different mesh segments 3) Can optionally return the sdf values of the different mesh
Example: tu = reload(tu)
- meshes_split,meshes_split_sdf = tu.mesh_segmentation(
mesh = real_soma
)
- mesh_tools.trimesh_utils.mesh_segmentation_from_skeleton(mesh, skeleton, skeleton_segment_width=0.01, initial_distance_threshold=0.2, skeletal_buffer=0.01, backup_distance_threshold=0.4, backup_skeletal_buffer=0.02, plot_correspondence_first_pass=False, plot=False)[source]
Purpose: To turn a skeleton into a mesh correspondence dictionary
Divide up skeleton
Find mesh that corresponds to branches
3) Refines the correspondence so only 1 skeletal branch matched to each face
- mesh_tools.trimesh_utils.mesh_segmentation_largest_conn_comp(mesh=None, filepath=None, clusters=2, smoothness=0.2, cgal_folder=PosixPath('.'), return_sdf=True, delete_temp_files=True, return_meshes=True, check_connect_comp=True, return_ordered_by_size=True, verbose=False, plot_segmentation=False, return_mesh_idx=False)[source]
Function tha segments the mesh and then either returns: 1) Face indexes of different mesh segments 2) The cut up mesh into different mesh segments 3) Can optionally return the sdf values of the different mesh
Example: tu = reload(tu)
- meshes_split,meshes_split_sdf = tu.mesh_segmentation(
mesh = real_soma
)
- mesh_tools.trimesh_utils.mesh_size(mesh, size_type='faces', percentile=70, replace_zero_values_with_center_distance=True)[source]
Purpose: Will return the size of a mesh based on the size type (vertices or faces)
- mesh_tools.trimesh_utils.mesh_vertex_graph(mesh)[source]
Purpose: Creates a weighted connectivity graph from the vertices and edges
- mesh_tools.trimesh_utils.mesh_volume(mesh, watertight_method='fill_mesh_holes_with_fan', return_closed_mesh=False, zero_out_not_closed_meshes=True, poisson_obj=None, fill_holes_obj=None, convex_hole_backup=True, verbose=False, default_volume_for_too_small_meshes=0, allow_if_no_border_verts=True)[source]
- mesh_tools.trimesh_utils.mesh_volume_ratio(mesh, bbox_type='bounding_box_oriented', verbose=False)[source]
compute the ratio of the bounding box to the volume of the mesh
- mesh_tools.trimesh_utils.mesh_with_ends_cutoff(mesh, n_iterations=5, return_largest_mesh=True, significance_threshold=100, verbose=False)[source]
Purpose: Will return a mesh with the ends with a border that are cut off by finding the border, expanding the border and then removing these faces and returning the largest piece
Pseudocode: 1) Expand he border meshes 2) Get a submesh without the border faces 3) Split the mesh into significants pieces 3b) Error if did not find any significant meshes 4) If return largest mesh is True, only return the top one
- mesh_tools.trimesh_utils.meshes_distance_matrix(mesh_list, distance_type='shortest_vertex_distance', verbose=False)[source]
Purpose: To determine the pairwise distance between meshes
- mesh_tools.trimesh_utils.meshes_within_close_proximity(mesh_list, distance_type='shortest_vertex_distance', distance_threshold=20000, return_distances=True, verbose=False)[source]
Purpose: To Get the meshes that are within close proximity of each other as defined by mesh centers or the absolute shortest vertex distance
- mesh_tools.trimesh_utils.min_cut_to_partition_mesh_vertices(mesh, source_coordinates, sink_coordinates, plot_source_sink_vertices=False, plot_cut_vertices=False, verbose=False, return_edge_midpoint=True)[source]
Purpose: To find the vertex points would need to cut to seperate groups of points on a mesh (or close to a mesh) so that the groups are on seperate component meshes
Pseudocode: 1) Create a kdtree of the mesh vertices and map the source/sink coordinate to vertex indices on the mesh 2) Get the mesh adjacency graph 3) Find the min_cut edges on the adajacency graph, if None return None 4) Return either vertex coordinates of all nodes in edges, or the coordinate of the middle point in the edges
Ex: source_coordinates = [skeleton_offset_points[0],skeleton_offset_points[1]] sink_coordinates = [skeleton_offset_points[2],skeleton_offset_points[3]] curr_output = tu.min_cut_to_partition_mesh_vertices(mesh_inter,
source_coordinates, sink_coordinates,
plot_source_sink_vertices= True,
verbose = True, return_edge_midpoint = True,
plot_cut_vertices = True)
curr_output
- mesh_tools.trimesh_utils.n_vertices_inside_mesh_bbox(mesh, mesh_for_bbox, return_inside=True, bbox_multiply_ratio=1, return_percentage=False, verbose=False)[source]
Purpose: to return the number of faces within the bounding box of another face
- mesh_tools.trimesh_utils.n_vertices_outside_mesh_bbox(mesh, mesh_for_bbox, bbox_multiply_ratio=1, return_percentage=False, verbose=False)[source]
- mesh_tools.trimesh_utils.original_mesh_faces_map(original_mesh, submesh, matching=True, print_flag=False, match_threshold=0.001, exact_match=False, error_for_exact_match=True, return_mesh=False, original_mesh_kdtree=None)[source]
PUrpose: Given a base mesh and mesh that was a submesh of that base mesh - find the original face indices of the submesh
Pseudocode: 0) calculate the face midpoints of each of the faces for original and submesh 1) Put the base mesh face midpoints into a KDTree 2) Query the fae midpoints of submesh against KDTree 3) Only keep those that correspond to the faces or do not correspond to the faces based on the parameter setting
Can be inversed so can find the mapping of all the faces that not match a mesh
- mesh_tools.trimesh_utils.original_mesh_vertices_map(original_mesh, submesh=None, vertices_coordinates=None, matching=True, match_threshold=0.001, print_flag=False)[source]
- Purpose: Given an original_mesh and either a
submesh
list of vertices coordinates
Find the indices of the original vertices in the original mesh
Pseudocode: 1) Get vertices to map to original 2) Construct a KDTree of the original mesh vertices 3) query the closest vertices on the original mesh
- mesh_tools.trimesh_utils.overlapping_attribute(mesh1, mesh2, attribute_name, verbose=False, return_idx=False)[source]
Will return the attributes that are overlapping for 2 meshes
if return idx will return idx of first
- mesh_tools.trimesh_utils.overlapping_vertices(mesh1, mesh2, verbose=False, return_idx=False)[source]
- mesh_tools.trimesh_utils.overlapping_vertices_from_face_lists(mesh, face_lists, return_idx=False)[source]
Purpose: To find the vertices shared between two arrays of face_idxs
Pseudocode: 1) Find the intersection of the vertices (after indexing faces into vertices array) 2) Index the vertices intersection into verteices
- mesh_tools.trimesh_utils.percentage_vertices_inside(main_mesh, test_mesh, n_sample_points=1000, use_convex_hull=True, verbose=False)[source]
Purpose: Function that will determine the percentage of vertices of one mesh being inside of another
Ex: tu.percentage_vertices_inside(
main_mesh = soma_meshes[3], test_mesh = soma_meshes[1], n_sample_points = 1000, use_convex_hull = True, verbose = True)
- mesh_tools.trimesh_utils.poisson_surface_reconstruction(mesh, output_folder='./temp', delete_temp_files=True, name=None, verbose=False, return_largest_mesh=False, return_significant_meshes=False, significant_mesh_threshold=1000, manifold_clean=True)[source]
Access to the meshlab poisson surface reconstruction. This will attempt to create a manifold and watertight mesh using a shrinkwrapping mehtod on the outside of the current mesh
Applications: 1) Turn mesh watertight 2) Turn mesh manifold
- mesh_tools.trimesh_utils.pymeshfix_clean(mesh, joincomp=True, remove_smallest_components=False, verbose=False)[source]
Purpose: Will apply the pymeshfix algorithm to clean the mesh
Application: Can help with soma identificaiton because otherwise nucleus could interfere with the segmentation
- mesh_tools.trimesh_utils.query_meshes_from_restrictions(meshes, query, stats_df=None, print_stats_df=False, functions=None, return_idx=False, verbose=False, plot=False)[source]
Purposse: To query a list of meshes using a query string or list of conditions and functions computed on the meshes
- mesh_tools.trimesh_utils.query_meshes_from_stats(meshes, query, stats_df=None, print_stats_df=False, functions=None, return_idx=False, verbose=False, plot=False)
Purposse: To query a list of meshes using a query string or list of conditions and functions computed on the meshes
- mesh_tools.trimesh_utils.radius_sphere_from_volume(volume)[source]
Purpose: To calculate the radius from the volume assuming spherical
V = (4/3)*np.pi*(r**3) (V*(3/(4*np.pi)))**(1/3)
- mesh_tools.trimesh_utils.ratio_closest_face_to_mesh_center_distance_to_width(mesh, width_func_name='ray_trace_percentile', percentile=70, verbose=False)[source]
- mesh_tools.trimesh_utils.ratio_farthest_face_to_mesh_center_distance_to_width(mesh, width_func_name='ray_trace_percentile', percentile=70, verbose=False)[source]
- mesh_tools.trimesh_utils.ratio_ipr_eigenvector_xz_diff_to_width(mesh, width_func_name='ray_trace_percentile', percentile=70, verbose=False, **kwargs)[source]
- mesh_tools.trimesh_utils.ratio_mesh_stat_to_width(mesh, mesh_stat_function, width_func_name='ray_trace_percentile', percentile=70, verbose=False, closest_distance=None, **kwargs)[source]
Purpose: To measure the ratio between the distance between the closest mesh face and the overall width of the mesh
- mesh_tools.trimesh_utils.ray_trace_distance(mesh, face_inds=None, vertex_inds=None, ray_origins=None, ray_directions=None, max_iter=10, rand_jitter=0.001, verbose=False, ray_inter=None, replace_zero_values_with_center_distance=False, debug=False)[source]
Purpose: To calculate the distance from a vertex or face midpoint to an intersecting side of the mesh - To help with width calculations
Pseudocode: 1) Get the ray origins and directions 2) Create a mask that tells which ray origins we have calculated a valid width for and an array to store the widths (start as -1) 3) Start an iteration loop that will only stop when have a valid width for all origin points
a. get the indices of which points we don’t have valid sdfs for and restrict the run to only those b. Add some gaussian noise to the normals of these rays c. Run the ray intersection to get the (multiple=False)
locations of intersections (mx3)
index_ray responsible for that intersection (m,)
mesh face that was intersected (m,)
Update the width array for the origins that returned a valid width (using the diagonal_dot instead of linalg.norm because faster )
Update the mask that shows which ray_origins have yet to be processed
Return the width array
- mesh_tools.trimesh_utils.ray_trace_distance_by_mesh_center_dist(mesh)[source]
Purpose: In case the ray trace comes back with an error or a measure of 0
Pseudocode: 1) get the center of the mesh 2) Find the distance of all face midpoints to mesh center
- mesh_tools.trimesh_utils.remove_mesh_interior(mesh, inside_pieces=None, size_threshold_to_remove=700, quality_max=0.1, verbose=True, return_removed_pieces=False, connectivity='vertices', try_hole_close=True, return_face_indices=False, **kwargs)[source]
Will remove interior faces of a mesh with a certain significant size
- mesh_tools.trimesh_utils.restrict_mesh_list_by_mesh(mesh_list, restriction_mesh, percentage_threshold=0.6, size_measure='faces', match_threshold=0.001, verbose=False, return_meshes=False, return_under_threshold=False)[source]
Purplse: To restrict a mesh list by the
- mesh_tools.trimesh_utils.restrict_meshes_from_stats(meshes, query, stats_df=None, print_stats_df=False, functions=None, return_idx=False, verbose=False, plot=False)
Purposse: To query a list of meshes using a query string or list of conditions and functions computed on the meshes
- mesh_tools.trimesh_utils.scatter_mesh_with_radius(array, radius)[source]
Purpose: to generate a mesh of spheres at certain coordinates with certain size
Given two sets of faces, find the edges which are in both sets of faces. :param faces_a: Array of faces :type faces_a: (n, 3) int :param faces_b: Array of faces :type faces_b: (m, 3) int
- Returns:
shared – Edges shared between faces
- Return type:
(p, 2) int
Pseudocode: 1) Get the unique edges of each of the faces
- mesh_tools.trimesh_utils.skeleton_and_mesh_segmentation(mesh=None, filepath=None, skeleton_kwargs=None, skeleton_function=None, plot_skeleton=False, segmentation_kwargs=None, plot_segmentation=False, verbose=False)[source]
Note: the default parameters for this skeletonization and segmentaiton are reverted to the original cgal default parameters so that smaller meshes will have a good skeletonization and segmentaiton
- tu.skeleton_and_mesh_segmentation(
filepath = “./elephant.off”, plot_segmentation = True,
)
- mesh_tools.trimesh_utils.skeleton_non_branching_from_mesh(mesh, plot=False)[source]
Purpose: To generate surface skeletons
- mesh_tools.trimesh_utils.skeleton_to_mesh_correspondence(mesh, skeletons, remove_inside_pieces_threshold=100, return_meshes=True, distance_by_mesh_center=True, connectivity='edges', verbose=False)[source]
Purpose: To get the first pass mesh correspondence of a skeleton or list of skeletons in reference to a mesh
Pseudocode: 1) If requested, remove the interior of the mesh (if this is set then can’t return indices) - if return indices is set then error if interior also set 2) for each skeleton:
Run the mesh correspondence adaptive function
check to see if got any output (if did not then return empty list or empty mesh)
If did add a submesh or indices to the return list
Example: return_value = tu.skeleton_to_mesh_correspondence( mesh = debug_mesh,
skeletons = viable_end_node_skeletons
)
- ipvu.plot_objects(meshes=return_value,
meshes_colors=”random”, skeletons=viable_end_node_skeletons,
skeletons_colors=”random”)
- mesh_tools.trimesh_utils.sort_meshes_largest_to_smallest(meshes, sort_attribute='faces', return_idx=False)[source]
- mesh_tools.trimesh_utils.split(mesh, only_watertight=False, adjacency=None, engine=None, return_components=True, return_face_idx_map=False, connectivity='vertices', return_mesh_list=False, **kwargs)[source]
Split a mesh into multiple meshes from face connectivity. If only_watertight is true it will only return watertight meshes and will attempt to repair single triangle or quad holes. :param mesh: :type mesh: trimesh.Trimesh :param only_watertight: Only return watertight components :type only_watertight: bool :param adjacency: Face adjacency to override full mesh :type adjacency: (n, 2) int :param engine: Which graph engine to use :type engine: str or None
- Returns:
meshes ((m,) trimesh.Trimesh) – Results of splitting
—————-**** THIS VERSION HAS BEEN ALTERED TO PASS BACK THE COMPONENTS INDICES TOO ****——————*
if return_components=True then will return an array of arrays that contain face indexes for all the submeshes split off
Ex
tu.split(elephant_and_box)
meshes = array([<trimesh.Trimesh(vertices.shape=(2775, 3), faces.shape=(5558, 3))>, – <trimesh.Trimesh(vertices.shape=(8, 3), faces.shape=(12, 3))>], dtype=object)
components = array([array([ 0, 3710, 3709, …, 1848, 1847, 1855]), – array([5567, 5566, 5565, 5564, 5563, 5559, 5561, 5560, 5558, 5568, 5562, 5569])], dtype=object)
- mesh_tools.trimesh_utils.split_by_vertices(mesh, return_components=False, return_face_idx_map=False, verbose=False)[source]
- mesh_tools.trimesh_utils.split_mesh_by_closest_skeleton(mesh, skeletons, return_meshes=False)[source]
Pseudocode: For each N branch: 1) Build a KDTree of the skeleton 2) query the mesh against the skeleton, get distances
Concatenate all the distances and turn into (DxN) matrix
Find the argmin of each row and that is the assignment
- mesh_tools.trimesh_utils.split_mesh_into_face_groups(base_mesh, face_mapping, return_idx=True, check_connect_comp=True, return_dict=True, plot=False)[source]
Will split a mesh according to a face coloring of labels to split into
- mesh_tools.trimesh_utils.split_significant_pieces(new_submesh, significance_threshold=100, print_flag=False, return_insignificant_pieces=False, return_face_indices=False, return_face_map_for_indices=False, connectivity='vertices')[source]
Will split a mesh based on connectivity of edges or vertices (can return insiginifcant pieces and their face indices)
Ex: (split_meshes,split_meshes_face_idx, split_meshes_insig,split_meshes_face_idx_insig) = tu.split_significant_pieces(main_mesh_total,connectivity=”edges”,
return_insignificant_pieces=True,
return_face_indices=True)
- mesh_tools.trimesh_utils.split_significant_pieces_old(new_submesh, significance_threshold=100, print_flag=False, return_insignificant_pieces=False, connectivity='vertices')[source]
- mesh_tools.trimesh_utils.split_vertex_list_into_connected_components(vertex_indices_list, mesh, vertex_graph=None, return_coordinates=True)[source]
Purpose: Given a list of vertices (in reference to a main mesh), returns the vertices divided into connected components on the graph
Pseudocode: 1) Get the subgraph of vertices in list 2) Create a subgraph from the vertices list 3) Find the connected components of the subgraph 4) Either return the vertex coordinates or indices
- mesh_tools.trimesh_utils.split_vertex_list_into_connected_components_old(vertex_indices_list, mesh=None, vertex_graph=None, return_coordinates=True)[source]
Purpose: Given a list of vertices (in reference to a main mesh), returns the vertices divided into connected components on the graph
Pseudocode: 1) Build graph from vertex and edges of mesh 2) Create a subgraph from the vertices list 3) Find the connected components of the subgraph 4) Either return the vertex coordinates or indices
- mesh_tools.trimesh_utils.stats_df(meshes, functions, suppress_errors=True, default_value=0, plot=False, labels=None)[source]
Purpose: Given a list of meshes and a list of functions that can be applied to mesh, want to compute a dataframe that stores the output of all the functions for each mesh in rows of a dataframe
Pseudocode: For each mesh:
- For each function:
Compute the value Store in a dictionary
Create pandas dataframe
- mesh_tools.trimesh_utils.stitch(mesh, faces=None, insert_vertices=False, calculate_normals=False, vertices_to_stitch=None, return_mesh=True, return_mesh_with_holes_stitched=False, plot=False, verbose=False)[source]
Create a fan stitch over the boundary of the specified faces. If the boundary is non-convex a triangle fan is going to be extremely wonky. :param vertices: Vertices in space. :type vertices: (n, 3) float :param faces: Face indexes to stitch with triangle fans. :type faces: (n,) int :param insert_vertices: Allow stitching to insert new vertices? :type insert_vertices: bool
- Returns:
fan ((m, 3) int) – New triangles referencing mesh.vertices.
vertices ((p, 3) float) – Inserted vertices (only returned if insert_vertices)
- mesh_tools.trimesh_utils.stitch_mesh_at_vertices(mesh, vertices, verbose=False, plot=False)[source]
Purpose: To get the mesh that would stitch up a certain boundary defined by vertices
Process: 1) Run stitching with certain vertices
- mesh_tools.trimesh_utils.stitch_mesh_holes(mesh, plot=False, verbose=False, in_place=False, try_with_fill_single_hole_fill_for_backup=True, **kwargs)
- mesh_tools.trimesh_utils.subtract_mesh(original_mesh, subtract_mesh, return_mesh=True, exact_match=True, match_threshold=0.001, error_for_exact_match=True)[source]
- mesh_tools.trimesh_utils.surface_area_to_volume(mesh, surface_area_divisor=1000000, volume_divisor=1000000000, verbose=False)[source]
- mesh_tools.trimesh_utils.test_inside_meshes(main_mesh, test_meshes, n_sample_points=10, use_convex_hull=True, inside_percentage_threshold=0.9, return_outside=False, return_meshes=False, verbose=False)[source]
To determine which of the test meshes are inside the main mesh
Ex: tu.test_inside_meshes(
main_mesh = soma_meshes[3], test_meshes = soma_meshes[1], n_sample_points = 1000, use_convex_hull = True, inside_percentage_threshold=0.9, verbose = True)
- mesh_tools.trimesh_utils.translate_mesh(mesh, translation=None, new_center=None, inplace=False)[source]
- mesh_tools.trimesh_utils.two_mesh_list_connectivity(mesh_list_1, mesh_list_2, main_mesh, return_weighted_edges=True, verbose=False)[source]
Purpose: To find the connectivity between two sets of mesh lists (and possibly return the number of vertices that are connecting them)
Pseudocode: 1) Stack the somas meshes and other meshes 2) Create a connectivity pairing to test 3) Run the mesh connectivity to get the correct pairings and the weights 4) Return the edges with the weights optionally attached
**stacked meshes could be faces indices or meshes themselves
- mesh_tools.trimesh_utils.valid_coordiantes_mapped_to_mesh(mesh, coordinates, mesh_kd=None, mapping_threshold=500, original_mesh=None, original_mesh_kdtree=None, original_mesh_faces=None, return_idx=True, return_errors=False, verbose=False)[source]
Purpose: To determine if coordinates are within a certain mapping distance to a mesh (to determine if they are valid or not)
If an original mesh is specified then if a coordinate maps to the original mesh but not the main mesh specified then it is not valid
- mesh_tools.trimesh_utils.vertex_indices_subgraph(mesh, vertex_indices, plot_graph=False, verbose=False)[source]
Purpose: To return a connectivity subgraph of the vertex indices
- mesh_tools.trimesh_utils.vertices_coordinates_to_faces(mesh, vertex_coordinates, error_on_unmatches=False, concatenate_unique_list=True)[source]
- mesh_tools.trimesh_utils.vertices_coordinates_to_faces_old(current_mesh, vertex_coordinates)[source]
Purpose: If have a list of vertex coordinates, to get the face indices associated with them
Example: To check that it worked well with picking out border sk.graph_skeleton_and_mesh(other_meshes=[curr_branch.mesh,curr_branch.mesh.submesh([unique_border_faces],append=True)],
other_meshes_colors=[“red”,”black”], mesh_alpha=1)
- mesh_tools.trimesh_utils.vertices_coordinates_to_vertex_index(mesh, vertex_coordinates, error_on_unmatches=True)[source]
Purpose: To map the vertex coordinates to vertex indices
- mesh_tools.trimesh_utils.vertices_mask_inside_mesh_bbox(mesh, mesh_for_bbox, bbox_multiply_ratio=1)[source]
- mesh_tools.trimesh_utils.vertices_mask_inside_meshes_bbox(mesh, meshes_for_bbox, bbox_multiply_ratio=1)[source]
- mesh_tools.trimesh_utils.vertices_to_faces(current_mesh, vertices, concatenate_unique_list=False)[source]
Purpose: If have a list of vertex indices, to get the face indices associated with them
- mesh_tools.trimesh_utils.waterfilling_face_idx(mesh, starting_face_idx, n_iterations=10, return_submesh=False, connectivity='vertices')[source]
Will extend certain faces by infecting neighbors for a certain number of iterations:
Example: curr_border_faces = tu.find_border_faces(curr_branch.mesh) expanded_border_mesh = tu.waterfilling_face_idx(curr_branch.mesh,
- curr_border_faces,
n_iterations=10,
return_submesh=True)
- sk.graph_skeleton_and_mesh(other_meshes=[curr_branch.mesh,expanded_border_mesh],
other_meshes_colors=[“black”,”red”])
- mesh_tools.trimesh_utils.width_of_submesh(mesh, submesh=None, submesh_face_idx=None, percentile=50, verbose=False, default_value_if_empty=0, ray_inter=None)
- mesh_tools.trimesh_utils.width_ray_trace_median(mesh, percentile=50, verbose=False, default_value_if_empty=0, ray_inter=None)
- mesh_tools.trimesh_utils.width_ray_trace_perc(mesh, percentile=50, verbose=False, default_value_if_empty=0, ray_inter=None)[source]
- mesh_tools.trimesh_utils.width_ray_trace_perc_of_submesh(mesh, submesh=None, submesh_face_idx=None, percentile=50, verbose=False, default_value_if_empty=0, ray_inter=None)[source]
- mesh_tools.trimesh_utils.widths_of_submeshes(mesh, submeshes=None, default_value_if_empty=100000, ray_inter=None, submeshes_face_idx=None, percentile=50, verbose=False)[source]
Purpose: To find the widths of submeshes in relation to a larger mesh
- mesh_tools.trimesh_utils.write_h5_file(mesh=None, vertices=None, faces=None, segment_id=12345, filepath='./', filename=None, return_file_path=True)[source]
Purpose: Will write a h5 py file to store a mesh
Pseudocode: 1) Extract the vertices and the faces 2) Create the complete file path with the write extension 3) Write the .h5 file 4) return the filepath