diff --git a/neurom/features/morphology.py b/neurom/features/morphology.py index 7374066f..a6255169 100644 --- a/neurom/features/morphology.py +++ b/neurom/features/morphology.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, Ecole Polytechnique Federale de Lausanne, Blue Brain Project +# Copyright (c) 2019, Ecole Polytechnique Federale de Lausanne, Blue Brain Project # All rights reserved. # # This file is part of NeuroM @@ -61,9 +61,21 @@ from neurom.utils import str_to_plane from neurom.geom import convex_hull + feature = partial(feature, namespace=NameSpace.NEURON) +def _map_neurites(function, morph, neurite_type, use_subtrees=False): + return list( + iter_neurites( + obj=morph, + mapfun=function, + filt=is_type(neurite_type), + use_subtrees=use_subtrees, + ) + ) + + @feature(shape=()) def soma_volume(morph): """Get the volume of a morphology's soma.""" @@ -89,13 +101,11 @@ def soma_radius(morph): @feature(shape=()) def max_radial_distance(morph, origin=None, neurite_type=NeuriteType.all, use_subtrees=False): """Get the maximum radial distances of the termination sections.""" - term_radial_distances = list( - iter_neurites( - morph, - mapfun=partial(nf.max_radial_distance, origin=origin), - filt=is_type(neurite_type), - use_subtrees=use_subtrees - ) + term_radial_distances = _map_neurites( + partial(nf.max_radial_distance, origin=origin), + morph, + neurite_type=neurite_type, + use_subtrees=use_subtrees, ) return max(term_radial_distances) if term_radial_distances else 0. @@ -103,53 +113,25 @@ def max_radial_distance(morph, origin=None, neurite_type=NeuriteType.all, use_su @feature(shape=(...,)) def number_of_sections_per_neurite(morph, neurite_type=NeuriteType.all, use_subtrees=False): """List of numbers of sections per neurite.""" - return list( - iter_neurites( - morph, - mapfun=nf.number_of_sections, - filt=is_type(neurite_type), - use_subtrees=use_subtrees - ) - ) + return _map_neurites(nf.number_of_sections, morph, neurite_type, use_subtrees) @feature(shape=(...,)) def total_length_per_neurite(morph, neurite_type=NeuriteType.all, use_subtrees=False): """Neurite lengths.""" - return list( - iter_neurites( - morph, - mapfun=nf.total_length, - filt=is_type(neurite_type), - use_subtrees=use_subtrees - ) - ) + return _map_neurites(nf.total_length, morph, neurite_type, use_subtrees) @feature(shape=(...,)) def total_area_per_neurite(morph, neurite_type=NeuriteType.all, use_subtrees=False): """Neurite areas.""" - return list( - iter_neurites( - morph, - mapfun=nf.total_area, - filt=is_type(neurite_type), - use_subtrees=use_subtrees - ) - ) + return _map_neurites(nf.total_area, morph, neurite_type, use_subtrees) @feature(shape=(...,)) def total_volume_per_neurite(morph, neurite_type=NeuriteType.all, use_subtrees=False): """Neurite volumes.""" - return list( - iter_neurites( - morph, - mapfun=nf.total_volume, - filt=is_type(neurite_type), - use_subtrees=use_subtrees - ) - ) + return _map_neurites(nf.total_volume, morph, neurite_type, use_subtrees) @feature(shape=(...,)) @@ -167,13 +149,7 @@ def azimuth(neurite): morphmath.vector(neurite.root_node.points[0], morph.soma.center) ) - return list( - iter_neurites( - morph, - mapfun=azimuth, - filt=is_type(neurite_type), - ) - ) + return _map_neurites(azimuth, morph, neurite_type, use_subtrees=False) @feature(shape=(...,)) @@ -192,25 +168,16 @@ def elevation(neurite): morphmath.vector(neurite.root_node.points[0], morph.soma.center) ) - return list( - iter_neurites( - morph, - mapfun=elevation, - filt=is_type(neurite_type), - ) - ) + return _map_neurites(elevation, morph, neurite_type, use_subtrees=False) @feature(shape=(...,)) def trunk_vectors(morph, neurite_type=NeuriteType.all, use_subtrees=False): """Calculate the vectors between all the trunks of the morphology and the soma center.""" - def vector_from_soma_to_root(neurite): + def vector_from_soma_to_root(neurite, *_, **__): return morphmath.vector(neurite.root_node.points[0], morph.soma.center) - return [ - vector_from_soma_to_root(n) - for n in iter_neurites(morph, filt=is_type(neurite_type), use_subtrees=use_subtrees) - ] + return _map_neurites(vector_from_soma_to_root, morph, neurite_type, use_subtrees=use_subtrees) @feature(shape=(...,)) @@ -430,7 +397,7 @@ def trunk_origin_radii( * else the mean radius of the points between the given ``min_length_filter`` and ``max_length_filter`` are returned. """ - def trunk_first_radius(neurite): + def trunk_first_radius(neurite, *_, **__): return neurite.root_node.points[0][COLS.R] if min_length_filter is not None and min_length_filter <= 0: @@ -455,7 +422,7 @@ def trunk_first_radius(neurite): "'max_length_filter' value." ) - def trunk_mean_radius(neurite): + def trunk_mean_radius(neurite, *_, **__): points = neurite.root_node.points @@ -493,40 +460,30 @@ def trunk_mean_radius(neurite): else trunk_mean_radius ) - return [ - function(neu) - for neu in iter_neurites(morph, filt=is_type(neurite_type), use_subtrees=use_subtrees) - ] + return _map_neurites(function, morph, neurite_type, use_subtrees) @feature(shape=(...,)) def trunk_section_lengths(morph, neurite_type=NeuriteType.all, use_subtrees=False): """List of lengths of trunk sections of neurites in a morph.""" - return [ - morphmath.section_length(n.root_node.points) - for n in iter_neurites(morph, filt=is_type(neurite_type), use_subtrees=use_subtrees) - ] + def trunk_section_length(neurite, *_, **__): + return morphmath.section_length(neurite.root_node.points) + + return _map_neurites(trunk_section_length, morph, neurite_type, use_subtrees) @feature(shape=()) def number_of_neurites(morph, neurite_type=NeuriteType.all, use_subtrees=False): """Number of neurites in a morph.""" - return sum( - 1 for _ in iter_neurites(morph, filt=is_type(neurite_type), use_subtrees=use_subtrees) - ) + def identity(n, *_, **__): + return n + return len(_map_neurites(identity, morph, neurite_type, use_subtrees)) @feature(shape=(...,)) def neurite_volume_density(morph, neurite_type=NeuriteType.all, use_subtrees=False): """Get volume density per neurite.""" - return list( - iter_neurites( - morph, - mapfun=nf.volume_density, - filt=is_type(neurite_type), - use_subtrees=use_subtrees - ) - ) + return _map_neurites(nf.volume_density, morph, neurite_type, use_subtrees) @feature(shape=(...,)) diff --git a/neurom/features/neurite.py b/neurom/features/neurite.py index 5e6b8bdb..fe2aff9f 100644 --- a/neurom/features/neurite.py +++ b/neurom/features/neurite.py @@ -1,4 +1,4 @@ - # Copyright (c) 2020, Ecole Polytechnique Federale de Lausanne, Blue Brain Project +# Copyright (c) 2020, Ecole Polytechnique Federale de Lausanne, Blue Brain Project # All rights reserved. # # This file is part of NeuroM @@ -50,7 +50,7 @@ from neurom import morphmath from neurom.utils import flatten from neurom.core.types import NeuriteType -from neurom.core.morphology import Section, iter_segments +from neurom.core.morphology import Section from neurom.core.dataformat import COLS from neurom.features import NameSpace, feature, bifurcation as bf, section as sf from neurom.geom import convex_hull @@ -92,9 +92,7 @@ def max_radial_distance(neurite, origin=None, section_type=NeuriteType.all): @feature(shape=()) def number_of_segments(neurite, section_type=NeuriteType.all): """Number of segments.""" - def count_segments(section): - return len(section.points) - 1 - return sum(_map_sections(count_segments, neurite, section_type=section_type)) + return sum(_map_sections(sf.number_of_segments, neurite, section_type=section_type)) @feature(shape=()) @@ -239,7 +237,7 @@ def segment_volumes(neurite, section_type=NeuriteType.all): @feature(shape=(...,)) def segment_radii(neurite, section_type=NeuriteType.all): """Arithmetic mean of the radii of the points in segments.""" - return _map_segments(sf.segment_radii, neurite, section_type=section_type) + return _map_segments(sf.segment_mean_radii, neurite, section_type=section_type) @feature(shape=(...,)) @@ -270,7 +268,7 @@ def segment_meander_angles(neurite, section_type=NeuriteType.all): @feature(shape=(..., 3)) def segment_midpoints(neurite, section_type=NeuriteType.all): """Return a list of segment mid-points.""" - return _map_segments(sf.segment_midpoints neurite, section_type=section_type) + return _map_segments(sf.segment_midpoints, neurite, section_type=section_type) @feature(shape=(...,)) @@ -302,7 +300,7 @@ def radial_distances(section): mid_pts = 0.5 * (section.points[:-1, COLS.XYZ] + section.points[1:, COLS.XYZ]) return np.linalg.norm(mid_pts - pos[COLS.XYZ], axis=1) - return _map_segments(_radial_distances, neurite, section_type=section_type) + return _map_segments(radial_distances, neurite, section_type=section_type) @feature(shape=(...,))