Skip to content

Commit 06ea314

Browse files
committed
Testing:Expand Test coverage
1 parent 5270615 commit 06ea314

File tree

2 files changed

+107
-40
lines changed

2 files changed

+107
-40
lines changed

rsciio/dm5/_api.py

Lines changed: 65 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,6 @@ def __init__(self, image_group, tags=None, unique_id=None, file=None):
337337
self.unique_id = image_group["UniqueID"]
338338
self.file = file
339339

340-
def __str__(self):
341-
return f"Image: {self.image_data['Data'].shape}"
342-
343340
@property
344341
def ndim(self):
345342
return len(self.image_data["Data"].shape)
@@ -362,7 +359,7 @@ def signal_dimensions(self):
362359
else: # Now we just try to guess from the DocumentObjectList
363360
try:
364361
class_name = self.file["ImageSourceList"]["[0]"].attrs["ClassName"]
365-
except KeyError:
362+
except KeyError: # pragma: no cover
366363
raise KeyError(
367364
"A Valid DM5 File needs to have an ImageSourceList with a "
368365
"ClassName attribute to determine how the data should be displayed."
@@ -380,9 +377,6 @@ def signal_dimensions(self):
380377
"Meta Data.Format attribute in the ImageTags group."
381378
)
382379

383-
def navigation_dimensions(self):
384-
return self.ndim - self.signal_dimensions()
385-
386380
def get_axis_dict(self, axis):
387381
"""
388382
Get the calibration data for a given axis.
@@ -441,10 +435,15 @@ def update_calibration(self, axis, name="", offset=0, scale=1, units="", **kwds)
441435
def brightness(self):
442436
"""
443437
Get the brightness of the image.
438+
439+
Note
440+
----
441+
Hyperspy has a rudimentary concept of brightness This currently doesn't do anything but should be implemented
442+
in the future.
444443
"""
445-
try:
444+
try: # pragma: no cover
446445
return dict(self.image_data["Calibrations"]["Brightness"].attrs)
447-
except KeyError:
446+
except KeyError: # pragma: no cover
448447
return {}
449448

450449
def update_brightness(self, brightness=None):
@@ -463,9 +462,13 @@ def update_dimension(self, axis, length=None):
463462
"""
464463
Update the dimension of the image for a given axis.
465464
466-
This is two places in the DM5 file???
467-
468-
Under Calibrations and under Dimension. I think that only the Calibrations should be updated.
465+
Parameters
466+
----------
467+
axis : int
468+
The axis to update the dimension for (Starting from 0 in array order). This will be reversed to match DM's
469+
axis order.
470+
length : int, optional
471+
The length of the axis.
469472
470473
"""
471474
axis = self.ndim - axis - 1
@@ -478,13 +481,19 @@ def update_dimension(self, axis, length=None):
478481
def _get_dimension(self, axis):
479482
try:
480483
return self.image_data["Dimensions"].attrs[f"[{axis}]"]
481-
except KeyError:
484+
except KeyError: # pragma: no cover
482485
shape = self.image_data["Data"].shape
483486
return shape[axis]
484487

485488
def get_data(self, lazy=False):
486489
"""
487490
Get the image data.
491+
492+
Parameters
493+
----------
494+
lazy : bool, optional
495+
Whether to return a dask array or a numpy
496+
488497
"""
489498
if lazy:
490499
return da.from_array(self.image_data["Data"])
@@ -494,6 +503,11 @@ def get_data(self, lazy=False):
494503
def update_data(self, data):
495504
"""
496505
Update the image data.
506+
507+
Parameters
508+
----------
509+
data : np.ndarray or da.Array
510+
The new image data.
497511
"""
498512
HyperspyWriter.overwrite_dataset(self.image_data, data, "Data")
499513
self.image_data.attrs.update(
@@ -522,7 +536,7 @@ def get_metadata(self):
522536
if "Microscope Info" in original_metadata:
523537
metadata["Acquisition_instrument"] = {}
524538
metadata["Acquisition_instrument"]["TEM"] = {}
525-
metadata["Acquisition_instrument"]["TEM"]["beam_energy "] = (
539+
metadata["Acquisition_instrument"]["TEM"]["beam_energy"] = (
526540
original_metadata["Microscope Info"].get("Voltage", 0) / 1000
527541
)
528542
metadata["Acquisition_instrument"]["TEM"]["acquisition_mode"] = (
@@ -534,18 +548,28 @@ def get_metadata(self):
534548
metadata["Acquisition_instrument"]["TEM"]["camera_length"] = (
535549
original_metadata["Microscope Info"].get("STEM Camera Length", 0)
536550
)
537-
metadata["Acquisition_instrument"] = original_metadata["Microscope Info"]
538-
metadata["Acquisition_instrument"] = {}
539551
return metadata, original_metadata
540552

541553
def update_metadata(
542554
self, metadata=None, signal_dimensions=None, navigation_dimensions=None
543555
):
544556
"""
545557
Update the metadata for the image.
558+
559+
Parameters
560+
----------
561+
metadata : dict, optional
562+
The metadata to update.
563+
signal_dimensions : int, optional
564+
The number of signal dimensions.
565+
navigation_dimensions : int, optional
566+
The number of navigation dimensions.
546567
"""
547568
if metadata is None:
548569
metadata = {}
570+
if navigation_dimensions is None and signal_dimensions is None:
571+
signal_dimensions = self.ndim
572+
navigation_dimensions = 0
549573
formatted_metadata = {}
550574

551575
formatted_metadata["Acquisition"] = {}
@@ -569,26 +593,35 @@ def update_metadata(
569593
if navigation_dimensions > 0:
570594
formatted_metadata["Meta Data"]["IsSequence"] = "true"
571595
dict2group(formatted_metadata, self.image_tags)
596+
597+
# Update Microscope Info
598+
if (
599+
"Acquisition_instrument" in metadata
600+
and "TEM" in metadata["Acquisition_instrument"]
601+
):
602+
self.image_tags.create_group("Microscope Info")
603+
microscope_info_dict = {
604+
"Voltage": metadata["Acquisition_instrument"]["TEM"].get(
605+
"beam_energy", 0
606+
)
607+
* 1000,
608+
"Illumination Mode": metadata["Acquisition_instrument"]["TEM"].get(
609+
"acquisition_mode", "Unknown"
610+
),
611+
"Indicated Magnification": metadata["Acquisition_instrument"][
612+
"TEM"
613+
].get("magnification", 0),
614+
"STEM Camera Length": metadata["Acquisition_instrument"]["TEM"].get(
615+
"camera_length", 0
616+
),
617+
}
618+
619+
dict2group(microscope_info_dict, self.image_tags["Microscope Info"])
620+
572621
self.image_tags.create_group("UserTags")
573622
dict2group(metadata, self.image_tags["UserTags"])
574623
return
575624

576-
def to_signal_dict(self):
577-
"""
578-
Convert the image to a Hyperspy signal dictionary.
579-
"""
580-
data = self.get_data()
581-
metadata, original_metadata = self.get_metadata()
582-
axes = []
583-
for axis in range(len(data.shape)):
584-
axes.append(self.get_axis_dict(axis))
585-
return {
586-
"data": data,
587-
"metadata": metadata,
588-
"original_metadata": original_metadata,
589-
"axes": axes,
590-
}
591-
592625

593626
def dict2group(dictionary, group):
594627
for key, value in dictionary.items():
@@ -611,16 +644,8 @@ def _group2dict(group, dictionary=None):
611644
value = value.decode()
612645
except UnicodeDecodeError:
613646
value = "Decoding error"
614-
if isinstance(value, (np.bytes_, str)):
615-
if value == "_None_":
616-
value = None
617647
elif isinstance(value, np.bool_):
618648
value = bool(value)
619-
elif isinstance(value, np.ndarray) and value.dtype.char == "S":
620-
# Convert strings to unicode
621-
value = value.astype("U")
622-
if value.dtype.str.endswith("U1"):
623-
value = value.tolist()
624649
dictionary[key] = value
625650

626651
if not isinstance(group, h5py.Dataset):

rsciio/tests/test_dm5.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from pathlib import Path
2626

27+
import dask.array as da
2728
import numpy as np
2829
import pytest
2930

@@ -83,3 +84,44 @@ def test_save_load_files(
8384
assert "nm" in s.axes_manager[i].units
8485
assert s.axes_manager[i].scale == 0.1
8586
assert s.axes_manager[i].size == int(original[i][-2:])
87+
88+
def test_save_load_undefined_axes(self, tmp_path):
89+
fname = tmp_path / "test_save_undefined.dm5"
90+
91+
data_shape = [10, 11, 12, 13]
92+
data = np.ones(data_shape, dtype=np.float32)
93+
signal = hs.signals.Signal2D(data)
94+
signal.save(fname, overwrite=True)
95+
s = hs.load(fname)
96+
for i in range(4):
97+
assert s.axes_manager[i].name == ""
98+
assert s.axes_manager[i].units == ""
99+
100+
def test_save_load_metadata(self, tmp_path):
101+
fname = tmp_path / "test_save_undefined.dm5"
102+
103+
data_shape = [10, 11, 12, 13]
104+
data = np.ones(data_shape, dtype=np.float32)
105+
signal = hs.signals.Signal2D(data)
106+
signal.metadata.General.title = "test"
107+
signal.metadata.add_node("Acquisition_instrument.TEM")
108+
signal.metadata.General["test"] = "test".encode()
109+
signal.metadata.Acquisition_instrument.TEM.beam_energy = 200
110+
signal.metadata.Acquisition_instrument.TEM.magnification = 100
111+
signal.metadata.Acquisition_instrument.TEM.camera_length = 10
112+
113+
signal.save(fname, overwrite=True)
114+
s = hs.load(fname)
115+
assert s.metadata.Acquisition_instrument.TEM.beam_energy == 200
116+
assert s.metadata.Acquisition_instrument.TEM.camera_length == 10
117+
assert s.metadata.Acquisition_instrument.TEM.magnification == 100
118+
119+
def test_save_load_lazy(self, tmp_path):
120+
fname = tmp_path / "test_save_lazy.dm5"
121+
122+
data_shape = [10, 11, 12, 13]
123+
data = np.ones(data_shape, dtype=np.float32)
124+
signal = hs.signals.Signal2D(data)
125+
signal.save(fname, overwrite=True)
126+
s = hs.load(fname, lazy=True)
127+
assert isinstance(s.data, da.Array)

0 commit comments

Comments
 (0)