def get_neuron_color(self, neuron, colorby="type"): """ Get a neuron's RGB color. Colors can be assigned based on different criteria like the neuron's type or by individual neuron etc... :param neuron: str, nueron name :param colorby: str, metadata attribute to use for coloring :returns: rgb values of color """ try: # make this work if called by a Scene class cs = self.atlas except: cs = self allowed = ["neuron", "individual", "ind", "pair", "class", "type"] if colorby not in allowed: raise ValueError( f"color by key should be one of {allowed} not {colorby}") if colorby == "type": color = cs.neurons_metadata.loc[cs.neurons_metadata.neuron == neuron]["type_color"].values[0] color = ImageColor.getrgb(color) elif (colorby == "individual" or colorby == "ind" or colorby == "neuron"): color = get_random_colors() else: raise NotImplementedError return color
def get_streamlines(self, sl_file, *args, colorby=None, color_each=False, **kwargs): """ Render streamline data downloaded from https://neuroinformatics.nl/HBP/allen-connectivity-viewer/streamline-downloader.html :param sl_file: path to JSON file with streamliens data [or list of files] :param colorby: str, criteria for how to color the streamline data (Default value = None) :param color_each: bool, if True, the streamlines for each injection is colored differently (Default value = False) :param *args: :param **kwargs: """ color = None if not color_each: if colorby is not None: try: color = self.structure_tree.get_structures_by_acronym([colorby])[0]['rgb_triplet'] if "color" in kwargs.keys(): del kwargs["color"] except: raise ValueError("Could not extract color for region: {}".format(colorby)) else: if colorby is not None: color = kwargs.pop("color", None) try: get_n_shades_of(color, 1) except: raise ValueError("Invalide color argument: {}".format(color)) if not isinstance(sl_file, (list, tuple)): sl_file = [sl_file] actors = [] if isinstance(sl_file[0], (str, pd.DataFrame)): # we have a list of files to add for slf in tqdm(sl_file): if not color_each: if color is not None: if isinstance(slf, str): streamlines = parse_streamline(filepath=slf, *args, color=color, **kwargs) else: streamlines = parse_streamline(data=slf, *args, color=color, **kwargs) else: if isinstance(slf, str): streamlines = parse_streamline(filepath=slf, *args, **kwargs) else: streamlines = parse_streamline(data=slf, *args, **kwargs) else: if color is not None: col = get_n_shades_of(color, 1)[0] else: col = get_random_colors(n_colors=1) if isinstance(slf, str): streamlines = parse_streamline(filepath=slf, color=col, *args, **kwargs) else: streamlines = parse_streamline(data= slf, color=col, *args, **kwargs) actors.extend(streamlines) else: raise ValueError("unrecognized argument sl_file: {}".format(sl_file)) return actors
def get_cells_colors_from_metadata(color_by_metadata, coords_df, color): """ Get color of each cell given some metadata entry :param color_by_metadata: str, column name with metadata info :param coords_df: dataframe with cell coordinates and metadata """ if color_by_metadata not in coords_df.columns: raise ValueError( 'Color_by_metadata argument should be the name of one of the columns of "coords"' ) # Get a map from metadata values to colors vals = list(coords_df[color_by_metadata].values) if len(vals) == 0: raise ValueError( f"Cant color by {color_by_metadata} as no values were found") if not isinstance(color, dict): # The user didn't pass a lookup, generate random base_cols = get_random_colors(n_colors=len(set(vals))) cols_lookup = {v: c for v, c in zip(set(vals), base_cols)} else: try: for val in list(set(vals)): color[val] except KeyError: raise ValueError( 'While using "color_by_metadata" with a dictionary of colors passed' + ' to "color", not every metadata value was assigned a color in the dictionary' + " please make sure that the color dictionary is complete") else: cols_lookup = color # Use the map to get a color for each cell color = [cols_lookup[v] for v in vals] return color
def parse_neurons_colors(neurons, color): """ Prepares the color info to render neurons :param neurons: str, list, dict. File(s) with neurons data or list of rendered neurons. :param color: default None. Can be: - None: each neuron is given a random color - color: rbg, hex etc. If a single color is passed all neurons will have that color - cmap: str with name of a colormap: neurons are colored based on their sequential order and cmap - dict: a dictionary specifying a color for soma, dendrites and axon actors, will be the same for all neurons - list: a list of length = number of neurons with either a single color for each neuron or a dictionary of colors for each neuron """ N = len(neurons) colors = dict( soma=None, axon=None, dendrites=None, ) # If no color is passed, get random colors if color is None: cols = get_random_colors(N) if not isinstance(cols, list): cols = [cols] colors = dict( soma=cols, axon=cols, dendrites=cols, ) else: if isinstance(color, str): # Deal with a a cmap being passed if color in _mapscales_cmaps: cols = [ colorMap(n, name=color, vmin=-2, vmax=N + 2) for n in np.arange(N) ] colors = dict( soma=cols.copy(), axon=cols.copy(), dendrites=cols.copy(), ) else: # Deal with a single color being passed cols = [getColor(color) for n in np.arange(N)] colors = dict( soma=cols.copy(), axon=cols.copy(), dendrites=cols.copy(), ) elif isinstance(color, dict): # Deal with a dictionary with color for each component if "soma" not in color.keys(): raise ValueError( f"When passing a dictionary as color argument, \ soma should be one fo the keys: {color}" ) dendrites_color = color.pop("dendrites", color["soma"]) axon_color = color.pop("axon", color["soma"]) colors = dict( soma=[color["soma"] for n in np.arange(N)], axon=[axon_color for n in np.arange(N)], dendrites=[dendrites_color for n in np.arange(N)], ) elif isinstance(color, (list, tuple)): # Check that the list content makes sense if len(color) != N: raise ValueError( "When passing a list of color arguments, the list length" + f" ({len(color)}) should match the number of neurons ({N})." ) if len(set([type(c) for c in color])) > 1: raise ValueError( "When passing a list of color arguments, all list elements" + " should have the same type (e.g. str or dict)") if isinstance(color[0], dict): # Deal with a list of dictionaries soma_colors, dendrites_colors, axon_colors = [], [], [] for col in color: if "soma" not in col.keys(): raise ValueError( f"When passing a dictionary as col argument, \ soma should be one fo the keys: {col}" ) dendrites_colors.append(col.pop("dendrites", col["soma"])) axon_colors.append(col.pop("axon", col["soma"])) soma_colors.append(col["soma"]) colors = dict( soma=soma_colors, axon=axon_colors, dendrites=dendrites_colors, ) else: if isinstance(color, tuple): color = [color] # Deal with a list of colors colors = dict( soma=color.copy(), axon=color.copy(), dendrites=color.copy(), ) else: raise ValueError( f"Color argument passed is not valid. Should be a \ str, dict, list or None, not {type(color)}:{color}" ) # Check colors, if everything went well we should have N colors per entry for k, v in colors.items(): if len(v) != N: raise ValueError( f"Something went wrong while preparing colors. Not all \ entries have right length. We got: {colors}") return colors
def add_image(self, image_file_path, color=None, alpha=None, obj_file_path=None, voxel_size=1, orientation="saggital", invert_axes=None, extension=".obj", step_size=2, keep_obj_file=True, overwrite='use', smooth=True): """ Loads a 3d image and processes it to extract mesh coordinates. Mesh coordinates are extracted with a fast marching algorithm and saved to a .obj file. This file is then used to render the mesh. :param image_file_path: str :param color: str (Default value = None) :param alpha: int (Default value = None) :param obj_file_path: str (Default value = None) :param voxel_size: float (Default value = 1) :param orientation: str (Default value = "saggital") :param invert_axes: tuple (Default value = None) :param extension: str (Default value = ".obj") :param step_size: int (Default value = 2) :param keep_obj_file: bool (Default value = True) :param overwrite: str (Default value = 'use') :param overwrite: if a (Default value = 'use') :param smooth: bool (Default value = True) """ # Check args if color is None: color = get_random_colors() # get a random color if alpha is None: alpha = brainrender.DEFAULT_STRUCTURE_ALPHA if obj_file_path is None: obj_file_path = os.path.splitext(image_file_path)[0] + extension if os.path.exists(obj_file_path): if overwrite == "use": print("Found a .obj file that matches your input data. Rendering that instead.") print("If you would like to change this behaviour, change the 'overwrite' argument.") elif overwrite == "overwrite": print("Found a .obj file that matches your input data. Overriding it.") print("If you would like to change this behaviour, change the 'overwrite' argument.") # Process the image and save as .obj file image_to_surface(image_file_path, obj_file_path, voxel_size=voxel_size, orientation=orientation, invert_axes=invert_axes, step_size=step_size) elif overwrite == "catch": raise FileExistsError("The .obj file exists alread, to overwrite change the 'overwrite' argument.") else: raise ValueError("Unrecognized value for argument overwrite: {}".format(overwrite)) else: print(f"Converting file: {image_file_path} to surface") image_to_surface(image_file_path, obj_file_path, voxel_size=voxel_size, orientation=orientation, invert_axes=invert_axes, step_size=step_size) # render obj file, smooth and clean up. actor = self.add_from_file(obj_file_path, c=color, alpha=alpha) if smooth: actors_funcs.smooth_actor(actor) if not keep_obj_file: os.remove(obj_file_path)
def edit_neurons(neurons, **kwargs): """ Modify neurons actors after they have been created, at render time. neurons should be a list of dictionaries with soma, dendrite and axon actors of each neuron. :param neurons: list of dictionaries with vtk actors for each neuron :param **kwargs: """ soma_color, axon_color, dendrites_color = None, None, None for neuron in neurons: if "random_color" in kwargs: if kwargs["random_color"]: if not isinstance(kwargs["random_color"], str): color = get_random_colors(n_colors=1) else: # random_color is a colormap color = colorMap(np.random.randint(1000), name=kwargs["random_color"], vmin=0, vmax=1000) axon_color = soma_color = dendrites_color = color elif "color_neurites" in kwargs: soma_color = neuron["soma"].color() if not kwargs["color_neurites"]: axon_color = dendrites_color = soma_color else: if not "axon_color" in kwargs: # print("no axon color provided, using somacolor") axon_color = soma_color else: axon_color = kwargs["axon_color"] if not "dendrites_color" in kwargs: # print("no dendrites color provided, using somacolor") dendrites_color = soma_color else: dendrites_color = kwargs["dendrites_color"] elif "soma_color" in kwargs: if check_colors(kwargs["soma_color"]): soma_color = kwargs["soma_color"] else: print("Invalid soma color provided") soma_color = neuron["soma"].color() elif "axon_color" in kwargs: if check_colors(kwargs["axon_color"]): axon_color = kwargs["axon_color"] else: print("Invalid axon color provided") axon_color = neuron["axon"].color() elif "dendrites_color" in kwargs: if check_colors(kwargs["dendrites_color"]): dendrites_color = kwargs["dendrites_color"] else: print("Invalid dendrites color provided") dendrites_color = neuron["dendrites"].color() if soma_color is not None: neuron["soma"].color(soma_color) if axon_color is not None: neuron["axon"].color(axon_color) if dendrites_color is not None: neuron["dendrites"].color(dendrites_color) if "mirror" in kwargs: if "mirror_coord" in kwargs: mcoord = kwargs["mirror_coord"] else: raise ValueError("Need to pass the mirror point coordinate") # mirror X positoin for name, actor in neuron.items(): if "only_soma" in kwargs: if kwargs["only_soma"] and name != "soma": continue # get mesh points coords and shift them to other hemisphere if isinstance(actor, list): continue coords = actor.points() shifted_coords = [[c[0], c[1], mcoord + (mcoord - c[2])] for c in coords] actor.points(shifted_coords) neuron[name] = actor.mirror(axis='n') return neurons
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 isinstance(neuron, dict): self.alleninfo = None soma_region = self.scene.get_structure_from_coordinates( get_coords(neuron['soma'])) else: self.alleninfo = None soma_region = None elif soma_region is None: self.alleninfo = None if soma is not None: soma_region = self.scene.get_structure_from_coordinates( get_coords(soma)) 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
def get_neurons(self, neurons, color=None, display_axon=True, display_dendrites=True, alpha=1, neurite_radius=None): """ Gets rendered morphological data of neurons reconstructions downloaded from the Mouse Light project at Janelia (or other sources). Accepts neurons argument as: - file(s) with morphological data - vtkplotter mesh actor(s) of entire neurons reconstructions - dictionary or list of dictionary with actors for different neuron parts :param neurons: str, list, dict. File(s) with neurons data or list of rendered neurons. :param display_axon, display_dendrites: if set to False the corresponding neurite is not rendered :param color: default None. Can be: - None: each neuron is given a random color - color: rbg, hex etc. If a single color is passed all neurons will have that color - cmap: str with name of a colormap: neurons are colored based on their sequential order and cmap - dict: a dictionary specifying a color for soma, dendrites and axon actors, will be the same for all neurons - list: a list of length = number of neurons with either a single color for each neuron or a dictionary of colors for each neuron :param alpha: float in range 0,1. Neurons transparency :param neurite_radius: float > 0 , radius of tube actor representing neurites """ if not isinstance(neurons, (list, tuple)): neurons = [neurons] # ------------------------------ Prepare colors ------------------------------ # N = len(neurons) colors = dict( soma = None, axon = None, dendrites = None, ) # If no color is passed, get random colors if color is None: cols = get_random_colors(N) colors = dict( soma = cols.copy(), axon = cols.copy(), dendrites = cols.copy(),) else: if isinstance(color, str): # Deal with a a cmap being passed if color in _mapscales_cmaps: cols = [colorMap(n, name=color, vmin=-2, vmax=N+2) for n in np.arange(N)] colors = dict( soma = cols.copy(), axon = cols.copy(), dendrites = cols.copy(),) else: # Deal with a single color being passed cols = [getColor(color) for n in np.arange(N)] colors = dict( soma = cols.copy(), axon = cols.copy(), dendrites = cols.copy(),) elif isinstance(color, dict): # Deal with a dictionary with color for each component if not 'soma' in color.keys(): raise ValueError(f"When passing a dictionary as color argument, \ soma should be one fo the keys: {color}") dendrites_color = color.pop('dendrites', color['soma']) axon_color = color.pop('axon', color['soma']) colors = dict( soma = [color['soma'] for n in np.arange(N)], axon = [axon_color for n in np.arange(N)], dendrites = [dendrites_color for n in np.arange(N)],) elif isinstance(color, (list, tuple)): # Check that the list content makes sense if len(color) != N: raise ValueError(f"When passing a list of color arguments, the list length"+ f" ({len(color)}) should match the number of neurons ({N}).") if len(set([type(c) for c in color])) > 1: raise ValueError(f"When passing a list of color arguments, all list elements"+ " should have the same type (e.g. str or dict)") if isinstance(color[0], dict): # Deal with a list of dictionaries soma_colors, dendrites_colors, axon_colors = [], [], [] for col in colors: if not 'soma' in col.keys(): raise ValueError(f"When passing a dictionary as col argument, \ soma should be one fo the keys: {col}") dendrites_colors.append(col.pop('dendrites', col['soma'])) axon_colors.append(col.pop('axon', col['soma'])) soma_colors.append(col['soma']) colors = dict( soma = soma_colors, axon = axon_colors, dendrites = dendrites_colors,) else: # Deal with a list of colors colors = dict( soma = color.copy(), axon = color.copy(), dendrites = color.copy(),) else: raise ValueError(f"Color argument passed is not valid. Should be a \ str, dict, list or None, not {type(color)}:{color}") # Check colors, if everything went well we should have N colors per entry for k,v in colors.items(): if len(v) != N: raise ValueError(f"Something went wrong while preparing colors. Not all \ entries have right length. We got: {colors}") # ---------------------------------- Render ---------------------------------- # _neurons_actors = [] for neuron in neurons: neuron_actors = {'soma':None, 'dendrites':None, 'axon': None} # Deal with neuron as filepath if isinstance(neuron, str): if os.path.isfile(neuron): if neuron.endswith('.swc'): neuron_actors, _ = get_neuron_actors_with_morphapi(swcfile=neuron, neurite_radius=neurite_radius) else: raise NotImplementedError('Currently we can only parse morphological reconstructions from swc files') else: raise ValueError(f"Passed neruon {neuron} is not a valid input. Maybe the file doesn't exist?") # Deal with neuron as single actor elif isinstance(neuron, Actor): # A single actor was passed, maybe it's the entire neuron neuron_actors['soma'] = neuron # store it as soma anyway pass # Deal with neuron as dictionary of actor elif isinstance(neuron, dict): neuron_actors['soma'] = neuron.pop('soma', None) neuron_actors['axon'] = neuron.pop('axon', None) # Get dendrites actors if 'apical_dendrites' in neuron.keys() or 'basal_dendrites' in neuron.keys(): if 'apical_dendrites' not in neuron.keys(): neuron_actors['dendrites'] = neuron['basal_dendrites'] elif 'basal_dendrites' not in neuron.keys(): neuron_actors['dendrites'] = neuron['apical_dendrites'] else: neuron_ctors['dendrites'] = merge(neuron['apical_dendrites'], neuron['basal_dendrites']) else: neuron_actors['dendrites'] = neuron.pop('dendrites', None) # Deal with neuron as instance of Neuron from morphapi elif isinstance(neuron, Neuron): neuron_actors, _ = get_neuron_actors_with_morphapi(neuron=neuron) # Deal with other inputs else: raise ValueError(f"Passed neuron {neuron} is not a valid input") # Check that we don't have anything weird in neuron_actors for key, act in neuron_actors.items(): if act is not None: if not isinstance(act, Actor): raise ValueError(f"Neuron actor {key} is {act.__type__} but should be a vtkplotter Mesh. Not: {act}") if not display_axon: neuron_actors['axon'] = None if not display_dendrites: neuron_actors['dendrites'] = None _neurons_actors.append(neuron_actors) # Color actors for n, neuron in enumerate(_neurons_actors): if neuron['axon'] is not None: neuron['axon'].c(colors['axon'][n]) neuron['soma'].c(colors['soma'][n]) if neuron['dendrites'] is not None: neuron['dendrites'].c(colors['dendrites'][n]) # Return if len(_neurons_actors) == 1: return _neurons_actors[0], None elif not _neurons_actors: return None, None else: return _neurons_actors, None