Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atlas packaging template #400

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fcb1bf9
First version of the template script
PolarBean Aug 28, 2024
6dc6052
make example_mouse follow the new template
PolarBean Aug 28, 2024
37a75fb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2024
193a78b
Fix error in example mouse
PolarBean Aug 28, 2024
cd9bafe
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2024
d96ed9d
mixed up reference and annotation
PolarBean Aug 28, 2024
2254886
added mandatory import to template
PolarBean Aug 28, 2024
813b251
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2024
c521a2f
remove import from example
PolarBean Aug 28, 2024
602dbef
added mandatory import to template
PolarBean Aug 28, 2024
f4b0452
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2024
9009dc2
include case where variables should be passed between functions
PolarBean Aug 29, 2024
1383b14
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mou…
PolarBean Sep 11, 2024
2b0d42e
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
c47ca80
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
262a07c
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
c51231c
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mou…
PolarBean Sep 11, 2024
fca9c7b
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mou…
PolarBean Sep 11, 2024
8052298
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 11, 2024
91c09f7
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
3ebdbd4
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
6cc3222
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
73387c0
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
23b3a16
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 11, 2024
4740c06
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mou…
PolarBean Sep 11, 2024
f6253e0
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mou…
PolarBean Sep 11, 2024
af0f450
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mou…
PolarBean Sep 11, 2024
91b97c8
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/template_sc…
PolarBean Sep 11, 2024
d9bc6e3
keep lines shorter than limit and only run code in __main__
PolarBean Sep 11, 2024
2ee5c7b
reformat example mouse
PolarBean Sep 11, 2024
1e68bed
use pooch to validate hash
PolarBean Sep 11, 2024
924e484
add bg_root_dir global
PolarBean Sep 11, 2024
d643653
fix references to annotation and reference
PolarBean Sep 11, 2024
78fe6d5
fix error in the way structure id paths were shown in the example
PolarBean Sep 12, 2024
642e0bb
fix typo
PolarBean Sep 12, 2024
536281f
Update brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mou…
PolarBean Sep 18, 2024
df352c3
adjust comment suggestion to be compliant with the 79 character line …
PolarBean Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 111 additions & 68 deletions brainglobe_atlasapi/atlas_generation/atlas_scripts/example_mouse.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
__version__ = "2"
__version__ = 1 # The version of the atlas in the brainglobe_atlasapi, this is internal, if this is the first time this atlas has been added the value should be 1
ATLAS_NAME = "example_mouse" # The expected format is FirstAuthor_SpeciesCommonName, i.e., kleven_rat
CITATION = "Wang et al 2020, https://doi.org/10.1016/j.cell.2020.04.007" # DOI of the most relevant citable document
SPECIES = "Mus musculus" # The scientific name of the species, i.e., Rattus norvegicus
ATLAS_LINK = "http://www.brain-map.org" # The URL for the data files
ORIENTATION = "asr" # The orientation of the atlas
ROOT_ID = 997 # The id of the highest level of the atlas. This is commonly called root or brain.
RESOLUTION = 100 # The resolution of your volume in microns.

PolarBean marked this conversation as resolved.
Show resolved Hide resolved
from pathlib import Path

Expand All @@ -8,60 +15,103 @@
from requests import exceptions
from tqdm import tqdm

from brainglobe_atlasapi.atlas_generation.wrapup import wrapup_atlas_from_data

def download_resources():
"""
Download the necessary resources for the atlas.
"""
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
pass

def create_atlas(working_dir, resolution):
# Specify information about the atlas:
RES_UM = resolution # 100
ATLAS_NAME = "example_mouse"
SPECIES = "Mus musculus"
ATLAS_LINK = "http://www.brain-map.org"
CITATION = "Wang et al 2020, https://doi.org/10.1016/j.cell.2020.04.007"
ORIENTATION = "asr"

# Temporary folder for nrrd files download:
download_dir_path = working_dir / "downloading_path"
def retrieve_template_and_reference():
"""
Retrieve the desired template and reference as two numpy arrays.

Returns:
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
tuple: A tuple containing two numpy arrays. The first array is the reference volume, and the second array is the annotated volume.
"""
# Create temporary download directory
download_dir_path = Path.cwd() / "downloading_path"
download_dir_path.mkdir(exist_ok=True)
PolarBean marked this conversation as resolved.
Show resolved Hide resolved

# Download annotated and template volume:
#########################################
# Setup the reference space cache
spacecache = ReferenceSpaceCache(
manifest=download_dir_path / "manifest.json",
# downloaded files are stored relative to here
resolution=RES_UM,
resolution=RESOLUTION,
reference_space_key="annotation/ccf_2017",
# use the latest version of the CCF
)

# Download
annotated_volume, _ = spacecache.get_annotation_volume()
template_volume, _ = spacecache.get_template_volume()
print("Download completed...")
# Download annotated and template volumes
reference_volume, _ = spacecache.get_template_volume()
annotation_volume, _ = spacecache.get_annotation_volume()
return reference_volume, annotated_volume


