Skip to content

Commit 0c4e3f0

Browse files
Add get_FOV_size() (#65)
* add dimensions_in_pixels and dimensions_in_voxels * update mock functions * update README * update docs * update notebooks * update changelog * Add FOV size calculation methods for Planar and Volumetric Imaging Spaces * Fix pixel_size_in_um type in mock_PlanarImagingSpace function * Add tests for FOV size calculations * update README * update CHANGELOG * update docs
1 parent 9d28fcb commit 0c4e3f0

File tree

7 files changed

+204
-1
lines changed

7 files changed

+204
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Refactored segmentation classes: Sub-Issue[#56](https://github.com/catalystneuro/ndx-microscopy/issues/56)
1717
- Renamed `Segmentation2D` to `PlanarSegmentation`
1818
- Renamed `Segmentation3D` to `VolumetricSegmentation`
19+
- Added `get_FOV_size()` method to both `PlanarImagingSpace` and `VolumetricImagingSpace` classes for calculating Field of View size in micrometers Sub-Issue[#53](https://github.com/catalystneuro/ndx-microscopy/issues/53)
1920

2021
# v0.2.1 (March 28, 2025)
2122

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,10 @@ classDiagram
367367
--------------------------------------
368368
pixel_size_in_um : float64[2], optional
369369
dimensions_in_pixels : float64[2], optional
370+
--------------------------------------
371+
methods
372+
--------------------------------------
373+
get_FOV_size()
370374
}
371375
372376
class VolumetricImagingSpace {
@@ -376,6 +380,10 @@ classDiagram
376380
--------------------------------------
377381
voxel_size_in_um : float64[3], optional
378382
dimensions_in_voxels : float64[3], optional
383+
--------------------------------------
384+
methods
385+
--------------------------------------
386+
get_FOV_size()
379387
}
380388
381389
class MicroscopyRig {

docs/source/api.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,19 @@ PlanarImagingSpace
8585
:undoc-members:
8686
:show-inheritance:
8787

88+
Methods
89+
^^^^^^^
90+
.. automethod:: ndx_microscopy.PlanarImagingSpace.get_FOV_size
8891
VolumetricImagingSpace
8992
---------------------
9093
.. autoclass:: ndx_microscopy.VolumetricImagingSpace
9194
:members:
9295
:undoc-members:
9396
:show-inheritance:
97+
98+
Methods
99+
^^^^^^^
100+
.. automethod:: ndx_microscopy.VolumetricImagingSpace.get_FOV_size
94101

95102
Microscopy Series Components
96103
=========================

docs/source/release_notes.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,28 @@
33
*************
44
Release Notes
55
*************
6+
Version 0.3.0 (May, 2025)
7+
==============================
8+
9+
Deprecations and Changes
10+
-------------------------
11+
12+
13+
Features
14+
--------
15+
16+
17+
Improvements
18+
------------
19+
20+
* Added `dimensions_in_pixels` to `PlanarImagingSpace` and `dimensions_in_voxels` to `VolumetricImagingSpace`
21+
* Added `get_FOV_size()` method to both `PlanarImagingSpace` and `VolumetricImagingSpace` classes for calculating Field of View size in micrometers
22+
23+
Notes
24+
------
25+
26+
27+
628

729
Version 0.3.0 (Jun 3, 2025)
830
==============================

src/pynwb/ndx_microscopy/ndx_microscopy.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,121 @@
55

66
extension_name = "ndx-microscopy"
77

8+
# PlanarImagingSpace API functions
9+
10+
PlanarImagingSpace = get_class("PlanarImagingSpace", extension_name)
11+
12+
13+
@docval(
14+
{
15+
"name": "dimensions_in_pixels",
16+
"type": (tuple, "array_data"),
17+
"doc": "the size of the image in pixels",
18+
"default": None,
19+
},
20+
{
21+
"name": "pixel_size_in_um",
22+
"type": (tuple, "array_data"),
23+
"doc": "the size of a pixel in micrometers",
24+
"default": None,
25+
},
26+
allow_extra=True,
27+
)
28+
def get_FOV_size(self, **kwargs):
29+
"""Get the size of the Field of View (FOV) in micrometers.
30+
31+
Parameters
32+
----------
33+
dimension_in_pixels : int or tuple, optional
34+
The size of the image in pixels. If not provided, will use the imaging space's dimension.
35+
pixel_size_in_um : float or tuple, optional
36+
The size of a pixel in micrometers. If not provided, will use the imaging space's pixel size.
37+
38+
Returns
39+
-------
40+
tuple
41+
The size of the FOV in micrometers as (height, width).
42+
"""
43+
dimensions_in_pixels, pixel_size_in_um = popargs("dimensions_in_pixels", "pixel_size_in_um", kwargs)
44+
# Use instance attributes if parameters not provided
45+
if dimensions_in_pixels is None:
46+
dimensions_in_pixels = getattr(self, "dimensions_in_pixels", None)
47+
if dimensions_in_pixels is None:
48+
raise ValueError("dimensions_in_pixels must be provided either as parameter or set on the imaging space")
49+
50+
if pixel_size_in_um is None:
51+
pixel_size_in_um = getattr(self, "pixel_size_in_um", None)
52+
if pixel_size_in_um is None:
53+
raise ValueError("pixel_size_in_um must be provided either as parameter or set on the imaging space")
54+
55+
# Convert to numpy arrays for element-wise multiplication
56+
dimensions_in_pixels = np.asarray(dimensions_in_pixels)
57+
pixel_size_in_um = np.asarray(pixel_size_in_um)
58+
59+
FOV_size = dimensions_in_pixels * pixel_size_in_um
60+
return tuple(FOV_size)
61+
62+
63+
PlanarImagingSpace.get_FOV_size = get_FOV_size
64+
65+
66+
# VolumetricImagingSpace API functions
67+
68+
VolumetricImagingSpace = get_class("VolumetricImagingSpace", extension_name)
69+
70+
71+
@docval(
72+
{
73+
"name": "dimensions_in_voxels",
74+
"type": (tuple, "array_data"),
75+
"doc": "the size of the image in voxels",
76+
"default": None,
77+
},
78+
{
79+
"name": "voxel_size_in_um",
80+
"type": (tuple, "array_data"),
81+
"doc": "the size of a voxel in micrometers",
82+
"default": None,
83+
},
84+
allow_extra=True,
85+
)
86+
def get_FOV_size(self, **kwargs):
87+
"""Get the size of the Field of View (FOV) in micrometers.
88+
89+
Parameters
90+
----------
91+
dimension_in_voxels : int or tuple, optional
92+
The size of the image in voxels. If not provided, will use the imaging space's dimension.
93+
voxel_size_in_um : float or tuple, optional
94+
The size of a voxel in micrometers. If not provided, will use the imaging space's voxel size.
95+
96+
Returns
97+
-------
98+
tuple
99+
The size of the FOV in micrometers as (depth, height, width).
100+
"""
101+
dimensions_in_voxels, voxel_size_in_um = popargs("dimensions_in_voxels", "voxel_size_in_um", kwargs)
102+
# Use instance attributes if parameters not provided
103+
if dimensions_in_voxels is None:
104+
dimensions_in_voxels = getattr(self, "dimensions_in_voxels", None)
105+
if dimensions_in_voxels is None:
106+
raise ValueError("dimensions_in_voxels must be provided either as parameter or set on the imaging space")
107+
108+
if voxel_size_in_um is None:
109+
voxel_size_in_um = getattr(self, "voxel_size_in_um", None)
110+
if voxel_size_in_um is None:
111+
raise ValueError("voxel_size_in_um must be provided either as parameter or set on the imaging space")
112+
113+
# Convert to numpy arrays for element-wise multiplication
114+
dimensions_in_voxels = np.asarray(dimensions_in_voxels)
115+
voxel_size_in_um = np.asarray(voxel_size_in_um)
116+
117+
FOV_size = dimensions_in_voxels * voxel_size_in_um
118+
return tuple(FOV_size)
119+
120+
121+
VolumetricImagingSpace.get_FOV_size = get_FOV_size
122+
8123

9124
# PlanarSegmentation API functions
10125

src/pynwb/ndx_microscopy/testing/_mock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def mock_PlanarImagingSpace(
169169
name: Optional[str] = None,
170170
description: str = "A mock instance of a PlanarImagingSpace type to be used for rapid testing.",
171171
origin_coordinates: Tuple[float, float, float] = (-1.2, -0.6, -2),
172-
pixel_size_in_um: Tuple[float, float, float] = (20, 20),
172+
pixel_size_in_um: Tuple[float, float] = (20, 20),
173173
dimensions_in_pixels: Tuple[int, int] = (100, 100),
174174
location: str = "The location targeted by the mock imaging space.",
175175
reference_frame: str = "The reference frame of the mock planar imaging space.",

src/pynwb/tests/test_api_functions.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,56 @@
1818
)
1919

2020

21+
def test_planar_get_fov_size_with_parameters():
22+
"""Test PlanarImagingSpace.get_FOV_size with explicit parameters."""
23+
planar_imaging_space = mock_PlanarImagingSpace()
24+
25+
# Test with explicit parameters
26+
dimensions = (100, 200) # x, y in pixels
27+
pixel_size = (0.5, 0.5) # x, y in micrometers
28+
29+
fov_size = planar_imaging_space.get_FOV_size(dimensions_in_pixels=dimensions, pixel_size_in_um=pixel_size)
30+
31+
expected_fov = (50.0, 100.0) # 100*0.5, 200*0.5
32+
assert fov_size == expected_fov
33+
34+
35+
def test_planar_get_fov_size_with_instance_attributes():
36+
"""Test PlanarImagingSpace.get_FOV_size using instance attributes."""
37+
planar_imaging_space = mock_PlanarImagingSpace(dimensions_in_pixels=[150, 300], pixel_size_in_um=[0.2, 0.3])
38+
39+
fov_size = planar_imaging_space.get_FOV_size()
40+
41+
expected_fov = (30.0, 90.0) # 150*0.2, 300*0.3
42+
assert fov_size == expected_fov
43+
44+
45+
def test_volumetric_get_fov_size_with_parameters():
46+
"""Test VolumetricImagingSpace.get_FOV_size with explicit parameters."""
47+
volumetric_imaging_space = mock_VolumetricImagingSpace()
48+
49+
# Test with explicit parameters
50+
dimensions = (50, 100, 200) # x, y, z in voxels
51+
voxel_size = (0.5, 0.5, 1.0) # x, y, z in micrometers
52+
53+
fov_size = volumetric_imaging_space.get_FOV_size(dimensions_in_voxels=dimensions, voxel_size_in_um=voxel_size)
54+
55+
expected_fov = (25.0, 50.0, 200.0) # 50*0.5, 100*0.5, 200*1.0
56+
assert fov_size == expected_fov
57+
58+
59+
def test_volumetric_get_fov_size_with_instance_attributes():
60+
"""Test VolumetricImagingSpace.get_FOV_size using instance attributes."""
61+
volumetric_imaging_space = mock_VolumetricImagingSpace(
62+
dimensions_in_voxels=[60, 120, 240], voxel_size_in_um=[0.2, 0.3, 0.5]
63+
)
64+
65+
fov_size = volumetric_imaging_space.get_FOV_size()
66+
67+
expected_fov = (12.0, 36.0, 120.0) # 60*0.2, 120*0.3, 240*0.5
68+
assert fov_size == expected_fov
69+
70+
2171
def test_planar_pixel_to_image_conversion():
2272
"""Test conversion from pixel_mask to image_mask for 2D."""
2373
planar_imaging_space = mock_PlanarImagingSpace()

0 commit comments

Comments
 (0)