def get_injection_sites(self, experiments, color=None): """ Creates Spherse at the location of injections with a volume proportional to the injected volume :param experiments: list of dictionaries with tractography data :param color: (Default value = None) """ # check arguments if not isinstance(experiments, list): raise ValueError("experiments must be a list") if not isinstance(experiments[0], dict): raise ValueError("experiments should be a list of dictionaries") #c= cgeck color if color is None: color = INJECTION_DEFAULT_COLOR injection_sites = [] for exp in experiments: injection_sites.append(shapes.Sphere(pos=(exp["injection_x"], exp["injection_y"], exp["injection_z"]), r = INJECTION_VOLUME_SIZE*exp["injection_volume"]*3, c=color )) return injection_sites
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 add_sphere_at_point(self, pos=[0, 0, 0], radius=100, color="black", alpha=1, **kwargs): """ Adds a shere at a location specified by the user :param pos: list of x,y,z coordinates (Default value = [0, 0, 0]) :param radius: int, radius of the sphere (Default value = 100) :param color: color of the sphere (Default value = "black") :param alpha: transparency of the sphere (Default value = 1) :param **kwargs: """ sphere = shapes.Sphere(pos=pos, r=radius, c=color, alpha=alpha, **kwargs) self.actors['others'].append(sphere) return sphere
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
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
def addAxes(axtype=None, c=None): """Draw axes on scene. Available axes types: :param int axtype: - 0, no axes, - 1, draw three gray grid walls - 2, show cartesian axes from (0,0,0) - 3, show positive range of cartesian axes from (0,0,0) - 4, show a triad at bottom left - 5, show a cube at bottom left - 6, mark the corners of the bounding box - 7, draw a simple ruler at the bottom of the window - 8, show the ``vtkCubeAxesActor`` object - 9, show the bounding box outLine - 10, show three circles representing the maximum bounding box """ vp = settings.plotter_instance if axtype is not None: vp.axes = axtype # overrride r = vp.renderers.index(vp.renderer) if not vp.axes: return if c is None: # automatic black or white c = (0.9, 0.9, 0.9) if numpy.sum(vp.renderer.GetBackground()) > 1.5: c = (0.1, 0.1, 0.1) if not vp.renderer: return if vp.axes_exist[r]: return # calculate max actors bounds bns = [] for a in vp.actors: if a and a.GetPickable(): b = a.GetBounds() if b: bns.append(b) if len(bns): max_bns = numpy.max(bns, axis=0) min_bns = numpy.min(bns, axis=0) vbb = (min_bns[0], max_bns[1], min_bns[2], max_bns[3], min_bns[4], max_bns[5]) else: vbb = vp.renderer.ComputeVisiblePropBounds() max_bns = vbb min_bns = vbb sizes = (max_bns[1] - min_bns[0], max_bns[3] - min_bns[2], max_bns[5] - min_bns[4]) ############################################################ if vp.axes == 1 or vp.axes == True: # gray grid walls nd = 4 # number of divisions in the smallest axis off = -0.04 # label offset step = numpy.min(sizes) / nd if not step: # bad proportions, use vtkCubeAxesActor vp.addAxes(axtype=8, c=c) vp.axes = 1 return rx, ry, rz = numpy.rint(sizes / step).astype(int) if max([rx / ry, ry / rx, rx / rz, rz / rx, ry / rz, rz / ry]) > 15: # bad proportions, use vtkCubeAxesActor vp.addAxes(axtype=8, c=c) vp.axes = 1 return gxy = shapes.Grid(pos=(0.5, 0.5, 0), normal=[0, 0, 1], bc=None, resx=rx, resy=ry) gxz = shapes.Grid(pos=(0.5, 0, 0.5), normal=[0, 1, 0], bc=None, resx=rz, resy=rx) gyz = shapes.Grid(pos=(0, 0.5, 0.5), normal=[1, 0, 0], bc=None, resx=rz, resy=ry) gxy.alpha(0.06).wire(False).color(c).lineWidth(1) gxz.alpha(0.04).wire(False).color(c).lineWidth(1) gyz.alpha(0.04).wire(False).color(c).lineWidth(1) xa = shapes.Line([0, 0, 0], [1, 0, 0], c=c, lw=1) ya = shapes.Line([0, 0, 0], [0, 1, 0], c=c, lw=1) za = shapes.Line([0, 0, 0], [0, 0, 1], c=c, lw=1) xt, yt, zt, ox, oy, oz = [None] * 6 if vp.xtitle: xtitle = vp.xtitle if min_bns[0] <= 0 and max_bns[1] > 0: # mark x origin ox = shapes.Cube([-min_bns[0] / sizes[0], 0, 0], side=0.008, c=c) if len(vp.xtitle) == 1: # add axis length info xtitle = vp.xtitle + " /" + utils.precision(sizes[0], 4) wpos = [1 - (len(vp.xtitle) + 1) / 40, off, 0] xt = shapes.Text(xtitle, pos=wpos, normal=(0, 0, 1), s=0.025, c=c, justify="bottom-right") if vp.ytitle: if min_bns[2] <= 0 and max_bns[3] > 0: # mark y origin oy = shapes.Cube([0, -min_bns[2] / sizes[1], 0], side=0.008, c=c) yt = shapes.Text(vp.ytitle, pos=(0, 0, 0), normal=(0, 0, 1), s=0.025, c=c, justify="bottom-right") if len(vp.ytitle) == 1: wpos = [off, 1 - (len(vp.ytitle) + 1) / 40, 0] yt.pos(wpos) else: wpos = [off * 0.7, 1 - (len(vp.ytitle) + 1) / 40, 0] yt.rotateZ(90).pos(wpos) if vp.ztitle: if min_bns[4] <= 0 and max_bns[5] > 0: # mark z origin oz = shapes.Cube([0, 0, -min_bns[4] / sizes[2]], side=0.008, c=c) zt = shapes.Text(vp.ztitle, pos=(0, 0, 0), normal=(1, -1, 0), s=0.025, c=c, justify="bottom-right") if len(vp.ztitle) == 1: wpos = [off * 0.6, off * 0.6, 1 - (len(vp.ztitle) + 1) / 40] zt.rotate(90, (1, -1, 0)).pos(wpos) else: wpos = [off * 0.3, off * 0.3, 1 - (len(vp.ztitle) + 1) / 40] zt.rotate(180, (1, -1, 0)).pos(wpos) acts = [gxy, gxz, gyz, xa, ya, za, xt, yt, zt, ox, oy, oz] for a in acts: if a: a.PickableOff() aa = Assembly(acts) aa.pos(min_bns[0], min_bns[2], min_bns[4]) aa.SetScale(sizes) aa.PickableOff() vp.renderer.AddActor(aa) vp.axes_exist[r] = aa elif vp.axes == 2 or vp.axes == 3: vbb = vp.renderer.ComputeVisiblePropBounds() # to be double checked xcol, ycol, zcol = "db", "dg", "dr" s = 1 alpha = 1 centered = False x0, x1, y0, y1, z0, z1 = vbb dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 aves = numpy.sqrt(dx * dx + dy * dy + dz * dz) / 2 x0, x1 = min(x0, 0), max(x1, 0) y0, y1 = min(y0, 0), max(y1, 0) z0, z1 = min(z0, 0), max(z1, 0) if vp.axes == 3: if x1 > 0: x0 = 0 if y1 > 0: y0 = 0 if z1 > 0: z0 = 0 dx, dy, dz = x1 - x0, y1 - y0, z1 - z0 acts = [] if x0 * x1 <= 0 or y0 * z1 <= 0 or z0 * z1 <= 0: # some ranges contain origin zero = shapes.Sphere(r=aves / 120 * s, c="k", alpha=alpha, res=10) acts += [zero] if len(vp.xtitle) and dx > aves / 100: xl = shapes.Cylinder([[x0, 0, 0], [x1, 0, 0]], r=aves / 250 * s, c=xcol, alpha=alpha) xc = shapes.Cone(pos=[x1, 0, 0], c=xcol, alpha=alpha, r=aves / 100 * s, height=aves / 25 * s, axis=[1, 0, 0], res=10) wpos = [ x1 - (len(vp.xtitle) + 1) * aves / 40 * s, -aves / 25 * s, 0 ] # aligned to arrow tip if centered: wpos = [(x0 + x1) / 2 - len(vp.xtitle) / 2 * aves / 40 * s, -aves / 25 * s, 0] xt = shapes.Text(vp.xtitle, pos=wpos, normal=(0, 0, 1), s=aves / 40 * s, c=xcol) acts += [xl, xc, xt] if len(vp.ytitle) and dy > aves / 100: yl = shapes.Cylinder([[0, y0, 0], [0, y1, 0]], r=aves / 250 * s, c=ycol, alpha=alpha) yc = shapes.Cone(pos=[0, y1, 0], c=ycol, alpha=alpha, r=aves / 100 * s, height=aves / 25 * s, axis=[0, 1, 0], res=10) wpos = [ -aves / 40 * s, y1 - (len(vp.ytitle) + 1) * aves / 40 * s, 0 ] if centered: wpos = [ -aves / 40 * s, (y0 + y1) / 2 - len(vp.ytitle) / 2 * aves / 40 * s, 0 ] yt = shapes.Text(vp.ytitle, pos=(0, 0, 0), normal=(0, 0, 1), s=aves / 40 * s, c=ycol) yt.rotate(90, [0, 0, 1]).pos(wpos) acts += [yl, yc, yt] if len(vp.ztitle) and dz > aves / 100: zl = shapes.Cylinder([[0, 0, z0], [0, 0, z1]], r=aves / 250 * s, c=zcol, alpha=alpha) zc = shapes.Cone(pos=[0, 0, z1], c=zcol, alpha=alpha, r=aves / 100 * s, height=aves / 25 * s, axis=[0, 0, 1], res=10) wpos = [ -aves / 50 * s, -aves / 50 * s, z1 - (len(vp.ztitle) + 1) * aves / 40 * s ] if centered: wpos = [ -aves / 50 * s, -aves / 50 * s, (z0 + z1) / 2 - len(vp.ztitle) / 2 * aves / 40 * s ] zt = shapes.Text(vp.ztitle, pos=(0, 0, 0), normal=(1, -1, 0), s=aves / 40 * s, c=zcol) zt.rotate(180, (1, -1, 0)).pos(wpos) acts += [zl, zc, zt] for a in acts: a.PickableOff() ass = Assembly(acts) ass.PickableOff() vp.renderer.AddActor(ass) vp.axes_exist[r] = ass elif vp.axes == 4: axact = vtk.vtkAxesActor() axact.SetShaftTypeToCylinder() axact.SetCylinderRadius(0.03) axact.SetXAxisLabelText(vp.xtitle) axact.SetYAxisLabelText(vp.ytitle) axact.SetZAxisLabelText(vp.ztitle) axact.GetXAxisShaftProperty().SetColor(0, 0, 1) axact.GetZAxisShaftProperty().SetColor(1, 0, 0) axact.GetXAxisTipProperty().SetColor(0, 0, 1) axact.GetZAxisTipProperty().SetColor(1, 0, 0) bc = numpy.array(vp.renderer.GetBackground()) if numpy.sum(bc) < 1.5: lc = (1, 1, 1) else: lc = (0, 0, 0) axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().BoldOff() axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ItalicOff() axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().ShadowOff() axact.GetXAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) axact.GetYAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) axact.GetZAxisCaptionActor2D().GetCaptionTextProperty().SetColor(lc) axact.PickableOff() icn = addIcon(axact, size=0.1) vp.axes_exist[r] = icn elif vp.axes == 5: axact = vtk.vtkAnnotatedCubeActor() axact.GetCubeProperty().SetColor(0.75, 0.75, 0.75) axact.SetTextEdgesVisibility(0) axact.SetFaceTextScale(0.4) axact.GetXPlusFaceProperty().SetColor(colors.getColor("b")) axact.GetXMinusFaceProperty().SetColor(colors.getColor("db")) axact.GetYPlusFaceProperty().SetColor(colors.getColor("g")) axact.GetYMinusFaceProperty().SetColor(colors.getColor("dg")) axact.GetZPlusFaceProperty().SetColor(colors.getColor("r")) axact.GetZMinusFaceProperty().SetColor(colors.getColor("dr")) axact.PickableOff() icn = addIcon(axact, size=0.06) vp.axes_exist[r] = icn elif vp.axes == 6: ocf = vtk.vtkOutlineCornerFilter() ocf.SetCornerFactor(0.1) largestact, sz = None, -1 for a in vp.actors: if a.GetPickable(): b = a.GetBounds() d = max(b[1] - b[0], b[3] - b[2], b[5] - b[4]) if sz < d: largestact = a sz = d if isinstance(largestact, Assembly): ocf.SetInputData(largestact.getActor(0).GetMapper().GetInput()) else: ocf.SetInputData(largestact.polydata()) ocf.Update() ocMapper = vtk.vtkHierarchicalPolyDataMapper() ocMapper.SetInputConnection(0, ocf.GetOutputPort(0)) ocActor = vtk.vtkActor() ocActor.SetMapper(ocMapper) bc = numpy.array(vp.renderer.GetBackground()) if numpy.sum(bc) < 1.5: lc = (1, 1, 1) else: lc = (0, 0, 0) ocActor.GetProperty().SetColor(lc) ocActor.PickableOff() vp.renderer.AddActor(ocActor) vp.axes_exist[r] = ocActor elif vp.axes == 7: # draws a simple ruler at the bottom of the window ls = vtk.vtkLegendScaleActor() ls.RightAxisVisibilityOff() ls.TopAxisVisibilityOff() ls.LegendVisibilityOff() ls.LeftAxisVisibilityOff() ls.GetBottomAxis().SetNumberOfMinorTicks(1) ls.GetBottomAxis().GetProperty().SetColor(c) ls.GetBottomAxis().GetLabelTextProperty().SetColor(c) ls.GetBottomAxis().GetLabelTextProperty().BoldOff() ls.GetBottomAxis().GetLabelTextProperty().ItalicOff() ls.GetBottomAxis().GetLabelTextProperty().ShadowOff() ls.PickableOff() vp.renderer.AddActor(ls) vp.axes_exist[r] = ls elif vp.axes == 8: ca = vtk.vtkCubeAxesActor() ca.SetBounds(vbb) if vp.camera: ca.SetCamera(vp.camera) else: ca.SetCamera(vp.renderer.GetActiveCamera()) ca.GetXAxesLinesProperty().SetColor(c) ca.GetYAxesLinesProperty().SetColor(c) ca.GetZAxesLinesProperty().SetColor(c) for i in range(3): ca.GetLabelTextProperty(i).SetColor(c) ca.GetTitleTextProperty(i).SetColor(c) ca.SetTitleOffset(5) ca.SetFlyMode(3) ca.SetXTitle(vp.xtitle) ca.SetYTitle(vp.ytitle) ca.SetZTitle(vp.ztitle) if vp.xtitle == "": ca.SetXAxisVisibility(0) ca.XAxisLabelVisibilityOff() if vp.ytitle == "": ca.SetYAxisVisibility(0) ca.YAxisLabelVisibilityOff() if vp.ztitle == "": ca.SetZAxisVisibility(0) ca.ZAxisLabelVisibilityOff() ca.PickableOff() vp.renderer.AddActor(ca) vp.axes_exist[r] = ca return elif vp.axes == 9: src = vtk.vtkCubeSource() src.SetXLength(vbb[1] - vbb[0]) src.SetYLength(vbb[3] - vbb[2]) src.SetZLength(vbb[5] - vbb[4]) src.Update() ca = Actor(src.GetOutput(), c=c, alpha=0.5, wire=1) ca.pos((vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2) ca.PickableOff() vp.renderer.AddActor(ca) vp.axes_exist[r] = ca elif vp.axes == 10: x0 = (vbb[0] + vbb[1]) / 2, (vbb[3] + vbb[2]) / 2, (vbb[5] + vbb[4]) / 2 rx, ry, rz = (vbb[1] - vbb[0]) / 2, (vbb[3] - vbb[2]) / 2, (vbb[5] - vbb[4]) / 2 rm = max(rx, ry, rz) xc = shapes.Disc(x0, (0, 0, 1), r1=rm, r2=rm, c='lr', bc=None, res=1, resphi=72) yc = shapes.Disc(x0, (0, 1, 0), r1=rm, r2=rm, c='lg', bc=None, res=1, resphi=72) zc = shapes.Disc(x0, (1, 0, 0), r1=rm, r2=rm, c='lb', bc=None, res=1, resphi=72) xc.clean().alpha(0.2).wire().lineWidth(2.5).PickableOff() yc.clean().alpha(0.2).wire().lineWidth(2.5).PickableOff() zc.clean().alpha(0.2).wire().lineWidth(2.5).PickableOff() ca = xc + yc + zc ca.PickableOff() vp.renderer.AddActor(ca) vp.axes_exist[r] = ca else: colors.printc('~bomb Keyword axes must be in range [0-10].', c=1) colors.printc(''' ~target Available axes types: 0 = no axes, 1 = draw three gray grid walls 2 = show cartesian axes from (0,0,0) 3 = show positive range of cartesian axes from (0,0,0) 4 = show a triad at bottom left 5 = show a cube at bottom left 6 = mark the corners of the bounding box 7 = draw a simple ruler at the bottom of the window 8 = show the vtkCubeAxesActor object 9 = show the bounding box outline 10 = show three circles representing the maximum bounding box ''', c=1, bold=0) if not vp.axes_exist[r]: vp.axes_exist[r] = True return
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