Skip to content

Commit

Permalink
Merge pull request #12 from TeamNCMC/config-brainglobe
Browse files Browse the repository at this point in the history
More explicit support for Brainglobe atlases
  • Loading branch information
GuillaumeLeGoc authored Jan 15, 2025
2 parents c13da04 + d10bbb3 commit 696cd84
Show file tree
Hide file tree
Showing 20 changed files with 214 additions and 100 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,8 @@ mkdocs serve
Head to [http://localhost:8000/](http://localhost:8000/) from a web browser.
The documentation is built with [MkDocs](https://www.mkdocs.org/) using the [Material theme](https://squidfunk.github.io/mkdocs-material/). [KaTeX](https://katex.org/) CSS and fonts are embedded instead of using a CDN, and are under a [MIT license](https://opensource.org/license/MIT).
## Contributing
## Credits
`cuisto` has been primarly developed by [Guillaume Le Goc](https://legoc.fr) in [Julien Bouvier's lab](https://www.bouvier-lab.com/) at [NeuroPSI](https://neuropsi.cnrs.fr/). The clever name was found by Aurélie Bodeau.
6 changes: 3 additions & 3 deletions configs/config_template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
# - matplotlib colors : https://matplotlib.org/stable/gallery/color/color_demo.html
#
# Configuration file part of the python cuisto package.
# version : 2.1
# version : 2.2
########################################################################################

object_type = "Cells" # name of QuPath base classification (eg. without the ": subclass" part)
segmentation_tag = "cells" # type of segmentation, matches directory name, used only in the full pipeline

[atlas] # information related to the atlas used
name = "allen_mouse_10um" # brainglobe-atlasapi atlas name
type = "brain" # brain or cord (eg. registration done in ABBA or abba_python)
midline = 5700 # midline Z coordinates (left/right limit) in microns
type = "abba" # abba or brainglobe : registration done with regular ABBA (except allen_mouse_10um_java) or abba_python)
midline = 5700 # midline coordinates in microns (left/right limit, Z for abba, X for brainglobe)
outline_structures = ["root", "CB", "MY", "P"] # structures to show an outline of in heatmaps

[channels] # information related to imaging channels
Expand Down
16 changes: 15 additions & 1 deletion cuisto/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ def __init__(self, config_file):
else:
self.bg_atlas = None

# name axes to handle ABBA/Brainglobe atlases differences
if self.atlas["type"] in ("abba", "brain"):
self.Xname = "Atlas_X" # antero-posterior (rostro-caudal)
self.Yname = "Atlas_Y" # infero-superior (dorso-ventral)
self.Zname = "Atlas_Z" # left-right (medio-lateral)
elif self.atlas["type"] in ("brainglobe", "cord"):
self.Xname = "Atlas_Z" # antero-posterior (rostro-caudal)
self.Yname = "Atlas_Y" # infero-superior (dorso-ventral)
self.Zname = "Atlas_X" # left-right (medio-lateral)
else:
raise ValueError(
f"{self.atlas['type']} not supported, choose either 'abba' or 'brainglobe'."
)

self.get_blacklist()
self.get_leaves_list()

Expand Down Expand Up @@ -125,6 +139,6 @@ def get_hue_palette(self, mode: str) -> dict:
}
else:
palette = None
warnings.warn(f"hue={self.regions["display"]["hue"]} not supported.")
warnings.warn(f"hue={self.regions['display']['hue']} not supported.")

return palette
24 changes: 12 additions & 12 deletions cuisto/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,12 +796,12 @@ def plot_2D_distributions(df: pd.DataFrame, cfg):
cbar_kws = dict(label="count")

# determine which axes are going to be inverted
if cfg.atlas["type"] == "brain":
if cfg.atlas["type"] in ("abba", "brain"):
cor_invertx = True
cor_inverty = False
top_invertx = True
top_inverty = False
elif cfg.atlas["type"] == "cord":
elif cfg.atlas["type"] in ("brainglobe", "cord"):
cor_invertx = False
cor_inverty = False
top_invertx = True
Expand All @@ -812,8 +812,8 @@ def plot_2D_distributions(df: pd.DataFrame, cfg):
outline_kws["view"] = "sagittal"
nice_joint_plot(
df,
x="Atlas_X",
y="Atlas_Y",
x=cfg.Xname,
y=cfg.Yname,
xlabel="Rostro-caudal (mm)",
ylabel="Dorso-ventral (mm)",
outline_kws=outline_kws,
Expand All @@ -825,8 +825,8 @@ def plot_2D_distributions(df: pd.DataFrame, cfg):
outline_kws["view"] = "coronal"
nice_joint_plot(
df,
x="Atlas_Z",
y="Atlas_Y",
x=cfg.Zname,
y=cfg.Yname,
xlabel="Medio-lateral (mm)",
ylabel="Dorso-ventral (mm)",
invertx=cor_invertx,
Expand All @@ -841,8 +841,8 @@ def plot_2D_distributions(df: pd.DataFrame, cfg):
outline_kws["view"] = "top"
nice_joint_plot(
df,
x="Atlas_X",
y="Atlas_Z",
x=cfg.Xname,
y=cfg.Zname,
xlabel="Rostro-caudal (mm)",
ylabel="Medio-lateral (mm)",
invertx=top_invertx,
Expand All @@ -864,8 +864,8 @@ def plot_2D_distributions(df: pd.DataFrame, cfg):
_ = nice_heatmap(
df,
animals,
x="Atlas_X",
y="Atlas_Y",
x=cfg.Xname,
y=cfg.Yname,
xlabel="Rostro-caudal (mm)",
ylabel="Dorso-ventral (mm)",
invertx=True,
Expand All @@ -879,8 +879,8 @@ def plot_2D_distributions(df: pd.DataFrame, cfg):
_ = nice_heatmap(
df,
animals,
x="Atlas_Z",
y="Atlas_Y",
x=cfg.Zname,
y=cfg.Yname,
xlabel="Medio-lateral (mm)",
ylabel="Dorso-ventral (mm)",
inverty=True,
Expand Down
22 changes: 16 additions & 6 deletions cuisto/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,17 @@ def cat_csv_dir(directory, **kwargs) -> pd.DataFrame:


def cat_json_dir(
directory: str, hemisphere_names: dict, atlas: BrainGlobeAtlas
directory: str,
hemisphere_names: dict,
atlas: BrainGlobeAtlas,
xname: str = "Atlas_X",
yname: str = "Atlas_Y",
zname: str = "Atlas_Z",
) -> pd.DataFrame:
"""
Scans a directory for json files and concatenate them in a single DataFrame.
The json files must be generated with 'pipelineImportExport.groovy" or
The json files must be generated with 'pipelineImportExport.groovy" or
'exportFibersAtlasCoordinates.groovy' from a QuPath project.
Parameters
Expand All @@ -129,6 +134,9 @@ def cat_json_dir(
something else (eg. "Ipsi." and "Contra.").
atlas : BrainGlobeAtlas
Atlas to read regions from.
xname, yname, zname : str, optional
How to name x, y and z coordinates. Default is ABBA convention, eg. Atlas_X,
Atlas_Y and Atlas_Z, resp. corresponding to AP, DV, ML.
Returns
-------
Expand Down Expand Up @@ -160,9 +168,9 @@ def cat_json_dir(
.reset_index()
.rename(
columns=dict(
x="Atlas_X",
y="Atlas_Y",
z="Atlas_Z",
x=xname,
y=yname,
z=zname,
index="Object ID",
classification="Classification",
)
Expand All @@ -177,7 +185,9 @@ def cat_json_dir(
df["Object type"] = "Detection"

# add brain regions
df = utils.add_brain_region(df, atlas, col="Parent")
df = utils.add_brain_region(
df, atlas, col="Parent", xname=xname, yname=yname, zname=zname
)

return df

Expand Down
16 changes: 8 additions & 8 deletions cuisto/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def process_animal(
df_detections,
cfg.hemispheres["names"],
cfg.atlas["midline"],
col="Atlas_Z",
col=cfg.Zname,
atlas_type=cfg.atlas["type"],
)
# add detection channel
Expand All @@ -104,19 +104,19 @@ def process_animal(
df_detections["Atlas_DV"],
df_detections["Atlas_ML"],
) = utils.ccf_to_stereo(
df_detections["Atlas_X"],
df_detections["Atlas_Y"],
df_detections["Atlas_Z"],
df_detections[cfg.Xname],
df_detections[cfg.Yname],
df_detections[cfg.Zname],
)
else:
(
df_detections["Atlas_AP"],
df_detections["Atlas_DV"],
df_detections["Atlas_ML"],
) = (
df_detections["Atlas_X"],
df_detections["Atlas_Y"],
df_detections["Atlas_Z"],
df_detections[cfg.Xname],
df_detections[cfg.Yname],
df_detections[cfg.Zname],
)

# - Computations
Expand Down Expand Up @@ -270,7 +270,7 @@ def process_animals(
# -- Saving
if out_fmt:
outdir = os.path.join(wdir, "quantification")
outfile = f"{cfg.object_type.lower()}_{cfg.atlas["type"]}_{'-'.join(animals)}.{out_fmt}"
outfile = f"{cfg.object_type.lower()}_{cfg.atlas['type']}_{'-'.join(animals)}.{out_fmt}"
dfs = dict(
df_regions=df_regions,
df_coordinates=df_coordinates,
Expand Down
41 changes: 25 additions & 16 deletions cuisto/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ def add_hemisphere(
hemisphere_names: dict,
midline: float = 5700,
col: str = "Atlas_Z",
atlas_type: str = "brain",
atlas_type: str = "abba",
) -> pd.DataFrame:
"""
Add hemisphere (left/right) as a measurement for detections or annotations.
Expand All @@ -321,9 +321,10 @@ def add_hemisphere(
col : str, optional
Name of the column containing the Z coordinate (medio-lateral) in microns.
Default is "Atlas_Z".
atlas_type : {"brain", "cord"}, optional
Type of atlas used for registration. Required because the brain atlas is swapped
between left and right while the spinal cord atlas is not. Default is "brain".
atlas_type : {"abba", "brainglobe"}, optional
Type of atlas used for registration. Required because the brain atlas provided
by ABBA is swapped between left and right while the brainglobe atlases are not.
Default is "abba".
Returns
-------
Expand All @@ -340,12 +341,12 @@ def add_hemisphere(

if kind == "detection":
# use midline
if atlas_type == "brain":
# brain atlas : beyond midline, it's left
if atlas_type in ("abba", "brain"):
# regular ABBA atlas : beyond midline, it's left
df.loc[df[col] >= midline, "hemisphere"] = hemisphere_names["Left"]
df.loc[df[col] < midline, "hemisphere"] = hemisphere_names["Right"]
elif atlas_type == "cord":
# cord atlas : below midline, it's left
elif atlas_type in ("brainglibe", "cord"):
# brainglobe atlas : below midline, it's left
df.loc[df[col] <= midline, "hemisphere"] = hemisphere_names["Left"]
df.loc[df[col] > midline, "hemisphere"] = hemisphere_names["Right"]

Expand Down Expand Up @@ -399,7 +400,12 @@ def add_channel(


def add_brain_region(
df: pd.DataFrame, atlas: BrainGlobeAtlas | None, col="Parent"
df: pd.DataFrame,
atlas: BrainGlobeAtlas | None,
col: str = "Parent",
xname: str = "Atlas_X",
yname: str = "Atlas_Z",
zname: str = "Altas_Z",
) -> pd.DataFrame:
"""
Add brain region to a DataFrame with `Atlas_X`, `Atlas_Y` and `Atlas_Z` columns.
Expand All @@ -417,6 +423,9 @@ def add_brain_region(
atlas : BrainGlobeAtlas or None
col : str, optional
Column in which to put the regions acronyms. Default is "Parent".
xname, yname, zname : str, optional
Name of the x, y, z coordinates columns in `df`. They should correspond to what
is expected by brainglobe-atlasapi : x is AP, y is DV and Z is ML.
Returns
-------
Expand All @@ -435,15 +444,15 @@ def add_brain_region(
lims = atlas.shape_um # out of brain

# set out-of-brain objects at 0 so we get "root" as their parent
df_in.loc[(df_in["Atlas_X"] >= lims[0]) | (df_in["Atlas_X"] < 0), "Atlas_X"] = 0
df_in.loc[(df_in["Atlas_Y"] >= lims[1]) | (df_in["Atlas_Y"] < 0), "Atlas_Y"] = 0
df_in.loc[(df_in["Atlas_Z"] >= lims[2]) | (df_in["Atlas_Z"] < 0), "Atlas_Z"] = 0
df_in.loc[(df_in[xname] >= lims[0]) | (df_in[xname] < 0), xname] = 0
df_in.loc[(df_in[yname] >= lims[1]) | (df_in[yname] < 0), yname] = 0
df_in.loc[(df_in[zname] >= lims[2]) | (df_in[zname] < 0), zname] = 0

# build the multi index, in pixels and integers
ixyz = (
df_in["Atlas_X"].divide(res[0]).astype(int),
df_in["Atlas_Y"].divide(res[1]).astype(int),
df_in["Atlas_Z"].divide(res[2]).astype(int),
df_in[xname].divide(res[0]).astype(int),
df_in[yname].divide(res[1]).astype(int),
df_in[zname].divide(res[2]).astype(int),
)
# convert i, j, k indices in raveled indices
linear_indices = np.ravel_multi_index(ixyz, dims=atlas.annotation.shape)
Expand Down Expand Up @@ -556,7 +565,7 @@ def get_data_coverage(df: pd.DataFrame, col="Atlas_AP", by="animal") -> pd.DataF
df : pd.DataFrame
_description_
col : str, optional
Key in `df`, default is "Atlas_X".
Key in `df`, default is "Atlas_AP".
by : str, optional
Key in `df` , default is "animal".
Expand Down
8 changes: 4 additions & 4 deletions docs/demo_notebooks/cells_distributions.ipynb

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions docs/demo_notebooks/fibers_length_multi.ipynb

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/guide-install-abba.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ You can head to [the ABBA documentation](https://abba-documentation.readthedocs.

You will find below installation instructions for the regular [ABBA Fiji plugin](#abba-fiji), which proposes only the mouse and rat brain atlases. To be able to use the [Brainglobe atlases](https://brainglobe.info/documentation/brainglobe-atlasapi/usage/atlas-details.html#available-atlases), you will need the [Python version](#abba-python). The two can be installed alongside each other.

!!! danger "Important"
Please have a look at this [few considerations](tips-abba.md#abba-and-brainglobe-atlases) related to coordinates systems in ABBA and Brainglobe atlases and their consequences on downstream analysis.

## ABBA Fiji
### Install Fiji
Install the "batteries-included" distribution of ImageJ, Fiji, from the [official website](https://fiji.sc/).
Expand Down
4 changes: 4 additions & 0 deletions docs/guide-register-abba.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

The [ABBA documentation](https://abba-documentation.readthedocs.io/en/latest/) is quite extensive and contains guided tutorials and a video tutorial. You should therefore check it out ! Nevertheless, you will find below some quick reminders.

!!! tip

Using either the regular Fiji version of ABBA or the [Python version](guide-install-abba.md#abba-python) (to have access to Brainglobe atlases) will lead to the same interface, depicted below. Nevertheless, keep in mind what kind of atlas (packaged with ABBA or Brainglobe atlas) as it needs to be configured, see more information on [this page](tips-abba.md#abba-and-brainglobe-atlases).

## Import a QuPath project
Always use ABBA with a QuPath project, if you import the images directly it will not be possible to export the results back to QuPath. In the toolbar, head to `Import > Import QuPath Project`.

Expand Down
Binary file added docs/images/ccfv3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 13 additions & 13 deletions docs/main-configuration-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ The configuration files are in the TOML file format, that are basically text fil

Most lines of each template file are commented to explain what each parameter do.

## config.toml
??? abstract "Click to see an example file"
```toml title="config_template.toml"
--8<-- "configs/config_template.toml"
```
This file is used to configure `cuisto` behavior. It specifies what to compute, how, and display parameters such as colors associated to each classifications, hemisphere names, distributions bins limits...

!!! warning
When editing your config.toml file, you're allowed to modify the *keys* **only** in the `[channels]` section.

??? example "Click for a more readable parameters explanation"
--8<-- "docs/api-config-config.md"

## atlas_blacklist.toml
??? abstract "Click to see an example file"
```toml title="atlas_blacklist.toml"
Expand All @@ -30,19 +43,6 @@ Keys `name`, `acronym` and `members` should belong to a `[section]`.
+ `acronym` is how the region will be refered to. It can be a new acronym, or an existing one.
+ `members` is a list of acronyms of atlas regions that should be part of the new one.

## config.toml
??? abstract "Click to see an example file"
```toml title="config_template.toml"
--8<-- "configs/config_template.toml"
```
This file is used to configure `cuisto` behavior. It specifies what to compute, how, and display parameters such as colors associated to each classifications, hemisphere names, distributions bins limits...

!!! warning
When editing your config.toml file, you're allowed to modify the *keys* **only** in the `[channels]` section.

??? example "Click for a more readable parameters explanation"
--8<-- "docs/api-config-config.md"

## info.toml
??? abstract "Click to see an example file"
```toml title="info_template.toml"
Expand Down
Loading

0 comments on commit 696cd84

Please sign in to comment.