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:

  1. cover entire mesh

  2. 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.compartment_utils.waterfill_starting_label_to_soma_border(curr_branch_mesh, border_vertices, label_to_expand, total_face_labels, print_flag=True, n_touching_soma_threshold=10, connectivity='vertices')[source]

Purpose: To expand a certain label so it is touching the soma border vertices

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.correspondence_utils.plot_correspondence(mesh, correspondence, idx_to_show=None, submesh_from_face_idx=True, verbose=True)[source]

Purpose: Want to plot mesh correspondence first pass

Pseudocode: For each entry: 1) Plot mesh (from idx) 2) plot skeleton

mesh_tools.correspondence_utils.plot_correspondence_on_single_mesh(mesh, correspondence)[source]

Purpose: To plot the correspondence dict once a 1 to 1 was generated

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)

__init__(decimation_ratio, temp_folder, overwrite=False, **kwargs)[source]
classmethod create_decimation_script(folder_path, decimation_ratio, output_path=None, overwrite=False, **custom_filters)[source]

Actually writes the decimation script to file

static initialize_script_filters(decimation_ratio)[source]
classmethod preprocessing(decimation_ratio, temp_folder='.', **kwargs)[source]
class mesh_tools.meshlab.FillHoles(temp_folder='./temp', max_hole_size=2000, self_itersect_faces=True, overwrite=False, **kwargs)[source]

Bases: FilterBase

Class that will apply hole filling filters to meshes

__init__(temp_folder='./temp', max_hole_size=2000, self_itersect_faces=True, overwrite=False, **kwargs)[source]
initialize_script_filters(max_hole_size=2000, self_itersect_faces=False)[source]
class mesh_tools.meshlab.FilterBase(temp_folder, script_filters, name='filter', overwrite=False, **kwargs)[source]

Bases: Meshlab

Base 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)

__init__(temp_folder, script_filters, name='filter', overwrite=False, **kwargs)[source]
create_filter_script(folder_path, output_path=None, overwrite=False, **custom_filters)[source]

Actually writes the decimation script to file

preprocessing(temp_folder='.', **kwargs)[source]
class mesh_tools.meshlab.Interior(temp_folder='./temp', overwrite=False, return_interior=True, quality_max=0.1, **kwargs)[source]

Bases: FilterBase

Class that will apply hole filling filters to meshes

__init__(temp_folder='./temp', overwrite=False, return_interior=True, quality_max=0.1, **kwargs)[source]
initialize_script_filters(return_interior=True, quality_max=0.1)[source]
class mesh_tools.meshlab.Meshlab(mls_script_path)[source]

Bases: object

__init__(mls_script_path)[source]
static fetch_mesh_from_off(mesh_path)[source]
static mesh_to_off(vertices, faces, output_path=None)[source]
unique_port = None
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)

__init__(temp_folder='./temp', overwrite=False, **kwargs)[source]
classmethod create_poisson_script(folder_path, output_path=None, overwrite=False, **custom_filters)[source]

Actually writes the decimation script to file

static initialize_script_filters()[source]
classmethod preprocessing(temp_folder='.', **kwargs)[source]
class mesh_tools.meshlab.Scripter(filters=None, deepcopy=True)[source]

Bases: object

__init__(filters=None, deepcopy=True)[source]

filters is a dict, generally generated by this class

add_filters(filters)[source]

params can either be a proper dictionary or a list of dictionaries

add_params(filter_name, params)[source]
adjust_param(filter_name, param_name, new_value)[source]
static combine_dicts(list_of_dicts)[source]
static create_filter(name, params=None, deepcopy=True)[source]
static create_param(name, type, value)[source]
delete_filter(filter_name)[source]
delete_param(filter_name, param_name)[source]
property filters
property mls_buffer
set_filter(filter_name, params)[source]
set_param(filter_name, param_name, param_type, param_value)[source]
test_filters()[source]
to_file(filepath, overwrite=False, error=False)[source]
mesh_tools.meshlab.clear_meshlab_port(current_port)[source]
mesh_tools.meshlab.set_meshlab_port(current_port=None)[source]

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 of meshparty.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 on

  • max_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.meshparty_skeletonize.width_median_weighted(widths, skeletons, filter_away_zeros=True, return_value_if_empty=10000, verbose=False)[source]

Calculaes the median width

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.bbox(skeleton)[source]
mesh_tools.skeleton_utils.bbox_volume(skeleton)[source]
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_coordinate_distances_cumsum(skeleton)[source]
mesh_tools.skeleton_utils.calculate_skeletal_distance(my_skeleton)
mesh_tools.skeleton_utils.calculate_skeletal_length(my_skeleton)
mesh_tools.skeleton_utils.calculate_skeleton_distance(my_skeleton)[source]
mesh_tools.skeleton_utils.calculate_skeleton_segment_distances(my_skeleton, cumsum=True)[source]
mesh_tools.skeleton_utils.center_skeleton(skeleton, center=None)[source]
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.check_skeleton_connected_component(skeleton)[source]
mesh_tools.skeleton_utils.check_skeleton_one_component(curr_skeleton)[source]
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_graph_to_skeleton(UG)[source]
mesh_tools.skeleton_utils.convert_nodes_edges_to_skeleton(nodes, edges)[source]
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

  1. remap the indices and delete the unique rows that were not used

  1. Do everything like normal

mesh_tools.skeleton_utils.convert_skeleton_to_graph_old(staring_edges, stitch_print=False)[source]
mesh_tools.skeleton_utils.convert_skeleton_to_nodes(skeleton)[source]
mesh_tools.skeleton_utils.convert_skeleton_to_nodes_edges(skeleton, verbose=False)[source]
mesh_tools.skeleton_utils.convert_skeleton_to_nodes_edges_old(bones_array, verbose=False)[source]
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:

  1. convert skeleton to graph

  2. find all node coordinates and names

  3. Find the closest distance from coordinates to cancel points

  4. 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.empty_skeleton()[source]
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_endpoints(db, order_by_coordinate=True)[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.gram_schmidt_columns(X)[source]
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.kd_tree_from_unique_coordinates(skeleton)[source]
mesh_tools.skeleton_utils.list_len_measure(curr_list, G)[source]
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.matching_skeleton_branches_by_endpoints(branches)[source]
mesh_tools.skeleton_utils.matching_skeleton_branches_by_vertices(branches)[source]
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.node_names_and_coord_from_G(G=None, skeleton=None)[source]
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

mesh_tools.skeleton_utils.offset_skeletons_aligned_at_shared_endpoint(skeletons, offset=1000, comparison_distance=2000, min_comparison_distance=1000, verbose=True, common_endpoint=None, comparison_endpoints=None, subtract_cutoff=True)[source]

Pseudocode:

  1. Get the shared endpoint of the branches

  2. 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_mesh(mesh, color=[1.0, 0.0, 0.0, 0.2], flip_y=True)[source]
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.read_skeleton_edges_coordinates(file_path)[source]
mesh_tools.skeleton_utils.read_skeleton_verts_edges(file_path)[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 ————

  1. 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)

mesh_tools.skeleton_utils.shared_coordiantes(skeletons, return_one=False)[source]

Purpose: To return the shared coordinates by skeletons

mesh_tools.skeleton_utils.shared_endpoint(skeleton_1, skeleton_2, return_possibly_two=False)[source]

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.skeletal_distance(curr_list, G, coordinates_dict)[source]
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_connected_components(skeleton)[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_from_h5py(file, verbose=False)[source]
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:

  1. get the endpoints

  2. compare the endpoints to the first

  3. 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

  1. 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_unique_coordinates(skeleton)[source]
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)

  1. Poisson Surface Reconstruction

  2. CGAL skeletonization of all signfiicant pieces
    (if above certain size ! threshold)

    –> if not skip straight to surface skeletonization

  3. 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.skelton_coordinate_path_to_skeleton(skeleton_coordinate_path)[source]
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.stack_skeletons(sk_list, graph_cleaning=False)[source]
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.surface_skeleton(mesh, plot=False, **kwargs)[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(mesh)[source]
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_max_x(mesh, **kwargs)[source]
mesh_tools.trimesh_utils.bbox_max_y(mesh, **kwargs)[source]
mesh_tools.trimesh_utils.bbox_max_z(mesh, **kwargs)[source]
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_min_x(mesh, **kwargs)[source]
mesh_tools.trimesh_utils.bbox_min_y(mesh, **kwargs)[source]
mesh_tools.trimesh_utils.bbox_min_z(mesh, **kwargs)[source]
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.bbox_volume(mesh)[source]
mesh_tools.trimesh_utils.bbox_volume_oriented(mesh)[source]
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_corner_max(mesh, **kwargs)[source]
mesh_tools.trimesh_utils.bounding_box_corner_min(mesh, **kwargs)[source]
mesh_tools.trimesh_utils.bounding_box_corners(mesh, bbox_multiply_ratio=1, oriented=False)[source]
mesh_tools.trimesh_utils.bounding_box_longest_side(mesh)[source]
mesh_tools.trimesh_utils.bounding_box_oriented_side_lengths(mesh, return_largest_side=False)[source]
mesh_tools.trimesh_utils.box_mesh(center, radius=100)[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.center_mesh(mesh, new_center)[source]
mesh_tools.trimesh_utils.center_mesh_at_point(mesh, new_center)[source]
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_coordinate_distance(mesh, coordinate)[source]
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_face_to_mesh_center_distance(mesh)[source]
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

  1. Find the index with the smallest distance

  2. 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.combine_meshes(mesh_pieces, merge_vertices=True)[source]
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_component_meshes(mesh, verbose=False, plot=False)[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.convert_o3d_to_trimesh(mesh)[source]
mesh_tools.trimesh_utils.convert_trimesh_to_o3d(mesh)[source]
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_bounding_box(coordinates, oriented=True)[source]
mesh_tools.trimesh_utils.coordinates_to_enclosing_sphere(coordinates, verbose=False)[source]
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.empty_mesh()[source]
mesh_tools.trimesh_utils.expand_border_faces(mesh, n_iterations=10, return_submesh=True)[source]
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.face_neighbors_by_vertices_seperate(mesh, faces_list)[source]
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.farthest_face_to_coordinate_distance(mesh, coordinate)[source]
mesh_tools.trimesh_utils.farthest_face_to_mesh_center_distance(mesh)[source]
mesh_tools.trimesh_utils.fill_holes(mesh, max_hole_size=2000, self_itersect_faces=False)[source]
mesh_tools.trimesh_utils.fill_holes_trimesh(mesh)[source]
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.fill_single_triangle_holes(mesh, in_place=False)[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

  1. Find which border group the vertices overlap with (0 distances)

  2. 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

  1. Get all the pairings of meshes to check and the distances between

  2. Find the order of pairs to check 1st by distance

  3. 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

  1. 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_faces(mesh)[source]
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_border_vertices(mesh, return_coordinates=False, plot=False)[source]
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_degenerate_faces(mesh, return_nondegenerate_faces=False)[source]
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

  1. Get the inner glia mesh

  2. Build a KDTree fo the inner glia

  3. Query the faces of the remainingn mesh

  4. Get all the faces beyond a certain distance and get the submesh

  5. Filter away all floating pieces in a certain region of the bounding box

mesh_tools.trimesh_utils.find_nondegenerate_faces(mesh)[source]
mesh_tools.trimesh_utils.get_non_manifold_edges(mesh)[source]
mesh_tools.trimesh_utils.get_non_manifold_vertices(mesh)[source]
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_largest_eigenvector_xy(mesh, percentage=70, verbose=False)[source]
mesh_tools.trimesh_utils.ipr_largest_eigenvector_xz(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.is_manifold(mesh)[source]
mesh_tools.trimesh_utils.is_mesh(obj)[source]
mesh_tools.trimesh_utils.is_watertight(mesh, allow_if_no_border_verts=False)[source]
mesh_tools.trimesh_utils.kdtree_length(kdtree_obj)[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_vertex_average(mesh_list)[source]
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_from_vertices_faces(vertices, faces)[source]
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_kdtree_face(mesh)[source]
mesh_tools.trimesh_utils.mesh_kdtree_vertices(mesh)[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

  1. Divide up skeleton

  2. 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_stats(mesh, stats_dicts=None, **kwargs)[source]
mesh_tools.trimesh_utils.mesh_to_kdtree(mesh)[source]
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_convex_hull(mesh)[source]
mesh_tools.trimesh_utils.mesh_volume_o3d(mesh)[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_faces(mesh)[source]
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
  1. submesh

  2. 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_faces(mesh1, mesh2, verbose=False, return_idx=False)[source]
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.plot_segmentation(meshes, cgal_info, mesh_alpha=1)[source]
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,)

  1. Update the width array for the origins that returned a valid width (using the diagonal_dot instead of linalg.norm because faster )

  2. Update the mask that shows which ray_origins have yet to be processed

  1. 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_degenerate_faces(mesh, return_face_idxs=False)[source]
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(original_mesh, restrict_meshes, return_mesh=True)[source]
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.rotate_mesh_from_matrix(mesh, matrix)[source]
mesh_tools.trimesh_utils.scatter_mesh_with_radius(array, radius)[source]

Purpose: to generate a mesh of spheres at certain coordinates with certain size

mesh_tools.trimesh_utils.shared_edges_between_faces_on_mesh(mesh, faces_a, faces_b, return_vertices_idx=False)[source]

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.skeletal_length_from_mesh(mesh, plot=False)[source]
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:

  1. Run the mesh correspondence adaptive function

  2. check to see if got any output (if did not then return empty list or empty mesh)

  3. 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.sphere_mesh(center=[0, 0, 0], radius=100)[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

  1. Concatenate all the distances and turn into (DxN) matrix

  2. 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.submesh(mesh, face_idx, always_return_mesh=True)[source]
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(mesh)[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.turn_off_logging()[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_components(mesh)[source]
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(mesh, percentile=50)[source]
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

mesh_tools.trimesh_utils.write_neuron_off(current_mesh, main_mesh_path)[source]

Module contents