Skip to content

Commit

Permalink
Cleaning up basic growers
Browse files Browse the repository at this point in the history
Clean optional orientations in grower

Change-Id: I3e20288fdb3791fe02751a7f3481ae80414ac49a
  • Loading branch information
lidakanari committed Mar 21, 2018
1 parent f6a38d2 commit 8584048
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 163 deletions.
1 change: 1 addition & 0 deletions tns/core/neuron.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __init__(self, name='Neuron'):
self.name = name
self.points = []
self.groups = [np.array([0, 1, -1])]
# The following implements the correct connectivity of sections, while growing.
self.sections = [[],]


Expand Down
145 changes: 99 additions & 46 deletions tns/core/soma.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,98 @@
class SomaGrower(object):
"""Soma class"""

def __init__(self, initial_point=(0.,0.,0.), radius=1.0):
def __init__(self, initial_point, radius=1.0):
"""TNS Soma Object
Parameters:
points: numpy array
The (x, y, z, d)-coordinates of the x-y surface trace of the soma.
"""
self.points = [[],]
self.points = []
self.radius = radius
self.center = initial_point


def interpolate(self, points, N=3):
def point_from_trunk_direction(self, phi, theta):
'''Returns the direction of the unit vector and a point
on the soma surface depending on the theta, phi angles.
theta corresponds to the angle on the x-y plane.
phi corresponds to the angle diversion on the z-axis.
'''
point_on_soma = [self.center[0] + self.radius * \
np.cos(phi) * np.sin(theta),
self.center[1] + self.radius * \
np.sin(phi) * np.sin(theta),
self.center[2] + self.radius * \
np.cos(theta)]

return point_on_soma


def orientation_from_point(self, point):
'''Returns the unit vector that corresponds to the orientation
of a point on the soma surface.
'''
point_on_soma = np.subtract(np.array(point), np.array(self.center))

return point_on_soma / np.linalg.norm(point_on_soma)


def contour_point(self, point):
'''Keeps the c-y coordinates of the input point
but replaces the third (z) coordinate with the equivalent
soma-z in order to create a contour at the soma level.
'''
return [point[0], point[1], self.center[2]]


def add_points_from_trunk_angles(self, trunk_angles, z_angles):
"""Generates a sequence of points in the circumference of
a circle of radius R, from a list of angles.
trunk_angles correspond to the angles on the x-y plane,
while z_angles correspond to the equivalent z-direction.
"""
sortIDs = np.argsort(trunk_angles)
angle_norm = 2.*np.pi / len(trunk_angles)
new_points = []

for i,theta in enumerate(np.array(trunk_angles)[sortIDs]):

phi = np.array(z_angles)[sortIDs][i]
ang = (i + 1) * angle_norm
point = self.point_from_trunk_direction(theta + ang, phi)

new_points.append(point)

self.points = self.points + new_points

return new_points


def add_points_from_orientations(self, vectors):
"""Generates a sequence of points in the circumference of
a circle of radius R, from a list of unit vectors.
vectors is expected to be a list of orientations.
"""
from tns.morphmath import rotation
new_points = []

for vect in vectors:

phi, theta = rotation.spherical_from_vector(vect)
point = self.point_from_trunk_direction(phi, theta)
new_points.append(point)

self.points = self.points + new_points

return new_points


def interpolate(self, points, interpolation=3):
"""Finds the convex hull from a list of points
and returns a number of points N that belong
and returns a number of interpolation points that belong
on this convex hull.
N: sets the minimum number of points to be generated.
interpolation: sets the minimum number of points to be generated.
points: initial set of points
"""
from scipy.spatial import ConvexHull
Expand All @@ -32,59 +107,37 @@ def interpolate(self, points, N=3):
if len(points)>3:
hull = ConvexHull(points)
selected = np.array(points)[hull.vertices]
if len(selected) > N:
if len(selected) > interpolation:
return selected.tolist()
elif len(points) > N:
elif len(points) > interpolation:
print fail_msg
return points.tolist()
else:
print fail_msg
return N*points.tolist()
return interpolation*points.tolist()
else:
print fail_msg
return N*points.tolist()
return interpolation*points.tolist()


