diff --git a/brainglobe_atlasapi/atlas_generation/atlas_scripts/kim_devccf_mouse.py b/brainglobe_atlasapi/atlas_generation/atlas_scripts/kim_devccf_mouse.py index ea0e8ae5..5fa6b084 100644 --- a/brainglobe_atlasapi/atlas_generation/atlas_scripts/kim_devccf_mouse.py +++ b/brainglobe_atlasapi/atlas_generation/atlas_scripts/kim_devccf_mouse.py @@ -2,10 +2,10 @@ import json import time from pathlib import Path -import pooch import numpy as np import pandas as pd +import pooch from brainglobe_utils.IO.image import load_nii from rich.progress import track @@ -15,7 +15,6 @@ create_region_mesh, ) from brainglobe_atlasapi.atlas_generation.wrapup import wrapup_atlas_from_data - from brainglobe_atlasapi.config import DEFAULT_WORKDIR from brainglobe_atlasapi.structure_tree_util import get_structures_tree @@ -29,101 +28,123 @@ PACKAGER = "Carlo Castoldi " ATLAS_FILE_URL = "https://doi.org/10.6084/m9.figshare.26377171.v1" -TIMEPOINTS = ( - "E11.5", - "E13.5", - "E15.5", - "E18.5", - "P04", - "P14", - "P56" -) +TIMEPOINTS = ("E11.5", "E13.5", "E15.5", "E18.5", "P04", "P14", "P56") MODALITIES = ( - "LSFM", # Light Sheet Fluorescence Microscopy + "LSFM", # Light Sheet Fluorescence Microscopy "MRI-adc", # MRI Apparent Diffusion Coefficient "MRI-dwi", # MRI Difusion Weighted Imaging - "MRI-fa", # MRI Fractional Anisotropy + "MRI-fa", # MRI Fractional Anisotropy "MRI-MTR", # MRI Magnetization Transfer Ratio - "MRI-T2" # MRI T2-weighted + "MRI-T2", # MRI T2-weighted ) + def pooch_init(download_dir_path: Path, timepoints: list[str]) -> pooch.Pooch: utils.check_internet_connection() - empty_registry = {a+".zip": None for a in [*timepoints, "DevCCFv1_OntologyStructure.xlsx"]} + empty_registry = { + a + ".zip": None + for a in [*timepoints, "DevCCFv1_OntologyStructure.xlsx"] + } p = pooch.create( path=download_dir_path, base_url="doi:10.6084/m9.figshare.26377171.v1/", - registry=empty_registry + registry=empty_registry, + ) + p.load_registry( + Path(__file__).parent.parent / "hashes" / (ATLAS_NAME + ".txt") ) - p.load_registry(Path(__file__).parent.parent/"hashes"/(ATLAS_NAME+".txt")) return p + def fetch_animal(pooch_: pooch.Pooch, age: str, modality: str): assert age in TIMEPOINTS, f"Unknown age timepoint: '{age}'" - archive = age+".zip" + archive = age + ".zip" if modality == "LSFM": resolution_um = 50 elif modality in MODALITIES: match age: case "E11.5": resolution_um = 31.5 - raise RuntimeError("Can't generate an atlas at embryonic stage with MRI reference images.") + raise RuntimeError( + "Can't generate an atlas at embryonic stage with MRI reference images." + ) case "E13.5": resolution_um = 34 - raise RuntimeError("Can't generate an atlas at embryonic stage with MRI reference images.") + raise RuntimeError( + "Can't generate an atlas at embryonic stage with MRI reference images." + ) case "E15.5": resolution_um = 37.5 - raise RuntimeError("Can't generate an atlas at embryonic stage with MRI reference images.") + raise RuntimeError( + "Can't generate an atlas at embryonic stage with MRI reference images." + ) case "E18.5": resolution_um = 40 - raise RuntimeError("Can't generate an atlas at embryonic stage with MRI reference images.") + raise RuntimeError( + "Can't generate an atlas at embryonic stage with MRI reference images." + ) case _: resolution_um = 50 else: raise RuntimeError(f"Unknown reference image modality: {modality}") members = [ f"{age}/{age.replace('.','-')}_DevCCF_Annotations_{resolution_um}um.nii.gz", - f"{age}/{age.replace('.','-')}_{modality}_{resolution_um}um.nii.gz" + f"{age}/{age.replace('.','-')}_{modality}_{resolution_um}um.nii.gz", ] - annotations_path, reference_path = pooch_.fetch(archive, - progressbar=True, - processor=pooch.Unzip(extract_dir=".", members=members) - ) + annotations_path, reference_path = pooch_.fetch( + archive, + progressbar=True, + processor=pooch.Unzip(extract_dir=".", members=members), + ) annotations = load_nii(annotations_path, as_array=True) reference = load_nii(reference_path, as_array=True) return annotations, reference, resolution_um + def fetch_ontology(pooch_: pooch.Pooch): - devccfv1_path = pooch_.fetch("DevCCFv1_OntologyStructure.xlsx", progressbar=True) + devccfv1_path = pooch_.fetch( + "DevCCFv1_OntologyStructure.xlsx", progressbar=True + ) xl = pd.ExcelFile(devccfv1_path) # xl.sheet_names # it has two excel sheets - # 'DevCCFv1_Ontology', 'README' + # 'DevCCFv1_Ontology', 'README' df = xl.parse("DevCCFv1_Ontology", header=1) df = df[["Acronym", "ID16", "Name", "Structure ID Path16", "R", "G", "B"]] - df.rename(columns={ - "Acronym": "acronym", - "ID16": "id", - "Name": "name", - "Structure ID Path16": "structure_id_path", - "R": "r", - "G": "g", - "B": "b" - }, inplace=True) + df.rename( + columns={ + "Acronym": "acronym", + "ID16": "id", + "Name": "name", + "Structure ID Path16": "structure_id_path", + "R": "r", + "G": "g", + "B": "b", + }, + inplace=True, + ) structures = list(df.to_dict(orient="index").values()) for structure in structures: if structure["acronym"] == "mouse": structure["acronym"] = "root" structure_path = structure["structure_id_path"] - structure["structure_id_path"] = [int(id) for id in structure_path.strip("/").split("/")] - structure["rgb_triplet"] = [structure["r"], structure["g"], structure["b"]] + structure["structure_id_path"] = [ + int(id) for id in structure_path.strip("/").split("/") + ] + structure["rgb_triplet"] = [ + structure["r"], + structure["g"], + structure["b"], + ] del structure["r"] del structure["g"] del structure["b"] return structures -def create_meshes(output_path: str|Path, - structures, annotation_volume, root_id): + +def create_meshes( + output_path: str | Path, structures, annotation_volume, root_id +): if not isinstance(output_path, Path): output_path = Path(output_path) output_path.mkdir(exist_ok=True) @@ -141,7 +162,9 @@ def create_meshes(output_path: str|Path, # Mesh creation closing_n_iters = 2 - decimate_fraction = 0.2 # fraction of the original number of vertices to be kept + decimate_fraction = ( + 0.2 # fraction of the original number of vertices to be kept + ) smooth = False # smooth meshes after creation start = time.time() for node in track( @@ -151,7 +174,7 @@ def create_meshes(output_path: str|Path, # total=5, description="Creating meshes", ): - output_file = output_path/f"{node.identifier}.obj" + output_file = output_path / f"{node.identifier}.obj" if output_file.exists(): # print(f"mesh already existing: {output_file.exists()} - {output_file}") continue @@ -176,6 +199,7 @@ def create_meshes(output_path: str|Path, ) return output_path + def create_mesh_dict(structures, meshes_dir_path): meshes_dict = dict() structures_with_mesh = [] @@ -198,21 +222,25 @@ def create_mesh_dict(structures, meshes_dir_path): ) return meshes_dict, structures_with_mesh + class HideChoicesRawTextHelpFormatter(argparse.RawTextHelpFormatter): def _get_help_string(self, action): help_text = action.help if action.choices: - help_text = help_text.split('(choices')[0].strip() + help_text = help_text.split("(choices")[0].strip() return help_text def _format_action_invocation(self, action): if action.option_strings: - return ', '.join(action.option_strings) + return ", ".join(action.option_strings) if action.metavar: return action.metavar return super()._format_action_invocation(action) -arg_parser = argparse.ArgumentParser(formatter_class=HideChoicesRawTextHelpFormatter) + +arg_parser = argparse.ArgumentParser( + formatter_class=HideChoicesRawTextHelpFormatter +) timepoints_help = """the age timepoint at which the atlas will be generated. Options are: - E11.5 ⅂ @@ -222,7 +250,7 @@ def _format_action_invocation(self, action): - P04 ⅂ - P14 |- Postnatal day - P56 ⅃ - + By default, it generates an atlas for each timepoint. """ modalities_help = """the acquisition modalities with which the reference image was captured. @@ -236,37 +264,49 @@ def _format_action_invocation(self, action): By default, it uses LSFM """ -arg_parser.add_argument("-t", "--timepoints", default=TIMEPOINTS, type=str, nargs="+", choices=TIMEPOINTS, help=timepoints_help) -arg_parser.add_argument("-m", "--modality", default="LSFM", type=str, choices=MODALITIES, help=modalities_help) +arg_parser.add_argument( + "-t", + "--timepoints", + default=TIMEPOINTS, + type=str, + nargs="+", + choices=TIMEPOINTS, + help=timepoints_help, +) +arg_parser.add_argument( + "-m", + "--modality", + default="LSFM", + type=str, + choices=MODALITIES, + help=modalities_help, +) if __name__ == "__main__": params = vars(arg_parser.parse_args()) timepoints = params["timepoints"] modality = params["modality"] atlas_name = f"{ATLAS_NAME}_{modality}" - bg_root_dir = DEFAULT_WORKDIR/atlas_name - download_dir_path = bg_root_dir/"downloads" + bg_root_dir = DEFAULT_WORKDIR / atlas_name + download_dir_path = bg_root_dir / "downloads" download_dir_path.mkdir(exist_ok=True, parents=True) pooch_ = pooch_init(download_dir_path, timepoints) structures = fetch_ontology(pooch_) # save regions list json: - with open(bg_root_dir/"structures.json", "w") as f: + with open(bg_root_dir / "structures.json", "w") as f: json.dump(structures, f) for age in timepoints: atlas_name = f"{atlas_name}_{age.replace('.', '-')}" - annotation_volume, reference_volume, resolution_um = fetch_animal(pooch_, age, modality) - atlas_dir = bg_root_dir/atlas_name + annotation_volume, reference_volume, resolution_um = fetch_animal( + pooch_, age, modality + ) + atlas_dir = bg_root_dir / atlas_name atlas_dir.mkdir(exist_ok=True) print(f"Saving atlas data at {atlas_dir}") # Create meshes: - meshes_dir_path = atlas_dir/"meshes" - create_meshes( - meshes_dir_path, - structures, - annotation_volume, - ROOT_ID - ) + meshes_dir_path = atlas_dir / "meshes" + create_meshes(meshes_dir_path, structures, annotation_volume, ROOT_ID) meshes_dict, structures_with_mesh = create_mesh_dict( structures, meshes_dir_path ) @@ -278,7 +318,7 @@ def _format_action_invocation(self, action): citation=CITATION, atlas_link=ATLAS_LINK, species=SPECIES, - resolution=(resolution_um,)*3, + resolution=(resolution_um,) * 3, orientation=ORIENTATION, root_id=ROOT_ID, reference_stack=reference_volume, @@ -287,9 +327,9 @@ def _format_action_invocation(self, action): meshes_dict=meshes_dict, working_dir=atlas_dir, atlas_packager=PACKAGER, - hemispheres_stack=None, # it is symmetric + hemispheres_stack=None, # it is symmetric cleanup_files=False, compress=True, scale_meshes=True, ) - print("Done. Atlas generated at: ", output_filename) \ No newline at end of file + print("Done. Atlas generated at: ", output_filename)