Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support falcon datasets with latest micromanager #237

Merged
merged 13 commits into from
Nov 12, 2024
8 changes: 4 additions & 4 deletions iohub/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,10 @@ def _get_position_coords(self):
if not self.reader.stage_positions:
raise ValueError("Stage positions not available.")
for idx, pos in enumerate(self.reader.stage_positions):
stage_pos = (
pos.get("XYStage") or pos.get("XY") or pos.get("XY Stage")
)
if stage_pos is None:
try:
xy_stage = pos["DefaultXYStage"]
stage_pos = pos[xy_stage]
except KeyError:
raise ValueError(
f"Stage position is not available for position {idx}"
)
Expand Down
68 changes: 40 additions & 28 deletions iohub/mm_fov.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import re
from pathlib import Path

from xarray import DataArray
Expand Down Expand Up @@ -109,42 +110,53 @@ def stage_positions(self, value):
@property
def hcs_position_labels(self):
"""Parse plate position labels generated by the HCS position generator,
e.g. 'A1-Site_0' or '1-Pos000_000', and split into row, column, and
FOV names.
e.g. 'A1-Site_0', '1-Pos000_000', or 'Pos-9-000_000', and split into
row, column, and FOV names.

Returns
-------
list[tuple[str, str, str]]
FOV name paths, e.g. ('A', '1', '0') or ('0', '1', '000000')
FOV name paths, e.g. ('A', '1', '0'), ('0', '1', '000000'), or
('0', '9', '000000').
"""
if not self.stage_positions:
raise ValueError("Stage position metadata not available.")

try:
# Look for "'A1-Site_0', 'H12-Site_1', ... " format
labels = [
pos["Label"].split("-Site_") for pos in self.stage_positions
]
return [(well[0], well[1:], fov) for well, fov in labels]
except Exception:
try:
# Look for "'1-Pos000_000', '2-Pos000_001', ... "
# and split into ('1', '000_000'), ...
labels = [
pos["Label"].split("-Pos") for pos in self.stage_positions
]
# remove underscore from FOV name, i.e. '000_000'
# collect all wells in row '0' so output is
# ('0', '1', '000000')
return [
("0", col, fov.replace("_", "")) for col, fov in labels
]
except Exception:
labels = [pos.get("Label") for pos in self.stage_positions]
raise ValueError(
"HCS position labels are in the format of "
"'A1-Site_0', 'H12-Site_1', or '1-Pos000_000' "
f"Got labels {labels}"
)
labels = [pos["Label"] for pos in self.stage_positions]
except KeyError:
raise ValueError("Stage positions do not have labels.")

# See https://chatgpt.com/share/e/670097cc-2854-8008-bd33-b54cad7c99b9
pattern = re.compile(
r"([A-Z])(\d+)-Site_(\d+)|"
r"Pos-(\d+)-(\d+)_(\d+)|"
r"(\d+)-Pos(\d+)_(\d+)"
)
row_col_fov = []
for label in labels:
if (match := re.match(pattern, label)) is not None:
if match.group(1): # "A1-Site_0" case
row_col_fov.append(
(match.group(1), match.group(2), match.group(3))
)
elif match.group(4): # "Pos-5-000_005" case
row_col_fov.append(
("0", match.group(4), match.group(5) + match.group(6))
)
elif match.group(7): # "1-Pos000_000" case
row_col_fov.append(
("0", match.group(7), match.group(8) + match.group(9))
)

if not row_col_fov:
raise ValueError(
"HCS position labels are in the format of "
"'A1-Site_0', 'H12-Site_1', or '1-Pos000_000', "
f"or 'Pos-1-000_000'. Got labels {labels}"
)

return row_col_fov

@property
def zyx_scale(self) -> tuple[float, float, float]:
Expand Down
4 changes: 3 additions & 1 deletion iohub/ndtiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@ def _get_summary_metadata(self):
"YPosition_um_Intended",
]
):
position_metadata[img_metadata["Core-XYStage"]] = (
xy_stage = img_metadata["Core-XYStage"]
position_metadata["DefaultXYStage"] = xy_stage
position_metadata[xy_stage] = (
img_metadata["XPosition_um_Intended"],
img_metadata["YPosition_um_Intended"],
)
Expand Down
2 changes: 2 additions & 0 deletions tests/mmstack/test_mmstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ def test_mmstack_metadata(ome_tiff):
assert isinstance(mmstack.micromanager_metadata, dict)
assert mmstack.micromanager_metadata["Summary"]
assert mmstack.micromanager_summary
if mmstack.stage_positions:
assert "DefaultXYStage" in mmstack.stage_positions[0]


def test_fov_axes_names(ome_tiff):
Expand Down
2 changes: 2 additions & 0 deletions tests/test_ndtiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def test_dataset_metadata(ndtiff_dataset):
assert isinstance(dataset.micromanager_metadata, dict)
assert dataset.micromanager_metadata["Summary"]
assert isinstance(dataset.micromanager_summary, dict)
if dataset.stage_positions:
assert "DefaultXYStage" in dataset.stage_positions[0]


@pytest.mark.parametrize("ndtiff_v2", ndtiff_v2_datasets)
Expand Down
Loading