def generate(self, neuron, trunk_angles, z_angles, plot_soma=True, interpolation=3):
"""Generates a soma as a sequence of points
def add_soma_points2neuron(self, neuron, interpolation=3):
"""Generates a soma from a list of points,
in the circumference of a circle of radius R.
The points are saved into the neuron object and
consist the first section of the cell.
If interpolation is selected points will be generated
until the expected number of soma points is reached.
"""
vectors = []

sortIDs = np.argsort(trunk_angles)

angle_norm = 2.*np.pi / len(trunk_angles)

for i,a in enumerate(np.array(trunk_angles)[sortIDs]):

phi = np.array(z_angles)[sortIDs][i]
ang = (i + 1) * angle_norm

# To smooth out the soma contour we interpolate
# among the existing soma points:
# theta = (a + np.array(trunk_angles)[sortIDs][i - 1]) / 2

theta = a

vectors.append([self.center[0] + self.radius * \
np.cos(theta + ang) * np.sin(phi),
self.center[1] + self.radius * \
np.sin(theta + ang) * np.sin(phi),
self.center[2]])
# Make soma a 2D contour
# For a 3d contour replace with
# self.center[1] + self.radius * \
# np.cos(phi)
# Soma points as a contour to be saved in neuron.
contour = np.array([self.contour_point(p) for p in self.points])

if interpolation is None:
self.points = [np.append(v, [0.]) for v in vectors]
# Fill in neuron points as a contour, including a zero radius for a 4D point.
neuron.points = neuron.points + [np.append(c, [0.0]) for c in contour]
else:
new_vectors = self.interpolate(np.array(vectors)[:, :2], interpolation)
self.points = [np.append(v, [self.center[2], 0.]) for v in new_vectors]

neuron.points = neuron.points + self.points
neuron.soma = self

return np.array(vectors)

new_vectors = self.interpolate(np.array(contour),
interpolation=interpolation)
neuron.points = neuron.points + [np.append(c, [0.0]) for c in new_vectors]

# Add soma section to neuron groups and initialize
neuron.groups = [np.array([ 0, 1, -1])]
14 changes: 5 additions & 9 deletions tns/core/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,12 @@ def __init__(self,
Parameters:
neuron: Obj neuron where groups and points are stored
initial_direction: 3D vector or random
initial_point: the root of the tree
radius: assuming that the radius is constant for now.
tree_type: an integer indicating the type of the tree (choose from 2, 3, 4, 5)
initial_direction: 3D vector
initial_point: 3D vector that defines the starting point of the tree
parameters including: tree_type, radius, randomness, targeting, apical_distance
tree_type: an integer indicating the type (choose from 2, 3, 4, 5)
"""
if len(initial_direction) == 3:
self.direction = initial_direction
elif initial_direction == "random":
self.direction = rd.get_random_point()

