Skip to content

Commit

Permalink
updated error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
keyaloding committed Aug 3, 2024
1 parent 68c7cfb commit 3bf7c8a
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 60 deletions.
163 changes: 108 additions & 55 deletions load_test.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 21,
"metadata": {},
"outputs": [
{
Expand All @@ -19,104 +19,157 @@
"%autoreload 2\n",
"import sleap_io as sio\n",
"from sleap_io.io.nwb import *\n",
"from pynwb.image import ImageSeries"
"from pynwb import NWBHDF5IO"
]
},
{
"cell_type": "code",
"execution_count": 47,
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"labels = sio.load_slp(\"tests/data/slp/minimal_instance.pkg.slp\")\n",
"skeletons_list = [slp_skeleton_to_nwb(skeleton) for skeleton in labels.skeletons]\n",
"video_info = write_video_to_path(labels.video, None)\n",
"pose_training = labels_to_pose_training(labels, skeletons_list=skeletons_list, video_info=video_info)\n",
"labels_converted = pose_training_to_labels(pose_training)\n",
"\n",
"assert len(labels.labeled_frames) == len(labels_converted.labeled_frames)\n",
"assert len(labels.skeletons) == len(labels_converted.skeletons)\n",
"assert len(labels.videos) == len(labels_converted.videos)"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"No accelerated colorspace conversion found from yuv420p to bgr24.\n"
"/Users/keya/mambaforge3/envs/io_dev/lib/python3.12/site-packages/hdmf/build/objectmapper.py:260: DtypeConversionWarning: Spec 'PoseEstimation/dimensions': Value with data type int64 is being converted to data type uint64 (min specification: uint8).\n",
" warnings.warn(full_warning_msg, DtypeConversionWarning)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"SLEAP_VIDEO_000_minimal_instance.pkg pynwb.base.ProcessingModule at 0x6427976352\n",
"Fields:\n",
" data_interfaces: {\n",
" PoseEstimation <class 'ndx_pose.pose.PoseEstimation'>,\n",
" PoseTraining <class 'abc.PoseTraining'>,\n",
" Skeletons <class 'abc.Skeletons'>\n",
" }\n",
" description: Processed SLEAP data\n",
"\n"
]
}
],
"source": [
"import os\n",
"\n",
"video = Video.from_filename(\"tests/data/videos/centered_pair_low_quality.mp4\")\n",
"path = write_video_to_path(video)"
"nwb = write_nwb(labels, \"test_file.nwb\", as_training=True, frame_inds=None)\n",
"from sleap_io.io.main import load_nwb\n",
"labels_new = read_nwb_training(\"test_file.nwb\")\n",
"assert len(labels.labeled_frames) == len(labels_new.labeled_frames)\n",
"assert len(labels.skeletons) == len(labels_new.skeletons)"
]
},
{
"cell_type": "code",
"execution_count": 33,
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"test pynwb.image.ImageSeries at 0x5715526816\n",
"root pynwb.file.NWBFile at 0x6435822944\n",
"Fields:\n",
" devices: {\n",
" camera <class 'pynwb.device.Device'>\n",
" }\n",
" file_create_date: [datetime.datetime(2024, 8, 2, 14, 55, 51, 189600, tzinfo=tzoffset(None, -25200))]\n",
" identifier: fbc61ca2-5119-11ef-a15d-eee9744406f7\n",
" processing: {\n",
" SLEAP_VIDEO_000_minimal_instance.pkg <class 'pynwb.base.ProcessingModule'>\n",
" }\n",
" session_description: Processed SLEAP pose data\n",
" session_start_time: 2024-08-02 21:55:51.186802+00:00\n",
" subject: subject pynwb.file.Subject at 0x6435821360\n",
"Fields:\n",
" comments: no comments\n",
" conversion: 1.0\n",
" description: test\n",
" external_file: ['frame_6.png' 'frame_7.png' 'frame_5.png' 'frame_4.png' 'frame_0.png'\n",
" 'frame_1.png' 'frame_3.png' 'frame_2.png' 'frame_8.png']\n",
" format: external\n",
" offset: 0.0\n",
" rate: 30.0\n",
" resolution: -1.0\n",
" starting_frame: [0 0 0 0 0 0 0 0 0]\n",
" starting_time: 0.0\n",
" starting_time_unit: seconds\n",
" unit: NA\n",
" age__reference: birth\n",
" species: No specified species\n",
" subject_id: No specified id\n",
"\n",
" timestamps_reference_time: 2024-08-02 21:55:51.186802+00:00\n",
"\n"
]
}
],
"source": [
"image_series = ImageSeries(\n",
" name=\"test\",\n",
" description=\"test\",\n",
" unit=\"NA\",\n",
" format=\"external\",\n",
" external_file=os.listdir(path),\n",
" starting_frame=[0 for _ in range(len(os.listdir(path)))],\n",
" rate=30.0,\n",
")\n",
"print(image_series)"
"with NWBHDF5IO(\"test_file.nwb\", \"r\") as nwb_file:\n",
" nwb = nwb_file.read()\n",
" print(nwb)"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/keya/mambaforge3/envs/io_dev/lib/python3.12/site-packages/hdmf/build/objectmapper.py:260: DtypeConversionWarning: Spec 'PoseEstimation/dimensions': Value with data type int64 is being converted to data type uint64 (min specification: uint8).\n",
" warnings.warn(full_warning_msg, DtypeConversionWarning)\n"
]
}
],
"source": [
"labels_original = sio.load_slp(\"tests/data/slp/minimal_instance.pkg.slp\")\n",
"labels_original.save(\"tests/data/slp/minimal_instance.pkg.nwb\", format=\"nwb_training\")\n"
]
},
{
"cell_type": "code",
"execution_count": 65,
"execution_count": 63,
"metadata": {},
"outputs": [
{
"ename": "TypeError",
"evalue": "object of type 'int' has no len()",
"name": "stdout",
"output_type": "stream",
"text": [
"SLEAP_VIDEO_000_minimal_instance.pkg pynwb.base.ProcessingModule at 0x12898282592\n",
"Fields:\n",
" data_interfaces: {\n",
" PoseEstimation <class 'ndx_pose.pose.PoseEstimation'>,\n",
" PoseTraining <class 'abc.PoseTraining'>,\n",
" Skeletons <class 'abc.Skeletons'>\n",
" }\n",
" description: Processed SLEAP data\n",
"\n",
"Labels(labeled_frames=1, videos=1, skeletons=1, tracks=0, suggestions=0)\n"
]
},
{
"ename": "AttributeError",
"evalue": "'Labels' object has no attribute 'training_frames'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[65], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m labels_original \u001b[38;5;241m=\u001b[39m sio\u001b[38;5;241m.\u001b[39mload_slp(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtests/data/slp/minimal_instance.pkg.slp\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m----> 2\u001b[0m \u001b[43mlabels_original\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msave\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtests/data/slp/minimal_instance.pkg.nwb\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mnwb_training\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;66;03m# labels_loaded = sio.load_nwb(\"tests/data/slp/minimal_instance.pkg.nwb\")\u001b[39;00m\n\u001b[1;32m 5\u001b[0m \n\u001b[1;32m 6\u001b[0m \u001b[38;5;66;03m# assert len(labels_original.labeled_frames) == len(labels_loaded.labeled_frames)\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;66;03m# assert len(labels_original.suggestions) == len(labels_loaded.suggestions)\u001b[39;00m\n\u001b[1;32m 11\u001b[0m \u001b[38;5;66;03m# assert labels_original.provenance == labels_loaded.provenance\u001b[39;00m\n",
"File \u001b[0;32m~/salk/io_fork/sleap_io/model/labels.py:372\u001b[0m, in \u001b[0;36mLabels.save\u001b[0;34m(self, filename, format, embed, **kwargs)\u001b[0m\n\u001b[1;32m 348\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Save labels to file in specified format.\u001b[39;00m\n\u001b[1;32m 349\u001b[0m \n\u001b[1;32m 350\u001b[0m \u001b[38;5;124;03mArgs:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 368\u001b[0m \u001b[38;5;124;03m This argument is only valid for the SLP backend.\u001b[39;00m\n\u001b[1;32m 369\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 370\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01msleap_io\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m save_file\n\u001b[0;32m--> 372\u001b[0m \u001b[43msave_file\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43membed\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43membed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
"File \u001b[0;32m~/salk/io_fork/sleap_io/io/main.py:242\u001b[0m, in \u001b[0;36msave_file\u001b[0;34m(labels, filename, format, **kwargs)\u001b[0m\n\u001b[1;32m 240\u001b[0m save_nwb(labels, filename, as_training\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[1;32m 241\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mformat\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnwb_training\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m--> 242\u001b[0m \u001b[43msave_nwb\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlabels\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mas_training\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mformat\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlabelstudio\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 244\u001b[0m save_labelstudio(labels, filename, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
"File \u001b[0;32m~/salk/io_fork/sleap_io/io/main.py:91\u001b[0m, in \u001b[0;36msave_nwb\u001b[0;34m(labels, filename, as_training, append)\u001b[0m\n\u001b[1;32m 89\u001b[0m nwb\u001b[38;5;241m.\u001b[39mappend_nwb(labels, filename, as_training\u001b[38;5;241m=\u001b[39mas_training)\n\u001b[1;32m 90\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m---> 91\u001b[0m \u001b[43mnwb\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwrite_nwb\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlabels\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mas_training\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mas_training\u001b[49m\u001b[43m)\u001b[49m\n",
"File \u001b[0;32m~/salk/io_fork/sleap_io/io/nwb.py:484\u001b[0m, in \u001b[0;36mwrite_nwb\u001b[0;34m(labels, nwbfile_path, nwb_file_kwargs, pose_estimation_metadata, as_training, frame_inds)\u001b[0m\n\u001b[1;32m 481\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m NWBHDF5IO(\u001b[38;5;28mstr\u001b[39m(nwbfile_path), \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mw\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mas\u001b[39;00m io:\n\u001b[1;32m 482\u001b[0m io\u001b[38;5;241m.\u001b[39mwrite(nwbfile)\n\u001b[0;32m--> 484\u001b[0m \u001b[43mwrite_video_to_path\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlabels\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvideo\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mframe_inds\u001b[49m\u001b[43m)\u001b[49m\n",
"File \u001b[0;32m~/salk/io_fork/sleap_io/io/nwb.py:259\u001b[0m, in \u001b[0;36mwrite_video_to_path\u001b[0;34m(video, frame_inds, image_format)\u001b[0m\n\u001b[1;32m 247\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 248\u001b[0m \u001b[38;5;124;03mWrite individual frames of a video to a path and return the pathname.\u001b[39;00m\n\u001b[1;32m 249\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 256\u001b[0m \u001b[38;5;124;03m The pathname of the images folder.\u001b[39;00m\n\u001b[1;32m 257\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 258\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m frame_inds \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 259\u001b[0m frame_inds \u001b[38;5;241m=\u001b[39m [idx \u001b[38;5;28;01mfor\u001b[39;00m idx \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mvideo\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mshape\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m)]\n\u001b[1;32m 261\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(video\u001b[38;5;241m.\u001b[39mfilename, \u001b[38;5;28mlist\u001b[39m):\n\u001b[1;32m 262\u001b[0m save_path \u001b[38;5;241m=\u001b[39m video\u001b[38;5;241m.\u001b[39mfilename[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m)[\u001b[38;5;241m0\u001b[39m]\n",
"\u001b[0;31mTypeError\u001b[0m: object of type 'int' has no len()"
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[63], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m pose_training \u001b[38;5;241m=\u001b[39m read_nwb_training(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtests/data/nwb/minimal_instance.pkg.nwb\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(pose_training)\n\u001b[0;32m----> 4\u001b[0m lables2 \u001b[38;5;241m=\u001b[39m \u001b[43mpose_training_to_labels\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpose_training\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(lables2)\n",
"File \u001b[0;32m~/salk/io_fork/sleap_io/io/nwb.py:68\u001b[0m, in \u001b[0;36mpose_training_to_labels\u001b[0;34m(pose_training)\u001b[0m\n\u001b[1;32m 66\u001b[0m labeled_frames \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 67\u001b[0m skeletons \u001b[38;5;241m=\u001b[39m {}\n\u001b[0;32m---> 68\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m training_frame \u001b[38;5;129;01min\u001b[39;00m \u001b[43mpose_training\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtraining_frames\u001b[49m\u001b[38;5;241m.\u001b[39mtraining_frames\u001b[38;5;241m.\u001b[39mvalues():\n\u001b[1;32m 69\u001b[0m source_video \u001b[38;5;241m=\u001b[39m training_frame\u001b[38;5;241m.\u001b[39msource_video\n\u001b[1;32m 70\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m source_video\u001b[38;5;241m.\u001b[39mformat \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexternal\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(source_video\u001b[38;5;241m.\u001b[39mexternal_file) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n",
"\u001b[0;31mAttributeError\u001b[0m: 'Labels' object has no attribute 'training_frames'"
]
}
],
"source": [
"labels_original = sio.load_slp(\"tests/data/slp/minimal_instance.pkg.slp\")\n",
"labels_original.save(\"tests/data/slp/minimal_instance.pkg.nwb\", format=\"nwb_training\")\n",
"\n",
"# labels_loaded = sio.load_nwb(\"tests/data/slp/minimal_instance.pkg.nwb\")\n",
"\n",
"# assert len(labels_original.labeled_frames) == len(labels_loaded.labeled_frames)\n",
"# assert len(labels_original.videos) == len(labels_loaded.videos)\n",
"# assert len(labels_original.skeletons) == len(labels_loaded.skeletons)\n",
"# assert len(labels_original.tracks) == len(labels_loaded.tracks)\n",
"# assert len(labels_original.suggestions) == len(labels_loaded.suggestions)\n",
"# assert labels_original.provenance == labels_loaded.provenance"
"labels2 = read_nwb_training(\"tests/data/nwb/minimal_instance.pkg.nwb\")\n",
"print(labels2)"
]
}
],
Expand Down
27 changes: 23 additions & 4 deletions sleap_io/io/nwb.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def labels_to_pose_training(

_, _, image_series = video_info
source_video = image_series
source_video_list.append(source_video)
if source_video not in source_video_list:
source_video_list.append(source_video)
training_frame = TrainingFrame(
name=f"training_frame_{i}",
annotator="N/A",
Expand Down Expand Up @@ -257,17 +258,35 @@ def write_video_to_path(
save_path = video.filename[0].split(".")[0]
else:
save_path = video.filename.split(".")[0]
os.makedirs(save_path, exist_ok=True)

try:
os.makedirs(save_path, exist_ok=True)
except PermissionError:
filename_with_extension = video.filename.split("/")[-1]
filename = filename_with_extension.split(".")[0]
print(filename)
save_path = input("Permission denied. Enter a new path:") + "/" + filename
os.makedirs(save_path, exist_ok=True)

if "cv2" in sys.modules:
for frame_idx in frame_inds:
frame = video[frame_idx]
try:
frame = video[frame_idx]
except FileNotFoundError:
video_filename = input("Video not found. Enter the video filename:")
video = Video.from_filename(video_filename)
frame = video[frame_idx]
frame_path = f"{save_path}/frame_{frame_idx}.{image_format}"
index_data[frame_idx] = frame_path
cv2.imwrite(frame_path, frame)
else:
for frame_idx in frame_inds:
frame = video[frame_idx]
try:
frame = video[frame_idx]
except FileNotFoundError:
video_filename = input("Video not found. Enter the filename:")
video = Video.from_filename(video_filename)
frame = video[frame_idx]
frame_path = f"{save_path}/frame_{frame_idx}.{image_format}"
index_data[frame_idx] = frame_path
iio.imwrite(frame_path, frame)
Expand Down
Binary file not shown.
Binary file added tests/data/nwb/minimal_instance.pkg.nwb
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/fixtures/nwb.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ def minimal_instance_pkg():
@pytest.fixture
def centered_pair_no_training():
"""NWB file converted from .slp in the GUI without NWB training data."""
return "tests/data/nwb/centered_pair_predictions_no_training.nwb"
return "tests/data/nwb/centered_pair_predictions_no_training.nwb"

0 comments on commit 3bf7c8a

Please sign in to comment.