def retrieve_hemisphere_map():
"""
Retrieve a hemisphere map for the atlas.

If your atlas is asymmetrical, you may want to use a hemisphere map. This is an array in the same shape as your template,
with 0's marking the left hemisphere, and 1's marking the right.

If your atlas is symmetrical, ignore this function.

PolarBean marked this conversation as resolved.
Show resolved Hide resolved
# Download structures tree and meshes:
######################################
oapi = OntologiesApi() # ontologies
struct_tree = spacecache.get_structure_tree() # structures tree
Returns:
numpy.array or None: A numpy array representing the hemisphere map, or None if the atlas is symmetrical.
"""
return None


def retrieve_structure_information():
"""
Retrieve the structures tree and meshes.

PolarBean marked this conversation as resolved.
Show resolved Hide resolved
Returns:
pandas.DataFrame: A DataFrame containing the atlas information.
"""
download_dir_path = Path.cwd() / "downloading_path"
oapi = OntologiesApi()
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
spacecache = ReferenceSpaceCache(
manifest=download_dir_path / "manifest.json",
resolution=RESOLUTION,
reference_space_key="annotation/ccf_2017",
)
struct_tree = spacecache.get_structure_tree() # Download structures tree

# Find id of set of regions with mesh:
select_set = (
"Structures whose surfaces are represented by a precomputed mesh"
)

mesh_set_ids = [
s["id"]
for s in oapi.get_structure_sets()
if s["description"] == select_set
]

structs_with_mesh = struct_tree.get_structures_by_set_id(mesh_set_ids)[:3]

# Directory for mesh saving:
meshes_dir = working_dir / "mesh_temp_download"
# Loop over structures, remove entries not used
for struct in structs_with_mesh:
[
struct.pop(k)
for k in ["graph_id", "structure_set_ids", "graph_order"]
]
return structs_with_mesh


def retrieve_or_construct_meshes():
"""
This function should return a dictionary of ids and corresponding paths to mesh files.
Some atlases are packaged with mesh files, in these cases we should use these files.
Then this function should download those meshes. In other cases we need to construct
the meshes ourselves. For this we have helper functions to achieve this.
"""
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
oapi = OntologiesApi()
space = ReferenceSpaceApi()
meshes_dir = Path.cwd() / "mesh_temp_download"
meshes_dir.mkdir(exist_ok=True)
PolarBean marked this conversation as resolved.
Show resolved Hide resolved

meshes_dict = dict()
structs_with_mesh = retrieve_structure_information()

for s in tqdm(structs_with_mesh):
name = s["id"]
filename = meshes_dir / f"{name}.obj"
Expand All @@ -74,41 +124,34 @@ def create_atlas(working_dir, resolution):
meshes_dict[name] = filename
except (exceptions.HTTPError, ConnectionError):
print(s)

# Loop over structures, remove entries not used:
for struct in structs_with_mesh:
[
struct.pop(k)
for k in ["graph_id", "structure_set_ids", "graph_order"]
]

# Wrap up, compress, and remove file:
print("Finalising atlas")
output_filename = wrapup_atlas_from_data(
atlas_name=ATLAS_NAME,
atlas_minor_version=__version__,
citation=CITATION,
atlas_link=ATLAS_LINK,
species=SPECIES,
resolution=(RES_UM,) * 3,
orientation=ORIENTATION,
root_id=997,
reference_stack=template_volume,
annotation_stack=annotated_volume,
structures_list=structs_with_mesh,
meshes_dict=meshes_dict,
working_dir=working_dir,
hemispheres_stack=None,
cleanup_files=False,
compress=True,
)

return output_filename


if __name__ == "__main__":
# Generated atlas path:
bg_root_dir = Path.home() / "brainglobe_workingdir" / "example"
bg_root_dir.mkdir(exist_ok=True)

# create_atlas(working_dir, 100)
return meshes_dict


### If the code above this line has been filled correctly, nothing needs to be edited below.
bg_root_dir = Path.home() / "brainglobe_workingdir" / ATLAS_NAME
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
bg_root_dir.mkdir(exist_ok=True)
download_resources()
reference_volume, annotated_volume = retrieve_template_and_reference()
hemispheres_stack = retrieve_hemisphere_map()
structures = retrieve_structure_information()
meshes_dict = retrieve_or_construct_meshes()

output_filename = wrapup_atlas_from_data(
atlas_name=ATLAS_NAME,
atlas_minor_version=__version__,
citation=CITATION,
atlas_link=ATLAS_LINK,
species=SPECIES,
resolution=(RESOLUTION,) * 3,
orientation=ORIENTATION,
root_id=ROOT_ID,
reference_stack=reference_volume,
annotation_stack=annotated_volume,
structures_list=structures,
meshes_dict=meshes_dict,
working_dir=bg_root_dir,
hemispheres_stack=hemispheres_stack,
cleanup_files=False,
compress=True,
scale_meshes=True,
)
111 changes: 111 additions & 0 deletions brainglobe_atlasapi/atlas_generation/atlas_scripts/template_script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from brainglobe_atlasapi.atlas_generation.wrapup import wrapup_atlas_from_data
PolarBean marked this conversation as resolved.
Show resolved Hide resolved

