Files
navigate/test/model/metadata_sources/test_bdv_metadata.py
2025-12-04 16:07:30 +08:00

186 lines
8.0 KiB
Python

import os
import numpy as np
import pytest
@pytest.mark.parametrize("ext", ["h5", "n5", "tiff"])
def test_bdv_metadata(ext):
from navigate.model.metadata_sources.bdv_metadata import BigDataViewerMetadata
md = BigDataViewerMetadata()
views = []
for _ in range(10):
views.append(
{
"x": np.random.randint(-1000, 1000),
"y": np.random.randint(-1000, 1000),
"z": np.random.randint(-1000, 1000),
"theta": np.random.randint(-1000, 1000),
"f": np.random.randint(-1000, 1000),
}
)
for view in views:
arr = md.stage_positions_to_affine_matrix(**view)
assert arr[0, 3] == view["y"] / md.dy
assert arr[1, 3] == view["x"] / md.dx
assert arr[2, 3] == view["z"] / md.dz
md.write_xml(f"test_bdv.{ext}", views)
os.remove("test_bdv.xml")
# Test defaults for shear transform.
assert md.rotate_data is False
assert md.shear_data is False
assert np.shape(md.rotate_transform) == (3, 4)
assert np.shape(md.shear_transform) == (3, 4)
# Confirm that the shear/rotation transforms are identity matrices by default
assert np.all(md.shear_transform == np.eye(3, 4))
assert np.all(md.rotate_transform == np.eye(3, 4))
# Confirm that the shear/rotation transforms are identity matrices by default
# even after calling calculate_shear_transform and calculate_rotate_transform
md.bdv_shear_transform()
md.bdv_rotate_transform()
assert np.all(md.shear_transform == np.eye(3, 4))
assert np.all(md.rotate_transform == np.eye(3, 4))
# Test that the shear/rotation transforms are correctly calculated.
md.shear_data = True
md.shear_dimension = "XZ"
md.shear_angle = 15
md.dx, md.dy, md.dz = 1, 1, 1
md.bdv_shear_transform()
assert md.shear_transform[0, 2] == np.tan(np.deg2rad(15))
md.rotate_data = True
md.rotate_angle_x = 15
md.rotate_angle_y = 0
md.rotate_angle_z = 0
md.bdv_rotate_transform()
assert md.rotate_transform[1, 1] == np.cos(np.deg2rad(15))
assert md.rotate_transform[1, 2] == -np.sin(np.deg2rad(15))
assert md.rotate_transform[2, 1] == np.sin(np.deg2rad(15))
assert md.rotate_transform[2, 2] == np.cos(np.deg2rad(15))
# Make sure we can still write the data.
md.write_xml(f"test_bdv.{ext}", views)
os.remove("test_bdv.xml")
@pytest.mark.parametrize("stack_cycling_mode", ["per_stack", "per_z"])
def test_bdv_xml_dict(dummy_model, stack_cycling_mode):
from navigate.model.metadata_sources.bdv_metadata import BigDataViewerMetadata
md = BigDataViewerMetadata()
md.configuration = dummy_model.configuration.copy()
# set shape from configuration and experiment
md.configuration["experiment"]["MicroscopeState"]["image_mode"] = "z-stack"
# timepoints, channels, z-slices, positions
for tp in [1]: #, 2, 3, 5]:
for pos in [1, 3, 5]:
for ch in [1, 2, 3]:
for z in [1, 5, 10, 20]:
md.configuration["experiment"]["MicroscopeState"]["timepoints"] = tp
# channel settings
channel_dict = md.configuration["experiment"]["MicroscopeState"]["channels"]
for i in range(1, ch+1):
channel_name = f"channel_{i}"
if channel_name not in channel_dict:
channel_dict[channel_name] = {
"is_selected": True,
"laser": "488nm",
"laser_index": 0,
"camera_exposure_time": 200.0,
"laser_power": 20.0,
"interval_time": 1.0,
"defocus": 104.0,
"filter_wheel_0": "Empty-Alignment",
"filter_position_0": 6,
"filter_wheel_1": "Empty-Alignment",
"filter_position_1": 6
}
else:
channel_dict[channel_name]["is_selected"] = True
for i in range(ch+1, 5):
channel_name = f"channel_{i}"
if channel_name in channel_dict:
channel_dict[channel_name]["is_selected"] = False
# z-stack settings
start_z_position = md.configuration["experiment"]["MicroscopeState"]["start_position"]
md.configuration["experiment"]["MicroscopeState"]["number_z_steps"] = z
md.configuration["experiment"]["MicroscopeState"]["step_size"] = 0.2
md.configuration["experiment"]["MicroscopeState"]["end_position"] = z * 0.2 + start_z_position
# multiposition settings
md.configuration["experiment"]["MicroscopeState"]["is_multiposition"] = (
True if pos > 1 else False
)
multipositions = [
["X", "Y", "Z", "THETA", "F"]
]
for p in range(pos):
position = [
np.random.uniform(0, 100), # X
np.random.uniform(0, 100), # Y
np.random.uniform(0, 100), # Z
np.random.uniform(0, 360), # THETA
np.random.uniform(0, 10), # F
]
multipositions.append(position)
md.configuration["multi_positions"] = multipositions
md.set_from_configuration_experiment()
assert md.shape_t == tp
assert md.shape_c == ch
assert md.shape_z == z
assert md.positions == pos
if pos > 1:
assert md._multiposition is True
else:
assert md._multiposition is False
# view
views = []
for p in range(1, pos):
for c in range(ch):
position = md.configuration["multi_positions"][p + 1]
for _z in range(z):
views.append(
{
"x": position[0],
"y": position[1],
"z": position[2] + start_z_position + _z * 0.2,
"theta": position[3],
"f": position[4],
}
)
xml_dict = md.bdv_xml_dict("test_file.h5", views)
assert "ImageLoader" in xml_dict["SequenceDescription"]
assert "ViewSetups" in xml_dict["SequenceDescription"]
assert "Timepoints" in xml_dict["SequenceDescription"]
assert "ViewRegistrations" in xml_dict
# verify affine values are the same for a position
view_registrations = xml_dict["ViewRegistrations"]["ViewRegistration"]
for i in range(len(view_registrations)):
# assert view id
view_id = view_registrations[i]["setup"]
if ch > 1:
assert view_id == ((i % ch) * pos + (i // ch))
# assert affine position consistency between channels
if i % ch == 0:
affine = view_registrations[i]["ViewTransform"][0]["affine"]["text"]
else:
affine_c = view_registrations[i]["ViewTransform"][0]["affine"]["text"]
assert affine == affine_c