Skip to content

Commit 3eeb60e

Browse files
Refine imaging modality attributes and enhance testing coverage for various scanning methods
1 parent 6ebd2e1 commit 3eeb60e

File tree

5 files changed

+243
-3
lines changed

5 files changed

+243
-3
lines changed

spec/ndx-microscopy.extensions.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ groups:
6868
attributes:
6969
- name: scan_direction
7070
dtype: text
71-
doc: Direction of line scanning (horizontal, vertical, or diagonal). # specify fast direction vs slow direction
71+
doc: Direction of line scanning (horizontal, vertical, or diagonal).
7272
required: false
7373
- name: line_rate
7474
dtype: float64
@@ -138,7 +138,7 @@ groups:
138138
doc: Random access scanning method for targeted, high-speed imaging of specific regions.
139139
attributes:
140140
- name: max_scan_points
141-
dtype: uint32
141+
dtype: numeric
142142
doc: Maximum number of points that can be scanned in a single frame.
143143
required: false
144144
- name: dwell_time

src/pynwb/ndx_microscopy/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
3535
)
3636

3737
Microscope = get_class("Microscope", extension_name)
38+
ImagingModality = get_class("ImagingModality", extension_name)
39+
LineScanning = get_class("LineScanning", extension_name)
40+
RasterScanning = get_class("RasterScanning", extension_name)
41+
ResonantScanning = get_class("ResonantScanning", extension_name)
42+
TemporalFocusing = get_class("TemporalFocusing", extension_name)
43+
LightSheet = get_class("LightSheet", extension_name)
44+
RandomAccessScanning = get_class("RandomAccessScanning", extension_name)
45+
3846
SummaryImage = get_class("SummaryImage", extension_name)
3947
PlanarImagingSpace = get_class("PlanarImagingSpace", extension_name)
4048
VolumetricImagingSpace = get_class("VolumetricImagingSpace", extension_name)
@@ -47,14 +55,20 @@
4755
MicroscopyResponseSeries = get_class("MicroscopyResponseSeries", extension_name)
4856
MicroscopyResponseSeriesContainer = get_class("MicroscopyResponseSeriesContainer", extension_name)
4957

50-
5158
__all__ = [
5259
"OpticalFilter",
5360
"ExcitationSource",
5461
"Indicator",
5562
"Photodetector",
5663
"DichroicMirror",
5764
"Microscope",
65+
"ImagingModality",
66+
"LineScanning",
67+
"RasterScanning",
68+
"ResonantScanning",
69+
"TemporalFocusing",
70+
"LightSheet",
71+
"RandomAccessScanning",
5872
"ExcitationLightPath",
5973
"EmissionLightPath",
6074
"ImagingSpace",

src/pynwb/ndx_microscopy/testing/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
mock_MultiPlaneMicroscopyContainer,
1414
mock_VolumetricImagingSpace,
1515
mock_VolumetricMicroscopySeries,
16+
mock_ImagingModality,
17+
mock_LineScanning,
18+
mock_RasterScanning,
19+
mock_ResonantScanning,
20+
mock_TemporalFocusing,
21+
mock_LightSheet,
22+
mock_RandomAccessScanning,
1623
)
1724

1825
__all__ = [
@@ -30,4 +37,11 @@
3037
"mock_VolumetricMicroscopySeries",
3138
"mock_MicroscopyResponseSeries",
3239
"mock_MicroscopyResponseSeriesContainer",
40+
"mock_ImagingModality",
41+
"mock_LineScanning",
42+
"mock_RasterScanning",
43+
"mock_ResonantScanning",
44+
"mock_TemporalFocusing",
45+
"mock_LightSheet",
46+
"mock_RandomAccessScanning",
3347
]

