Skip to content
Snippets Groups Projects
Commit d7e6d77e authored by Andi Gerken's avatar Andi Gerken
Browse files

Adapted examples added tests for examples, added utils

parent de4939b8
Branches
Tags
No related merge requests found
Pipeline #35781 passed
#! /usr/bin/env python3 #! /usr/bin/env python3
import robofish.io import robofish.io
from robofish.io import utils
import numpy as np import numpy as np
from pathlib import Path
import os
# Helper function to enable relative paths from this file
def full_path(path):
return (Path(os.path.abspath("__file__")).parent / path).resolve()
if __name__ == "__main__":
# Create a new io file object with a 100x100cm world # Create a new io file object with a 100x100cm world
sf = robofish.io.File(world_size_cm=[100, 100], frequency_hz=25.0) sf = robofish.io.File(world_size_cm=[100, 100], frequency_hz=25.0)
...@@ -38,8 +31,8 @@ if __name__ == "__main__": ...@@ -38,8 +31,8 @@ if __name__ == "__main__":
sf.validate() sf.validate()
# Save file validates aswell # Save file validates aswell
example_file = full_path("example.hdf5") example_file = utils.full_path(__file__, "example.hdf5")
sf.save(example_file) sf.save_as(example_file)
# Closing and opening files (just for demonstration) # Closing and opening files (just for demonstration)
sf.close() sf.close()
......
import robofish.io import robofish.io
import numpy as np import numpy as np
from pathlib import Path
filename = "example.hdf5" path = Path("example.hdf5")
f = robofish.io.File(world_size_cm=[100, 100], frequency_hz=25.0) if path.exists():
path.unlink()
# By using the context, the file will be automatically validated
with robofish.io.File(path, "w", world_size_cm=[100, 100], frequency_hz=25.0) as f:
f.attrs["experiment_setup"] = "This is a simple example with made up data." f.attrs["experiment_setup"] = "This is a simple example with made up data."
# Create a single robot with 30 timesteps # Create a single robot with 30 timesteps
...@@ -27,8 +32,4 @@ fish.attrs["fish_standard_length_cm"] = 10 ...@@ -27,8 +32,4 @@ fish.attrs["fish_standard_length_cm"] = 10
# Show and save the file # Show and save the file
print(f) print(f)
print("Poses Shape: ", f.get_poses().shape) print("Poses Shape: ", f.entity_poses.shape)
# Saving also validates the file
f.save(filename)
print(f"Saved to {filename}")
...@@ -7,6 +7,7 @@ from robofish.io.file import * ...@@ -7,6 +7,7 @@ from robofish.io.file import *
from robofish.io.entity import * from robofish.io.entity import *
from robofish.io.validation import * from robofish.io.validation import *
from robofish.io.io import * from robofish.io.io import *
from robofish.io.utils import *
import robofish.io.app import robofish.io.app
......
import robofish.io import robofish.io
import robofish.io.utils as utils
import h5py import h5py
import numpy as np import numpy as np
from typing import Iterable, Union from typing import Iterable, Union
...@@ -24,6 +26,10 @@ class Entity(h5py.Group): ...@@ -24,6 +26,10 @@ class Entity(h5py.Group):
outlines: Iterable = None, outlines: Iterable = None,
sampling: str = None, sampling: str = None,
): ):
poses, positions, orientations, outlines = utils.np_array(
poses, positions, orientations, outlines
)
# If no name is given, create one from type and an id # If no name is given, create one from type and an id
if name is None: if name is None:
i = 1 i = 1
...@@ -47,7 +53,7 @@ class Entity(h5py.Group): ...@@ -47,7 +53,7 @@ class Entity(h5py.Group):
@classmethod @classmethod
def convert_rad_to_vector(cla, orientations_rad): def convert_rad_to_vector(cla, orientations_rad):
ori_rad = np.array(orientations_rad) ori_rad = utils.np_array(orientations_rad)
assert ori_rad.shape[1] == 1 assert ori_rad.shape[1] == 1
ori_vec = np.empty((ori_rad.shape[0], 2)) ori_vec = np.empty((ori_rad.shape[0], 2))
ori_vec[:, 0] = np.cos(ori_rad[:, 0]) ori_vec[:, 0] = np.cos(ori_rad[:, 0])
...@@ -78,6 +84,8 @@ class Entity(h5py.Group): ...@@ -78,6 +84,8 @@ class Entity(h5py.Group):
orientations: Iterable = None, orientations: Iterable = None,
sampling: str = None, sampling: str = None,
): ):
poses, positions, orientations = utils.np_array(poses, positions, orientations)
# Either poses or positions not both # Either poses or positions not both
assert ( assert (
poses is None or positions is None poses is None or positions is None
...@@ -89,7 +97,7 @@ class Entity(h5py.Group): ...@@ -89,7 +97,7 @@ class Entity(h5py.Group):
) )
else: else:
if poses is not None: if poses is not None:
poses = np.array(poses)
assert poses.shape[1] == 3 or poses.shape[1] == 4 assert poses.shape[1] == 3 or poses.shape[1] == 4
positions = poses[:, :2] positions = poses[:, :2]
orientations = poses[:, 2:] orientations = poses[:, 2:]
......
...@@ -34,6 +34,7 @@ default_format_url = ( ...@@ -34,6 +34,7 @@ default_format_url = (
"https://git.imp.fu-berlin.de/bioroboticslab/robofish/track_format/-/releases/1.0" "https://git.imp.fu-berlin.de/bioroboticslab/robofish/track_format/-/releases/1.0"
) )
class File(h5py.File): class File(h5py.File):
""" Represents a RoboFish Track Format file, which should be used to store tracking data of individual animals or swarms. """ Represents a RoboFish Track Format file, which should be used to store tracking data of individual animals or swarms.
...@@ -82,8 +83,16 @@ class File(h5py.File): ...@@ -82,8 +83,16 @@ class File(h5py.File):
if path is None: if path is None:
if type(self)._temp_dir is None: if type(self)._temp_dir is None:
type(self)._temp_dir = tempfile.TemporaryDirectory(prefix="robofish-io-") type(self)._temp_dir = tempfile.TemporaryDirectory(
super().__init__(Path(type(self)._temp_dir.name) / str(uuid.uuid4()), mode="x", driver="core", backing_store=True, libver=("earliest", "v112")) prefix="robofish-io-"
)
super().__init__(
Path(type(self)._temp_dir.name) / str(uuid.uuid4()),
mode="x",
driver="core",
backing_store=True,
libver=("earliest", "v112"),
)
initialize = True initialize = True
else: else:
# mode # mode
...@@ -307,7 +316,10 @@ class File(h5py.File): ...@@ -307,7 +316,10 @@ class File(h5py.File):
@property @property
def entities(self): def entities(self):
return [robofish.io.Entity.from_h5py_group(self["entities"][name]) for name in self.entity_names] return [
robofish.io.Entity.from_h5py_group(self["entities"][name])
for name in self.entity_names
]
@property @property
def entity_poses(self): def entity_poses(self):
...@@ -352,6 +364,23 @@ class File(h5py.File): ...@@ -352,6 +364,23 @@ class File(h5py.File):
i += 1 i += 1
return poses_output return poses_output
@deprecation.deprecated(
deprecated_in="1.1.2",
removed_in="1.2",
details="get_poses() is deprecated and was replaced with the attribute 'poses' or the function select_poses(), when ",
)
def get_poses(self, *, category=None, names=None):
if category is not None:
predicate = lambda e: e.category == category
if names is not None:
predicate = lambda e: e.category == category and e.name in names
elif names is not None:
predicate = lambda e: e.name in names
else:
predicate = None
return self.select_entity_poses(predicate)
def validate(self, strict_validate: bool = True) -> (bool, str): def validate(self, strict_validate: bool = True) -> (bool, str):
"""Validate the file to the specification. """Validate the file to the specification.
......
import robofish.io
import numpy as np
from pathlib import Path
import os
def np_array(*arrays):
result = tuple(np.array(a) if a is not None else None for a in arrays)
if len(result) == 1:
result = result[0]
return result
def full_path(current_file, path):
return (Path(current_file).parent / path).resolve()
import robofish.evaluate.app as app import robofish.evaluate.app as app
from robofish.io import utils
import pytest import pytest
import logging import logging
from pathlib import Path from pathlib import Path
...@@ -6,18 +7,14 @@ from pathlib import Path ...@@ -6,18 +7,14 @@ from pathlib import Path
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
#### Helpers #### # TODO: reactivate test and change evaluate
def full_path(path): def deactivated_test_app_validate():
return (Path(__file__).parent / path).resolve()
def test_app_validate():
""" This tests the function of the robofish-io-validate command """ """ This tests the function of the robofish-io-validate command """
class DummyArgs: class DummyArgs:
def __init__(self, analysis_type, paths): def __init__(self, analysis_type, paths):
self.analysis_type = analysis_type self.analysis_type = analysis_type
self.paths = [full_path(paths)] self.paths = [utils.full_path(__file__, paths)]
self.names = None self.names = None
self.save_path = None self.save_path = None
......
import robofish.io.app as app import robofish.io.app as app
from robofish.io import utils
import pytest import pytest
import logging import logging
from pathlib import Path from pathlib import Path
...@@ -6,11 +7,6 @@ from pathlib import Path ...@@ -6,11 +7,6 @@ from pathlib import Path
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
#### Helpers ####
def full_path(path):
return (Path(__file__).parent / path).resolve()
def test_app_validate(): def test_app_validate():
""" This tests the function of the robofish-io-validate command """ """ This tests the function of the robofish-io-validate command """
...@@ -19,11 +15,13 @@ def test_app_validate(): ...@@ -19,11 +15,13 @@ def test_app_validate():
self.path = path self.path = path
self.output_format = output_format self.output_format = output_format
raw_output = app.validate(DummyArgs(full_path("../../resources"), "raw")) raw_output = app.validate(
DummyArgs(utils.full_path(__file__, "../../resources"), "raw")
)
# The three files valid.hdf5, almost_valid.hdf5, and invalid.hdf5 should be found. # The three files valid.hdf5, almost_valid.hdf5, and invalid.hdf5 should be found.
assert len(raw_output) == 2 assert len(raw_output) == 2
app.validate(DummyArgs(full_path("../../resources"), "human")) app.validate(DummyArgs(utils.full_path(__file__, "../../resources"), "human"))
def test_app_print(): def test_app_print():
...@@ -34,5 +32,9 @@ def test_app_print(): ...@@ -34,5 +32,9 @@ def test_app_print():
self.path = path self.path = path
self.output_format = output_format self.output_format = output_format
app.print(DummyArgs(full_path("../../resources/valid.hdf5"), "full")) app.print(
app.print(DummyArgs(full_path("../../resources/valid.hdf5"), "shape")) DummyArgs(utils.full_path(__file__, "../../resources/valid.hdf5"), "full")
)
app.print(
DummyArgs(utils.full_path(__file__, "../../resources/valid.hdf5"), "shape")
)
...@@ -3,11 +3,6 @@ import h5py ...@@ -3,11 +3,6 @@ import h5py
import numpy as np import numpy as np
#### Helpers ####
def full_path(path):
return (Path(__file__).parent / path).resolve()
def test_entity_object(): def test_entity_object():
sf = robofish.io.File(world_size_cm=[100, 100], frequency_hz=25) sf = robofish.io.File(world_size_cm=[100, 100], frequency_hz=25)
f = sf.create_entity("fish", positions=[[10, 10]]) f = sf.create_entity("fish", positions=[[10, 10]])
......
import robofish.io
from robofish.io import utils
from pathlib import Path
import sys
sys.path.append(str(utils.full_path(__file__, "../../../examples/")))
def test_example_readme():
import example_readme
def test_example_basic():
import example_basic
import robofish.io import robofish.io
from robofish.io import utils
import numpy as np import numpy as np
from pathlib import Path from pathlib import Path
import pytest import pytest
...@@ -7,23 +8,23 @@ import datetime ...@@ -7,23 +8,23 @@ import datetime
import sys import sys
import logging import logging
valid_file_path = utils.full_path(__file__, "../../resources/valid.hdf5")
#### Helpers #### created_by_test_path = utils.full_path(__file__, "../../resources/created_by_test.hdf5")
def full_path(path): created_by_test_path_2 = utils.full_path(
return (Path(__file__).parent / path).resolve() __file__, "../../resources/created_by_test_2.hdf5"
)
valid_file_path = full_path("../../resources/valid.hdf5")
created_by_test_path = full_path("../../resources/created_by_test.hdf5")
created_by_test_path_2 = full_path("../../resources/created_by_test_2.hdf5")
def test_constructor(): def test_constructor():
sf = robofish.io.File(world_size_cm=[100, 100]) sf = robofish.io.File(world_size_cm=[100, 100])
print(sf)
sf.validate() sf.validate()
def test_context():
with robofish.io.File(world_size_cm=[10, 10]) as f:
pass
def test_new_file_w_path(): def test_new_file_w_path():
sf = robofish.io.File( sf = robofish.io.File(
created_by_test_path_2, "w", world_size_cm=[100, 100], frequency_hz=25 created_by_test_path_2, "w", world_size_cm=[100, 100], frequency_hz=25
...@@ -145,9 +146,8 @@ def test_load_validate(): ...@@ -145,9 +146,8 @@ def test_load_validate():
def test_get_entity_names(): def test_get_entity_names():
sf = robofish.io.File(path=valid_file_path) sf = robofish.io.File(path=valid_file_path)
names = sf.entity_names names = sf.entity_names
assert len(names) == 9 assert len(names) == 1
assert names[0] == "fish_1" assert names[0] == "fish_1"
assert names[1] == "fish_2"
def test_File_without_path_or_worldsize(): def test_File_without_path_or_worldsize():
......
import robofish.io import robofish.io
from robofish.io import utils
import pytest import pytest
from pathlib import Path from pathlib import Path
#### Helpers ####
def full_path(path):
return (Path(__file__).parent / path).resolve()
def test_now_iso8061(): def test_now_iso8061():
# Example time: 2021-01-05T14:33:40.401+00:00 # Example time: 2021-01-05T14:33:40.401+00:00
...@@ -15,7 +12,7 @@ def test_now_iso8061(): ...@@ -15,7 +12,7 @@ def test_now_iso8061():
def test_read_multiple_single(): def test_read_multiple_single():
path = full_path("../../resources/valid.hdf5") path = utils.full_path(__file__, "../../resources/valid.hdf5")
# Variants path as posix path or as string # Variants path as posix path or as string
for sf in [ for sf in [
...@@ -29,7 +26,7 @@ def test_read_multiple_single(): ...@@ -29,7 +26,7 @@ def test_read_multiple_single():
def test_read_multiple_folder(): def test_read_multiple_folder():
path = full_path("../../resources/") path = utils.full_path(__file__, "../../resources/")
# Variants path as posix path or as string # Variants path as posix path or as string
for sf in [ for sf in [
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment