from datasci_tools import numpy_dep as np
[docs]def all_paths_to_leaf_nodes(
limb_obj,
verbose = False):
return xu.all_paths_to_leaf_nodes(
G = limb_obj.concept_network_directional,
verbose = verbose,
)
# ---------- statistics ----------------
default_relation_value = -1
[docs]def parent_skeletal_angle(
limb_obj,
branch_idx,
verbose = False,
default_value = None,
**kwargs):
"""
Purpose: to get the branching angle with parent
from the skeleton vectors
Pseudocode:
1) Get parent branch
2) Get parent and child vector
3) Get the angle between the two
Ex:
from neurd import limb_utils as lu
lu.parent_skeletal_angle(
branch_idx = 2,
limb_obj = neuron_obj[1],
verbose = True,
)
"""
if limb_obj[branch_idx].endpoints_upstream_downstream_idx is None:
bu.set_branches_endpoints_upstream_downstream_idx_on_limb(limb_obj)
parent_idx = nru.parent_node(limb_obj,branch_idx)
if verbose:
print(f"parent_idx = {parent_idx}")
if parent_idx is None:
return default_value
branch_obj = limb_obj[parent_idx]
branch_obj_2 = limb_obj[branch_idx]
return np.round(nu.angle_between_vectors(
branch_obj.skeleton_vector_downstream,
branch_obj_2.skeleton_vector_upstream),2
)
[docs]def relation_skeletal_angle(
limb_obj,
branch_idx,
relation,
nodes_idx = None,
default_value = None,
verbose = False,
extrema_value = None,
return_dict = True,
**kwargs
):
"""
Purpose: To find the sibling angles with all siblings
"""
#print(f"nodes_idx = {nodes_idx}")
if limb_obj[branch_idx].endpoints_upstream_downstream_idx is None:
bu.set_branches_endpoints_upstream_downstream_idx_on_limb(limb_obj)
if default_value is None:
default_value = default_relation_value
singular_flag = False
if relation == "children":
node_func = nru.children_nodes
branch_vec_func = "skeleton_vector_downstream"
elif relation == "siblings":
node_func = nru.sibling_nodes
branch_vec_func = "skeleton_vector_upstream"
else:
raise Exception("")
if nodes_idx is None:
nodes_idx = node_func(limb_obj,branch_idx)
elif not nu.is_array_like(nodes_idx):
singular_flag = True
nodes_idx = nu.convert_to_array_like(nodes_idx)
else:
pass
if nodes_idx is None or len(nodes_idx) == 0:
return default_value
sibling_idx = nu.convert_to_array_like(nodes_idx)
if verbose:
print(f"{relation}_idx= {sibling_idx}")
branch_obj = limb_obj[branch_idx]
sibling_angles = {}
for s in sibling_idx:
branch_obj_2 = limb_obj[s]
curr_angle = np.round(nu.angle_between_vectors(
getattr(branch_obj,branch_vec_func),
branch_obj_2.skeleton_vector_upstream),2
)
if verbose:
print(f"{branch_idx} and {s} angle: {curr_angle}")
sibling_angles[s] = curr_angle
if singular_flag and return_dict:
sibling_angles = sibling_angles[sibling_idx[0]]
if extrema_value:
return getattr(np,extrema_value)(list(sibling_angles.values()))
if return_dict:
return sibling_angles
else:
return list(sibling_angles.values())
[docs]def siblings_skeletal_angle(
limb_obj,
branch_idx,
sibling_idx = None,
default_value = None,
verbose = False,
**kwargs
):
return lu.relation_skeletal_angle(limb_obj,
branch_idx,
relation="siblings",
nodes_idx = sibling_idx,
default_value = default_value,
verbose = verbose,
**kwargs
)
[docs]def children_skeletal_angle(
limb_obj,
branch_idx,
nodes_idx = None,
default_value = None,
verbose = False,
**kwargs
):
return lu.relation_skeletal_angle(limb_obj,
branch_idx,
relation="children",
nodes_idx = nodes_idx,
default_value = default_value,
verbose = verbose,
**kwargs
)
[docs]def siblings_skeletal_angle_max(
limb_obj,
branch_idx,
**kwargs
):
return lu.siblings_skeletal_angle(
limb_obj,
branch_idx,
extrema_value = "max",
**kwargs
)
[docs]def siblings_skeletal_angle_min(
limb_obj,
branch_idx,
**kwargs
):
return lu.siblings_skeletal_angle(
limb_obj,
branch_idx,
extrema_value = "min",
**kwargs
)
[docs]def children_skeletal_angle_max(
limb_obj,
branch_idx,
**kwargs
):
return lu.children_skeletal_angle(
limb_obj,
branch_idx,
extrema_value = "max",
**kwargs
)
[docs]def children_skeletal_angle_min(
limb_obj,
branch_idx,
**kwargs
):
return lu.children_skeletal_angle(
limb_obj,
branch_idx,
extrema_value = "min",
**kwargs
)
[docs]def most_usptream_endpoints_of_branches_on_limb(
limb_obj,
branches_idx,
verbose = False,
plot = False,
scatter_size = 0.5,
group_by_conn_comp = True,
include_downstream_endpoint = True,
**kwargs):
"""
Purpose: To get all of the upstream endpoints of the
connected components of a list of branches
Pseudocode:
1) Get the connected components of the branches
2) For each connected component find
i) the most upstream branch
ii) the upstream coordinate for that branch
(could be a little offset of the upstream branch to prevent overal)
"""
if len(branches_idx) == 0:
return []
if group_by_conn_comp:
conn_comp = nru.connected_components_from_branches(
limb_obj,
branches=branches_idx,
)
else:
conn_comp = [[k] for k in np.unique(branches_idx)]
coordinates = []
for i,cc in enumerate(conn_comp):
if verbose:
print(f"\n--Working on conn comp {i}: # of branches{len(cc)}")
#i) the most upstream branch
upstream_branch = nru.most_upstream_branch(limb_obj,cc)
if verbose:
print(f"upstream_branch = {upstream_branch}")
#ii) the upstream coordinate for that branch
# (could be a little offset of the upstream branch to prevent overal)
try:
branch_obj = limb_obj[upstream_branch]
branch_obj.endpoint_upstream
except:
bu.set_branches_endpoints_upstream_downstream_idx_on_limb(limb_obj)
branch_obj = limb_obj[upstream_branch]
#coord = branch_obj.endpoint_upstream_with_offset
sk_coords = branch_obj.skeletal_coordinates_upstream_to_downstream
if include_downstream_endpoint:
coord = np.array(sk_coords[[1,-1]])
else:
coord = np.array(sk_coords[[1]])
if verbose:
print(f"coord = {coord}")
coordinates.append(coord)
#coordinates = np.vstack(coordinates).reshape(-1,3)
if plot:
nviz.plot_objects(
limb_obj.mesh,
meshes = [limb_obj[k].mesh for k in branches_idx],
meshes_colors = "blue",
scatters=coordinates,
scatters_colors="red",
scatter_size=scatter_size,
**kwargs)
return coordinates
[docs]def most_upstream_endpoints_of_limb_branch(
neuron_obj,
limb_branch_dict,
verbose = False,
verbose_most_upstream=False,
plot = False,
return_array = False,
group_by_conn_comp = True,
include_downstream_endpoint = True,
):
"""
Pseudocode:
Ex:
lu.most_upstream_endpoints_of_limb_branch_conn_comp(
neuron_obj,
limb_branch_dict=dict(L1=[1,2],L2=[19,16]),
verbose = False,
verbose_most_upstream=False,
plot = False,
return_array = True,
)
"""
return_limb_coords = {}
if len(limb_branch_dict) == 0:
pass
else:
for limb_name,branches in limb_branch_dict.items():
if verbose:
print(f"\n---Working on limb {limb_name}: {branches}-----")
coords = lu.most_usptream_endpoints_of_branches_on_limb(
neuron_obj[limb_name],
branches_idx=branches,
verbose = verbose_most_upstream,
plot = plot,
group_by_conn_comp = group_by_conn_comp,
include_downstream_endpoint = include_downstream_endpoint,)
return_limb_coords[limb_name] = coords
if return_array:
if len(return_limb_coords) > 0:
return np.vstack(list(return_limb_coords.values()))
else:
return np.array([])
else:
return return_limb_coords
[docs]def width_upstream(
limb_obj,
branch_idx,
verbose = False,
min_skeletal_length = 2000,
skip_low_skeletal_length_upstream = True,
default_value = 10000000):
"""
Purpoose: To get the width of the upstream segement
Pseudocode:
1) Get the parent node
2) Get the parent width
Ex:
from neurd import limb_utils as lu
lu.width_upstream(neuron_obj[1],5,verbose = True)
"""
parent_node = nru.parent_node(limb_obj,branch_idx)
if min_skeletal_length is None:
min_skeletal_length = 0
parent_sk_length = None
width = default_value
while parent_node is not None:
parent_sk_length = limb_obj[parent_node].skeletal_length
if parent_sk_length < min_skeletal_length:
parent_node = nru.parent_node(limb_obj,parent_node)
else:
break
if parent_node is not None:
width = nru.width(limb_obj[parent_node])
parent_sk_length = limb_obj[parent_node].skeletal_length
if verbose:
print(f"parent_node = {parent_node} (width = {width}, parent_sk_length = {parent_sk_length})")
return width
# ------------ automatically create limb functions out of existing functions ------
#--- from neurd_packages ---
from . import branch_utils as bu
from . import neuron_searching as ns
from . import neuron_statistics as nst
from . import neuron_utils as nru
from . import neuron_visualizations as nviz
#--- from datasci_tools ---
from datasci_tools import networkx_utils as xu
from datasci_tools import numpy_dep as np
from datasci_tools import numpy_utils as nu
from . import limb_utils as lu
ns.set_limb_functions_for_search(lu,verbose = False)
[docs]def skeletal_angles_df(neuron_obj,
functions_list=(lu.parent_skeletal_angle_limb_ns,
lu.siblings_skeletal_angle_max_limb_ns,
lu.children_skeletal_angle_max_limb_ns)
):
angles_df = nst.stats_df(neuron_obj,functions_list)
return angles_df
[docs]def root_width(limb_obj):
return limb_obj[limb_obj.current_starting_node].width_upstream
[docs]def best_feature_match_in_descendents(
limb,
branch_idx,
feature,
verbose = True,
):
child_nodes = xu.all_children_nodes(limb.concept_network,branch_idx,depth_limit = None)
child_features = [getattr(limb[c],feature) for c in child_nodes]
branch_feature = getattr(limb[branch_idx],feature)
abs_diff = [np.abs(cf - branch_feature) for cf in child_features]
min_idx = np.argmin(abs_diff)
min_child = child_nodes[min_idx]
min_child_value = child_features[min_idx]
if verbose:
print(f"child_nodes = {child_nodes}")
print(f"All children {feature} = {child_features}")
print(f"Best match of Branch {branch_idx} {feature} ({branch_feature:.2f}):")
print(f" Child Node {min_child}, {feature} = {min_child_value:.2f}")
return min_child
[docs]def root_skeleton_vector_from_soma(
neuron_obj,
limb_idx,
soma_name = "S0",
normalize = True):
limb = neuron_obj[limb_idx]
root_skeleton_vector_from_soma = limb.current_starting_coordinate - neuron_obj[soma_name].mesh_center
if normalize:
root_skeleton_vector_from_soma = root_skeleton_vector_from_soma/np.linalg.norm(root_skeleton_vector_from_soma)
return root_skeleton_vector_from_soma