def render_neuron(self, neuron, neuron_number, neuron_name): """ This function takes care of rendering a single neuron. :param neuron: dictionary with neurons data :param neuron_number: number of neuron being rendered :param neuron_name: name of the neuron, used to load/save the .vtk files with the rendered neuron. """ # Prepare variables for rendering soma_color, axon_color, dendrites_color, soma_region = self._render_neuron_get_params(neuron_number, neuron=neuron) # create soma actor neuron_actors = None if USE_MORPHOLOGY_CACHE: params = dict( force_to_hemisphere = self.force_to_hemisphere, neurite_radius = self.neurite_radius, ) neuron_actors = self._load_cached_neuron(neuron_name, params) if neuron_actors is not None: # Color the loaded neuron for component, color in zip(['soma', 'dendrites', 'axon'], [soma_color, dendrites_color, axon_color]): if component in list(neuron_actors.keys()): neuron_actors[component].color(color) return neuron_actors, {'soma':soma_region, 'dendrites':None, 'axons':None} if not USE_MORPHOLOGY_CACHE or neuron_actors is None: print("Parsing neuron: " + neuron_name) neuron_actors = {} self.soma_coords = get_coords(neuron["soma"], mirror=self.mirror_coord, mirror_ax=self.mirror_ax) neuron_actors['soma'] = shapes.Sphere(pos=self.soma_coords, c=soma_color, r=SOMA_RADIUS) # Draw dendrites and axons if self.render_neurites: if self.is_json: neuron_actors['dendrites'], dendrites_regions = self.neurites_parser(pd.DataFrame(neuron["dendrite"]), dendrites_color) neuron_actors['axon'], axon_regions = self.neurites_parser(pd.DataFrame(neuron["axon"]), axon_color) else: neuron_actors['dendrites'], dendrites_regions = self.neurites_parser_swc(pd.DataFrame(neuron["dendrite"]), dendrites_color) neuron_actors['axon'], axon_regions = self.neurites_parser_swc(pd.DataFrame(neuron["axon"]), axon_color) else: neuron_actors['dendrites'], dendrites_regions = [], None neuron_actors['axon'], axon_regions = [], None self.decimate_neuron_actors(neuron_actors) self.smooth_neurons(neuron_actors) # force to hemisphere if self.force_to_hemisphere is not None: neuron_actors = self.mirror_neuron(neuron_actors) if USE_MORPHOLOGY_CACHE: self._cache_neuron(neuron_actors, neuron_name, params) return neuron_actors, {'soma':soma_region, 'dendrites':dendrites_regions, 'axon':axon_regions}
def parse_neurons_swc_allen(self, morphology, color='blackboard', alpha=1): """ SWC parser for Allen neuron's morphology data, they're a bit different from the Mouse Light SWC :param morphology: data with morphology :param neuron_number: int, number of the neuron being rendered. """ # Create soma actor radius = 1 neuron_actors = [ shapes.Sphere(pos=get_coords(morphology.soma)[::-1], c=color, r=radius * 3) ] # loop over trees for tree in morphology._tree_list: tree = pd.DataFrame(tree) branching_points = [ t.id for i, t in tree.iterrows() if len(t.children) > 2 and t.id < len(tree) ] branch_starts = [] for bp in branching_points: branch_starts.extend(tree.iloc[bp].children) for bp in branch_starts: parent = tree.iloc[tree.iloc[bp].parent] branch = [(parent.x, parent.y, parent.z)] point = tree.iloc[bp] while True: branch.append((point.x, point.y, point.z)) if not point.children: break else: try: point = tree.iloc[point.children[0]] except: break # Create actor neuron_actors.append( shapes.Tube(branch, r=radius, c='red', alpha=1, res=24)) actor = merge(*neuron_actors) actor.color(color) actor.alpha(alpha) return actor
def neurites_parser_swc(self, neurites, color): """ Parses neuron's neurites when the data are provided as .swc :param neurites: datafarme with neurites samples :param color: color for vtk actor """ coords = [self.soma_coords] coords.extend([get_coords(sample, mirror=self.mirror_coord, mirror_ax=self.mirror_ax) for i, sample in neurites.iterrows()]) lines = shapes.Spheres(coords, r=38, c=color, res=4) regions = [] return lines, regions
def parse_neurons_swc_allen(self, morphology, neuron_number): """ SWC parser for Allen neuron's morphology data, they're a bit different from the Mouse Light SWC :param morphology: data with morphology :param neuron_number: int, number of the neuron being rendered. """ # Get params neurite_radius = self._get_neurites_radius() soma_color, axon_color, dendrites_color, soma_region = \ self._render_neuron_get_params(neuron_number, soma=morphology.soma) # Create soma actor neuron_actors, regions = { "soma": None, "axon": [], "dendrites": [] }, { 'soma': soma_region, 'dendrites': [], 'axon': [] } neuron_actors['soma'] = Sphere(pos=get_coords(morphology.soma)[::-1], c=soma_color, r=SOMA_RADIUS) # loop over trees if self.render_neurites: for tree in morphology._tree_list: tree = pd.DataFrame(tree) # get node numbers in t # get the first non soma node first_node_type = tree.loc[ tree.type != morphology.SOMA].type.values[0] # get the branch type if first_node_type == morphology.AXON: neurite = "axon" color = axon_color else: neurite = "dendrites" color = dendrites_color # Get all the points that make the branch branch_points = [[x, y, z] for x, y, z in zip( tree.x.values, tree.y.values, tree.z.values)] # Create actor neuron_actors[neurite].append(\ shapes.Tube(branch_points, r=neurite_radius, c=color, alpha=1, res=NEURON_RESOLUTION)) # merge actors' meshes to make rendering faster for neurite, color in zip(["axon", "dendrites"], [axon_color, dendrites_color]): if neuron_actors[neurite]: neuron_actors[neurite] = merge(*neuron_actors[neurite]) neuron_actors[neurite].color(color) self.decimate_neuron_actors(neuron_actors) self.smooth_neurons(neuron_actors) # force to hemisphere if self.force_to_hemisphere is not None: neuron_actors = self.mirror_neuron(neuron_actors) # Check output if not neuron_actors["axon"]: neuron_actors["axon"] = None if not neuron_actors["dendrites"]: neuron_actors["dendrites"] = None return neuron_actors, regions
def neurites_parser(self, neurites, color): """ Given a dataframe with all the samples for some neurites, create "Tube" actors that render each neurite segment. ---------------------------------------------------------------- This function works by first identifyingt the branching points of a neurite structure. Then each segment between either two branchin points or between a branching point and a terminal is modelled as a Tube. This minimizes the number of actors needed to represent the neurites while stil accurately modelling the neuron. Known issue: the axon initial segment is missing from renderings. :param neurites: dataframe with each sample for the neurites :param color: color to be assigned to the Tube actor """ neurite_radius = self._get_neurites_radius() # get branching points try: parent_counts = neurites["parentNumber"].value_counts() except: if len(neurites) == 0: print("Couldn't find neurites data") return [], [] else: raise ValueError( "Something went wrong while rendering neurites:\n{}". format(neurites)) branching_points = parent_counts.loc[parent_counts > 1] # loop over each branching point actors = [] for idx, bp in branching_points.iteritems(): # get neurites after the branching point bp = neurites.loc[neurites.sampleNumber == idx] post_bp = neurites.loc[neurites.parentNumber == idx] # loop on each branch after the branching point for bi, branch in post_bp.iterrows(): if bi == 0: branch_points = [ self.soma_coords, get_coords(bp, mirror=self.mirror_coord, mirror_ax=self.mirror_ax), get_coords(branch, mirror=self.mirror_coord, mirror_ax=self.mirror_ax) ] # this list stores all the samples that are part of a branch else: branch_points = [ get_coords(bp, mirror=self.mirror_coord, mirror_ax=self.mirror_ax), get_coords(branch, mirror=self.mirror_coord, mirror_ax=self.mirror_ax) ] # loop over all following points along the branch, until you meet either a terminal or another branching point. store the points idx = branch.sampleNumber while True: nxt = neurites.loc[neurites.parentNumber == idx] if len(nxt) != 1: break else: branch_points.append( get_coords(nxt, mirror=self.mirror_coord, mirror_ax=self.mirror_ax)) idx += 1 # if the branch is too short for a tube, create a sphere instead if len( branch_points ) < 2: # plot either a line between two branch_points or a spheere actors.append(Sphere(branch_points[0], c="g", r=100)) continue # create tube actor actors.append( shapes.Tube(branch_points, r=neurite_radius, c=color, alpha=1, res=NEURON_RESOLUTION)) # merge actors' meshes to make rendering faster merged = merge(*actors) if merged is None: return None, None merged.color(color) # get regions the neurites go through regions = [] if "allenId" in neurites.columns: for rid in set(neurites.allenId.values): try: region = self.alleninfo.loc[self.alleninfo.allenId == rid].acronym.values[0] regions.append( self.scene.get_structure_parent(region)['acronym']) except: pass return merged, regions
def _render_neuron_get_params(self, neuron_number, neuron=None, soma_region=None, soma=None): """ Makes sure that all the parameters to specify how neurons should be rendered. :param neuron_number: number of the neuron being rendered :param neuron: neuron's metadata (Default value = None) :param soma_region: str with the acronym of the region the soma is in (Default value = None) :param soma: list with XYZ coordinates of the neuron's soma. (Default value = None) """ # Define colors of different components if not self.color_by_region: if self.random_color: if not isinstance(self.random_color, str): color = get_random_colors(n_colors=1) else: # random_color is a colormap color = colorMap(neuron_number, name=self.random_color, vmin=0, vmax=self.n_neurons) axon_color = soma_color = dendrites_color = color else: if self.soma_color is None: soma_color = get_random_colors(n_colors=1) if not self.color_neurites: axon_color = dendrites_color = soma_color = self.soma_color else: soma_color = self.soma_color if self.axon_color is None: axon_color = soma_color else: axon_color = self.axon_color if self.dendrites_color is None: dendrites_color = soma_color else: dendrites_color = self.dendrites_color # check that the colors make sense if not check_colors([soma_color, axon_color, dendrites_color]): raise ValueError( "The colors chosen are not valid: soma - {}, dendrites {}, axon {}" .format(soma_color, dendrites_color, axon_color)) # check if we have lists of colors or single colors if isinstance(soma_color, list): if isinstance(soma_color[0], str) or isinstance( soma_color[0], list): soma_color = soma_color[neuron_number] if isinstance(dendrites_color, list): if isinstance(dendrites_color[0], str) or isinstance( dendrites_color[0], list): dendrites_color = dendrites_color[neuron_number] if isinstance(axon_color, list): if isinstance(axon_color[0], str) or isinstance( axon_color[0], list): axon_color = axon_color[neuron_number] # get allen info: it containes the allenID of each brain region # each sample has the corresponding allen ID so we can recontruct in which brain region it is if neuron is not None: if 'allenInformation' in list(neuron.keys()): self.alleninfo = pd.DataFrame( neuron['allenInformation'] ) # get brain structure in which is the soma soma_region = self.scene.get_structure_parent( self.alleninfo.loc[ self.alleninfo.allenId == neuron['soma'] ['allenId']].acronym.values[0])['acronym'] else: self.alleninfo = None soma_region = self.scene.get_structure_from_coordinates( get_coords(neuron['soma'])) if soma_region is not None: soma_region = soma_region['acronym'] elif soma_region is None: self.alleninfo = None if soma is not None: soma_region = self.scene.get_structure_from_coordinates( get_coords(soma)) if soma_region is not None: soma_region = soma_region['acronym'] else: raise ValueError( "You need to pass either a neuron, or a soma region or a soma" ) else: self.alleninfo = None if soma_region is not None: soma_region = self.scene.get_structure_parent( soma_region)['acronym'] else: soma_region = "root" if self.color_by_region: try: region_color = self.scene.structure_tree.get_structures_by_acronym( [soma_region])[0]['rgb_triplet'] except: print( "could not find default color for region: {}. Using random color instead" .format(soma_region)) region_color = get_random_colors(n_colors=1) axon_color = soma_color = dendrites_color = region_color return soma_color, axon_color, dendrites_color, soma_region