Beispiel #1
0
    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
Beispiel #2
0
    def parse_neuron_swc(self,
                         filepath,
                         color='blackboard',
                         alpha=1,
                         radius_multiplier=.1,
                         overwrite=False):
        """
		Given an swc file, render the neuron

		:param filepath: str with path to swc file
		:param neuron_number: numnber of neuron being rendered

		"""
        # See if we rendered this neuron already
        if not overwrite:
            loaded = self.load_save_neuron(filepath)
            if loaded is not None:
                return loaded.color(color)

        print(f"Parsing swc file: {filepath}")
        # details on swc files: http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html
        _sample = namedtuple("sample", "sampleN structureID x y z r parent"
                             )  # sampleN structureID x y z r parent

        if not os.path.isfile(filepath) or not ".swc" in filepath.lower():
            raise ValueError("unrecognized file path: {}".format(filepath))

        try:
            return self.parse_neurons_swc_allen(filepath)
        except:
            pass  #  the .swc file fas not generate with by allen

        f = open(filepath)
        content = f.readlines()
        f.close()
        content = [
            sample.replace("\n", "") for sample in content if sample[0] != '#'
        ]
        content = [sample for sample in content if len(sample) > 3]

        # crate empty dicts for soma axon and dendrites
        data = dict(id=[],
                    parentNumber=[],
                    radius=[],
                    sampleNumber=[],
                    x=[],
                    y=[],
                    z=[])

        # start looping around samples
        for sample in content:
            s = _sample(
                *[float(samp) for samp in sample.lstrip().rstrip().split(" ")])

            # append data to dictionary
            data['id'] = s.structureID
            data['parentNumber'].append(int(s.parent))
            data['radius'].append(s.r)
            data['x'].append(s.x)
            data['y'].append(s.y)
            data['z'].append(s.z)
            data['sampleNumber'].append(int(s.sampleN))

        # Get branches and soma
        print("		reconstructing neurites trees")
        data = pd.DataFrame(data)
        radius = data['radius'].values[0] * radius_multiplier

        soma = data.iloc[0]
        soma = shapes.Sphere(pos=[soma.x, soma.y, soma.z],
                             c=color,
                             r=radius * 4)
        neuron_actors = [soma]

        branches_end, branches_start = [], []  # Get branches start and end
        for parent in data.parentNumber.values:
            sons = data.loc[data.parentNumber == parent]
            if len(sons) > 1:
                branches_end.append(parent)
                for i, son in sons.iterrows():
                    branches_start.append(son.sampleNumber)

        print("		creating actors")
        for start in branches_start:
            node = data.loc[data.sampleNumber == start]
            parent = data.loc[data.sampleNumber == node.parentNumber.values[0]]

            branch = [(parent.x.values[0], parent.y.values[0],
                       parent.z.values[0])]
            while True:
                branch.append(
                    (node.x.values[0], node.y.values[0], node.z.values[0]))

                node = data.loc[data.parentNumber ==
                                node.sampleNumber.values[0]]
                if not len(node): break
                if node.sampleNumber.values[0] in branches_end:
                    branch.append(
                        (node.x.values[0], node.y.values[0], node.z.values[0]))
                    break

            neuron_actors.append(
                shapes.Tube(branch, r=radius, c='red', alpha=1, res=24))

        # Merge actors and save
        actor = merge(*neuron_actors)
        actor.color(color)
        actor.alpha(alpha)

        self.load_save_neuron(filepath, neuron=actor)
        return actor
