diff --git a/ncempy/data/au_145mm_68kx_microprobe_01.h5 b/ncempy/data/au_145mm_68kx_microprobe_01.h5
new file mode 100755
index 0000000..53d8730
Binary files /dev/null and b/ncempy/data/au_145mm_68kx_microprobe_01.h5 differ
diff --git a/ncempy/data/au_145mm_68kx_microprobe_01_data_000001.h5 b/ncempy/data/au_145mm_68kx_microprobe_01_data_000001.h5
new file mode 100755
index 0000000..b731bde
Binary files /dev/null and b/ncempy/data/au_145mm_68kx_microprobe_01_data_000001.h5 differ
diff --git a/ncempy/data/au_145mm_68kx_microprobe_01_master.h5 b/ncempy/data/au_145mm_68kx_microprobe_01_master.h5
new file mode 100755
index 0000000..cd3c4e1
Binary files /dev/null and b/ncempy/data/au_145mm_68kx_microprobe_01_master.h5 differ
diff --git a/ncempy/io/__init__.py b/ncempy/io/__init__.py
index fbc954a..8bfc32d 100644
--- a/ncempy/io/__init__.py
+++ b/ncempy/io/__init__.py
@@ -1,12 +1,12 @@
+from pathlib import Path
+
from . import dm
from . import ser
from . import emd
from . import mrc
from . import emdVelox
from . import smv
-
-from pathlib import Path
-
+from . import dectris
def read(filename, dsetNum=0):
"""
@@ -40,11 +40,27 @@ def read(filename, dsetNum=0):
out = ser.serReader(filename)
elif suffix in ('.dm3', '.dm4'):
out = dm.dmReader(filename)
- elif suffix in ('.emd', '.h5', '.hdf5'):
+ elif suffix in ('.h5', '.hdf5'):
+ try:
+ # Try Berkeley EMD
+ out = emd.emdReader(filename, dsetNum)
+ except emd.NoEmdDataSets:
+ # Try Dectris Arina
+ out = dectris.dectrisReader(filename)
+ except:
+ print('ncempy.read: Unknown H5 file.')
+ raise
+ elif suffix == '.emd':
try:
+ # Try Berkeley EMD
out = emd.emdReader(filename, dsetNum)
except emd.NoEmdDataSets:
- out = emdVelox.emdVeloxReader(filename, dsetNum)
+ # Try Velox EMD
+ try:
+ out = emdVelox.emdVeloxReader(filename, dsetNum)
+ except KeyError:
+ print('ncempy.read: Unknown EMD file.')
+ raise
elif suffix in ('.mrc', '.rec', '.st', '.ali'):
out = mrc.mrcReader(filename)
elif suffix in ('.smv', '.img'):
diff --git a/ncempy/io/dectris.py b/ncempy/io/dectris.py
new file mode 100644
index 0000000..6ec6b0c
--- /dev/null
+++ b/ncempy/io/dectris.py
@@ -0,0 +1,198 @@
+"""
+This module provides an interface to Dectris Arina data sets
+"""
+
+from pathlib import Path
+import h5py
+import numpy as np
+import hdf5plugin
+
+class fileDECTRIS:
+ """ Class to represent Dectris Arina data sets
+
+ Attributes
+ ----------
+ raw_shape : list
+ The shape of the raw data. This is three-dimensional: [num_frames, frameY, frameX].
+ data_shape : list
+ The four-dimensional shape of the dataset. By default, the
+ scanned region is square.
+ file_hdl : h5py.File
+ The h5py file handle which provides direct access to the underlying hdf5 file structure.
+ data_type : numpy.dtype
+ The data type of the values in the data set.
+ """
+ def __init__(self, filename, bad_pixels=None, verbose=False):
+ """ Initialize a data set by opening the master file and determining the file size
+
+ Parameters
+ ----------
+ filename : str or pathlib.Path or file object
+ The HDF5 master file to open.
+ verbose : bool, default False
+ If True, prints out debugging information
+ """
+
+ self._verbose = verbose
+ self.raw_shape = [0, 0, 0] # shape of data on disk
+ self.data_shape = [0, 0, 0, 0] # the shape of the final 4D dataset
+ self.file_hdl = None
+ self.data_dtype = None
+ self.bad_pixel_value = bad_pixels
+
+ # Pixels to remove automatically
+ # self.bad_pixels = ((49, 75), (93,118), (95,119), (108, 57)) # NCEM bad pixels
+
+ if hasattr(filename, 'read'):
+ try:
+ self.file_path = Path(filename.name)
+ self.file_name = self.file_path.name
+ except AttributeError:
+ self.file_path = None
+ self.file_name = None
+ else:
+ # check filename type, change to pathlib.Path
+ if isinstance(filename, str):
+ filename = Path(filename)
+ elif isinstance(filename, Path):
+ pass
+ else:
+ raise TypeError('Filename is supposed to be a string or pathlib.Path or file object')
+ self.file_path = Path(filename)
+ self.file_name = self.file_path.name
+
+ # Try opening the file
+ try:
+ self.file_hdl = h5py.File(filename, 'r')
+ assert self.file_hdl['/entry/data']
+ except:
+ print('Error opening file: "{}"'.format(filename))
+ raise
+
+ # if this is a HDF5 file
+ if self.file_hdl:
+ # Find the initial shape of the data set
+ for v in self.file_hdl['/entry/data'].values():
+ self.raw_shape[0] = self.raw_shape[0] + v.shape[0]
+ self.raw_shape[1] = v.shape[1]
+ self.raw_shape[2] = v.shape[2]
+ self.data_dtype = v.dtype
+
+ def __del__(self):
+ """ Destructor for EMD file object.
+
+ """
+ # close the file
+ # if(not self.file_hdl.closed):
+ self.file_hdl.close()
+
+ def __enter__(self):
+ """Implement python's with statement for context managers.
+
+ """
+ return self
+
+ def __exit__(self, exception_type, exception_value, traceback):
+ """Implement python's with statement fr context managers.
+ and close the file via __del__()
+ """
+ self.__del__()
+ return None
+
+ def getDataset(self, remove_bad_pixels=False, assume_shape=None):
+ """ Read the data from the HDF5 files
+
+ Parameters
+ ----------
+ remove_bad_pixels : bool, default False
+ If True, _remove_bad_pixels function is called after the data is loaded.
+ assume_shape : tuple, optional
+ If this is set, then this tuple is used as the scanning shape overriding
+ the assumption of a square real space scanning grid
+ """
+ # Pre allocate space
+ data = np.zeros(self.raw_shape, dtype=self.data_dtype)
+ # Read in the data in all linked files
+ ii = 0
+ for v in self.file_hdl['/entry/data'].values():
+ data[ii:ii+v.shape[0]] = v[:]
+ ii += v.shape[0]
+
+ if assume_shape:
+ self.data_shape = (assume_shape[0], assume_shape[1],
+ data.shape[1], data.shape[2])
+ else:
+ # Reshape assuming square
+ shape_square = int((data.shape[0])**0.5)
+ assert data.shape[0] == shape_square**2
+ self.data_shape = (shape_square, shape_square,
+ data.shape[1], data.shape[2])
+ data = data.reshape(self.data_shape)
+ if remove_bad_pixels:
+ self._remove_bad_pixels()
+
+ data_out = {}
+ data_out['data'] = data
+ return data_out
+
+ def getMetadata(self):
+ """ The dectris Arina files sometimes output an extra file with
+ metadata in it. This checks for that file and reads the meta data
+ if if exists. The units are assumed to be nanometers.
+
+ Returns
+ -------
+ : dict
+ Meta data as a dictionary
+
+ """
+
+ filename_parts = self.file_path.stem.split('_')
+ metadata_file_path = self.file_path.parent / Path('_'.join(filename_parts[0:-1])).with_suffix('.h5')
+ if metadata_file_path.exists():
+ try:
+ metadata = {}
+ with h5py.File(metadata_file_path, 'r') as f0:
+ for k,v in f0["STEM Metadata"].attrs.items():
+ metadata[k] = v
+
+ pixel_size0 = metadata["Pixel Size"] # convert to ncempy standard
+ metadata['pixelSize'] = (pixel_size0, pixel_size0)
+ metadata['pixelUnit'] = ('n_m', 'n_m')
+
+ return metadata
+ except:
+ raise
+
+ def remove_bad_pixels(self, data, value=0, bad_pixels=None):
+ """ Some pixels are known to be very high or very low. This function will replace the
+ pixel values.
+
+ Parameters
+ ----------
+ data : numpy.ndarray
+ The 4D-STEM data set
+ value : int or float
+ The value to replace the bad pixels by.
+ bad_pixels : numpy.ndarray
+ A m by 2 ndarray where m is the number of bad pixels and the locations
+ are specified in order for frame axis 2 and 3.
+
+ """
+ if bad_pixels:
+ self.bad_pixels = bad_pixels
+ for bad in self.bad_pixels:
+ data[:, :, bad[0], bad[1]] = value
+
+def dectrisReader(file_name):
+ if isinstance(file_name, str):
+ file_name = Path(file_name)
+
+ with fileDECTRIS(file_name) as f1: # open the file and init the class
+ im1 = f1.getDataset() # read in the dataset
+ md = f1.getMetadata()
+ if md:
+ extra_metadata = {'pixelSize': md['pixelSize'], 'pixelUnit':md['pixelUnit'], 'filename': f1.file_name}
+ im1.update(extra_metadata)
+ return im1
+
\ No newline at end of file
diff --git a/ncempy/io/dm.py b/ncempy/io/dm.py
index ff4eb18..17694d4 100644
--- a/ncempy/io/dm.py
+++ b/ncempy/io/dm.py
@@ -386,7 +386,7 @@ def parseHeader(self):
else: # this file only contains tags (such as a GTG file)
self.thumbnail = False
- def getMetadata(self, index):
+ def getMetadata(self, index, metadata_keys=None):
""" Get the useful metadata in the file. This parses the allTags dictionary and retrieves only the useful
information about hte experimental parameters. This is a (useful) subset of the information contains in the
allTags attribute.
@@ -394,11 +394,21 @@ def getMetadata(self, index):
Note: some DM files contain extra information called the Tecnai Microscope Info. This is added to the metadata
dictionary as a string.
+ The "good" keys include:
+ ['Calibrations', 'Acquisition', 'DataBar', 'EELS', 'Meta Data', 'Microscope Info', '4Dcamera Parameters', 'Session Info']
+ To extract other metadata not found in the above list use the metadata_keys input to this function.
Parameters
----------
index : int
The number of the dataset to get the metadata from.
+ metadata_keys : list or tuple
+ Extra keys in a list or tuple to extract from the DM tags as metadata.
+
+ Returns
+ -------
+ : dict
+ A subset of the DM tags returned as a dictionary with useful meatdata about the experiment.
"""
# The first dataset is usually a thumbnail. Test for this and skip the thumbnail automatically
# metadata indexing starts at 1 but the index keyword starts at 0
@@ -416,6 +426,11 @@ def getMetadata(self, index):
# Most of the useful keys. Two other keys Tecnai.Microscope Info is treated specially below
good_keys = ['Calibrations', 'Acquisition', 'DataBar', 'EELS', 'Meta Data', 'Microscope Info', '4Dcamera Parameters', 'Session Info']
+ # Add extra keys in case the user wants to extract other metadata
+ if metadata_keys:
+ assert isinstance(metadata_keys, (list, tuple))
+ good_keys.extend(metadata_keys)
+
# Determine useful meta data
prefix1 = '.ImageList.{}.ImageTags.'.format(index)
prefix2 = '.ImageList.{}.ImageData.'.format(index)
@@ -891,7 +906,6 @@ def writeTags(self, new_folder_path_for_tags=None):
# Change output path
if new_folder_path_for_tags:
- print('choosing different path')
out_directory = Path(new_folder_path_for_tags)
else:
out_directory = self.file_path.parent
diff --git a/ncempy/io/emd.py b/ncempy/io/emd.py
index 9600ec5..cbae541 100644
--- a/ncempy/io/emd.py
+++ b/ncempy/io/emd.py
@@ -511,7 +511,7 @@ def put_comment(self, msg, timestamp=None):
# create timestamp if missing
if not timestamp:
- timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S (UTC)')
+ timestamp = datetime.datetime.now(datetime.UTC).strftime('%Y-%m-%d %H:%M:%S (UTC)')
else:
# try to convert given timestamp to string
try:
@@ -527,7 +527,38 @@ def put_comment(self, msg, timestamp=None):
else:
# create new entry
self.comments.attrs[timestamp] = msg
+
+ def getMetadata(self, group):
+ """Get the useful metdata (experimental information) available in the file. These
+ are the attrs of the user, microscope, and sample groups. The metdata is returned
+ as a dictionary.
+ Parameters
+ ----------
+ group: h5py._hl.group.Group or int
+ Reference to the HDF5 group to load. If int is used then the item corresponding to self.list_emds
+ is loaded
+
+ Returns
+ -------
+ : dict
+ A dictionary of meta data keys and values.
+ """
+ meta_data = {}
+ try:
+ meta_data.update(self.user.attrs)
+ except AttributeError:
+ pass
+ try:
+ meta_data.update(self.microscope.attrs)
+ except AttributeError:
+ pass
+ try:
+ meta_data.update(self.sample.attrs)
+ except AttributeError:
+ pass
+
+ return meta_data
def defaultDims(data, pixel_size=None, pixel_unit=None):
""" A helper function that can generate a properly setup dim tuple
diff --git a/ncempy/io/emdVelox.py b/ncempy/io/emdVelox.py
index 22a00fd..169ec83 100644
--- a/ncempy/io/emdVelox.py
+++ b/ncempy/io/emdVelox.py
@@ -134,24 +134,32 @@ def __str__(self):
return out
def _find_groups(self):
- """ Find all groups that contain image data.
+ """ Find all groups that contain data: spectrum data, image data, and spectrum image.
Note
----
- This currently only finds images.
+ Previously, this only found images.
"""
try:
- # Get all of the groups in the Image group
- self.list_data = list(self._file_hdl['Data/Image'].values())
+ # Get all of the groups in the Spectrum, Image, and SpectrumImage groups
+ # TODO: Is there an order in which Spectrums and Images should be included? (e.g. by detector name or element name?)
+ self.list_data = list(self._file_hdl['Data/Spectrum'].values()) + list(self._file_hdl['Data/Image'].values()) + list(self._file_hdl['Data/SpectrumImage'].values())
except:
self.list_data = []
raise
self.list_emds = self.list_data # make a copy to match the Berkeley EMD attribute
-
+
def get_dataset(self, group, memmap=False):
""" Get the data from a group and the associated metadata.
+ This is a convenience function and calls getDataset
+ """
+ return self.getDataset(group, memmap=memmap)
+
+ def getDataset(self, group, memmap=False):
+ """ Get the data from a group and the associated metadata.
+
Parameters
----------
group : HDF5 dataset or int
@@ -187,22 +195,33 @@ def get_dataset(self, group, memmap=False):
return data, metaData
def parseMetaData(self, group):
- """ Parse metadata in a data group. Determines the pixelSize and
- detector name. The EMDVelox data sets have extensive metadata
- stored as a JSON type string.
+ """ Convenience function that calls _parseMetadata.
+ This function should not be directly used. Please use
+ getMetadata instead."""
+ return self._parseMetadata(group)
+
+ def _parseMetadata(self, group):
+ """ Parse metadata in a data group. The EMDVelox data sets have
+ extensive metadata stored as a JSON type string. This function
+ converts it to a dictionary. All converted metadata is stored in
+ the metadataJSON parameter.
+
+ For historical reasons this also returns a dicitonary with some
+ useful metadata.
+
+ For better metadata output please use the getMetadata function.
Parameters
----------
- group : h5py.Group or int
- The h5py group to load the metadata from which is easily retrived from the list_data attribute.
- If input is an int then the
- group corresponding to list_data attribute is used. The string metadata is loaded
- and parsed by the json module into a dictionary.
+ group : h5py.Group or int
+ The h5py group to load the metadata from which is easily retrived from the list_data attribute.
+ If input is an int then the group corresponding to list_data attribute is used. The string
+ metadata is loaded and parsed by the json module into a dictionary.
Returns
-------
- md : dict
- The JSON information in the file returned as a python dictionary.
+ : dict
+ The JSON information in the file returned as a python dictionary.
"""
try:
@@ -244,8 +263,40 @@ def parseMetaData(self, group):
md['dwellTime'] = 0
return md
+
+ def getMetadata(self, group):
+ """ Reads important metadata from Velox EMD files.
-
+ Parameters
+ ----------
+ group : h5py.Group or int
+ The h5py group to load the metadata from which is easily retrived from the list_data attribute.
+ If input is an int then the group corresponding to list_data attribute is used. The string
+ metadata is loaded and parsed by the json module into a dictionary.
+ """
+ self._parseMetadata(group)
+ meta_data = {}
+
+ keys_to_ignore = ('EnergyFilter', 'Vacuum', 'GasInjectionSystems', 'SharedProperties')
+ # previously: useful_keys = ('Optics', 'Stage', 'Scan', 'BinaryResult')
+ for kk in self.metaDataJSON.keys():
+ if kk not in keys_to_ignore and kk != 'CustomProperties':
+ meta_data.update(self.metaDataJSON[kk])
+
+ # handle CustomProperties separately
+ for kk, vv in self.metaDataJSON['CustomProperties'].items():
+ try:
+ if isinstance(vv, dict):
+ if vv['type'] == 'string':
+ meta_data[kk] = str(vv['value'])
+ elif vv['type'] == 'double':
+ meta_data[kk] = float(vv['value'])
+ else:
+ meta_data[kk] = vv['value']
+ except:
+ pass
+ return meta_data
+
def emdVeloxReader(filename, dsetNum=0):
""" A simple helper function to read in the data and metadata in a
structured format similar to the other ncempy readers.
diff --git a/ncempy/io/mrc.py b/ncempy/io/mrc.py
index 20c0cfc..15d1778 100644
--- a/ncempy/io/mrc.py
+++ b/ncempy/io/mrc.py
@@ -388,6 +388,20 @@ def getMemmap(self):
return mm
+ def getMetadata(self):
+ meta_data = {}
+
+ # Save most useful metaData
+ meta_data.update({'pixelSize': self.voxelSize, 'voxelSize': self.voxelSize,
+ 'cellAngles': self.cellAngles, 'axisOrientations': self.axisOrientations})
+ if hasattr(self, 'FEIinfo'):
+ # add in the special FEIinfo if it exists
+ try:
+ meta_data.update(self.FEIinfo)
+ except TypeError:
+ pass
+ return meta_data
+
def _applyAxisOrientations(self, arrayIn):
""" This is untested and unused.
diff --git a/ncempy/io/ser.py b/ncempy/io/ser.py
index d854376..73c553c 100644
--- a/ncempy/io/ser.py
+++ b/ncempy/io/ser.py
@@ -471,6 +471,28 @@ def getDataset(self, index, verbose=False):
return dataset, meta
+ def getMetadata(self):
+ """Retrieve meta data on experimental parmaeters and settings from
+ the file. This is global metdata for the entire set of images in
+ the SER file. Metadata such as the pixel size needs to be retrived
+ for each image separately using getDataset.
+
+ """
+ meta_data = {}
+
+ # Add extra meta data from the EMI file if it exists
+ if self._emi is not None:
+ meta_data.update(self._emi)
+
+ meta_data.update(self.head) # some header data for the ser file
+
+ # Clean the dictionary
+ for k, v in meta_data.items():
+ if isinstance(v, bytes):
+ meta_data[k] = v.decode('UTF8')
+
+ return meta_data
+
def _getTag(self, index, verbose=False):
"""Retrieve tag from data file.
@@ -950,36 +972,11 @@ def read_emi(filename):
# dict to store _emi stuff
_emi = {}
- # need anything readable from to
- # collect = False
- # data = b''
- # for line in f_emi:
- # if b'' in line:
- # collect = True
- # if collect:
- # data += line.strip()
- # if b'' in line:
- # collect = False
-
- # close the file
- # f_emi.close()
-
metaStart = emi_data.find(b'')
metaEnd = emi_data.find(b'') # need to add len('') = 13 to encompass this final tag
root = ET.fromstring(emi_data[metaStart:metaEnd + 13])
- # strip of binary stuff still around
- # data = data.decode('ascii', errors='ignore')
- # matchObj = re.search('(.+?)' + data + '')
-
# single items
_emi['Uuid'] = root.findtext('Uuid')
_emi['AcquireDate'] = root.findtext('AcquireDate')
diff --git a/ncempy/io/smv.py b/ncempy/io/smv.py
index a8d2958..b6434a4 100644
--- a/ncempy/io/smv.py
+++ b/ncempy/io/smv.py
@@ -177,6 +177,14 @@ def parseHeader(self):
raise(f'File data type not supported: {val}')
def getDataset(self):
+ """Read the data from the file
+
+ Returns
+ -------
+ : dict
+ A dictionary containng the data in a dictionary with the key 'data'
+
+ """
self.readHeader()
self.parseHeader()
@@ -186,6 +194,20 @@ def getDataset(self):
data_out = {}
data_out['data'] = data
return data_out
+
+ def getMetadata(self):
+ """Reads the metadata from the file
+
+ Returns
+ -------
+ : dict
+ A dicitons contained useful experimental metadata.
+ """
+ self.readHeader()
+ meta_data = {}
+ meta_data.update(self.header_info)
+ meta_data.update(self.custom_info)
+ return meta_data
def smvWriter(out_path, dp, camera_length=110, lamda=0.0197, pixel_size=0.01,
beam_center=None, binned_by=1, newline=None, custom_header=None):
diff --git a/ncempy/test/test_io.py b/ncempy/test/test_io.py
index 863ee49..c56fbc0 100644
--- a/ncempy/test/test_io.py
+++ b/ncempy/test/test_io.py
@@ -102,6 +102,7 @@ def test_read(data_location):
"""Test the general reader function"""
all_files = Path(data_location).glob('*.*')
for file in all_files:
- file_dict = nio.read(file)
- if file_dict:
- assert 'data' in file_dict
+ if file.stem not in ('au_145mm_68kx_microprobe_01_data_000001', 'au_145mm_68kx_microprobe_01'):
+ file_dict = nio.read(file)
+ if file_dict:
+ assert 'data' in file_dict
diff --git a/ncempy/test/test_io_dectris.py b/ncempy/test/test_io_dectris.py
new file mode 100644
index 0000000..eb5311a
--- /dev/null
+++ b/ncempy/test/test_io_dectris.py
@@ -0,0 +1,49 @@
+"""
+Tests for the basic functionality of the dectris io module.
+"""
+
+import pytest
+
+import time
+from pathlib import Path
+import tempfile
+import numpy as np
+
+import ncempy.io.dectris
+
+
+class Testdectris:
+ """
+ Test the dectris io module
+ """
+
+ @pytest.fixture
+ def data_location(self):
+ # Get the location of the test data files
+ test_path = Path(__file__).resolve()
+ root_path = test_path.parents[1]
+ return root_path / Path('data')
+
+ def test_read_data(self, data_location):
+ file_path = data_location / Path('au_145mm_68kx_microprobe_01_master.h5')
+ with ncempy.io.dectris.fileDECTRIS(file_path) as f0:
+ dd = f0.getDataset()
+ assert 'data' in dd
+
+ def test_read_metadata(self, data_location):
+ file_path = data_location / Path('au_145mm_68kx_microprobe_01_master.h5')
+ with ncempy.io.dectris.fileDECTRIS(file_path) as f0:
+ md = f0.getMetadata()
+ if md:
+ assert 'pixelSize' in md
+
+ def test_str_input(self, data_location):
+ file_path = data_location / Path('au_145mm_68kx_microprobe_01_master.h5')
+ with ncempy.io.dectris.fileDECTRIS(str(file_path)) as f0:
+ assert f0.raw_shape[1] == 192
+
+ def test_dectrisReader(self, data_location):
+ import ncempy
+ out = ncempy.read(data_location / Path('au_145mm_68kx_microprobe_01_master.h5'))
+ assert 'pixelSize' in out
+ assert 'data' in out
\ No newline at end of file
diff --git a/ncempy/test/test_io_dm.py b/ncempy/test/test_io_dm.py
index 8a5237d..5188fca 100644
--- a/ncempy/test/test_io_dm.py
+++ b/ncempy/test/test_io_dm.py
@@ -182,9 +182,13 @@ def test_file_object(self, data_location):
def test_metadata(self, data_location):
file_name = data_location / Path('08_carbon.dm3')
- #file_name = '/mnt/nvme1/percius/microED/2023.05.15/scan666.dm4'
with ncempy.io.dm.fileDM(file_name) as dm0:
- print(file_name)
_ = dm0.getMetadata(0)
- print(_['Calibrations Brightness Scale'])
+ assert _['Acquisition Device Name'] == 'EF-CCD'
+
+ def test_custom_metadata(self, data_location):
+ file_name = data_location / Path('08_carbon.dm3')
+ with ncempy.io.dm.fileDM(file_name) as dm0:
+ _ = dm0.getMetadata(0, metadata_keys=['Dimensions',])
+ assert _['Dimensions 1'] == 2048
diff --git a/ncempy/test/test_io_emd.py b/ncempy/test/test_io_emd.py
index 4f87734..46f6b56 100644
--- a/ncempy/test/test_io_emd.py
+++ b/ncempy/test/test_io_emd.py
@@ -119,8 +119,8 @@ def test_file_object(self, data_location):
# Test fileEMD class input with file object
file_name = data_location / Path('Acquisition_18.emd')
fid = open(file_name, 'rb')
- emd0 = ncempy.io.emd.fileEMD(fid)
- assert hasattr(emd0, 'file_hdl')
+ with ncempy.io.emd.fileEMD(fid) as emd0:
+ assert hasattr(emd0, 'file_hdl')
def test_memmap(self, data_location):
emd1 = ncempy.io.emd.fileEMD(data_location / Path('Acquisition_18.emd'))
@@ -190,3 +190,12 @@ def test_no_emds(self, temp_file, data_location):
ncempy.io.emd.emdReader(data_location / Path('STEM HAADF-DF4-DF2-BF Diffraction Micro.emd'))
except ncempy.io.emd.NoEmdDataSets:
pass
+
+ def test_metadata(self, data_location):
+ f = data_location / Path('Acquisition_18.emd')
+ import h5py
+ # Create a data set with missing attributes in the dim vectors
+ with ncempy.io.emd.fileEMD(f, readonly=True) as f0:
+ md = f0.getMetadata(0)
+
+ assert md['binning'] == 4
diff --git a/ncempy/test/test_io_emdVelox.py b/ncempy/test/test_io_emdVelox.py
index d82a843..b6fe4ba 100644
--- a/ncempy/test/test_io_emdVelox.py
+++ b/ncempy/test/test_io_emdVelox.py
@@ -24,12 +24,10 @@ def data_location(self):
def test_readEMDVelox(self, data_location):
dd0 = ncempy.io.emdVelox.emdVeloxReader(data_location / Path('STEM HAADF-DF4-DF2-BF Diffraction Micro.emd'),
dsetNum=0)
- print(dd0['data'].ndim)
assert dd0['data'].ndim == 2
dd2 = ncempy.io.emdVelox.emdVeloxReader(data_location / Path('STEM HAADF-DF4-DF2-BF Diffraction Micro.emd'),
dsetNum=2)
- print(dd2['data'].ndim)
assert dd2['data'].ndim == 2
def test_read_emd_stem(self, data_location):
@@ -37,7 +35,6 @@ def test_read_emd_stem(self, data_location):
with ncempy.io.emdVelox.fileEMDVelox(data_location / Path('STEM HAADF Diffraction Micro.emd')) as emd0:
dd, md = emd0.get_dataset(0)
assert dd.ndim == 2
- print(round(md['pixelSize'][0], ndigits=4))
assert md['pixelSizeUnit'][0] == 'nm'
assert md['pixelUnit'][0] == 'nm'
@@ -63,8 +60,14 @@ def test_read_emd_diffraction(self, data_location):
assert md['pixelUnit'][0] == '1/m'
def test_file_object(self, data_location):
- # Test fileSER class input with file object
+ """Test fileEMDVelox class input with file object"""
file_name = data_location / Path('STEM HAADF-DF4-DF2-BF Diffraction Micro.emd')
fid = open(file_name, 'rb')
emd0 = ncempy.io.emdVelox.fileEMDVelox(fid)
assert hasattr(emd0, '_file_hdl')
+
+ def test_metadata(self, data_location):
+ file_path = data_location / Path('STEM HAADF Diffraction Micro.emd')
+ with ncempy.io.emdVelox.fileEMDVelox(file_path) as emd0:
+ md = emd0.getMetadata(0)
+ assert md['AccelerationVoltage'] == '300000'
diff --git a/ncempy/test/test_io_mrc.py b/ncempy/test/test_io_mrc.py
index ba0ec14..23b161c 100644
--- a/ncempy/test/test_io_mrc.py
+++ b/ncempy/test/test_io_mrc.py
@@ -39,3 +39,9 @@ def test_file_object(self, temp_file):
with open(temp_file, 'rb') as f0:
mrc0 = ncempy.io.mrc.fileMRC(f0)
assert hasattr(mrc0, 'fid')
+
+ # def test_metadata(self):
+ # file_path = Path('/mnt/NAS-NCEM_Data/TitanX/KateG/KateG/Greenlee_20190413_FeFeO_KGBox4G2/tiltSeries_20190413_FeFeO_neg70to65.mrc')
+ # with ncempy.io.mrc.fileMRC(file_path) as f0:
+ # md = f0.getMetadata()
+ # md['tilt_axis'])
diff --git a/ncempy/test/test_io_ser.py b/ncempy/test/test_io_ser.py
index aba72c8..b17d716 100644
--- a/ncempy/test/test_io_ser.py
+++ b/ncempy/test/test_io_ser.py
@@ -64,3 +64,9 @@ def test_file_object(self, data_location):
fid = open(file_name, 'rb')
ser0 = ncempy.io.ser.fileSER(fid)
assert hasattr(ser0, '_file_hdl')
+
+ def test_metdata(self, data_location):
+ file_name = data_location / Path('16_STOimage_1.ser')
+ with ncempy.io.ser.fileSER(file_name) as f0:
+ md = f0.getMetadata()
+ assert md['High tension [kV]'] == 80
diff --git a/ncempy/test/test_io_smv.py b/ncempy/test/test_io_smv.py
index c8f50ba..d5cf9e4 100644
--- a/ncempy/test/test_io_smv.py
+++ b/ncempy/test/test_io_smv.py
@@ -86,4 +86,10 @@ def test_custom_header(self, temp_file):
d = ncempy.io.smv.smvReader(temp_file)
with ncempy.io.smv.fileSMV(temp_file) as f0:
f0.readHeader()
- assert f0.custom_info['4DCAMERA_scan'] == 10
\ No newline at end of file
+ assert f0.custom_info['4DCAMERA_scan'] == 10
+
+ def test_metadata(self, data_location):
+ file_path = data_location / Path('biotin_smv.img')
+ with ncempy.io.smv.fileSMV(file_path) as f0:
+ md = f0.getMetadata()
+ assert md['SIZE1'] == 2048
\ No newline at end of file
diff --git a/setup.py b/setup.py
index b43b889..4271ab3 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
- version='1.12',
+ version='1.15',
description='openNCEM\'s Python Package',
long_description=long_description,
@@ -55,9 +55,11 @@
# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
- 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
+ 'Programming Language :: Python :: 3.11',
+ 'Programming Language :: Python :: 3.12',
+ 'Programming Language :: Python :: 3.13',
],
# What does your project relate to?
@@ -76,7 +78,7 @@
# requirements files see:
# https://packaging.python.org/en/latest/requirements.html
- install_requires=['numpy>=2', 'scipy', 'matplotlib', 'h5py>=3'],
+ install_requires=['numpy>=2', 'scipy', 'matplotlib', 'h5py>=3', 'hdf5plugin'],
# List additional groups of dependencies here (e.g. development
# dependencies). You can install these using the following syntax,