pyTEMlib 0.2025.4.2__py3-none-any.whl → 0.2025.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyTEMlib might be problematic. Click here for more details.
- build/lib/pyTEMlib/__init__.py +33 -0
- build/lib/pyTEMlib/animation.py +640 -0
- build/lib/pyTEMlib/atom_tools.py +238 -0
- build/lib/pyTEMlib/config_dir.py +31 -0
- build/lib/pyTEMlib/crystal_tools.py +1219 -0
- build/lib/pyTEMlib/diffraction_plot.py +756 -0
- build/lib/pyTEMlib/dynamic_scattering.py +293 -0
- build/lib/pyTEMlib/eds_tools.py +826 -0
- build/lib/pyTEMlib/eds_xsections.py +432 -0
- build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
- build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
- build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
- build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
- build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
- build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
- build/lib/pyTEMlib/file_reader.py +274 -0
- build/lib/pyTEMlib/file_tools.py +811 -0
- build/lib/pyTEMlib/get_bote_salvat.py +69 -0
- build/lib/pyTEMlib/graph_tools.py +1153 -0
- build/lib/pyTEMlib/graph_viz.py +599 -0
- build/lib/pyTEMlib/image/__init__.py +37 -0
- build/lib/pyTEMlib/image/image_atoms.py +270 -0
- build/lib/pyTEMlib/image/image_clean.py +197 -0
- build/lib/pyTEMlib/image/image_distortion.py +299 -0
- build/lib/pyTEMlib/image/image_fft.py +277 -0
- build/lib/pyTEMlib/image/image_graph.py +926 -0
- build/lib/pyTEMlib/image/image_registration.py +316 -0
- build/lib/pyTEMlib/image/image_utilities.py +309 -0
- build/lib/pyTEMlib/image/image_window.py +421 -0
- build/lib/pyTEMlib/image_tools.py +699 -0
- build/lib/pyTEMlib/interactive_image.py +1 -0
- build/lib/pyTEMlib/kinematic_scattering.py +1196 -0
- build/lib/pyTEMlib/microscope.py +61 -0
- build/lib/pyTEMlib/probe_tools.py +906 -0
- build/lib/pyTEMlib/sidpy_tools.py +153 -0
- build/lib/pyTEMlib/simulation_tools.py +104 -0
- build/lib/pyTEMlib/test.py +437 -0
- build/lib/pyTEMlib/utilities.py +314 -0
- build/lib/pyTEMlib/version.py +5 -0
- build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
- pyTEMlib/__init__.py +25 -3
- pyTEMlib/animation.py +31 -22
- pyTEMlib/atom_tools.py +29 -34
- pyTEMlib/config_dir.py +2 -28
- pyTEMlib/crystal_tools.py +129 -165
- pyTEMlib/eds_tools.py +559 -342
- pyTEMlib/eds_xsections.py +432 -0
- pyTEMlib/eels_tools/__init__.py +44 -0
- pyTEMlib/eels_tools/core_loss_tools.py +751 -0
- pyTEMlib/eels_tools/eels_database.py +134 -0
- pyTEMlib/eels_tools/low_loss_tools.py +655 -0
- pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
- pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
- pyTEMlib/file_reader.py +274 -0
- pyTEMlib/file_tools.py +260 -1130
- pyTEMlib/get_bote_salvat.py +69 -0
- pyTEMlib/graph_tools.py +101 -174
- pyTEMlib/graph_viz.py +150 -0
- pyTEMlib/image/__init__.py +37 -0
- pyTEMlib/image/image_atoms.py +270 -0
- pyTEMlib/image/image_clean.py +197 -0
- pyTEMlib/image/image_distortion.py +299 -0
- pyTEMlib/image/image_fft.py +277 -0
- pyTEMlib/image/image_graph.py +926 -0
- pyTEMlib/image/image_registration.py +316 -0
- pyTEMlib/image/image_utilities.py +309 -0
- pyTEMlib/image/image_window.py +421 -0
- pyTEMlib/image_tools.py +154 -928
- pyTEMlib/kinematic_scattering.py +1 -1
- pyTEMlib/probe_tools.py +1 -1
- pyTEMlib/test.py +437 -0
- pyTEMlib/utilities.py +314 -0
- pyTEMlib/version.py +2 -3
- pyTEMlib/xrpa_x_sections.py +14 -10
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/METADATA +13 -16
- pytemlib-0.2025.9.1.dist-info/RECORD +86 -0
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/WHEEL +1 -1
- pytemlib-0.2025.9.1.dist-info/top_level.txt +6 -0
- pyTEMlib/core_loss_widget.py +0 -721
- pyTEMlib/eels_dialog.py +0 -754
- pyTEMlib/eels_dialog_utilities.py +0 -1199
- pyTEMlib/eels_tools.py +0 -2359
- pyTEMlib/file_tools_qt.py +0 -193
- pyTEMlib/image_dialog.py +0 -158
- pyTEMlib/image_dlg.py +0 -146
- pyTEMlib/info_widget.py +0 -1086
- pyTEMlib/info_widget3.py +0 -1120
- pyTEMlib/low_loss_widget.py +0 -479
- pyTEMlib/peak_dialog.py +0 -1129
- pyTEMlib/peak_dlg.py +0 -286
- pytemlib-0.2025.4.2.dist-info/RECORD +0 -38
- pytemlib-0.2025.4.2.dist-info/top_level.txt +0 -1
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
pyTEMlib/file_reader.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""file_reader: tools to load other data files
|
|
2
|
+
|
|
3
|
+
##################################
|
|
4
|
+
|
|
5
|
+
2025 copied from file_tools
|
|
6
|
+
|
|
7
|
+
##################################
|
|
8
|
+
"""
|
|
9
|
+
import typing
|
|
10
|
+
import collections
|
|
11
|
+
import xml
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# =============================================
|
|
16
|
+
# Include pycroscopy libraries #
|
|
17
|
+
# =============================================
|
|
18
|
+
import pyNSID
|
|
19
|
+
import sidpy
|
|
20
|
+
|
|
21
|
+
def etree_to_dict(element: xml.etree.ElementTree.Element) -> dict[str, typing.Any]:
|
|
22
|
+
"""Recursively converts an ElementTree object into a nested dictionary."""
|
|
23
|
+
d = {element.tag: {} if element.attrib else None}
|
|
24
|
+
children = list(element)
|
|
25
|
+
if children:
|
|
26
|
+
dd = collections.defaultdict(list)
|
|
27
|
+
for dc in map(etree_to_dict, children):
|
|
28
|
+
for k, v in dc.items():
|
|
29
|
+
dd[k].append(v)
|
|
30
|
+
d = {element.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
|
|
31
|
+
if element.attrib:
|
|
32
|
+
d[element.tag].update(('@' + k, v) for k, v in element.attrib.items())
|
|
33
|
+
if element.text:
|
|
34
|
+
text = element.text.strip()
|
|
35
|
+
if children or element.attrib:
|
|
36
|
+
if text:
|
|
37
|
+
d[element.tag]['#text'] = text
|
|
38
|
+
else:
|
|
39
|
+
d[element.tag] = text
|
|
40
|
+
return d
|
|
41
|
+
|
|
42
|
+
def read_adorned_metadata(image: typing.Any) -> tuple[str, dict[str, typing.Any]]:
|
|
43
|
+
"""Extract metadata from an adorned image."""
|
|
44
|
+
xml_str = image.metadata.metadata_as_xml
|
|
45
|
+
root = xml.etree.ElementTree.fromstring(xml_str)
|
|
46
|
+
metadata_dict = etree_to_dict(root)
|
|
47
|
+
detector = 'detector'
|
|
48
|
+
|
|
49
|
+
if 'Detectors' in metadata_dict['Metadata']:
|
|
50
|
+
if 'ScanningDetector' in metadata_dict['Metadata']['Detectors']:
|
|
51
|
+
detector = metadata_dict['Metadata']['Detectors']['ScanningDetector']['DetectorName']
|
|
52
|
+
elif 'ImagingDetector' in metadata_dict['Metadata']['Detectors']:
|
|
53
|
+
detector = metadata_dict['Metadata']['Detectors']['ImagingDetector']['DetectorName']
|
|
54
|
+
segment = ''
|
|
55
|
+
if 'CustomPropertyGroup' in metadata_dict['Metadata']:
|
|
56
|
+
if 'CustomProperties' in metadata_dict['Metadata']['CustomPropertyGroup']:
|
|
57
|
+
for list_item in metadata_dict['Metadata']['CustomPropertyGroup']['CustomProperties']:
|
|
58
|
+
if isinstance(list_item, dict):
|
|
59
|
+
for key in list_item:
|
|
60
|
+
for item in list_item[key]:
|
|
61
|
+
if '@name' in item:
|
|
62
|
+
if item['@name']== 'DetectorCommercialName':
|
|
63
|
+
detector = item['@value']
|
|
64
|
+
if item['@name']== 'StemSegment':
|
|
65
|
+
segment = '_'+item['@value']
|
|
66
|
+
return detector+segment, metadata_dict['Metadata']
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_metadata_from_adorned(ds: sidpy.Dataset):
|
|
70
|
+
"""Extract relevant metadata from adorned image metadata and add to sidpy.Dataset"""
|
|
71
|
+
exp_meta = {}
|
|
72
|
+
orig_meta = ds.original_metadata
|
|
73
|
+
if 'Optics' in orig_meta:
|
|
74
|
+
if 'LastMeasuredScreenCurrent' in orig_meta['Optics']:
|
|
75
|
+
exp_meta['current'] = float(orig_meta['Optics']['LastMeasuredScreenCurrent'])
|
|
76
|
+
if 'ConvergenceAngle' in orig_meta['Optics']:
|
|
77
|
+
exp_meta['convergence_angle'] = float(orig_meta['Optics']['ConvergenceAngle'])
|
|
78
|
+
if 'AccelerationVoltage' in orig_meta['Optics']:
|
|
79
|
+
exp_meta['acceleration_voltage'] = float(orig_meta['Optics']['AccelerationVoltage'])
|
|
80
|
+
if 'SpotIndex' in orig_meta['Optics']:
|
|
81
|
+
exp_meta['spot_size'] = orig_meta['Optics']['SpotIndex']
|
|
82
|
+
if 'StagesSettings' in orig_meta:
|
|
83
|
+
if 'StagePosition' in orig_meta['StagesSettings']:
|
|
84
|
+
exp_meta['stage_position'] = orig_meta['StagesSettings']['StagePosition']
|
|
85
|
+
if 'Detectors' in orig_meta:
|
|
86
|
+
if 'ScanningDetector' in orig_meta['Detectors']:
|
|
87
|
+
exp_meta['detector'] = orig_meta['Detectors']['ScanningDetector']['DetectorName']
|
|
88
|
+
elif 'ImagingDetector' in orig_meta['Detectors']:
|
|
89
|
+
exp_meta['detector'] = orig_meta['Detectors']['ImagingDetector']['DetectorName']
|
|
90
|
+
exp_meta['exposure_time'] = orig_meta['Detectors']['ImagingDetector']['ExposureTime']
|
|
91
|
+
ds.metadata['experiment'] = exp_meta
|
|
92
|
+
|
|
93
|
+
def adorned_to_sidpy(images: typing.Union[list[typing.Any], typing.Any]
|
|
94
|
+
) -> dict[str, sidpy.Dataset]:
|
|
95
|
+
"""
|
|
96
|
+
Convert a list of adorned images to a dictionary of Sidpy datasets.
|
|
97
|
+
Each dataset is created from the image data and adorned metadata.
|
|
98
|
+
The datasets are stored in a dictionary with keys 'Channel_000', 'Channel_001', etc.
|
|
99
|
+
The dimensions of the datasets are set based on the image data shape and pixel sizes.
|
|
100
|
+
The original metadata is also stored in the dataset.
|
|
101
|
+
Args:
|
|
102
|
+
images (list or object): A list of adorned images or a single adorned image.
|
|
103
|
+
Returns:
|
|
104
|
+
dict: A dictionary of Sidpy datasets, where each dataset corresponds to an image.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
data_sets = {}
|
|
108
|
+
if not isinstance(images, list):
|
|
109
|
+
images = [images]
|
|
110
|
+
for index, image in enumerate(images):
|
|
111
|
+
name, original_metadata = read_adorned_metadata(image)
|
|
112
|
+
data_sets[f'Channel_{index:03}'] = sidpy.Dataset.from_array(image.data.T, title=name)
|
|
113
|
+
ds = data_sets[f'Channel_{index:03}']
|
|
114
|
+
|
|
115
|
+
ds.original_metadata = original_metadata
|
|
116
|
+
|
|
117
|
+
pixel_size_x_m = float(ds.original_metadata['BinaryResult']['PixelSize']['X']['#text'])
|
|
118
|
+
pixel_size_y_m = float(ds.original_metadata['BinaryResult']['PixelSize']['Y']['#text'])
|
|
119
|
+
pixel_size_x_nm = pixel_size_x_m * 1e9
|
|
120
|
+
pixel_size_y_nm = pixel_size_y_m * 1e9
|
|
121
|
+
if image.data.ndim == 3:
|
|
122
|
+
ds.data_type = 'image_stack'
|
|
123
|
+
ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]),
|
|
124
|
+
name='frame', units='frame', quantity='Length',
|
|
125
|
+
dimension_type='temporal'))
|
|
126
|
+
ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_y_nm,
|
|
127
|
+
name='y', units='nm', quantity='Length',
|
|
128
|
+
dimension_type='spatial'))
|
|
129
|
+
ds.set_dimension(2, sidpy.Dimension(np.arange(image.data.shape[2]) * pixel_size_x_nm,
|
|
130
|
+
name='x', units='nm', quantity='Length',
|
|
131
|
+
dimension_type='spatial'))
|
|
132
|
+
else:
|
|
133
|
+
ds.data_type = 'image'
|
|
134
|
+
ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]) * pixel_size_y_nm,
|
|
135
|
+
name='y', units='nm', quantity='Length',
|
|
136
|
+
dimension_type='spatial'))
|
|
137
|
+
ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_x_nm,
|
|
138
|
+
name='x', units='nm', quantity='Length',
|
|
139
|
+
dimension_type='spatial'))
|
|
140
|
+
get_metadata_from_adorned(ds)
|
|
141
|
+
return data_sets
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
###############################################
|
|
145
|
+
# Support old pyTEM file format
|
|
146
|
+
###############################################
|
|
147
|
+
|
|
148
|
+
def read_old_h5group(current_channel: typing.Any) -> typing.Optional[sidpy.Dataset]:
|
|
149
|
+
"""Make a sidpy.Dataset from pyUSID style hdf5 group
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
current_channel: h5_group
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
sidpy.Dataset
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
dim_dir = []
|
|
161
|
+
if 'nDim_Data' in current_channel:
|
|
162
|
+
h5_dataset = current_channel['nDim_Data']
|
|
163
|
+
reader = pyNSID.NSIDReader(h5_dataset.file.filename)
|
|
164
|
+
dataset = reader.read(h5_dataset)
|
|
165
|
+
dataset.h5_file = current_channel.file
|
|
166
|
+
return dataset
|
|
167
|
+
if 'Raw_Data' in current_channel:
|
|
168
|
+
if 'image_stack' in current_channel:
|
|
169
|
+
sid_dataset = sidpy.Dataset.from_array(np.swapaxes(current_channel['image_stack'][()], 2, 0))
|
|
170
|
+
dim_dir = ['SPATIAL', 'SPATIAL', 'TEMPORAL']
|
|
171
|
+
elif 'data' in current_channel:
|
|
172
|
+
sid_dataset = sidpy.Dataset.from_array(current_channel['data'][()])
|
|
173
|
+
dim_dir = ['SPATIAL', 'SPATIAL']
|
|
174
|
+
else:
|
|
175
|
+
size_x = int(current_channel['spatial_size_x'][()])
|
|
176
|
+
size_y = int(current_channel['spatial_size_y'][()])
|
|
177
|
+
if 'spectral_size_x' in current_channel:
|
|
178
|
+
size_s = int(current_channel['spectral_size_x'][()])
|
|
179
|
+
else:
|
|
180
|
+
size_s = 0
|
|
181
|
+
data = np.reshape(current_channel['Raw_Data'][()], (size_x, size_y, size_s))
|
|
182
|
+
sid_dataset = sidpy.Dataset.from_array(data)
|
|
183
|
+
if size_x > 1:
|
|
184
|
+
dim_dir.append('SPATIAL')
|
|
185
|
+
if size_y > 1:
|
|
186
|
+
dim_dir.append('SPATIAL')
|
|
187
|
+
if size_s > 1:
|
|
188
|
+
dim_dir.append('SPECTRAL')
|
|
189
|
+
sid_dataset.h5_dataset = current_channel['Raw_Data']
|
|
190
|
+
|
|
191
|
+
elif 'data' in current_channel:
|
|
192
|
+
sid_dataset = sidpy.Dataset.from_array(current_channel['data'][()])
|
|
193
|
+
dim_dir = ['SPATIAL', 'SPATIAL']
|
|
194
|
+
sid_dataset.h5_dataset = current_channel['data']
|
|
195
|
+
else:
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
if 'SPATIAL' in dim_dir:
|
|
199
|
+
if 'SPECTRAL' in dim_dir:
|
|
200
|
+
sid_dataset.data_type = sidpy.DataType.SPECTRAL_IMAGE
|
|
201
|
+
elif 'TEMPORAL' in dim_dir:
|
|
202
|
+
sid_dataset.data_type = sidpy.DataType.IMAGE_STACK
|
|
203
|
+
else:
|
|
204
|
+
sid_dataset.data_type = sidpy.DataType.IMAGE
|
|
205
|
+
else:
|
|
206
|
+
sid_dataset.data_type = sidpy.DataType.SPECTRUM
|
|
207
|
+
|
|
208
|
+
sid_dataset.quantity = 'intensity'
|
|
209
|
+
sid_dataset.units = 'counts'
|
|
210
|
+
if 'analysis' in current_channel:
|
|
211
|
+
sid_dataset.source = current_channel['analysis'][()]
|
|
212
|
+
|
|
213
|
+
set_dimensions(sid_dataset, current_channel)
|
|
214
|
+
|
|
215
|
+
return sid_dataset
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def set_dimensions(dset, current_channel):
|
|
219
|
+
"""Attaches correct dimension from old pyTEMlib style.
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
dset: sidpy.Dataset
|
|
224
|
+
current_channel: hdf5.Group
|
|
225
|
+
"""
|
|
226
|
+
dim = 0
|
|
227
|
+
if dset.data_type == sidpy.DataType.IMAGE_STACK:
|
|
228
|
+
dset.set_dimension(dim, sidpy.Dimension(np.arange(dset.shape[dim]), name='frame',
|
|
229
|
+
units='frame', quantity='stack',
|
|
230
|
+
dimension_type='TEMPORAL'))
|
|
231
|
+
dim += 1
|
|
232
|
+
if 'IMAGE' in dset.data_type:
|
|
233
|
+
|
|
234
|
+
if 'spatial_scale_x' in current_channel:
|
|
235
|
+
scale_x = current_channel['spatial_scale_x'][()]
|
|
236
|
+
else:
|
|
237
|
+
scale_x = 1
|
|
238
|
+
if 'spatial_units' in current_channel:
|
|
239
|
+
units_x = current_channel['spatial_units'][()]
|
|
240
|
+
if len(units_x) < 2:
|
|
241
|
+
units_x = 'pixel'
|
|
242
|
+
else:
|
|
243
|
+
units_x = 'generic'
|
|
244
|
+
if 'spatial_scale_y' in current_channel:
|
|
245
|
+
scale_y = current_channel['spatial_scale_y'][()]
|
|
246
|
+
else:
|
|
247
|
+
scale_y = 0
|
|
248
|
+
dset.set_dimension(dim, sidpy.Dimension('x', np.arange(dset.shape[dim])*scale_x,
|
|
249
|
+
units=units_x, quantity='Length',
|
|
250
|
+
dimension_type='SPATIAL'))
|
|
251
|
+
dim += 1
|
|
252
|
+
dset.set_dimension(dim, sidpy.Dimension('y', np.arange(dset.shape[dim])*scale_y,
|
|
253
|
+
units=units_x, quantity='Length',
|
|
254
|
+
dimension_type='SPATIAL'))
|
|
255
|
+
dim += 1
|
|
256
|
+
if dset.data_type in [sidpy.DataType.SPECTRUM, sidpy.DataType.SPECTRAL_IMAGE]:
|
|
257
|
+
if 'spectral_scale_x' in current_channel:
|
|
258
|
+
scale_s = current_channel['spectral_scale_x'][()]
|
|
259
|
+
else:
|
|
260
|
+
scale_s = 1.0
|
|
261
|
+
if 'spectral_units_x' in current_channel:
|
|
262
|
+
units_s = current_channel['spectral_units_x']
|
|
263
|
+
else:
|
|
264
|
+
units_s = 'eV'
|
|
265
|
+
|
|
266
|
+
if 'spectral_offset_x' in current_channel:
|
|
267
|
+
offset = current_channel['spectral_offset_x']
|
|
268
|
+
else:
|
|
269
|
+
offset = 0.0
|
|
270
|
+
dset.set_dimension(dim, sidpy.Dimension(np.arange(dset.shape[dim]) * scale_s + offset,
|
|
271
|
+
name='energy',
|
|
272
|
+
units=units_s,
|
|
273
|
+
quantity='energy_loss',
|
|
274
|
+
dimension_type='SPECTRAL'))
|