From 6f5ceae572394e84d6da3170b757a14b069ab30e Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:54:04 -0400 Subject: [PATCH] [Enhancement IV] Add roi response series (#19) * add multichannel volume * swap to datainterface * fix import * fix test name * fix intercompatability * fix light sources * fix * add plane segmentation stuff * fix autogenerated arg * variable depth volume * swap to um * override for roi response to avoid construct error * add container too * ryans suggestion * adjust to use full list * adjust constructor test * reorder kwargs in mock * adjust kwargs order in mock * Implement lists of object references with tests * Adjust constructor test to match * fix outer spec to match altered one * Update spec/ndx-microscopy.extensions.yaml * Update spec/ndx-microscopy.extensions.yaml * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * alessandras comments and tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix import * fix import * fix import * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * debug * debugs * debugs * debugs * debugs * debugs * debugs * debugs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * start adding tests * start adding tests * complete tests * debug * debug --------- Co-authored-by: CodyCBakerPhD Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- spec/ndx-microscopy.extensions.yaml | 28 ++++ src/pynwb/ndx_microscopy/__init__.py | 6 + src/pynwb/ndx_microscopy/testing/__init__.py | 4 + src/pynwb/ndx_microscopy/testing/_mock.py | 89 ++++++++++-- src/pynwb/tests/test_constructors.py | 132 ++++++++++-------- src/pynwb/tests/test_roundtrip.py | 135 +++++++++++++------ 6 files changed, 288 insertions(+), 106 deletions(-) diff --git a/spec/ndx-microscopy.extensions.yaml b/spec/ndx-microscopy.extensions.yaml index 118a9da..dd0279f 100644 --- a/spec/ndx-microscopy.extensions.yaml +++ b/spec/ndx-microscopy.extensions.yaml @@ -506,3 +506,31 @@ 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: MicroscopyResponseSeries + neurodata_type_inc: TimeSeries + doc: ROI responses extracted from optical imaging. + datasets: + - name: data + dtype: numeric + dims: + - - number_of_frames + - number_of_rois + shape: + - - null + - null + doc: Signals from ROIs. + - name: table_region + neurodata_type_inc: DynamicTableRegion + doc: DynamicTableRegion referencing plane segmentation containing more information about the ROIs + stored in this series. + + - neurodata_type_def: MicroscopyResponseSeriesContainer + neurodata_type_inc: NWBDataInterface + default_name: MicroscopyResponseSeriesContainer + doc: A container of many MicroscopyResponseSeries. + groups: + - neurodata_type_inc: MicroscopyResponseSeries + doc: MicroscopyResponseSeries object(s) containing fluorescence data for a ROI. + quantity: '+' diff --git a/src/pynwb/ndx_microscopy/__init__.py b/src/pynwb/ndx_microscopy/__init__.py index 8dfeac9..d5f989b 100644 --- a/src/pynwb/ndx_microscopy/__init__.py +++ b/src/pynwb/ndx_microscopy/__init__.py @@ -35,6 +35,10 @@ MultiChannelMicroscopyVolume = get_class("MultiChannelMicroscopyVolume", extension_name) VariableDepthMultiChannelMicroscopyVolume = get_class("VariableDepthMultiChannelMicroscopyVolume", extension_name) +MicroscopyResponseSeries = get_class("MicroscopyResponseSeries", extension_name) +MicroscopyResponseSeriesContainer = get_class("MicroscopyResponseSeriesContainer", extension_name) + + __all__ = [ "Microscope", "MicroscopyLightSource", @@ -50,4 +54,6 @@ "VolumetricMicroscopySeries", "MultiChannelMicroscopyVolume", "VariableDepthMultiChannelMicroscopyVolume", + "MicroscopyResponseSeries", + "MicroscopyResponseSeriesContainer", ] diff --git a/src/pynwb/ndx_microscopy/testing/__init__.py b/src/pynwb/ndx_microscopy/testing/__init__.py index e27103f..848c090 100644 --- a/src/pynwb/ndx_microscopy/testing/__init__.py +++ b/src/pynwb/ndx_microscopy/testing/__init__.py @@ -3,6 +3,8 @@ mock_MicroscopyLightSource, mock_MicroscopyOpticalChannel, mock_MicroscopyPlaneSegmentation, + mock_MicroscopyResponseSeries, + mock_MicroscopyResponseSeriesContainer, mock_MicroscopySegmentations, mock_MultiChannelMicroscopyVolume, mock_PlanarImagingSpace, @@ -25,5 +27,7 @@ "mock_VariableDepthMicroscopySeries", "mock_VolumetricMicroscopySeries", "mock_MultiChannelMicroscopyVolume", + "mock_MicroscopyResponseSeries", + "mock_MicroscopyResponseSeriesContainer", "mock_VariableDepthMultiChannelMicroscopyVolume", ] diff --git a/src/pynwb/ndx_microscopy/testing/_mock.py b/src/pynwb/ndx_microscopy/testing/_mock.py index 862889d..cb7d919 100644 --- a/src/pynwb/ndx_microscopy/testing/_mock.py +++ b/src/pynwb/ndx_microscopy/testing/_mock.py @@ -11,7 +11,7 @@ def mock_Microscope( *, name: Optional[str] = None, - description: str = "This is a mock instance of a Microscope type to be used for rapid testing.", + description: str = "A mock instance of a Microscope type to be used for rapid testing.", manufacturer: str = "A fake manufacturer of the mock microscope.", model: str = "A fake model of the mock microscope.", ) -> ndx_microscopy.Microscope: @@ -27,7 +27,7 @@ def mock_Microscope( def mock_MicroscopyLightSource( *, name: Optional[str] = None, - description: str = "This is a mock instance of a MicroscopyLightSource type to be used for rapid testing.", + description: str = "A mock instance of a MicroscopyLightSource type to be used for rapid testing.", manufacturer: str = "A fake manufacturer of the mock light source.", model: str = "A fake model of the mock light source.", filter_description: str = "A description about the fake filter used by the mock light source.", @@ -57,7 +57,7 @@ def mock_MicroscopyLightSource( def mock_MicroscopyOpticalChannel( *, name: Optional[str] = None, - description: str = "This is a mock instance of a MicroscopyOpticalChannel type to be used for rapid testing.", + description: str = "A mock instance of a MicroscopyOpticalChannel type to be used for rapid testing.", indicator: str = "The indicator targeted by the mock optical channel.", filter_description: str = "A description about the fake filter used by the mock optical channel.", emission_wavelength_in_nm: float = 450.0, @@ -76,7 +76,7 @@ def mock_PlanarImagingSpace( *, microscope: ndx_microscopy.Microscope, name: Optional[str] = None, - description: str = "This is a mock instance of a PlanarImagingSpace type to be used for rapid testing.", + description: str = "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_um: Tuple[float, float, float] = (20, 20), location: str = "The location targeted by the mock imaging space.", @@ -98,7 +98,7 @@ def mock_VolumetricImagingSpace( *, microscope: ndx_microscopy.Microscope, name: Optional[str] = None, - description: str = "This is a mock instance of a VolumetricImagingSpace type to be used for rapid testing.", + description: str = "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_um: Tuple[float, float, float] = (20, 20, 50), location: str = "The location targeted by the mock imaging space.", @@ -140,7 +140,7 @@ 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.", + description: str = "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: @@ -166,7 +166,7 @@ def mock_PlanarMicroscopySeries( imaging_space: ndx_microscopy.PlanarImagingSpace, optical_channel: ndx_microscopy.MicroscopyOpticalChannel, name: Optional[str] = None, - description: str = "This is a mock instance of a PlanarMicroscopySeries type to be used for rapid testing.", + description: str = "A mock instance of a PlanarMicroscopySeries type to be used for rapid testing.", data: Optional[np.ndarray] = None, unit: str = "a.u.", conversion: float = 1.0, @@ -221,7 +221,7 @@ def mock_VariableDepthMicroscopySeries( imaging_space: ndx_microscopy.PlanarImagingSpace, optical_channel: ndx_microscopy.MicroscopyOpticalChannel, name: Optional[str] = None, - description: str = "This is a mock instance of a PlanarMicroscopySeries type to be used for rapid testing.", + description: str = "A mock instance of a PlanarMicroscopySeries type to be used for rapid testing.", data: Optional[np.ndarray] = None, depth_per_frame_in_um: Optional[np.ndarray] = None, unit: str = "a.u.", @@ -284,7 +284,7 @@ def mock_VolumetricMicroscopySeries( imaging_space: ndx_microscopy.VolumetricImagingSpace, optical_channel: ndx_microscopy.MicroscopyOpticalChannel, name: Optional[str] = None, - description: str = "This is a mock instance of a VolumetricMicroscopySeries type to be used for rapid testing.", + description: str = "A mock instance of a VolumetricMicroscopySeries type to be used for rapid testing.", data: Optional[np.ndarray] = None, unit: str = "a.u.", conversion: float = 1.0, @@ -339,7 +339,7 @@ def mock_MultiChannelMicroscopyVolume( 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.", + description: str = "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, @@ -363,6 +363,73 @@ def mock_MultiChannelMicroscopyVolume( return volumetric_microscopy_series +def mock_MicroscopyResponseSeries( + *, + table_region: pynwb.core.DynamicTableRegion, + name: Optional[str] = None, + description: str = "A mock instance of a MicroscopyResponseSeries type to be used for rapid testing.", + data: Optional[np.ndarray] = None, + unit: str = "a.u.", + conversion: float = 1.0, + offset: float = 0.0, + starting_time: Optional[float] = None, + rate: Optional[float] = None, + timestamps: Optional[np.ndarray] = None, +) -> ndx_microscopy.MicroscopyResponseSeries: + series_name = name or name_generator("MicroscopyResponseSeries") + + number_of_frames = 100 + number_of_rois = len(table_region.data) + series_data = data if data is not None else np.ones(shape=(number_of_frames, number_of_rois)) + + if timestamps is None: + series_starting_time = starting_time or 0.0 + series_rate = rate or 10.0 + series_timestamps = None + else: + if starting_time is not None or rate is not None: + warnings.warn( + message=( + "Timestamps were provided in addition to either rate or starting_time! " + "Please specify only timestamps, or both starting_time and rate. Timestamps will take precedence." + ), + stacklevel=2, + ) + + series_starting_time = None + series_rate = None + series_timestamps = timestamps + + microscopy_response_series = ndx_microscopy.MicroscopyResponseSeries( + name=series_name, + description=description, + table_region=table_region, + data=series_data, + unit=unit, + conversion=conversion, + offset=offset, + starting_time=series_starting_time, + rate=series_rate, + timestamps=series_timestamps, + ) + + return microscopy_response_series + + +def mock_MicroscopyResponseSeriesContainer( + *, + microscopy_response_series: List[ndx_microscopy.MicroscopyResponseSeries], + name: Optional[str] = None, +) -> ndx_microscopy.MicroscopyResponseSeriesContainer: + container_name = name or name_generator("MicroscopyResponseSeriesContainer") + + microscopy_response_series_container = ndx_microscopy.MicroscopyResponseSeriesContainer( + name=container_name, microscopy_response_series=microscopy_response_series + ) + + return microscopy_response_series_container + + def mock_VariableDepthMultiChannelMicroscopyVolume( *, microscope: ndx_microscopy.Microscope, @@ -370,7 +437,7 @@ def mock_VariableDepthMultiChannelMicroscopyVolume( 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.", + description: str = "A mock instance of a MultiChannelMicroscopyVolume type to be used for rapid testing.", data: Optional[np.ndarray] = None, depth_per_frame_in_um: Optional[np.ndarray] = None, unit: str = "n.a.", diff --git a/src/pynwb/tests/test_constructors.py b/src/pynwb/tests/test_constructors.py index f5c29aa..bcbb3cf 100644 --- a/src/pynwb/tests/test_constructors.py +++ b/src/pynwb/tests/test_constructors.py @@ -1,112 +1,134 @@ """Test in-memory Python API constructors for the ndx-microscopy extension.""" +import pynwb.testing.mock.ophys import pytest +import ndx_microscopy.testing import pynwb -from ndx_microscopy.testing import ( - mock_Microscope, - mock_MicroscopyLightSource, - mock_MicroscopyOpticalChannel, - mock_MicroscopyPlaneSegmentation, - mock_MicroscopySegmentations, - mock_MultiChannelMicroscopyVolume, - mock_PlanarImagingSpace, - mock_PlanarMicroscopySeries, - mock_VariableDepthMicroscopySeries, - mock_VariableDepthMultiChannelMicroscopyVolume, - mock_VolumetricImagingSpace, - mock_VolumetricMicroscopySeries, -) def test_constructor_microscope(): - mock_Microscope() + ndx_microscopy.testing.mock_Microscope() def test_constructor_light_source(): - mock_MicroscopyLightSource() + ndx_microscopy.testing.mock_MicroscopyLightSource() def test_constructor_microscopy_optical_channel(): - mock_MicroscopyOpticalChannel() + ndx_microscopy.testing.mock_MicroscopyOpticalChannel() def test_constructor_planar_image_space(): - microscope = mock_Microscope() + microscope = ndx_microscopy.testing.mock_Microscope() - mock_PlanarImagingSpace(microscope=microscope) + ndx_microscopy.testing.mock_PlanarImagingSpace(microscope=microscope) def test_constructor_volumetric_image_space(): - microscope = mock_Microscope() + microscope = ndx_microscopy.testing.mock_Microscope() - mock_VolumetricImagingSpace(microscope=microscope) + ndx_microscopy.testing.mock_VolumetricImagingSpace(microscope=microscope) def test_constructor_microscopy_segmentations(): - mock_MicroscopySegmentations() + ndx_microscopy.testing.mock_MicroscopySegmentations() def test_constructor_microscopy_plane_segmentation(): - microscope = mock_Microscope() - imaging_space = mock_PlanarImagingSpace(microscope=microscope) + microscope = ndx_microscopy.testing.mock_Microscope() + imaging_space = ndx_microscopy.testing.mock_PlanarImagingSpace(microscope=microscope) - mock_MicroscopyPlaneSegmentation(imaging_space=imaging_space) + ndx_microscopy.testing.mock_MicroscopyPlaneSegmentation(imaging_space=imaging_space) def test_constructor_microscopy_image_segmentation_with_plane_segmentation(): - microscope = mock_Microscope() - imaging_space = mock_PlanarImagingSpace(microscope=microscope) + microscope = ndx_microscopy.testing.mock_Microscope() + imaging_space = ndx_microscopy.testing.mock_PlanarImagingSpace(microscope=microscope) - plane_segmentation_1 = mock_MicroscopyPlaneSegmentation( + plane_segmentation_1 = ndx_microscopy.testing.mock_MicroscopyPlaneSegmentation( imaging_space=imaging_space, name="MicroscopyPlaneSegmentation1" ) - plane_segmentation_2 = mock_MicroscopyPlaneSegmentation( + plane_segmentation_2 = ndx_microscopy.testing.mock_MicroscopyPlaneSegmentation( imaging_space=imaging_space, name="MicroscopyPlaneSegmentation2" ) microscopy_plane_segmentations = [plane_segmentation_1, plane_segmentation_2] - mock_MicroscopySegmentations(microscopy_plane_segmentations=microscopy_plane_segmentations) + ndx_microscopy.testing.mock_MicroscopySegmentations(microscopy_plane_segmentations=microscopy_plane_segmentations) def test_constructor_planar_microscopy_series(): - microscope = mock_Microscope() - light_source = mock_MicroscopyLightSource() - imaging_space = mock_PlanarImagingSpace(microscope=microscope) - optical_channel = mock_MicroscopyOpticalChannel() + microscope = ndx_microscopy.testing.mock_Microscope() + light_source = ndx_microscopy.testing.mock_MicroscopyLightSource() + imaging_space = ndx_microscopy.testing.mock_PlanarImagingSpace(microscope=microscope) + optical_channel = ndx_microscopy.testing.mock_MicroscopyOpticalChannel() - mock_PlanarMicroscopySeries( + ndx_microscopy.testing.mock_PlanarMicroscopySeries( microscope=microscope, light_source=light_source, imaging_space=imaging_space, optical_channel=optical_channel ) def test_constructor_variable_depth_microscopy_series(): - microscope = mock_Microscope() - light_source = mock_MicroscopyLightSource() - imaging_space = mock_PlanarImagingSpace(microscope=microscope) - optical_channel = mock_MicroscopyOpticalChannel() + microscope = ndx_microscopy.testing.mock_Microscope() + light_source = ndx_microscopy.testing.mock_MicroscopyLightSource() + imaging_space = ndx_microscopy.testing.mock_PlanarImagingSpace(microscope=microscope) + optical_channel = ndx_microscopy.testing.mock_MicroscopyOpticalChannel() - mock_VariableDepthMicroscopySeries( + ndx_microscopy.testing.mock_VariableDepthMicroscopySeries( microscope=microscope, light_source=light_source, imaging_space=imaging_space, optical_channel=optical_channel ) def test_constructor_volumetric_microscopy_series(): - microscope = mock_Microscope() - light_source = mock_MicroscopyLightSource() - imaging_space = mock_VolumetricImagingSpace(microscope=microscope) - optical_channel = mock_MicroscopyOpticalChannel() + microscope = ndx_microscopy.testing.mock_Microscope() + light_source = ndx_microscopy.testing.mock_MicroscopyLightSource() + imaging_space = ndx_microscopy.testing.mock_VolumetricImagingSpace(microscope=microscope) + optical_channel = ndx_microscopy.testing.mock_MicroscopyOpticalChannel() - mock_VolumetricMicroscopySeries( + ndx_microscopy.testing.mock_VolumetricMicroscopySeries( microscope=microscope, light_source=light_source, imaging_space=imaging_space, optical_channel=optical_channel ) +def test_constructor_microscopy_response_series(): + number_of_rois = 10 + + plane_segmentation = pynwb.testing.mock.ophys.mock_PlaneSegmentation() + + table_region = pynwb.core.DynamicTableRegion( + name="table_region", + description="", + data=[x for x in range(number_of_rois)], + table=plane_segmentation, + ) + + ndx_microscopy.testing.mock_MicroscopyResponseSeries(table_region=table_region) + + +def test_constructor_microscopy_response_series_container(): + number_of_rois = 10 + + plane_segmentation = pynwb.testing.mock.ophys.mock_PlaneSegmentation() + + table_region = pynwb.core.DynamicTableRegion( + name="table_region", + description="", + data=[x for x in range(number_of_rois)], + table=plane_segmentation, + ) + + microscopy_response_series = ndx_microscopy.testing.mock_MicroscopyResponseSeries(table_region=table_region) + + ndx_microscopy.testing.mock_MicroscopyResponseSeriesContainer( + microscopy_response_series=[microscopy_response_series] + ) + + def test_constructor_multi_channel_microscopy_volume(): - microscope = mock_Microscope() - imaging_space = mock_VolumetricImagingSpace(microscope=microscope) - light_sources = [mock_MicroscopyLightSource()] - optical_channels = [mock_MicroscopyOpticalChannel()] + microscope = ndx_microscopy.testing.mock_Microscope() + imaging_space = ndx_microscopy.testing.mock_VolumetricImagingSpace(microscope=microscope) + light_sources = [ndx_microscopy.testing.mock_MicroscopyLightSource()] + optical_channels = [ndx_microscopy.testing.mock_MicroscopyOpticalChannel()] light_sources_used_by_volume = pynwb.base.VectorData( name="light_sources", description="Light sources used by this MultiChannelVolume.", data=light_sources @@ -119,7 +141,7 @@ def test_constructor_multi_channel_microscopy_volume(): ), data=optical_channels, ) - mock_MultiChannelMicroscopyVolume( + ndx_microscopy.testing.mock_MultiChannelMicroscopyVolume( microscope=microscope, imaging_space=imaging_space, light_sources=light_sources_used_by_volume, @@ -128,10 +150,10 @@ def test_constructor_multi_channel_microscopy_volume(): def test_constructor_variable_depth_multi_channel_microscopy_volume(): - microscope = mock_Microscope() - imaging_space = mock_VolumetricImagingSpace(microscope=microscope) - light_sources = [mock_MicroscopyLightSource()] - optical_channels = [mock_MicroscopyOpticalChannel()] + microscope = ndx_microscopy.testing.mock_Microscope() + imaging_space = ndx_microscopy.testing.mock_VolumetricImagingSpace(microscope=microscope) + light_sources = [ndx_microscopy.testing.mock_MicroscopyLightSource()] + optical_channels = [ndx_microscopy.testing.mock_MicroscopyOpticalChannel()] light_sources_used_by_volume = pynwb.base.VectorData( name="light_sources", description="Light sources used by this MultiChannelVolume.", data=light_sources @@ -144,7 +166,7 @@ def test_constructor_variable_depth_multi_channel_microscopy_volume(): ), data=optical_channels, ) - mock_VariableDepthMultiChannelMicroscopyVolume( + ndx_microscopy.testing.mock_VariableDepthMultiChannelMicroscopyVolume( microscope=microscope, imaging_space=imaging_space, light_sources=light_sources_used_by_volume, diff --git a/src/pynwb/tests/test_roundtrip.py b/src/pynwb/tests/test_roundtrip.py index 4d9f830..80d6c54 100644 --- a/src/pynwb/tests/test_roundtrip.py +++ b/src/pynwb/tests/test_roundtrip.py @@ -4,20 +4,8 @@ from pynwb.testing import TestCase as pynwb_TestCase from pynwb.testing.mock.file import mock_NWBFile +import ndx_microscopy.testing import pynwb -from ndx_microscopy.testing import ( - mock_Microscope, - mock_MicroscopyLightSource, - mock_MicroscopyOpticalChannel, - mock_MicroscopyPlaneSegmentation, - mock_MicroscopySegmentations, - mock_MultiChannelMicroscopyVolume, - mock_PlanarImagingSpace, - mock_PlanarMicroscopySeries, - mock_VariableDepthMicroscopySeries, - mock_VolumetricImagingSpace, - mock_VolumetricMicroscopySeries, -) class TestPlanarMicroscopySeriesSimpleRoundtrip(pynwb_TestCase): @@ -32,19 +20,19 @@ def tearDown(self): def test_roundtrip(self): nwbfile = mock_NWBFile() - microscope = mock_Microscope(name="Microscope") + microscope = ndx_microscopy.testing.mock_Microscope(name="Microscope") nwbfile.add_device(devices=microscope) - light_source = mock_MicroscopyLightSource(name="MicroscopyLightSource") + light_source = ndx_microscopy.testing.mock_MicroscopyLightSource(name="MicroscopyLightSource") nwbfile.add_device(devices=light_source) - imaging_space = mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) + imaging_space = ndx_microscopy.testing.mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_spacec() - optical_channel = mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") + optical_channel = ndx_microscopy.testing.mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") nwbfile.add_lab_meta_data(lab_meta_data=optical_channel) - planar_microscopy_series = mock_PlanarMicroscopySeries( + planar_microscopy_series = ndx_microscopy.testing.mock_PlanarMicroscopySeries( name="PlanarMicroscopySeries", microscope=microscope, light_source=light_source, @@ -80,19 +68,21 @@ def tearDown(self): def test_roundtrip(self): nwbfile = mock_NWBFile() - microscope = mock_Microscope(name="Microscope") + microscope = ndx_microscopy.testing.mock_Microscope(name="Microscope") nwbfile.add_device(devices=microscope) - light_source = mock_MicroscopyLightSource(name="MicroscopyLightSource") + light_source = ndx_microscopy.testing.mock_MicroscopyLightSource(name="MicroscopyLightSource") nwbfile.add_device(devices=light_source) - imaging_space = mock_VolumetricImagingSpace(name="VolumetricImagingSpace", microscope=microscope) + imaging_space = ndx_microscopy.testing.mock_VolumetricImagingSpace( + name="VolumetricImagingSpace", microscope=microscope + ) nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_spacec() - optical_channel = mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") + optical_channel = ndx_microscopy.testing.mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") nwbfile.add_lab_meta_data(lab_meta_data=optical_channel) - volumetric_microscopy_series = mock_VolumetricMicroscopySeries( + volumetric_microscopy_series = ndx_microscopy.testing.mock_VolumetricMicroscopySeries( name="VolumetricMicroscopySeries", microscope=microscope, light_source=light_source, @@ -130,19 +120,19 @@ def tearDown(self): def test_roundtrip(self): nwbfile = mock_NWBFile() - microscope = mock_Microscope(name="Microscope") + microscope = ndx_microscopy.testing.mock_Microscope(name="Microscope") nwbfile.add_device(devices=microscope) - light_source = mock_MicroscopyLightSource(name="MicroscopyLightSource") + light_source = ndx_microscopy.testing.mock_MicroscopyLightSource(name="MicroscopyLightSource") nwbfile.add_device(devices=light_source) - imaging_space = mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) + imaging_space = ndx_microscopy.testing.mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() - optical_channel = mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") + optical_channel = ndx_microscopy.testing.mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") nwbfile.add_lab_meta_data(lab_meta_data=optical_channel) - variable_depth_microscopy_series = mock_VariableDepthMicroscopySeries( + variable_depth_microscopy_series = ndx_microscopy.testing.mock_VariableDepthMicroscopySeries( name="VariableDepthMicroscopySeries", microscope=microscope, light_source=light_source, @@ -180,19 +170,21 @@ def tearDown(self): def test_roundtrip(self): nwbfile = mock_NWBFile() - microscope = mock_Microscope(name="Microscope") + microscope = ndx_microscopy.testing.mock_Microscope(name="Microscope") nwbfile.add_device(devices=microscope) - imaging_space = mock_VolumetricImagingSpace(name="VolumetricImagingSpace", microscope=microscope) + imaging_space = ndx_microscopy.testing.mock_VolumetricImagingSpace( + name="VolumetricImagingSpace", microscope=microscope + ) nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() light_sources = list() - light_source_0 = mock_MicroscopyLightSource(name="LightSource") + light_source_0 = ndx_microscopy.testing.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") + optical_channel_0 = ndx_microscopy.testing.mock_MicroscopyOpticalChannel(name="MicroscopyOpticalChannel") nwbfile.add_lab_meta_data(lab_meta_data=optical_channel_0) optical_channels.append(optical_channel_0) @@ -209,7 +201,7 @@ def test_roundtrip(self): ), data=optical_channels, ) - multi_channel_microscopy_volume = mock_MultiChannelMicroscopyVolume( + multi_channel_microscopy_volume = ndx_microscopy.testing.mock_MultiChannelMicroscopyVolume( name="MultiChannelMicroscopyVolume", microscope=microscope, imaging_space=imaging_space, @@ -247,25 +239,25 @@ def tearDown(self): def test_roundtrip(self): nwbfile = mock_NWBFile() - microscope = mock_Microscope(name="Microscope") + microscope = ndx_microscopy.testing.mock_Microscope(name="Microscope") nwbfile.add_device(devices=microscope) - imaging_space = mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) + imaging_space = ndx_microscopy.testing.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( + plane_segmentation_1 = ndx_microscopy.testing.mock_MicroscopyPlaneSegmentation( imaging_space=imaging_space, name="MicroscopyPlaneSegmentation1" ) - plane_segmentation_2 = mock_MicroscopyPlaneSegmentation( + plane_segmentation_2 = ndx_microscopy.testing.mock_MicroscopyPlaneSegmentation( imaging_space=imaging_space, name="MicroscopyPlaneSegmentation2" ) microscopy_plane_segmentations = [plane_segmentation_1, plane_segmentation_2] - segmentations = mock_MicroscopySegmentations( + segmentations = ndx_microscopy.testing.mock_MicroscopySegmentations( name="MicroscopySegmentations", microscopy_plane_segmentations=microscopy_plane_segmentations ) - processing_module = nwbfile.create_processing_module(name="ophys", description="") - processing_module.add(segmentations) + ophys_module = nwbfile.create_processing_module(name="ophys", description="") + ophys_module.add(segmentations) with pynwb.NWBHDF5IO(path=self.nwbfile_path, mode="w") as io: io.write(nwbfile) @@ -280,5 +272,68 @@ def test_roundtrip(self): self.assertContainerEqual(segmentations, read_nwbfile.processing["ophys"]["MicroscopySegmentations"]) +class TestMicroscopyResponseSeriesSimpleRoundtrip(pynwb_TestCase): + """Simple roundtrip test for MicroscopyResponseSeries.""" + + def setUp(self): + self.nwbfile_path = "test_microscopy_response_series_roundtrip.nwb" + + def tearDown(self): + pynwb.testing.remove_test_file(self.nwbfile_path) + + def test_roundtrip(self): + nwbfile = mock_NWBFile() + + microscope = ndx_microscopy.testing.mock_Microscope(name="Microscope") + nwbfile.add_device(devices=microscope) + + imaging_space = ndx_microscopy.testing.mock_PlanarImagingSpace(name="PlanarImagingSpace", microscope=microscope) + nwbfile.add_lab_meta_data(lab_meta_data=imaging_space) # Would prefer .add_imaging_space() + + microscopy_plane_segmentations = ndx_microscopy.testing.mock_MicroscopyPlaneSegmentation( + name="MicroscopyPlaneSegmentation", imaging_space=imaging_space + ) + + segmentations = ndx_microscopy.testing.mock_MicroscopySegmentations( + name="MicroscopySegmentations", microscopy_plane_segmentations=[microscopy_plane_segmentations] + ) + ophys_module = nwbfile.create_processing_module(name="ophys", description="") + ophys_module.add(segmentations) + + number_of_rois = 10 + plane_segmentation_region = pynwb.ophys.DynamicTableRegion( + name="table_region", # Name must be exactly this + description="", + data=[x for x in range(number_of_rois)], + table=microscopy_plane_segmentations, + ) + microscopy_response_series = ndx_microscopy.testing.mock_MicroscopyResponseSeries( + name="MicroscopyResponseSeries", + table_region=plane_segmentation_region, + ) + + microscopy_response_series_container = ndx_microscopy.MicroscopyResponseSeriesContainer( + name="MicroscopyResponseSeriesContainer", microscopy_response_series=[microscopy_response_series] + ) + ophys_module.add(microscopy_response_series_container) + + 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"]) + + self.assertContainerEqual( + microscopy_response_series_container, + read_nwbfile.processing["ophys"]["MicroscopyResponseSeriesContainer"], + ) + + if __name__ == "__main__": pytest.main() # Required since not a typical package structure