PolarBean marked this conversation as resolved.
Show resolved Hide resolved
###Metadata
__version__ = 1 # The version of the atlas in the brainglobe_atlasapi, this is internal, if this is the first time this atlas has been added the value should be 1
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
ATLAS_NAME = None # The expected format is FirstAuthor_SpeciesCommonName, ie; kleven_rat
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
CITATION = None # DOI of the most relevant citable document
SPECIES = None # The scientific name of the species, ie; Rattus norvegicus
ATLAS_LINK = None # The URL for the data files
ORIENTATION = None # The orientation of the atlas, for more information on how to determine this click here: ........
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
ROOT_ID = None # The id of the highest level of the atlas. This is commonly called root or brain. Include some information on what to do if your atlas is not hierarchical
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include some information on what to do if your atlas is not hierarchical

Not sure I understand this: is this information we should include to help people running this script?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in people will use this script to package their own atlases. We should explain how to handle particular edge cases they may run into. for example what if the atlas is not hierarchical? Do we then just say treat everything having a structure_id_path of brain, region? or what if their atlas doesnt have RGB colours assigned to each region, is it then acceptable for them to randomly generate colours? I think the template should either explain these cases or point to a resource that does.

RESOLUTION = None # the resolution of your volume in microns. details on how to format this parameter for non isotropic datasets or datasets with multiple resolutions.


def download_resources():
"""
Download the necessary resources for the atlas.

If possible, please use the Pooch library to retrieve any resources.
"""
pass


def retrieve_template_and_reference():
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
"""
Retrieve the desired template and reference as two numpy arrays.

Returns:
tuple: A tuple containing two numpy arrays. The first array is the template volume, and the second array is the reference volume.
"""
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
template = None
reference = None
return template, reference


def retrieve_hemisphere_map():
"""
Retrieve a hemisphere map for the atlas.

If your atlas is asymmetrical, you may want to use a hemisphere map. This is an array in the same shape as your template,
with 0's marking the left hemisphere, and 1's marking the right.

If your atlas is symmetrical, ignore this function.

Returns:
numpy.array or None: A numpy array representing the hemisphere map, or None if the atlas is symmetrical.
"""
return None


def retrieve_structure_information():
"""
This function should return a pandas DataFrame with information about your atlas.

The DataFrame should be in the following format:

╭─────────────┬───────────────────────────────────┬─────────┬───────────────────────┬───────────────────╮
| id | name | acronym | structure_id_path | rgb_triplet |
| | | | | |
├─────────────┼───────────────────────────────────┼─────────┼───────────────────────┼───────────────────┤
| 997 | root | root | [] | [255, 255, 255] |
├─────────────┼───────────────────────────────────┼─────────┼───────────────────────┼───────────────────┤
| 8 | Basic cell groups and regions | grey | [997] | [191, 218, 227] |
├─────────────┼───────────────────────────────────┼─────────┼───────────────────────┼───────────────────┤
| 567 | Cerebrum | CH | [997, 8] | [176, 240, 255] |
╰─────────────┴───────────────────────────────────┴─────────┴───────────────────────┴───────────────────╯
PolarBean marked this conversation as resolved.
Show resolved Hide resolved

Returns:
pandas.DataFrame: A DataFrame containing the atlas information.
"""
return None


def retrieve_or_construct_meshes():
"""
This function should return a dictionary of ids and corresponding paths to mesh files.
Some atlases are packaged with mesh files, in these cases we should use these files.
Then this function should download those meshes. In other cases we need to construct
the meshes ourselves. For this we have helper functions to achieve this.
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
"""
meshes_dict = {}
return meshes_dict


### If the code above this line has been filled correctly, nothing needs to be edited below (unless variables need to be passed between the functions).
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
bg_root_dir = Path.home() / "brainglobe_workingdir" / ATLAS_NAME
bg_root_dir.mkdir(exist_ok=True)
download_resources()
template_volume, reference_volume = retrieve_template_and_reference()
PolarBean marked this conversation as resolved.
Show resolved Hide resolved
hemispheres_stack = retrieve_hemisphere_map()
structures = retrieve_structure_information()
meshes_dict = retrieve_or_construct_meshes()

output_filename = wrapup_atlas_from_data(
atlas_name=ATLAS_NAME,
atlas_minor_version=__version__,
citation=CITATION,
atlas_link=ATLAS_LINK,
species=SPECIES,
resolution=(RESOLUTION,) * 3,
orientation=ORIENTATION,
root_id=ROOT_ID,
reference_stack=template_volume,
annotation_stack=annotated_volume,
structures_list=structures,
meshes_dict=meshes_dict,
working_dir=working_dir,
hemispheres_stack=None,
cleanup_files=False,
compress=True,
scale_meshes=True,
)
Loading