Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support linking to multiple camera devices #5

Closed
wants to merge 13 commits into from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pe = PoseEstimation(
source_software_version='2.2b8',
nodes=['front_left_paw', 'front_right_paw'],
edges=np.array([[0, 1]], dtype='uint8'),
# devices=[camera1, camera2], # this is not yet supported
devices=[camera1, camera2],
)

behavior_pm = nwbfile.create_processing_module(
Expand Down
4 changes: 4 additions & 0 deletions spec/ndx-pose.extensions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,7 @@ groups:
- neurodata_type_inc: PoseEstimationSeries
doc: Estimated position data for each body part.
quantity: '*'
links:
- target_type: Device
doc: Cameras used to record the videos.
quantity: '*'
38 changes: 19 additions & 19 deletions src/pynwb/ndx_pose/pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ class PoseEstimation(MultiContainerInterface):
'type': PoseEstimationSeries,
'attr': 'pose_estimation_series'
},
# {
# 'add': 'add_device',
# 'get': 'get_devices',
# 'type': Device,
# 'attr': 'devices'
# # TODO prevent these from being children / add better support for links
# # may require update to HDMF to add a key 'child': False
# }
{
'add': 'add_device',
'get': 'get_devices',
'type': Device,
'attr': 'devices'
# TODO prevent these from being children / add better support for links
# may require update to HDMF to add a key 'child': False
}
]

__nwbfields__ = ('description', 'original_videos', 'labeled_videos', 'dimensions', 'scorer', 'source_software',
Expand Down Expand Up @@ -112,9 +112,9 @@ class PoseEstimation(MultiContainerInterface):
'doc': ("Array of pairs of indices corresponding to edges between nodes. Index values correspond to row "
"indices of the 'nodes' field. Index values use 0-indexing."),
'default': None},
# {'name': 'devices', 'type': ('array_data', 'data'),
# 'doc': ('Cameras used to record the videos.'),
# 'default': None},
{'name': 'devices', 'type': ('array_data', 'data'),
'doc': ('Cameras used to record the videos.'),
'default': None},
allow_positional=AllowPositional.ERROR
)
def __init__(self, **kwargs):
Expand All @@ -123,7 +123,7 @@ def __init__(self, **kwargs):
dimensions, scorer = popargs('dimensions', 'scorer', kwargs)
source_software, source_software_version = popargs('source_software', 'source_software_version', kwargs)
nodes, edges = popargs('nodes', 'edges', kwargs)
# devices = popargs('devices', kwargs)
devices = popargs('devices', kwargs)
call_docval_func(super().__init__, kwargs)
self.pose_estimation_series = pose_estimation_series
self.description = description
Expand All @@ -135,15 +135,15 @@ def __init__(self, **kwargs):
self.source_software_version = source_software_version
self.nodes = nodes
self.edges = edges
# self.devices = devices
self.devices = devices

# TODO include calibration images for 3D estimates?

# if original_videos is not None and (devices is None or len(original_videos) != len(devices)):
# raise ValueError("The number of original videos should equal the number of camera devices.")
# if labeled_videos is not None and (devices is None or len(labeled_videos) != len(devices)):
# raise ValueError("The number of labeled videos should equal the number of camera devices.")
# if dimensions is not None and (devices is None or len(dimensions) != len(devices)):
# raise ValueError("The number of dimensions should equal the number of camera devices.")
if original_videos is not None and (devices is None or len(original_videos) != len(devices)):
raise ValueError("The number of original videos should equal the number of camera devices.")
if labeled_videos is not None and (devices is None or len(labeled_videos) != len(devices)):
raise ValueError("The number of labeled videos should equal the number of camera devices.")
if dimensions is not None and (devices is None or len(dimensions) != len(devices)):
raise ValueError("The number of dimensions should equal the number of camera devices.")

# TODO validate nodes and edges correspondence, convert edges to uint
8 changes: 4 additions & 4 deletions src/pynwb/tests/integration/hdf5/test_pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def test_roundtrip(self):
source_software_version='2.2b8',
nodes=['front_left_paw', 'front_right_paw'],
edges=np.array([[0, 1]], dtype='uint8'),
# devices=[self.nwbfile.devices['camera1'], self.nwbfile.devices['camera2']],
devices=[self.nwbfile.devices['camera1'], self.nwbfile.devices['camera2']],
)

behavior_pm = self.nwbfile.create_processing_module(
Expand All @@ -189,9 +189,9 @@ def test_roundtrip(self):
self.assertEqual(len(read_pe.pose_estimation_series), 2)
self.assertContainerEqual(read_pe.pose_estimation_series['front_left_paw'], pose_estimation_series[0])
self.assertContainerEqual(read_pe.pose_estimation_series['front_right_paw'], pose_estimation_series[1])
# self.assertEqual(len(read_pe.devices), 2)
# self.assertContainerEqual(read_pe.devices['camera1'], self.nwbfile.devices['camera1'])
# self.assertContainerEqual(read_pe.devices['camera2'], self.nwbfile.devices['camera2'])
self.assertEqual(len(read_pe.devices), 2)
self.assertContainerEqual(read_pe.devices['camera1'], self.nwbfile.devices['camera1'])
self.assertContainerEqual(read_pe.devices['camera2'], self.nwbfile.devices['camera2'])


# NOTE it is recommended to add links to devices in the constructor of PoseEstimation. however,
Expand Down
2 changes: 1 addition & 1 deletion src/pynwb/tests/test_example_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_example_usage():
source_software_version='2.2b8',
nodes=['front_left_paw', 'front_right_paw'],
edges=np.array([[0, 1]], dtype='uint8'),
# devices=[camera1, camera2], # this is not yet supported
devices=[camera1, camera2],
)

behavior_pm = nwbfile.create_processing_module(
Expand Down
8 changes: 4 additions & 4 deletions src/pynwb/tests/unit/test_pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_constructor(self):
source_software_version='2.2b8',
nodes=['front_left_paw', 'front_right_paw'],
edges=np.array([[0, 1]], dtype='uint8'),
# devices=[self.nwbfile.devices['camera1'], self.nwbfile.devices['camera2']],
devices=[self.nwbfile.devices['camera1'], self.nwbfile.devices['camera2']],
)

self.assertEqual(pe.name, 'PoseEstimation')
Expand All @@ -109,6 +109,6 @@ def test_constructor(self):
self.assertEqual(pe.source_software_version, '2.2b8')
self.assertEqual(pe.nodes, ['front_left_paw', 'front_right_paw'])
np.testing.assert_array_equal(pe.edges, np.array([[0, 1]], dtype='uint8'))
# self.assertEqual(len(pe.devices), 2)
# self.assertIs(pe.devices['camera1'], self.nwbfile.devices['camera1'])
# self.assertIs(pe.devices['camera2'], self.nwbfile.devices['camera2'])
self.assertEqual(len(pe.devices), 2)
self.assertIs(pe.devices['camera1'], self.nwbfile.devices['camera1'])
self.assertIs(pe.devices['camera2'], self.nwbfile.devices['camera2'])
15 changes: 7 additions & 8 deletions src/spec/create_extension_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,13 @@ def main():
quantity='?',
),
],
# TODO: collections of multiple links is currently buggy in PyNWB/HDMF
# links=[
# NWBLinkSpec(
# target_type='Device',
# doc='Cameras used to record the videos.',
# quantity='*',
# ),
# ],
links=[
NWBLinkSpec(
target_type='Device',
doc='Cameras used to record the videos.',
quantity='*',
),
],
)

new_data_types = [pose_estimation_series, pose_estimation]
Expand Down