diff --git a/morph_utils/ccf.py b/morph_utils/ccf.py index 46c7523..79ea5dd 100644 --- a/morph_utils/ccf.py +++ b/morph_utils/ccf.py @@ -289,7 +289,7 @@ def get_ccf_structure(voxel, name_map=None, annotation=None, coordinate_to_voxel return name_map[structure_id] def projection_matrix_for_swc(input_swc_file, mask_method = "tip_and_branch", - tip_count = False, annotation=None, + count_method = "node", annotation=None, annotation_path = None, volume_shape=(1320, 800, 1140), resolution=10, node_type_list=[2], resample_spacing=None): @@ -304,7 +304,8 @@ def projection_matrix_for_swc(input_swc_file, mask_method = "tip_and_branch", 'tip_and_branch' will return a projection matrix masking only structures with tip and branch nodes. If 'tip' will only look at structures with tip nodes. And last, if 'branch' will only look at structures with branch nodes. - tip_count (bool): if True, will count number of tips instead of the length of axon + count_method (str): ['node','tip','branch']. When 'node', will measure axon length by multiplying by internode spacing. + Otherwise will return the count of tip or branch nodes in each structure annotation (array, optional): 3 dimensional ccf annotation array. Defaults to None. annotation_path (str, optional): path to nrrd file to use (optional). Defaults to None. volume_shape (tuple, optional): the size in voxels of the ccf atlas (annotation volume). Defaults to (1320, 800, 1140). @@ -317,7 +318,7 @@ def projection_matrix_for_swc(input_swc_file, mask_method = "tip_and_branch", filename (str) specimen_projection_summary (dict): keys are strings of structures and values are the quantitiave projection - values. Either axon length, or number numbe of nodes depending on tip_count argument. + values. Either axon length, or number numbe of nodes depending on count_method argument. """ @@ -329,8 +330,11 @@ def projection_matrix_for_swc(input_swc_file, mask_method = "tip_and_branch", print(f"WARNING: Annotation path provided does not exist, defaulting to 10um resolution, (1320,800, 1140) ccf.\n{annotation_path}") annotation_path = None annotation = open_ccf_annotation(with_nrrd=True, annotation_path=annotation_path) - - + + if count_method not in ['node','tip','branch']: + msg = f"count_method must be 'node','tip', or 'branch'. You passed in: {count_method}" + raise ValueError(msg) + sg_df = load_structure_graph() name_map = NAME_MAP full_name_to_abbrev_dict = dict(zip(sg_df.name, sg_df.index)) @@ -358,7 +362,9 @@ def projection_matrix_for_swc(input_swc_file, mask_method = "tip_and_branch", # annotate each node if morph_df.empty: - print("Its empty") + + msg = "morph_df is empty, possibly caused by `morph_df = morph_df[morph_df['type'].isin(node_type_list)]`" + warnings.warn(msg) return input_swc_file, {} morph_df['ccf_structure'] = morph_df.apply(lambda rw: full_name_to_abbrev_dict[get_ccf_structure( np.array([rw.x, rw.y, rw.z]) , name_map, annotation, True)], axis=1) @@ -379,15 +385,12 @@ def node_ider(morph,i): # determine ipsi/contra projections morph_df["ccf_structure_sided"] = morph_df.apply(lambda row: "ipsi_{}".format(row.ccf_structure) if row.z1 and parent['id'] in seen_ids: if parent['id']!=morph.get_soma()['id']: + # this needs to happend before we add current_node to the list this_list.append(parent) - + + # now add current node, and update that we've seen curent node this_list.append(current_node) seen_ids.update([current_node['id']]) children_list = morph.get_children(current_node) if len(children_list)!=1: - irreducible_segments.append(this_list) + # if `current_node` is the immediate child of `child`, and is irreducible, + # then `this_list` will only contain `current_node` + if this_list!= [current_node]: + # otherwise, add this_list to our meta list + irreducible_segments.append(this_list) + + # refresh the list this_list = [] for ch_no in children_list: + # add the children, and the upstream irreducible parent node will be + # added using the logic from beore queue.appendleft(ch_no) @@ -227,8 +239,12 @@ def resample_morphology(morph, spacing_size): node_ct+=1 new_node_2 = copy(irr_node_2) if len(reducible_arr)==0: + # if there are no reducible nodes, the down tree reducible node + # should point to the upstream one new_node_2['parent'] = red_1_parent_id else: + # otherwise, the downstream irreducible node should point to + # the last redubile node id, which will be node_ct-1 new_node_2['parent'] = node_ct-1 new_node_2['id']=node_ct new_nodes.append(new_node_2) @@ -239,10 +255,10 @@ def resample_morphology(morph, spacing_size): node_ct+=1 - resampled_morph = Morphology(new_nodes, - parent_id_cb=lambda x:x['parent'], - node_id_cb=lambda x:x['id']) - + resampled_morph = Morphology(new_nodes, + parent_id_cb=lambda x:x['parent'], + node_id_cb=lambda x:x['id']) + return resampled_morph diff --git a/setup.cfg b/setup.cfg index 9b73ca8..c56fcad 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,5 +26,6 @@ console_scripts = morph_utils_full_morph_soma_correction = morph_utils.executable_scripts.full_morphology_soma_correction:console_script morph_utils_extract_projection_matrix = morph_utils.executable_scripts.projection_matrix_from_swc_directory:console_script morph_utils_extract_projection_matrix_single_cell = morph_utils.executable_scripts.projection_matrix_for_single_cell:console_script + morph_utils_aggregate_single_cell_projs = morph_utils.executable_scripts.aggregate_single_cell_projection_csvs:console_script morph_utils_move_somas_left_hemisphere = morph_utils.executable_scripts.move_somas_to_left_hemisphere_swc_directory:console_script morph_utils_local_crop_ccf_swcs = morph_utils.executable_scripts.local_crop_ccf_swc_directory:console_script