From a7ae617cd26a19cde01f60adc01dfdd6515464cc Mon Sep 17 00:00:00 2001 From: niksirbi Date: Tue, 17 Dec 2024 18:56:26 +0000 Subject: [PATCH] WIP plotting annotation overlay on reference --- examples/plots/atlas_annotations.py | 172 ++++++++++++++++++ examples/plots/colors.csv | 26 +++ .../plots}/oldenburg_blackcap_colors.csv | 2 +- 3 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 examples/plots/atlas_annotations.py create mode 100644 examples/plots/colors.csv rename {data => examples/plots}/oldenburg_blackcap_colors.csv (95%) diff --git a/examples/plots/atlas_annotations.py b/examples/plots/atlas_annotations.py new file mode 100644 index 0000000..1d27cbb --- /dev/null +++ b/examples/plots/atlas_annotations.py @@ -0,0 +1,172 @@ +"""Create plots showing atlas annotations overlaid on the reference image + +This script needs: +- A local path to where the BrainGlobe atlas is stored +- A path to a .csv file specifying RGB values for each region in the atlas +""" +# %% +# Imports + +import os +from pathlib import Path + +import matplotlib.colors as mcolors +import numpy as np +import pandas as pd +from brainglobe_utils.IO.image import load_any +from matplotlib import pyplot as plt + +from brainglobe_template_builder.plots import save_figure + +# %% +# Specify paths + +# Path to the BrainGlobe atlas +atlas_name = "oldenburg_blackcap_25um_v1.1" +atlas_dir = Path.home() / ".brainglobe" / atlas_name +reference_path = atlas_dir / "reference.tiff" +annotation_path = atlas_dir / "annotation.tiff" +structures_csv_path = atlas_dir / "structures.csv" + +# Path to the csv file containing the RGB values for each region +# Expected columns are: "acronym", "R", "G", "B" +# The .csv is in the same folder as this script +colors_csv_filename = "oldenburg_blackcap_colors.csv" +currenct_script_dir = Path(os.path.dirname(os.path.abspath(__file__))) +colors_csv_path = currenct_script_dir / colors_csv_filename + +# Path to save the plots +save_dir = Path.home() / "Downloads" + +# %% +# Load data + +reference_img = load_any(reference_path) +annotation_img = load_any(annotation_path) + +# Load both "structures" and "colors" csv files +structures = pd.read_csv(structures_csv_path) +colors = pd.read_csv(colors_csv_path, dtype={"R": int, "G": int, "B": int}) +# Merge the two dataframes on the "acronym" column +# Maintain the order of the "colors" dataframe +structures = pd.merge( + structures, colors, on="acronym", how="right", validate="one_to_one" +) +# add an alpha column with all areas being 0.8 +structures.loc[:, "A"] = 1.0 +# Keep only the columns needed for the colormap +structures = structures[["id", "acronym", "R", "G", "B", "A"]] +# Divide RGB values by 255 (normalise to 0 - 1) +for col in ["R", "G", "B"]: + structures[col] = structures[col] / 255 +# Add an id=0 (empty areas), with RGBA values (0, 0, 0, 0) +new_row = pd.DataFrame( + {"id": [0], "acronym": "empty", "R": [0], "G": [0], "B": [0], "A": [0]} +) +structures = pd.concat([new_row, structures], ignore_index=True) +# Sort daraframe by id (increasing id values) +structures = structures.sort_values(by="id") +# assign new_id in increasing order +structures.loc[:, "new_id"] = range(len(structures)) +# Save the new dataframe to a csv file here +structures.to_csv(currenct_script_dir / "colors.csv", index=False) + +# Create a dictionary mapping the id to the RGBA values +id_to_rgba = { + row["new_id"]: (row["R"], row["G"], row["B"], row["A"]) + for _, row in structures.iterrows() +} +# Create a colormap using the RGBA values +annotation_cmap = mcolors.ListedColormap([id_to_rgba[id] for id in id_to_rgba]) +# Create a normalization for the colormap based on the id values +annotation_cmap_norm = mcolors.BoundaryNorm( + list(id_to_rgba.keys()), annotation_cmap.N +) + +# Remap the annotation image to the new ids +for id in structures["id"].unique(): + new_id = structures.loc[structures["id"] == id, "new_id"].values[0] + annotation_img[annotation_img == id] = new_id + + +# %% +# Define funciton for plotting + + +def plot_slices( + reference: np.ndarray, + slices: list[int], + annotation=np.ndarray | None, + axis: int = 0, + vmin_perc: float = 1, + vmax_perc: float = 99, + save_path: Path | None = None, +): + """Plot slices from a 3D image with optional annotation overlay. + + The slices are shown in a single column. + + Parameters + ---------- + reference : np.ndarray + Reference 3D image to plot slices from. + slices : list[int] + List of slice indices to plot. + annotation : np.ndarray, optional + Annotation image to overlay on the reference image, by default None. + If supplied, must have the same shape as the reference image. + axis : int, optional + Axis along which to take slices, by default 0. + vmin_perc : float, optional + Lower percentile for reference image, by default 1. + vmax_perc : float, optional + Upper percentile for reference image, by default 99. + save_path : Path, optional + Path to save the plot, by default None (does not save). + """ + n_slices = len(slices) + ref_slices = [reference.take(s, axis=axis) for s in slices] + height, width = ref_slices[0].shape + fig_width = width / 100 + fig_height = height / 100 * n_slices + fig, ax = plt.subplots(n_slices, 1, figsize=(fig_width, fig_height)) + + if annotation is not None: + ann_slices = [annotation.take(s, axis=axis) for s in slices] + # Make the left half of each slice to 0 + for ann_slice in ann_slices: + ann_slice[:, : width // 2] = 0 + + for i in range(n_slices): + ref_frame = ref_slices[i] + ax[i].imshow( + ref_frame, + cmap="gray", + vmin=np.percentile(ref_frame, vmin_perc), + vmax=np.percentile(ref_frame, vmax_perc), + ) + + if annotation is not None: + ann_frame = ann_slices[i] + ax[i].imshow( + ann_frame, + cmap=annotation_cmap, + norm=annotation_cmap_norm, + ) + ax[i].axis("off") + + fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0) + if save_path: + save_dir, save_name = save_path.parent, save_path.name.split(".")[0] + save_figure(fig, save_dir, save_name) + + +# %% +# Save plot + +plot_slices( + reference_img, + slices=[120, 260, 356], + annotation=annotation_img, + save_path=save_dir / "test.png", +) diff --git a/examples/plots/colors.csv b/examples/plots/colors.csv new file mode 100644 index 0000000..1926ff9 --- /dev/null +++ b/examples/plots/colors.csv @@ -0,0 +1,26 @@ +id,acronym,R,G,B,A,new_id +0,empty,0.0,0.0,0.0,0.0,0 +1,P,0.19215686274509805,0.6862745098039216,0.9607843137254902,1.0,1 +2,St,0.9450980392156862,0.796078431372549,0.22745098039215686,1.0,2 +3,Di,0.9411764705882353,0.3568627450980392,0.07058823529411765,1.0,3 +4,Me,0.2784313725490196,0.4823529411764706,0.9490196078431372,1.0,4 +5,Pn,0.8745098039215686,0.8745098039215686,0.21568627450980393,1.0,5 +6,Cb,0.6745098039215687,0.984313725490196,0.2196078431372549,1.0,6 +10,PrVd,0.8862745098039215,0.2627450980392157,0.0392156862745098,1.0,7 +17,PrVv,0.996078431372549,0.6,0.17254901960784313,1.0,8 +18,SpVl,0.23921568627450981,0.20784313725490197,0.5450980392156862,1.0,9 +19,SpVm,0.18823529411764706,0.07058823529411765,0.23137254901960785,1.0,10 +20,Ento,0.13333333333333333,0.9215686274509803,0.6666666666666666,1.0,11 +30,HP,0.47843137254901963,0.01568627450980392,0.011764705882352941,1.0,12 +40,N,0.2196078431372549,0.15294117647058825,0.42745098039215684,1.0,13 +50,H,0.7843137254901961,0.9372549019607843,0.20392156862745098,1.0,14 +60,M,0.5607843137254902,1.0,0.28627450980392155,1.0,15 +70,A,0.12549019607843137,0.7803921568627451,0.8745098039215686,1.0,16 +80,OB,0.24705882352941178,0.9647058823529412,0.5411764705882353,1.0,17 +90,CDL,0.2549019607843137,0.5882352941176471,1.0,1.0,18 +110,N.Rot,0.27450980392156865,0.3803921568627451,0.8392156862745098,1.0,19 +140,Gld,0.2549019607843137,0.27450980392156865,0.6745098039215687,1.0,20 +220,OT,0.1843137254901961,0.9450980392156862,0.6078431372549019,1.0,21 +305,CN-HP,0.9882352941176471,0.7019607843137254,0.21176470588235294,1.0,22 +402,NFT,0.9803921568627451,0.4823529411764706,0.12156862745098039,1.0,23 +505,CN-H,0.8156862745098039,0.1843137254901961,0.0196078431372549,1.0,24 diff --git a/data/oldenburg_blackcap_colors.csv b/examples/plots/oldenburg_blackcap_colors.csv similarity index 95% rename from data/oldenburg_blackcap_colors.csv rename to examples/plots/oldenburg_blackcap_colors.csv index 2c58d6c..c530710 100644 --- a/data/oldenburg_blackcap_colors.csv +++ b/examples/plots/oldenburg_blackcap_colors.csv @@ -20,6 +20,6 @@ CN-H,208,47,5 NFT,250,123,31 Gld,65,70,172 PrVd,226,67,10 -PRVv,254,153,44 +PrVv,254,153,44 SpVl,61,53,139 SpVm,48,18,59