feat: init
This commit is contained in:
0
test/tools/__init__.py
Normal file
0
test/tools/__init__.py
Normal file
109
test/tools/test_common_dict_tools.py
Normal file
109
test/tools/test_common_dict_tools.py
Normal file
@@ -0,0 +1,109 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only
|
||||
# (subject to the limitations in the disclaimer below)
|
||||
# provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Standard Library Imports
|
||||
|
||||
# Third Party Imports
|
||||
|
||||
# Local Imports
|
||||
from navigate.tools.common_dict_tools import update_stage_dict
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
def test_update_nested_dict():
|
||||
from navigate.tools.common_dict_tools import update_nested_dict
|
||||
|
||||
test_dict = {"one": 2, "three": {4: {"metastasis": False}}}
|
||||
|
||||
test_dict_updated = update_nested_dict(
|
||||
test_dict, lambda k, v: k == "metastasis", lambda x: True
|
||||
)
|
||||
|
||||
assert test_dict_updated == {"one": 2, "three": {4: {"metastasis": True}}}
|
||||
|
||||
|
||||
def create_mock_target():
|
||||
"""Create a mock target object (Model or Controller) for testing."""
|
||||
target = MagicMock()
|
||||
target.configuration = {
|
||||
"experiment": {
|
||||
"MicroscopeState": {"channels": None},
|
||||
"GalvoParameters": {},
|
||||
"CameraParameters": {},
|
||||
},
|
||||
"etl_constants": {"ETLConstants": {"low": {}, "high": {}}},
|
||||
}
|
||||
return target
|
||||
|
||||
|
||||
def create_mock_stage_target():
|
||||
"""Create a mock target object (Model or Controller) for testing."""
|
||||
target = MagicMock()
|
||||
target.configuration = {
|
||||
"experiment": {"StageParameters": {"x": None, "y": None, "z": None}}
|
||||
}
|
||||
return target
|
||||
|
||||
|
||||
class UpdateStageDictTestCase(unittest.TestCase):
|
||||
def test_update_single_axis(self):
|
||||
target = create_mock_stage_target()
|
||||
pos_dict = {"x_position": 10.0}
|
||||
update_stage_dict(target, pos_dict)
|
||||
self.assertEqual(
|
||||
target.configuration["experiment"]["StageParameters"]["x"], 10.0
|
||||
)
|
||||
|
||||
def test_update_multiple_axes(self):
|
||||
target = create_mock_stage_target()
|
||||
pos_dict = {"y_position": 5.0, "z_position": 2.5}
|
||||
update_stage_dict(target, pos_dict)
|
||||
self.assertEqual(
|
||||
target.configuration["experiment"]["StageParameters"]["y"], 5.0
|
||||
)
|
||||
self.assertEqual(
|
||||
target.configuration["experiment"]["StageParameters"]["z"], 2.5
|
||||
)
|
||||
|
||||
def test_update_invalid_axis(self):
|
||||
target = create_mock_stage_target()
|
||||
pos_dict = {"invalid_axis": 3.14}
|
||||
update_stage_dict(target, pos_dict)
|
||||
self.assertNotIn(
|
||||
"invalid_axis", target.configuration["experiment"]["StageParameters"]
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
165
test/tools/test_common_functions.py
Normal file
165
test/tools/test_common_functions.py
Normal file
@@ -0,0 +1,165 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only (subject to the
|
||||
# limitations in the disclaimer below) provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
# Standard library imports
|
||||
import os
|
||||
import unittest
|
||||
from multiprocessing import Manager
|
||||
from multiprocessing.managers import DictProxy, ListProxy
|
||||
|
||||
# Third party imports
|
||||
|
||||
# Local application imports
|
||||
import navigate.tools.common_functions as common_functions
|
||||
|
||||
|
||||
def func1():
|
||||
print("Function 1 called")
|
||||
|
||||
|
||||
def func2():
|
||||
print("Function 2 called")
|
||||
|
||||
|
||||
def func3():
|
||||
print("Function 3 called")
|
||||
|
||||
|
||||
class CombineFuncsTestCase(unittest.TestCase):
|
||||
def test_combine_funcs(self):
|
||||
combined_func = common_functions.combine_funcs(func1, func2, func3)
|
||||
# Redirect stdout to capture print statements
|
||||
import sys
|
||||
from io import StringIO
|
||||
|
||||
original_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
|
||||
combined_func()
|
||||
|
||||
# Get the printed output
|
||||
output = sys.stdout.getvalue()
|
||||
|
||||
# Reset stdout
|
||||
sys.stdout = original_stdout
|
||||
|
||||
# Check if the functions were called and printed in the expected order
|
||||
expected_output = "Function 1 called\nFunction 2 called\nFunction 3 called\n"
|
||||
self.assertEqual(output, expected_output)
|
||||
|
||||
|
||||
class BuildRefNameTestCase(unittest.TestCase):
|
||||
def test_build_ref_name(self):
|
||||
separator = "_"
|
||||
args = ["John", "Doe", 30]
|
||||
ref_name = common_functions.build_ref_name(separator, *args)
|
||||
expected_ref_name = "John_Doe_30"
|
||||
self.assertEqual(ref_name, expected_ref_name)
|
||||
|
||||
def test_build_ref_name_with_custom_separator(self):
|
||||
separator = "-"
|
||||
args = ["Jane", "Smith", 25]
|
||||
ref_name = common_functions.build_ref_name(separator, *args)
|
||||
expected_ref_name = "Jane-Smith-25"
|
||||
self.assertEqual(ref_name, expected_ref_name)
|
||||
|
||||
def test_build_ref_name_with_empty_arguments(self):
|
||||
separator = "_"
|
||||
args = []
|
||||
ref_name = common_functions.build_ref_name(separator, *args)
|
||||
self.assertEqual(ref_name, "")
|
||||
|
||||
|
||||
class CopyProxyObjectTestCase(unittest.TestCase):
|
||||
def test_copy_proxy_object_with_dict_proxy(self):
|
||||
manager = Manager()
|
||||
original_dict = manager.dict({"key1": "value1", "key2": ["value2"]})
|
||||
copied_dict = common_functions.copy_proxy_object(original_dict)
|
||||
self.assertIsNot(original_dict, copied_dict)
|
||||
assert isinstance(copied_dict, dict)
|
||||
assert isinstance(original_dict, DictProxy)
|
||||
assert copied_dict["key1"] == "value1"
|
||||
assert copied_dict["key2"] == ["value2"]
|
||||
|
||||
def test_copy_proxy_object_with_list_proxy(self):
|
||||
manager = Manager()
|
||||
original_list = manager.list(["item1", {"key": "value"}])
|
||||
copied_list = common_functions.copy_proxy_object(original_list)
|
||||
self.assertIsNot(original_list, copied_list)
|
||||
assert isinstance(copied_list, list)
|
||||
assert isinstance(original_list, ListProxy)
|
||||
assert copied_list[0] == "item1"
|
||||
assert copied_list[1] == {"key": "value"}
|
||||
|
||||
def test_copy_proxy_object_with_non_proxy_object(self):
|
||||
non_proxy_object = {"key": "value"}
|
||||
copied_object = common_functions.copy_proxy_object(non_proxy_object)
|
||||
self.assertIs(non_proxy_object, copied_object)
|
||||
assert copied_object["key"] == "value"
|
||||
assert isinstance(copied_object, dict)
|
||||
|
||||
|
||||
class TestLoadModuleFromFile(unittest.TestCase):
|
||||
def setUp(self):
|
||||
dummy_module = """
|
||||
class DummyModule:
|
||||
def __init__(self):
|
||||
self.dummy_variable = "hello"
|
||||
|
||||
def dummy_function(self):
|
||||
print(self.dummy_variable)
|
||||
|
||||
"""
|
||||
with open("dummy_module.py", "w") as f:
|
||||
f.write(dummy_module)
|
||||
|
||||
def tearDown(self):
|
||||
os.remove("dummy_module.py")
|
||||
|
||||
def test_load_module(self):
|
||||
module = common_functions.load_module_from_file(
|
||||
"DummyModule", "./dummy_module.py"
|
||||
)
|
||||
self.assertIsNotNone(module)
|
||||
self.assertTrue(hasattr(module, "DummyModule"))
|
||||
self.assertTrue(hasattr(module.DummyModule, "dummy_function"))
|
||||
|
||||
def test_invalid_module_file(self):
|
||||
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
common_functions.load_module_from_file(
|
||||
"nonexistent_module", "./dummy_module2.py"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
57
test/tools/test_decorators.py
Normal file
57
test/tools/test_decorators.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import unittest
|
||||
from time import sleep
|
||||
from src.navigate.tools.decorators import function_timer, FeatureList, AcquisitionMode, log_initialization
|
||||
|
||||
class TestFunctionTimer(unittest.TestCase):
|
||||
|
||||
@function_timer
|
||||
def sample_function(self, duration):
|
||||
sleep(duration)
|
||||
return "Completed"
|
||||
|
||||
def test_function_timer(self):
|
||||
duration = 1
|
||||
result = self.sample_function(duration)
|
||||
self.assertEqual(result, "Completed")
|
||||
|
||||
def test_function_timer_zero_duration(self):
|
||||
duration = 0
|
||||
result = self.sample_function(duration)
|
||||
self.assertEqual(result, "Completed")
|
||||
|
||||
|
||||
def sample_feature():
|
||||
return "Feature Executed"
|
||||
|
||||
class TestFeatureList(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.feature_list = FeatureList(sample_feature)
|
||||
|
||||
def test_feature_list_name(self):
|
||||
self.assertEqual(self.feature_list.feature_list_name, "Sample Feature")
|
||||
|
||||
def test_feature_list_execution(self):
|
||||
result = self.feature_list()
|
||||
self.assertEqual(result, "Feature Executed")
|
||||
|
||||
class SampleClass:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
class TestAcquisitionMode(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.acquisition_mode = AcquisitionMode(SampleClass)
|
||||
|
||||
def test_acquisition_mode_initialization(self):
|
||||
instance = self.acquisition_mode(10)
|
||||
self.assertIsInstance(instance, SampleClass)
|
||||
self.assertEqual(instance.value, 10)
|
||||
|
||||
def test_acquisition_mode_flag(self):
|
||||
self.acquisition_mode.__is_acquisition_mode = True
|
||||
self.assertTrue(self.acquisition_mode.__is_acquisition_mode)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
258
test/tools/test_file_functions.py
Normal file
258
test/tools/test_file_functions.py
Normal file
@@ -0,0 +1,258 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only (subject to the
|
||||
# limitations in the disclaimer below) provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Standard library imports
|
||||
import unittest
|
||||
import os
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
# Third party imports
|
||||
|
||||
# Local application imports
|
||||
from navigate.tools.file_functions import (
|
||||
create_save_path,
|
||||
save_yaml_file,
|
||||
delete_folder,
|
||||
load_yaml_file,
|
||||
)
|
||||
|
||||
|
||||
class CreateSavePathTestCase(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.save_root = "test_dir"
|
||||
os.mkdir(self.save_root)
|
||||
self.date_string = str(datetime.now().date())
|
||||
|
||||
def tearDown(self) -> None:
|
||||
delete_folder("test_dir")
|
||||
|
||||
def test_existing_root_directory_no_existing_cell_directories(self):
|
||||
"""Test 1: Testing with existing root directory and no existing cell
|
||||
directories."""
|
||||
saving_settings = {
|
||||
"root_directory": self.save_root,
|
||||
"user": "John Doe",
|
||||
"tissue": "Liver",
|
||||
"celltype": "Hepatocyte",
|
||||
"label": "Sample1",
|
||||
"prefix": "Cell_",
|
||||
}
|
||||
expected_save_directory = os.path.join(
|
||||
self.save_root,
|
||||
"John-Doe",
|
||||
"Liver",
|
||||
"Hepatocyte",
|
||||
"Sample1",
|
||||
self.date_string,
|
||||
"Cell_001",
|
||||
)
|
||||
save_directory = create_save_path(saving_settings)
|
||||
|
||||
# Assert that the save directory is correct
|
||||
self.assertEqual(save_directory, expected_save_directory)
|
||||
|
||||
# Assert that the save directory and cell directory are created
|
||||
self.assertTrue(os.path.exists(save_directory))
|
||||
|
||||
# Delete the created directory
|
||||
self.tearDown()
|
||||
|
||||
def test_existing_root_directory_existing_cell_directories(self):
|
||||
"""Test 2: Testing with existing root directory and existing cell
|
||||
sub-directory."""
|
||||
|
||||
os.makedirs(
|
||||
os.path.join(
|
||||
self.save_root,
|
||||
"John-Doe",
|
||||
"Liver",
|
||||
"Hepatocyte",
|
||||
"Sample1",
|
||||
self.date_string,
|
||||
"Cell_001",
|
||||
)
|
||||
)
|
||||
|
||||
saving_settings = {
|
||||
"root_directory": self.save_root,
|
||||
"user": "John Doe",
|
||||
"tissue": "Liver",
|
||||
"celltype": "Hepatocyte",
|
||||
"label": "Sample1",
|
||||
"prefix": "Cell_",
|
||||
}
|
||||
|
||||
save_directory = create_save_path(saving_settings)
|
||||
|
||||
# Assert that the save directory is correct
|
||||
self.assertEqual(
|
||||
save_directory,
|
||||
os.path.join(
|
||||
self.save_root,
|
||||
"John-Doe",
|
||||
"Liver",
|
||||
"Hepatocyte",
|
||||
"Sample1",
|
||||
self.date_string,
|
||||
"Cell_002",
|
||||
),
|
||||
)
|
||||
|
||||
# Assert that the save directory and cell directory are created
|
||||
self.assertTrue(os.path.exists(save_directory))
|
||||
|
||||
# Delete the created directory
|
||||
self.tearDown()
|
||||
|
||||
def test_spaces_in_strings(self):
|
||||
saving_settings = {
|
||||
"root_directory": self.save_root,
|
||||
"user": "John Doe",
|
||||
"tissue": "Liver Tissue",
|
||||
"celltype": "Hepatocyte Cell Type",
|
||||
"label": "Sample 1",
|
||||
"prefix": "Cell_",
|
||||
}
|
||||
|
||||
save_directory = create_save_path(saving_settings)
|
||||
expected_save_directory = os.path.join(
|
||||
self.save_root,
|
||||
"John-Doe",
|
||||
"Liver-Tissue",
|
||||
"Hepatocyte-Cell-Type",
|
||||
"Sample-1",
|
||||
self.date_string,
|
||||
"Cell_001",
|
||||
)
|
||||
|
||||
# Assert that the save directory is correct
|
||||
self.assertEqual(save_directory, expected_save_directory)
|
||||
|
||||
# Assert that the save directory and cell directory are created
|
||||
self.assertTrue(os.path.exists(save_directory))
|
||||
|
||||
# Delete the created directory
|
||||
self.tearDown()
|
||||
|
||||
|
||||
class SaveYAMLFileTestCase(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
os.mkdir("test_dir")
|
||||
self.save_root = "test_dir"
|
||||
|
||||
def tearDown(self) -> None:
|
||||
delete_folder("test_dir")
|
||||
|
||||
def test_save_yaml_file_success(self):
|
||||
content_dict = {"name": "John Doe", "age": 30, "location": "New York"}
|
||||
|
||||
result = save_yaml_file(self.save_root, content_dict)
|
||||
|
||||
# Assert that the file was saved successfully
|
||||
self.assertTrue(result)
|
||||
|
||||
# Assert that the file exists
|
||||
file_path = os.path.join(self.save_root, "experiment.yml")
|
||||
self.assertTrue(os.path.exists(file_path))
|
||||
|
||||
# Assert that the file content is correct
|
||||
with open(file_path, "r") as f:
|
||||
saved_content = json.load(f)
|
||||
self.assertEqual(saved_content, content_dict)
|
||||
|
||||
def test_save_yaml_file_failure(self):
|
||||
# Test with non-existing directory
|
||||
content_dict = {"name": "John Doe", "age": 30, "location": "New York"}
|
||||
|
||||
result = save_yaml_file(
|
||||
os.path.join(self.save_root, "does-not-exist"), content_dict
|
||||
)
|
||||
|
||||
# Assert that the file save failed
|
||||
self.assertFalse(
|
||||
result,
|
||||
"File save should have failed. Function does not "
|
||||
"create the path if it does not exist.",
|
||||
)
|
||||
|
||||
# Assert that the file does not exist
|
||||
file_path = os.path.join(self.save_root, "experiment.yml")
|
||||
self.assertFalse(os.path.exists(file_path))
|
||||
|
||||
|
||||
class TestDeleteFolder(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
os.mkdir("test_dir")
|
||||
self.save_root = "test_dir"
|
||||
|
||||
def test_delete_folder(self):
|
||||
delete_folder(self.save_root)
|
||||
self.assertFalse(os.path.exists(self.save_root))
|
||||
|
||||
|
||||
class TestLoadYamlFile(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
os.mkdir("test_dir")
|
||||
self.save_root = "test_dir"
|
||||
|
||||
def tearDown(self) -> None:
|
||||
delete_folder("test_dir")
|
||||
|
||||
def test_load_existing_yaml_file(self):
|
||||
content_dict = {"name": "John Doe", "age": 30, "location": "New York"}
|
||||
|
||||
result = save_yaml_file(self.save_root, content_dict, "test.yml")
|
||||
file_path = os.path.join(self.save_root, "test.yml")
|
||||
result = load_yaml_file(file_path)
|
||||
self.assertIsNotNone(result)
|
||||
self.assertIsInstance(result, dict)
|
||||
assert result == content_dict
|
||||
|
||||
def test_load_nonexistent_yaml_file(self):
|
||||
file_path = os.path.join(self.save_root, "test.yml")
|
||||
result = load_yaml_file(file_path)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_load_invalid_yaml_file(self):
|
||||
file_path = os.path.join(self.save_root, "test.yml")
|
||||
with open(file_path, "w") as f:
|
||||
f.write(
|
||||
"""
|
||||
{"name": "John Doe", "age": 30, "locati
|
||||
"""
|
||||
)
|
||||
result = load_yaml_file(file_path)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
108
test/tools/test_image.py
Normal file
108
test/tools/test_image.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only
|
||||
# (subject to the limitations in the disclaimer below)
|
||||
# provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Standard Library Imports
|
||||
import unittest
|
||||
|
||||
# Third-Party Imports
|
||||
import numpy as np
|
||||
from PIL import Image, ImageFont
|
||||
|
||||
# import pytest
|
||||
|
||||
# Local Imports
|
||||
from navigate.tools.image import text_array, create_arrow_image
|
||||
|
||||
|
||||
class TextArrayTestCase(unittest.TestCase):
|
||||
def test_text_array(self):
|
||||
text = "55"
|
||||
offset = (0, 0)
|
||||
|
||||
result = text_array(text, offset)
|
||||
|
||||
# Assert that the result is a numpy array
|
||||
self.assertIsInstance(result, np.ndarray)
|
||||
|
||||
# Assert that the values in the result array are either True or False
|
||||
assert result.dtype is np.dtype("bool")
|
||||
|
||||
def test_text_array_output_type(self):
|
||||
"""Confirm output is np.ndarray object"""
|
||||
text_output = text_array(text="Navigate")
|
||||
assert type(text_output) == np.ndarray
|
||||
|
||||
def test_text_array_output_height(self):
|
||||
"""Confirm that the output is approximately the correct height
|
||||
|
||||
Initially thought that the height should be ~= font_size, but this
|
||||
turned out to be much more variable than I anticipated
|
||||
"""
|
||||
text = "Navigate"
|
||||
text_output = text_array(text=text)
|
||||
font = ImageFont.load_default() # match font size in text_array()
|
||||
height = np.shape(text_output)[0]
|
||||
width = np.shape(text_output)[1]
|
||||
_, _, expected_width, expected_height = font.getbbox(text)
|
||||
assert width == expected_width
|
||||
assert height == expected_height
|
||||
|
||||
|
||||
class TestCreateArrowImage(unittest.TestCase):
|
||||
def test_create_arrow_image(self):
|
||||
xys = [(50, 50), (150, 50), (200, 100)]
|
||||
image = create_arrow_image(xys, direction="right")
|
||||
self.assertIsInstance(image, Image.Image)
|
||||
self.assertEqual(image.width, 300)
|
||||
self.assertEqual(image.height, 200)
|
||||
|
||||
xys = [(50, 50), (150, 50), (200, 100)]
|
||||
image = create_arrow_image(xys, 400, 300, direction="left")
|
||||
self.assertIsInstance(image, Image.Image)
|
||||
self.assertEqual(image.width, 400)
|
||||
self.assertEqual(image.height, 300)
|
||||
|
||||
image2 = create_arrow_image(xys, 500, 400, direction="up", image=image)
|
||||
self.assertIsInstance(image2, Image.Image)
|
||||
self.assertEqual(image2.width, 400)
|
||||
self.assertEqual(image2.height, 300)
|
||||
assert image == image2
|
||||
|
||||
image3 = create_arrow_image(xys, 500, 400, direction="down", image=image)
|
||||
self.assertIsInstance(image3, Image.Image)
|
||||
self.assertEqual(image3.width, 400)
|
||||
self.assertEqual(image3.height, 300)
|
||||
assert image == image3
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
99
test/tools/test_linear_algebra.py
Normal file
99
test/tools/test_linear_algebra.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import unittest
|
||||
import numpy as np
|
||||
from navigate.tools.linear_algebra import affine_rotation, affine_shear
|
||||
|
||||
|
||||
class TestAffineRotation(unittest.TestCase):
|
||||
def test_no_rotation(self):
|
||||
result = affine_rotation()
|
||||
expected = np.eye(4, 4)
|
||||
np.testing.assert_equal(result, expected)
|
||||
|
||||
def test_rotation_x(self):
|
||||
result = affine_rotation(x=45).ravel()
|
||||
expected = np.array(
|
||||
(
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.70710678,
|
||||
-0.70710678,
|
||||
0.0,
|
||||
0.0,
|
||||
0.70710678,
|
||||
0.70710678,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
)
|
||||
)
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
|
||||
def test_rotation_y(self):
|
||||
result = affine_rotation(y=45)
|
||||
expected = np.array(
|
||||
[
|
||||
[0.70710678, 0.0, 0.70710678, 0.0],
|
||||
[0.0, 1.0, 0.0, 0.0],
|
||||
[-0.70710678, 0.0, 0.70710678, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
]
|
||||
)
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
|
||||
def test_rotation_z(self):
|
||||
result = affine_rotation(z=45)
|
||||
expected = np.array(
|
||||
[
|
||||
[0.70710678, -0.70710678, 0.0, 0.0],
|
||||
[0.70710678, 0.70710678, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
]
|
||||
)
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
|
||||
def test_rotation_xy(self):
|
||||
result = affine_rotation(x=10, y=33)
|
||||
import numpy as np
|
||||
|
||||
expected = np.array(
|
||||
[
|
||||
[0.83867057, 0.0, 0.54463904, 0.0],
|
||||
[-0.09457558, 0.98480775, 0.14563362, 0.0],
|
||||
[-0.53636474, -0.17364818, 0.82592928, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
]
|
||||
)
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
|
||||
|
||||
class TestAffineShear(unittest.TestCase):
|
||||
def test_no_shear(self):
|
||||
# Test with zero angles or voxel sizes
|
||||
result = affine_shear(0, 0, 0)
|
||||
expected = np.eye(4, 4)
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
|
||||
def test_shear_xy(self):
|
||||
result = affine_shear(1, 1, 1, dimension="XY", angle=45)
|
||||
expected = np.eye(4, 4)
|
||||
expected[0, 1] = 1.0
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
|
||||
def test_different_pixe_sizes(self):
|
||||
result = affine_shear(167, 167, 333, dimension="XY", angle=45)
|
||||
expected = np.eye(4, 4)
|
||||
expected[0, 1] = 0.501502
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
|
||||
def test_shear_xz(self):
|
||||
# Test shear in XZ dimension
|
||||
result = affine_shear(167, 167, 333, dimension="XZ", angle=45)
|
||||
expected = np.eye(4, 4)
|
||||
expected[0, 2] = 0.501502
|
||||
np.testing.assert_array_almost_equal(result, expected)
|
||||
121
test/tools/test_main_functions.py
Normal file
121
test/tools/test_main_functions.py
Normal file
@@ -0,0 +1,121 @@
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from argparse import Namespace
|
||||
from pathlib import Path
|
||||
from src.navigate.tools.main_functions import evaluate_parser_input_arguments
|
||||
|
||||
class TestEvaluateParserInputArguments(unittest.TestCase):
|
||||
|
||||
@patch('src.navigate.tools.main_functions.get_configuration_paths')
|
||||
def test_default_arguments(self, mock_get_configuration_paths):
|
||||
# Mock the return value of get_configuration_paths
|
||||
mock_get_configuration_paths.return_value = (
|
||||
'default_config_path',
|
||||
'default_experiment_path',
|
||||
'default_waveform_constants_path',
|
||||
'default_rest_api_path',
|
||||
'default_waveform_templates_path',
|
||||
'default_gui_configuration_path',
|
||||
'default_multi_positions_path'
|
||||
)
|
||||
|
||||
args = Namespace(
|
||||
configurator=False,
|
||||
config_file=None,
|
||||
experiment_file=None,
|
||||
waveform_constants_file=None,
|
||||
rest_api_file=None,
|
||||
waveform_templates_file=None,
|
||||
gui_config_file=None,
|
||||
multi_positions_file=None,
|
||||
logging_config=None,
|
||||
)
|
||||
|
||||
result = evaluate_parser_input_arguments(args)
|
||||
expected = (
|
||||
'default_config_path',
|
||||
'default_experiment_path',
|
||||
'default_waveform_constants_path',
|
||||
'default_rest_api_path',
|
||||
'default_waveform_templates_path',
|
||||
None,
|
||||
False,
|
||||
'default_gui_configuration_path',
|
||||
'default_multi_positions_path'
|
||||
)
|
||||
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
@patch('src.navigate.tools.main_functions.get_configuration_paths')
|
||||
@patch('pathlib.Path.exists', MagicMock(return_value=True))
|
||||
def test_non_default_arguments(self, mock_get_configuration_paths):
|
||||
# Mock the return value of get_configuration_paths
|
||||
mock_get_configuration_paths.return_value = (
|
||||
'default_config_path',
|
||||
'default_experiment_path',
|
||||
'default_waveform_constants_path',
|
||||
'default_rest_api_path',
|
||||
'default_waveform_templates_path',
|
||||
'default_gui_configuration_path',
|
||||
'default_multi_positions_path'
|
||||
)
|
||||
|
||||
args = Namespace(
|
||||
configurator=True,
|
||||
config_file=Path('/path/to/config.yml'),
|
||||
experiment_file=Path('/path/to/experiment.yml'),
|
||||
waveform_constants_path=Path('/path/to/waveform_constants.yml'),
|
||||
waveform_constants_file=Path('/path/to/waveform_constants.yml'),
|
||||
rest_api_file=Path('/path/to/rest_api.yml'),
|
||||
waveform_templates_file=Path('/path/to/waveform_templates.yml'),
|
||||
gui_config_file=Path('/path/to/gui_config.yml'),
|
||||
multi_positions_file=Path('/path/to/multi_positions.yml'),
|
||||
logging_config=Path('/path/to/logging.yml')
|
||||
)
|
||||
|
||||
result = evaluate_parser_input_arguments(args)
|
||||
expected = (
|
||||
Path('/path/to/config.yml'),
|
||||
Path('/path/to/experiment.yml'),
|
||||
Path('/path/to/waveform_constants.yml'),
|
||||
Path('/path/to/rest_api.yml'),
|
||||
Path('/path/to/waveform_templates.yml'),
|
||||
Path('/path/to/logging.yml'),
|
||||
True,
|
||||
Path('/path/to/gui_config.yml'),
|
||||
Path('/path/to/multi_positions.yml')
|
||||
)
|
||||
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
@patch('src.navigate.tools.main_functions.get_configuration_paths')
|
||||
@patch('pathlib.Path.exists', MagicMock(return_value=False))
|
||||
def test_invalid_path(self, mock_get_configuration_paths):
|
||||
# Mock the return value of get_configuration_paths
|
||||
mock_get_configuration_paths.return_value = (
|
||||
'default_config_path',
|
||||
'default_experiment_path',
|
||||
'default_waveform_constants_path',
|
||||
'default_rest_api_path',
|
||||
'default_waveform_templates_path',
|
||||
'default_gui_configuration_path',
|
||||
'default_multi_positions_path'
|
||||
)
|
||||
|
||||
args = Namespace(
|
||||
configurator=True,
|
||||
config_file=Path('/invalid/path/to/config.yml'),
|
||||
experiment_file=None,
|
||||
waveform_constants_file=None,
|
||||
rest_api_file=None,
|
||||
waveform_templates_file=None,
|
||||
gui_config_file=None,
|
||||
multi_positions_file=None,
|
||||
logging_config=None
|
||||
)
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
evaluate_parser_input_arguments(args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
248
test/tools/test_multipos_table_tools.py
Normal file
248
test/tools/test_multipos_table_tools.py
Normal file
@@ -0,0 +1,248 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only (subject to the
|
||||
# limitations in the disclaimer below) provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Standard library imports
|
||||
import unittest
|
||||
import pytest
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from math import ceil
|
||||
|
||||
# Third party imports
|
||||
import numpy as np
|
||||
|
||||
# Local application imports
|
||||
from navigate.tools.multipos_table_tools import (
|
||||
update_table,
|
||||
)
|
||||
from navigate.view.main_window_content.multiposition_tab import MultiPositionTable
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pair", zip([5.6, -3.8, 0], [1, -1, 1]))
|
||||
def test_sign(pair):
|
||||
from navigate.tools.multipos_table_tools import sign
|
||||
|
||||
x, cmp_x = pair
|
||||
|
||||
assert sign(x) == cmp_x
|
||||
|
||||
|
||||
def listize(x):
|
||||
if type(x) == np.ndarray:
|
||||
return list(x)
|
||||
else:
|
||||
return [x]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x_start", listize((np.random.rand() - 0.5) * 1000))
|
||||
@pytest.mark.parametrize("x_tiles", listize(np.random.randint(0, 5)))
|
||||
@pytest.mark.parametrize("x_length", listize(np.random.rand() * 1000))
|
||||
@pytest.mark.parametrize("y_start", listize(((np.random.rand() - 0.5) * 1000)))
|
||||
@pytest.mark.parametrize("y_tiles", listize(np.random.randint(0, 5)))
|
||||
@pytest.mark.parametrize("y_length", listize(np.random.rand() * 1000))
|
||||
@pytest.mark.parametrize("z_start", listize(((np.random.rand() - 0.5) * 1000)))
|
||||
@pytest.mark.parametrize("z_tiles", listize(np.random.randint(0, 5)))
|
||||
@pytest.mark.parametrize("z_length", listize(np.random.rand() * 1000))
|
||||
@pytest.mark.parametrize("theta_start", listize(((np.random.rand() - 0.5) * 180)))
|
||||
@pytest.mark.parametrize("theta_tiles", listize(np.random.randint(0, 5)))
|
||||
@pytest.mark.parametrize("theta_length", listize((np.random.rand() * 5)))
|
||||
@pytest.mark.parametrize("f_start", listize(((np.random.rand() - 0.5) * 1000)))
|
||||
@pytest.mark.parametrize("f_tiles", listize(np.random.randint(0, 5)))
|
||||
@pytest.mark.parametrize("f_length", listize(np.random.rand() * 1000))
|
||||
@pytest.mark.parametrize("overlap", listize(np.random.rand()))
|
||||
@pytest.mark.parametrize("f_track_with_z", [True, False])
|
||||
def test_compute_tiles_from_bounding_box(
|
||||
x_start,
|
||||
x_tiles,
|
||||
x_length,
|
||||
y_start,
|
||||
y_tiles,
|
||||
y_length,
|
||||
z_start,
|
||||
z_tiles,
|
||||
z_length,
|
||||
theta_start,
|
||||
theta_tiles,
|
||||
theta_length,
|
||||
f_start,
|
||||
f_tiles,
|
||||
f_length,
|
||||
overlap,
|
||||
f_track_with_z,
|
||||
):
|
||||
from navigate.tools.multipos_table_tools import compute_tiles_from_bounding_box
|
||||
|
||||
axes, tiles = compute_tiles_from_bounding_box(
|
||||
x_start,
|
||||
x_tiles,
|
||||
x_length,
|
||||
y_start,
|
||||
y_tiles,
|
||||
y_length,
|
||||
z_start,
|
||||
z_tiles,
|
||||
z_length,
|
||||
theta_start,
|
||||
theta_tiles,
|
||||
theta_length,
|
||||
f_start,
|
||||
f_tiles,
|
||||
f_length,
|
||||
overlap,
|
||||
f_track_with_z,
|
||||
)
|
||||
|
||||
x_tiles = 1 if x_tiles <= 0 else x_tiles
|
||||
y_tiles = 1 if y_tiles <= 0 else y_tiles
|
||||
z_tiles = 1 if z_tiles <= 0 else z_tiles
|
||||
theta_tiles = 1 if theta_tiles <= 0 else theta_tiles
|
||||
f_tiles = 1 if f_tiles <= 0 else f_tiles
|
||||
|
||||
x_max = x_start + (1 - overlap) * x_length * (x_tiles - 1)
|
||||
y_max = y_start + (1 - overlap) * y_length * (y_tiles - 1)
|
||||
z_max = z_start + (1 - overlap) * z_length * (z_tiles - 1)
|
||||
theta_max = theta_start + (1 - overlap) * theta_length * (theta_tiles - 1)
|
||||
f_max = f_start + (1 - overlap) * f_length * (f_tiles - 1)
|
||||
|
||||
# check extrema
|
||||
assert tiles[0, 0] == x_start
|
||||
assert tiles[0, 1] == y_start
|
||||
assert tiles[0, 2] == z_start
|
||||
assert tiles[0, 3] == theta_start
|
||||
assert tiles[0, 4] == f_start
|
||||
assert tiles[-1, 0] == x_max
|
||||
assert tiles[-1, 1] == y_max
|
||||
assert tiles[-1, 2] == z_max
|
||||
assert tiles[-1, 3] == theta_max
|
||||
if f_track_with_z:
|
||||
assert tiles[-1, 4] <= f_max # Due to clipping. TODO: Fix
|
||||
else:
|
||||
assert tiles[-1, 4] == f_max
|
||||
|
||||
# check bounding box
|
||||
assert np.min(tiles[:, 0]) == x_start
|
||||
assert np.max(tiles[:, 0]) == x_max
|
||||
assert np.min(tiles[:, 1]) == y_start
|
||||
assert np.max(tiles[:, 1]) == y_max
|
||||
assert np.min(tiles[:, 2]) == z_start
|
||||
assert np.max(tiles[:, 2]) == z_max
|
||||
assert np.min(tiles[:, 3]) == theta_start
|
||||
assert np.max(tiles[:, 3]) == theta_max
|
||||
assert np.min(tiles[:, 4]) == f_start
|
||||
if f_track_with_z:
|
||||
assert np.max(tiles[:, 4]) <= f_max # Due to clipping. TODO: Fix
|
||||
else:
|
||||
assert np.max(tiles[:, 4]) == f_max
|
||||
|
||||
if f_track_with_z:
|
||||
# check length
|
||||
assert len(tiles) == x_tiles * y_tiles * z_tiles * theta_tiles
|
||||
else:
|
||||
assert len(tiles) == x_tiles * y_tiles * z_tiles * theta_tiles * f_tiles
|
||||
|
||||
|
||||
@pytest.mark.parametrize("dist", listize(np.random.rand(3) * 1000))
|
||||
@pytest.mark.parametrize("overlap", listize(np.random.rand(3)))
|
||||
@pytest.mark.parametrize("roi_length", listize(np.random.rand(3) * 1000))
|
||||
def test_calc_num_tiles(dist, overlap, roi_length):
|
||||
from navigate.tools.multipos_table_tools import calc_num_tiles
|
||||
|
||||
# dist = 300
|
||||
# overlap = .75
|
||||
# roi_length = 525
|
||||
expected_num_tiles = ceil(
|
||||
# abs(dist - overlap * roi_length) / abs(roi_length - overlap * roi_length)
|
||||
(dist - overlap * roi_length)
|
||||
/ (roi_length - overlap * roi_length)
|
||||
)
|
||||
|
||||
result = calc_num_tiles(dist, overlap, roi_length)
|
||||
|
||||
assert result == expected_num_tiles
|
||||
|
||||
|
||||
class UpdateTableTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.root = tk.Tk()
|
||||
self.frame = ttk.Frame()
|
||||
self.table = MultiPositionTable(parent=self.frame)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.root.destroy()
|
||||
|
||||
def test_update_table_1(self):
|
||||
pos = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
|
||||
|
||||
update_table(
|
||||
table=self.table, pos=pos, axes=["X", "Y", "Z", "THETA", "F"], append=False
|
||||
)
|
||||
|
||||
np.testing.assert_array_equal(self.table.model.df["X"], pos[:, 0])
|
||||
np.testing.assert_array_equal(self.table.model.df["Y"], pos[:, 1])
|
||||
np.testing.assert_array_equal(self.table.model.df["Z"], pos[:, 2])
|
||||
np.testing.assert_array_equal(self.table.model.df["THETA"], pos[:, 3])
|
||||
np.testing.assert_array_equal(self.table.model.df["F"], pos[:, 4])
|
||||
assert self.table.currentrow == 2
|
||||
|
||||
new_positions = np.array([[16, 17, 18, 19, 20], [21, 22, 23, 24, 25]])
|
||||
|
||||
print(self.table.model.df.shape)
|
||||
update_table(
|
||||
self.table,
|
||||
pos=new_positions,
|
||||
axes=["X", "Y", "Z", "THETA", "F"],
|
||||
append=True,
|
||||
)
|
||||
assert self.table.currentrow == 4
|
||||
np.testing.assert_array_equal(
|
||||
self.table.model.df["X"][3:,],
|
||||
new_positions[:, 0],
|
||||
)
|
||||
np.testing.assert_array_equal(
|
||||
self.table.model.df["Y"][3:,],
|
||||
new_positions[:, 1],
|
||||
)
|
||||
np.testing.assert_array_equal(
|
||||
self.table.model.df["Z"][3:,],
|
||||
new_positions[:, 2],
|
||||
)
|
||||
np.testing.assert_array_equal(
|
||||
self.table.model.df["THETA"][3:,],
|
||||
new_positions[:, 3],
|
||||
)
|
||||
np.testing.assert_array_equal(
|
||||
self.table.model.df["F"][3:,],
|
||||
new_positions[:, 4],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
91
test/tools/test_sdf.py
Normal file
91
test/tools/test_sdf.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import unittest
|
||||
import numpy as np
|
||||
from src.navigate.tools.sdf import sphere, box, ellipsoid
|
||||
|
||||
class TestSphere(unittest.TestCase):
|
||||
|
||||
def test_sphere_center(self):
|
||||
p = np.array([[0], [0], [0]])
|
||||
R = 1
|
||||
result = sphere(p, R)
|
||||
expected = -1
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_sphere_surface(self):
|
||||
p = np.array([[1], [0], [0]])
|
||||
R = 1
|
||||
result = sphere(p, R)
|
||||
expected = 0
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_sphere_outside(self):
|
||||
p = np.array([[2], [0], [0]])
|
||||
R = 1
|
||||
result = sphere(p, R)
|
||||
expected = 1
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_sphere_inside(self):
|
||||
p = np.array([[0.5], [0], [0]])
|
||||
R = 1
|
||||
result = sphere(p, R)
|
||||
expected = -0.5
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
||||
class TestBox(unittest.TestCase):
|
||||
|
||||
def test_box_center(self):
|
||||
p = np.array([[0], [0], [0]])
|
||||
w = (1, 1, 1)
|
||||
result = box(p, w)
|
||||
expected = -1
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_box_surface(self):
|
||||
p = np.array([[1], [0], [0]])
|
||||
w = (1, 1, 1)
|
||||
result = box(p, w)
|
||||
expected = 0
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_box_outside(self):
|
||||
p = np.array([[2], [0], [0]])
|
||||
w = (1, 1, 1)
|
||||
result = box(p, w)
|
||||
expected = 1
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_box_inside(self):
|
||||
p = np.array([[0.5], [0], [0]])
|
||||
w = (1, 1, 1)
|
||||
result = box(p, w)
|
||||
expected = -0.5
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
||||
class TestEllipsoid(unittest.TestCase):
|
||||
|
||||
def test_ellipsoid_surface(self):
|
||||
p = np.array([[1], [0], [0]])
|
||||
r = (1, 1, 1)
|
||||
result = ellipsoid(p, r)
|
||||
expected = 0
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_ellipsoid_outside(self):
|
||||
p = np.array([[2], [0], [0]])
|
||||
r = (1, 1, 1)
|
||||
result = ellipsoid(p, r)
|
||||
expected = 1
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_ellipsoid_inside(self):
|
||||
p = np.array([[0.5], [0], [0]])
|
||||
r = (1, 1, 1)
|
||||
result = ellipsoid(p, r)
|
||||
expected = -0.5
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
30
test/tools/test_slicing.py
Normal file
30
test/tools/test_slicing.py
Normal file
@@ -0,0 +1,30 @@
|
||||
def test_slice_len():
|
||||
import random
|
||||
from navigate.tools.slicing import slice_len
|
||||
|
||||
for _ in range(100):
|
||||
n0 = random.randint(1,10)
|
||||
n1 = random.randint(1,10)
|
||||
sl = slice(0,n0,1)
|
||||
|
||||
assert slice_len(sl, n1) == min(n0,n1)
|
||||
|
||||
def test_ensure_iter():
|
||||
from navigate.tools.slicing import ensure_iter
|
||||
|
||||
ensure_iter(2, 0, 1) == range(0, 1)
|
||||
ensure_iter(2, 1, 1) == range(0, 1)
|
||||
ensure_iter(2, 0, 5) == range(2, 3)
|
||||
ensure_iter(2, 1, 5) == range(0, 5)
|
||||
ensure_iter(slice(0, 2), 0, 1) == range(0, 1)
|
||||
ensure_iter(slice(0, 2), 1, 1) == range(0, 1)
|
||||
ensure_iter(slice(0, 2), 0, 5) == range(0, 2)
|
||||
ensure_iter(slice(0, 2), 1, 5) == range(0, 5)
|
||||
|
||||
def test_ensure_slice():
|
||||
from navigate.tools.slicing import ensure_slice
|
||||
|
||||
ensure_slice(2, 0) == slice(2, 3)
|
||||
ensure_slice(2, 1) == slice(None, None, None)
|
||||
ensure_slice(slice(0, 2), 0) == slice(0, 2, None)
|
||||
ensure_slice(slice(0, 2), 1) == slice(None, None, None)
|
||||
126
test/tools/test_waveform_template_functs.py
Normal file
126
test/tools/test_waveform_template_functs.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only
|
||||
# (subject to the limitations in the disclaimer below)
|
||||
# provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Standard library imports
|
||||
import unittest
|
||||
|
||||
# Third-party imports
|
||||
|
||||
# Local application imports
|
||||
from navigate.tools.waveform_template_funcs import get_waveform_template_parameters
|
||||
|
||||
|
||||
class TestGetWaveformTemplateParameters(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.waveform_template_name = "template1"
|
||||
self.waveform_template_dict = {
|
||||
"template1": {"repeat": 3, "expand": 2},
|
||||
"template2": {"repeat": "repeat_param", "expand": "expand_param"},
|
||||
}
|
||||
self.microscope_state_dict = {"repeat_param": 4, "expand_param": 5}
|
||||
|
||||
def test_get_waveform_template_parameters(self):
|
||||
repeat_num, expand_num = get_waveform_template_parameters(
|
||||
self.waveform_template_name,
|
||||
self.waveform_template_dict,
|
||||
self.microscope_state_dict,
|
||||
)
|
||||
|
||||
self.assertEqual(repeat_num, 3)
|
||||
self.assertEqual(expand_num, 2)
|
||||
|
||||
def test_get_waveform_template_parameters_with_microscope_state(self):
|
||||
self.waveform_template_name = "template2"
|
||||
repeat_num, expand_num = get_waveform_template_parameters(
|
||||
self.waveform_template_name,
|
||||
self.waveform_template_dict,
|
||||
self.microscope_state_dict,
|
||||
)
|
||||
|
||||
self.assertEqual(repeat_num, 4)
|
||||
self.assertEqual(expand_num, 5)
|
||||
|
||||
def test_get_waveform_template_parameters_with_missing_template(self):
|
||||
self.waveform_template_name = "template3"
|
||||
repeat_num, expand_num = get_waveform_template_parameters(
|
||||
self.waveform_template_name,
|
||||
self.waveform_template_dict,
|
||||
self.microscope_state_dict,
|
||||
)
|
||||
|
||||
self.assertEqual(repeat_num, 1)
|
||||
self.assertEqual(expand_num, 1)
|
||||
|
||||
|
||||
class TestGetWaveformTemplateParametersExceptions(unittest.TestCase):
|
||||
def test_key_error_waveform_template_name(self):
|
||||
waveform_template_dict = {"template1": {"repeat": 2, "expand": 3}}
|
||||
microscope_state_dict = {}
|
||||
result = get_waveform_template_parameters(
|
||||
"nonexistent_template", waveform_template_dict, microscope_state_dict
|
||||
)
|
||||
self.assertEqual(
|
||||
result,
|
||||
(1, 1),
|
||||
"Default values should be returned when waveform template "
|
||||
"name is not found",
|
||||
)
|
||||
|
||||
def test_key_error_repeat_key(self):
|
||||
waveform_template_dict = {"template1": {"expand": 3}}
|
||||
microscope_state_dict = {}
|
||||
result = get_waveform_template_parameters(
|
||||
"template1", waveform_template_dict, microscope_state_dict
|
||||
)
|
||||
self.assertEqual(
|
||||
result,
|
||||
(1, 3),
|
||||
"Default value for repeat_num should be returned "
|
||||
"when repeat key is not found",
|
||||
)
|
||||
|
||||
def test_key_error_expand_key(self):
|
||||
waveform_template_dict = {"template1": {"repeat": 2}}
|
||||
microscope_state_dict = {}
|
||||
result = get_waveform_template_parameters(
|
||||
"template1", waveform_template_dict, microscope_state_dict
|
||||
)
|
||||
self.assertEqual(
|
||||
result,
|
||||
(2, 1),
|
||||
"Default value for expand_num should be returned"
|
||||
" when expand key is not found",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
193
test/tools/test_xml_tools.py
Normal file
193
test/tools/test_xml_tools.py
Normal file
@@ -0,0 +1,193 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only
|
||||
# (subject to the limitations in the disclaimer below)
|
||||
# provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Standard library imports
|
||||
import glob
|
||||
import xml.etree.ElementTree as ET
|
||||
import unittest
|
||||
|
||||
# Third party imports
|
||||
|
||||
# Local application imports
|
||||
from navigate.tools import xml_tools
|
||||
from navigate.tools.xml_tools import parse_xml
|
||||
|
||||
|
||||
def test_xml_parse_write():
|
||||
|
||||
for fn in glob.glob("./xml_examples/*.xml"):
|
||||
# Open XML file
|
||||
with open(fn, "r") as fp:
|
||||
example = fp.read()
|
||||
root = ET.fromstring(example)
|
||||
|
||||
# Parse it
|
||||
parsed = xml_tools.parse_xml(root)
|
||||
# Convert it back to a string
|
||||
xml_string = xml_tools.dict_to_xml(parsed, root.tag)
|
||||
|
||||
# get rid of <?xml and the <!-- comment
|
||||
example_str = (
|
||||
"".join(example.split("\n")[2:]).replace(" ", "").replace("\t", "")
|
||||
)
|
||||
xml_str = (
|
||||
xml_string.strip().replace(" ", "").replace("\n", "").replace("\t", "")
|
||||
)
|
||||
|
||||
# Make sure the strings match, sans white space
|
||||
assert example_str == xml_str
|
||||
|
||||
# Parse the string we created
|
||||
root2 = ET.fromstring(xml_string)
|
||||
parsed2 = xml_tools.parse_xml(root2)
|
||||
|
||||
# Make sure it matches the dictionary parsed from the original document
|
||||
assert parsed == parsed2
|
||||
|
||||
|
||||
class TestDictToXml(unittest.TestCase):
|
||||
def test_dict_to_xml_with_single_level(self):
|
||||
# Test case with a dictionary containing a single level
|
||||
d = {"name": "John", "age": 30, "city": "New York"}
|
||||
expected_xml = '<name name="John" age="30" city="New York"/>\n'
|
||||
actual_xml = xml_tools.dict_to_xml(d)
|
||||
self.assertEqual(actual_xml, expected_xml)
|
||||
|
||||
def test_dict_to_xml_with_nested_dict(self):
|
||||
# Test case with a dictionary containing nested dictionaries
|
||||
d = {
|
||||
"person": {"name": "John", "age": 30, "city": "New York"},
|
||||
"address": {"street": "123 Main St", "zipcode": "10001"},
|
||||
}
|
||||
expected_xml = (
|
||||
'<root>\n <person name="John" age="30" '
|
||||
'city="New York"/>\n <address street="123 '
|
||||
'Main St" zipcode="10001"/>\n</root>\n'
|
||||
)
|
||||
actual_xml = xml_tools.dict_to_xml(d, tag="root")
|
||||
self.assertEqual(actual_xml, expected_xml)
|
||||
|
||||
def test_dict_to_xml_with_nested_list(self):
|
||||
# Test case with a dictionary containing nested lists
|
||||
d = {
|
||||
"students": [
|
||||
{"name": "Alice", "age": 20},
|
||||
{"name": "Bob", "age": 22},
|
||||
{"name": "Charlie", "age": 21},
|
||||
]
|
||||
}
|
||||
expected_xml = (
|
||||
'<class>\n <students name="Alice" '
|
||||
'age="20"/>\n <students name="Bob" '
|
||||
'age="22"/>\n <students name="Charlie" age="21"/>\n</class>\n'
|
||||
)
|
||||
actual_xml = xml_tools.dict_to_xml(d, tag="class")
|
||||
self.assertEqual(actual_xml, expected_xml)
|
||||
|
||||
def test_dict_to_xml_with_text_value(self):
|
||||
# Test case with a dictionary containing a text value
|
||||
d = {
|
||||
"person": {
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"city": "New York",
|
||||
"text": "Hello, world!",
|
||||
}
|
||||
}
|
||||
expected_xml = (
|
||||
'<root>\n <person name="John" age="30" '
|
||||
'city="New York">Hello, world!</person>\n</root>\n'
|
||||
)
|
||||
actual_xml = xml_tools.dict_to_xml(d, tag="root")
|
||||
self.assertEqual(actual_xml, expected_xml)
|
||||
|
||||
|
||||
class TestParseXml(unittest.TestCase):
|
||||
|
||||
def test_parse_xml_single_level(self):
|
||||
xml_string = '<person name="John" age="30" city="New York"/>'
|
||||
root = ET.fromstring(xml_string)
|
||||
expected_dict = {"name": "John", "age": "30", "city": "New York"}
|
||||
self.assertEqual(parse_xml(root), expected_dict)
|
||||
|
||||
def test_parse_xml_nested_elements(self):
|
||||
xml_string = """
|
||||
<root>
|
||||
<person name="John" age="30" city="New York"/>
|
||||
<address street="123 Main St" zipcode="10001"/>
|
||||
</root>
|
||||
"""
|
||||
root = ET.fromstring(xml_string)
|
||||
expected_dict = {
|
||||
"person": {"name": "John", "age": "30", "city": "New York"},
|
||||
"address": {"street": "123 Main St", "zipcode": "10001"},
|
||||
}
|
||||
self.assertEqual(parse_xml(root), expected_dict)
|
||||
|
||||
def test_parse_xml_with_text(self):
|
||||
xml_string = """
|
||||
<root>
|
||||
<person name="John" age="30" city="New York">Hello, world!</person>
|
||||
</root>
|
||||
"""
|
||||
root = ET.fromstring(xml_string)
|
||||
expected_dict = {
|
||||
"person": {
|
||||
"name": "John",
|
||||
"age": "30",
|
||||
"city": "New York",
|
||||
"text": "Hello, world!",
|
||||
}
|
||||
}
|
||||
self.assertEqual(parse_xml(root), expected_dict)
|
||||
|
||||
def test_parse_xml_with_repeated_elements(self):
|
||||
xml_string = """
|
||||
<class>
|
||||
<student name="Alice" age="20"/>
|
||||
<student name="Bob" age="22"/>
|
||||
<student name="Charlie" age="21"/>
|
||||
</class>
|
||||
"""
|
||||
root = ET.fromstring(xml_string)
|
||||
expected_dict = {
|
||||
"student": [
|
||||
{"name": "Alice", "age": "20"},
|
||||
{"name": "Bob", "age": "22"},
|
||||
{"name": "Charlie", "age": "21"},
|
||||
]
|
||||
}
|
||||
self.assertEqual(parse_xml(root), expected_dict)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
55
test/tools/xml_examples/drosophila.xml
Normal file
55
test/tools/xml_examples/drosophila.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--BigDataViewer XML Example from https://fly.mpi-cbg.de/~pietzsch/bdv-example/drosophila.xml-->
|
||||
<SpimData version="0.2">
|
||||
<BasePath type="relative">.</BasePath>
|
||||
<SequenceDescription>
|
||||
<ImageLoader format="bdv.hdf5">
|
||||
<hdf5 type="relative">drosophila.h5</hdf5>
|
||||
</ImageLoader>
|
||||
<ViewSetups>
|
||||
<ViewSetup>
|
||||
<id>0</id>
|
||||
</ViewSetup>
|
||||
<ViewSetup>
|
||||
<id>1</id>
|
||||
</ViewSetup>
|
||||
</ViewSetups>
|
||||
<Timepoints type="range">
|
||||
<first>0</first>
|
||||
<last>2</last>
|
||||
</Timepoints>
|
||||
</SequenceDescription>
|
||||
<ViewRegistrations>
|
||||
<ReferenceTimepoint>350</ReferenceTimepoint>
|
||||
<ViewRegistration timepoint="0" setup="0">
|
||||
<ViewTransform type="affine">
|
||||
<affine>0.996591329574585 0.0014791092835366726 0.01073253620415926 -5.384683609008789 -0.001930794445797801 0.9954455494880676 -0.003766203299164772 -81.54486083984375 -4.968568682670593E-4 -6.0385558754205704E-5 3.490110397338867 9.85491943359375</affine>
|
||||
</ViewTransform>
|
||||
</ViewRegistration>
|
||||
<ViewRegistration timepoint="0" setup="1">
|
||||
<ViewTransform type="affine">
|
||||
<affine>0.9967471361160278 -0.0018281697994098067 -0.0159448329359293 -3.9299731254577637 -0.005900930613279343 0.49216321110725403 -3.0123801231384277 262.5342712402344 0.004747752100229263 0.8682872653007507 1.7716364860534668 -426.66754150390625</affine>
|
||||
</ViewTransform>
|
||||
</ViewRegistration>
|
||||
<ViewRegistration timepoint="1" setup="0">
|
||||
<ViewTransform type="affine">
|
||||
<affine>0.9966375827789307 0.0014226322527974844 0.009849724359810352 -5.8060078620910645 -0.0018444646848365664 0.9956076145172119 -0.005629136227071285 -69.27687072753906 -3.634979948401451E-4 6.720321252942085E-4 3.4895498752593994 10.738128662109375</affine>
|
||||
</ViewTransform>
|
||||
</ViewRegistration>
|
||||
<ViewRegistration timepoint="1" setup="1">
|
||||
<ViewTransform type="affine">
|
||||
<affine>0.996777355670929 -0.001879655523225665 -0.015927843749523163 -4.662176132202148 -0.005749744363129139 0.4920715391635895 -3.0124056339263916 275.1748046875 0.004699353594332933 0.8685303926467896 1.7690489292144775 -422.2758483886719</affine>
|
||||
</ViewTransform>
|
||||
</ViewRegistration>
|
||||
<ViewRegistration timepoint="2" setup="0">
|
||||
<ViewTransform type="affine">
|
||||
<affine>0.9967382550239563 0.0013750510988757014 0.00983844231814146 -5.692346572875977 -0.0018074746476486325 0.9957119822502136 -0.0039688413962721825 -70.14445495605469 -3.824750892817974E-4 3.17456666380167E-4 3.4910457134246826 13.597747802734375</affine>
|
||||
</ViewTransform>
|
||||
</ViewRegistration>
|
||||
<ViewRegistration timepoint="2" setup="1">
|
||||
<ViewTransform type="affine">
|
||||
<affine>0.9968152642250061 -0.001965513452887535 -0.015846025198698044 -5.178727149963379 -0.005820842459797859 0.4911666214466095 -3.0130810737609863 278.82867431640625 0.004750141873955727 0.8690722584724426 1.7654311656951904 -415.7231140136719</affine>
|
||||
</ViewTransform>
|
||||
</ViewRegistration>
|
||||
</ViewRegistrations>
|
||||
</SpimData>
|
||||
289
test/tools/xml_examples/samples-SP2.ome.xml
Normal file
289
test/tools/xml_examples/samples-SP2.ome.xml
Normal file
@@ -0,0 +1,289 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--OME-XML example from https://github.com/ome/samples/blob/master/leica/martin/samples-SP2/samples-SP2.ome.xml <OME xmlns="http://www.openmicroscopy.org/Schemas/OME/2015-01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openmicroscopy.org/Schemas/OME/2015-01 http://www.openmicroscopy.org/Schemas/OME/2015-01/ome.xsd">-->
|
||||
<OME>
|
||||
<Instrument ID="Instrument:0">
|
||||
<Detector ID="Detector:0:0" Offset="-34.599999999999994" Type="PMT" Voltage="742.0867526377491" VoltageUnit="V"/>
|
||||
<Detector ID="Detector:0:1" Offset="0.0" Type="PMT" Voltage="391.2661195779601" VoltageUnit="V"/>
|
||||
<Objective Correction="Other" ID="Objective:0:0" Immersion="Oil" LensNA="1.25" Model="HCX PL APO CS" NominalMagnification="40.0" SerialNumber="506179"/>
|
||||
<Filter ID="Filter:0:0" Model="SP Mirror 1"/>
|
||||
<Filter ID="Filter:0:1" Model="SP Mirror 2">
|
||||
<TransmittanceRange CutIn="578.0" CutInUnit="nm" CutOut="629.0" CutOutUnit="nm"/>
|
||||
</Filter>
|
||||
<Filter ID="Filter:0:2" Model="SP Mirror 3"/>
|
||||
</Instrument>
|
||||
<Instrument ID="Instrument:1">
|
||||
<Detector ID="Detector:1:0" Offset="0.5" Type="PMT" Voltage="631.8874560375147" VoltageUnit="V"/>
|
||||
<Detector ID="Detector:1:1" Offset="-1.2999999999999972" Type="PMT" Voltage="640.0937866354044" VoltageUnit="V"/>
|
||||
<Detector ID="Detector:1:2" Offset="-4.400000000000006" Type="PMT" Voltage="163.8335287221571" VoltageUnit="V"/>
|
||||
<Objective Correction="Other" ID="Objective:1:0" Immersion="Oil" LensNA="1.32" Model="HCX PL APO CS" NominalMagnification="63.0" SerialNumber="506182"/>
|
||||
<Filter ID="Filter:1:0" Model="SP Mirror 1">
|
||||
<TransmittanceRange CutIn="500.00000000000006" CutInUnit="nm" CutOut="540.0" CutOutUnit="nm"/>
|
||||
</Filter>
|
||||
<Filter ID="Filter:1:1" Model="SP Mirror 2">
|
||||
<TransmittanceRange CutIn="650.0" CutInUnit="nm" CutOut="700.0" CutOutUnit="nm"/>
|
||||
</Filter>
|
||||
<Filter ID="Filter:1:2" Model="SP Mirror 3"/>
|
||||
</Instrument>
|
||||
<Instrument ID="Instrument:2">
|
||||
<Detector ID="Detector:2:0" Offset="1.0" Type="PMT" Voltage="820.926143024619" VoltageUnit="V"/>
|
||||
<Detector ID="Detector:2:1" Offset="0.7999999999999972" Type="PMT" Voltage="451.34818288393905" VoltageUnit="V"/>
|
||||
<Detector ID="Detector:2:2" Offset="-13.400000000000006" Type="PMT" Voltage="286.9284876905041" VoltageUnit="V"/>
|
||||
<Objective Correction="Other" ID="Objective:2:0" Immersion="Oil" LensNA="1.32" Model="HCX PL APO CS" NominalMagnification="63.0" SerialNumber="506182"/>
|
||||
<Filter ID="Filter:2:0" Model="SP Mirror 1">
|
||||
<TransmittanceRange CutIn="500.00000000000006" CutInUnit="nm" CutOut="560.0" CutOutUnit="nm"/>
|
||||
</Filter>
|
||||
<Filter ID="Filter:2:1" Model="SP Mirror 2">
|
||||
<TransmittanceRange CutIn="575.0" CutInUnit="nm" CutOut="693.0000000000002" CutOutUnit="nm"/>
|
||||
</Filter>
|
||||
<Filter ID="Filter:2:2" Model="SP Mirror 3"/>
|
||||
</Instrument>
|
||||
<Instrument ID="Instrument:3">
|
||||
<Detector ID="Detector:3:0" Offset="3.200000000000003" Type="PMT" Voltage="430.2461899179367" VoltageUnit="V"/>
|
||||
<Objective Correction="Other" ID="Objective:3:0" Immersion="Other" LensNA="0.3" Model="HC PL FLUOTAR" NominalMagnification="10.0" SerialNumber="506507"/>
|
||||
<Filter ID="Filter:3:0" Model="SP Mirror 1"/>
|
||||
<Filter ID="Filter:3:1" Model="SP Mirror 2"/>
|
||||
<Filter ID="Filter:3:2" Model="SP Mirror 3">
|
||||
<TransmittanceRange CutIn="790.0000000000015" CutInUnit="nm" CutOut="800.0000000000016" CutOutUnit="nm"/>
|
||||
</Filter>
|
||||
</Instrument>
|
||||
<Image ID="Image:0" Name="XYZ-Ch">
|
||||
<Description/>
|
||||
<InstrumentRef ID="Instrument:0"/>
|
||||
<ObjectiveSettings ID="Objective:0:0"/>
|
||||
<StageLabel Name="Position" Z="-2.2590361445783174" ZUnit="reference frame"/>
|
||||
<Pixels BigEndian="false" DimensionOrder="XYCZT" ID="Pixels:0" Interleaved="false" PhysicalSizeX="2.9296875" PhysicalSizeXUnit="µm" PhysicalSizeY="2.9296875" PhysicalSizeYUnit="µm" PhysicalSizeZ="3.378378378378379" PhysicalSizeZUnit="µm" SignificantBits="8" SizeC="2" SizeT="1" SizeX="128" SizeY="128" SizeZ="50" Type="uint8">
|
||||
<Channel Color="-16776961" ID="Channel:0:0" Name="TRITC" PinholeSize="81.40681653372008" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:0:0"/>
|
||||
<LightPath>
|
||||
<EmissionFilterRef ID="Filter:0:1"/>
|
||||
</LightPath>
|
||||
</Channel>
|
||||
<Channel Color="-1" ID="Channel:0:1" Name="PMT Trans" PinholeSize="81.40681653372008" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:0:1"/>
|
||||
<LightPath/>
|
||||
</Channel>
|
||||
<MetadataOnly/>
|
||||
<Plane DeltaT="0.0" DeltaTUnit="s" TheC="0" TheT="0" TheZ="0"/>
|
||||
<Plane DeltaT="0.0" DeltaTUnit="s" TheC="1" TheT="0" TheZ="0"/>
|
||||
<Plane DeltaT="2.656" DeltaTUnit="s" TheC="0" TheT="0" TheZ="1"/>
|
||||
<Plane DeltaT="2.656" DeltaTUnit="s" TheC="1" TheT="0" TheZ="1"/>
|
||||
<Plane DeltaT="5.328" DeltaTUnit="s" TheC="0" TheT="0" TheZ="2"/>
|
||||
<Plane DeltaT="5.328" DeltaTUnit="s" TheC="1" TheT="0" TheZ="2"/>
|
||||
<Plane DeltaT="8.0" DeltaTUnit="s" TheC="0" TheT="0" TheZ="3"/>
|
||||
<Plane DeltaT="8.0" DeltaTUnit="s" TheC="1" TheT="0" TheZ="3"/>
|
||||
<Plane DeltaT="10.656" DeltaTUnit="s" TheC="0" TheT="0" TheZ="4"/>
|
||||
<Plane DeltaT="10.656" DeltaTUnit="s" TheC="1" TheT="0" TheZ="4"/>
|
||||
<Plane DeltaT="13.328" DeltaTUnit="s" TheC="0" TheT="0" TheZ="5"/>
|
||||
<Plane DeltaT="13.328" DeltaTUnit="s" TheC="1" TheT="0" TheZ="5"/>
|
||||
<Plane DeltaT="15.984" DeltaTUnit="s" TheC="0" TheT="0" TheZ="6"/>
|
||||
<Plane DeltaT="15.984" DeltaTUnit="s" TheC="1" TheT="0" TheZ="6"/>
|
||||
<Plane DeltaT="18.656" DeltaTUnit="s" TheC="0" TheT="0" TheZ="7"/>
|
||||
<Plane DeltaT="18.656" DeltaTUnit="s" TheC="1" TheT="0" TheZ="7"/>
|
||||
<Plane DeltaT="21.328" DeltaTUnit="s" TheC="0" TheT="0" TheZ="8"/>
|
||||
<Plane DeltaT="21.328" DeltaTUnit="s" TheC="1" TheT="0" TheZ="8"/>
|
||||
<Plane DeltaT="23.984" DeltaTUnit="s" TheC="0" TheT="0" TheZ="9"/>
|
||||
<Plane DeltaT="23.984" DeltaTUnit="s" TheC="1" TheT="0" TheZ="9"/>
|
||||
<Plane DeltaT="26.656" DeltaTUnit="s" TheC="0" TheT="0" TheZ="10"/>
|
||||
<Plane DeltaT="26.656" DeltaTUnit="s" TheC="1" TheT="0" TheZ="10"/>
|
||||
<Plane DeltaT="29.313" DeltaTUnit="s" TheC="0" TheT="0" TheZ="11"/>
|
||||
<Plane DeltaT="29.313" DeltaTUnit="s" TheC="1" TheT="0" TheZ="11"/>
|
||||
<Plane DeltaT="31.984" DeltaTUnit="s" TheC="0" TheT="0" TheZ="12"/>
|
||||
<Plane DeltaT="31.984" DeltaTUnit="s" TheC="1" TheT="0" TheZ="12"/>
|
||||
<Plane DeltaT="34.656" DeltaTUnit="s" TheC="0" TheT="0" TheZ="13"/>
|
||||
<Plane DeltaT="34.656" DeltaTUnit="s" TheC="1" TheT="0" TheZ="13"/>
|
||||
<Plane DeltaT="37.313" DeltaTUnit="s" TheC="0" TheT="0" TheZ="14"/>
|
||||
<Plane DeltaT="37.313" DeltaTUnit="s" TheC="1" TheT="0" TheZ="14"/>
|
||||
<Plane DeltaT="39.984" DeltaTUnit="s" TheC="0" TheT="0" TheZ="15"/>
|
||||
<Plane DeltaT="39.984" DeltaTUnit="s" TheC="1" TheT="0" TheZ="15"/>
|
||||
<Plane DeltaT="42.656" DeltaTUnit="s" TheC="0" TheT="0" TheZ="16"/>
|
||||
<Plane DeltaT="42.656" DeltaTUnit="s" TheC="1" TheT="0" TheZ="16"/>
|
||||
<Plane DeltaT="45.313" DeltaTUnit="s" TheC="0" TheT="0" TheZ="17"/>
|
||||
<Plane DeltaT="45.313" DeltaTUnit="s" TheC="1" TheT="0" TheZ="17"/>
|
||||
<Plane DeltaT="47.984" DeltaTUnit="s" TheC="0" TheT="0" TheZ="18"/>
|
||||
<Plane DeltaT="47.984" DeltaTUnit="s" TheC="1" TheT="0" TheZ="18"/>
|
||||
<Plane DeltaT="50.641" DeltaTUnit="s" TheC="0" TheT="0" TheZ="19"/>
|
||||
<Plane DeltaT="50.641" DeltaTUnit="s" TheC="1" TheT="0" TheZ="19"/>
|
||||
<Plane DeltaT="53.313" DeltaTUnit="s" TheC="0" TheT="0" TheZ="20"/>
|
||||
<Plane DeltaT="53.313" DeltaTUnit="s" TheC="1" TheT="0" TheZ="20"/>
|
||||
<Plane DeltaT="55.984" DeltaTUnit="s" TheC="0" TheT="0" TheZ="21"/>
|
||||
<Plane DeltaT="55.984" DeltaTUnit="s" TheC="1" TheT="0" TheZ="21"/>
|
||||
<Plane DeltaT="58.641" DeltaTUnit="s" TheC="0" TheT="0" TheZ="22"/>
|
||||
<Plane DeltaT="58.641" DeltaTUnit="s" TheC="1" TheT="0" TheZ="22"/>
|
||||
<Plane DeltaT="61.313" DeltaTUnit="s" TheC="0" TheT="0" TheZ="23"/>
|
||||
<Plane DeltaT="61.313" DeltaTUnit="s" TheC="1" TheT="0" TheZ="23"/>
|
||||
<Plane DeltaT="63.969" DeltaTUnit="s" TheC="0" TheT="0" TheZ="24"/>
|
||||
<Plane DeltaT="63.969" DeltaTUnit="s" TheC="1" TheT="0" TheZ="24"/>
|
||||
<Plane DeltaT="66.641" DeltaTUnit="s" TheC="0" TheT="0" TheZ="25"/>
|
||||
<Plane DeltaT="66.641" DeltaTUnit="s" TheC="1" TheT="0" TheZ="25"/>
|
||||
<Plane DeltaT="69.313" DeltaTUnit="s" TheC="0" TheT="0" TheZ="26"/>
|
||||
<Plane DeltaT="69.313" DeltaTUnit="s" TheC="1" TheT="0" TheZ="26"/>
|
||||
<Plane DeltaT="71.969" DeltaTUnit="s" TheC="0" TheT="0" TheZ="27"/>
|
||||
<Plane DeltaT="71.969" DeltaTUnit="s" TheC="1" TheT="0" TheZ="27"/>
|
||||
<Plane DeltaT="74.641" DeltaTUnit="s" TheC="0" TheT="0" TheZ="28"/>
|
||||
<Plane DeltaT="74.641" DeltaTUnit="s" TheC="1" TheT="0" TheZ="28"/>
|
||||
<Plane DeltaT="77.297" DeltaTUnit="s" TheC="0" TheT="0" TheZ="29"/>
|
||||
<Plane DeltaT="77.297" DeltaTUnit="s" TheC="1" TheT="0" TheZ="29"/>
|
||||
<Plane DeltaT="79.969" DeltaTUnit="s" TheC="0" TheT="0" TheZ="30"/>
|
||||
<Plane DeltaT="79.969" DeltaTUnit="s" TheC="1" TheT="0" TheZ="30"/>
|
||||
<Plane DeltaT="82.641" DeltaTUnit="s" TheC="0" TheT="0" TheZ="31"/>
|
||||
<Plane DeltaT="82.641" DeltaTUnit="s" TheC="1" TheT="0" TheZ="31"/>
|
||||
<Plane DeltaT="85.297" DeltaTUnit="s" TheC="0" TheT="0" TheZ="32"/>
|
||||
<Plane DeltaT="85.297" DeltaTUnit="s" TheC="1" TheT="0" TheZ="32"/>
|
||||
<Plane DeltaT="87.969" DeltaTUnit="s" TheC="0" TheT="0" TheZ="33"/>
|
||||
<Plane DeltaT="87.969" DeltaTUnit="s" TheC="1" TheT="0" TheZ="33"/>
|
||||
<Plane DeltaT="90.625" DeltaTUnit="s" TheC="0" TheT="0" TheZ="34"/>
|
||||
<Plane DeltaT="90.625" DeltaTUnit="s" TheC="1" TheT="0" TheZ="34"/>
|
||||
<Plane DeltaT="93.297" DeltaTUnit="s" TheC="0" TheT="0" TheZ="35"/>
|
||||
<Plane DeltaT="93.297" DeltaTUnit="s" TheC="1" TheT="0" TheZ="35"/>
|
||||
<Plane DeltaT="95.969" DeltaTUnit="s" TheC="0" TheT="0" TheZ="36"/>
|
||||
<Plane DeltaT="95.969" DeltaTUnit="s" TheC="1" TheT="0" TheZ="36"/>
|
||||
<Plane DeltaT="98.625" DeltaTUnit="s" TheC="0" TheT="0" TheZ="37"/>
|
||||
<Plane DeltaT="98.625" DeltaTUnit="s" TheC="1" TheT="0" TheZ="37"/>
|
||||
<Plane DeltaT="101.297" DeltaTUnit="s" TheC="0" TheT="0" TheZ="38"/>
|
||||
<Plane DeltaT="101.297" DeltaTUnit="s" TheC="1" TheT="0" TheZ="38"/>
|
||||
<Plane DeltaT="103.969" DeltaTUnit="s" TheC="0" TheT="0" TheZ="39"/>
|
||||
<Plane DeltaT="103.969" DeltaTUnit="s" TheC="1" TheT="0" TheZ="39"/>
|
||||
<Plane DeltaT="106.625" DeltaTUnit="s" TheC="0" TheT="0" TheZ="40"/>
|
||||
<Plane DeltaT="106.625" DeltaTUnit="s" TheC="1" TheT="0" TheZ="40"/>
|
||||
<Plane DeltaT="109.297" DeltaTUnit="s" TheC="0" TheT="0" TheZ="41"/>
|
||||
<Plane DeltaT="109.297" DeltaTUnit="s" TheC="1" TheT="0" TheZ="41"/>
|
||||
<Plane DeltaT="111.953" DeltaTUnit="s" TheC="0" TheT="0" TheZ="42"/>
|
||||
<Plane DeltaT="111.953" DeltaTUnit="s" TheC="1" TheT="0" TheZ="42"/>
|
||||
<Plane DeltaT="114.625" DeltaTUnit="s" TheC="0" TheT="0" TheZ="43"/>
|
||||
<Plane DeltaT="114.625" DeltaTUnit="s" TheC="1" TheT="0" TheZ="43"/>
|
||||
<Plane DeltaT="117.297" DeltaTUnit="s" TheC="0" TheT="0" TheZ="44"/>
|
||||
<Plane DeltaT="117.297" DeltaTUnit="s" TheC="1" TheT="0" TheZ="44"/>
|
||||
<Plane DeltaT="119.953" DeltaTUnit="s" TheC="0" TheT="0" TheZ="45"/>
|
||||
<Plane DeltaT="119.953" DeltaTUnit="s" TheC="1" TheT="0" TheZ="45"/>
|
||||
<Plane DeltaT="122.625" DeltaTUnit="s" TheC="0" TheT="0" TheZ="46"/>
|
||||
<Plane DeltaT="122.625" DeltaTUnit="s" TheC="1" TheT="0" TheZ="46"/>
|
||||
<Plane DeltaT="125.281" DeltaTUnit="s" TheC="0" TheT="0" TheZ="47"/>
|
||||
<Plane DeltaT="125.281" DeltaTUnit="s" TheC="1" TheT="0" TheZ="47"/>
|
||||
<Plane DeltaT="127.953" DeltaTUnit="s" TheC="0" TheT="0" TheZ="48"/>
|
||||
<Plane DeltaT="127.953" DeltaTUnit="s" TheC="1" TheT="0" TheZ="48"/>
|
||||
<Plane DeltaT="130.625" DeltaTUnit="s" TheC="0" TheT="0" TheZ="49"/>
|
||||
<Plane DeltaT="130.625" DeltaTUnit="s" TheC="1" TheT="0" TheZ="49"/>
|
||||
</Pixels>
|
||||
</Image>
|
||||
<Image ID="Image:1" Name="XY-Ch-trans">
|
||||
<Description/>
|
||||
<InstrumentRef ID="Instrument:1"/>
|
||||
<ObjectiveSettings ID="Objective:1:0"/>
|
||||
<StageLabel Name="Position" Z="-9.422826440898726" ZUnit="reference frame"/>
|
||||
<Pixels BigEndian="false" DimensionOrder="XYCZT" ID="Pixels:1" Interleaved="false" PhysicalSizeX="0.23251488095238093" PhysicalSizeXUnit="µm" PhysicalSizeY="0.2325148809523809" PhysicalSizeYUnit="µm" PhysicalSizeZ="0.04070335395636601" PhysicalSizeZUnit="µm" SignificantBits="8" SizeC="3" SizeT="1" SizeX="512" SizeY="512" SizeZ="1" Type="uint8">
|
||||
<Channel Color="16711935" ID="Channel:1:0" Name="PMT 1" PinholeSize="20.420594633792604" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:1:0"/>
|
||||
<LightPath>
|
||||
<EmissionFilterRef ID="Filter:1:0"/>
|
||||
</LightPath>
|
||||
</Channel>
|
||||
<Channel Color="-16776961" ID="Channel:1:1" Name="PMT 2" PinholeSize="20.420594633792604" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:1:1"/>
|
||||
<LightPath>
|
||||
<EmissionFilterRef ID="Filter:1:1"/>
|
||||
</LightPath>
|
||||
</Channel>
|
||||
<Channel Color="-1" ID="Channel:1:2" Name="PMT Trans" PinholeSize="20.420594633792604" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:1:2"/>
|
||||
<LightPath/>
|
||||
</Channel>
|
||||
<MetadataOnly/>
|
||||
<Plane DeltaT="0.0" DeltaTUnit="s" TheC="0" TheT="0" TheZ="0"/>
|
||||
<Plane DeltaT="0.0" DeltaTUnit="s" TheC="1" TheT="0" TheZ="0"/>
|
||||
<Plane DeltaT="0.0" DeltaTUnit="s" TheC="2" TheT="0" TheZ="0"/>
|
||||
</Pixels>
|
||||
</Image>
|
||||
<Image ID="Image:2" Name="XZY-Ch-trans">
|
||||
<Description/>
|
||||
<InstrumentRef ID="Instrument:2"/>
|
||||
<ObjectiveSettings ID="Objective:2:0"/>
|
||||
<StageLabel Name="Position" Z="0.38668186258547055" ZUnit="reference frame"/>
|
||||
<Pixels BigEndian="false" DimensionOrder="XYZCT" ID="Pixels:2" Interleaved="false" PhysicalSizeX="0.11625744047619045" PhysicalSizeXUnit="µm" PhysicalSizeY="0.11625744047619047" PhysicalSizeYUnit="µm" PhysicalSizeZ="1.5874308042982737" PhysicalSizeZUnit="µm" SignificantBits="8" SizeC="3" SizeT="1" SizeX="512" SizeY="512" SizeZ="1" Type="uint8">
|
||||
<Channel Color="-16776961" ID="Channel:2:0" Name="TEXAS RED" PinholeSize="243.3357505438723" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:2:0"/>
|
||||
<LightPath>
|
||||
<EmissionFilterRef ID="Filter:2:1"/>
|
||||
</LightPath>
|
||||
</Channel>
|
||||
<Channel Color="16711935" ID="Channel:2:1" Name="FITC" PinholeSize="243.3357505438723" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:2:1"/>
|
||||
<LightPath>
|
||||
<EmissionFilterRef ID="Filter:2:0"/>
|
||||
</LightPath>
|
||||
</Channel>
|
||||
<Channel Color="-1" ID="Channel:2:2" Name="PMT Trans" PinholeSize="243.3357505438723" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:2:2"/>
|
||||
<LightPath/>
|
||||
</Channel>
|
||||
<MetadataOnly/>
|
||||
<Plane DeltaT="0.0" DeltaTUnit="s" TheC="0" TheT="0" TheZ="0"/>
|
||||
<Plane DeltaT="3.844" DeltaTUnit="s" TheC="1" TheT="0" TheZ="0"/>
|
||||
<Plane DeltaT="3.844" DeltaTUnit="s" TheC="2" TheT="0" TheZ="0"/>
|
||||
</Pixels>
|
||||
</Image>
|
||||
<Image ID="Image:3" Name="XY-lambda">
|
||||
<Description/>
|
||||
<InstrumentRef ID="Instrument:3"/>
|
||||
<ObjectiveSettings ID="Objective:3:0"/>
|
||||
<StageLabel Name="Position" Z="-0.020351676978177666" ZUnit="reference frame"/>
|
||||
<Pixels BigEndian="false" DimensionOrder="XYCTZ" ID="Pixels:3" Interleaved="false" PhysicalSizeX="2.9296875" PhysicalSizeXUnit="µm" PhysicalSizeY="2.9296875" PhysicalSizeYUnit="µm" SignificantBits="8" SizeC="1" SizeT="50" SizeX="128" SizeY="128" SizeZ="1" Type="uint8">
|
||||
<Channel Color="-1" ID="Channel:3:0" Name="PMT 3" PinholeSize="600.0" PinholeSizeUnit="µm" SamplesPerPixel="1">
|
||||
<DetectorSettings ID="Detector:3:0"/>
|
||||
<LightPath>
|
||||
<EmissionFilterRef ID="Filter:3:2"/>
|
||||
</LightPath>
|
||||
</Channel>
|
||||
<MetadataOnly/>
|
||||
<Plane DeltaT="0.0" DeltaTUnit="s" TheC="0" TheT="0" TheZ="0"/>
|
||||
<Plane DeltaT="0.266" DeltaTUnit="s" TheC="0" TheT="1" TheZ="0"/>
|
||||
<Plane DeltaT="0.532" DeltaTUnit="s" TheC="0" TheT="2" TheZ="0"/>
|
||||
<Plane DeltaT="0.797" DeltaTUnit="s" TheC="0" TheT="3" TheZ="0"/>
|
||||
<Plane DeltaT="1.063" DeltaTUnit="s" TheC="0" TheT="4" TheZ="0"/>
|
||||
<Plane DeltaT="1.657" DeltaTUnit="s" TheC="0" TheT="5" TheZ="0"/>
|
||||
<Plane DeltaT="2.25" DeltaTUnit="s" TheC="0" TheT="6" TheZ="0"/>
|
||||
<Plane DeltaT="2.719" DeltaTUnit="s" TheC="0" TheT="7" TheZ="0"/>
|
||||
<Plane DeltaT="3.188" DeltaTUnit="s" TheC="0" TheT="8" TheZ="0"/>
|
||||
<Plane DeltaT="3.657" DeltaTUnit="s" TheC="0" TheT="9" TheZ="0"/>
|
||||
<Plane DeltaT="4.125" DeltaTUnit="s" TheC="0" TheT="10" TheZ="0"/>
|
||||
<Plane DeltaT="4.594" DeltaTUnit="s" TheC="0" TheT="11" TheZ="0"/>
|
||||
<Plane DeltaT="5.063" DeltaTUnit="s" TheC="0" TheT="12" TheZ="0"/>
|
||||
<Plane DeltaT="5.532" DeltaTUnit="s" TheC="0" TheT="13" TheZ="0"/>
|
||||
<Plane DeltaT="5.891" DeltaTUnit="s" TheC="0" TheT="14" TheZ="0"/>
|
||||
<Plane DeltaT="6.25" DeltaTUnit="s" TheC="0" TheT="15" TheZ="0"/>
|
||||
<Plane DeltaT="6.61" DeltaTUnit="s" TheC="0" TheT="16" TheZ="0"/>
|
||||
<Plane DeltaT="6.969" DeltaTUnit="s" TheC="0" TheT="17" TheZ="0"/>
|
||||
<Plane DeltaT="7.328" DeltaTUnit="s" TheC="0" TheT="18" TheZ="0"/>
|
||||
<Plane DeltaT="7.688" DeltaTUnit="s" TheC="0" TheT="19" TheZ="0"/>
|
||||
<Plane DeltaT="8.047" DeltaTUnit="s" TheC="0" TheT="20" TheZ="0"/>
|
||||
<Plane DeltaT="8.407" DeltaTUnit="s" TheC="0" TheT="21" TheZ="0"/>
|
||||
<Plane DeltaT="8.75" DeltaTUnit="s" TheC="0" TheT="22" TheZ="0"/>
|
||||
<Plane DeltaT="9.11" DeltaTUnit="s" TheC="0" TheT="23" TheZ="0"/>
|
||||
<Plane DeltaT="9.469" DeltaTUnit="s" TheC="0" TheT="24" TheZ="0"/>
|
||||
<Plane DeltaT="9.828" DeltaTUnit="s" TheC="0" TheT="25" TheZ="0"/>
|
||||
<Plane DeltaT="10.188" DeltaTUnit="s" TheC="0" TheT="26" TheZ="0"/>
|
||||
<Plane DeltaT="10.547" DeltaTUnit="s" TheC="0" TheT="27" TheZ="0"/>
|
||||
<Plane DeltaT="10.891" DeltaTUnit="s" TheC="0" TheT="28" TheZ="0"/>
|
||||
<Plane DeltaT="11.25" DeltaTUnit="s" TheC="0" TheT="29" TheZ="0"/>
|
||||
<Plane DeltaT="11.61" DeltaTUnit="s" TheC="0" TheT="30" TheZ="0"/>
|
||||
<Plane DeltaT="11.969" DeltaTUnit="s" TheC="0" TheT="31" TheZ="0"/>
|
||||
<Plane DeltaT="12.328" DeltaTUnit="s" TheC="0" TheT="32" TheZ="0"/>
|
||||
<Plane DeltaT="12.688" DeltaTUnit="s" TheC="0" TheT="33" TheZ="0"/>
|
||||
<Plane DeltaT="13.047" DeltaTUnit="s" TheC="0" TheT="34" TheZ="0"/>
|
||||
<Plane DeltaT="13.391" DeltaTUnit="s" TheC="0" TheT="35" TheZ="0"/>
|
||||
<Plane DeltaT="13.75" DeltaTUnit="s" TheC="0" TheT="36" TheZ="0"/>
|
||||
<Plane DeltaT="14.11" DeltaTUnit="s" TheC="0" TheT="37" TheZ="0"/>
|
||||
<Plane DeltaT="14.469" DeltaTUnit="s" TheC="0" TheT="38" TheZ="0"/>
|
||||
<Plane DeltaT="14.828" DeltaTUnit="s" TheC="0" TheT="39" TheZ="0"/>
|
||||
<Plane DeltaT="15.094" DeltaTUnit="s" TheC="0" TheT="40" TheZ="0"/>
|
||||
<Plane DeltaT="15.36" DeltaTUnit="s" TheC="0" TheT="41" TheZ="0"/>
|
||||
<Plane DeltaT="15.625" DeltaTUnit="s" TheC="0" TheT="42" TheZ="0"/>
|
||||
<Plane DeltaT="15.891" DeltaTUnit="s" TheC="0" TheT="43" TheZ="0"/>
|
||||
<Plane DeltaT="16.157" DeltaTUnit="s" TheC="0" TheT="44" TheZ="0"/>
|
||||
<Plane DeltaT="16.422" DeltaTUnit="s" TheC="0" TheT="45" TheZ="0"/>
|
||||
<Plane DeltaT="16.688" DeltaTUnit="s" TheC="0" TheT="46" TheZ="0"/>
|
||||
<Plane DeltaT="16.953" DeltaTUnit="s" TheC="0" TheT="47" TheZ="0"/>
|
||||
<Plane DeltaT="17.219" DeltaTUnit="s" TheC="0" TheT="48" TheZ="0"/>
|
||||
<Plane DeltaT="17.485" DeltaTUnit="s" TheC="0" TheT="49" TheZ="0"/>
|
||||
</Pixels>
|
||||
</Image>
|
||||
</OME>
|
||||
Reference in New Issue
Block a user