diff --git a/README.md b/README.md index 8e534b6154f46fbe5b82e4830a13857d0f1b9831..677e3a4d077fc09e1c21b3eb3b01166e52e41268 100644 --- a/README.md +++ b/README.md @@ -31,37 +31,33 @@ We show a simple example below. More examples can be found in ```examples/``` import robofish.io import numpy as np -filename = "example.hdf5" - -f = robofish.io.File(world_size_cm=[100, 100], frequency_hz=25.0) -f.attrs["experiment_setup"] = "This is a simple example with made up data." - -# Create a single robot with 30 timesteps -# positions are passed separately -# orientations are passed as with two columns -> orientation_x and orientation_y -f.create_entity( - category="robot", - name="robot", - positions=np.zeros((100, 2)), - orientations=np.ones((100, 2)) * [0, 1], -) - -# Create fishes with 30 poses (x, y, orientation_rad) -poses = np.zeros((100, 3)) -poses[:, 0] = np.arange(-50, 50) -poses[:, 1] = np.arange(-50, 50) -poses[:, 2] = np.arange(0, 2 * np.pi, step=2 * np.pi / 100) -fish = f.create_entity("fish", poses=poses) -fish.attrs["species"] = "My rotating spaghetti fish" -fish.attrs["fish_standard_length_cm"] = 10 - -# Show and save the file -print(f) -print("Poses Shape: ", f.get_poses().shape) - -# Saving also validates the file -f.save(filename) -print(f"Saved to {filename}") + +# By using the context, the file will be automatically validated +with robofish.io.File("Example.hdf5", "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." + + # Create a single robot with 30 timesteps + # positions are passed separately + # orientations are passed as with two columns -> orientation_x and orientation_y + f.create_entity( + category="robot", + name="robot", + positions=np.zeros((100, 2)), + orientations=np.ones((100, 2)) * [0, 1], + ) + + # Create fishes with 30 poses (x, y, orientation_rad) + poses = np.zeros((100, 3)) + poses[:, 0] = np.arange(-50, 50) + poses[:, 1] = np.arange(-50, 50) + poses[:, 2] = np.arange(0, 2 * np.pi, step=2 * np.pi / 100) + fish = f.create_entity("fish", poses=poses) + fish.attrs["species"] = "My rotating spaghetti fish" + fish.attrs["fish_standard_length_cm"] = 10 + + # Show and save the file + print(f) + print("Poses Shape: ", f.entity_poses.shape) ``` diff --git a/examples/example_basic.ipynb b/examples/example_basic.ipynb index 5445fe80256a9ee78bc0ae8e3c439135750b41b8..3569083dc85945861dea2aecdba701dbfe2d3327 100644 --- a/examples/example_basic.ipynb +++ b/examples/example_basic.ipynb @@ -14,84 +14,101 @@ "['fish_1', 'fish_2', 'fish_3', 'obstacle_1', 'robot']\n", "\n", "All poses\n", - "[[[8.86584103e-01 2.35670820e-01 5.41754842e-01 4.49850202e-01]\n", - " [8.15511882e-01 4.78223324e-01 6.29803419e-01 1.12592392e-01]\n", - " [1.53732300e-01 7.24954247e-01 9.38574493e-01 4.65665817e-01]\n", - " [9.10354614e-01 4.47880208e-01 3.81429136e-01 9.67544317e-01]\n", - " [6.07822955e-01 5.20158827e-01 8.17965686e-01 8.42760384e-01]]\n", + "[[[1.36782631e-01 4.68791187e-01 2.42182687e-01 6.48771763e-01]\n", + " [7.93363988e-01 3.50356251e-01 3.64646018e-01 1.12773582e-01]\n", + " [2.60495663e-01 8.65413904e-01 2.21576691e-02 6.10984743e-01]\n", + " ...\n", + " [8.34640980e-01 9.05928910e-01 7.33852565e-01 9.64702129e-01]\n", + " [5.20252772e-02 1.44064710e-01 9.47307721e-02 3.03431451e-01]\n", + " [1.99740291e-01 3.40426266e-02 5.65072261e-02 4.65648413e-01]]\n", "\n", - " [[2.29353935e-01 8.80753636e-01 7.94585168e-01 2.22074524e-01]\n", - " [6.13970399e-01 1.33511815e-02 2.89155185e-01 2.65219092e-01]\n", - " [6.62197351e-01 6.47982001e-01 9.46004018e-02 6.59599364e-01]\n", - " [4.86104101e-01 4.23153102e-01 1.39821902e-01 3.11809748e-01]\n", - " [8.03322852e-01 9.52799857e-01 3.89638603e-01 6.43237352e-01]]\n", + " [[2.59539992e-01 3.93728465e-01 9.71068442e-01 6.31185651e-01]\n", + " [1.83127314e-01 1.43956905e-02 4.26256537e-01 5.80079734e-01]\n", + " [3.76704991e-01 6.72040820e-01 5.88318594e-02 3.48662198e-01]\n", + " ...\n", + " [3.48356485e-01 9.14380550e-01 4.48769242e-01 7.38050520e-01]\n", + " [6.12327099e-01 1.99107945e-01 8.90928864e-01 2.84884181e-02]\n", + " [8.27723503e-01 6.57829344e-01 4.65144426e-01 4.01587933e-01]]\n", "\n", - " [[9.70978260e-01 6.75936878e-01 6.23196602e-01 8.42264950e-01]\n", - " [4.07079160e-01 8.46290290e-01 5.64092159e-01 3.56871307e-01]\n", - " [4.84096229e-01 8.60232174e-01 1.39015794e-01 7.82253265e-01]\n", - " [1.24170482e-01 2.21511930e-01 8.88282284e-02 4.53450561e-01]\n", - " [1.28404438e-01 2.87771430e-02 4.57022637e-01 9.80571806e-01]]\n", + " [[8.43813717e-01 3.53193313e-01 3.37166399e-01 4.36319530e-01]\n", + " [9.16836739e-01 2.00342074e-01 1.78178921e-01 6.41124010e-01]\n", + " [4.94625986e-01 2.56477743e-01 3.52547020e-01 7.87709892e-01]\n", + " ...\n", + " [5.02124608e-01 6.97787464e-01 2.58715957e-01 2.88078666e-01]\n", + " [9.08326209e-01 2.46579379e-01 1.59346357e-01 6.96394265e-01]\n", + " [3.52213830e-01 6.03601038e-02 2.54920900e-01 3.10269982e-01]]\n", "\n", - " [[5.00000000e+01 5.00000000e+01 0.00000000e+00 0.00000000e+00]\n", + " [[5.00000000e+01 5.00000000e+01 1.00000000e+00 0.00000000e+00]\n", " [ nan nan nan nan]\n", " [ nan nan nan nan]\n", + " ...\n", + " [ nan nan nan nan]\n", " [ nan nan nan nan]\n", " [ nan nan nan nan]]\n", "\n", - " [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00]\n", - " [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00]\n", - " [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00]\n", - " [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00]\n", - " [ nan nan nan nan]]]\n", + " [[5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01]\n", + " [5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01]\n", + " [5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01]\n", + " ...\n", + " [5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01]\n", + " [5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01]\n", + " [5.00000000e+01 5.00000000e+01 5.00000000e+01 5.00000000e+01]]]\n", "\n", "Fish poses\n", - "[[[0.8865841 0.23567082 0.54175484 0.4498502 ]\n", - " [0.81551188 0.47822332 0.62980342 0.11259239]\n", - " [0.1537323 0.72495425 0.93857449 0.46566582]\n", - " [0.91035461 0.44788021 0.38142914 0.96754432]\n", - " [0.60782295 0.52015883 0.81796569 0.84276038]]\n", + "[[[0.13678263 0.46879119 0.24218269 0.64877176]\n", + " [0.79336399 0.35035625 0.36464602 0.11277358]\n", + " [0.26049566 0.8654139 0.02215767 0.61098474]\n", + " ...\n", + " [0.83464098 0.90592891 0.73385257 0.96470213]\n", + " [0.05202528 0.14406471 0.09473077 0.30343145]\n", + " [0.19974029 0.03404263 0.05650723 0.46564841]]\n", "\n", - " [[0.22935393 0.88075364 0.79458517 0.22207452]\n", - " [0.6139704 0.01335118 0.28915519 0.26521909]\n", - " [0.66219735 0.647982 0.0946004 0.65959936]\n", - " [0.4861041 0.4231531 0.1398219 0.31180975]\n", - " [0.80332285 0.95279986 0.3896386 0.64323735]]\n", + " [[0.25953999 0.39372846 0.97106844 0.63118565]\n", + " [0.18312731 0.01439569 0.42625654 0.58007973]\n", + " [0.37670499 0.67204082 0.05883186 0.3486622 ]\n", + " ...\n", + " [0.34835649 0.91438055 0.44876924 0.73805052]\n", + " [0.6123271 0.19910794 0.89092886 0.02848842]\n", + " [0.8277235 0.65782934 0.46514443 0.40158793]]\n", "\n", - " [[0.97097826 0.67593688 0.6231966 0.84226495]\n", - " [0.40707916 0.84629029 0.56409216 0.35687131]\n", - " [0.48409623 0.86023217 0.13901579 0.78225327]\n", - " [0.12417048 0.22151193 0.08882823 0.45345056]\n", - " [0.12840444 0.02877714 0.45702264 0.98057181]]]\n", + " [[0.84381372 0.35319331 0.3371664 0.43631953]\n", + " [0.91683674 0.20034207 0.17817892 0.64112401]\n", + " [0.49462599 0.25647774 0.35254702 0.78770989]\n", + " ...\n", + " [0.50212461 0.69778746 0.25871596 0.28807867]\n", + " [0.90832621 0.24657938 0.15934636 0.69639426]\n", + " [0.35221383 0.0603601 0.2549209 0.31026998]]]\n", "\n", "File structure\n", - " version:\t[1 0]\n", - " world size:\t[100. 100.]\n", + " format_url:\thttps://git.imp.fu-berlin.de/bioroboticslab/robofish/track_format/-/releases/1.0\n", + " format_version:\t[1 0]\n", + " world_size_cm:\t[100. 100.]\n", "| entities\n", "|---| fish_1\n", - "|---|--- type:\tfish\n", - "|---|--- poses:\t Shape (5, 4)\n", - "|---|---| time\n", - "|---|---|--- monotonic points:\t Shape (5,)\n", + "|---|--- category:\tfish\n", + "|---|--- orientations:\t Shape (1000, 2)\n", + "|---|--- positions:\t Shape (1000, 2)\n", "|---| fish_2\n", - "|---|--- type:\tfish\n", - "|---|--- poses:\t Shape (5, 4)\n", - "|---|---| time\n", - "|---|---|--- monotonic points:\t Shape (5,)\n", + "|---|--- category:\tfish\n", + "|---|--- orientations:\t Shape (1000, 2)\n", + "|---|--- positions:\t Shape (1000, 2)\n", "|---| fish_3\n", - "|---|--- type:\tfish\n", - "|---|--- poses:\t Shape (5, 4)\n", - "|---|---| time\n", - "|---|---|--- monotonic points:\t Shape (5,)\n", + "|---|--- category:\tfish\n", + "|---|--- orientations:\t Shape (1000, 2)\n", + "|---|--- positions:\t Shape (1000, 2)\n", "|---| obstacle_1\n", - "|---|--- type:\tobstacle\n", + "|---|--- category:\tobstacle\n", + "|---|--- orientations:\t Shape (1, 2)\n", "|---|--- outlines:\t Shape (1, 4, 2)\n", - "|---|--- poses:\t Shape (1, 4)\n", - "|---|---| time\n", + "|---|--- positions:\t Shape (1, 2)\n", "|---| robot\n", - "|---|--- type:\trobot\n", - "|---|--- poses:\t Shape (4, 4)\n", - "|---|---| time\n", - "|---|---|--- monotonic step:\t40\n", + "|---|--- category:\trobot\n", + "|---|--- orientations:\t Shape (1000, 2)\n", + "|---|--- positions:\t Shape (1000, 2)\n", + "| samplings\n", + "|--- default:\t25 hz\n", + "|---| 25 hz\n", + "|---|--- frequency_hz:\t25.0\n", "\n" ] } @@ -100,69 +117,60 @@ "#! /usr/bin/env python3\n", "\n", "import robofish.io\n", + "from robofish.io import utils\n", "import numpy as np\n", - "from pathlib import Path\n", - "import os\n", - "\n", - "\n", - "# Helper function to enable relative paths from this file\n", - "def full_path(path):\n", - " return (Path(os.path.abspath(\"__file__\")).parent / path).resolve()\n", "\n", "\n", - "if __name__ == \"__main__\":\n", + "def create_example_file(path):\n", " # Create a new io file object with a 100x100cm world\n", - " sf = robofish.io.File(world_size=[100, 100])\n", + " sf = robofish.io.File(world_size_cm=[100, 100], frequency_hz=25.0)\n", "\n", " # create a simple obstacle, fixed in place, fixed outline\n", - " obstacle_pose = [[50, 50, 0, 0]]\n", " obstacle_outline = [[[-10, -10], [-10, 0], [0, 0], [0, -10]]]\n", " obstacle_name = sf.create_entity(\n", - " \"obstacle\", poses=obstacle_pose, outlines=obstacle_outline\n", + " \"obstacle\", positions=[[50, 50]], orientations=[[0]], outlines=obstacle_outline\n", " )\n", "\n", - " # create a robofish with 100 timesteps and 40ms between the timesteps. If we would not give a name, the name would be generated to be robot_1.\n", - " robofish_timesteps = 4\n", - " robofish_poses = np.zeros((robofish_timesteps, 4))\n", - " sf.create_entity(\"robot\", robofish_poses, name=\"robot\", monotonic_step=40)\n", + " # create a robofish with 1000 timesteps. If we would not give a name, the name would be generated to be robot_1.\n", + " robofish_timesteps = 1000\n", + " robofish_poses = np.ones((robofish_timesteps, 4)) * 50\n", + " robot = sf.create_entity(\"robot\", robofish_poses, name=\"robot\")\n", "\n", " # create multiple fishes with timestamps. Since we don't specify names, but only the type \"fish\" the fishes will be named [\"fish_1\", \"fish_2\", \"fish_3\"]\n", " agents = 3\n", - " timesteps = 5\n", - " timestamps = np.linspace(0, timesteps + 1, timesteps)\n", + " timesteps = 1000\n", + " # timestamps = np.linspace(0, timesteps + 1, timesteps)\n", " agent_poses = np.random.random((agents, timesteps, 4))\n", "\n", - " fish_names = sf.create_multiple_entities(\n", - " \"fish\", agent_poses, monotonic_points=timestamps\n", - " )\n", + " fishes = sf.create_multiple_entities(\"fish\", agent_poses)\n", "\n", " # This would throw an exception if the file was invalid\n", " sf.validate()\n", "\n", " # Save file validates aswell\n", - " example_file = full_path(\"example.hdf5\")\n", - " sf.save(example_file)\n", + "\n", + " sf.save_as(path)\n", "\n", " # Closing and opening files (just for demonstration)\n", " sf.close()\n", - " sf = robofish.io.File(path=example_file)\n", + " sf = robofish.io.File(path=path)\n", "\n", " print(\"\\nEntity Names\")\n", " print(sf.entity_names)\n", "\n", - " # Get an array with all poses. As the length of poses varies per agent, it\n", - " # is filled up with nans. The result is not interpolated and the time scales\n", - " # per agent are different. It is planned to create a warning in the case of\n", - " # different time scales and have another function, which generates an\n", - " # interpolated array.\n", + " # Get an array with all poses. As the length of poses varies per agent, it is filled up with nans.\n", " print(\"\\nAll poses\")\n", - " print(sf.select_poses())\n", + " print(sf.entity_poses)\n", "\n", " print(\"\\nFish poses\")\n", - " print(sf.select_poses(lambda e: e.category == \"fish\"))\n", + " print(sf.select_entity_poses(lambda e: e.category == \"fish\"))\n", "\n", " print(\"\\nFile structure\")\n", - " print(sf)\n" + " print(sf)\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " create_example_file(\"example.hdf5\")\n" ] } ], @@ -187,4 +195,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +}