From 97bfa2038119bf748c1ad100e4cd5b576ad105c0 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 24 Jun 2024 13:04:18 -0400 Subject: [PATCH 01/34] add multichannel volume --- spec/ndx-microscopy.extensions.yaml | 68 +++++++++++++++++++- src/pynwb/ndx_microscopy/__init__.py | 2 + src/pynwb/ndx_microscopy/testing/__init__.py | 2 + src/pynwb/ndx_microscopy/testing/_mock.py | 33 +++++++++- src/pynwb/tests/test_constructors.py | 15 +++++ src/pynwb/tests/test_roundtrip.py | 52 ++++++++++++++- 6 files changed, 169 insertions(+), 3 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index f76bc1d..3fd246e 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -247,7 +247,7 @@ groups: - frames - height - width - - depth + - depths shape: - null - null @@ -258,3 +258,69 @@ groups: doc: Link to VolumetricImagingSpace object containing metadata about the region of physical space this imaging data was recorded from. target_type: VolumetricImagingSpace + + - neurodata_type_def: MultiChannelMicroscopyVolume + neurodata_type_inc: NWBData + doc: Static (not time-varying) volumetric imaging data acquired from multiple optical channels. + attributes: + - name: description + dtype: text + doc: Description of the MultiChannelVolume. + required: false + - name: unit + dtype: text + doc: Base unit of measurement for working with the data. Actual stored values are + not necessarily stored in these units. To access the data in these units, + multiply 'data' by 'conversion' and add 'offset'. + - name: conversion + dtype: float32 + default_value: 1.0 + doc: Scalar to multiply each element in data to convert it to the specified 'unit'. + If the data are stored in acquisition system units or other units + that require a conversion to be interpretable, multiply the data by 'conversion' + to convert the data to the specified 'unit'. e.g. if the data acquisition system + stores values in this object as signed 16-bit integers (int16 range + -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data + acquisition system gain is 8000X, then the 'conversion' multiplier to get from + raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + required: false + - name: offset + dtype: float32 + default_value: 0.0 + doc: Scalar to add to the data after scaling by 'conversion' to finalize its coercion + to the specified 'unit'. Two common examples of this include (a) data stored in an + unsigned type that requires a shift after scaling to re-center the data, + and (b) specialized recording devices that naturally cause a scalar offset with + respect to the true units. + required: false + datasets: + - name: data + doc: Recorded imaging data, shaped by (frame height, frame width, number of depth planes, number of optical + channels). + dtype: numeric + dims: + - height + - width + - depths + - channels + shape: + - null + - null + - null + - null + links: + - name: microscope + doc: Link to a Microscope object containing metadata about the device used to acquire this imaging data. + target_type: Microscope + - name: light_source + doc: Link to a LightSource object containing metadata about the device used to illuminate the imaging space. + target_type: LightSource + # TODO: figure out best way to link to list of optical channels + - name: optical_channels + doc: Link to an ordered list of MicroscopyOpticalChannel objects containing metadata about the indicator and + filters used to collect this data. + target_type: MicroscopyOpticalChannel + - name: imaging_space + doc: Link to VolumetricImagingSpace object containing metadata about the region of physical space this imaging data + was recorded from. + target_type: VolumetricImagingSpace diff --git a/src/pynwb/ndx_microscopy/__init__.py b/src/pynwb/ndx_microscopy/__init__.py index a8cf19a..e7e3d86 100644 --- a/src/pynwb/ndx_microscopy/__init__.py +++ b/src/pynwb/ndx_microscopy/__init__.py @@ -30,6 +30,7 @@ PlanarMicroscopySeries = get_class("PlanarMicroscopySeries", extension_name) VariableDepthMicroscopySeries = get_class("VariableDepthMicroscopySeries", extension_name) VolumetricMicroscopySeries = get_class("VolumetricMicroscopySeries", extension_name) +MultiChannelMicroscopyVolume = get_class("MultiChannelMicroscopyVolume", extension_name) __all__ = [ "Microscope", @@ -42,4 +43,5 @@ "PlanarMicroscopySeries", "VariableDepthMicroscopySeries", "VolumetricMicroscopySeries", + "MultiChannelMicroscopyVolume", ] diff --git a/src/pynwb/ndx_microscopy/testing/__init__.py b/src/pynwb/ndx_microscopy/testing/__init__.py index adad764..17e66d9 100644 --- a/src/pynwb/ndx_microscopy/testing/__init__.py +++ b/src/pynwb/ndx_microscopy/testing/__init__.py @@ -2,6 +2,7 @@ mock_LightSource, mock_Microscope, mock_MicroscopyOpticalChannel, + mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, mock_VariableDepthMicroscopySeries, @@ -18,4 +19,5 @@ "mock_PlanarMicroscopySeries", "mock_VariableDepthMicroscopySeries", "mock_VolumetricMicroscopySeries", + "mock_MultiChannelMicroscopyVolume", ] diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 7eebeb5..7570713 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -1,5 +1,5 @@ import warnings -from typing import Optional, Tuple +from typing import List, Optional, Tuple import numpy as np from pynwb.testing.mock.utils import name_generator @@ -286,3 +286,34 @@ def mock_VolumetricMicroscopySeries( timestamps=series_timestamps, ) return volumetric_microscopy_series + + +def mock_MultiChannelMicroscopyVolume( + *, + microscope: ndx_microscopy.Microscope, + light_source: ndx_microscopy.LightSource, + imaging_space: ndx_microscopy.VolumetricImagingSpace, + optical_channels: List[ndx_microscopy.MicroscopyOpticalChannel], + name: Optional[str] = None, + description: str = "This is a mock instance of a MultiChannelMicroscopyVolume type to be used for rapid testing.", + data: Optional[np.ndarray] = None, + unit: str = "n.a.", + conversion: float = 1.0, + offset: float = 0.0, +) -> ndx_microscopy.MultiChannelMicroscopyVolume: + series_name = name or name_generator("MultiChannelMicroscopyVolume") + imaging_data = data if data is not None else np.ones(shape=(10, 20, 7, 3)) + + volumetric_microscopy_series = ndx_microscopy.MultiChannelMicroscopyVolume( + name=series_name, + description=description, + microscope=microscope, + light_source=light_source, + imaging_space=imaging_space, + optical_channels=optical_channels[0], # TODO: figure out how to specify list + data=imaging_data, + unit=unit, + conversion=conversion, + offset=offset, + ) + return volumetric_microscopy_series diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index 1410594..bd6ab83 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -6,6 +6,7 @@ mock_LightSource, mock_Microscope, mock_MicroscopyOpticalChannel, + mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, mock_VariableDepthMicroscopySeries, @@ -71,5 +72,19 @@ def test_constructor_volumetric_microscopy_series(): ) +def test_constructor_multi_channel_microscopy_volume(): + microscope = mock_Microscope() + light_source = mock_LightSource() + imaging_space = mock_VolumetricImagingSpace(microscope=microscope) + optical_channel = mock_MicroscopyOpticalChannel() + + mock_MultiChannelMicroscopyVolume( + microscope=microscope, + light_source=light_source, + imaging_space=imaging_space, + optical_channels=[optical_channel], + ) + + if __name__ == "__main__": pytest.main() # Required since not a typical package structure diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index 15644b1..2d827b5 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -133,7 +133,7 @@ def test_roundtrip(self): nwbfile.add_device(devices=light_source) imaging_space = mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) - nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_spacec() + nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() optical_channel = mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") nwbfile.add_lab_meta_data(lab_meta_data=optical_channel) @@ -162,3 +162,53 @@ def test_roundtrip(self): self.assertContainerEqual( variable_depth_microscopy_series, read_nwbfile.acquisition["VariableDepthMicroscopySeries"] ) + + +class TestMultiChannelMicroscopyVolumeSimpleRoundtrip(pynwb_TestCase): + """Simple roundtrip test for MultiChannelMicroscopyVolume.""" + + def setUp(self): + self.nwbfile_path = "test.nwb" + + def tearDown(self): + pynwb.testing.remove_test_file(self.nwbfile_path) + + def test_roundtrip(self): + nwbfile = mock_NWBFile() + + microscope = mock_Microscope(name="Microscope") + nwbfile.add_device(devices=microscope) + + light_source = mock_LightSource(name="LightSource") + nwbfile.add_device(devices=light_source) + + imaging_space = mock_VolumetricImagingSpace(name="VolumetricImagingSpace", microscope=microscope) + nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() + + optical_channel = mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") + nwbfile.add_lab_meta_data(lab_meta_data=optical_channel) + + multi_channel_microscopy_volume = mock_MultiChannelMicroscopyVolume( + name="MultiChannelMicroscopyVolume", + microscope=microscope, + light_source=light_source, + imaging_space=imaging_space, + optical_channels=[optical_channel], + ) + nwbfile.add_acquisition(nwbdata=multi_channel_microscopy_volume) + + with pynwb.NWBHDF5IO(path=self.nwbfile_path, mode="w") as io: + io.write(nwbfile) + + with pynwb.NWBHDF5IO(path=self.nwbfile_path, mode="r", load_namespaces=True) as io: + read_nwbfile = io.read() + + self.assertContainerEqual(microscope, read_nwbfile.devices["Microscope"]) + self.assertContainerEqual(light_source, read_nwbfile.devices["LightSource"]) + + self.assertContainerEqual(imaging_space, read_nwbfile.lab_meta_data["PlanarImagingSpace"]) + self.assertContainerEqual(optical_channel, read_nwbfile.lab_meta_data["MicroscopyOpticalChannel"]) + + self.assertContainerEqual( + multi_channel_microscopy_volume, read_nwbfile.acquisition["MultiChannelMicroscopyVolume"] + ) From 91db41b1f61822b39b28efef4938c5232e7c4ce5 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 24 Jun 2024 15:54:29 -0400 Subject: [PATCH 02/34] swap to datainterface --- spec/ndx-microscopy.extensions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index 3fd246e..518d146 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -260,7 +260,7 @@ groups: target_type: VolumetricImagingSpace - neurodata_type_def: MultiChannelMicroscopyVolume - neurodata_type_inc: NWBData + neurodata_type_inc: NWBDataInterface doc: Static (not time-varying) volumetric imaging data acquired from multiple optical channels. attributes: - name: description From 200eb9bee63fd23f4c5999828964a596c930b23c Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 24 Jun 2024 15:57:38 -0400 Subject: [PATCH 03/34] fix import --- src/pynwb/tests/test_roundtrip.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index 2d827b5..c04933f 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -8,6 +8,7 @@ mock_LightSource, mock_Microscope, mock_MicroscopyOpticalChannel, + mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, mock_VariableDepthMicroscopySeries, From b84aa0bfc6243ba7e1da8b905622c8a9f2e9e656 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 24 Jun 2024 16:06:06 -0400 Subject: [PATCH 04/34] fix test name --- src/pynwb/tests/test_roundtrip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index c04933f..fbad5f1 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -207,7 +207,7 @@ def test_roundtrip(self): self.assertContainerEqual(microscope, read_nwbfile.devices["Microscope"]) self.assertContainerEqual(light_source, read_nwbfile.devices["LightSource"]) - self.assertContainerEqual(imaging_space, read_nwbfile.lab_meta_data["PlanarImagingSpace"]) + self.assertContainerEqual(imaging_space, read_nwbfile.lab_meta_data["VolumetricImagingSpace"]) self.assertContainerEqual(optical_channel, read_nwbfile.lab_meta_data["MicroscopyOpticalChannel"]) self.assertContainerEqual( From c8b5dfd23c41247880a7156349d4ebd10e047c93 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 26 Jun 2024 12:26:39 -0400 Subject: [PATCH 05/34] fix intercompatability --- spec/ndx-microscopy.extensions.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index da3d397..8d03861 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -313,8 +313,8 @@ groups: doc: Link to a Microscope object containing metadata about the device used to acquire this imaging data. target_type: Microscope - name: light_source - doc: Link to a LightSource object containing metadata about the device used to illuminate the imaging space. - target_type: LightSource + doc: Link to a MicroscopyLightSource object containing metadata about the device used to illuminate the imaging space. + target_type: MicroscopyLightSource # TODO: figure out best way to link to list of optical channels - name: optical_channels doc: Link to an ordered list of MicroscopyOpticalChannel objects containing metadata about the indicator and From ad6b5e74adf681bb4ac9c191629ead857d79ba62 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 26 Jun 2024 12:30:14 -0400 Subject: [PATCH 06/34] fix light sources --- spec/ndx-microscopy.extensions.yaml | 5 +++-- src/pynwb/ndx_microscopy/testing/_mock.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index 8d03861..c5fb049 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -312,8 +312,9 @@ groups: - name: microscope doc: Link to a Microscope object containing metadata about the device used to acquire this imaging data. target_type: Microscope - - name: light_source - doc: Link to a MicroscopyLightSource object containing metadata about the device used to illuminate the imaging space. + # TODO: figure out best way to link to list of light sources + - name: light_sources + doc: Link to an ordered list of MicroscopyLightSource objects containing metadata about the excitation method. target_type: MicroscopyLightSource # TODO: figure out best way to link to list of optical channels - name: optical_channels diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 6a32364..4a09dfb 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -291,7 +291,7 @@ def mock_VolumetricMicroscopySeries( def mock_MultiChannelMicroscopyVolume( *, microscope: ndx_microscopy.Microscope, - light_source: ndx_microscopy.LightSource, + light_sources: ndx_microscopy.MicroscopyLightSource, imaging_space: ndx_microscopy.VolumetricImagingSpace, optical_channels: List[ndx_microscopy.MicroscopyOpticalChannel], name: Optional[str] = None, @@ -308,7 +308,7 @@ def mock_MultiChannelMicroscopyVolume( name=series_name, description=description, microscope=microscope, - light_source=light_source, + light_sources=light_source[0], # TODO: figure out how to specify list imaging_space=imaging_space, optical_channels=optical_channels[0], # TODO: figure out how to specify list data=imaging_data, From 8b69db4fb9318e1d3a9c1dfa34aaf6ac40611e9e Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 26 Jun 2024 13:06:41 -0400 Subject: [PATCH 07/34] fix --- src/pynwb/ndx_microscopy/testing/_mock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 4a09dfb..12cf5eb 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -291,7 +291,7 @@ def mock_VolumetricMicroscopySeries( def mock_MultiChannelMicroscopyVolume( *, microscope: ndx_microscopy.Microscope, - light_sources: ndx_microscopy.MicroscopyLightSource, + light_sources: List[ndx_microscopy.MicroscopyLightSource], imaging_space: ndx_microscopy.VolumetricImagingSpace, optical_channels: List[ndx_microscopy.MicroscopyOpticalChannel], name: Optional[str] = None, @@ -308,7 +308,7 @@ def mock_MultiChannelMicroscopyVolume( name=series_name, description=description, microscope=microscope, - light_sources=light_source[0], # TODO: figure out how to specify list + light_sources=light_sources[0], # TODO: figure out how to specify list imaging_space=imaging_space, optical_channels=optical_channels[0], # TODO: figure out how to specify list data=imaging_data, From 00d2270a0e50236f6072aadadf5e4f9a101a76d2 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 26 Jun 2024 15:33:39 -0400 Subject: [PATCH 08/34] add plane segmentation stuff --- src/pynwb/ndx_microscopy/__init__.py | 4 +++ src/pynwb/ndx_microscopy/testing/__init__.py | 4 +++ src/pynwb/ndx_microscopy/testing/_mock.py | 32 +++++++++++++++++++- src/pynwb/tests/test_constructors.py | 28 +++++++++++++++++ src/pynwb/tests/test_roundtrip.py | 3 ++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/pynwb/ndx_microscopy/__init__.py b/src/pynwb/ndx_microscopy/__init__.py index 722b281..e27a792 100644 --- a/src/pynwb/ndx_microscopy/__init__.py +++ b/src/pynwb/ndx_microscopy/__init__.py @@ -26,6 +26,8 @@ ImagingSpace = get_class("ImagingSpace", extension_name) PlanarImagingSpace = get_class("PlanarImagingSpace", extension_name) VolumetricImagingSpace = get_class("VolumetricImagingSpace", extension_name) +MicroscopyImageSegmentation = get_class("MicroscopyImageSegmentation", extension_name) +MicroscopyPlaneSegmentation = get_class("MicroscopyPlaneSegmentation", extension_name) MicroscopySeries = get_class("MicroscopySeries", extension_name) PlanarMicroscopySeries = get_class("PlanarMicroscopySeries", extension_name) VariableDepthMicroscopySeries = get_class("VariableDepthMicroscopySeries", extension_name) @@ -39,6 +41,8 @@ "ImagingSpace", "PlanarImagingSpace", "VolumetricImagingSpace", + "MicroscopyImageSegmentation", + "MicroscopyPlaneSegmentation", "MicroscopySeries", "PlanarMicroscopySeries", "VariableDepthMicroscopySeries", diff --git a/src/pynwb/ndx_microscopy/testing/__init__.py b/src/pynwb/ndx_microscopy/testing/__init__.py index b305dac..35a2a97 100644 --- a/src/pynwb/ndx_microscopy/testing/__init__.py +++ b/src/pynwb/ndx_microscopy/testing/__init__.py @@ -1,7 +1,9 @@ from ._mock import ( mock_Microscope, + mock_MicroscopyImageSegmentation, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, + mock_MicroscopyPlaneSegmentation, mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, @@ -16,6 +18,8 @@ "mock_MicroscopyOpticalChannel", "mock_PlanarImagingSpace", "mock_VolumetricImagingSpace", + "mock_MicroscopyImageSegmentation", + "mock_MicroscopyPlaneSegmentation", "mock_PlanarMicroscopySeries", "mock_VariableDepthMicroscopySeries", "mock_VolumetricMicroscopySeries", diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 12cf5eb..17d66a3 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -1,5 +1,5 @@ import warnings -from typing import List, Optional, Tuple +from typing import Iterable, List, Optional, Tuple import numpy as np from pynwb.testing.mock.utils import name_generator @@ -115,6 +115,36 @@ def mock_VolumetricImagingSpace( return volumetric_imaging_space +def mock_MicroscopyImageSegmentation( + name: Optional[str] = None, plane_segmentations: Optional[Iterable[PlaneSegmentation]] = None +) -> ndx_microscopy.MicroscopyImageSegmentation: + name = name or name_generator("MicroscopyImageSegmentation") + plane_segmentations = plane_segmentations or [mock_MicroscopyPlaneSegmentation()] + + image_segmentation = ImageSegmentation(name=name, plane_segmentations=plane_segmentations) + + return image_segmentation + + +def mock_MicroscopyPlaneSegmentation( + imaging_space: ImagingSpace, + name: Optional[str] = None, + description: str = "This is a mock instance of a MicroscopyPlaneSegmentation type to be used for rapid testing.", + number_of_rois: int = 5, + image_shape: Tuple[int, int] = (10, 10), +) -> ndx_microscopy.MicroscopyPlaneSegmentation: + name = name or name_generator("MicroscopyPlaneSegmentation") + + plane_segmentation = ndx_microscopy.MicroscopyPlaneSegmentation( + name=name, description=description, imaging_space=imaging_space + ) + + for _ in range(number_of_rois): + plane_segmentation.add_roi(image_mask=np.zeros(image_shape, dtype=bool)) + + return plane_segmentation + + def mock_PlanarMicroscopySeries( *, microscope: ndx_microscopy.Microscope, diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index fd05c43..3ad18b1 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -4,8 +4,10 @@ from ndx_microscopy.testing import ( mock_Microscope, + mock_MicroscopyImageSegmentation, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, + mock_MicroscopyPlaneSegmentation, mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, @@ -39,6 +41,32 @@ def test_constructor_volumetric_image_space(): mock_VolumetricImagingSpace(microscope=microscope) +def test_constructor_microscopy_image_segmentation(): + mock_MicroscopyImageSegmentation() + + +def test_constructor_microscopy_plane_segmentation(): + microscope = mock_Microscope() + imaging_space = mock_PlanarImagingSpace(microscope=microscope) + + mock_MicroscopyPlaneSegmentation(imaging_space=imaging_space) + + +def test_constructor_microscopy_image_segmentation_with_plane_segmentation(): + microscope = mock_Microscope() + imaging_space = mock_PlanarImagingSpace(microscope=microscope) + + plane_segmentation_1 = mock_MicroscopyPlaneSegmentation( + imaging_space=imaging_space, name="MicroscopyPlaneSegmentation1" + ) + plane_segmentation_2 = mock_MicroscopyPlaneSegmentation( + imaging_space=imaging_space, name="MicroscopyPlaneSegmentation2" + ) + plane_segmentations = [plane_segmentation_1, plane_segmentation_2] + + mock_MicroscopyImageSegmentation(plane_segmentations=plane_segmentations) + + def test_constructor_planar_microscopy_series(): microscope = mock_Microscope() light_source = mock_MicroscopyLightSource() diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index 58c320b..4ca2694 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -213,3 +213,6 @@ def test_roundtrip(self): self.assertContainerEqual( multi_channel_microscopy_volume, read_nwbfile.acquisition["MultiChannelMicroscopyVolume"] ) + + +# TODO: add roundtrip for planesegmentation, imagesegmentation, etc. From c6b529cd27214e010994c56bf26c9c2b22444480 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 26 Jun 2024 15:42:23 -0400 Subject: [PATCH 09/34] fix autogenerated arg --- spec/ndx-microscopy.extensions.yaml | 126 ++++++++++++++++++---- src/pynwb/ndx_microscopy/testing/_mock.py | 8 +- src/pynwb/tests/test_constructors.py | 4 +- 3 files changed, 112 insertions(+), 26 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index c5fb049..1682d56 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -95,7 +95,7 @@ groups: doc: General estimate of location in the brain being subset by this space. Specify the area, layer, etc. Use standard atlas names for anatomical regions when possible. - Specify 'whole-brain' if the entire brain is strictly contained within the space. + Specify 'whole brain' if the entire brain is strictly contained within the space. required: false links: - name: microscope @@ -106,22 +106,14 @@ groups: neurodata_type_inc: ImagingSpace doc: Metadata about the 2-dimensional slice of physical space that imaging data was recorded from. datasets: - - name: grid_spacing + - name: grid_spacing_in_um dtype: float64 dims: - - x, y shape: - - 2 - doc: Amount of space between pixels in the specified unit. - Specify 'z' only when imaging volume is a regular grid; otherwise only specify 'x' and 'y'. - See origin_coordinates to determine where the grid begins. + doc: Amount of space between pixels in micrometers. quantity: '?' -# TODO: deal with grid_spacing units -# attributes: -# - name: unit -# dtype: text -# default_value: micrometers -# doc: Measurement units for grid spacing. The default value is 'micrometers'. attributes: - name: reference_frame dtype: text @@ -146,22 +138,14 @@ groups: neurodata_type_inc: ImagingSpace doc: Metadata about the 3-dimensional region of physical space that imaging data was recorded from. datasets: - - name: grid_spacing - doc: Amount of space between pixels in (x, y) or voxels in (x, y, z), in the specified unit. - Specify 'z' only when imaging volume is a regular grid; otherwise only specify 'x' and 'y'. - See origin_coordinates to determine where the grid begins. + - name: grid_spacing_in_um + doc: Amount of space between voxels in micrometers. dtype: float64 dims: - - x, y, z shape: - - 3 quantity: '?' -# TODO: deal with grid_spacing units -# attributes: -# - name: unit -# dtype: text -# default_value: micrometers -# doc: Measurement units for grid spacing. The default value is 'micrometers'. attributes: - name: reference_frame doc: Describes the reference frame of origin_coordinates and grid_spacing. @@ -182,6 +166,106 @@ groups: dtype: text required: false + + # These are needed to allow linkage of processed data to the new objects, until this is merged to core + # Technically the RoiResponseSeries shouldn't need to be modified since it just takes a DynamicTableRegion and + # does not care about the target + - neurodata_type_def: MicroscopyImageSegmentation + neurodata_type_inc: NWBDataInterface + default_name: MicroscopyImageSegmentation + doc: Stores pixels in an image that represent different regions of interest (ROIs) + or masks. All segmentation for a given imaging plane is stored together, with + storage for multiple imaging planes (masks) supported. Each ROI is stored in its + own subgroup, with the ROI group containing both a 2D mask and a list of pixels + that make up this mask. Segments can also be used for masking neuropil. If segmentation + is allowed to change with time, a new imaging plane (or module) is required and + ROI names should remain consistent between them. + groups: + - neurodata_type_inc: MicroscopyPlaneSegmentation + doc: Results from image segmentation of a specific imaging plane. + quantity: '+' + + + - neurodata_type_def: MicroscopyPlaneSegmentation + neurodata_type_inc: DynamicTable + doc: Results from image segmentation of a specific imaging plane. + datasets: + - name: image_mask + neurodata_type_inc: VectorData + dims: + - - num_roi + - num_x + - num_y + - - num_roi + - num_x + - num_y + - num_z + shape: + - - null + - null + - null + - - null + - null + - null + - null + doc: ROI masks for each ROI. Each image mask is the size of the original imaging + plane (or volume) and members of the ROI are finite non-zero. + quantity: '?' + - name: pixel_mask_index + neurodata_type_inc: VectorIndex + doc: Index into pixel_mask. + quantity: '?' + - name: pixel_mask + neurodata_type_inc: VectorData + dtype: + - name: x + dtype: uint32 + doc: Pixel x-coordinate. + - name: y + dtype: uint32 + doc: Pixel y-coordinate. + - name: weight + dtype: float32 + doc: Weight of the pixel. + doc: 'Pixel masks for each ROI: a list of indices and weights for the ROI. Pixel + masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation' + quantity: '?' + - name: voxel_mask_index + neurodata_type_inc: VectorIndex + doc: Index into voxel_mask. + quantity: '?' + - name: voxel_mask + neurodata_type_inc: VectorData + dtype: + - name: x + dtype: uint32 + doc: Voxel x-coordinate. + - name: y + dtype: uint32 + doc: Voxel y-coordinate. + - name: z + dtype: uint32 + doc: Voxel z-coordinate. + - name: weight + dtype: float32 + doc: Weight of the voxel. + doc: 'Voxel masks for each ROI: a list of indices and weights for the ROI. Voxel + masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation' + quantity: '?' + groups: + - name: reference_images + doc: Image stacks that the segmentation masks apply to. + groups: + - neurodata_type_inc: ImageSeries + doc: One or more image stacks that the masks apply to (can be one-element + stack). + quantity: '*' + links: + - name: imaging_space + target_type: ImagingSpace + doc: Link to ImagingSpace object from which this data was generated. + + - neurodata_type_def: MicroscopySeries neurodata_type_inc: TimeSeries doc: Imaging data acquired over time from an optical channel in a microscope while a light source illuminates the diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 17d66a3..605622e 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -116,12 +116,14 @@ def mock_VolumetricImagingSpace( def mock_MicroscopyImageSegmentation( - name: Optional[str] = None, plane_segmentations: Optional[Iterable[PlaneSegmentation]] = None + name: Optional[str] = None, microscopy_plane_segmentations: Optional[Iterable[PlaneSegmentation]] = None ) -> ndx_microscopy.MicroscopyImageSegmentation: name = name or name_generator("MicroscopyImageSegmentation") - plane_segmentations = plane_segmentations or [mock_MicroscopyPlaneSegmentation()] + microscopy_plane_segmentations = microscopy_plane_segmentations or [mock_MicroscopyPlaneSegmentation()] - image_segmentation = ImageSegmentation(name=name, plane_segmentations=plane_segmentations) + image_segmentation = ndx_microscopy.ImageSegmentation( + name=name, microscopy_plane_segmentations=microscopy_plane_segmentations + ) return image_segmentation diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index 3ad18b1..c56f4a9 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -62,9 +62,9 @@ def test_constructor_microscopy_image_segmentation_with_plane_segmentation(): plane_segmentation_2 = mock_MicroscopyPlaneSegmentation( imaging_space=imaging_space, name="MicroscopyPlaneSegmentation2" ) - plane_segmentations = [plane_segmentation_1, plane_segmentation_2] + microscopy_plane_segmentations = [plane_segmentation_1, plane_segmentation_2] - mock_MicroscopyImageSegmentation(plane_segmentations=plane_segmentations) + mock_MicroscopyImageSegmentation(microscopy_plane_segmentations=microscopy_plane_segmentations) def test_constructor_planar_microscopy_series(): From 22836edd6b2f778d83c6ea735ead93d1dd665192 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 26 Jun 2024 16:26:50 -0400 Subject: [PATCH 10/34] variable depth volume --- spec/ndx-microscopy.extensions.yaml | 79 +++++++++++++++++++- src/pynwb/ndx_microscopy/__init__.py | 2 + src/pynwb/ndx_microscopy/testing/__init__.py | 2 + src/pynwb/ndx_microscopy/testing/_mock.py | 39 +++++++++- src/pynwb/tests/test_constructors.py | 15 ++++ 5 files changed, 135 insertions(+), 2 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index 1682d56..655d4fd 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -311,7 +311,7 @@ groups: datasets: - name: depth_per_frame_in_mm doc: Depth of each frame in the data array. - These values offset the 'z' value of the origin_coordinates of the linked imaging_space object. + These values offset the 'z' value of the `origin_coordinates` of the linked `imaging_space` object. dtype: numeric dims: - frames @@ -343,6 +343,7 @@ groups: was recorded from. target_type: VolumetricImagingSpace + - neurodata_type_def: MultiChannelMicroscopyVolume neurodata_type_inc: NWBDataInterface doc: Static (not time-varying) volumetric imaging data acquired from multiple optical channels. @@ -409,3 +410,79 @@ groups: doc: Link to VolumetricImagingSpace object containing metadata about the region of physical space this imaging data was recorded from. target_type: VolumetricImagingSpace + + + - neurodata_type_def: VariableDepthMultiChannelMicroscopyVolume + neurodata_type_inc: NWBDataInterface + doc: Static (not time-varying) irregularly spaced volumetric imaging data acquired from multiple optical channels. + attributes: + - name: description + dtype: text + doc: Description of the VariableDepthMultiChannelMicroscopyVolume. + required: false + - name: unit + dtype: text + doc: Base unit of measurement for working with the data. Actual stored values are + not necessarily stored in these units. To access the data in these units, + multiply 'data' by 'conversion' and add 'offset'. + - name: conversion + dtype: float32 + default_value: 1.0 + doc: Scalar to multiply each element in data to convert it to the specified 'unit'. + If the data are stored in acquisition system units or other units + that require a conversion to be interpretable, multiply the data by 'conversion' + to convert the data to the specified 'unit'. e.g. if the data acquisition system + stores values in this object as signed 16-bit integers (int16 range + -32,768 to 32,767) that correspond to a 5V range (-2.5V to 2.5V), and the data + acquisition system gain is 8000X, then the 'conversion' multiplier to get from + raw data acquisition values to recorded volts is 2.5/32768/8000 = 9.5367e-9. + required: false + - name: offset + dtype: float32 + default_value: 0.0 + doc: Scalar to add to the data after scaling by 'conversion' to finalize its coercion + to the specified 'unit'. Two common examples of this include (a) data stored in an + unsigned type that requires a shift after scaling to re-center the data, + and (b) specialized recording devices that naturally cause a scalar offset with + respect to the true units. + required: false + datasets: + - name: data + doc: Recorded imaging data, shaped by (frame height, frame width, number of depth planes, number of optical + channels). + dtype: numeric + dims: + - height + - width + - depths + - channels + shape: + - null + - null + - null + - null + - name: depth_per_frame_in_mm + doc: Depth of each frame in the data array. + These values offset the 'z' value of the `origin_coordinates` of the linked `imaging_space` object. + dtype: numeric + dims: + - depths + shape: + - null + links: + - name: microscope + doc: Link to a Microscope object containing metadata about the device used to acquire this imaging data. + target_type: Microscope + # TODO: figure out best way to link to list of light sources + - name: light_sources + doc: Link to an ordered list of MicroscopyLightSource objects containing metadata about the excitation method. + target_type: MicroscopyLightSource + # TODO: figure out best way to link to list of optical channels + - name: optical_channels + doc: Link to an ordered list of MicroscopyOpticalChannel objects containing metadata about the indicator and + filters used to collect this data. + target_type: MicroscopyOpticalChannel + - name: imaging_space + doc: Link to VolumetricImagingSpace object containing metadata about the region of physical space this imaging data + was recorded from. + target_type: VolumetricImagingSpace diff --git a/src/pynwb/ndx_microscopy/__init__.py b/src/pynwb/ndx_microscopy/__init__.py index e27a792..abc6b72 100644 --- a/src/pynwb/ndx_microscopy/__init__.py +++ b/src/pynwb/ndx_microscopy/__init__.py @@ -33,6 +33,7 @@ VariableDepthMicroscopySeries = get_class("VariableDepthMicroscopySeries", extension_name) VolumetricMicroscopySeries = get_class("VolumetricMicroscopySeries", extension_name) MultiChannelMicroscopyVolume = get_class("MultiChannelMicroscopyVolume", extension_name) +VariableDepthMultiChannelMicroscopyVolume = get_class("VariableDepthMultiChannelMicroscopyVolume", extension_name) __all__ = [ "Microscope", @@ -48,4 +49,5 @@ "VariableDepthMicroscopySeries", "VolumetricMicroscopySeries", "MultiChannelMicroscopyVolume", + "VariableDepthMultiChannelMicroscopyVolume", ] diff --git a/src/pynwb/ndx_microscopy/testing/__init__.py b/src/pynwb/ndx_microscopy/testing/__init__.py index 35a2a97..e88baad 100644 --- a/src/pynwb/ndx_microscopy/testing/__init__.py +++ b/src/pynwb/ndx_microscopy/testing/__init__.py @@ -8,6 +8,7 @@ mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, mock_VariableDepthMicroscopySeries, + mock_VariableDepthMultiChannelMicroscopyVolume, mock_VolumetricImagingSpace, mock_VolumetricMicroscopySeries, ) @@ -24,4 +25,5 @@ "mock_VariableDepthMicroscopySeries", "mock_VolumetricMicroscopySeries", "mock_MultiChannelMicroscopyVolume", + "mock_VariableDepthMultiChannelMicroscopyVolume", ] diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 605622e..5998785 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -221,7 +221,6 @@ def mock_VariableDepthMicroscopySeries( ) -> ndx_microscopy.VariableDepthMicroscopySeries: series_name = name or name_generator("VariableDepthMicroscopySeries") series_data = data if data is not None else np.ones(shape=(15, 5, 5)) - series_depth_per_frame_in_mm = ( depth_per_frame_in_mm if depth_per_frame_in_mm is not None @@ -349,3 +348,41 @@ def mock_MultiChannelMicroscopyVolume( offset=offset, ) return volumetric_microscopy_series + + +def mock_VariableDepthMultiChannelMicroscopyVolume( + *, + microscope: ndx_microscopy.Microscope, + light_sources: List[ndx_microscopy.MicroscopyLightSource], + imaging_space: ndx_microscopy.VolumetricImagingSpace, + optical_channels: List[ndx_microscopy.MicroscopyOpticalChannel], + name: Optional[str] = None, + description: str = "This is a mock instance of a MultiChannelMicroscopyVolume type to be used for rapid testing.", + data: Optional[np.ndarray] = None, + depth_per_frame_in_mm: Optional[np.ndarray] = None, + unit: str = "n.a.", + conversion: float = 1.0, + offset: float = 0.0, +) -> ndx_microscopy.VariableDepthMultiChannelMicroscopyVolume: + series_name = name or name_generator("MultiChannelMicroscopyVolume") + imaging_data = data if data is not None else np.ones(shape=(10, 20, 7, 3)) + volume_depth_per_frame_in_mm = ( + depth_per_frame_in_mm + if depth_per_frame_in_mm is not None + else np.linspace(start=0.0, stop=1.0, num=series_data.shape[0]) + ) + + variable_depth_multi_channel_microscopy_volume = ndx_microscopy.VariableDepthMultiChannelMicroscopyVolume( + name=series_name, + description=description, + microscope=microscope, + light_sources=light_sources[0], # TODO: figure out how to specify list + imaging_space=imaging_space, + optical_channels=optical_channels[0], # TODO: figure out how to specify list + data=imaging_data, + depth_per_frame_in_mm=volume_depth_per_frame_in_mm, + unit=unit, + conversion=conversion, + offset=offset, + ) + return variable_depth_multi_channel_microscopy_volume diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index c56f4a9..b94669e 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -12,6 +12,7 @@ mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, mock_VariableDepthMicroscopySeries, + mock_VariableDepthMultiChannelMicroscopyVolume, mock_VolumetricImagingSpace, mock_VolumetricMicroscopySeries, ) @@ -114,5 +115,19 @@ def test_constructor_multi_channel_microscopy_volume(): ) +def test_constructor_variable_depth_multi_channel_microscopy_volume(): + microscope = mock_Microscope() + light_source = mock_LightSource() + imaging_space = mock_VolumetricImagingSpace(microscope=microscope) + optical_channel = mock_MicroscopyOpticalChannel() + + mock_VariableDepthMultiChannelMicroscopyVolume( + microscope=microscope, + light_source=light_source, + imaging_space=imaging_space, + optical_channels=[optical_channel], + ) + + if __name__ == "__main__": pytest.main() # Required since not a typical package structure From 04230711e5ade2effc11800a329a91de635f96a6 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:37:48 -0400 Subject: [PATCH 11/34] ryans suggestion --- spec/ndx-microscopy.extensions.yaml | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index c5fb049..7e31250 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -308,19 +308,28 @@ groups: - null - null - null + - name: light_sources + doc: Reference to an ordered list of MicroscopyLightSource objects containing metadata about the excitation method. + dtype: + reftype: object + target_type: MicroscopyLightSource + dims: + - num_light_sources + shape: + - null + - name: optical_channels + doc: Reference to an ordered list of MicroscopyOpticalChannel objects containing metadata about the indicator and filters used to collect this data. + dtype: + reftype: object + target_type: MicroscopyOpticalChannel + dims: + - num_channels + shape: + - null links: - name: microscope doc: Link to a Microscope object containing metadata about the device used to acquire this imaging data. target_type: Microscope - # TODO: figure out best way to link to list of light sources - - name: light_sources - doc: Link to an ordered list of MicroscopyLightSource objects containing metadata about the excitation method. - target_type: MicroscopyLightSource - # TODO: figure out best way to link to list of optical channels - - name: optical_channels - doc: Link to an ordered list of MicroscopyOpticalChannel objects containing metadata about the indicator and - filters used to collect this data. - target_type: MicroscopyOpticalChannel - name: imaging_space doc: Link to VolumetricImagingSpace object containing metadata about the region of physical space this imaging data was recorded from. From dd9ecb57d1969f6f0c8b68388a6beeb08b36d1bf Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:38:41 -0400 Subject: [PATCH 12/34] adjust to use full list --- src/pynwb/ndx_microscopy/testing/_mock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 12cf5eb..8c4931d 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -308,9 +308,9 @@ def mock_MultiChannelMicroscopyVolume( name=series_name, description=description, microscope=microscope, - light_sources=light_sources[0], # TODO: figure out how to specify list + light_sources=light_sources, imaging_space=imaging_space, - optical_channels=optical_channels[0], # TODO: figure out how to specify list + optical_channels=optical_channels, data=imaging_data, unit=unit, conversion=conversion, From dbdad41573b1530daf6814d67d65d14432bd4792 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:39:33 -0400 Subject: [PATCH 13/34] adjust constructor test --- src/pynwb/tests/test_constructors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index fd05c43..b812aec 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -80,8 +80,8 @@ def test_constructor_multi_channel_microscopy_volume(): mock_MultiChannelMicroscopyVolume( microscope=microscope, - light_source=light_source, imaging_space=imaging_space, + light_sources=[light_source], optical_channels=[optical_channel], ) From 963e89464e9efc8eea309ae252fa11670f3bdf63 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:40:41 -0400 Subject: [PATCH 14/34] reorder kwargs in mock --- src/pynwb/ndx_microscopy/testing/_mock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 8c4931d..ff90e8c 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -291,8 +291,8 @@ def mock_VolumetricMicroscopySeries( def mock_MultiChannelMicroscopyVolume( *, microscope: ndx_microscopy.Microscope, - light_sources: List[ndx_microscopy.MicroscopyLightSource], imaging_space: ndx_microscopy.VolumetricImagingSpace, + light_sources: List[ndx_microscopy.MicroscopyLightSource], optical_channels: List[ndx_microscopy.MicroscopyOpticalChannel], name: Optional[str] = None, description: str = "This is a mock instance of a MultiChannelMicroscopyVolume type to be used for rapid testing.", From 0962561544d0cdc12ccbbd7194f6bfbc55d29f03 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:41:23 -0400 Subject: [PATCH 15/34] adjust kwargs order in mock --- src/pynwb/ndx_microscopy/testing/_mock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index ff90e8c..f744a2c 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -308,8 +308,8 @@ def mock_MultiChannelMicroscopyVolume( name=series_name, description=description, microscope=microscope, - light_sources=light_sources, imaging_space=imaging_space, + light_sources=light_sources, optical_channels=optical_channels, data=imaging_data, unit=unit, From 7a7ae66c81e29035adc16d00256a37f32c3d4bd4 Mon Sep 17 00:00:00 2001 From: CodyCBakerPhD Date: Thu, 11 Jul 2024 14:00:22 -0400 Subject: [PATCH 16/34] Implement lists of object references with tests --- src/pynwb/ndx_microscopy/testing/_mock.py | 5 +-- src/pynwb/tests/test_constructors.py | 2 +- src/pynwb/tests/test_roundtrip.py | 44 ++++++++++++++++------- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index f744a2c..7024cdb 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -2,6 +2,7 @@ from typing import List, Optional, Tuple import numpy as np +import pynwb.base from pynwb.testing.mock.utils import name_generator import ndx_microscopy @@ -292,8 +293,8 @@ def mock_MultiChannelMicroscopyVolume( *, microscope: ndx_microscopy.Microscope, imaging_space: ndx_microscopy.VolumetricImagingSpace, - light_sources: List[ndx_microscopy.MicroscopyLightSource], - optical_channels: List[ndx_microscopy.MicroscopyOpticalChannel], + light_sources: pynwb.base.VectorData, + optical_channels: pynwb.base.VectorData, name: Optional[str] = None, description: str = "This is a mock instance of a MultiChannelMicroscopyVolume type to be used for rapid testing.", data: Optional[np.ndarray] = None, diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index b812aec..721d55b 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -74,7 +74,7 @@ def test_constructor_volumetric_microscopy_series(): def test_constructor_multi_channel_microscopy_volume(): microscope = mock_Microscope() - light_source = mock_LightSource() + light_source = mock_MicroscopyLightSource() imaging_space = mock_VolumetricImagingSpace(microscope=microscope) optical_channel = mock_MicroscopyOpticalChannel() diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index 58c320b..b8415a4 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -21,7 +21,7 @@ class TestPlanarMicroscopySeriesSimpleRoundtrip(pynwb_TestCase): """Simple roundtrip test for PlanarMicroscopySeries.""" def setUp(self): - self.nwbfile_path = "test.nwb" + self.nwbfile_path = "test_planar_microscopy_series_roundtrip.nwb" def tearDown(self): pynwb.testing.remove_test_file(self.nwbfile_path) @@ -69,7 +69,7 @@ class TestVolumetricMicroscopySeriesSimpleRoundtrip(pynwb_TestCase): """Simple roundtrip test for VolumetricMicroscopySeries.""" def setUp(self): - self.nwbfile_path = "test.nwb" + self.nwbfile_path = "test_volumetric_microscopy_series_roundtrip.nwb" def tearDown(self): pynwb.testing.remove_test_file(self.nwbfile_path) @@ -119,7 +119,7 @@ class TestVariableDepthMicroscopySeriesSimpleRoundtrip(pynwb_TestCase): """Simple roundtrip test for VariableDepthMicroscopySeries.""" def setUp(self): - self.nwbfile_path = "test.nwb" + self.nwbfile_path = "test_variable_depth_microscopy_series_roundtrip.nwb" def tearDown(self): pynwb.testing.remove_test_file(self.nwbfile_path) @@ -169,7 +169,7 @@ class TestMultiChannelMicroscopyVolumeSimpleRoundtrip(pynwb_TestCase): """Simple roundtrip test for MultiChannelMicroscopyVolume.""" def setUp(self): - self.nwbfile_path = "test.nwb" + self.nwbfile_path = "test_multi_channel_microscopy_volume_roundtrip.nwb" def tearDown(self): pynwb.testing.remove_test_file(self.nwbfile_path) @@ -180,21 +180,35 @@ def test_roundtrip(self): microscope = mock_Microscope(name="Microscope") nwbfile.add_device(devices=microscope) - light_source = mock_LightSource(name="LightSource") - nwbfile.add_device(devices=light_source) - imaging_space = mock_VolumetricImagingSpace(name="VolumetricImagingSpace", microscope=microscope) nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() - optical_channel = mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") - nwbfile.add_lab_meta_data(lab_meta_data=optical_channel) + light_sources = list() + light_source_0 = mock_MicroscopyLightSource(name="LightSource") + nwbfile.add_device(devices=light_source_0) + light_sources.append(light_source_0) + + optical_channels = list() + optical_channel_0 = mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") + nwbfile.add_lab_meta_data(lab_meta_data=optical_channel_0) + optical_channels.append(optical_channel_0) + # TODO: It might be more convenient in Python to have a custom constructor that takes in a list of + # light sources and optical channels and does the VectorData wrapping internally + light_sources_used_by_volume = pynwb.base.VectorData( + name="light_sources", description="Light sources used by this MultiChannelVolume.", data=light_sources + ) + optical_channels_used_by_volume = pynwb.base.VectorData( + name="optical_channels", + description="Optical channels ordered to correspond to the third axis (e.g., [0, 0, :, 0]) of the data for this MultiChannelVolume.", + data=optical_channels, + ) multi_channel_microscopy_volume = mock_MultiChannelMicroscopyVolume( name="MultiChannelMicroscopyVolume", microscope=microscope, - light_source=light_source, imaging_space=imaging_space, - optical_channels=[optical_channel], + light_sources=light_sources_used_by_volume, + optical_channels=optical_channels_used_by_volume, ) nwbfile.add_acquisition(nwbdata=multi_channel_microscopy_volume) @@ -205,11 +219,15 @@ def test_roundtrip(self): read_nwbfile = io.read() self.assertContainerEqual(microscope, read_nwbfile.devices["Microscope"]) - self.assertContainerEqual(light_source, read_nwbfile.devices["LightSource"]) + self.assertContainerEqual(light_source_0, read_nwbfile.devices["LightSource"]) self.assertContainerEqual(imaging_space, read_nwbfile.lab_meta_data["VolumetricImagingSpace"]) - self.assertContainerEqual(optical_channel, read_nwbfile.lab_meta_data["MicroscopyOpticalChannel"]) + self.assertContainerEqual(optical_channel_0, read_nwbfile.lab_meta_data["MicroscopyOpticalChannel"]) self.assertContainerEqual( multi_channel_microscopy_volume, read_nwbfile.acquisition["MultiChannelMicroscopyVolume"] ) + + +if __name__ == "__main__": + pytest.main() # Required since not a typical package structure From 4a6444ac5a30fbeb71678df4071ca75b8d0ebb37 Mon Sep 17 00:00:00 2001 From: CodyCBakerPhD Date: Thu, 11 Jul 2024 14:07:28 -0400 Subject: [PATCH 17/34] Adjust constructor test to match --- src/pynwb/tests/test_constructors.py | 20 ++++++++++++++++---- src/pynwb/tests/test_roundtrip.py | 5 ++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index 721d55b..7585c1d 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -2,6 +2,7 @@ import pytest +import pynwb from ndx_microscopy.testing import ( mock_Microscope, mock_MicroscopyLightSource, @@ -74,15 +75,26 @@ def test_constructor_volumetric_microscopy_series(): def test_constructor_multi_channel_microscopy_volume(): microscope = mock_Microscope() - light_source = mock_MicroscopyLightSource() imaging_space = mock_VolumetricImagingSpace(microscope=microscope) - optical_channel = mock_MicroscopyOpticalChannel() + light_sources = [mock_MicroscopyLightSource()] + optical_channels = [mock_MicroscopyOpticalChannel()] + light_sources_used_by_volume = pynwb.base.VectorData( + name="light_sources", description="Light sources used by this MultiChannelVolume.", data=light_sources + ) + optical_channels_used_by_volume = pynwb.base.VectorData( + name="optical_channels", + description=( + "Optical channels ordered to correspond to the third axis (e.g., [0, 0, :, 0]) " + "of the data for this MultiChannelVolume." + ), + data=optical_channels, + ) mock_MultiChannelMicroscopyVolume( microscope=microscope, imaging_space=imaging_space, - light_sources=[light_source], - optical_channels=[optical_channel], + light_sources=light_sources_used_by_volume, + optical_channels=optical_channels_used_by_volume, ) diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index b8415a4..530a703 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -200,7 +200,10 @@ def test_roundtrip(self): ) optical_channels_used_by_volume = pynwb.base.VectorData( name="optical_channels", - description="Optical channels ordered to correspond to the third axis (e.g., [0, 0, :, 0]) of the data for this MultiChannelVolume.", + description=( + "Optical channels ordered to correspond to the third axis (e.g., [0, 0, :, 0]) " + "of the data for this MultiChannelVolume." + ), data=optical_channels, ) multi_channel_microscopy_volume = mock_MultiChannelMicroscopyVolume( From 4912507837e09ee74649f9c805693c40046f5c72 Mon Sep 17 00:00:00 2001 From: CodyCBakerPhD Date: Thu, 11 Jul 2024 14:13:52 -0400 Subject: [PATCH 18/34] fix outer spec to match altered one --- spec/ndx-microscopy.extensions.yaml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index 7e31250..15a6988 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -309,23 +309,17 @@ groups: - null - null - name: light_sources - doc: Reference to an ordered list of MicroscopyLightSource objects containing metadata about the excitation method. + doc: Reference to a list of MicroscopyLightSource objects containing metadata about the excitation methods. + neurodata_type_inc: VectorData dtype: reftype: object target_type: MicroscopyLightSource - dims: - - num_light_sources - shape: - - null - name: optical_channels doc: Reference to an ordered list of MicroscopyOpticalChannel objects containing metadata about the indicator and filters used to collect this data. + neurodata_type_inc: VectorData dtype: reftype: object target_type: MicroscopyOpticalChannel - dims: - - num_channels - shape: - - null links: - name: microscope doc: Link to a Microscope object containing metadata about the device used to acquire this imaging data. From 5b645dee3c6a69de967a275e00ff49e9b6231c45 Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:59:37 -0400 Subject: [PATCH 19/34] Update spec/ndx-microscopy.extensions.yaml --- spec/ndx-microscopy.extensions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index f2df838..cbc979f 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -464,7 +464,7 @@ groups: - null - null - null - - name: depth_per_frame_in_mm + - name: depth_per_frame_in_um doc: Depth of each frame in the data array. These values offset the 'z' value of the `origin_coordinates` of the linked `imaging_space` object. dtype: numeric From eb9ee22841555ec718a2e845861ba21c91d6130f Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:59:50 -0400 Subject: [PATCH 20/34] Update spec/ndx-microscopy.extensions.yaml --- spec/ndx-microscopy.extensions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index cbc979f..f2df838 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -464,7 +464,7 @@ groups: - null - null - null - - name: depth_per_frame_in_um + - name: depth_per_frame_in_mm doc: Depth of each frame in the data array. These values offset the 'z' value of the `origin_coordinates` of the linked `imaging_space` object. dtype: numeric From bb380a48a58734e45dd96e24269e8f0d5e8f2197 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 19:03:52 +0000 Subject: [PATCH 21/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/pynwb/tests/test_constructors.py | 2 +- src/pynwb/tests/test_roundtrip.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index 2c8fc3a..7903230 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -4,13 +4,13 @@ import pynwb from ndx_microscopy.testing import ( + mock_LightSource, mock_Microscope, mock_MicroscopyImageSegmentation, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, mock_MicroscopyPlaneSegmentation, mock_MultiChannelMicroscopyVolume, - mock_LightSource, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, mock_VariableDepthMicroscopySeries, diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index eb33e02..255df6c 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -1,10 +1,10 @@ """Test roundtrip (write and read back) of the Python API for the ndx-microscopy extension.""" -import pynwb import pytest from pynwb.testing import TestCase as pynwb_TestCase from pynwb.testing.mock.file import mock_NWBFile +import pynwb from ndx_microscopy.testing import ( mock_Microscope, mock_MicroscopyLightSource, From 94ee2286723c422aa2b97da07b452757250045f2 Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 15:22:53 -0400 Subject: [PATCH 22/34] alessandras comments and tests --- spec/ndx-microscopy.extensions.yaml | 13 +++---- src/pynwb/ndx_microscopy/__init__.py | 4 +- src/pynwb/ndx_microscopy/testing/_mock.py | 13 ++++--- src/pynwb/tests/test_constructors.py | 8 ++-- src/pynwb/tests/test_roundtrip.py | 45 ++++++++++++++++++++++- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index 0382803..2fc474b 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -170,9 +170,9 @@ groups: # These are needed to allow linkage of processed data to the new objects, until this is merged to core # Technically the RoiResponseSeries shouldn't need to be modified since it just takes a DynamicTableRegion and # does not care about the target - - neurodata_type_def: MicroscopyImageSegmentation + - neurodata_type_def: MicroscopySegmentations neurodata_type_inc: NWBDataInterface - default_name: MicroscopyImageSegmentation + default_name: MicroscopySegmentations doc: Stores pixels in an image that represent different regions of interest (ROIs) or masks. All segmentation for a given imaging plane is stored together, with storage for multiple imaging planes (masks) supported. Each ROI is stored in its @@ -253,12 +253,11 @@ groups: masks are concatenated and parsing of this dataset is maintained by the PlaneSegmentation' quantity: '?' groups: - - name: reference_images - doc: Image stacks that the segmentation masks apply to. + - name: summary_images + doc: Summary images that are related to the plane segmentation, e.g., mean, correlation, maximum projection. groups: - - neurodata_type_inc: ImageSeries - doc: One or more image stacks that the masks apply to (can be one-element - stack). + - neurodata_type_inc: Images + doc: An container for the estimated summary images. quantity: '*' links: - name: imaging_space diff --git a/src/pynwb/ndx_microscopy/__init__.py b/src/pynwb/ndx_microscopy/__init__.py index abc6b72..8dfeac9 100644 --- a/src/pynwb/ndx_microscopy/__init__.py +++ b/src/pynwb/ndx_microscopy/__init__.py @@ -26,7 +26,7 @@ ImagingSpace = get_class("ImagingSpace", extension_name) PlanarImagingSpace = get_class("PlanarImagingSpace", extension_name) VolumetricImagingSpace = get_class("VolumetricImagingSpace", extension_name) -MicroscopyImageSegmentation = get_class("MicroscopyImageSegmentation", extension_name) +MicroscopySegmentations = get_class("MicroscopySegmentations", extension_name) MicroscopyPlaneSegmentation = get_class("MicroscopyPlaneSegmentation", extension_name) MicroscopySeries = get_class("MicroscopySeries", extension_name) PlanarMicroscopySeries = get_class("PlanarMicroscopySeries", extension_name) @@ -42,7 +42,7 @@ "ImagingSpace", "PlanarImagingSpace", "VolumetricImagingSpace", - "MicroscopyImageSegmentation", + "MicroscopySegmentations", "MicroscopyPlaneSegmentation", "MicroscopySeries", "PlanarMicroscopySeries", diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 8f16678..5ab81cc 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -116,21 +116,22 @@ def mock_VolumetricImagingSpace( return volumetric_imaging_space -def mock_MicroscopyImageSegmentation( - name: Optional[str] = None, microscopy_plane_segmentations: Optional[Iterable[PlaneSegmentation]] = None +def mock_MicroscopySegmentations( + name: Optional[str] = None, + microscopy_plane_segmentations: Optional[Iterable[ndx_microscopy.PlaneSegmentation]] = None, ) -> ndx_microscopy.MicroscopyImageSegmentation: - name = name or name_generator("MicroscopyImageSegmentation") + name = name or name_generator("MicroscopySegmentations") microscopy_plane_segmentations = microscopy_plane_segmentations or [mock_MicroscopyPlaneSegmentation()] - image_segmentation = ndx_microscopy.ImageSegmentation( + segmentations = ndx_microscopy.MicroscopySegmentations( name=name, microscopy_plane_segmentations=microscopy_plane_segmentations ) - return image_segmentation + return segmentations def mock_MicroscopyPlaneSegmentation( - imaging_space: ImagingSpace, + imaging_space: ndx_microscopy.ImagingSpace, name: Optional[str] = None, description: str = "This is a mock instance of a MicroscopyPlaneSegmentation type to be used for rapid testing.", number_of_rois: int = 5, diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index 2c8fc3a..5f00b9b 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -5,7 +5,7 @@ import pynwb from ndx_microscopy.testing import ( mock_Microscope, - mock_MicroscopyImageSegmentation, + mock_MicroscopySegmentations, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, mock_MicroscopyPlaneSegmentation, @@ -44,8 +44,8 @@ def test_constructor_volumetric_image_space(): mock_VolumetricImagingSpace(microscope=microscope) -def test_constructor_microscopy_image_segmentation(): - mock_MicroscopyImageSegmentation() +def test_constructor_microscopy_segmentations(): + mock_MicroscopySegmentations() def test_constructor_microscopy_plane_segmentation(): @@ -67,7 +67,7 @@ def test_constructor_microscopy_image_segmentation_with_plane_segmentation(): ) microscopy_plane_segmentations = [plane_segmentation_1, plane_segmentation_2] - mock_MicroscopyImageSegmentation(microscopy_plane_segmentations=microscopy_plane_segmentations) + mock_MicroscopySegmentations(microscopy_plane_segmentations=microscopy_plane_segmentations) def test_constructor_planar_microscopy_series(): diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index eb33e02..c5c715d 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -15,6 +15,8 @@ mock_VariableDepthMicroscopySeries, mock_VolumetricImagingSpace, mock_VolumetricMicroscopySeries, + mock_MicroscopySegmentations, + mock_MicroscopyPlaneSegmentation, ) @@ -233,7 +235,48 @@ def test_roundtrip(self): ) -# TODO: add roundtrip for planesegmentation, imagesegmentation, etc. +class TestMicroscopySegmentationsSimpleRoundtrip(pynwb_TestCase): + """Simple roundtrip test for MicroscopySegmentations.""" + + def setUp(self): + self.nwbfile_path = "test_microscopy_segmentations_roundtrip.nwb" + + def tearDown(self): + pynwb.testing.remove_test_file(self.nwbfile_path) + + def test_roundtrip(self): + nwbfile = mock_NWBFile() + + microscope = mock_Microscope() + nwbfile.add_device(devices=microscope) + + imaging_space = mock_PlanarImagingSpace(microscope=microscope) + nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() + + plane_segmentation_1 = mock_MicroscopyPlaneSegmentation( + imaging_space=imaging_space, name="MicroscopyPlaneSegmentation1" + ) + plane_segmentation_2 = mock_MicroscopyPlaneSegmentation( + imaging_space=imaging_space, name="MicroscopyPlaneSegmentation2" + ) + microscopy_plane_segmentations = [plane_segmentation_1, plane_segmentation_2] + + segmentations = mock_MicroscopySegmentations(microscopy_plane_segmentations=microscopy_plane_segmentations) + processing_module = nwbfile.create_processing_module(name="ophys") + processing_module.add(segmentations) + + with pynwb.NWBHDF5IO(path=self.nwbfile_path, mode="w") as io: + io.write(nwbfile) + + with pynwb.NWBHDF5IO(path=self.nwbfile_path, mode="r", load_namespaces=True) as io: + read_nwbfile = io.read() + + self.assertContainerEqual(microscope, read_nwbfile.devices["Microscope"]) + + self.assertContainerEqual(imaging_space, read_nwbfile.lab_meta_data["PlanarImagingSpace"]) + + self.assertContainerEqual(segmentations, read_nwbfile.processing["ophys"]["MicroscopySegmentations"]) + if __name__ == "__main__": pytest.main() # Required since not a typical package structure From e583e39bb728b1d50e1264914a34561cdd888d66 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 19:23:20 +0000 Subject: [PATCH 23/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/pynwb/tests/test_constructors.py | 2 +- src/pynwb/tests/test_roundtrip.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index 8b325fb..2c3d781 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -6,10 +6,10 @@ from ndx_microscopy.testing import ( mock_LightSource, mock_Microscope, - mock_MicroscopySegmentations, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, mock_MicroscopyPlaneSegmentation, + mock_MicroscopySegmentations, mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index 2a97616..ff17ef3 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -9,14 +9,14 @@ mock_Microscope, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, + mock_MicroscopyPlaneSegmentation, + mock_MicroscopySegmentations, mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, mock_VariableDepthMicroscopySeries, mock_VolumetricImagingSpace, mock_VolumetricMicroscopySeries, - mock_MicroscopySegmentations, - mock_MicroscopyPlaneSegmentation, ) From b581087d6c5f88f9c7aca68570a00145a4d47fc2 Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 15:28:31 -0400 Subject: [PATCH 24/34] fix import --- src/pynwb/ndx_microscopy/testing/_mock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 5ab81cc..ce88466 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -118,7 +118,7 @@ def mock_VolumetricImagingSpace( def mock_MicroscopySegmentations( name: Optional[str] = None, - microscopy_plane_segmentations: Optional[Iterable[ndx_microscopy.PlaneSegmentation]] = None, + microscopy_plane_segmentations: Optional[Iterable[ndx_microscopy.MicroscopyPlaneSegmentation]] = None, ) -> ndx_microscopy.MicroscopyImageSegmentation: name = name or name_generator("MicroscopySegmentations") microscopy_plane_segmentations = microscopy_plane_segmentations or [mock_MicroscopyPlaneSegmentation()] From c4e8f3a6deee569ed2b0d94217751c865f3ed1f2 Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 15:30:22 -0400 Subject: [PATCH 25/34] fix import --- src/pynwb/ndx_microscopy/testing/_mock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index ce88466..14c8741 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -119,7 +119,7 @@ def mock_VolumetricImagingSpace( def mock_MicroscopySegmentations( name: Optional[str] = None, microscopy_plane_segmentations: Optional[Iterable[ndx_microscopy.MicroscopyPlaneSegmentation]] = None, -) -> ndx_microscopy.MicroscopyImageSegmentation: +) -> ndx_microscopy.MicroscopySegmentations: name = name or name_generator("MicroscopySegmentations") microscopy_plane_segmentations = microscopy_plane_segmentations or [mock_MicroscopyPlaneSegmentation()] From 93a72a85259e552562d1c3c6b2688efff5f0a7f0 Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 15:32:21 -0400 Subject: [PATCH 26/34] fix import --- src/pynwb/ndx_microscopy/testing/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/__init__.py b/src/pynwb/ndx_microscopy/testing/__init__.py index e88baad..0ebf8a5 100644 --- a/src/pynwb/ndx_microscopy/testing/__init__.py +++ b/src/pynwb/ndx_microscopy/testing/__init__.py @@ -1,6 +1,6 @@ from ._mock import ( mock_Microscope, - mock_MicroscopyImageSegmentation, + mock_MicroscopySegmentations, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, mock_MicroscopyPlaneSegmentation, @@ -19,7 +19,7 @@ "mock_MicroscopyOpticalChannel", "mock_PlanarImagingSpace", "mock_VolumetricImagingSpace", - "mock_MicroscopyImageSegmentation", + "mock_MicroscopySegmentations", "mock_MicroscopyPlaneSegmentation", "mock_PlanarMicroscopySeries", "mock_VariableDepthMicroscopySeries", From a300f9d7540e6dfcddd1e09a6c757339b7c66dd8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 19:32:38 +0000 Subject: [PATCH 27/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/pynwb/ndx_microscopy/testing/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pynwb/ndx_microscopy/testing/__init__.py b/src/pynwb/ndx_microscopy/testing/__init__.py index 0ebf8a5..e27103f 100644 --- a/src/pynwb/ndx_microscopy/testing/__init__.py +++ b/src/pynwb/ndx_microscopy/testing/__init__.py @@ -1,9 +1,9 @@ from ._mock import ( mock_Microscope, - mock_MicroscopySegmentations, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, mock_MicroscopyPlaneSegmentation, + mock_MicroscopySegmentations, mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, mock_PlanarMicroscopySeries, From 335c81d428cb3a38d3bcb5685cabd288a2248cf2 Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 15:44:49 -0400 Subject: [PATCH 28/34] debug --- src/pynwb/tests/test_constructors.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index 2c3d781..b1a2ab7 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -4,7 +4,6 @@ import pynwb from ndx_microscopy.testing import ( - mock_LightSource, mock_Microscope, mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, From 70fa119404a781721f4faab4b467f3648121e6d6 Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 15:48:52 -0400 Subject: [PATCH 29/34] debugs --- src/pynwb/ndx_microscopy/testing/_mock.py | 10 ++++++---- src/pynwb/tests/test_constructors.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 14c8741..ff8bb4d 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -78,7 +78,7 @@ def mock_PlanarImagingSpace( name: Optional[str] = None, description: str = "This is a mock instance of a PlanarImagingSpace type to be used for rapid testing.", origin_coordinates: Tuple[float, float, float] = (-1.2, -0.6, -2), - grid_spacing: Tuple[float, float, float] = (0.2, 0.2), + grid_spacing_in_mm: Tuple[float, float, float] = (0.2, 0.2), location: str = "The location targeted by the mock imaging space.", reference_frame: str = "The reference frame of the mock planar imaging space.", ) -> ndx_microscopy.PlanarImagingSpace: @@ -87,7 +87,7 @@ def mock_PlanarImagingSpace( description=description, microscope=microscope, origin_coordinates=origin_coordinates, - grid_spacing=grid_spacing, + grid_spacing_in_mm=grid_spacing_in_mm, location=location, reference_frame=reference_frame, ) @@ -100,7 +100,7 @@ def mock_VolumetricImagingSpace( name: Optional[str] = None, description: str = "This is a mock instance of a VolumetricImagingSpace type to be used for rapid testing.", origin_coordinates: Tuple[float, float, float] = (-1.2, -0.6, -2), - grid_spacing: Tuple[float, float, float] = (0.2, 0.2, 0.5), + grid_spacing_in_mm: Tuple[float, float, float] = (0.2, 0.2, 0.5), location: str = "The location targeted by the mock imaging space.", reference_frame: str = "The reference frame of the mock volumetric imaging space.", ) -> ndx_microscopy.VolumetricImagingSpace: @@ -109,7 +109,7 @@ def mock_VolumetricImagingSpace( description=description, microscope=microscope, origin_coordinates=origin_coordinates, - grid_spacing=grid_spacing, + grid_spacing_in_mm=grid_spacing_in_mm, location=location, reference_frame=reference_frame, ) @@ -117,6 +117,7 @@ def mock_VolumetricImagingSpace( def mock_MicroscopySegmentations( + *, name: Optional[str] = None, microscopy_plane_segmentations: Optional[Iterable[ndx_microscopy.MicroscopyPlaneSegmentation]] = None, ) -> ndx_microscopy.MicroscopySegmentations: @@ -131,6 +132,7 @@ def mock_MicroscopySegmentations( def mock_MicroscopyPlaneSegmentation( + *, imaging_space: ndx_microscopy.ImagingSpace, name: Optional[str] = None, description: str = "This is a mock instance of a MicroscopyPlaneSegmentation type to be used for rapid testing.", diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index b1a2ab7..af884f5 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -129,7 +129,7 @@ def test_constructor_multi_channel_microscopy_volume(): def test_constructor_variable_depth_multi_channel_microscopy_volume(): microscope = mock_Microscope() - light_source = mock_LightSource() + light_source = mock_MicroscopyLightSource() imaging_space = mock_VolumetricImagingSpace(microscope=microscope) optical_channel = mock_MicroscopyOpticalChannel() From 5ac7eefd882319102b6bf9fa562252c141ee8cfe Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 15:59:49 -0400 Subject: [PATCH 30/34] debugs --- src/pynwb/ndx_microscopy/testing/_mock.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index ff8bb4d..98b7ad9 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -78,7 +78,7 @@ def mock_PlanarImagingSpace( name: Optional[str] = None, description: str = "This is a mock instance of a PlanarImagingSpace type to be used for rapid testing.", origin_coordinates: Tuple[float, float, float] = (-1.2, -0.6, -2), - grid_spacing_in_mm: Tuple[float, float, float] = (0.2, 0.2), + grid_spacing_in_um: Tuple[float, float, float] = (20, 20), location: str = "The location targeted by the mock imaging space.", reference_frame: str = "The reference frame of the mock planar imaging space.", ) -> ndx_microscopy.PlanarImagingSpace: @@ -87,7 +87,7 @@ def mock_PlanarImagingSpace( description=description, microscope=microscope, origin_coordinates=origin_coordinates, - grid_spacing_in_mm=grid_spacing_in_mm, + grid_spacing_in_um=grid_spacing_in_um, location=location, reference_frame=reference_frame, ) @@ -100,7 +100,7 @@ def mock_VolumetricImagingSpace( name: Optional[str] = None, description: str = "This is a mock instance of a VolumetricImagingSpace type to be used for rapid testing.", origin_coordinates: Tuple[float, float, float] = (-1.2, -0.6, -2), - grid_spacing_in_mm: Tuple[float, float, float] = (0.2, 0.2, 0.5), + grid_spacing_in_um: Tuple[float, float, float] = (20, 20, 50), location: str = "The location targeted by the mock imaging space.", reference_frame: str = "The reference frame of the mock volumetric imaging space.", ) -> ndx_microscopy.VolumetricImagingSpace: @@ -109,7 +109,7 @@ def mock_VolumetricImagingSpace( description=description, microscope=microscope, origin_coordinates=origin_coordinates, - grid_spacing_in_mm=grid_spacing_in_mm, + grid_spacing_in_um=grid_spacing_in_um, location=location, reference_frame=reference_frame, ) From 7effd8ea2927858d1c3d38d6645a48178d07160a Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 17:30:40 -0400 Subject: [PATCH 31/34] debugs --- src/pynwb/ndx_microscopy/testing/_mock.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 98b7ad9..0af0239 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -122,7 +122,12 @@ def mock_MicroscopySegmentations( microscopy_plane_segmentations: Optional[Iterable[ndx_microscopy.MicroscopyPlaneSegmentation]] = None, ) -> ndx_microscopy.MicroscopySegmentations: name = name or name_generator("MicroscopySegmentations") - microscopy_plane_segmentations = microscopy_plane_segmentations or [mock_MicroscopyPlaneSegmentation()] + + microscope = mock_Microscope() + imaging_space = mock_PlanarImagingSpace(microscope=microscope) + microscopy_plane_segmentations = microscopy_plane_segmentations or [ + mock_MicroscopyPlaneSegmentation(imaging_space=imaging_space) + ] segmentations = ndx_microscopy.MicroscopySegmentations( name=name, microscopy_plane_segmentations=microscopy_plane_segmentations @@ -142,11 +147,15 @@ def mock_MicroscopyPlaneSegmentation( name = name or name_generator("MicroscopyPlaneSegmentation") plane_segmentation = ndx_microscopy.MicroscopyPlaneSegmentation( - name=name, description=description, imaging_space=imaging_space + name=name, + description=description, + imaging_space=imaging_space, ) + image_masks = list() for _ in range(number_of_rois): - plane_segmentation.add_roi(image_mask=np.zeros(image_shape, dtype=bool)) + image_masks.append(np.zeros(image_shape, dtype=bool)) + plane_segmentation.add_column(name="image_mask", description="", data=image_masks) return plane_segmentation From 1d6fede3a6692d1c5a256bc1a6b66faec4e0818a Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 17:35:02 -0400 Subject: [PATCH 32/34] debugs --- src/pynwb/ndx_microscopy/testing/_mock.py | 1 + src/pynwb/tests/test_constructors.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 0af0239..6e34605 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -151,6 +151,7 @@ def mock_MicroscopyPlaneSegmentation( description=description, imaging_space=imaging_space, ) + plane_segmentation.add_column(name="id", description="", data=list(range(number_of_rois))) image_masks = list() for _ in range(number_of_rois): diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index af884f5..bf412f3 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -135,8 +135,8 @@ def test_constructor_variable_depth_multi_channel_microscopy_volume(): mock_VariableDepthMultiChannelMicroscopyVolume( microscope=microscope, - light_source=light_source, imaging_space=imaging_space, + light_sources=[light_source], optical_channels=[optical_channel], ) From 1d68a4a222af82e3aa7780f3999d262b0955bc06 Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Wed, 17 Jul 2024 17:40:20 -0400 Subject: [PATCH 33/34] debugs --- src/pynwb/tests/test_constructors.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index bf412f3..f5c29aa 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -129,15 +129,26 @@ def test_constructor_multi_channel_microscopy_volume(): def test_constructor_variable_depth_multi_channel_microscopy_volume(): microscope = mock_Microscope() - light_source = mock_MicroscopyLightSource() imaging_space = mock_VolumetricImagingSpace(microscope=microscope) - optical_channel = mock_MicroscopyOpticalChannel() + light_sources = [mock_MicroscopyLightSource()] + optical_channels = [mock_MicroscopyOpticalChannel()] + light_sources_used_by_volume = pynwb.base.VectorData( + name="light_sources", description="Light sources used by this MultiChannelVolume.", data=light_sources + ) + optical_channels_used_by_volume = pynwb.base.VectorData( + name="optical_channels", + description=( + "Optical channels ordered to correspond to the third axis (e.g., [0, 0, :, 0]) " + "of the data for this MultiChannelVolume." + ), + data=optical_channels, + ) mock_VariableDepthMultiChannelMicroscopyVolume( microscope=microscope, imaging_space=imaging_space, - light_sources=[light_source], - optical_channels=[optical_channel], + light_sources=light_sources_used_by_volume, + optical_channels=optical_channels_used_by_volume, ) From 5400462f7fac226c13239b8ef9a827fdbc477b7e Mon Sep 17 00:00:00 2001 From: codycbakerphd Date: Thu, 18 Jul 2024 13:20:44 -0400 Subject: [PATCH 34/34] debugs --- src/pynwb/ndx_microscopy/testing/_mock.py | 14 ++++++-------- src/pynwb/tests/test_roundtrip.py | 10 ++++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 6e34605..482e308 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -147,11 +147,9 @@ def mock_MicroscopyPlaneSegmentation( name = name or name_generator("MicroscopyPlaneSegmentation") plane_segmentation = ndx_microscopy.MicroscopyPlaneSegmentation( - name=name, - description=description, - imaging_space=imaging_space, + name=name, description=description, imaging_space=imaging_space, id=list(range(number_of_rois)) ) - plane_segmentation.add_column(name="id", description="", data=list(range(number_of_rois))) + # plane_segmentation.add_column(name="id", description="", data=list(range(number_of_rois))) image_masks = list() for _ in range(number_of_rois): @@ -367,9 +365,9 @@ def mock_MultiChannelMicroscopyVolume( def mock_VariableDepthMultiChannelMicroscopyVolume( *, microscope: ndx_microscopy.Microscope, - light_sources: List[ndx_microscopy.MicroscopyLightSource], imaging_space: ndx_microscopy.VolumetricImagingSpace, - optical_channels: List[ndx_microscopy.MicroscopyOpticalChannel], + light_sources: pynwb.base.VectorData, + optical_channels: pynwb.base.VectorData, name: Optional[str] = None, description: str = "This is a mock instance of a MultiChannelMicroscopyVolume type to be used for rapid testing.", data: Optional[np.ndarray] = None, @@ -392,9 +390,9 @@ def mock_VariableDepthMultiChannelMicroscopyVolume( name=series_name, description=description, microscope=microscope, - light_sources=light_sources[0], # TODO: figure out how to specify list imaging_space=imaging_space, - optical_channels=optical_channels[0], # TODO: figure out how to specify list + light_sources=light_sources, + optical_channels=optical_channels, data=imaging_data, depth_per_frame_in_mm=volume_depth_per_frame_in_mm, unit=unit, diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index ff17ef3..4d9f830 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -247,10 +247,10 @@ def tearDown(self): def test_roundtrip(self): nwbfile = mock_NWBFile() - microscope = mock_Microscope() + microscope = mock_Microscope(name="Microscope") nwbfile.add_device(devices=microscope) - imaging_space = mock_PlanarImagingSpace(microscope=microscope) + imaging_space = mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() plane_segmentation_1 = mock_MicroscopyPlaneSegmentation( @@ -261,8 +261,10 @@ def test_roundtrip(self): ) microscopy_plane_segmentations = [plane_segmentation_1, plane_segmentation_2] - segmentations = mock_MicroscopySegmentations(microscopy_plane_segmentations=microscopy_plane_segmentations) - processing_module = nwbfile.create_processing_module(name="ophys") + segmentations = mock_MicroscopySegmentations( + name="MicroscopySegmentations", microscopy_plane_segmentations=microscopy_plane_segmentations + ) + processing_module = nwbfile.create_processing_module(name="ophys", description="") processing_module.add(segmentations) with pynwb.NWBHDF5IO(path=self.nwbfile_path, mode="w") as io: