Skip to content

Commit

Permalink
Fixed tests, restructuring iterability of structures
Browse files Browse the repository at this point in the history
  • Loading branch information
vigji committed Jun 2, 2020
1 parent f03e40d commit da69b88
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 81 deletions.
20 changes: 7 additions & 13 deletions atlas_gen/atlas_scripts/mpin_zfish_atlas.py
@@ -1,5 +1,4 @@
from pathlib import Path
import tempfile
import warnings

import nrrd
Expand All @@ -12,13 +11,13 @@
from brainatlas_api.structures import StructureTree

# Specify information about the atlas:
RES_UM = 1.0
RES_UM = 1
VERSION = 2
ATLAS_NAME = f"mpin_zfish"
SPECIES = "Mus musculus"
ATLAS_LINK = "http://www.brain-map.org.com"
SPECIES = "Danio rerio"
ATLAS_LINK = "http://fishatlas.neuro.mpg.de"
CITATION = "Kunst et al 2019, https://doi.org/10.1016/j.neuron.2019.04.034"
ORIENTATION = "ial"
ORIENTATION = "lai"


BASE_URL = r"https://fishatlas.neuro.mpg.de"
Expand Down Expand Up @@ -96,25 +95,21 @@ def collect_all_inplace(
collect_all_inplace(region, traversing_list, download_path, mesh_dict)


# Temporary folder for nrrd files download:
temp_path = Path(tempfile.mkdtemp())
download_dir_path = temp_path / "downloading_path"
download_dir_path.mkdir()

# Download reference:
#####################
reference_url = f"{BASE_URL}/media/brain_browser/Brain/MovieViewBrain/standard_brain_fixed_SYP_T_GAD1b.nrrd"
out_file_path = download_dir_path / "reference.nrrd"
out_file_path = bg_root_dir / "reference.nrrd"

retrieve_over_http(reference_url, out_file_path)

refstack, h = nrrd.read(str(out_file_path))
print(refstack.shape)

# Download structures tree and meshes:
######################################
regions_url = f"{BASE_URL}/neurons/get_brain_regions"

meshes_dir_path = download_dir_path / "meshes_temp_download"
meshes_dir_path = bg_root_dir / "meshes_temp_download"
meshes_dir_path.mkdir(exist_ok=True)

# Download structures hierarchy:
Expand Down Expand Up @@ -143,7 +138,6 @@ def collect_all_inplace(
structures_dict, structures_list, meshes_dir_path, meshes_dict
)

print(structures_list)
# Wrap up, compress, and remove file:0
print(f"Finalising atlas")
wrapup_atlas_from_data(
Expand Down
6 changes: 3 additions & 3 deletions brainatlas_api/bg_atlas.py
Expand Up @@ -94,9 +94,9 @@ def download_extract_file(self):
destination_path.unlink()


class TestAtlas(BrainGlobeAtlas):
atlas_name = "test_allen_100um"
version = "0.1"
class ExampleAtlas(BrainGlobeAtlas):
atlas_name = "example_mouse_100um"
version = "0.2"


class FishAtlas(BrainGlobeAtlas):
Expand Down
42 changes: 31 additions & 11 deletions brainatlas_api/core.py
Expand Up @@ -30,11 +30,15 @@ def __init__(self, path):
self.root_dir = Path(path)
self.metadata = read_json(self.root_dir / METADATA_FILENAME)

# Class for structures:
# Load structures list:
structures_list = read_json(self.root_dir / STRUCTURES_FILENAME)
self.structures = StructuresDict(
structures_list, self.root_dir / MESHES_DIRNAME
)
# Add entry for file paths:
for struct in structures_list:
struct["mesh_filename"] = (
self.root_dir / MESHES_DIRNAME / "{}.obj".format(struct["id"])
)

self.structures = StructuresDict(structures_list,)

self._reference = None
self._annotation = None
Expand All @@ -57,7 +61,9 @@ def hemispheres(self):
if self._hemispheres is None:
# If reference is symmetric generate hemispheres block:
if self.metadata["symmetric"]:
self._hemispheres = make_hemispheres_stack(self.shape)
self._hemispheres = make_hemispheres_stack(
self.metadata["shape"]
)
else:
self._hemispheres = read_tiff(
self.root_dir / HEMISPHERES_FILENAME
Expand All @@ -67,15 +73,29 @@ def hemispheres(self):
def hemisphere_from_coords(self, coords):
return self.hemispheres[_idx_from_coords(coords)]

def structure_from_coords(self, coords):
return self.annotation[_idx_from_coords(coords)]
def structure_from_coords(self, coords, as_acronym=False):
rid = self.annotation[_idx_from_coords(coords)]
if as_acronym:
d = self.structures[rid]
return d["acronym"]
else:
return rid

# Meshes-related methods:
def get_mesh(self, region_id):
return self.structure_mesh_dict[region_id]
def get_from_structure(self, structure, key):
if isinstance(structure, list) or isinstance(structure, tuple):
return [self.get_from_structure(s, key) for s in structure]
else:
return self.structures[structure][key]

def mesh_from_structure(self, region_id):
return self.structures[region_id]["mesh"]

def meshfile_from_structure(self, region_id):
return self.structures[region_id]["mesh_filename"]

def get_brain_mesh(self):
return self.get_mesh_from_name("root")
def root_mesh(self):
return self.mesh_from_structure("root")

# ------- BrainRender methods, might be useful to implement here ------- #

Expand Down
2 changes: 1 addition & 1 deletion brainatlas_api/descriptors.py
Expand Up @@ -37,4 +37,4 @@
ANNOTATION_DTYPE = np.int32

# Standard orientation origin: Anterior, Left, Superior
ATLAS_ORIENTATION = "als"
ATLAS_ORIENTATION = "asl"
49 changes: 30 additions & 19 deletions brainatlas_api/structure_class.py
@@ -1,14 +1,18 @@
import meshio as mio
from collections import UserDict


class Structure(UserDict):
class Structure(dict):
def __getitem__(self, item):
if item == "mesh" and self.data[item] is None:
if item == "mesh" and super().__getitem__(item) is None:
# TODO gracefully fail with warning if no mesh:
self.data[item] = mio.read(self["mesh_filename"])
try:
super().__setitem__(item, mio.read(self["mesh_filename"]))
except (TypeError, mio.ReadError):
raise mio.ReadError(
"No valid mesh for region: {}".format(self.data["acronym"])
)

return self.data[item]
return super().__getitem__(item)

def get_center_of_mass(self):
pass
Expand All @@ -20,7 +24,7 @@ def descendants(self):
pass


class StructuresDict(UserDict):
class StructuresDict(dict):
""" Class to handle dual indexing by either acronym or id.
Parameters
Expand All @@ -29,25 +33,32 @@ class StructuresDict(UserDict):
path to folder containing all meshes .obj files
"""

def __init__(self, structures_list, mesh_dir_path):
def __init__(self, structures_list):
super().__init__()

# Acronym to id map:
self.acronym_to_id_map = {
r["acronym"]: r["id"] for r in structures_list
}

for struct_dict in structures_list:
sid = struct_dict["id"]
mesh_filename = mesh_dir_path / f"{sid}.obj"
self.data[sid] = Structure(
mesh_filename=mesh_filename, mesh=None, **struct_dict
)
for struct in structures_list:
sid = struct["id"]
super().__setitem__(sid, Structure(**struct, mesh=None))

def __getitem__(self, item):
if isinstance(item, int):
return self.data[item]
elif isinstance(item, str):
return self.data[self.acronym_to_id_map[item]]
elif isinstance(item, list):
return [self.__getitem__(i) for i in item]
""" Core implementation of the class support for different indexing.
Parameters
----------
item :
Returns
-------
"""
try:
item = int(item)
except ValueError:
item = self.acronym_to_id_map[item]

return super().__getitem__(int(item))
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -4,3 +4,4 @@ tifffile
treelib
pandas
requests
meshio
8 changes: 5 additions & 3 deletions tests/conftest.py
@@ -1,8 +1,10 @@
import pytest
from brainatlas_api.bg_atlas import TestAtlas
import tempfile
from brainatlas_api.bg_atlas import ExampleAtlas

# import tempfile


@pytest.fixture(scope="module")
def atlas_path():
return TestAtlas(brainglobe_path=tempfile.mkdtemp()).root_dir
# brainglobe_path=tempfile.mkdtemp()
return ExampleAtlas().root_dir
62 changes: 31 additions & 31 deletions tests/test_atlas.py
Expand Up @@ -2,45 +2,38 @@

import numpy as np

from brainatlas_api.bg_atlas import TestAtlas
from brainatlas_api.bg_atlas import ExampleAtlas


@pytest.fixture()
def atlas():
return TestAtlas()
return ExampleAtlas()


def test_initialization(atlas):
assert atlas.metadata == {
"name": "test_allen_100um",
"name": "example_mouse",
"citation": "Wang et al 2020, https://doi.org/10.1016/j.cell.2020.04.007",
"atlas_link": "http://www.brain-map.org.com",
"symmetric": True,
"resolution": [100, 100, 100],
"species": "mouse (Mus musculus)",
"version": "0.1",
"species": "Mus musculus",
"version": "0.2",
"shape": [132, 80, 114],
"trasform_to_bg": [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
],
}


@pytest.mark.parametrize(
"attribute, val",
[
("shape", [132, 80, 114]),
("resolution", [100, 100, 100]),
("name", "test_allen_100um"),
("symmetric", True),
],
)
def test_attributes(atlas, attribute, val):
assert getattr(atlas, attribute) == val


@pytest.mark.parametrize(
"stack_name, val",
[
("reference", [[[146, 155], [153, 157]], [[148, 150], [153, 153]]]),
("annotated", [[[59, 362], [59, 362]], [[59, 362], [59, 362]]]),
("annotation", [[[59, 362], [59, 362]], [[59, 362], [59, 362]]]),
("hemispheres", [[[0, 0], [0, 0]], [[1, 1], [1, 1]]]),
],
)
Expand All @@ -49,30 +42,37 @@ def test_stacks(atlas, stack_name, val):
assert np.allclose(loaded_stack[65:67, 39:41, 57:59], val)


def test_maps(atlas):
assert atlas.acronym_to_id_map == {"root": 997, "grey": 8, "CH": 567}

assert atlas.id_to_acronym_map == {997: "root", 8: "grey", 567: "CH"}
def test_structures(atlas):
assert {s["acronym"]: k for k, s in atlas.structures.items()} == {
"root": 997,
"grey": 8,
"CH": 567,
}
assert atlas.get_from_structure([997, 8, 567], "acronym") == [
"root",
"grey",
"CH",
]


@pytest.mark.parametrize(
"coords", [[39.0, 36.0, 57.0], (39, 36, 57), np.array([39.0, 36.0, 57.0])]
)
def test_data_from_coords(atlas, coords):
assert atlas.get_structure_id_from_coords(coords) == 997
assert atlas.get_structure_name_from_coords(coords) == "root"
assert atlas.get_hemisphere_from_coords(coords) == 0
assert atlas.structure_from_coords(coords) == 997
assert atlas.structure_from_coords(coords, as_acronym=True) == "root"
assert atlas.hemisphere_from_coords(coords) == 0


def test_meshfile_from_id(atlas):
assert (
atlas.get_mesh_file_from_acronym("CH")
atlas.meshfile_from_structure("CH")
== atlas.root_dir / "meshes/567.obj"
)


def test_mesh_from_id(atlas):
# TODO will change depeding on mesh loading package
vert, vnorms, faces, fnorms = atlas.get_mesh_from_id(567)
assert np.allclose(vert[0], [8019.52, 3444.48, 507.104])
assert np.allclose(faces[0], [0, 1, 2])
# TODO will change depending on mesh loading package
mesh = atlas.structures[567]["mesh"]
assert np.allclose(mesh.points[0], [8019.52, 3444.48, 507.104])
assert np.allclose(mesh.cells[0].data[0], [0, 1, 2])
36 changes: 36 additions & 0 deletions tests/test_structure_dict.py
@@ -0,0 +1,36 @@
from brainatlas_api.structure_class import StructuresDict


struct_list = [
{
"acronym": "root",
"id": 997,
"name": "root",
"structure_id_path": [997],
"rgb_triplet": [255, 255, 255],
"mesh_filename": None,
},
{
"acronym": "grey",
"id": 8,
"name": "Basic cell groups and regions",
"structure_id_path": [997, 8],
"rgb_triplet": [191, 218, 227],
"mesh_filename": None,
},
{
"acronym": "CH",
"id": 567,
"name": "Cerebrum",
"structure_id_path": [997, 8, 567],
"rgb_triplet": [176, 240, 255],
"mesh_filename": None,
},
]


def test_structure_indexing():
struct_dict = StructuresDict(struct_list)
assert struct_dict[997] == struct_dict["root"]
assert struct_dict[997.0] == struct_dict["root"]
assert struct_dict["997"] == struct_dict["root"]

0 comments on commit da69b88

Please sign in to comment.