self.direction = initial_direction
self.point = list(initial_point)
self.radius = parameters["radius"]
self.type = parameters["tree_type"] # 2: axon, 3: basal, 4: apical, 5: other
Expand Down
5 changes: 3 additions & 2 deletions tns/extract_input/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@ def parameters(name="Test_neuron", origin=(0.,0.,0.), neurite_types=['basal', 'a
input_parameters["apical"].update(parameters_default)
input_parameters["apical"].update({"apical_distance": 0.0,
"tree_type":4,
"orientation":(0.,1.,0.),
"orientation":[(0.,1.,0.)],
"branching_method": "directional",})

if 'axon' in neurite_types:
input_parameters["axon"].update(parameters_default)
input_parameters["axon"].update({"tree_type":2,
"orientation":(0.,-1.,0.),
"orientation":[(0.,-1.,0.)],
"branching_method": "bio_oriented",})

input_parameters['grow_types'] = neurite_types

return input_parameters

Expand Down
78 changes: 36 additions & 42 deletions tns/extract_input/from_neurom.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,29 @@ def soma_data(pop, distr):

def trunk_data(pop, distr):
# Extract trunk relative orientations to reshample
angles = [nm.get('trunk_angles', n) for n in pop]
angles = [i for a in angles for i in a]
heights, bins = np.histogram(angles, bins=30)

distr.update({"trunk":{}})
def get_trunk_neurite_type(pop, distr, tree_type='basal', nm_type=nm.BASAL_DENDRITE):
'''Extracts the trunk data for a specific tree type'''

distr["trunk"]["orientation_deviation"] = {"data":
{"bins": (bins[1:] + bins[:-1]) / 2.,
"weights": heights}}
angles = [nm.get('trunk_angles', n, neurite_type=nm_type) for n in pop]
angles = [i for a in angles for i in a]
heights, bins = np.histogram(angles, bins=30)

# Set trunk azimuth as a predefined uniform
distr["trunk"]["azimuth"] = {"uniform": {"min":np.pi, "max":0.0}}
if tree_type not in distr:
distr[tree_type] = {}

distr[tree_type].update({"trunk":{}})

distr[tree_type]["trunk"]["orientation_deviation"] = {"data":
{"bins": (bins[1:] + bins[:-1]) / 2.,
"weights": heights}}

# Set trunk azimuth as a predefined uniform
distr[tree_type]["trunk"]["azimuth"] = {"uniform": {"min":np.pi, "max":0.0}}

get_trunk_neurite_type(pop, distr, tree_type='basal', nm_type=nm.BASAL_DENDRITE)
get_trunk_neurite_type(pop, distr, tree_type='apical', nm_type=nm.APICAL_DENDRITE)
get_trunk_neurite_type(pop, distr, tree_type='axon', nm_type=nm.AXON)


def radial_density(pop, distr):
Expand Down Expand Up @@ -69,37 +80,20 @@ def number_neurites_data(pop, distr):
# Extract number of neurites as a precise distribution
# The output is given in integer numbers which are
# the permitted values for the number of trees.
nneurites = nm.get('number_of_neurites', pop, neurite_type=nm.BASAL_DENDRITE)
heights, bins = np.histogram(nneurites,
bins=np.arange(np.min(nneurites), np.max(nneurites)+2))

# Add basal key if not in distributions
if "basal" not in distr:
distr["basal"] = {}
distr["basal"]["num_trees"] = {"data":
{"bins": bins[:-1],
"weights": heights}}

nneurites = nm.get('number_of_neurites', pop, neurite_type=nm.AXON)
heights, bins = np.histogram(nneurites,
bins=np.arange(np.min(nneurites), np.max(nneurites)+2))

# Add axon key if not in distributions
if "axon" not in distr:
distr["axon"] = {}
distr["axon"]["num_trees"] = {"data":
{"bins": bins[:-1],
"weights": heights}}


nneurites = nm.get('number_of_neurites', pop, neurite_type=nm.APICAL_DENDRITE)
heights, bins = np.histogram(nneurites,
bins=np.arange(np.min(nneurites), np.max(nneurites)+2))

# Add apical key if not in distributions
if "apical" not in distr:
distr["apical"] = {}
distr["apical"]["num_trees"] = {"data":
{"bins": bins[:-1],
"weights": heights}}
def get_nneurite_type(pop, distr, tree_type='basal', nm_type=nm.BASAL_DENDRITE):
'''Extracts the number of neurites for a specific tree type'''

nneurites = nm.get('number_of_neurites', pop, neurite_type=nm_type)
heights, bins = np.histogram(nneurites,
bins=np.arange(np.min(nneurites), np.max(nneurites)+2))

if tree_type not in distr:
distr[tree_type] = {}

distr[tree_type]["num_trees"] = {"data":
{"bins": bins[:-1],
"weights": heights}}

get_nneurite_type(pop, distr, tree_type='basal', nm_type=nm.BASAL_DENDRITE)
get_nneurite_type(pop, distr, tree_type='apical', nm_type=nm.APICAL_DENDRITE)
get_nneurite_type(pop, distr, tree_type='axon', nm_type=nm.AXON)
Loading

0 comments on commit 8584048

Please sign in to comment.