Beispiel #3
0
    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]
            parent = neurites.loc[neurites.sampleNumber ==
                                  bp.parentNumber.values[0]]

            # loop on each branch after the branching point
            for bi, branch in post_bp.iterrows():

                # Start coordinates in a list, including parent and branch point
                if len(parent):
                    branch_points = [
                        get_coords(parent,
                                   mirror=self.mirror_coord,
                                   mirror_ax=self.mirror_ax)
                    ]
                else:
                    branch_points = []
                branch_points.extend([
                    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 = nxt.sampleNumber.values[0]

                # 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(shapes.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
Beispiel #4
0
def parse_streamline(*args,
                     filepath=None,
                     data=None,
                     show_injection_site=True,
                     color='ivory',
                     alpha=.8,
                     radius=10,
                     **kwargs):
    """
        Given a path to a .json file with streamline data (or the data themselves), render the streamline as tubes actors.
        Either  filepath or data should be passed

        :param filepath: str, optional. Path to .json file with streamline data (Default value = None)
        :param data: panadas.DataFrame, optional. DataFrame with streamline data. (Default value = None)
        :param color: str color of the streamlines (Default value = 'ivory')
        :param alpha: float transparency of the streamlines (Default value = .8)
        :param radius: int radius of the streamlines actor (Default value = 10)
        :param show_injection_site: bool, if True spheres are used to render the injection volume (Default value = True)
        :param *args: 
        :param **kwargs: 

    """
    if filepath is not None and data is None:
        data = load_json(filepath)
        # data = {k:{int(k2):v2 for k2, v2 in v.items()} for k,v in data.items()}
    elif filepath is None and data is not None:
        pass
    else:
        raise ValueError(
            "Need to pass eiteher a filepath or data argument to parse_streamline"
        )

    # create actors for streamlines
    lines = []
    if len(data['lines']) == 1:
        lines_data = data['lines'][0]
    else:
        lines_data = data['lines']
    for line in lines_data:
        points = [[l['x'], l['y'], l['z']] for l in line]
        lines.append(
            shapes.Tube(points,
                        r=radius,
                        c=color,
                        alpha=alpha,
                        res=STREAMLINES_RESOLUTION))

    coords = []
    if show_injection_site:
        if len(data['injection_sites']) == 1:
            injection_data = data['injection_sites'][0]
        else:
            injection_data = data['injection_sites']

        for inj in injection_data:
            coords.append(list(inj.values()))
        spheres = [shapes.Spheres(coords, r=INJECTION_VOLUME_SIZE)]
    else:
        spheres = []

    merged = merge(*lines, *spheres)
    merged.color(color)
    merged.alpha(alpha)
    return [merged]
Beispiel #5
0
    def get_tractography(self, tractography, color=None,  color_by="manual", others_alpha=1, verbose=True,
                        VIP_regions=[], VIP_color=None, others_color="white", include_all_inj_regions=False,
                        extract_region_from_inj_coords=False, display_injection_volume=True):
        """
        Renders tractography data and adds it to the scene. A subset of tractography data can receive special treatment using the  with VIP regions argument:
        if the injection site for the tractography data is in a VIP regions, this is colored differently.

        :param tractography: list of dictionaries with tractography data
        :param color: color of rendered tractography data

        :param color_by: str, specifies which criteria to use to color the tractography (Default value = "manual")
        :param others_alpha: float (Default value = 1)
        :param verbose: bool (Default value = True)
        :param VIP_regions: list of brain regions with VIP treatement (Default value = [])
        :param VIP_color: str, color to use for VIP data (Default value = None)
        :param others_color: str, color for not VIP data (Default value = "white")
        :param include_all_inj_regions: bool (Default value = False)
        :param extract_region_from_inj_coords: bool (Default value = False)
        :param display_injection_volume: float, if True a spehere is added to display the injection coordinates and volume (Default value = True)
        """

        # check argument
        if not isinstance(tractography, list):
            if isinstance(tractography, dict):
                tractography = [tractography]
            else:
                raise ValueError("the 'tractography' variable passed must be a list of dictionaries")
        else:
            if not isinstance(tractography[0], dict):
                raise ValueError("the 'tractography' variable passed must be a list of dictionaries")

        if not isinstance(VIP_regions, list):
            raise ValueError("VIP_regions should be a list of acronyms")

        # check coloring mode used and prepare a list COLORS to use for coloring stuff
        if color_by == "manual":
            # check color argument
            if color is None:
                color = TRACT_DEFAULT_COLOR
                COLORS = [color for i in range(len(tractography))]
            elif isinstance(color, list):
                if not len(color) == len(tractography):
                    raise ValueError("If a list of colors is passed, it must have the same number of items as the number of tractography traces")
                else:
                    for col in color:
                        if not check_colors(col): raise ValueError("Color variable passed to tractography is invalid: {}".format(col))

                    COLORS = color
            else:
                if not check_colors(color):
                    raise ValueError("Color variable passed to tractography is invalid: {}".format(color))
                else:
                    COLORS = [color for i in range(len(tractography))]

        elif color_by == "region":
            COLORS = [self.get_region_color(t['structure-abbrev']) for t in tractography]

        elif color_by == "target_region":
            if VIP_color is not None:
                if not check_colors(VIP_color) or not check_colors(others_color):
                    raise ValueError("Invalid VIP or other color passed")
                try:
                    if include_all_inj_regions:
                        COLORS = [VIP_color if is_any_item_in_list( [x['abbreviation'] for x in t['injection-structures']], VIP_regions)\
                            else others_color for t in tractography]
                    else:
                        COLORS = [VIP_color if t['structure-abbrev'] in VIP_regions else others_color for t in tractography]
                except:
                    raise ValueError("Something went wrong while getting colors for tractography")
            else:
                COLORS = [self.get_region_color(t['structure-abbrev']) if t['structure-abbrev'] in VIP_regions else others_color for t in tractography]
        else:
            raise ValueError("Unrecognised 'color_by' argument {}".format(color_by))

        # add actors to represent tractography data
        actors, structures_acronyms = [], []
        if VERBOSE and verbose:
            print("Structures found to be projecting to target: ")

        # Loop over injection experiments
        for i, (t, color) in enumerate(zip(tractography, COLORS)):
            # Use allen metadata
            if include_all_inj_regions:
                inj_structures = [x['abbreviation'] for x in t['injection-structures']]
            else:
                inj_structures = [self.get_structure_parent(t['structure-abbrev'])['acronym']]

            if VERBOSE and verbose and not is_any_item_in_list(inj_structures, structures_acronyms):
                print("     -- ({})".format(t['structure-abbrev']))
                structures_acronyms.append(t['structure-abbrev'])

            # get tractography points and represent as list
            if color_by == "target_region" and not is_any_item_in_list(inj_structures, VIP_regions):
                alpha = others_alpha
            else:
                alpha = TRACTO_ALPHA

            if alpha == 0:
                continue # skip transparent ones

            # check if we need to manually check injection coords
            if extract_region_from_inj_coords:
                try:
                    region = self.get_structure_from_coordinates(t['injection-coordinates'], 
                                                            just_acronym=False)
                    if region is None: continue
                    inj_structures = [self.get_structure_parent(region['acronym'])['acronym']]
                except:
                    raise ValueError(self.get_structure_from_coordinates(t['injection-coordinates'], 
                                                            just_acronym=False))
                if inj_structures is None: continue
                elif isinstance(extract_region_from_inj_coords, list):
                    # check if injection coord are in one of the brain regions in list, otherwise skip
                    if not is_any_item_in_list(inj_structures, extract_region_from_inj_coords):
                        continue

            # represent injection site as sphere
            if display_injection_volume:
                actors.append(shapes.Sphere(pos=t['injection-coordinates'],
                                c=color, r=INJECTION_VOLUME_SIZE*t['injection-volume'], alpha=TRACTO_ALPHA))

            points = [p['coord'] for p in t['path']]
            actors.append(shapes.Tube(points, r=TRACTO_RADIUS, c=color, alpha=alpha, res=TRACTO_RES))

        return actors