Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ test = [
"pytest-pretty>=1.0.0",
"pytest>=7.0.0",
"tifffile>=2023.4.12",
"xarray>=2023.1.0",
# 2023.9.0 is the first release that works with numpy 2 (np.unicode_ removal)
"xarray>=2023.9.0",
]
dev = [
{ include-group = "test" },
Expand Down
12 changes: 9 additions & 3 deletions src/nd2/_parse/_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,24 @@ def _calc_zstack_home_index(
step_um: float,
tol: float = 0.05,
) -> int:
if count <= 1:
return 0

home_range_f = abs(low_um - home_um)
home_range_i = abs(high_um - home_um)

if type_ in {2, 3}:
hrange = (inverted and home_range_i) or home_range_f
hrange = home_range_i if inverted else home_range_f
elif type_ in {6, 7}:
hrange = (inverted and home_range_f) or home_range_i
hrange = home_range_f if inverted else home_range_i
else:
return (count - 1) // 2

if step_um <= 0:
return min(int((count - 1) * hrange / abs(high_um - low_um)), count - 1)
z_range = abs(high_um - low_um)
if not z_range: # degenerate loop: every plane at the same z
return 0
return min(int((count - 1) * hrange / z_range), count - 1)
else:
return min(int(abs(ceil((hrange - tol * step_um) / step_um))), count - 1)

Expand Down
62 changes: 62 additions & 0 deletions tests/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,65 @@ def _clx_int32_field(name: str, val: int) -> bytes:
result2 = json_from_clx_lite_variant(chunk2, strip_prefix=False)
assert isinstance(result2["Data"], dict)
assert result2["Data"]["Answer"] == 42


def test_degenerate_zstack_loop():
"""A 1-plane ZStackLoop with dZStep=0 and dZLow == dZHigh must not divide by zero.

Regression test for https://github.com/tlambert03/nd2/issues/305
NIS-Elements writes such a loop for single-plane acquisitions on systems with
a piezo Z device configured.
"""
loop = _parse._parse_z_stack_loop(
{
"uiCount": 1,
"iType": 3,
"bZInverted": True,
"dZHome": 100.0,
"dZLow": 100.0,
"dZStep": 0.0,
"dZHigh": 100.0,
"wsZDevice": "NIDAQ Piezo Z (name: Piezo Z)",
}
)
assert loop.count == 1
assert loop.parameters.homeIndex == 0
assert loop.parameters.stepUm == 0.0


def test_zstack_home_index_zero_range():
"""A multi-plane loop with dZLow == dZHigh has no meaningful home index."""
assert (
_parse._calc_zstack_home_index(
False, 3, 3, home_um=100.0, low_um=100.0, high_um=100.0, step_um=0.0
)
== 0
)


@pytest.mark.parametrize("type_", [2, 3])
def test_zstack_home_index_inverted_at_high(type_: int):
"""For types 2/3 an inverted loop uses the distance from dZHigh...

...even when that distance is exactly zero.
"""
assert (
_parse._calc_zstack_home_index(
True, 5, type_, home_um=4.0, low_um=0.0, high_um=4.0, step_um=1.0
)
== 0
)


@pytest.mark.parametrize("type_", [6, 7])
def test_zstack_home_index_inverted_at_low(type_: int):
"""For types 6/7 an inverted loop uses the distance from dZLow...

...even when that distance is exactly zero.
"""
assert (
_parse._calc_zstack_home_index(
True, 5, type_, home_um=0.0, low_um=0.0, high_um=4.0, step_um=1.0
)
== 0
)
Loading