pyTEMlib 0.2025.2.2__py3-none-any.whl → 0.2025.4.0__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.
- pyTEMlib/atom_tools.py +2 -0
- pyTEMlib/core_loss_widget.py +2 -4
- pyTEMlib/eels_dialog.py +1 -1
- pyTEMlib/eels_tools.py +11 -10
- pyTEMlib/file_tools.py +106 -16
- pyTEMlib/graph_tools.py +95 -35
- pyTEMlib/image_tools.py +78 -93
- pyTEMlib/probe_tools.py +200 -4
- pyTEMlib/version.py +2 -2
- {pytemlib-0.2025.2.2.dist-info → pytemlib-0.2025.4.0.dist-info}/METADATA +4 -3
- {pytemlib-0.2025.2.2.dist-info → pytemlib-0.2025.4.0.dist-info}/RECORD +15 -15
- {pytemlib-0.2025.2.2.dist-info → pytemlib-0.2025.4.0.dist-info}/WHEEL +1 -1
- {pytemlib-0.2025.2.2.dist-info → pytemlib-0.2025.4.0.dist-info}/entry_points.txt +0 -0
- {pytemlib-0.2025.2.2.dist-info → pytemlib-0.2025.4.0.dist-info/licenses}/LICENSE +0 -0
- {pytemlib-0.2025.2.2.dist-info → pytemlib-0.2025.4.0.dist-info}/top_level.txt +0 -0
pyTEMlib/atom_tools.py
CHANGED
pyTEMlib/core_loss_widget.py
CHANGED
|
@@ -500,8 +500,7 @@ class CoreLoss(object):
|
|
|
500
500
|
self.core_loss_tab[11, 0].value = edge['areal_density']
|
|
501
501
|
self.core_loss_tab[11, 2].value = 'a.u.'
|
|
502
502
|
else:
|
|
503
|
-
dispersion = self.parent.energy_scale
|
|
504
|
-
self.parent.energy_scale[0]
|
|
503
|
+
dispersion = self.parent.energy_scale.slope
|
|
505
504
|
self.core_loss_tab[11, 0].value = np.round(
|
|
506
505
|
edge['areal_density']/self.dataset.metadata['experiment']['flux_ppm']*1e-6, 2)
|
|
507
506
|
self.core_loss_tab[11, 2].value = 'atoms/nm²'
|
|
@@ -663,8 +662,7 @@ class CoreLoss(object):
|
|
|
663
662
|
|
|
664
663
|
edge['areal_density'] = self.core_loss_tab[11, 0].value
|
|
665
664
|
if self.parent.y_scale != 1.0:
|
|
666
|
-
dispersion = self.parent.energy_scale
|
|
667
|
-
self.parent.energy_scale[0]
|
|
665
|
+
dispersion = self.parent.energy_scale.slope
|
|
668
666
|
edge['areal_density'] = self.core_loss_tab[11, 0].value * \
|
|
669
667
|
self.dataset.metadata['experiment']['flux_ppm']/1e-6
|
|
670
668
|
if 'model' in self.edges:
|
pyTEMlib/eels_dialog.py
CHANGED
|
@@ -335,7 +335,7 @@ class CompositionWidget(object):
|
|
|
335
335
|
self.dd = (self.energy_scale[0], self.energy_scale[1])
|
|
336
336
|
|
|
337
337
|
self.dataset.metadata['experiment']['offset'] = self.energy_scale[0]
|
|
338
|
-
self.dataset.metadata['experiment']['dispersion'] = self.
|
|
338
|
+
self.dataset.metadata['experiment']['dispersion'] = self.spec_dim.slope
|
|
339
339
|
if 'edges' not in self.dataset.metadata or self.dataset.metadata['edges'] == {}:
|
|
340
340
|
self.dataset.metadata['edges'] = {'0': {}, 'model': {}, 'use_low_loss': False}
|
|
341
341
|
|
pyTEMlib/eels_tools.py
CHANGED
|
@@ -42,7 +42,6 @@ from pyTEMlib.xrpa_x_sections import x_sections
|
|
|
42
42
|
|
|
43
43
|
import sidpy
|
|
44
44
|
from sidpy.proc.fitter import SidFitter
|
|
45
|
-
from sidpy.base.num_utils import get_slope
|
|
46
45
|
|
|
47
46
|
# we have a function called find peaks - is it necessary?
|
|
48
47
|
# or could we just use scipy.signal import find_peaks
|
|
@@ -604,7 +603,8 @@ def fit_plasmon(dataset: Union[sidpy.Dataset, np.ndarray], startFitEnergy: float
|
|
|
604
603
|
guess_pos = np.argmax(fit_dset)
|
|
605
604
|
guess_amplitude = fit_dset[guess_pos]
|
|
606
605
|
guess_width =(endFitEnergy-startFitEnergy)/4
|
|
607
|
-
guess_pos = energy[guess_pos]
|
|
606
|
+
guess_pos = energy[start_fit_pixel+guess_pos]
|
|
607
|
+
|
|
608
608
|
if guess_width >8:
|
|
609
609
|
guess_width=8
|
|
610
610
|
try:
|
|
@@ -618,7 +618,7 @@ def fit_plasmon(dataset: Union[sidpy.Dataset, np.ndarray], startFitEnergy: float
|
|
|
618
618
|
p0=[guess_pos, guess_width, guess_amplitude])
|
|
619
619
|
except:
|
|
620
620
|
popt=[0,0,0]
|
|
621
|
-
|
|
621
|
+
|
|
622
622
|
plasmon = dataset.like_data(energy_loss_function(energy, popt[0], popt[1], popt[2]))
|
|
623
623
|
plasmon *= anglog
|
|
624
624
|
start_plasmon = np.searchsorted(energy, 0)+1
|
|
@@ -652,16 +652,16 @@ def fit_plasmon(dataset: Union[sidpy.Dataset, np.ndarray], startFitEnergy: float
|
|
|
652
652
|
def angle_correction(spectrum):
|
|
653
653
|
|
|
654
654
|
acceleration_voltage = spectrum.metadata['experiment']['acceleration_voltage']
|
|
655
|
-
energy_scale = spectrum.get_spectral_dims(return_axis=True)[0]
|
|
655
|
+
energy_scale = spectrum.get_spectral_dims(return_axis=True)[0]
|
|
656
656
|
# eff_beta = effective_collection_angle(energy_scale, spectrum.metadata['experiment']['convergence_angle'],
|
|
657
657
|
# spectrum.metadata['experiment']['collection_angle'],acceleration_voltage)
|
|
658
658
|
|
|
659
659
|
|
|
660
|
-
epc = energy_scale
|
|
660
|
+
epc = energy_scale.slope # input('ev per channel : ');
|
|
661
661
|
|
|
662
662
|
alpha = spectrum.metadata['experiment']['convergence_angle'] # input('Alpha (mrad) : ');
|
|
663
663
|
beta = spectrum.metadata['experiment']['collection_angle']# input('Beta (mrad) : ');
|
|
664
|
-
e = energy_scale
|
|
664
|
+
e = energy_scale.values
|
|
665
665
|
e0 = acceleration_voltage/1000 # input('E0 (keV) : ');
|
|
666
666
|
|
|
667
667
|
T = 1000.0*e0*(1.+e0/1022.12)/(1.0+e0/511.06)**2 # %eV # equ.5.2a or Appendix E p 427
|
|
@@ -1261,7 +1261,7 @@ def second_derivative(dataset: sidpy.Dataset, sensitivity: float=2.5) -> None:
|
|
|
1261
1261
|
"""Calculates second derivative of a sidpy.dataset"""
|
|
1262
1262
|
|
|
1263
1263
|
dim = dataset.get_spectral_dims()
|
|
1264
|
-
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1264
|
+
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1265
1265
|
if dataset.data_type.name == 'SPECTRAL_IMAGE':
|
|
1266
1266
|
spectrum = dataset.view.get_spectrum()
|
|
1267
1267
|
else:
|
|
@@ -1269,7 +1269,7 @@ def second_derivative(dataset: sidpy.Dataset, sensitivity: float=2.5) -> None:
|
|
|
1269
1269
|
|
|
1270
1270
|
spec = scipy.ndimage.gaussian_filter(spectrum, 3)
|
|
1271
1271
|
|
|
1272
|
-
dispersion =
|
|
1272
|
+
dispersion = energy_scale.slope
|
|
1273
1273
|
second_dif = np.roll(spec, -3) - 2 * spec + np.roll(spec, +3)
|
|
1274
1274
|
second_dif[:3] = 0
|
|
1275
1275
|
second_dif[-3:] = 0
|
|
@@ -1405,8 +1405,9 @@ def identify_edges(dataset: sidpy.Dataset, noise_level: float=2.0):
|
|
|
1405
1405
|
|
|
1406
1406
|
"""
|
|
1407
1407
|
dim = dataset.get_spectral_dims()
|
|
1408
|
-
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1409
|
-
dispersion =
|
|
1408
|
+
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1409
|
+
dispersion = energy_scale.slope
|
|
1410
|
+
|
|
1410
1411
|
spec = scipy.ndimage.gaussian_filter(dataset, 3/dispersion) # smooth with 3eV wideGaussian
|
|
1411
1412
|
|
|
1412
1413
|
first_derivative = spec - np.roll(spec, +2)
|
pyTEMlib/file_tools.py
CHANGED
|
@@ -23,6 +23,9 @@ import ase.io
|
|
|
23
23
|
import SciFiReaders
|
|
24
24
|
import pyNSID
|
|
25
25
|
import sidpy
|
|
26
|
+
import sidpy
|
|
27
|
+
import xml.etree.ElementTree as ET
|
|
28
|
+
import collections
|
|
26
29
|
import ipywidgets as widgets
|
|
27
30
|
from IPython.display import display
|
|
28
31
|
|
|
@@ -33,8 +36,6 @@ import pyTEMlib.crystal_tools
|
|
|
33
36
|
from pyTEMlib.config_dir import config_path
|
|
34
37
|
from pyTEMlib.sidpy_tools import *
|
|
35
38
|
|
|
36
|
-
from pyTEMlib.sidpy_tools import *
|
|
37
|
-
|
|
38
39
|
Qt_available = True
|
|
39
40
|
try:
|
|
40
41
|
from PyQt5 import QtCore, QtWidgets, QtGui
|
|
@@ -44,8 +45,6 @@ except ModuleNotFoundError:
|
|
|
44
45
|
|
|
45
46
|
Dimension = sidpy.Dimension
|
|
46
47
|
|
|
47
|
-
# Austin commented the line below - it is not used anywhere in the code, and it gives import errors 9-14-2024
|
|
48
|
-
# get_slope = sidpy.base.num_utils.get_slopes
|
|
49
48
|
__version__ = '2024.9.14'
|
|
50
49
|
|
|
51
50
|
from traitlets import Unicode, Bool, validate, TraitError
|
|
@@ -116,7 +115,6 @@ class FileWidget2(ipywidgets.DOMWidget):
|
|
|
116
115
|
value='None',
|
|
117
116
|
description='directory:',
|
|
118
117
|
disabled=False,
|
|
119
|
-
button_style='',
|
|
120
118
|
layout=widgets.Layout(width='90%'))
|
|
121
119
|
|
|
122
120
|
|
|
@@ -203,8 +201,7 @@ class FileWidget3(FileWidget2):
|
|
|
203
201
|
self.loaded_datasets = widgets.Dropdown(options=self.dataset_list,
|
|
204
202
|
value=self.dataset_list[0],
|
|
205
203
|
description='loaded datasets:',
|
|
206
|
-
disabled=False
|
|
207
|
-
button_style='')
|
|
204
|
+
disabled=False)
|
|
208
205
|
|
|
209
206
|
ui = widgets.HBox([select_button, add_button, self.loaded_datasets])
|
|
210
207
|
display(ui)
|
|
@@ -313,14 +310,12 @@ class FileWidget(ipywidgets.DOMWidget):
|
|
|
313
310
|
value='None',
|
|
314
311
|
description='directory:',
|
|
315
312
|
disabled=False,
|
|
316
|
-
button_style='',
|
|
317
313
|
layout=widgets.Layout(width='90%'))
|
|
318
314
|
self.dataset_list = ['None']
|
|
319
315
|
self.loaded_datasets = widgets.Dropdown(options=self.dataset_list,
|
|
320
316
|
value=self.dataset_list[0],
|
|
321
317
|
description='loaded datasets:',
|
|
322
|
-
disabled=False
|
|
323
|
-
button_style='')
|
|
318
|
+
disabled=False)
|
|
324
319
|
|
|
325
320
|
self.set_options()
|
|
326
321
|
ui = widgets.VBox([self.path_choice, self.select_files, widgets.HBox([select_button, add_button,
|
|
@@ -446,8 +441,7 @@ class ChooseDataset(object):
|
|
|
446
441
|
self.select_image = widgets.Dropdown(options=self.dataset_list,
|
|
447
442
|
value=self.dataset_list[0],
|
|
448
443
|
description='select dataset:',
|
|
449
|
-
disabled=False
|
|
450
|
-
button_style='')
|
|
444
|
+
disabled=False)
|
|
451
445
|
if show_dialog:
|
|
452
446
|
display(self.select_image)
|
|
453
447
|
|
|
@@ -484,7 +478,7 @@ class ChooseDataset(object):
|
|
|
484
478
|
self.dataset = self.datasets[self.key]
|
|
485
479
|
self.dataset.title = self.dataset.title.split('/')[-1]
|
|
486
480
|
self.dataset.title = self.dataset.title.split('/')[-1]
|
|
487
|
-
|
|
481
|
+
|
|
488
482
|
|
|
489
483
|
def add_to_dict(file_dict, name):
|
|
490
484
|
full_name = os.path.join(file_dict['directory'], name)
|
|
@@ -884,7 +878,6 @@ def open_file(filename=None, h5_group=None, write_hdf_file=False, sum_frames=Fa
|
|
|
884
878
|
else:
|
|
885
879
|
if isinstance(datasets, dict):
|
|
886
880
|
dataset_dict = datasets
|
|
887
|
-
|
|
888
881
|
else:
|
|
889
882
|
dataset_dict = {}
|
|
890
883
|
for index, dataset in enumerate(datasets):
|
|
@@ -926,23 +919,28 @@ def open_file(filename=None, h5_group=None, write_hdf_file=False, sum_frames=Fa
|
|
|
926
919
|
dset.title = dset.title + '_SI'
|
|
927
920
|
dset = dset.T
|
|
928
921
|
dset.title = dset.title[11:]
|
|
922
|
+
dset.add_provenance('pyTEMlib', 'open_file', version=pyTEMlib.__version__, linked_data='emi_converted_by_hyperspy')
|
|
929
923
|
dataset_dict[f'Channel_{index:03d}'] = dset
|
|
924
|
+
|
|
930
925
|
return dataset_dict
|
|
931
926
|
except ImportError:
|
|
932
927
|
print('This file type needs hyperspy to be installed to be able to be read')
|
|
933
928
|
return
|
|
934
929
|
elif extension == '.emd':
|
|
935
930
|
reader = SciFiReaders.EMDReader(filename, sum_frames=sum_frames)
|
|
936
|
-
|
|
931
|
+
provenance = 'SciFiReader.EMDReader'
|
|
937
932
|
elif 'edax' in extension.lower():
|
|
938
933
|
if 'h5' in extension:
|
|
939
934
|
reader = SciFiReaders.EDAXReader(filename)
|
|
935
|
+
provenance = 'SciFiReader.EDAXReader'
|
|
940
936
|
|
|
941
937
|
elif extension in ['.ndata', '.h5']:
|
|
942
938
|
reader = SciFiReaders.NionReader(filename)
|
|
943
|
-
|
|
939
|
+
provenance = 'SciFiReader.NionReader'
|
|
940
|
+
|
|
944
941
|
elif extension in ['.mrc']:
|
|
945
942
|
reader = SciFiReaders.MRCReader(filename)
|
|
943
|
+
provenance = 'SciFiReader.MRCReader'
|
|
946
944
|
|
|
947
945
|
else:
|
|
948
946
|
raise NotImplementedError('extension not supported')
|
|
@@ -994,6 +992,7 @@ def open_file(filename=None, h5_group=None, write_hdf_file=False, sum_frames=Fa
|
|
|
994
992
|
if isinstance(dset, dict):
|
|
995
993
|
dataset_dict = dset
|
|
996
994
|
for dataset in dataset_dict.values():
|
|
995
|
+
dataset.add_provenance('pyTEMlib', 'open_file', version=pyTEMlib.__version__, linked_data = 'SciFiReader')
|
|
997
996
|
dataset.metadata['filename'] = filename
|
|
998
997
|
|
|
999
998
|
elif isinstance(dset, list):
|
|
@@ -1431,6 +1430,97 @@ def h5_get_crystal_structure(structure_group):
|
|
|
1431
1430
|
# ToDo: Read all of info dictionary
|
|
1432
1431
|
return atoms
|
|
1433
1432
|
|
|
1433
|
+
import collections
|
|
1434
|
+
def etree_to_dict(element):
|
|
1435
|
+
"""Recursively converts an ElementTree object into a nested dictionary."""
|
|
1436
|
+
d = {element.tag: {} if element.attrib else None}
|
|
1437
|
+
children = list(element)
|
|
1438
|
+
if children:
|
|
1439
|
+
dd = collections.defaultdict(list)
|
|
1440
|
+
for dc in map(etree_to_dict, children):
|
|
1441
|
+
for k, v in dc.items():
|
|
1442
|
+
dd[k].append(v)
|
|
1443
|
+
d = {element.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
|
|
1444
|
+
if element.attrib:
|
|
1445
|
+
d[element.tag].update(('@' + k, v) for k, v in element.attrib.items())
|
|
1446
|
+
if element.text:
|
|
1447
|
+
text = element.text.strip()
|
|
1448
|
+
if children or element.attrib:
|
|
1449
|
+
if text:
|
|
1450
|
+
d[element.tag]['#text'] = text
|
|
1451
|
+
else:
|
|
1452
|
+
d[element.tag] = text
|
|
1453
|
+
return d
|
|
1454
|
+
|
|
1455
|
+
def read_adorned_metadata(image):
|
|
1456
|
+
xml_str = image.metadata.metadata_as_xml
|
|
1457
|
+
root = ET.fromstring(xml_str)
|
|
1458
|
+
metadata_dict = etree_to_dict(root)
|
|
1459
|
+
detector = 'detector'
|
|
1460
|
+
if 'Detectors' in metadata_dict['Metadata']['Detectors']['ScanningDetector']:
|
|
1461
|
+
if 'ScanningDetector' in metadata_dict['Metadata']['Detectors']['ScanningDetector']:
|
|
1462
|
+
detector = metadata_dict['Metadata']['Detectors']['ScanningDetector']['DetectorName']
|
|
1463
|
+
|
|
1464
|
+
segment = ''
|
|
1465
|
+
if 'CustomPropertyGroup' in metadata_dict['Metadata']:
|
|
1466
|
+
if 'CustomProperties' in metadata_dict['Metadata']['CustomPropertyGroup']:
|
|
1467
|
+
for list_item in metadata_dict['Metadata']['CustomPropertyGroup']['CustomProperties']:
|
|
1468
|
+
|
|
1469
|
+
if isinstance(list_item, dict):
|
|
1470
|
+
for key in list_item:
|
|
1471
|
+
for item in list_item[key]:
|
|
1472
|
+
if '@name' in item:
|
|
1473
|
+
if item['@name']== 'DetectorCommercialName':
|
|
1474
|
+
detector = item['@value']
|
|
1475
|
+
if item['@name']== 'StemSegment':
|
|
1476
|
+
segment = '_'+item['@value']
|
|
1477
|
+
return detector+segment, metadata_dict['Metadata']
|
|
1478
|
+
|
|
1479
|
+
def adorned_to_sidpy(images):
|
|
1480
|
+
"""
|
|
1481
|
+
Convert a list of adorned images to a dictionary of Sidpy datasets.
|
|
1482
|
+
Each dataset is created from the image data and adorned metadata.
|
|
1483
|
+
The datasets are stored in a dictionary with keys 'Channel_000', 'Channel_001', etc.
|
|
1484
|
+
The dimensions of the datasets are set based on the image data shape and pixel sizes.
|
|
1485
|
+
The original metadata is also stored in the dataset.
|
|
1486
|
+
Args:
|
|
1487
|
+
images (list or object): A list of adorned images or a single adorned image.
|
|
1488
|
+
Returns:
|
|
1489
|
+
dict: A dictionary of Sidpy datasets, where each dataset corresponds to an image.
|
|
1490
|
+
"""
|
|
1491
|
+
|
|
1492
|
+
data_sets = {}
|
|
1493
|
+
if not isinstance(images, list):
|
|
1494
|
+
images = [images]
|
|
1495
|
+
for index, image in enumerate(images):
|
|
1496
|
+
name, original_metadata = read_adorned_metadata(image)
|
|
1497
|
+
data_sets[f'Channel_{index:03}'] = sidpy.Dataset.from_array(image.data.T, title=name)
|
|
1498
|
+
ds = data_sets[f'Channel_{index:03}']
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
ds.original_metadata = original_metadata
|
|
1502
|
+
|
|
1503
|
+
pixel_size_x_m = float(ds.original_metadata['BinaryResult']['PixelSize']['X']['#text'])
|
|
1504
|
+
pixel_size_y_m = float(ds.original_metadata['BinaryResult']['PixelSize']['Y']['#text'])
|
|
1505
|
+
pixel_size_x_nm = pixel_size_x_m * 1e9
|
|
1506
|
+
pixel_size_y_nm = pixel_size_y_m * 1e9
|
|
1507
|
+
if image.data.ndim == 3:
|
|
1508
|
+
ds.data_type = 'image_stack'
|
|
1509
|
+
ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]),
|
|
1510
|
+
name='frame', units='frame', quantity='Length', dimension_type='temporal'))
|
|
1511
|
+
ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_y_nm,
|
|
1512
|
+
name='y', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1513
|
+
ds.set_dimension(2, sidpy.Dimension(np.arange(image.data.shape[2]) * pixel_size_x_nm,
|
|
1514
|
+
name='x', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1515
|
+
else:
|
|
1516
|
+
ds.data_type = 'image'
|
|
1517
|
+
ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]) * pixel_size_y_nm,
|
|
1518
|
+
name='y', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1519
|
+
ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_x_nm,
|
|
1520
|
+
name='x', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1521
|
+
|
|
1522
|
+
return data_sets
|
|
1523
|
+
|
|
1434
1524
|
|
|
1435
1525
|
###############################################
|
|
1436
1526
|
# Support old pyTEM file format
|
pyTEMlib/graph_tools.py
CHANGED
|
@@ -5,7 +5,6 @@ import numpy as np
|
|
|
5
5
|
# import ase
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
|
-
# from scipy.spatial import cKDTree, Voronoi, ConvexHull
|
|
9
8
|
import scipy.spatial
|
|
10
9
|
import scipy.optimize
|
|
11
10
|
import scipy.interpolate
|
|
@@ -20,7 +19,7 @@ import pyTEMlib.crystal_tools
|
|
|
20
19
|
from tqdm.auto import tqdm, trange
|
|
21
20
|
|
|
22
21
|
from .graph_viz import *
|
|
23
|
-
|
|
22
|
+
QT_available = False
|
|
24
23
|
|
|
25
24
|
###########################################################################
|
|
26
25
|
# utility functions
|
|
@@ -212,6 +211,14 @@ def get_voronoi(tetrahedra, atoms, bond_radii=None, optimize=True):
|
|
|
212
211
|
"""
|
|
213
212
|
|
|
214
213
|
extent = atoms.cell.lengths()
|
|
214
|
+
print('extent', extent)
|
|
215
|
+
|
|
216
|
+
if np.abs(atoms.positions[:, 2]).sum() <= 0.01:
|
|
217
|
+
positions = atoms.positions[:, :2]
|
|
218
|
+
extent = extent[:2]
|
|
219
|
+
else:
|
|
220
|
+
positions = atoms.positions
|
|
221
|
+
|
|
215
222
|
if atoms.info is None:
|
|
216
223
|
atoms.info = {}
|
|
217
224
|
|
|
@@ -232,8 +239,8 @@ def get_voronoi(tetrahedra, atoms, bond_radii=None, optimize=True):
|
|
|
232
239
|
r_a = []
|
|
233
240
|
for vert in vertices:
|
|
234
241
|
r_a.append(bond_radii[vert])
|
|
235
|
-
voronoi, radius = interstitial_sphere_center(
|
|
236
|
-
|
|
242
|
+
voronoi, radius = interstitial_sphere_center(positions[vertices], r_a, optimize=optimize)
|
|
243
|
+
|
|
237
244
|
r_a = np.average(r_a) # np.min(r_a)
|
|
238
245
|
r_aa.append(r_a)
|
|
239
246
|
|
|
@@ -247,7 +254,7 @@ def get_voronoi(tetrahedra, atoms, bond_radii=None, optimize=True):
|
|
|
247
254
|
def find_overlapping_spheres(voronoi_vertices, r_vv, r_a, cheat=1.):
|
|
248
255
|
"""Find overlapping spheres"""
|
|
249
256
|
|
|
250
|
-
vertex_tree = scipy.spatial.
|
|
257
|
+
vertex_tree = scipy.spatial.KDTree(voronoi_vertices)
|
|
251
258
|
|
|
252
259
|
pairs = vertex_tree.query_pairs(r=r_a * 2)
|
|
253
260
|
|
|
@@ -424,7 +431,7 @@ def get_non_periodic_supercell(super_cell):
|
|
|
424
431
|
return atoms
|
|
425
432
|
|
|
426
433
|
def get_connectivity_matrix(crystal, atoms, polyhedra):
|
|
427
|
-
crystal_tree = scipy.spatial.
|
|
434
|
+
crystal_tree = scipy.spatial.KDTree(crystal.positions)
|
|
428
435
|
|
|
429
436
|
|
|
430
437
|
connectivity_matrix = np.zeros([len(atoms),len(atoms)], dtype=int)
|
|
@@ -476,8 +483,8 @@ def get_bonds(crystal, shift= 0., verbose = False, cheat=1.0):
|
|
|
476
483
|
other = []
|
|
477
484
|
super_cell_atoms =[]
|
|
478
485
|
|
|
479
|
-
atoms_tree = scipy.spatial.
|
|
480
|
-
crystal_tree = scipy.spatial.
|
|
486
|
+
atoms_tree = scipy.spatial.KDTree(atoms.positions-crystal.cell.lengths())
|
|
487
|
+
crystal_tree = scipy.spatial.KDTree(crystal.positions)
|
|
481
488
|
connectivity_matrix = np.zeros([len(atoms),len(atoms)], dtype=float)
|
|
482
489
|
|
|
483
490
|
for polyhedron in polyhedra.values():
|
|
@@ -699,18 +706,21 @@ def find_polyhedra(atoms, optimize=True, cheat=1.0, bond_radii=None):
|
|
|
699
706
|
raise TypeError('This function needs an ase.Atoms object')
|
|
700
707
|
|
|
701
708
|
if np.abs(atoms.positions[:, 2]).sum() <= 0.01:
|
|
702
|
-
|
|
709
|
+
positions = atoms.positions[:, :2]
|
|
710
|
+
print('2D')
|
|
703
711
|
else:
|
|
704
|
-
|
|
712
|
+
positions = atoms.positions
|
|
713
|
+
tetrahedra = scipy.spatial.Delaunay(positions)
|
|
705
714
|
|
|
706
715
|
voronoi_vertices, voronoi_tetrahedrons, r_vv, r_a = get_voronoi(tetrahedra, atoms, optimize=optimize, bond_radii=bond_radii)
|
|
707
|
-
|
|
708
|
-
|
|
716
|
+
|
|
717
|
+
if positions.shape[1] < 3:
|
|
718
|
+
r_vv = np.array(r_vv)*1.
|
|
709
719
|
overlapping_pairs = find_overlapping_spheres(voronoi_vertices, r_vv, r_a, cheat=cheat)
|
|
710
720
|
|
|
711
721
|
clusters, visited_all = find_interstitial_clusters(overlapping_pairs)
|
|
712
722
|
|
|
713
|
-
if
|
|
723
|
+
if positions.shape[1] < 3:
|
|
714
724
|
rings = get_polygons(atoms, clusters, voronoi_tetrahedrons)
|
|
715
725
|
return rings
|
|
716
726
|
else:
|
|
@@ -770,7 +780,7 @@ def sort_polyhedra_by_vertices(polyhedra, visible=range(4, 100), z_lim=[0, 100],
|
|
|
770
780
|
##########################
|
|
771
781
|
# New Graph Stuff
|
|
772
782
|
##########################
|
|
773
|
-
def
|
|
783
|
+
def breadth_first_search2(graph, initial, projected_crystal):
|
|
774
784
|
""" breadth first search of atoms viewed as a graph
|
|
775
785
|
|
|
776
786
|
the projection dictionary has to contain the following items
|
|
@@ -794,15 +804,23 @@ def breadth_first_search(graph, initial, projected_crystal):
|
|
|
794
804
|
"""
|
|
795
805
|
|
|
796
806
|
projection_tags = projected_crystal.info['projection']
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
807
|
+
if 'lattice_vector' in projection_tags:
|
|
808
|
+
a_lattice_vector = projection_tags['lattice_vector']['a']
|
|
809
|
+
b_lattice_vector = projection_tags['lattice_vector']['b']
|
|
810
|
+
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
811
|
+
near = []
|
|
812
|
+
else:
|
|
813
|
+
# get lattice vectors to hopp along through graph
|
|
814
|
+
projected_unit_cell = projected_crystal.cell[:2, :2]
|
|
815
|
+
a_lattice_vector = projected_unit_cell[0]
|
|
816
|
+
b_lattice_vector = projected_unit_cell[1]
|
|
817
|
+
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
818
|
+
near = projection_tags['near_base'] # all nearest atoms
|
|
804
819
|
# get k next nearest neighbours for each node
|
|
805
|
-
|
|
820
|
+
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
821
|
+
near = np.append(main, near, axis=0)
|
|
822
|
+
|
|
823
|
+
neighbour_tree = scipy.spatial.KDTree(graph)
|
|
806
824
|
distances, indices = neighbour_tree.query(graph, # let's get all neighbours
|
|
807
825
|
k=50) # projection_tags['number_of_nearest_neighbours']*2 + 1)
|
|
808
826
|
# print(projection_tags['number_of_nearest_neighbours'] * 2 + 1)
|
|
@@ -835,6 +853,53 @@ def breadth_first_search(graph, initial, projected_crystal):
|
|
|
835
853
|
|
|
836
854
|
return graph[visited], ideal
|
|
837
855
|
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
def breadth_first_search_felxible(graph, initial, lattice_parameter, tolerance=1):
|
|
859
|
+
""" breadth first search of atoms viewed as a graph
|
|
860
|
+
This is a rotational invariant search of atoms in a lattice, and returns the angles of unit cells.
|
|
861
|
+
We only use the ideal lattice parameter to determine the lattice.
|
|
862
|
+
"""
|
|
863
|
+
if isinstance(lattice_parameter, ase.Atoms):
|
|
864
|
+
lattice_parameter = lattice_parameter.cell.lengths()[:2]
|
|
865
|
+
elif isinstance(lattice_parameter, float):
|
|
866
|
+
lattice_parameter = [lattice_parameter]
|
|
867
|
+
lattice_parameter = np.array(lattice_parameter)
|
|
868
|
+
|
|
869
|
+
neighbour_tree = scipy.spatial.KDTree(graph)
|
|
870
|
+
distances, indices = neighbour_tree.query(graph, # let's get all neighbours
|
|
871
|
+
k=50) # projection_tags['number_of_nearest_neighbours']*2 + 1)
|
|
872
|
+
visited = [] # the atoms we visited
|
|
873
|
+
angles = [] # atoms at ideal lattice
|
|
874
|
+
sub_lattice = [] # atoms in base and disregarded
|
|
875
|
+
queue = [initial]
|
|
876
|
+
queue_angles=[0]
|
|
877
|
+
|
|
878
|
+
while queue:
|
|
879
|
+
node = queue.pop(0)
|
|
880
|
+
angle = queue_angles.pop(0)
|
|
881
|
+
if node not in visited:
|
|
882
|
+
visited.append(node)
|
|
883
|
+
angles.append(angle)
|
|
884
|
+
neighbors = indices[node]
|
|
885
|
+
for i, neighbour in enumerate(neighbors):
|
|
886
|
+
if neighbour not in visited:
|
|
887
|
+
hopp = graph[node] - graph[neighbour]
|
|
888
|
+
distance_to_ideal = np.linalg.norm(hopp)
|
|
889
|
+
if np.min(np.abs(distance_to_ideal - lattice_parameter)) < tolerance:
|
|
890
|
+
queue.append(neighbour)
|
|
891
|
+
queue_angles.append(np.arctan2(hopp[1], hopp[0]))
|
|
892
|
+
angles[0] = angles[1]
|
|
893
|
+
out_atoms = np.stack([graph[visited][:, 0], graph[visited][:, 1], angles])
|
|
894
|
+
return out_atoms.T, visited
|
|
895
|
+
|
|
896
|
+
def delete_rim_atoms(atoms, extent, rim_distance):
|
|
897
|
+
rim = np.where(atoms[:, :2] - extent > -rim_distance)[0]
|
|
898
|
+
middle_atoms = np.delete(atoms, rim, axis=0)
|
|
899
|
+
rim = np.where(middle_atoms[:, :2].min(axis=1)<rim_distance)[0]
|
|
900
|
+
middle_atoms = np.delete(middle_atoms, rim, axis=0)
|
|
901
|
+
return middle_atoms
|
|
902
|
+
|
|
838
903
|
####################
|
|
839
904
|
# Distortion Matrix
|
|
840
905
|
####################
|
|
@@ -992,7 +1057,7 @@ def get_significant_vertices(vertices, distance=3):
|
|
|
992
1057
|
list of points that are all a minimum of 3 apart.
|
|
993
1058
|
"""
|
|
994
1059
|
|
|
995
|
-
tt = scipy.spatial.
|
|
1060
|
+
tt = scipy.spatial.KDTree(np.array(vertices))
|
|
996
1061
|
near = tt.query_ball_point(vertices, distance)
|
|
997
1062
|
ideal_vertices = []
|
|
998
1063
|
for indices in near:
|
|
@@ -1146,21 +1211,16 @@ def undistort_stack(distortion_matrix, data):
|
|
|
1146
1211
|
nimages = data.shape[0]
|
|
1147
1212
|
done = 0
|
|
1148
1213
|
|
|
1149
|
-
|
|
1150
|
-
progress = ft.ProgressDialog("Correct Scan Distortions", nimages)
|
|
1214
|
+
|
|
1151
1215
|
for i in range(nimages):
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
# progress output :
|
|
1158
|
-
sys.stdout.write("[%-50s] %d%%" % ('=' * done, 2 * done))
|
|
1159
|
-
sys.stdout.flush()
|
|
1216
|
+
done = int((i + 1) / nimages * 50)
|
|
1217
|
+
sys.stdout.write('\r')
|
|
1218
|
+
# progress output :
|
|
1219
|
+
sys.stdout.write("[%-50s] %d%%" % ('=' * done, 2 * done))
|
|
1220
|
+
sys.stdout.flush()
|
|
1160
1221
|
|
|
1161
1222
|
interpolated[i, :, :] = griddata(corrected, intensity_values[i, :], (grid_x, grid_y), method='linear')
|
|
1162
|
-
|
|
1163
|
-
progress.set_value(nimages)
|
|
1223
|
+
|
|
1164
1224
|
print(':-)')
|
|
1165
1225
|
print('You have successfully completed undistortion of image stack')
|
|
1166
1226
|
return interpolated
|
pyTEMlib/image_tools.py
CHANGED
|
@@ -182,24 +182,21 @@ def fourier_transform(dset: sidpy.Dataset) -> sidpy.Dataset:
|
|
|
182
182
|
assert isinstance(dset, sidpy.Dataset), 'Expected a sidpy Dataset'
|
|
183
183
|
|
|
184
184
|
selection = []
|
|
185
|
-
|
|
186
|
-
# image_dim = get_image_dims(sidpy.DimensionTypes.SPATIAL)
|
|
187
|
-
|
|
185
|
+
image_dims = pyTEMlib.sidpy_tools.get_image_dims(dset)
|
|
188
186
|
if dset.data_type == sidpy.DataType.IMAGE_STACK:
|
|
189
|
-
image_dim = dset.get_image_dims()
|
|
190
187
|
stack_dim = dset.get_dimensions_by_type('TEMPORAL')
|
|
191
188
|
|
|
192
|
-
if len(
|
|
189
|
+
if len(image_dims) != 2:
|
|
193
190
|
raise ValueError('need at least two SPATIAL dimension for an image stack')
|
|
194
191
|
|
|
195
192
|
for i in range(dset.ndim):
|
|
196
|
-
if i in
|
|
193
|
+
if i in image_dims:
|
|
197
194
|
selection.append(slice(None))
|
|
198
195
|
if len(stack_dim) == 0:
|
|
199
|
-
|
|
196
|
+
stack_dims = i
|
|
200
197
|
selection.append(slice(None))
|
|
201
198
|
elif i in stack_dim:
|
|
202
|
-
|
|
199
|
+
stack_dims = i
|
|
203
200
|
selection.append(slice(None))
|
|
204
201
|
else:
|
|
205
202
|
selection.append(slice(0, 1))
|
|
@@ -212,6 +209,7 @@ def fourier_transform(dset: sidpy.Dataset) -> sidpy.Dataset:
|
|
|
212
209
|
return
|
|
213
210
|
|
|
214
211
|
new_image = new_image - new_image.min()
|
|
212
|
+
|
|
215
213
|
fft_transform = (np.fft.fftshift(np.fft.fft2(new_image)))
|
|
216
214
|
|
|
217
215
|
image_dims = pyTEMlib.sidpy_tools.get_image_dims(dset)
|
|
@@ -227,12 +225,11 @@ def fourier_transform(dset: sidpy.Dataset) -> sidpy.Dataset:
|
|
|
227
225
|
fft_dset.modality = 'fft'
|
|
228
226
|
|
|
229
227
|
fft_dset.set_dimension(0, sidpy.Dimension(np.fft.fftshift(np.fft.fftfreq(new_image.shape[0],
|
|
230
|
-
d=
|
|
231
|
-
|
|
228
|
+
d=dset.x[1]-dset.x[0])),
|
|
232
229
|
name='u', units=units_x, dimension_type='RECIPROCAL',
|
|
233
230
|
quantity='reciprocal_length'))
|
|
234
231
|
fft_dset.set_dimension(1, sidpy.Dimension(np.fft.fftshift(np.fft.fftfreq(new_image.shape[1],
|
|
235
|
-
d=
|
|
232
|
+
d=dset.y[1]- dset.y[0])),
|
|
236
233
|
name='v', units=units_y, dimension_type='RECIPROCAL',
|
|
237
234
|
quantity='reciprocal_length'))
|
|
238
235
|
|
|
@@ -319,7 +316,9 @@ def diffractogram_spots(dset, spot_threshold, return_center=True, eps=0.1):
|
|
|
319
316
|
print(f'Found {spots_random.shape[0]} reflections')
|
|
320
317
|
|
|
321
318
|
# Needed for conversion from pixel to Reciprocal space
|
|
322
|
-
|
|
319
|
+
image_dims = dset.get_image_dims(return_axis=True)
|
|
320
|
+
rec_scale = np.array([image_dims[0].slope, image_dims[1].slope])
|
|
321
|
+
|
|
323
322
|
spots_random[:, :2] = spots_random[:, :2]*rec_scale+[dset.u.values[0], dset.v.values[0]]
|
|
324
323
|
# sort reflections
|
|
325
324
|
spots_random[:, 2] = np.linalg.norm(spots_random[:, 0:2], axis=1)
|
|
@@ -516,7 +515,8 @@ def complete_registration(main_dataset, storage_channel=None):
|
|
|
516
515
|
|
|
517
516
|
rigid_registered_dataset = rigid_registration(main_dataset)
|
|
518
517
|
|
|
519
|
-
|
|
518
|
+
print(rigid_registered_dataset)
|
|
519
|
+
rigid_registered_dataset.data_type = 'IMAGE_STACK'
|
|
520
520
|
print('Non-Rigid_Registration')
|
|
521
521
|
|
|
522
522
|
non_rigid_registered = demon_registration(rigid_registered_dataset)
|
|
@@ -580,7 +580,6 @@ def demon_registration(dataset, verbose=False):
|
|
|
580
580
|
resampler.SetDefaultPixelValue(0)
|
|
581
581
|
|
|
582
582
|
for i in trange(nimages):
|
|
583
|
-
|
|
584
583
|
moving = sitk.GetImageFromArray(dataset[i])
|
|
585
584
|
moving_f = sitk.DiscreteGaussian(moving, 2.0)
|
|
586
585
|
displacement_field = demons.Execute(fixed, moving_f)
|
|
@@ -596,20 +595,21 @@ def demon_registration(dataset, verbose=False):
|
|
|
596
595
|
demon_registered.title = 'Non-Rigid Registration'
|
|
597
596
|
demon_registered.source = dataset.title
|
|
598
597
|
|
|
599
|
-
demon_registered.metadata =
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
598
|
+
demon_registered.metadata =dataset.metadata.copy()
|
|
599
|
+
if 'analysis' not in demon_registered.metadata:
|
|
600
|
+
demon_registered.metadata['analysis'] = {}
|
|
601
|
+
demon_registered.metadata['analysis']['non_rigid_demon_registration'] = {'package': 'simpleITK',
|
|
602
|
+
'method': 'DiscreteGaussian',
|
|
603
|
+
'variance': 2,
|
|
604
|
+
'input_dataset': dataset.source}
|
|
605
|
+
demon_registered.data_type = 'IMAGE_STACK'
|
|
606
606
|
return demon_registered
|
|
607
607
|
|
|
608
608
|
|
|
609
609
|
###############################
|
|
610
610
|
# Rigid Registration New 05/09/2020
|
|
611
611
|
|
|
612
|
-
def rigid_registration(dataset,
|
|
612
|
+
def rigid_registration(dataset, normalization=None):
|
|
613
613
|
"""
|
|
614
614
|
Rigid registration of image stack with pixel accuracy
|
|
615
615
|
|
|
@@ -631,51 +631,32 @@ def rigid_registration(dataset, sub_pixel=True):
|
|
|
631
631
|
raise TypeError('We need a sidpy.Dataset')
|
|
632
632
|
if dataset.data_type.name != 'IMAGE_STACK':
|
|
633
633
|
raise TypeError('Registration makes only sense for an image stack')
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
print('need two spatial dimensions')
|
|
649
|
-
if len(frame_dim) != 1:
|
|
650
|
-
print('need one frame dimensions')
|
|
651
|
-
|
|
652
|
-
nopix = dataset.shape[spatial_dim[0]]
|
|
653
|
-
nopiy = dataset.shape[spatial_dim[1]]
|
|
654
|
-
nimages = dataset.shape[frame_dim[0]]
|
|
655
|
-
|
|
656
|
-
print('Stack contains ', nimages, ' images, each with', nopix, ' pixels in x-direction and ', nopiy,
|
|
657
|
-
' pixels in y-direction')
|
|
658
|
-
|
|
659
|
-
fixed = dataset[tuple(selection)].squeeze().compute()
|
|
660
|
-
fft_fixed = np.fft.fft2(fixed)
|
|
634
|
+
|
|
635
|
+
if isinstance (normalization, str):
|
|
636
|
+
if normalization.lower() != 'phase':
|
|
637
|
+
nomralization = None
|
|
638
|
+
else:
|
|
639
|
+
normalization = None
|
|
640
|
+
|
|
641
|
+
if dataset.get_dimensions_by_type('TEMPORAL')[0] != 0:
|
|
642
|
+
raise TypeError('Image stack does not have correct frame dimension')
|
|
643
|
+
|
|
644
|
+
stack_dim = dataset.get_dimensions_by_type('TEMPORAL', return_axis=True)[0]
|
|
645
|
+
image_dim = dataset.get_image_dims(return_axis=True)
|
|
646
|
+
if len(image_dim) != 2:
|
|
647
|
+
raise ValueError('need at least two SPATIAL dimension for an image stack')
|
|
661
648
|
|
|
662
649
|
relative_drift = [[0., 0.]]
|
|
663
|
-
|
|
664
|
-
for i in
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
cc_image = np.fft.fftshift(np.fft.ifft2(image_product))
|
|
674
|
-
shift = np.array(ndimage.maximum_position(cc_image.real))-cc_image.shape[0]/2
|
|
675
|
-
fft_fixed = fft_moving
|
|
676
|
-
relative_drift.append(shift)
|
|
677
|
-
rig_reg, drift = rig_reg_drift(dataset, relative_drift)
|
|
678
|
-
crop_reg, input_crop = crop_image_stack(rig_reg, drift)
|
|
650
|
+
im1 = np.fft.fft2(np.array(dataset[0]))
|
|
651
|
+
for i in range(1, len(stack_dim)):
|
|
652
|
+
im2 = np.fft.fft2(np.array(dataset[i]))
|
|
653
|
+
shift, error, _ = skimage.registration.phase_cross_correlation(im1, im2, normalization=normalization, space='fourier')
|
|
654
|
+
print(shift)
|
|
655
|
+
im1 = im2.copy()
|
|
656
|
+
relative_drift.append(shift)
|
|
657
|
+
|
|
658
|
+
rig_reg, drift = pyTEMlib.image_tools.rig_reg_drift(dataset, relative_drift)
|
|
659
|
+
crop_reg, input_crop = pyTEMlib.image_tools.crop_image_stack(rig_reg, drift)
|
|
679
660
|
|
|
680
661
|
rigid_registered = sidpy.Dataset.from_array(crop_reg,
|
|
681
662
|
title='Rigid Registration',
|
|
@@ -684,21 +665,22 @@ def rigid_registration(dataset, sub_pixel=True):
|
|
|
684
665
|
units=dataset.units)
|
|
685
666
|
rigid_registered.title = 'Rigid_Registration'
|
|
686
667
|
rigid_registered.source = dataset.title
|
|
687
|
-
rigid_registered.metadata = {'
|
|
688
|
-
'input_crop': input_crop, 'input_shape': dataset.shape[1:]}
|
|
668
|
+
rigid_registered.metadata['analysis'] = {'rigid_registration': {'drift': drift,
|
|
669
|
+
'input_crop': input_crop, 'input_shape': dataset.shape[1:]}}
|
|
689
670
|
rigid_registered.metadata['experiment'] = dataset.metadata['experiment'].copy()
|
|
690
671
|
rigid_registered.set_dimension(0, sidpy.Dimension(np.arange(rigid_registered.shape[0]),
|
|
691
672
|
name='frame', units='frame', quantity='time',
|
|
692
673
|
dimension_type='temporal'))
|
|
693
674
|
|
|
694
|
-
array_x =
|
|
695
|
-
rigid_registered.set_dimension(1, sidpy.Dimension(array_x,
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
array_y =
|
|
699
|
-
rigid_registered.set_dimension(2, sidpy.Dimension(array_y,
|
|
700
|
-
|
|
701
|
-
|
|
675
|
+
array_x = image_dim[0].values[input_crop[0]:input_crop[1]]
|
|
676
|
+
rigid_registered.set_dimension(1, sidpy.Dimension(array_x, name='x',
|
|
677
|
+
units='nm', quantity='Length',
|
|
678
|
+
dimension_type='spatial'))
|
|
679
|
+
array_y =image_dim[1].values[input_crop[2]:input_crop[3]]
|
|
680
|
+
rigid_registered.set_dimension(2, sidpy.Dimension(array_y, name='y',
|
|
681
|
+
units='nm', quantity='Length',
|
|
682
|
+
dimension_type='spatial'))
|
|
683
|
+
rigid_registered.data_type = 'IMAGE_STACK'
|
|
702
684
|
return rigid_registered.rechunk({0: 'auto', 1: -1, 2: -1})
|
|
703
685
|
|
|
704
686
|
|
|
@@ -773,13 +755,12 @@ def crop_image_stack(rig_reg, drift):
|
|
|
773
755
|
-------
|
|
774
756
|
numpy array
|
|
775
757
|
"""
|
|
758
|
+
xpmax = int(rig_reg.shape[1] - -np.floor(np.min(np.array(drift)[:, 0])))
|
|
759
|
+
xpmin = int(np.ceil(np.max(np.array(drift)[:, 0])))
|
|
760
|
+
ypmax = int(rig_reg.shape[1] - -np.floor(np.min(np.array(drift)[:, 1])))
|
|
761
|
+
ypmin = int(np.ceil(np.max(np.array(drift)[:, 1])))
|
|
776
762
|
|
|
777
|
-
xpmin
|
|
778
|
-
xpmax = int(rig_reg.shape[1] - np.ceil(np.max(np.array(drift)[:, 0])))
|
|
779
|
-
ypmin = int(-np.floor(np.min(np.array(drift)[:, 1])))
|
|
780
|
-
ypmax = int(rig_reg.shape[2] - np.ceil(np.max(np.array(drift)[:, 1])))
|
|
781
|
-
|
|
782
|
-
return rig_reg[:, xpmin:xpmax, ypmin:ypmax], [xpmin, xpmax, ypmin, ypmax]
|
|
763
|
+
return rig_reg[:, xpmin:xpmax, ypmin:ypmax:], [xpmin, xpmax, ypmin, ypmax]
|
|
783
764
|
|
|
784
765
|
|
|
785
766
|
class ImageWithLineProfile:
|
|
@@ -902,9 +883,9 @@ def get_profile(dataset, line, spline_order=-1):
|
|
|
902
883
|
"""
|
|
903
884
|
xv, yv = get_line_selection_points(line)
|
|
904
885
|
if dataset.data_type.name == 'IMAGE':
|
|
905
|
-
dataset.get_image_dims()
|
|
906
|
-
xv /=
|
|
907
|
-
yv /=
|
|
886
|
+
image_dims = dataset.get_image_dims(return_axis=True)
|
|
887
|
+
xv /= image_dims[0].slope
|
|
888
|
+
yv /= image_dims[1].slope
|
|
908
889
|
profile = scipy.ndimage.map_coordinates(np.array(dataset), [xv, yv])
|
|
909
890
|
|
|
910
891
|
profile_dataset = sidpy.Dataset.from_array(profile.sum(axis=0))
|
|
@@ -1101,7 +1082,7 @@ def clean_svd(im, pixel_size=1, source_size=5):
|
|
|
1101
1082
|
patch_size = int(source_size/pixel_size)
|
|
1102
1083
|
if patch_size < 3:
|
|
1103
1084
|
patch_size = 3
|
|
1104
|
-
patches = image.extract_patches_2d(im, (patch_size, patch_size))
|
|
1085
|
+
patches = image.extract_patches_2d(np.array(im), (patch_size, patch_size))
|
|
1105
1086
|
patches = patches.reshape(patches.shape[0], patches.shape[1]*patches.shape[2])
|
|
1106
1087
|
|
|
1107
1088
|
num_components = 32
|
|
@@ -1110,6 +1091,8 @@ def clean_svd(im, pixel_size=1, source_size=5):
|
|
|
1110
1091
|
u_im_size = int(np.sqrt(u.shape[0]))
|
|
1111
1092
|
reduced_image = u[:, 0].reshape(u_im_size, u_im_size)
|
|
1112
1093
|
reduced_image = reduced_image/reduced_image.sum()*im.sum()
|
|
1094
|
+
if isinstance(im, sidpy.Dataset):
|
|
1095
|
+
reduced_image = im.like_data(reduced_image)
|
|
1113
1096
|
return reduced_image
|
|
1114
1097
|
|
|
1115
1098
|
|
|
@@ -1402,11 +1385,16 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1402
1385
|
error = np.ones(o_image.shape, dtype=np.complex64)
|
|
1403
1386
|
est = np.ones(o_image.shape, dtype=np.complex64)
|
|
1404
1387
|
source = np.ones(o_image.shape, dtype=np.complex64)
|
|
1388
|
+
o_image = o_image - o_image.min()
|
|
1389
|
+
image_mult = o_image.max()
|
|
1390
|
+
o_image = o_image / o_image.max()
|
|
1405
1391
|
source.real = o_image
|
|
1406
1392
|
|
|
1407
1393
|
response_ft = fftpack.fft2(probe_c)
|
|
1408
1394
|
|
|
1409
|
-
ap_angle = o_image.metadata['experiment']['convergence_angle']
|
|
1395
|
+
ap_angle = o_image.metadata['experiment']['convergence_angle']
|
|
1396
|
+
if ap_angle > .1:
|
|
1397
|
+
ap_angle /= 1000 # now in rad
|
|
1410
1398
|
|
|
1411
1399
|
e0 = float(o_image.metadata['experiment']['acceleration_voltage'])
|
|
1412
1400
|
|
|
@@ -1444,13 +1432,10 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1444
1432
|
est_old = est.copy()
|
|
1445
1433
|
error = source / np.real(fftpack.fftshift(fftpack.ifft2(fftpack.fft2(est) * response_ft)))
|
|
1446
1434
|
est = est * np.real(fftpack.fftshift(fftpack.ifft2(fftpack.fft2(error) * np.conjugate(response_ft))))
|
|
1447
|
-
|
|
1448
|
-
# est = np.real(fftpack.fftshift(fftpack.ifft2(fftpack.fft2(est)*fftpack.fftshift(aperture) )))
|
|
1449
|
-
|
|
1435
|
+
|
|
1450
1436
|
error_new = np.real(np.sum(np.power(error, 2))) - error_old
|
|
1451
1437
|
dest = np.sum(np.power((est - est_old).real, 2)) / np.sum(est) * 100
|
|
1452
|
-
|
|
1453
|
-
|
|
1438
|
+
|
|
1454
1439
|
if error_old != 0:
|
|
1455
1440
|
de = error_new / error_old * 1.0
|
|
1456
1441
|
else:
|
|
@@ -1466,10 +1451,10 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1466
1451
|
print('terminate')
|
|
1467
1452
|
progress.update(1)
|
|
1468
1453
|
progress.write(f"converged in {i} iterations")
|
|
1469
|
-
# progress.close()
|
|
1470
1454
|
print('\n Lucy-Richardson deconvolution converged in ' + str(i) + ' iterations')
|
|
1471
|
-
est2 = np.real(fftpack.ifft2(fftpack.fft2(est) * fftpack.fftshift(aperture)))
|
|
1455
|
+
est2 = np.real(fftpack.ifft2(fftpack.fft2(est) * fftpack.fftshift(aperture)))*image_mult
|
|
1472
1456
|
out_dataset = o_image.like_data(est2)
|
|
1473
1457
|
out_dataset.title = 'Lucy Richardson deconvolution'
|
|
1474
1458
|
out_dataset.data_type = 'image'
|
|
1475
1459
|
return out_dataset
|
|
1460
|
+
|
pyTEMlib/probe_tools.py
CHANGED
|
@@ -107,6 +107,7 @@ def get_chi(ab, size_x, size_y, verbose=False):
|
|
|
107
107
|
return chi, aperture
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
|
|
110
111
|
def print_aberrations(ab):
|
|
111
112
|
from IPython.display import HTML, display
|
|
112
113
|
output = '<html><body>'
|
|
@@ -133,6 +134,203 @@ def print_aberrations(ab):
|
|
|
133
134
|
display(HTML(output))
|
|
134
135
|
|
|
135
136
|
|
|
137
|
+
|
|
138
|
+
def print_aberrations_polar(ab):
|
|
139
|
+
from IPython.display import HTML, display
|
|
140
|
+
|
|
141
|
+
ab['C12_r'], ab['C12_phi'] = cart2pol(ab['C12a'], ab['C12b'])
|
|
142
|
+
ab['C21_r'], ab['C21_phi'] = cart2pol(ab['C21a'], ab['C21b'])
|
|
143
|
+
ab['C23_r'], ab['C23_phi'] = cart2pol(ab['C23a'], ab['C23b'])
|
|
144
|
+
ab['C32_r'], ab['C32_phi'] = cart2pol(ab['C32a'], ab['C32b'])
|
|
145
|
+
ab['C34_r'], ab['C34_phi'] = cart2pol(ab['C34a'], ab['C34b'])
|
|
146
|
+
ab['C41_r'], ab['C41_phi'] = cart2pol(ab['C41a'], ab['C41b'])
|
|
147
|
+
ab['C43_r'], ab['C43_phi'] = cart2pol(ab['C43a'], ab['C43b'])
|
|
148
|
+
ab['C45_r'], ab['C45_phi'] = cart2pol(ab['C45a'], ab['C45b'])
|
|
149
|
+
ab['C52_r'], ab['C52_phi'] = cart2pol(ab['C52a'], ab['C52b'])
|
|
150
|
+
ab['C54_r'], ab['C54_phi'] = cart2pol(ab['C54a'], ab['C54b'])
|
|
151
|
+
ab['C56_r'], ab['C56_phi'] = cart2pol(ab['C56a'], ab['C56b'])
|
|
152
|
+
|
|
153
|
+
output = '<html><body>'
|
|
154
|
+
output += f"Aberrations [nm] for acceleration voltage: {ab['acceleration_voltage'] / 1e3:.0f} kV"
|
|
155
|
+
output += '<table>'
|
|
156
|
+
output += f"<tr><td> C10 </td><td> {ab['C10']:.1f} </tr>"
|
|
157
|
+
output += f"<tr><td> C12(A1): r </td><td> {ab['C12_r']:20.1f} <td> φ </td><td> {ab['C12_phi']:20.1f} </tr>"
|
|
158
|
+
output += f"<tr><td> C21a (B2): r</td><td> {ab['C21_r']:20.1f} <td> φ </td><td> {ab['C21_phi']:20.1f} "
|
|
159
|
+
output += f" <td> C23a (A2) </td><td> {ab['C23_r']:20.1f} <td> φ </td><td> {ab['C23_phi']:20.1f} </tr>"
|
|
160
|
+
output += f"<tr><td> C30 </td><td> {ab['C30']:.1f} </tr>"
|
|
161
|
+
output += f"<tr><td> C32 (S3) </td><td> {ab['C32_r']:20.1f} <td> φ </td><td> {ab['C32_phi']:20.1f} "
|
|
162
|
+
output += f"<td> C34a (A3) </td><td> {ab['C34a']:20.1f} <td> φ </td><td> {ab['C34_phi']:20.1f} </tr>"
|
|
163
|
+
output += f"<tr><td> C41 (B4) </td><td> {ab['C41_r']:.3g} <td> φ </td><td> {ab['C41_phi']:20.1f} "
|
|
164
|
+
output += f" <td> C43 (D4) </td><td> {ab['C43_r']:.3g} <td> φ (D4) </td><td> {ab['C43_phi']:20.1f} "
|
|
165
|
+
output += f" <td> C45 (A4) </td><td> {ab['C45_r']:.3g} <td> φ (A4)</td><td> {ab['C45_phi']:20.1f} </tr>"
|
|
166
|
+
output += f"<tr><td> C50 </td><td> {ab['C50']:.3g} </tr>"
|
|
167
|
+
output += f"<tr><td> C52 </td><td> {ab['C52a']:.3g} <td> φ </td><td> {ab['C52_phi']:20.1f} "
|
|
168
|
+
output += f"<td> C54 </td><td> {ab['C54_r']:.3g} <td> C54 φ </td><td> {ab['C54_phi']:.1f} "
|
|
169
|
+
output += f"<td> C56 </td><td> {ab['C56_r']:.3g} <td> C56 </td><td> {ab['C56_phi']:.1f} </tr>"
|
|
170
|
+
output += f"<tr><td> Cc </td><td> {ab['Cc']:.3g} </tr>"
|
|
171
|
+
|
|
172
|
+
output += '</table></body></html>'
|
|
173
|
+
|
|
174
|
+
display(HTML(output))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def pol2cart(rho, theta):
|
|
178
|
+
x = rho * np.cos(theta)
|
|
179
|
+
y = rho * np.sin(theta)
|
|
180
|
+
return x, y
|
|
181
|
+
|
|
182
|
+
def ceos_to_nion(ab):
|
|
183
|
+
aberrations = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 0, 'C21b': 0, 'C23a': 0, 'C23b': 0, 'C30': 0.,
|
|
184
|
+
'C32a': 0., 'C32b': -0., 'C34a': 0., 'C34b': 0., 'C41a': 0., 'C41b': -0., 'C43a': 0.,
|
|
185
|
+
'C43b': -0., 'C45a': -0., 'C45b': -0., 'C50': 0., 'C52a': -0., 'C52b': 0.,
|
|
186
|
+
'C54a': -0., 'C54b': -0., 'C56a': -0., 'C56b': 0., 'C70': 0.}
|
|
187
|
+
aberrations['acceleration_voltage'] = 200000
|
|
188
|
+
for key in ab.keys():
|
|
189
|
+
if key == 'C1':
|
|
190
|
+
aberrations['C10'] = ab['C10']
|
|
191
|
+
elif key == 'A1-a':
|
|
192
|
+
x, y = pol2cart(ab['A1-a'], ab['A1-p'])
|
|
193
|
+
aberrations['C12a'] = x
|
|
194
|
+
aberrations['C12b'] = y
|
|
195
|
+
elif key == 'B2-a':
|
|
196
|
+
x, y = pol2cart(ab['B2-a'], ab['B2-p'])
|
|
197
|
+
aberrations['C21a'] = 3 * x
|
|
198
|
+
aberrations['C21b'] = 3 * y
|
|
199
|
+
elif key == 'A2-a':
|
|
200
|
+
x, y = pol2cart(ab['A2-a'], ab['A2-p'])
|
|
201
|
+
aberrations['C23a'] = x
|
|
202
|
+
aberrations['C23b'] = y
|
|
203
|
+
elif key == 'C3':
|
|
204
|
+
aberrations['C30'] = ab['C3']
|
|
205
|
+
elif key == 'S3-a':
|
|
206
|
+
x, y = pol2cart(ab['S3-a'], ab['S3-p'])
|
|
207
|
+
aberrations['C32a'] = 4 * x
|
|
208
|
+
aberrations['C32b'] = 4 * y
|
|
209
|
+
elif key == 'A3-a':
|
|
210
|
+
x, y = pol2cart(ab['A3-a'], ab['A3-p'])
|
|
211
|
+
aberrations['C34a'] = x
|
|
212
|
+
aberrations['C34b'] = y
|
|
213
|
+
elif key == 'B4-a':
|
|
214
|
+
x, y = pol2cart(ab['B4-a'], ab['B4-p'])
|
|
215
|
+
aberrations['C41a'] = 4 * x
|
|
216
|
+
aberrations['C41b'] = 4 * y
|
|
217
|
+
elif key == 'D4-a':
|
|
218
|
+
x, y = pol2cart(ab['D4-a'], ab['D4-p'])
|
|
219
|
+
aberrations['C43a'] = 4 * x
|
|
220
|
+
aberrations['C43b'] = 4 * y
|
|
221
|
+
elif key == 'A4-a':
|
|
222
|
+
x, y = pol2cart(ab['A4-a'], ab['A4-p'])
|
|
223
|
+
aberrations['C45a'] = x
|
|
224
|
+
aberrations['C45b'] = y
|
|
225
|
+
elif key == 'C5':
|
|
226
|
+
aberrations['C50'] = ab['C5']
|
|
227
|
+
elif key == 'A5-a':
|
|
228
|
+
x, y = pol2cart(ab['A5-a'], ab['A5-p'])
|
|
229
|
+
aberrations['C56a'] = x
|
|
230
|
+
aberrations['C56b'] = y
|
|
231
|
+
return aberrations
|
|
232
|
+
|
|
233
|
+
def ceos_carth_to_nion(ab):
|
|
234
|
+
aberrations = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 0, 'C21b': 0, 'C23a': 0, 'C23b': 0, 'C30': 0.,
|
|
235
|
+
'C32a': 0., 'C32b': -0., 'C34a': 0., 'C34b': 0., 'C41a': 0., 'C41b': -0., 'C43a': 0.,
|
|
236
|
+
'C43b': -0., 'C45a': -0., 'C45b': -0., 'C50': 0., 'C52a': -0., 'C52b': 0.,
|
|
237
|
+
'C54a': -0., 'C54b': -0., 'C56a': -0., 'C56b': 0., 'C70': 0.}
|
|
238
|
+
aberrations['acceleration_voltage'] = 200000
|
|
239
|
+
for key in ab.keys():
|
|
240
|
+
if key == 'C1':
|
|
241
|
+
aberrations['C10'] = ab['C1'][0]*1e9
|
|
242
|
+
elif key == 'A1':
|
|
243
|
+
aberrations['C12a'] = ab['A1'][0]*1e9
|
|
244
|
+
aberrations['C12b'] = ab['A1'][1]*1e9
|
|
245
|
+
elif key == 'B2':
|
|
246
|
+
print('B2', ab['B2'])
|
|
247
|
+
aberrations['C21a'] = 3 * ab['B2'][0]*1e9
|
|
248
|
+
aberrations['C21b'] = 3 * ab['B2'][1]*1e9
|
|
249
|
+
elif key == 'A2':
|
|
250
|
+
aberrations['C23a'] = ab['A2'][0]*1e9
|
|
251
|
+
aberrations['C23b'] = ab['A2'][1]*1e9
|
|
252
|
+
elif key == 'C3':
|
|
253
|
+
aberrations['C30'] = ab['C3'][0]*1e9
|
|
254
|
+
elif key == 'S3':
|
|
255
|
+
aberrations['C32a'] = 4 * ab['S3'][0]*1e9
|
|
256
|
+
aberrations['C32b'] = 4 * ab['S3'][1]*1e9
|
|
257
|
+
elif key == 'A3':
|
|
258
|
+
aberrations['C34a'] = ab['A3'][0]*1e9
|
|
259
|
+
aberrations['C34b'] = ab['A3'][1]*1e9
|
|
260
|
+
elif key == 'B4':
|
|
261
|
+
aberrations['C41a'] = 4 * ab['B4'][0]*1e9
|
|
262
|
+
aberrations['C41b'] = 4 * ab['B4'][1]*1e9
|
|
263
|
+
elif key == 'D4':
|
|
264
|
+
aberrations['C43a'] = 4 * ab['D4'][0]*1e9
|
|
265
|
+
aberrations['C43b'] = 4 * ab['D4'][1]*1e9
|
|
266
|
+
elif key == 'A4':
|
|
267
|
+
aberrations['C45a'] = ab['A4'][0]*1e9
|
|
268
|
+
aberrations['C45b'] = ab['A4'][1]*1e9
|
|
269
|
+
elif key == 'C5':
|
|
270
|
+
aberrations['C50'] = ab['C5'][0]*1e9
|
|
271
|
+
elif key == 'A5':
|
|
272
|
+
aberrations['C56a'] = ab['A5'][0]*1e9
|
|
273
|
+
aberrations['C56b'] = ab['A5'][1]*1e9
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
return aberrations
|
|
277
|
+
|
|
278
|
+
def cart2pol(x, y):
|
|
279
|
+
theta = np.arctan2(y, x)
|
|
280
|
+
rho = np.hypot(x, y)
|
|
281
|
+
return theta, rho
|
|
282
|
+
|
|
283
|
+
def nion_to_ceos(ab):
|
|
284
|
+
aberrations = {'C1': 0, 'A1-a': 0, 'A1-b': 0, 'B2-a': 0, 'B2-p': 0, 'A2-a': 0, 'A2-p': 0, 'C3': 0.,
|
|
285
|
+
'S3-a': 0., 'S3-p': -0., 'A3-a': 0., 'A3-p': 0., 'B4-a': 0., 'B4-p': -0., 'D4-a': 0.,
|
|
286
|
+
'D4-p': -0., 'A4-s': -0., 'A4-p': -0., 'C5': 0., 'A5-a': -0., 'A5-p': 0.}
|
|
287
|
+
aberrations['acceleration_voltage'] = 200000
|
|
288
|
+
for key in ab.keys():
|
|
289
|
+
if key == 'C10':
|
|
290
|
+
aberrations['C1'] = ab['C10']
|
|
291
|
+
elif key == 'C12a':
|
|
292
|
+
r, p = cart2pol(ab['C12a'], ab['C12b'])
|
|
293
|
+
aberrations['A1-a'] = r
|
|
294
|
+
aberrations['A1-p'] = p
|
|
295
|
+
elif key == 'C21a':
|
|
296
|
+
r, p = cart2pol(ab['C21a'], ab['C21b'])
|
|
297
|
+
aberrations['B2-a'] = r/3
|
|
298
|
+
aberrations['B2-p'] = p
|
|
299
|
+
elif key == 'C23a':
|
|
300
|
+
r, p = cart2pol(ab['C23a'], ab['C23b'])
|
|
301
|
+
aberrations['A2-a'] = r
|
|
302
|
+
aberrations['A2-p'] = p
|
|
303
|
+
elif key == 'C30':
|
|
304
|
+
aberrations['C3'] = ab['C30']
|
|
305
|
+
elif key == 'C32a':
|
|
306
|
+
r, p = cart2pol(ab['C32a'], ab['C32b'])
|
|
307
|
+
aberrations['S3-a'] = r/4
|
|
308
|
+
aberrations['S3-p'] = p
|
|
309
|
+
elif key == 'C34a':
|
|
310
|
+
r, p = cart2pol(ab['C34a'], ab['C34b'])
|
|
311
|
+
aberrations['A3-a'] = r
|
|
312
|
+
aberrations['A3-p'] = p
|
|
313
|
+
elif key == 'C41a':
|
|
314
|
+
r, p = cart2pol(ab['C41a'], ab['C41b'])
|
|
315
|
+
aberrations['B4-a'] = r/4
|
|
316
|
+
aberrations['B4-p'] = p
|
|
317
|
+
elif key == 'C43a':
|
|
318
|
+
r, p = cart2pol(ab['C43a'], ab['C43b'])
|
|
319
|
+
aberrations['D4-a'] = r/4
|
|
320
|
+
aberrations['D4-p'] = p
|
|
321
|
+
elif key == 'C31a':
|
|
322
|
+
r, p = cart2pol(ab['C41a'], ab['C41b'])
|
|
323
|
+
aberrations['A4-a'] = r
|
|
324
|
+
aberrations['A4-p'] = p
|
|
325
|
+
elif key == 'C50':
|
|
326
|
+
aberrations['C5'] = ab['C50']
|
|
327
|
+
elif key == 'C56a':
|
|
328
|
+
r, p = cart2pol(ab['C56a'], ab['C56b'])
|
|
329
|
+
aberrations['A5-a'] = r
|
|
330
|
+
aberrations['A5-p'] = p
|
|
331
|
+
|
|
332
|
+
return aberrations
|
|
333
|
+
|
|
136
334
|
def get_ronchigram(size, ab, scale='mrad'):
|
|
137
335
|
""" Get Ronchigram
|
|
138
336
|
|
|
@@ -447,15 +645,13 @@ def get_target_aberrations(TEM_name, acceleration_voltage):
|
|
|
447
645
|
'C30': 123,
|
|
448
646
|
'C32a': 95.3047364258614, 'C32b': -189.72105710231244, 'C34a': -47.45099594807912, 'C34b': -94.67424667529909,
|
|
449
647
|
'C41a': -905.31842572806, 'C41b': 981.316128853203, 'C43a': 4021.8433526960034, 'C43b': 131.72716642732158,
|
|
450
|
-
'C45a': -4702.390968272048, 'C45b': -208.25028574642903, 'C50': -
|
|
451
|
-
'C50': 552000., 'C52a': -0., 'C52b': 0.,
|
|
648
|
+
'C45a': -4702.390968272048, 'C45b': -208.25028574642903, 'C50': 552000., 'C52a': -0., 'C52b': 0.,
|
|
452
649
|
'C54a': -0., 'C54b': -0., 'C56a': -36663.643489934424, 'C56b': 21356.079837905396,
|
|
453
650
|
'acceleration_voltage': 200000,
|
|
454
651
|
'FOV': 34.241659495148205,
|
|
455
652
|
'Cc': 1* 1e6,
|
|
456
653
|
'convergence_angle': 30,
|
|
457
654
|
'wavelength': 0.0025079340450548005}
|
|
458
|
-
|
|
459
655
|
return ab
|
|
460
656
|
|
|
461
657
|
|
|
@@ -488,7 +684,7 @@ def get_ronchigram_2(size, ab, scale='mrad', threshold=3):
|
|
|
488
684
|
aperture[mask] = 0.
|
|
489
685
|
|
|
490
686
|
v_noise = np.random.rand(size_x, size_y)
|
|
491
|
-
smoothing =
|
|
687
|
+
smoothing = 10
|
|
492
688
|
phi_r = ndimage.gaussian_filter(v_noise, sigma=(smoothing, smoothing), order=0)
|
|
493
689
|
|
|
494
690
|
sigma = 6 # 6 for carbon and thin
|
pyTEMlib/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyTEMlib
|
|
3
|
-
Version: 0.2025.
|
|
3
|
+
Version: 0.2025.4.0
|
|
4
4
|
Summary: pyTEM: TEM Data Quantification library through a model-based approach
|
|
5
5
|
Home-page: https://pycroscopy.github.io/pyTEMlib/about.html
|
|
6
6
|
Author: Gerd Duscher
|
|
@@ -39,7 +39,7 @@ Requires-Dist: spglib
|
|
|
39
39
|
Requires-Dist: scikit-image
|
|
40
40
|
Requires-Dist: scikit-learn
|
|
41
41
|
Requires-Dist: pyNSID>=0.0.7
|
|
42
|
-
Requires-Dist: sidpy>=0.12.
|
|
42
|
+
Requires-Dist: sidpy>=0.12.7
|
|
43
43
|
Requires-Dist: SciFiReaders>=0.0.8
|
|
44
44
|
Dynamic: author
|
|
45
45
|
Dynamic: author-email
|
|
@@ -48,6 +48,7 @@ Dynamic: description
|
|
|
48
48
|
Dynamic: home-page
|
|
49
49
|
Dynamic: keywords
|
|
50
50
|
Dynamic: license
|
|
51
|
+
Dynamic: license-file
|
|
51
52
|
Dynamic: platform
|
|
52
53
|
Dynamic: requires-dist
|
|
53
54
|
Dynamic: summary
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
pyTEMlib/__init__.py,sha256=nEN93amIEoZxO7rJgN71ABeCoXrnmaywBbE97l5lPio,178
|
|
2
2
|
pyTEMlib/animation.py,sha256=G9ykYo6yB5jexjKienShO9C2b8xEXKbf7t47apwcwTw,25188
|
|
3
|
-
pyTEMlib/atom_tools.py,sha256=
|
|
3
|
+
pyTEMlib/atom_tools.py,sha256=iXJbK66k3HbTNOGSWRLeUV01JlggUo1Qq89HJqbSv54,7341
|
|
4
4
|
pyTEMlib/config_dir.py,sha256=4evlo9P2Yht-AnqaLI-WweLjDQcislbAP3I7P7EZsPU,2085
|
|
5
|
-
pyTEMlib/core_loss_widget.py,sha256=
|
|
5
|
+
pyTEMlib/core_loss_widget.py,sha256=XQk071ZHMGYOM6RTSIaXQlZtnFQjUyxTWbQ2ZXOFMfU,30901
|
|
6
6
|
pyTEMlib/crystal_tools.py,sha256=g4OXyvd5NLw7vaXhjDP3P6VZpVV6eiyuPn8MdgR2amI,61652
|
|
7
7
|
pyTEMlib/diffraction_plot.py,sha256=pM5d3bdBGa8LlPZ5lw8sLT94mlYTXELxPLv-jUP2FWY,27959
|
|
8
8
|
pyTEMlib/dynamic_scattering.py,sha256=O9MxnxfndWJ2VhQRjksKNQ4yY7y-gN_hitRQ4Qox4ns,9472
|
|
9
9
|
pyTEMlib/eds_tools.py,sha256=Ilof2Cars-1ILXx5g2RsU2G4BgrPwjOHgQ7-OabmbrU,28000
|
|
10
|
-
pyTEMlib/eels_dialog.py,sha256=
|
|
10
|
+
pyTEMlib/eels_dialog.py,sha256=QG_PU3uuzus_3I3zjfaxb2a9iYq8B053zYw-B52JklM,32595
|
|
11
11
|
pyTEMlib/eels_dialog_utilities.py,sha256=73W9jFbPx-eeLEiSaBptTgGLr40bIYYfSyzLnZbhfvo,51761
|
|
12
|
-
pyTEMlib/eels_tools.py,sha256=
|
|
13
|
-
pyTEMlib/file_tools.py,sha256=
|
|
12
|
+
pyTEMlib/eels_tools.py,sha256=pl75ZDQHz1JngxD84T_595Kfrl5TEJSETPGgkXfBtrA,88688
|
|
13
|
+
pyTEMlib/file_tools.py,sha256=AIeWD3JaB1OI32_pj_RSqQuRGjPM-wiD0zTbL5W17Tc,64559
|
|
14
14
|
pyTEMlib/file_tools_qt.py,sha256=tLZACS4JyGH_AOzNR_SGAhjA01y4VJB261opPhGMlm8,7223
|
|
15
|
-
pyTEMlib/graph_tools.py,sha256=
|
|
15
|
+
pyTEMlib/graph_tools.py,sha256=VWuTgFGeu4gn4cfRgf-76kO6u2B1ZV_dz6gLfx2k4NY,46570
|
|
16
16
|
pyTEMlib/graph_viz.py,sha256=m5PwSn6l2r0bsaLWBDSHc9IGR3_PneG2BrZgnEdi07I,13644
|
|
17
17
|
pyTEMlib/image_dialog.py,sha256=F-ZgKq7UnMtPPd1b9eqb7t8MXDfWN-8hVKwB2Il0x28,6235
|
|
18
18
|
pyTEMlib/image_dlg.py,sha256=n5gradDiYOFGEQ3k_Wlk9RUYYzl4bl_hKLzNVcYteNE,5694
|
|
19
|
-
pyTEMlib/image_tools.py,sha256=
|
|
19
|
+
pyTEMlib/image_tools.py,sha256=iBVsAr6-oQY6y2LTSbUIz6nMeAIOsSX5zPBezekJsFg,52334
|
|
20
20
|
pyTEMlib/info_widget.py,sha256=lkzQOuNVlkaasiZDtc5UtYk541-plYNfnW4DQwQB_iA,53467
|
|
21
21
|
pyTEMlib/info_widget3.py,sha256=QSbdSj6m57KQTir2fNhulVgjOu9EQL31c-9SzTlghnE,55495
|
|
22
22
|
pyTEMlib/interactive_image.py,sha256=5PwypcA1OjLAD-fi8bmWWFHuOjdIPVY9Dh59V24WuDA,34
|
|
@@ -25,14 +25,14 @@ pyTEMlib/low_loss_widget.py,sha256=0SxHOAuUnuNjDrJSNS2PeWD6hjyuB5FCYxmVfsDTq5c,2
|
|
|
25
25
|
pyTEMlib/microscope.py,sha256=iigUF1UImHEfmL2wqEBBj3aNRgEYouDbIln8VCo4_KM,1545
|
|
26
26
|
pyTEMlib/peak_dialog.py,sha256=r3mhYvECC9niFFItS1oGa96F2nFthsE5kfpA3yVXQaE,51872
|
|
27
27
|
pyTEMlib/peak_dlg.py,sha256=qcjcnhwpGa4jBCeXzwQz9sCyX-tHsLLQ67ToqfKOiQY,11550
|
|
28
|
-
pyTEMlib/probe_tools.py,sha256=
|
|
28
|
+
pyTEMlib/probe_tools.py,sha256=sDW9CW3SMwjvSHYcEufceismHv_LVkqxcS-gCtEklCg,37926
|
|
29
29
|
pyTEMlib/sidpy_tools.py,sha256=0oIx-qMtEmcZmLazQKW19dd-KoxyY3B15aIeMcyHA8E,4878
|
|
30
30
|
pyTEMlib/simulation_tools.py,sha256=RmegD5TpQMU68uASvzZWVplAqs7bM5KkF6bWDWLjyc0,2799
|
|
31
|
-
pyTEMlib/version.py,sha256=
|
|
31
|
+
pyTEMlib/version.py,sha256=znL_4Pt7sBR4l1I39FM04SGxnneUfbZrv9R_6tFaynA,94
|
|
32
32
|
pyTEMlib/xrpa_x_sections.py,sha256=m4gaH7gaJiNi-CsIT9aKoH4fB6MQIAe876kxEmzSebI,1825392
|
|
33
|
-
pytemlib-0.2025.
|
|
34
|
-
pytemlib-0.2025.
|
|
35
|
-
pytemlib-0.2025.
|
|
36
|
-
pytemlib-0.2025.
|
|
37
|
-
pytemlib-0.2025.
|
|
38
|
-
pytemlib-0.2025.
|
|
33
|
+
pytemlib-0.2025.4.0.dist-info/licenses/LICENSE,sha256=7HdBF6SXIBd38bHOKkQd4DYR1KV-OYm9mwB16fM-984,1062
|
|
34
|
+
pytemlib-0.2025.4.0.dist-info/METADATA,sha256=mV1ni5hE1q19Z8qOzNfWGb9YAOy0rv69JZd3J1e14MM,3515
|
|
35
|
+
pytemlib-0.2025.4.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
|
36
|
+
pytemlib-0.2025.4.0.dist-info/entry_points.txt,sha256=zn2yO1IWTutI3c7C9e3GdARCvm43JURoOhqQ8YylV4Y,43
|
|
37
|
+
pytemlib-0.2025.4.0.dist-info/top_level.txt,sha256=rPLVH0UJxrPSPgSoKScTjL1K_X69JFzsYYnDnYTYIlU,9
|
|
38
|
+
pytemlib-0.2025.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|