-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #53 from AFM-SPM/SylviaWhittle/51-topostats-file-f…
…ormat Add file type: .topostats
- Loading branch information
Showing
8 changed files
with
350 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
"""For decoding and loading .topostats (HDF5 format) AFM file format into Python Nympy arrays.""" | ||
|
||
from __future__ import annotations | ||
from pathlib import Path | ||
|
||
import h5py | ||
|
||
from AFMReader.logging import logger | ||
from AFMReader.io import unpack_hdf5 | ||
|
||
logger.enable(__package__) | ||
|
||
|
||
def load_topostats(file_path: Path | str) -> tuple: | ||
""" | ||
Extract image and pixel to nm scaling from the .topostats (HDF5 format) file. | ||
Parameters | ||
---------- | ||
file_path : Path or str | ||
Path to the .topostats file. | ||
Returns | ||
------- | ||
tuple(np.ndarray, float) | ||
A tuple containing the image, its pixel to nm scaling factor and the data dictionary | ||
containing all the extra image data and metadata in dictionary format. | ||
Raises | ||
------ | ||
OSError | ||
If the file is not found. | ||
Examples | ||
-------- | ||
>>> image, pixel_to_nm_scaling = load_topostats("path/to/topostats_file.topostats") | ||
""" | ||
logger.info(f"Loading image from : {file_path}") | ||
file_path = Path(file_path) | ||
filename = file_path.stem | ||
try: | ||
with h5py.File(file_path, "r") as f: | ||
data = unpack_hdf5(open_hdf5_file=f, group_path="/") | ||
|
||
file_version = data["topostats_file_version"] | ||
logger.info(f"[{filename}] TopoStats file version : {file_version}") | ||
image = data["image"] | ||
pixel_to_nm_scaling = data["pixel_to_nm_scaling"] | ||
|
||
except OSError as e: | ||
if "Unable to open file" in str(e): | ||
logger.error(f"[{filename}] File not found : {file_path}") | ||
raise e | ||
|
||
return (image, pixel_to_nm_scaling, data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ dependencies = [ | |
"tifffile", | ||
"pySPM", | ||
"loguru", | ||
"h5py", | ||
] | ||
|
||
[project.optional-dependencies] | ||
|
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
"""Test the reading and writing of data from / to files.""" | ||
|
||
from pathlib import Path | ||
|
||
import numpy as np | ||
import h5py | ||
|
||
|
||
from AFMReader.io import unpack_hdf5 | ||
|
||
|
||
def test_unpack_hdf5_all_together_group_path_default(tmp_path: Path) -> None: | ||
"""Test loading a nested dictionary with arrays from HDF5 format with group path as default.""" | ||
to_save = { | ||
"a": 1, | ||
"b": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), | ||
"c": "test", | ||
"d": {"e": 1, "f": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), "g": "test"}, | ||
} | ||
|
||
group_path = "/" | ||
|
||
# Manually save the dictionary to HDF5 format | ||
with h5py.File(tmp_path / "hdf5_file_nested_with_arrays_group_path_standard.hdf5", "w") as f: | ||
# Write the datasets and groups to the file without using the dict_to_hdf5 function | ||
f.create_dataset("a", data=to_save["a"]) | ||
f.create_dataset("b", data=to_save["b"]) | ||
f.create_dataset("c", data=to_save["c"]) | ||
d = f.create_group("d") | ||
d.create_dataset("e", data=to_save["d"]["e"]) | ||
d.create_dataset("f", data=to_save["d"]["f"]) | ||
d.create_dataset("g", data=to_save["d"]["g"]) | ||
|
||
# Load it back in and check if the dictionary is the same | ||
with h5py.File(tmp_path / "hdf5_file_nested_with_arrays_group_path_standard.hdf5", "r") as f: | ||
result = unpack_hdf5(open_hdf5_file=f, group_path=group_path) | ||
|
||
np.testing.assert_equal(result, to_save) | ||
|
||
|
||
def test_unpack_hdf5_all_together_group_path_non_standard(tmp_path: Path) -> None: | ||
"""Test loading a nested dictionary with arrays from HDF5 format with a non-standard group path.""" | ||
to_save = { | ||
"a": 1, | ||
"b": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), | ||
"c": "test", | ||
"d": {"e": 1, "f": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), "g": "test"}, | ||
} | ||
|
||
group_path = "/d/" | ||
|
||
expected = { | ||
"e": 1, | ||
"f": np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), | ||
"g": "test", | ||
} | ||
|
||
# Manually save the dictionary to HDF5 format | ||
with h5py.File(tmp_path / "hdf5_file_all_together_group_path_nonstandard.hdf5", "w") as f: | ||
# Write the datasets and groups to the file without using the dict_to_hdf5 function | ||
f.create_dataset("a", data=to_save["a"]) | ||
f.create_dataset("b", data=to_save["b"]) | ||
f.create_dataset("c", data=to_save["c"]) | ||
d = f.create_group("d") | ||
d.create_dataset("e", data=to_save["d"]["e"]) | ||
d.create_dataset("f", data=to_save["d"]["f"]) | ||
d.create_dataset("g", data=to_save["d"]["g"]) | ||
|
||
# Load it back in and check if the dictionary is the same | ||
with h5py.File(tmp_path / "hdf5_file_all_together_group_path_nonstandard.hdf5", "r") as f: | ||
result = unpack_hdf5(open_hdf5_file=f, group_path=group_path) | ||
|
||
np.testing.assert_equal(result, expected) | ||
|
||
|
||
def test_unpack_hdf5_int(tmp_path: Path) -> None: | ||
"""Test loading a dictionary with an integer from HDF5 format.""" | ||
to_save = {"a": 1, "b": 2} | ||
|
||
group_path = "/" | ||
|
||
# Manually save the dictionary to HDF5 format | ||
with h5py.File(tmp_path / "hdf5_file_int.hdf5", "w") as f: | ||
# Write the datasets and groups to the file without using the dict_to_hdf5 function | ||
f.create_dataset("a", data=to_save["a"]) | ||
f.create_dataset("b", data=to_save["b"]) | ||
|
||
# Load it back in and check if the dictionary is the same | ||
with h5py.File(tmp_path / "hdf5_file_int.hdf5", "r") as f: | ||
result = unpack_hdf5(open_hdf5_file=f, group_path=group_path) | ||
|
||
np.testing.assert_equal(result, to_save) | ||
|
||
|
||
def test_unpack_hdf5_float(tmp_path: Path) -> None: | ||
"""Test loading a dictionary with a float from HDF5 format.""" | ||
to_save = {"a": 0.01, "b": 0.02} | ||
|
||
group_path = "/" | ||
|
||
# Manually save the dictionary to HDF5 format | ||
with h5py.File(tmp_path / "hdf5_file_float.hdf5", "w") as f: | ||
# Write the datasets and groups to the file without using the dict_to_hdf5 function | ||
f.create_dataset("a", data=to_save["a"]) | ||
f.create_dataset("b", data=to_save["b"]) | ||
|
||
# Load it back in and check if the dictionary is the same | ||
with h5py.File(tmp_path / "hdf5_file_float.hdf5", "r") as f: | ||
result = unpack_hdf5(open_hdf5_file=f, group_path=group_path) | ||
|
||
np.testing.assert_equal(result, to_save) | ||
|
||
|
||
def test_unpack_hdf5_str(tmp_path: Path) -> None: | ||
"""Test loading a dictionary with a string from HDF5 format.""" | ||
to_save = {"a": "test", "b": "test2"} | ||
|
||
group_path = "/" | ||
|
||
# Manually save the dictionary to HDF5 format | ||
with h5py.File(tmp_path / "hdf5_file_str.hdf5", "w") as f: | ||
# Write the datasets and groups to the file without using the dict_to_hdf5 function | ||
f.create_dataset("a", data=to_save["a"]) | ||
f.create_dataset("b", data=to_save["b"]) | ||
|
||
# Load it back in and check if the dictionary is the same | ||
with h5py.File(tmp_path / "hdf5_file_str.hdf5", "r") as f: | ||
result = unpack_hdf5(open_hdf5_file=f, group_path=group_path) | ||
|
||
np.testing.assert_equal(result, to_save) | ||
|
||
|
||
def test_unpack_hdf5_dict_nested_dict(tmp_path: Path) -> None: | ||
"""Test loading a nested dictionary from HDF5 format.""" | ||
to_save = { | ||
"a": 1, | ||
"b": 2, | ||
"c": {"d": 3, "e": 4}, | ||
} | ||
|
||
group_path = "/" | ||
|
||
# Manually save the dictionary to HDF5 format | ||
with h5py.File(tmp_path / "hdf5_file_nested_dict.hdf5", "w") as f: | ||
# Write the datasets and groups to the file without using the dict_to_hdf5 function | ||
f.create_dataset("a", data=to_save["a"]) | ||
f.create_dataset("b", data=to_save["b"]) | ||
c = f.create_group("c") | ||
c.create_dataset("d", data=to_save["c"]["d"]) | ||
c.create_dataset("e", data=to_save["c"]["e"]) | ||
|
||
# Load it back in and check if the dictionary is the same | ||
with h5py.File(tmp_path / "hdf5_file_nested_dict.hdf5", "r") as f: | ||
result = unpack_hdf5(open_hdf5_file=f, group_path=group_path) | ||
|
||
np.testing.assert_equal(result, to_save) | ||
|
||
|
||
def test_unpack_hdf5_nested_dict_group_path(tmp_path: Path) -> None: | ||
"""Test loading a nested dictionary from HDF5 format with a non-standard group path.""" | ||
to_save = { | ||
"a": 1, | ||
"b": 2, | ||
"c": {"d": 3, "e": 4}, | ||
} | ||
|
||
group_path = "/c/" | ||
|
||
expected = { | ||
"d": 3, | ||
"e": 4, | ||
} | ||
|
||
# Manually save the dictionary to HDF5 format | ||
with h5py.File(tmp_path / "hdf5_file_nested_dict_group_path.hdf5", "w") as f: | ||
# Write the datasets and groups to the file without using the dict_to_hdf5 function | ||
f.create_dataset("a", data=to_save["a"]) | ||
f.create_dataset("b", data=to_save["b"]) | ||
c = f.create_group("c") | ||
c.create_dataset("d", data=to_save["c"]["d"]) | ||
c.create_dataset("e", data=to_save["c"]["e"]) | ||
|
||
# Load it back in and check if the dictionary is the same | ||
with h5py.File(tmp_path / "hdf5_file_nested_dict_group_path.hdf5", "r") as f: | ||
result = unpack_hdf5(open_hdf5_file=f, group_path=group_path) | ||
|
||
np.testing.assert_equal(result, expected) |
Oops, something went wrong.