From 527061561b8205eb8b81ca6c4da9a3a03ff8127d Mon Sep 17 00:00:00 2001 From: Carter Francis Date: Mon, 7 Oct 2024 16:24:33 -0500 Subject: [PATCH] Refactor:Fix Failing Tests --- rsciio/dm5/__init__.py | 1 - rsciio/dm5/_api.py | 481 ++++++++++++++++++++---------------- rsciio/tests/test_dm5.py | 38 ++- rsciio/tests/test_import.py | 4 +- 4 files changed, 298 insertions(+), 226 deletions(-) diff --git a/rsciio/dm5/__init__.py b/rsciio/dm5/__init__.py index 644ecfb0d..49230cbba 100644 --- a/rsciio/dm5/__init__.py +++ b/rsciio/dm5/__init__.py @@ -5,4 +5,3 @@ def __dir__(): return sorted(__all__) - diff --git a/rsciio/dm5/_api.py b/rsciio/dm5/_api.py index 7f0c464e5..64c8eed10 100644 --- a/rsciio/dm5/_api.py +++ b/rsciio/dm5/_api.py @@ -16,32 +16,29 @@ # You should have received a copy of the GNU General Public License # along with RosettaSciIO. If not, see . -import h5py import dask.array as da +import h5py import numpy as np from rsciio._docstrings import FILENAME_DOC, LAZY_DOC, RETURNS_DOC from rsciio.hspy._api import HyperspyWriter - # for some reason this is slightly different in dm4??? # Data Type 10: Integer? # data_types = { - np.short().dtype: [2, 2], # 2 byte integer signed + np.short().dtype: [2, 2], # 2 byte integer signed np.float32().dtype: [2, 4], # 4 byte real (IEEE 754) - - np.uint8().dtype: [6, 1], # 1 byte integer unsigned - np.int8().dtype: [6, 1], # 1 byte integer signed + np.uint8().dtype: [6, 1], # 1 byte integer unsigned + np.int8().dtype: [6, 1], # 1 byte integer signed np.int16().dtype: [1, 2], # 2 byte integer signed np.int32().dtype: [7, 4], # 4 byte integer signed np.uint32().dtype: [7, 4], # 4 byte integer signed - np.float64().dtype: [12, 8], # 8 byte real (IEEE 754) - np.uint16().dtype: [10,2], # 2 byte integer unsigned - np.complex64().dtype: [13, 8], # 8 byte complex - np.complex128().dtype: [13, 16], # 16 byte complex - - } + np.float64().dtype: [12, 8], # 8 byte real (IEEE 754) + np.uint16().dtype: [10, 2], # 2 byte integer unsigned + np.complex64().dtype: [13, 8], # 8 byte complex + np.complex128().dtype: [13, 16], # 16 byte complex +} class DM5: @@ -60,11 +57,12 @@ class DM5: """ - def __init__(self, file_path, mode='r'): + + def __init__(self, file_path, mode="r"): self.file = h5py.File(file_path, mode=mode) - if mode == 'r': - self.image_list = self.file["ImageList"] # list of images + if mode == "r": + self.image_list = self.file["ImageList"] # list of images else: self.image_list = self.file.create_group("ImageList") @@ -79,151 +77,192 @@ def write_sources(self, signal_dimensions, navigation_dimensions): self.file.create_group("ImageSourceList") self.file["ImageSourceList"].create_group("[0]") - if signal_dimensions==2 and navigation_dimensions ==2: # this is for 4D Data - self.file["ImageSourceList"]["[0]"].attrs.update({"ClassName":"ImageSource:4DSummed", # Need the right ClassName - "Do Sum": 1, # Sum the image? - "ImageRef":0, # Image to take the sum of... (Similar to Hyperspy) - "LayerFstEnd": 0, - "LayerFstStart": 0, - "LayerSndEnd": 0, - "LayerSndStart": 0, - "Summed Fst Dimension": 0, # Sum 1st dimension - "Summed Snd Dimension": 1, # Sum 2nd dimension - }) # data is reversed from usual in DM but not numpy + if signal_dimensions == 2 and navigation_dimensions == 2: # this is for 4D Data + self.file["ImageSourceList"]["[0]"].attrs.update( + { + "ClassName": "ImageSource:4DSummed", # Need the right ClassName + "Do Sum": 1, # Sum the image? + "ImageRef": 0, # Image to take the sum of... (Similar to Hyperspy) + "LayerFstEnd": 0, + "LayerFstStart": 0, + "LayerSndEnd": 0, + "LayerSndStart": 0, + "Summed Fst Dimension": 0, # Sum 1st dimension + "Summed Snd Dimension": 1, # Sum 2nd dimension + } + ) # data is reversed from usual in DM but not numpy self.file["ImageSourceList"]["[0]"].create_group("Id") self.file["ImageSourceList"]["[0]"]["Id"].attrs.update({"[0]": 0}) # Write the Document Objects similar to ROIs in Hyperspy for Visualization self.file.create_group("DocumentObjectList") self.file["DocumentObjectList"].create_group("[0]") - self.file["DocumentObjectList"]["[0]"].attrs.update({"AnnotationType": 20, # Create an image - 'ImageSource': 0, # - 'ImageDisplayType': 1, # 1 = Image - 'RangeAdjust': 1.0, - 'SparseSurvey_GridSize': 32, - 'SparseSurvey_NumberPixels': 64, # fast survey? - 'SparseSurvey_UseNumberPixels': 1, - 'SurveyTechnique': 2, - }) + self.file["DocumentObjectList"]["[0]"].attrs.update( + { + "AnnotationType": 20, # Create an image + "ImageSource": 0, # + "ImageDisplayType": 1, # 1 = Image + "RangeAdjust": 1.0, + "SparseSurvey_GridSize": 32, + "SparseSurvey_NumberPixels": 64, # fast survey? + "SparseSurvey_UseNumberPixels": 1, + "SurveyTechnique": 2, + } + ) self.file["DocumentObjectList"]["[0]"].create_group("ImageDisplayInfo") - self.file["DocumentObjectList"]["[0]"]["ImageDisplayInfo"].attrs.update({"EstimatedMin": 0, - "HiLimitContrastDeltaTriggerPercentage": 0, - }) - - - elif signal_dimensions == 1 and navigation_dimensions == 2: # Spectrum Image (Data might need to be reversed...) - self.file["ImageSourceList"]["[0]"].attrs.update({"ClassName": "ImageSource:Summed", - "Do Sum": 1, - "ImageRef": 0, - "LayerEnd": 0, - "LayerStart": 0, - "Summed Dimension": 2, - }) # Sum last dimension + self.file["DocumentObjectList"]["[0]"]["ImageDisplayInfo"].attrs.update( + { + "EstimatedMin": 0, + "HiLimitContrastDeltaTriggerPercentage": 0, + } + ) + + elif ( + signal_dimensions == 1 and navigation_dimensions == 2 + ): # Spectrum Image (Data might need to be reversed...) + self.file["ImageSourceList"]["[0]"].attrs.update( + { + "ClassName": "ImageSource:Summed", + "Do Sum": 1, + "ImageRef": 0, + "LayerEnd": 0, + "LayerStart": 0, + "Summed Dimension": 2, + } + ) # Sum last dimension self.file["ImageSourceList"]["[0]"].create_group("Id") self.file["ImageSourceList"]["[0]"]["Id"].attrs.update({"[0]": 0}) # Write the Document Objects similar to ROIs in Hyperspy for Visualization. self.file.create_group("DocumentObjectList") self.file["DocumentObjectList"].create_group("[0]") - self.file["DocumentObjectList"]["[0]"].attrs.update({"AnnotationType": 20, - 'ImageSource': 0, # Reference to the ImageSource - 'ImageDisplayType': 1, # 1 = Image? - 'IsMoveable': 1, - 'IsResizable': 1, - 'IsSelectable': 1, - 'IsTransferrable': 1, #sp? - 'IsTranslatable': 1, - 'IsVisible': 1, - 'UniqueID': 8, - }) - - - - elif signal_dimensions ==2 and navigation_dimensions==1: # Image Stack (In Situ) + self.file["DocumentObjectList"]["[0]"].attrs.update( + { + "AnnotationType": 20, + "ImageSource": 0, # Reference to the ImageSource + "ImageDisplayType": 1, # 1 = Image? + "IsMoveable": 1, + "IsResizable": 1, + "IsSelectable": 1, + "IsTransferrable": 1, # sp? + "IsTranslatable": 1, + "IsVisible": 1, + "UniqueID": 8, + } + ) + + elif ( + signal_dimensions == 2 and navigation_dimensions == 1 + ): # Image Stack (In Situ) self.file["ImageSourceList"]["[0]"].attrs.update( - {"ClassName": "ImageSource:Summed", # Need the right ImageSource! - "Do Sum": 1, # Sum the image? - "ImageRef": 0, # Image to take the sum of... (Similar to Hyperspy) - "LayerEnd": 0, - "LayerStart": 0, - "Summed Dimension": 2, # Sum last dimension? - }) + { + "ClassName": "ImageSource:Summed", # Need the right ImageSource! + "Do Sum": 1, # Sum the image? + "ImageRef": 0, # Image to take the sum of... (Similar to Hyperspy) + "LayerEnd": 0, + "LayerStart": 0, + "Summed Dimension": 2, # Sum last dimension? + } + ) self.file.attrs.update({"InImageMode": 1}) self.file["ImageSourceList"]["[0]"].create_group("Id") self.file["ImageSourceList"]["[0]"]["Id"].attrs.update({"[0]": 0}) self.file.create_group("DocumentObjectList") self.file["DocumentObjectList"].create_group("[0]") - self.file["DocumentObjectList"]["[0]"].attrs.update({"AnnotationType": 20, - 'ImageSource': 0, # Reference to the ImageSource - 'ImageDisplayType': 1, # 1 = Image? - 'IsMoveable': 1, - 'IsResizable': 1, - 'IsSelectable': 1, - 'IsTransferrable': 1, #sp? - 'IsTranslatable': 1, - 'IsVisible': 1, - 'UniqueID': 8, - }) + self.file["DocumentObjectList"]["[0]"].attrs.update( + { + "AnnotationType": 20, + "ImageSource": 0, # Reference to the ImageSource + "ImageDisplayType": 1, # 1 = Image? + "IsMoveable": 1, + "IsResizable": 1, + "IsSelectable": 1, + "IsTransferrable": 1, # sp? + "IsTranslatable": 1, + "IsVisible": 1, + "UniqueID": 8, + } + ) self.file["DocumentObjectList"]["[0]"].create_group("AnnotationGroupList") - self.file["DocumentObjectList"]["[0]"]["AnnotationGroupList"].create_group("[0]") - self.file["DocumentObjectList"]["[0]"]["AnnotationGroupList"]["[0]"].attrs.update({"AnnotationType": 23, - "Name": "SICursor", - "Rectangle": (0, 0, 1, 1), - }) + self.file["DocumentObjectList"]["[0]"]["AnnotationGroupList"].create_group( + "[0]" + ) + self.file["DocumentObjectList"]["[0]"]["AnnotationGroupList"][ + "[0]" + ].attrs.update( + { + "AnnotationType": 23, + "Name": "SICursor", + "Rectangle": (0, 0, 1, 1), + } + ) self.file["DocumentObjectList"]["[0]"].create_group("ImageDisplayInfo") - self.file["DocumentObjectList"]["[0]"]["ImageDisplayInfo"].attrs.update({"EstimatedMin": 0, - "HiLimitContrastDeltaTriggerPercentage": 0}) + self.file["DocumentObjectList"]["[0]"]["ImageDisplayInfo"].attrs.update( + {"EstimatedMin": 0, "HiLimitContrastDeltaTriggerPercentage": 0} + ) elif signal_dimensions == 1 and navigation_dimensions == 1: self.file["ImageSourceList"]["[0]"].attrs.update( - {"ClassName": "ImageSource:Summed", # Need the right ImageSource! - "Do Sum": 1, # Sum the image? - "ImageRef": 0, # Image to take the sum of... (Similar to Hyperspy) - "LayerEnd": 0, - "LayerStart": 0, - "Summed Dimension": 1, # Sum last dimension? - }) + { + "ClassName": "ImageSource:Summed", # Need the right ImageSource! + "Do Sum": 1, # Sum the image? + "ImageRef": 0, # Image to take the sum of... (Similar to Hyperspy) + "LayerEnd": 0, + "LayerStart": 0, + "Summed Dimension": 1, # Sum last dimension? + } + ) self.file["ImageSourceList"]["[0]"].create_group("Id") self.file["ImageSourceList"]["[0]"]["Id"].attrs.update({"[0]": 0}) self.file.create_group("DocumentObjectList") self.file["DocumentObjectList"].create_group("[0]") - self.file["DocumentObjectList"]["[0]"].attrs.update({"AnnotationType": 20, - 'ImageSource': 0, # Reference to the ImageSource - 'ImageDisplayType': 1, # 1 = Image? - 'RangeAdjust': 1.0, - 'IsMoveable': 1, - 'IsResizable': 1, - 'IsSelectable': 1, - 'IsTransferrable': 1, # sp? - 'IsTranslatable': 1, - 'IsVisible': 1, - 'UniqueID': 8, - }) + self.file["DocumentObjectList"]["[0]"].attrs.update( + { + "AnnotationType": 20, + "ImageSource": 0, # Reference to the ImageSource + "ImageDisplayType": 1, # 1 = Image? + "RangeAdjust": 1.0, + "IsMoveable": 1, + "IsResizable": 1, + "IsSelectable": 1, + "IsTransferrable": 1, # sp? + "IsTranslatable": 1, + "IsVisible": 1, + "UniqueID": 8, + } + ) else: self.file["ImageSourceList"]["[0]"].attrs.update( - {"ClassName": "ImageSource:Simple", # Need the right ImageSource! - "ImageRef": 0, # Reference to self (Usually 0 is a ??thumbnail?? which we don't write) - }) + { + "ClassName": "ImageSource:Simple", # Need the right ImageSource! + "ImageRef": 0, # Reference to self (Usually 0 is a ??thumbnail?? which we don't write) + } + ) self.file.create_group("DocumentObjectList") self.file["DocumentObjectList"].create_group("[0]") - self.file["DocumentObjectList"]["[0]"].attrs.update({"AnnotationType": 20, - 'ImageSource': 0, # Reference to the ImageSource - 'ImageDisplayType': 1, # 1 = Image? - 'RangeAdjust': 1.0, - 'IsMoveable': 1, - 'IsResizable': 1, - 'IsSelectable': 1, - 'IsTransferrable': 1, # sp? - 'IsTranslatable': 1, - 'IsVisible': 1, - 'UniqueID': 8, - }) + self.file["DocumentObjectList"]["[0]"].attrs.update( + { + "AnnotationType": 20, + "ImageSource": 0, # Reference to the ImageSource + "ImageDisplayType": 1, # 1 = Image? + "RangeAdjust": 1.0, + "IsMoveable": 1, + "IsResizable": 1, + "IsSelectable": 1, + "IsTransferrable": 1, # sp? + "IsTranslatable": 1, + "IsVisible": 1, + "UniqueID": 8, + } + ) def write_header_info(self): """ Just write the minimum "header" info to open the file in DM. """ - self.file.attrs.update({"InImageMode": 1}) # The rest of the attributes are for defining the window size... + self.file.attrs.update( + {"InImageMode": 1} + ) # The rest of the attributes are for defining the window size... def write_image_behavior(self): """ @@ -238,14 +277,14 @@ def read_images(self): for image_group in self.image_list.values(): self.images.append(Image(image_group, file=self.file)) - def write_image(self, data, axes_dicts, metadata =None, brightness=None): + def write_image(self, data, axes_dicts, metadata=None, brightness=None): """ Write an image to the DM5 file. """ previous_images = [int(k.strip("[ ]")) for k in self.image_list.keys()] if len(previous_images) == 0: - new_image_number = '[0]' + new_image_number = "[0]" else: new_image_number = f"[{max(previous_images) + 1}]" @@ -256,25 +295,29 @@ def write_image(self, data, axes_dicts, metadata =None, brightness=None): image = Image(self.image_list[new_image_number], file=self.file) self.images.append(image) - navigation_dimensions = len([axis for axis in axes_dicts if axis["navigate"] == True]) - signal_dimensions = len([axis for axis in axes_dicts if axis["navigate"] == False]) + navigation_dimensions = len([axis for axis in axes_dicts if axis["navigate"]]) + signal_dimensions = len([axis for axis in axes_dicts if not axis["navigate"]]) image.update_data(data) for axis, axis_dict in enumerate(axes_dicts): image.update_calibration(axis, **axis_dict) image.update_dimension(axis, axis_dict["size"]) - image.update_metadata(metadata, signal_dimensions=signal_dimensions, - navigation_dimensions=navigation_dimensions) + image.update_metadata( + metadata, + signal_dimensions=signal_dimensions, + navigation_dimensions=navigation_dimensions, + ) image.update_brightness(brightness) - self.write_sources(navigation_dimensions= navigation_dimensions, - signal_dimensions=signal_dimensions) + self.write_sources( + navigation_dimensions=navigation_dimensions, + signal_dimensions=signal_dimensions, + ) self.write_image_behavior() self.write_header_info() - class Image: """ The internal representation of an image in a DM5 file. @@ -289,9 +332,9 @@ class Image: """ def __init__(self, image_group, tags=None, unique_id=None, file=None): - self.image_data = image_group['ImageData'] - self.image_tags = image_group['ImageTags'] - self.unique_id = image_group['UniqueID'] + self.image_data = image_group["ImageData"] + self.image_tags = image_group["ImageTags"] + self.unique_id = image_group["UniqueID"] self.file = file def __str__(self): @@ -299,7 +342,7 @@ def __str__(self): @property def ndim(self): - return len(self.image_data['Data'].shape) + return len(self.image_data["Data"].shape) def signal_dimensions(self): """ @@ -316,12 +359,14 @@ def signal_dimensions(self): return 1 elif format == "Image": return 2 - else: # Now we just try to guess from the DocumentObjectList + else: # Now we just try to guess from the DocumentObjectList try: - class_name =self.file["ImageSourceList"]["[0]"].attrs["ClassName"] + class_name = self.file["ImageSourceList"]["[0]"].attrs["ClassName"] except KeyError: - raise KeyError("A Valid DM5 File needs to have an ImageSourceList with a " - "ClassName attribute to determine how the data should be displayed.") + raise KeyError( + "A Valid DM5 File needs to have an ImageSourceList with a " + "ClassName attribute to determine how the data should be displayed." + ) if self.ndim == 2 and class_name == "ImageSource:Simple": return 2 elif self.ndim == 1 and class_name == "ImageSource:Simple": @@ -329,10 +374,12 @@ def signal_dimensions(self): elif self.ndim == 4 and class_name == "ImageSource:4DSummed": return 2 else: + raise NotImplementedError( + "Determining the Type for unlabeled DM5 files. Please add the" + " 'Image', 'Spectrum', 'Spectrum Image' 'Diffraction image' tag to the" + "Meta Data.Format attribute in the ImageTags group." + ) - raise NotImplementedError("Determining the Type for unlabeled DM5 files. Please add the" - " 'Image', 'Spectrum', 'Spectrum Image' 'Diffraction image' tag to the" - "Meta Data.Format attribute in the ImageTags group.") def navigation_dimensions(self): return self.ndim - self.signal_dimensions() @@ -352,14 +399,17 @@ def get_axis_dict(self, axis): axis = self.ndim - axis - 1 navigate = axis >= self.signal_dimensions() - calibration_dict = dict(self.image_data['Calibrations']["Dimension"][f"[{axis}]"].attrs) - axis_dict = {"name": calibration_dict.get("Label", ""), - "offset": calibration_dict.get("Origin", 0), - "scale": calibration_dict.get("Scale", 1), - "units": calibration_dict.get("Units", ""), - "size": self._get_dimension(axis), - "navigate": navigate - } + calibration_dict = dict( + self.image_data["Calibrations"]["Dimension"][f"[{axis}]"].attrs + ) + axis_dict = { + "name": calibration_dict.get("Label", ""), + "offset": calibration_dict.get("Origin", 0), + "scale": calibration_dict.get("Scale", 1), + "units": calibration_dict.get("Units", ""), + "size": self._get_dimension(axis), + "navigate": navigate, + } return axis_dict @@ -368,25 +418,25 @@ def update_calibration(self, axis, name="", offset=0, scale=1, units="", **kwds) Add calibration data to the image for a given axis. """ axis = self.ndim - axis - 1 - if not "Calibrations" in self.image_data: + if "Calibrations" not in self.image_data: self.image_data.create_group("Calibrations") - if not "Dimension" in self.image_data['Calibrations']: - self.image_data['Calibrations'].create_group("Dimension") - if not f"[{axis}]" in self.image_data['Calibrations']["Dimension"]: - self.image_data['Calibrations']["Dimension"].create_group(f"[{axis}]") - if not isinstance(units, str): # Handle Undefined Units + if "Dimension" not in self.image_data["Calibrations"]: + self.image_data["Calibrations"].create_group("Dimension") + if f"[{axis}]" not in self.image_data["Calibrations"]["Dimension"]: + self.image_data["Calibrations"]["Dimension"].create_group(f"[{axis}]") + if not isinstance(units, str): # Handle Undefined Units units = "" if not isinstance(name, str): name = "" - self.image_data['Calibrations']["Dimension"][f"[{axis}]"].attrs.update({ - "Label": name, - "Origin": float(offset), - "Scale": float(scale), - "Units": units - }) - self.image_data['Calibrations'].attrs.update({"DisplayCalibratedUnits": 1}) - - + self.image_data["Calibrations"]["Dimension"][f"[{axis}]"].attrs.update( + { + "Label": name, + "Origin": float(offset), + "Scale": float(scale), + "Units": units, + } + ) + self.image_data["Calibrations"].attrs.update({"DisplayCalibratedUnits": 1}) def brightness(self): """ @@ -402,13 +452,10 @@ def update_brightness(self, brightness=None): Update the brightness of the image. """ if brightness is None: - brightness = {"Label": "b", - "Origin": 0, - "Scale": 1, - "Units": "b"} - if not "Calibrations" in self.image_data: + brightness = {"Label": "b", "Origin": 0, "Scale": 1, "Units": "b"} + if "Calibrations" not in self.image_data: self.image_data.create_group("Calibrations") - if not "Brightness" in self.image_data["Calibrations"]: + if "Brightness" not in self.image_data["Calibrations"]: self.image_data["Calibrations"].create_group("Brightness") self.image_data["Calibrations"]["Brightness"].attrs.update(brightness) @@ -422,17 +469,17 @@ def update_dimension(self, axis, length=None): """ axis = self.ndim - axis - 1 - if not "Dimensions" in self.image_data: + if "Dimensions" not in self.image_data: self.image_data.create_group("Dimensions") if length is None: length = self._get_dimension(axis) - self.image_data['Dimensions'].attrs.update({f"[{axis}]": length}) + self.image_data["Dimensions"].attrs.update({f"[{axis}]": length}) def _get_dimension(self, axis): try: - return self.image_data['Dimensions'].attrs[f"[{axis}]"] + return self.image_data["Dimensions"].attrs[f"[{axis}]"] except KeyError: - shape = self.image_data['Data'].shape + shape = self.image_data["Data"].shape return shape[axis] def get_data(self, lazy=False): @@ -440,20 +487,21 @@ def get_data(self, lazy=False): Get the image data. """ if lazy: - return da.from_array(self.image_data['Data']) + return da.from_array(self.image_data["Data"]) else: - return np.array(self.image_data['Data']) + return np.array(self.image_data["Data"]) def update_data(self, data): """ Update the image data. """ - HyperspyWriter.overwrite_dataset(self.image_data, - data, - "Data") - self.image_data.attrs.update({"DataType": data_types[data.dtype][0], - "PixelDepth": data_types[data.dtype][1]}) - + HyperspyWriter.overwrite_dataset(self.image_data, data, "Data") + self.image_data.attrs.update( + { + "DataType": data_types[data.dtype][0], + "PixelDepth": data_types[data.dtype][1], + } + ) def get_metadata(self): """ @@ -464,8 +512,7 @@ def get_metadata(self): metadata = {} metadata["General"] = {} - metadata["General"]["title"] = "" # DM uses the filename as the title - + metadata["General"]["title"] = "" # DM uses the filename as the title if "Acquisition" in original_metadata: metadata["Acquisition"] = original_metadata["Acquisition"] @@ -476,19 +523,24 @@ def get_metadata(self): metadata["Acquisition_instrument"] = {} metadata["Acquisition_instrument"]["TEM"] = {} metadata["Acquisition_instrument"]["TEM"]["beam_energy "] = ( - original_metadata["Microscope Info"].get("Voltage", 0)/1000) + original_metadata["Microscope Info"].get("Voltage", 0) / 1000 + ) metadata["Acquisition_instrument"]["TEM"]["acquisition_mode"] = ( - original_metadata["Microscope Info"].get("Illumination Mode", "Unknown")) + original_metadata["Microscope Info"].get("Illumination Mode", "Unknown") + ) metadata["Acquisition_instrument"]["TEM"]["magnification"] = ( - original_metadata["Microscope Info"].get("Indicated Magnification", 0)) + original_metadata["Microscope Info"].get("Indicated Magnification", 0) + ) metadata["Acquisition_instrument"]["TEM"]["camera_length"] = ( - original_metadata["Microscope Info"].get("STEM Camera Length", 0)) + original_metadata["Microscope Info"].get("STEM Camera Length", 0) + ) metadata["Acquisition_instrument"] = original_metadata["Microscope Info"] metadata["Acquisition_instrument"] = {} return metadata, original_metadata - - def update_metadata(self, metadata=None, signal_dimensions=None, navigation_dimensions=None): + def update_metadata( + self, metadata=None, signal_dimensions=None, navigation_dimensions=None + ): """ Update the metadata for the image. """ @@ -498,11 +550,13 @@ def update_metadata(self, metadata=None, signal_dimensions=None, navigation_dime formatted_metadata["Acquisition"] = {} formatted_metadata["Meta Data"] = {} - if signal_dimensions == 2 and navigation_dimensions == 2: # 4D Data + if signal_dimensions == 2 and navigation_dimensions == 2: # 4D Data formatted_metadata["Meta Data"]["Format"] = "Diffraction image" formatted_metadata["Meta Data"]["Acquisition Mode"] = "Parallel imaging" formatted_metadata["Meta Data"]["Experiment keywords"] = {} - formatted_metadata["Meta Data"]["Experiment keywords"]["[0]"] = "Label: Diffraction" + formatted_metadata["Meta Data"]["Experiment keywords"]["[0]"] = ( + "Label: Diffraction" + ) elif signal_dimensions == 1 and navigation_dimensions == 2: formatted_metadata["Meta Data"]["Format"] = "Spectrum image" elif signal_dimensions == 1 and navigation_dimensions == 1: @@ -528,7 +582,12 @@ def to_signal_dict(self): axes = [] for axis in range(len(data.shape)): axes.append(self.get_axis_dict(axis)) - return {"data": data, "metadata": metadata, "original_metadata": original_metadata, "axes": axes} + return { + "data": data, + "metadata": metadata, + "original_metadata": original_metadata, + "axes": axes, + } def dict2group(dictionary, group): @@ -542,6 +601,7 @@ def dict2group(dictionary, group): except TypeError: group.attrs[key] = "_Unsupported_" + def _group2dict(group, dictionary=None): if dictionary is None: dictionary = {} @@ -569,6 +629,7 @@ def _group2dict(group, dictionary=None): _group2dict(group[key], dictionary[key]) return dictionary + def file_reader(filename, lazy=False, **kwds): """ Read data from hdf5-files saved with the HyperSpy hdf5-format @@ -587,43 +648,43 @@ def file_reader(filename, lazy=False, **kwds): # in case blosc compression is used # module needs to be imported to register plugin import hdf5plugin # noqa: F401 - except ImportError: # pragma: no cover + except ImportError: # pragma: no cover pass - DM5_file = DM5(filename, mode='r') + DM5_file = DM5(filename, mode="r") DM5_file.read_images() if len(DM5_file.images) == 1: index = 0 - else:# len(DM5_file.images) > 1: - index = 1 # I think that the first image is a thumbnail... + else: # len(DM5_file.images) > 1: + index = 1 # I think that the first image is a thumbnail... metadata, original_metadata = DM5_file.images[index].get_metadata() axes = [] data = DM5_file.images[index].get_data(lazy=lazy) shape = data.shape for axis in range(len(shape)): axes.append(DM5_file.images[index].get_axis_dict(axis)) - return [{"data": data, "metadata": metadata, "original_metadata": original_metadata, "axes": axes},] + return [ + { + "data": data, + "metadata": metadata, + "original_metadata": original_metadata, + "axes": axes, + }, + ] + def file_writer(filename, signal): """ Write data to a HDF5 file. """ - axes = signal["axes"] # in array orde + axes = signal["axes"] # in array orde data = signal["data"] metadata = signal["metadata"] - DM5_file = DM5(filename, mode='w') + DM5_file = DM5(filename, mode="w") DM5_file.write_image(data, axes_dicts=axes, metadata=metadata) DM5_file.file.close() - file_reader.__doc__ %= (FILENAME_DOC, LAZY_DOC, RETURNS_DOC) - - - - - - - diff --git a/rsciio/tests/test_dm5.py b/rsciio/tests/test_dm5.py index 81d6ae567..7e3adc962 100644 --- a/rsciio/tests/test_dm5.py +++ b/rsciio/tests/test_dm5.py @@ -30,23 +30,35 @@ hs = pytest.importorskip("hyperspy.api", reason="hyperspy not installed") - TEST_DATA_PATH = Path(__file__).parent / "data" / "dm5" class TestDM5: - - - @pytest.mark.parametrize("navigation_dimension", [0,1, 2]) + @pytest.mark.parametrize("navigation_dimension", [0, 1, 2]) @pytest.mark.parametrize("signal_dimension", [1, 2]) - @pytest.mark.parametrize("dtype", [np.uint8, np.uint16, np.float32, np.float64, np.complex128, - np.int8, np.int16, np.int32,]) - def test_save_load_files(self, navigation_dimension, signal_dimension, dtype, tmp_path): - - fname = tmp_path / f"test_save_files_nav{navigation_dimension}_sig{signal_dimension}_{dtype().dtype}.dm5" + @pytest.mark.parametrize( + "dtype", + [ + np.uint8, + np.uint16, + np.float32, + np.float64, + np.complex128, + np.int8, + np.int16, + np.int32, + ], + ) + def test_save_load_files( + self, navigation_dimension, signal_dimension, dtype, tmp_path + ): + fname = ( + tmp_path + / f"test_save_files_nav{navigation_dimension}_sig{signal_dimension}_{dtype().dtype}.dm5" + ) dim = navigation_dimension + signal_dimension - data_shape = [10,11,12,13,14][:dim] + data_shape = [10, 11, 12, 13, 14][:dim] data = np.ones(data_shape, dtype=dtype) if signal_dimension == 1: signal = hs.signals.Signal1D(data) @@ -55,8 +67,8 @@ def test_save_load_files(self, navigation_dimension, signal_dimension, dtype, tm names = ["a", "b", "c", "d", "e"] for i in range(dim): ax = signal.axes_manager[i] - ax.name = names[i]+ str(ax.size) - ax.units = names[i] +" nm" + ax.name = names[i] + str(ax.size) + ax.units = names[i] + " nm" ax.scale = 0.1 original = [signal.axes_manager[i].name for i in range(dim)] signal.save(fname, overwrite=True) @@ -70,4 +82,4 @@ def test_save_load_files(self, navigation_dimension, signal_dimension, dtype, tm assert s.axes_manager[i].name == original[i] assert "nm" in s.axes_manager[i].units assert s.axes_manager[i].scale == 0.1 - assert s.axes_manager[i].size == int(original[i][-2:]) \ No newline at end of file + assert s.axes_manager[i].size == int(original[i][-2:]) diff --git a/rsciio/tests/test_import.py b/rsciio/tests/test_import.py index dbe849fb1..1ca02f548 100644 --- a/rsciio/tests/test_import.py +++ b/rsciio/tests/test_import.py @@ -44,7 +44,7 @@ def test_import_all(): # Remove plugins which require not installed optional dependencies h5py = importlib.util.find_spec("h5py") if h5py is None: - plugin_name_to_remove.extend(["EMD", "HSPY", "NeXus"]) + plugin_name_to_remove.extend(["EMD", "HSPY", "NeXus", "DM5"]) imageio = importlib.util.find_spec("imageio") if imageio is None: @@ -123,7 +123,7 @@ def test_dir_plugins(plugin): pytest.importorskip("pyUSID") elif plugin["name"] == "ZSPY": pytest.importorskip("zarr") - elif plugin["name"] in ["EMD", "HSPY", "NeXus"]: + elif plugin["name"] in ["EMD", "HSPY", "NeXus", "DM5"]: pytest.importorskip("h5py") plugin_module = importlib.import_module(plugin_string)