-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add file type: .topostats #53
Merged
Merged
Changes from 9 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
689de88
Add hdf5 unpacker
SylviaWhittle 08f5457
Add h5py as a dependency
SylviaWhittle 7339f54
Add tests for hdf5 unpacking
SylviaWhittle e6f69f2
Add topostats loader
SylviaWhittle bdad4cc
Add test for loading topostats files
SylviaWhittle 25041a2
Add test file for topostats loading test
SylviaWhittle 3131efc
Update README with .topostats documentation
SylviaWhittle 0fdb9cc
Replaced 2nd .ibw in notebook with .topostats
MaxGamill-Sheffield b849675
[pre-commit.ci] Fixing issues with pre-commit
pre-commit-ci[bot] 848fe9d
Improve docs for unpack hdf5
SylviaWhittle 157ab92
Improve naming of topostats test file to reflect version 0.1
SylviaWhittle 11ae337
Add filename to topostats file version logging line
SylviaWhittle 0145511
Fix loading the renamed test file
SylviaWhittle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a big issue and certainly not something to do here but we could move the comments in the cells out to Markdown cells. |
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.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps include
filename
as is done in thelogger.error()
below so people know what file is which version?