src/pynwb/ndx_microscopy/testing/_mock.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,127 @@ def mock_EmissionLightPath(
7070
return emission_light_path
7171

7272

73+
def mock_ImagingModality(
74+
*,
75+
name: Optional[str] = None,
76+
description: str = "A mock instance of an ImagingModality type to be used for rapid testing.",
77+
) -> ndx_microscopy.ImagingModality:
78+
"""Base class for describing microscopy imaging modalities."""
79+
imaging_modality = ndx_microscopy.ImagingModality(
80+
name=name or name_generator("ImagingModality"),
81+
description=description,
82+
)
83+
return imaging_modality
84+
85+
86+
def mock_LineScanning(
87+
*,
88+
name: Optional[str] = None,
89+
description: str = "A mock instance of a LineScanning type to be used for rapid testing.",
90+
scan_direction: Optional[str] = "horizontal",
91+
line_rate: Optional[float] = 500.0,
92+
dwell_time: Optional[float] = 0.001,
93+
) -> ndx_microscopy.LineScanning:
94+
"""Line scanning method used in microscopy, particularly common in two-photon imaging."""
95+
line_scanning = ndx_microscopy.LineScanning(
96+
name=name or name_generator("LineScanning"),
97+
description=description,
98+
scan_direction=scan_direction,
99+
line_rate=line_rate,
100+
dwell_time=dwell_time,
101+
)
102+
return line_scanning
103+
104+
105+
def mock_RasterScanning(
106+
*,
107+
name: Optional[str] = None,
108+
description: str = "A mock instance of a RasterScanning type to be used for rapid testing.",
109+
scan_pattern: Optional[str] = "bidirectional",
110+
dwell_time: Optional[float] = 0.0005,
111+
) -> ndx_microscopy.RasterScanning:
112+
"""Raster scanning method with grid-like point-by-point or line-by-line acquisition."""
113+
raster_scanning = ndx_microscopy.RasterScanning(
114+
name=name or name_generator("RasterScanning"),
115+
description=description,
116+
scan_pattern=scan_pattern,
117+
dwell_time=dwell_time,
118+
)
119+
return raster_scanning
120+
121+
122+
def mock_ResonantScanning(
123+
*,
124+
name: Optional[str] = None,
125+
description: str = "A mock instance of a ResonantScanning type to be used for rapid testing.",
126+
resonant_frequency: Optional[float] = 8000.0,
127+
resonant_amplitude: Optional[float] = 1.5,
128+
) -> ndx_microscopy.ResonantScanning:
129+
"""Resonant scanning method using a rapidly oscillating mirror for high-speed imaging."""
130+
resonant_scanning = ndx_microscopy.ResonantScanning(
131+
name=name or name_generator("ResonantScanning"),
132+
description=description,
133+
resonant_frequency=resonant_frequency,
134+
resonant_amplitude=resonant_amplitude,
135+
)
136+
return resonant_scanning
137+
138+
139+
def mock_TemporalFocusing(
140+
*,
141+
name: Optional[str] = None,
142+
description: str = "A mock instance of a TemporalFocusing type to be used for rapid testing.",
143+
lateral_point_spread_function_in_um: str = "0.5 ± 0.1",
144+
axial_point_spread_function_in_um: str = "2.0 ± 0.3",
145+
pulse_duration: Optional[float] = 0.0000001, # 100 nanoseconds
146+
) -> ndx_microscopy.TemporalFocusing:
147+
"""Temporal focusing scanning method for depth-resolved imaging."""
148+
temporal_focusing = ndx_microscopy.TemporalFocusing(
149+
name=name or name_generator("TemporalFocusing"),
150+
description=description,
151+
lateral_point_spread_function_in_um=lateral_point_spread_function_in_um,
152+
axial_point_spread_function_in_um=axial_point_spread_function_in_um,
153+
pulse_duration=pulse_duration,
154+
)
155+
return temporal_focusing
156+
157+
158+
def mock_LightSheet(
159+
*,
160+
name: Optional[str] = None,
161+
description: str = "A mock instance of a LightSheet type to be used for rapid testing.",
162+
sheet_thickness_in_um: Optional[float] = 5.0,
163+
illumination_angle: Optional[float] = 45.0,
164+
) -> ndx_microscopy.LightSheet:
165+
"""Light sheet method."""
166+
light_sheet = ndx_microscopy.LightSheet(
167+
name=name or name_generator("LightSheet"),
168+
description=description,
169+
sheet_thickness_in_um=sheet_thickness_in_um,
170+
illumination_angle=illumination_angle,
171+
)
172+
return light_sheet
173+
174+
175+
def mock_RandomAccessScanning(
176+
*,
177+
name: Optional[str] = None,
178+
description: str = "A mock instance of a RandomAccessScanning type to be used for rapid testing.",
179+
max_scan_points: Optional[int] = 1000,
180+
dwell_time: Optional[float] = 0.0001,
181+
scanning_pattern: Optional[str] = "spiral",
182+
) -> ndx_microscopy.RandomAccessScanning:
183+
"""Random access scanning method for targeted, high-speed imaging of specific regions."""
184+
random_access_scanning = ndx_microscopy.RandomAccessScanning(
185+
name=name or name_generator("RandomAccessScanning"),
186+
description=description,
187+
max_scan_points=max_scan_points,
188+
dwell_time=dwell_time,
189+
scanning_pattern=scanning_pattern,
190+
)
191+
return random_access_scanning
192+
193+
73194
def mock_PlanarImagingSpace(
74195
*,
75196
name: Optional[str] = None,
@@ -79,6 +200,7 @@ def mock_PlanarImagingSpace(
79200
location: str = "The location targeted by the mock imaging space.",
80201
reference_frame: str = "The reference frame of the mock planar imaging space.",
81202
orientation: str = "The orientation of the mock planar imaging space.",
203+
imaging_modality: ndx_microscopy.ImagingModality = None,
82204
) -> ndx_microscopy.PlanarImagingSpace:
83205
planar_imaging_space = ndx_microscopy.PlanarImagingSpace(
84206
name=name or name_generator("PlanarImagingSpace"),
@@ -88,6 +210,7 @@ def mock_PlanarImagingSpace(
88210
location=location,
89211
reference_frame=reference_frame,
90212
orientation=orientation,
213+
imaging_modality=imaging_modality or mock_ImagingModality(),
91214
)
92215
return planar_imaging_space
93216

@@ -101,6 +224,7 @@ def mock_VolumetricImagingSpace(
101224
location: str = "The location targeted by the mock imaging space.",
102225
reference_frame: str = "The reference frame of the mock volumetric imaging space.",
103226
orientation: str = "The orientation of the mock planar imaging space.",
227+
imaging_modality: ndx_microscopy.ImagingModality = None,
104228
) -> ndx_microscopy.VolumetricImagingSpace:
105229
volumetric_imaging_space = ndx_microscopy.VolumetricImagingSpace(
106230
name=name or name_generator("VolumetricImagingSpace"),
@@ -110,6 +234,7 @@ def mock_VolumetricImagingSpace(
110234
location=location,
111235
reference_frame=reference_frame,
112236
orientation=orientation,
237+
imaging_modality=imaging_modality or mock_ImagingModality(),
113238
)
114239
return volumetric_imaging_space
115240

src/pynwb/tests/test_constructors.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,27 @@
1717
mock_VolumetricMicroscopySeries,
1818
mock_MicroscopyResponseSeries,
1919
mock_MicroscopyResponseSeriesContainer,
20+
mock_ImagingModality,
21+
mock_LineScanning,
22+
mock_RasterScanning,
23+
mock_ResonantScanning,
24+
mock_TemporalFocusing,
25+
mock_LightSheet,
26+
mock_RandomAccessScanning,
2027
)
2128
from ndx_microscopy import (
2229
Segmentation,
2330
Segmentation2D,
2431
Segmentation3D,
2532
PlanarImagingSpace,
2633
VolumetricImagingSpace,
34+
ImagingModality,
35+
LineScanning,
36+
RasterScanning,
37+
ResonantScanning,
38+
TemporalFocusing,
39+
LightSheet,
40+
RandomAccessScanning,
2741
)
2842

2943

@@ -47,6 +61,79 @@ def test_constructor_emission_light_path():
4761
)
4862

4963

64+
def test_constructor_imaging_modality():
65+
"""Test constructor for base ImagingModality class."""
66+
imaging_modality = mock_ImagingModality()
67+
assert imaging_modality.description == "A mock instance of an ImagingModality type to be used for rapid testing."
68+
assert isinstance(imaging_modality, ImagingModality)
69+
70+
71+
def test_constructor_line_scanning():
72+
"""Test constructor for LineScanning class."""
73+
line_scanning = mock_LineScanning()
74+
assert line_scanning.description == "A mock instance of a LineScanning type to be used for rapid testing."
75+
assert line_scanning.scan_direction == "horizontal"
76+
assert line_scanning.line_rate == 500.0
77+
assert line_scanning.dwell_time == 0.001
78+
assert isinstance(line_scanning, LineScanning)
79+
assert isinstance(line_scanning, ImagingModality) # Test inheritance
80+
81+
82+
def test_constructor_raster_scanning():
83+
"""Test constructor for RasterScanning class."""
84+
raster_scanning = mock_RasterScanning()
85+
assert raster_scanning.description == "A mock instance of a RasterScanning type to be used for rapid testing."
86+
assert raster_scanning.scan_pattern == "bidirectional"
87+
assert raster_scanning.dwell_time == 0.0005
88+
assert isinstance(raster_scanning, RasterScanning)
89+
assert isinstance(raster_scanning, ImagingModality) # Test inheritance
90+
91+
92+
def test_constructor_resonant_scanning():
93+
"""Test constructor for ResonantScanning class."""
94+
resonant_scanning = mock_ResonantScanning()
95+
assert resonant_scanning.description == "A mock instance of a ResonantScanning type to be used for rapid testing."
96+
assert resonant_scanning.resonant_frequency == 8000.0
97+
assert resonant_scanning.resonant_amplitude == 1.5
98+
assert isinstance(resonant_scanning, ResonantScanning)
99+
assert isinstance(resonant_scanning, ImagingModality) # Test inheritance
100+
101+
102+
def test_constructor_temporal_focusing():
103+
"""Test constructor for TemporalFocusing class."""
104+
temporal_focusing = mock_TemporalFocusing()
105+
assert temporal_focusing.description == "A mock instance of a TemporalFocusing type to be used for rapid testing."
106+
assert temporal_focusing.lateral_point_spread_function_in_um == "0.5 ± 0.1"
107+
assert temporal_focusing.axial_point_spread_function_in_um == "2.0 ± 0.3"
108+
assert temporal_focusing.pulse_duration == 0.0000001
109+
assert isinstance(temporal_focusing, TemporalFocusing)
110+
assert isinstance(temporal_focusing, ImagingModality) # Test inheritance
111+
112+
113+
def test_constructor_light_sheet():
114+
"""Test constructor for LightSheet class."""
115+
light_sheet = mock_LightSheet()
116+
assert light_sheet.description == "A mock instance of a LightSheet type to be used for rapid testing."
117+
assert light_sheet.sheet_thickness_in_um == 5.0
118+
assert light_sheet.illumination_angle == 45.0
119+
assert isinstance(light_sheet, LightSheet)
120+
assert isinstance(light_sheet, ImagingModality) # Test inheritance
121+
122+
123+
def test_constructor_random_access_scanning():
124+
"""Test constructor for RandomAccessScanning class."""
125+
random_access_scanning = mock_RandomAccessScanning()
126+
assert (
127+
random_access_scanning.description
128+
== "A mock instance of a RandomAccessScanning type to be used for rapid testing."
129+
)
130+
assert random_access_scanning.max_scan_points == 1000
131+
assert random_access_scanning.dwell_time == 0.0001
132+
assert random_access_scanning.scanning_pattern == "spiral"
133+
assert isinstance(random_access_scanning, RandomAccessScanning)
134+
assert isinstance(random_access_scanning, ImagingModality) # Test inheritance
135+
136+
50137
def test_constructor_planar_imaging_space():
51138
planar_imaging_space = mock_PlanarImagingSpace()
52139
assert (

0 commit comments

Comments
 (0)