From 7ef6f0a4b2e4869facf70f42d694c010d5038922 Mon Sep 17 00:00:00 2001 From: polarbean <harry.carey95@gmail.com> Date: Wed, 21 Aug 2024 17:02:35 +0200 Subject: [PATCH] add support for brainglobe atlases --- PyNutil/main.py | 60 ++++++++++++++--------- messing_around_files/brainglobe.py | 22 +++++++++ test/test1.json | 3 +- test/test10_PyNutil_web.json | 2 +- test/test2.json | 3 +- test/test3.json | 3 +- test/test4_2017.json | 3 +- test/test5_NOP_s037.json | 3 +- test/test6_artifical.json | 2 +- test/test7_PyNutil.json | 2 +- test/test8_PyNutil_bigcaudoputamen.json | 2 +- workflows/calculate_objects_per_region.py | 4 ++ testOOP.py => workflows/testOOP.py | 10 ++-- 13 files changed, 77 insertions(+), 42 deletions(-) create mode 100644 messing_around_files/brainglobe.py rename testOOP.py => workflows/testOOP.py (57%) diff --git a/PyNutil/main.py b/PyNutil/main.py index a64249a..57194a4 100644 --- a/PyNutil/main.py +++ b/PyNutil/main.py @@ -5,6 +5,8 @@ from .counting_and_load import label_points, pixel_count_per_region import json import pandas as pd from datetime import datetime +import numpy as np +import brainglobe_atlasapi import os @@ -19,8 +21,8 @@ class PyNutil: The path to the alignment JSON file. colour : int The colour of the segmentation data to extract. - volume_path : str - The name of the atlas volume to use. + atlas_name : str + The name of the atlas volume to use. Uses BrainGlobe API name settings_file : str, optional The path to a JSON file containing the above parameters. @@ -76,7 +78,8 @@ class PyNutil: segmentation_folder=None, alignment_json=None, colour=None, - volume_path=None, + atlas_name=None, + atlas_resolution_micron=None, settings_file=None, ) -> None: self.config, self.metadata_path = metadata_loader.load_config() @@ -87,28 +90,29 @@ class PyNutil: segmentation_folder = settings["segmentation_folder"] alignment_json = settings["alignment_json"] colour = settings["colour"] - volume_path = settings["volume_path"] + atlas_name = settings["atlas_name"] except KeyError as exc: raise KeyError( - "settings file must contain segmentation_folder, alignment_json, colour, and volume_path" + "settings file must contain segmentation_folder, alignment_json, colour, and atlas_name" ) from exc # check if any values are None - if None in [segmentation_folder, alignment_json, colour, volume_path]: + if None in [segmentation_folder, alignment_json, colour, atlas_name]: raise ValueError( "segmentation_folder, alignment_json, colour, and volume_path must all be specified and not be None" ) - if volume_path not in self.config["annotation_volumes"]: - raise ValueError( - f"Atlas {volume_path} not found in config file, valid atlases are: \n{' , '.join(list(self.config['annotation_volumes'].keys()))}" - ) + # if atlas_name not in self.config["annotation_volumes"]: + # raise ValueError( + # f"Atlas {atlas_name} not found in config file, valid atlases are: \n{' , '.join(list(self.config['annotation_volumes'].keys()))}" + # ) self.segmentation_folder = segmentation_folder self.alignment_json = alignment_json self.colour = colour - self.atlas = volume_path - self.atlas_volume, self.atlas_labels = self.load_atlas_data() + self.atlas_name = atlas_name + self.atlas_volume, self.atlas_labels = self.load_atlas_data(atlas_name=atlas_name) + ###This is just because of the migration to BrainGlobe - def load_atlas_data(self): + def load_atlas_data(self, atlas_name): """Loads the atlas volume and labels from disk. Returns @@ -119,16 +123,26 @@ class PyNutil: """ # load the metadata json as well as the path to stored data files # this could potentially be moved into init - atlas_root_path = self.config["annotation_volume_directory"] - current_atlas_path = self.config["annotation_volumes"][self.atlas]["volume"] print("loading atlas volume") - start_time = datetime.now() - atlas_volume = read_atlas_volume(f"{atlas_root_path}{current_atlas_path}") - time_taken = datetime.now() - start_time - print(f"atlas volume loaded in: {time_taken} ✅") - atlas_label_path = self.config["annotation_volumes"][self.atlas]["labels"] - print("loading atlas labels") - atlas_labels = pd.read_csv(f"{atlas_root_path}{atlas_label_path}") + atlas = brainglobe_atlasapi.BrainGlobeAtlas(atlas_name=atlas_name) + atlas_structures = {'idx':[i['id'] for i in atlas.structures_list], + 'name':[i['name'] for i in atlas.structures_list], + 'r':[i['rgb_triplet'][0] for i in atlas.structures_list], + 'g':[i['rgb_triplet'][1] for i in atlas.structures_list], + 'b':[i['rgb_triplet'][2] for i in atlas.structures_list] + } + atlas_structures['idx'].insert(0,0) + atlas_structures['name'].insert(0,'Clear Label') + atlas_structures['r'].insert(0,0) + atlas_structures['g'].insert(0,0) + atlas_structures['b'].insert(0,0) + + atlas_labels = pd.DataFrame(atlas_structures) + if "allen_mouse_" in atlas_name: + print("reorienting allen atlas into quicknii space...") + atlas_volume = np.transpose(atlas.annotation,[2,0,1])[:,::-1,::-1] + else: + atlas_volume = atlas.annotation print("atlas labels loaded ✅") return atlas_volume, atlas_labels @@ -249,7 +263,7 @@ class PyNutil: all_region_df = self.atlas_labels.merge(ra, on="idx", how="left") current_df_new = all_region_df.merge( current_df, on="idx", how="left", suffixes=(None, "_y") - ).drop(columns=["a", "VIS", "MSH", "name_y", "r_y", "g_y", "b_y"]) + ).drop(columns=["name_y", "r_y", "g_y", "b_y"]) current_df_new["area_fraction"] = ( current_df_new["pixel_count"] / current_df_new["region_area"] ) diff --git a/messing_around_files/brainglobe.py b/messing_around_files/brainglobe.py new file mode 100644 index 0000000..fdd39f8 --- /dev/null +++ b/messing_around_files/brainglobe.py @@ -0,0 +1,22 @@ +import brainglobe_atlasapi +import pandas as pd +import nrrd +import matplotlib.pyplot as plt +import numpy as np +brainglobe_atlasapi.list_atlases.show_atlases() +allen = brainglobe_atlasapi.BrainGlobeAtlas('allen_mouse_25um') + +keys = allen.structures.keys() + + + +##current structure format +labels = pd.read_csv(r'/home/harryc/github/PyNutil/PyNutil/metadata/annotation_volumes/allen2017_colours.csv') + +labels + + +orig_vol = nrrd.read(r"/home/harryc/github/PyNutil/PyNutil/metadata/annotation_volumes/annotation_25_reoriented_2017.nrrd") + +plt.imshow(orig_vol[0][200]>0) + diff --git a/test/test1.json b/test/test1.json index 0355d8c..ca83423 100644 --- a/test/test1.json +++ b/test/test1.json @@ -1,5 +1,4 @@ -{ "volume_path": "allen2017", - "label_path": "annotation_volumes/allen2017_colours.csv", +{ "atlas_name": "allen_mouse_25um", "segmentation_folder": "test_data/ttA_2877_NOP_segmentations", "alignment_json": "test_data/ttA_2877_NOP_horizontal_final_2017.json", "nonlinear": true, diff --git a/test/test10_PyNutil_web.json b/test/test10_PyNutil_web.json index fd8a95a..397836f 100644 --- a/test/test10_PyNutil_web.json +++ b/test/test10_PyNutil_web.json @@ -1,5 +1,5 @@ { - "volume_path": "allen2017", + "atlas_name": "allen_mouse_25um", "label_path": "PyNutil/annotation_volumes/allen2017_colours.csv", "segmentation_folder": "PyNutil/test_data/PyTest_web", "alignment_json": "PyNutil/test_data/PyNutil_test.waln", diff --git a/test/test2.json b/test/test2.json index 9618297..f3ba1f3 100644 --- a/test/test2.json +++ b/test/test2.json @@ -1,5 +1,4 @@ -{ "volume_path": "../annotation_volumes/annotation_10_reoriented_2022.nrrd", - "label_path": "../annotation_volumes/allen2022_colours_updated.csv", +{ "atlas_name": "allen_mouse_25um", "segmentation_folder": "../test_data/oneSection15", "alignment_json": "../test_data/C68_nonlinear_no_markers.json", "nonlinear": true, diff --git a/test/test3.json b/test/test3.json index 1c9343f..fb6838e 100644 --- a/test/test3.json +++ b/test/test3.json @@ -1,5 +1,4 @@ -{ "volume_path": "allen2017", - "label_path": "annotation_volumes/allen2017_colours.csv", +{ "atlas_name": "allen_mouse_25um", "segmentation_folder": "test_data/oneSection15", "alignment_json": "test_data/C68_nonlinear.json", "nonlinear": true, diff --git a/test/test4_2017.json b/test/test4_2017.json index bdb3959..4545491 100644 --- a/test/test4_2017.json +++ b/test/test4_2017.json @@ -1,5 +1,4 @@ -{ "volume_path": "allen2017", - "label_path": "annotation_volumes/allen2017_colours.csv", +{ "atlas_name": "allen_mouse_25um", "segmentation_folder": "test_data/oneSection15", "alignment_json": "test_data/C68_nonlinear_no_markers.json", "nonlinear": true, diff --git a/test/test5_NOP_s037.json b/test/test5_NOP_s037.json index d53d90f..3155140 100644 --- a/test/test5_NOP_s037.json +++ b/test/test5_NOP_s037.json @@ -1,5 +1,4 @@ -{ "volume_path": "allen2017", - "label_path": "annotation_volumes/allen2017_colours.csv", +{ "atlas_name": "allen_mouse_25um", "segmentation_folder": "test_data/ttA_2877_NOP_s037_seg", "alignment_json": "test_data/ttA_2877_NOP_horizontal_final_2017.json", "nonlinear": false, diff --git a/test/test6_artifical.json b/test/test6_artifical.json index c8f58ec..0de2663 100644 --- a/test/test6_artifical.json +++ b/test/test6_artifical.json @@ -1,4 +1,4 @@ -{ "volume_path": "../annotation_volumes/annotation_10_reoriented_2017.nrrd", +{ "atlas_name": "allen_mouse_25um", "label_path": "../annotation_volumes/allen2017_colours.csv", "segmentation_folder": "../test_data/ttA_2877_NOP_segmentations", "alignment_json": "../test_data/ttA_2877_NOP_horizontal_final_2017.json", diff --git a/test/test7_PyNutil.json b/test/test7_PyNutil.json index 07d54af..9cbfa89 100644 --- a/test/test7_PyNutil.json +++ b/test/test7_PyNutil.json @@ -1,4 +1,4 @@ -{ "volume_path": "allen2017", +{ "atlas_name": "allen_mouse_25um", "label_path": "annotation_volumes/allen2017_colours.csv", "segmentation_folder": "test_data/PyTest_seg", "alignment_json": "test_data/PyNutil_testdataset_Nonlin_SY.json", diff --git a/test/test8_PyNutil_bigcaudoputamen.json b/test/test8_PyNutil_bigcaudoputamen.json index 7c26469..089d7f9 100644 --- a/test/test8_PyNutil_bigcaudoputamen.json +++ b/test/test8_PyNutil_bigcaudoputamen.json @@ -1,5 +1,5 @@ { - "volume_path": "allen2017", + "atlas_name": "allen_mouse_25um", "label_path": "PyNutil/annotation_volumes/allen2017_colours.csv", "segmentation_folder": "PyNutil/test_data/PyTest_bigcaudoputamen_seg", "alignment_json": "PyNutil/test_data/PyNutil_testdataset_Nonlin_SY_fixed_bigcaudoputamen.json", diff --git a/workflows/calculate_objects_per_region.py b/workflows/calculate_objects_per_region.py index a72a549..dcb3fbd 100644 --- a/workflows/calculate_objects_per_region.py +++ b/workflows/calculate_objects_per_region.py @@ -1,3 +1,7 @@ +import os +import sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + import cv2 from skimage import measure diff --git a/testOOP.py b/workflows/testOOP.py similarity index 57% rename from testOOP.py rename to workflows/testOOP.py index 63cb085..52b11fc 100644 --- a/testOOP.py +++ b/workflows/testOOP.py @@ -1,10 +1,10 @@ +import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from PyNutil import PyNutil import os -os.chdir("..") - - -pnt = PyNutil(settings_file=r"PyNutil/test/test10_PyNutil_web.json") +pnt = PyNutil(settings_file=r"test/test8_PyNutil_bigcaudoputamen.json") ##use_flat can be set to True if you want to use the flat file ## for method select between "all", "per_pixel" and "per_object" @@ -12,7 +12,7 @@ pnt.get_coordinates(object_cutoff=0, method="all", use_flat=False) pnt.quantify_coordinates() -pnt.save_analysis("PyNutil/outputs/test10_PyNutil_web_all") +pnt.save_analysis("../PyNutil/outputs/test9_PyNutil_bigcaudoputamen_new") # remove name, r, g, b, from pixel_ # add to region_areas df -- GitLab