pyTEMlib 0.2023.4.0__py2.py3-none-any.whl → 0.2023.8.0__py2.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/crystal_tools.py +47 -5
- pyTEMlib/eels_dialog.py +656 -41
- pyTEMlib/eels_dialog_utilities.py +8 -4
- pyTEMlib/eels_dlg.py +4 -3
- pyTEMlib/eels_tools.py +93 -39
- pyTEMlib/file_tools.py +95 -13
- pyTEMlib/graph_tools.py +716 -10
- pyTEMlib/image_dialog.py +1 -1
- pyTEMlib/image_dlg.py +1 -1
- pyTEMlib/image_tools.py +82 -39
- pyTEMlib/info_dialog.py +356 -15
- pyTEMlib/info_dlg.py +1 -1
- pyTEMlib/info_widget.py +655 -0
- pyTEMlib/interactive_eels.py +12 -4
- pyTEMlib/peak_dialog.py +621 -22
- pyTEMlib/peak_dlg.py +1 -1
- pyTEMlib/version.py +2 -2
- pyTEMlib/viz.py +217 -0
- {pyTEMlib-0.2023.4.0.dist-info → pyTEMlib-0.2023.8.0.dist-info}/METADATA +5 -5
- pyTEMlib-0.2023.8.0.dist-info/RECORD +40 -0
- {pyTEMlib-0.2023.4.0.dist-info → pyTEMlib-0.2023.8.0.dist-info}/WHEEL +1 -1
- pyTEMlib-0.2023.4.0.dist-info/RECORD +0 -39
- {pyTEMlib-0.2023.4.0.dist-info → pyTEMlib-0.2023.8.0.dist-info}/LICENSE +0 -0
- {pyTEMlib-0.2023.4.0.dist-info → pyTEMlib-0.2023.8.0.dist-info}/entry_points.txt +0 -0
- {pyTEMlib-0.2023.4.0.dist-info → pyTEMlib-0.2023.8.0.dist-info}/top_level.txt +0 -0
pyTEMlib/image_dialog.py
CHANGED
pyTEMlib/image_dlg.py
CHANGED
pyTEMlib/image_tools.py
CHANGED
|
@@ -446,9 +446,9 @@ def demon_registration(dataset, verbose=False):
|
|
|
446
446
|
|
|
447
447
|
def rigid_registration(dataset):
|
|
448
448
|
"""
|
|
449
|
-
Rigid registration of image stack with
|
|
449
|
+
Rigid registration of image stack with pixel accuracy
|
|
450
450
|
|
|
451
|
-
Uses
|
|
451
|
+
Uses simple cross_correlation
|
|
452
452
|
(we determine drift from one image to next)
|
|
453
453
|
|
|
454
454
|
Parameters
|
|
@@ -461,56 +461,71 @@ def rigid_registration(dataset):
|
|
|
461
461
|
rigid_registered: sidpy.Dataset
|
|
462
462
|
Registered Stack and drift (with respect to center image)
|
|
463
463
|
"""
|
|
464
|
-
|
|
464
|
+
|
|
465
465
|
if not isinstance(dataset, sidpy.Dataset):
|
|
466
466
|
raise TypeError('We need a sidpy.Dataset')
|
|
467
467
|
if dataset.data_type.name != 'IMAGE_STACK':
|
|
468
468
|
raise TypeError('Registration makes only sense for an image stack')
|
|
469
|
-
|
|
470
|
-
frame_dim =
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
469
|
+
|
|
470
|
+
frame_dim = []
|
|
471
|
+
spatial_dim = []
|
|
472
|
+
selection = []
|
|
473
|
+
|
|
474
|
+
for i, axis in dataset._axes.items():
|
|
475
|
+
if axis.dimension_type.name == 'SPATIAL':
|
|
476
|
+
spatial_dim.append(i)
|
|
477
|
+
selection.append(slice(None))
|
|
478
|
+
else:
|
|
479
|
+
frame_dim.append(i)
|
|
480
|
+
selection.append(slice(0, 1))
|
|
481
|
+
|
|
482
|
+
if len(spatial_dim) != 2:
|
|
483
|
+
print('need two spatial dimensions')
|
|
484
|
+
if len(frame_dim) != 1:
|
|
485
|
+
print('need one frame dimensions')
|
|
486
|
+
|
|
487
|
+
nopix = dataset.shape[spatial_dim[0]]
|
|
488
|
+
nopiy = dataset.shape[spatial_dim[1]]
|
|
475
489
|
nimages = dataset.shape[frame_dim[0]]
|
|
476
|
-
|
|
490
|
+
|
|
477
491
|
print('Stack contains ', nimages, ' images, each with', nopix, ' pixels in x-direction and ', nopiy,
|
|
478
492
|
' pixels in y-direction')
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
fft_fixed = np.fft.fft2(fixed)
|
|
484
|
-
|
|
493
|
+
|
|
494
|
+
fixed = dataset[tuple(selection)].squeeze().compute()
|
|
495
|
+
fft_fixed = np.fft.fft2(fixed)
|
|
496
|
+
|
|
485
497
|
relative_drift = [[0., 0.]]
|
|
486
|
-
|
|
498
|
+
|
|
487
499
|
for i in trange(nimages):
|
|
488
|
-
|
|
500
|
+
selection[frame_dim[0]] = slice(i, i+1)
|
|
501
|
+
moving = dataset[tuple(selection)].squeeze().compute()
|
|
489
502
|
fft_moving = np.fft.fft2(moving)
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
shift = registration.phase_cross_correlation(fft_fixed, fft_moving, upsample_factor=1000, space='fourier')
|
|
503
|
+
image_product = fft_fixed * fft_moving.conj()
|
|
504
|
+
cc_image = np.fft.fftshift(np.fft.ifft2(image_product))
|
|
505
|
+
shift =np.array(ndimage.maximum_position(cc_image.real))-cc_image.shape[0]/2
|
|
494
506
|
fft_fixed = fft_moving
|
|
495
|
-
relative_drift.append(shift
|
|
496
|
-
|
|
497
|
-
rig_reg, drift = rig_reg_drift(data_array, relative_drift)
|
|
507
|
+
relative_drift.append(shift)
|
|
508
|
+
rig_reg, drift = rig_reg_drift(dataset, relative_drift)
|
|
498
509
|
|
|
510
|
+
|
|
499
511
|
crop_reg, input_crop = crop_image_stack(rig_reg, drift)
|
|
500
|
-
|
|
501
|
-
rigid_registered =
|
|
502
|
-
rigid_registered.set_dimension(0, dataset._axes[frame_dim[0]])
|
|
503
|
-
rigid_registered.set_dimension(1, dataset._axes[image_dims[0]][input_crop[0]:input_crop[1]])
|
|
504
|
-
rigid_registered.set_dimension(2, dataset._axes[image_dims[1]][input_crop[2]:input_crop[3]])
|
|
505
|
-
rigid_registered.data_type = 'Image_stack'
|
|
506
|
-
|
|
512
|
+
|
|
513
|
+
rigid_registered = dataset.like_data(crop_reg)
|
|
507
514
|
rigid_registered.title = 'Rigid Registration'
|
|
508
515
|
rigid_registered.source = dataset.title
|
|
509
516
|
rigid_registered.metadata = {'analysis': 'rigid sub-pixel registration', 'drift': drift,
|
|
510
|
-
'input_crop': input_crop, 'input_shape':
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
517
|
+
'input_crop': input_crop, 'input_shape': dataset.shape[1:]}
|
|
518
|
+
if hasattr(rigid_registered, 'z'):
|
|
519
|
+
del rigid_registered.z
|
|
520
|
+
if hasattr(rigid_registered, 'x'):
|
|
521
|
+
del rigid_registered.x
|
|
522
|
+
if hasattr(rigid_registered, 'y'):
|
|
523
|
+
del rigid_registered.y
|
|
524
|
+
# rigid_registered._axes = {}
|
|
525
|
+
rigid_registered.set_dimension(0, dataset._axes[frame_dim[0]])
|
|
526
|
+
rigid_registered.set_dimension(1, dataset._axes[spatial_dim[0]][input_crop[0]:input_crop[1]])
|
|
527
|
+
rigid_registered.set_dimension(2, dataset._axes[spatial_dim[1]][input_crop[2]:input_crop[3]])
|
|
528
|
+
return rigid_registered.rechunk({0: 'auto', 1: -1, 2: -1})
|
|
514
529
|
|
|
515
530
|
def rig_reg_drift(dset, rel_drift):
|
|
516
531
|
""" Shifting images on top of each other
|
|
@@ -564,7 +579,7 @@ def rig_reg_drift(dset, rel_drift):
|
|
|
564
579
|
for i in range(rig_reg.shape[0]):
|
|
565
580
|
selection[frame_dim[0]] = slice(i, i+1)
|
|
566
581
|
# Now we shift
|
|
567
|
-
rig_reg[i, :, :] = ndimage.shift(dset[tuple(selection)].squeeze(), [drift[i, 0], drift[i, 1]], order=3)
|
|
582
|
+
rig_reg[i, :, :] = ndimage.shift(dset[tuple(selection)].squeeze().compute(), [drift[i, 0], drift[i, 1]], order=3)
|
|
568
583
|
return rig_reg, drift
|
|
569
584
|
|
|
570
585
|
|
|
@@ -590,7 +605,6 @@ def crop_image_stack(rig_reg, drift):
|
|
|
590
605
|
|
|
591
606
|
return rig_reg[:, xpmin:xpmax, ypmin:ypmax], [xpmin, xpmax, ypmin, ypmax]
|
|
592
607
|
|
|
593
|
-
|
|
594
608
|
class ImageWithLineProfile:
|
|
595
609
|
"""Image with line profile"""
|
|
596
610
|
|
|
@@ -795,6 +809,7 @@ def cart2pol(points):
|
|
|
795
809
|
|
|
796
810
|
rho = np.linalg.norm(points[:, 0:2], axis=1)
|
|
797
811
|
phi = np.arctan2(points[:, 1], points[:, 0])
|
|
812
|
+
|
|
798
813
|
return rho, phi
|
|
799
814
|
|
|
800
815
|
|
|
@@ -839,7 +854,7 @@ def xy2polar(points, rounding=1e-3):
|
|
|
839
854
|
|
|
840
855
|
r, phi = cart2pol(points)
|
|
841
856
|
|
|
842
|
-
phi = phi
|
|
857
|
+
phi = phi # %np.pi # only positive angles
|
|
843
858
|
r = (np.floor(r/rounding))*rounding # Remove rounding error differences
|
|
844
859
|
|
|
845
860
|
sorted_indices = np.lexsort((phi, r)) # sort first by r and then by phi
|
|
@@ -926,6 +941,33 @@ def calculate_scherzer(wavelength, cs):
|
|
|
926
941
|
return scherzer
|
|
927
942
|
|
|
928
943
|
|
|
944
|
+
def get_rotation(experiment_spots, crystal_spots):
|
|
945
|
+
"""Get rotation by comparing spots in diffractogram to diffraction Bragg spots
|
|
946
|
+
|
|
947
|
+
Parameter
|
|
948
|
+
---------
|
|
949
|
+
experiment_spots: numpy array (nx2)
|
|
950
|
+
positions (in 1/nm) of spots in diffractogram
|
|
951
|
+
crystal_spots: numpy array (nx2)
|
|
952
|
+
positions (in 1/nm) of Bragg spots according to kinematic scattering theory
|
|
953
|
+
|
|
954
|
+
"""
|
|
955
|
+
|
|
956
|
+
r_experiment, phi_experiment = cart2pol(experiment_spots)
|
|
957
|
+
|
|
958
|
+
# get crystal spots of same length and sort them by angle as well
|
|
959
|
+
r_crystal, phi_crystal, crystal_indices = xy2polar(crystal_spots)
|
|
960
|
+
angle_index = np.argmin(np.abs(r_experiment-r_crystal[1]) )
|
|
961
|
+
rotation_angle = phi_experiment[angle_index]%(2*np.pi) - phi_crystal[1]
|
|
962
|
+
print(phi_experiment[angle_index])
|
|
963
|
+
st = np.sin(rotation_angle)
|
|
964
|
+
ct = np.cos(rotation_angle)
|
|
965
|
+
rotation_matrix = np.array([[ct, -st], [st, ct]])
|
|
966
|
+
|
|
967
|
+
return rotation_matrix, rotation_angle
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
|
|
929
971
|
def calibrate_image_scale(fft_tags, spots_reference, spots_experiment):
|
|
930
972
|
"""depreciated get change of scale from comparison of spots to Bragg angles """
|
|
931
973
|
gx = fft_tags['spatial_scale_x']
|
|
@@ -947,6 +989,7 @@ def calibrate_image_scale(fft_tags, spots_reference, spots_experiment):
|
|
|
947
989
|
return dg
|
|
948
990
|
|
|
949
991
|
|
|
992
|
+
|
|
950
993
|
def align_crystal_reflections(spots, crystals):
|
|
951
994
|
""" Depreciated - use diffraction spots"""
|
|
952
995
|
|
pyTEMlib/info_dialog.py
CHANGED
|
@@ -12,17 +12,21 @@ try:
|
|
|
12
12
|
from PyQt5 import QtCore, QtWidgets
|
|
13
13
|
except:
|
|
14
14
|
Qt_available = False
|
|
15
|
-
print('Qt dialogs are not available')
|
|
15
|
+
# print('Qt dialogs are not available')
|
|
16
|
+
|
|
16
17
|
|
|
17
|
-
from pyTEMlib import info_dlg
|
|
18
18
|
import pyTEMlib.eels_dialog_utilities as ieels
|
|
19
19
|
from pyTEMlib.microscope import microscope
|
|
20
|
-
|
|
20
|
+
import ipywidgets
|
|
21
|
+
import matplotlib.pylab as plt
|
|
22
|
+
import matplotlib
|
|
23
|
+
from IPython.display import display
|
|
21
24
|
from pyTEMlib import file_tools as ft
|
|
22
25
|
_version = 000
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
if Qt_available:
|
|
29
|
+
from pyTEMlib import info_dlg
|
|
26
30
|
class InfoDialog(QtWidgets.QDialog):
|
|
27
31
|
"""
|
|
28
32
|
Input Dialog for EELS Analysis
|
|
@@ -40,7 +44,6 @@ if Qt_available:
|
|
|
40
44
|
self.set_action()
|
|
41
45
|
self.datasets = datasets
|
|
42
46
|
|
|
43
|
-
|
|
44
47
|
self.spec_dim = []
|
|
45
48
|
self.energy_scale = np.array([])
|
|
46
49
|
self.experiment = {}
|
|
@@ -55,6 +58,8 @@ if Qt_available:
|
|
|
55
58
|
# make a dummy dataset for testing
|
|
56
59
|
key = 'Channel_000'
|
|
57
60
|
self.datasets={key: ft.make_dummy_dataset(sidpy.DataType.SPECTRUM)}
|
|
61
|
+
if key is None:
|
|
62
|
+
key = list(self.datasets.keys())[0]
|
|
58
63
|
self.dataset = self.datasets[key]
|
|
59
64
|
self.key = key
|
|
60
65
|
if not isinstance(self.dataset, sidpy.Dataset):
|
|
@@ -244,21 +249,24 @@ if Qt_available:
|
|
|
244
249
|
flux_dataset = self.datasets[key]
|
|
245
250
|
if flux_dataset.data_type.name == 'IMAGE' or 'SPECTRUM' in flux_dataset.data_type.name:
|
|
246
251
|
if 'exposure_time' in flux_dataset.metadata['experiment']:
|
|
247
|
-
|
|
252
|
+
if 'number_of_frames' in flux_dataset.metadata['experiment']:
|
|
253
|
+
exposure_time = flux_dataset.metadata['experiment']['single_exposure_time'] * flux_dataset.metadata['experiment']['number_of_frames']
|
|
254
|
+
else:
|
|
255
|
+
exposure_time = flux_dataset.metadata['experiment']['exposure_time']
|
|
248
256
|
else:
|
|
249
|
-
exposure_time = 1.0
|
|
257
|
+
exposure_time = -1.0
|
|
250
258
|
flux_dataset.metadata['experiment']['exposure_time'] = -1
|
|
251
259
|
print('Did not find exposure time assume 1s')
|
|
252
260
|
if exposure_time > 0:
|
|
253
|
-
new_flux = np.sum(np.array(flux_dataset*1e-6))/exposure_time*exposure_time
|
|
261
|
+
new_flux = np.sum(np.array(flux_dataset*1e-6))/exposure_time*self.dataset.metadata['experiment']['exposure_time']
|
|
254
262
|
title = flux_dataset.title
|
|
255
263
|
metadata = flux_dataset.metadata
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
264
|
+
self.experiment['flux_ppm'] = new_flux
|
|
265
|
+
self.experiment['flux_units'] = 'Mcounts '
|
|
266
|
+
self.experiment['flux_source'] = title
|
|
267
|
+
self.experiment['flux_metadata'] = metadata
|
|
260
268
|
|
|
261
|
-
|
|
269
|
+
self.update()
|
|
262
270
|
|
|
263
271
|
def on_check(self):
|
|
264
272
|
sender = self.sender()
|
|
@@ -290,15 +298,13 @@ if Qt_available:
|
|
|
290
298
|
index = self.ui.select_flux.currentIndex()
|
|
291
299
|
self.ui.statusBar.showMessage('list'+str(index))
|
|
292
300
|
if index == 1:
|
|
293
|
-
ft.add_dataset_from_file(self.datasets,
|
|
301
|
+
ft.add_dataset_from_file(self.datasets, key_name='Reference')
|
|
294
302
|
self.set_flux_list()
|
|
295
303
|
else:
|
|
296
304
|
key = str(self.ui.select_flux.currentText()).split(':')[0]
|
|
297
305
|
self.set_flux(key)
|
|
298
306
|
|
|
299
307
|
self.update()
|
|
300
|
-
|
|
301
|
-
|
|
302
308
|
|
|
303
309
|
def set_action(self):
|
|
304
310
|
self.ui.statusBar.showMessage('action')
|
|
@@ -322,3 +328,338 @@ if Qt_available:
|
|
|
322
328
|
|
|
323
329
|
self.ui.binXEdit.editingFinished.connect(self.on_enter)
|
|
324
330
|
self.ui.binYEdit.editingFinished.connect(self.on_enter)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def get_sidebar():
|
|
334
|
+
side_bar = ipywidgets.GridspecLayout(17, 3,width='auto', grid_gap="0px")
|
|
335
|
+
|
|
336
|
+
side_bar[0, :2] = ipywidgets.Dropdown(
|
|
337
|
+
options=[('None', 0)],
|
|
338
|
+
value=0,
|
|
339
|
+
description='Main Dataset:',
|
|
340
|
+
disabled=False)
|
|
341
|
+
|
|
342
|
+
row = 1
|
|
343
|
+
side_bar[row, :3] = ipywidgets.Button(description='Energy Scale',
|
|
344
|
+
layout=ipywidgets.Layout(width='auto', grid_area='header'),
|
|
345
|
+
style=ipywidgets.ButtonStyle(button_color='lightblue'))
|
|
346
|
+
row += 1
|
|
347
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=7.5,description='Offset:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
348
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
|
|
349
|
+
row += 1
|
|
350
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Dispersion:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
351
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
|
|
352
|
+
|
|
353
|
+
row += 1
|
|
354
|
+
side_bar[row, :3] = ipywidgets.Button(description='Microscope',
|
|
355
|
+
layout=ipywidgets.Layout(width='auto', grid_area='header'),
|
|
356
|
+
style=ipywidgets.ButtonStyle(button_color='lightblue'))
|
|
357
|
+
row += 1
|
|
358
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=7.5,description='Conv.Angle:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
359
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="mrad", layout=ipywidgets.Layout(width='100px'))
|
|
360
|
+
row += 1
|
|
361
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Coll.Angle:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
362
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="mrad", layout=ipywidgets.Layout(width='100px'))
|
|
363
|
+
row += 1
|
|
364
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Acc Voltage:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
365
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="keV", layout=ipywidgets.Layout(width='100px'))
|
|
366
|
+
row += 1
|
|
367
|
+
|
|
368
|
+
side_bar[row, :3] = ipywidgets.Button(description='Quantification',
|
|
369
|
+
layout=ipywidgets.Layout(width='auto', grid_area='header'),
|
|
370
|
+
style=ipywidgets.ButtonStyle(button_color='lightblue'))
|
|
371
|
+
row+=1
|
|
372
|
+
side_bar[row, :2] = ipywidgets.Dropdown(
|
|
373
|
+
options=[('None', 0)],
|
|
374
|
+
value=0,
|
|
375
|
+
description='Reference:',
|
|
376
|
+
disabled=False)
|
|
377
|
+
side_bar[row,2] = ipywidgets.ToggleButton(
|
|
378
|
+
description='Probability',
|
|
379
|
+
disabled=False,
|
|
380
|
+
button_style='', # 'success', 'info', 'warning', 'danger' or ''
|
|
381
|
+
tooltip='Changes y-axis to probability if flux is given',
|
|
382
|
+
layout=ipywidgets.Layout(width='100px'))
|
|
383
|
+
row += 1
|
|
384
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Exp_Time:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
385
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="s", layout=ipywidgets.Layout(width='100px'))
|
|
386
|
+
row += 1
|
|
387
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=7.5,description='Flux:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
388
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="Mcounts", layout=ipywidgets.Layout(width='100px'))
|
|
389
|
+
row += 1
|
|
390
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Conversion:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
391
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value=r"e$^-$/counts", layout=ipywidgets.Layout(width='100px'))
|
|
392
|
+
row += 1
|
|
393
|
+
side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Current:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
394
|
+
side_bar[row, 2] = ipywidgets.widgets.Label(value="pA", layout=ipywidgets.Layout(width='100px') )
|
|
395
|
+
|
|
396
|
+
row += 1
|
|
397
|
+
|
|
398
|
+
side_bar[row, :3] = ipywidgets.Button(description='Spectrum Image',
|
|
399
|
+
layout=ipywidgets.Layout(width='auto', grid_area='header'),
|
|
400
|
+
style=ipywidgets.ButtonStyle(button_color='lightblue'))
|
|
401
|
+
|
|
402
|
+
row += 1
|
|
403
|
+
side_bar[row, :2] = ipywidgets.IntText(value=1, description='bin X:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
404
|
+
row += 1
|
|
405
|
+
side_bar[row, :2] = ipywidgets.IntText(value=1, description='bin X:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
406
|
+
|
|
407
|
+
for i in range(14, 17):
|
|
408
|
+
side_bar[i, 0].layout.display = "none"
|
|
409
|
+
return side_bar
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class SpectrumPlot(sidpy.viz.dataset_viz.CurveVisualizer):
|
|
413
|
+
def __init__(self, dset, spectrum_number=0, figure=None, **kwargs):
|
|
414
|
+
with plt.ioff():
|
|
415
|
+
self.figure = plt.figure()
|
|
416
|
+
self.figure.canvas.toolbar_position = 'right'
|
|
417
|
+
self.figure.canvas.toolbar_visible = True
|
|
418
|
+
|
|
419
|
+
super().__init__(dset, spectrum_number=spectrum_number, figure=self.figure, **kwargs)
|
|
420
|
+
|
|
421
|
+
self.start_cursor = ipywidgets.FloatText(value=0, description='Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
422
|
+
self.end_cursor = ipywidgets.FloatText(value=0, description='End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
423
|
+
self.panel = ipywidgets.VBox([ipywidgets.HBox([ipywidgets.Label('',layout=ipywidgets.Layout(width='100px')), ipywidgets.Label('Cursor:'),
|
|
424
|
+
self.start_cursor,ipywidgets.Label('eV'),
|
|
425
|
+
self.end_cursor, ipywidgets.Label('eV')]),
|
|
426
|
+
self.figure.canvas])
|
|
427
|
+
|
|
428
|
+
self.selector = matplotlib.widgets.SpanSelector(self.axis, self.line_select_callback,
|
|
429
|
+
direction="horizontal",
|
|
430
|
+
interactive=True,
|
|
431
|
+
props=dict(facecolor='blue', alpha=0.2))
|
|
432
|
+
#self.axis.legend()
|
|
433
|
+
display(self.panel)
|
|
434
|
+
|
|
435
|
+
def line_select_callback(self, x_min, x_max):
|
|
436
|
+
self.start_cursor.value = np.round(x_min, 3)
|
|
437
|
+
self.end_cursor.value = np.round(x_max, 3)
|
|
438
|
+
self.start_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.start_cursor.value)
|
|
439
|
+
self.end_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.end_cursor.value)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class SIPlot(sidpy.viz.dataset_viz.SpectralImageVisualizer):
|
|
443
|
+
def __init__(self, dset, figure=None, horizontal=True, **kwargs):
|
|
444
|
+
if figure is None:
|
|
445
|
+
with plt.ioff():
|
|
446
|
+
self.figure = plt.figure()
|
|
447
|
+
else:
|
|
448
|
+
self.figure = figure
|
|
449
|
+
self.figure.canvas.toolbar_position = 'right'
|
|
450
|
+
self.figure.canvas.toolbar_visible = True
|
|
451
|
+
|
|
452
|
+
super().__init__(dset, figure= self.figure, horizontal=horizontal, **kwargs)
|
|
453
|
+
|
|
454
|
+
self.start_cursor = ipywidgets.FloatText(value=0, description='Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
455
|
+
self.end_cursor = ipywidgets.FloatText(value=0, description='End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
|
|
456
|
+
self.panel = ipywidgets.VBox([ipywidgets.HBox([ipywidgets.Label('',layout=ipywidgets.Layout(width='100px')), ipywidgets.Label('Cursor:'),
|
|
457
|
+
self.start_cursor,ipywidgets.Label('eV'),
|
|
458
|
+
self.end_cursor, ipywidgets.Label('eV')]),
|
|
459
|
+
self.figure.canvas])
|
|
460
|
+
self.axis = self.axes[-1]
|
|
461
|
+
self.selector = matplotlib.widgets.SpanSelector(self.axis, self.line_select_callback,
|
|
462
|
+
direction="horizontal",
|
|
463
|
+
interactive=True,
|
|
464
|
+
props=dict(facecolor='blue', alpha=0.2))
|
|
465
|
+
|
|
466
|
+
def line_select_callback(self, x_min, x_max):
|
|
467
|
+
self.start_cursor.value = np.round(x_min, 3)
|
|
468
|
+
self.end_cursor.value = np.round(x_max, 3)
|
|
469
|
+
self.start_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.start_cursor.value)
|
|
470
|
+
self.end_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.end_cursor.value)
|
|
471
|
+
|
|
472
|
+
def _update(self, ev=None):
|
|
473
|
+
|
|
474
|
+
xlim = self.axes[1].get_xlim()
|
|
475
|
+
ylim = self.axes[1].get_ylim()
|
|
476
|
+
self.axes[1].clear()
|
|
477
|
+
self.get_spectrum()
|
|
478
|
+
if len(self.energy_scale)!=self.spectrum.shape[0]:
|
|
479
|
+
self.spectrum = self.spectrum.T
|
|
480
|
+
self.axes[1].plot(self.energy_scale, self.spectrum.compute(), label='experiment')
|
|
481
|
+
|
|
482
|
+
if self.set_title:
|
|
483
|
+
self.axes[1].set_title('spectrum {}, {}'.format(self.x, self.y))
|
|
484
|
+
self.fig.tight_layout()
|
|
485
|
+
self.selector = matplotlib.widgets.SpanSelector(self.axes[1], self.line_select_callback,
|
|
486
|
+
direction="horizontal",
|
|
487
|
+
interactive=True,
|
|
488
|
+
props=dict(facecolor='blue', alpha=0.2))
|
|
489
|
+
|
|
490
|
+
self.axes[1].set_xlim(xlim)
|
|
491
|
+
self.axes[1].set_ylim(ylim)
|
|
492
|
+
self.axes[1].set_xlabel(self.xlabel)
|
|
493
|
+
self.axes[1].set_ylabel(self.ylabel)
|
|
494
|
+
|
|
495
|
+
self.fig.canvas.draw_idle()
|
|
496
|
+
|
|
497
|
+
class InfoWidget(object):
|
|
498
|
+
def __init__(self, datasets=None):
|
|
499
|
+
self.datasets = datasets
|
|
500
|
+
self.dataset = None
|
|
501
|
+
|
|
502
|
+
self.sidebar = get_sidebar()
|
|
503
|
+
|
|
504
|
+
self.set_dataset()
|
|
505
|
+
self.set_action()
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
self.app_layout = ipywidgets.AppLayout(
|
|
509
|
+
left_sidebar=self.sidebar,
|
|
510
|
+
center=self.view.panel,
|
|
511
|
+
footer=None,#message_bar,
|
|
512
|
+
pane_heights=[0, 10, 0],
|
|
513
|
+
pane_widths=[4, 10, 0],
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
display(self.app_layout)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def get_spectrum(self):
|
|
520
|
+
if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
|
|
521
|
+
spectrum = self.dataset.view.get_spectrum()
|
|
522
|
+
self.axis = self.dataset.view.axes[1]
|
|
523
|
+
else:
|
|
524
|
+
spectrum = np.array(self.dataset)
|
|
525
|
+
self.axis = self.dataset.view.axis
|
|
526
|
+
|
|
527
|
+
spectrum *= self.y_scale
|
|
528
|
+
return spectrum
|
|
529
|
+
|
|
530
|
+
def plot(self, scale=True):
|
|
531
|
+
spectrum = self.get_spectrum()
|
|
532
|
+
self.energy_scale = self.dataset.energy_loss.values
|
|
533
|
+
x_limit = self.axis.get_xlim()
|
|
534
|
+
y_limit = np.array(self.axis.get_ylim())
|
|
535
|
+
"""
|
|
536
|
+
self.axis.clear()
|
|
537
|
+
|
|
538
|
+
self.axis.plot(self.energy_scale, spectrum, label='spectrum')
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
self.axis.set_xlabel(self.datasets[self.key].labels[0])
|
|
542
|
+
self.axis.set_ylabel(self.datasets[self.key].data_descriptor)
|
|
543
|
+
self.axis.ticklabel_format(style='sci', scilimits=(-2, 3))
|
|
544
|
+
if scale:
|
|
545
|
+
self.axis.set_ylim(np.array(y_limit)*self.change_y_scale)
|
|
546
|
+
self.change_y_scale = 1.0
|
|
547
|
+
if self.y_scale != 1.:
|
|
548
|
+
self.axis.set_ylabel('scattering probability (ppm/eV)')
|
|
549
|
+
self.selector = matplotlib.widgets.SpanSelector(self.axis, self.line_select_callback,
|
|
550
|
+
direction="horizontal",
|
|
551
|
+
interactive=True,
|
|
552
|
+
props=dict(facecolor='blue', alpha=0.2))
|
|
553
|
+
self.axis.legend()
|
|
554
|
+
self.figure.canvas.draw_idle()
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
def set_dataset(self, index=0):
|
|
558
|
+
|
|
559
|
+
spectrum_list = []
|
|
560
|
+
reference_list =[('None', -1)]
|
|
561
|
+
dataset_index = self.sidebar[0, 0].value
|
|
562
|
+
for index, key in enumerate(self.datasets.keys()):
|
|
563
|
+
if 'Reference' not in key:
|
|
564
|
+
if 'SPECTR' in self.datasets[key].data_type.name:
|
|
565
|
+
spectrum_list.append((f'{key}: {self.datasets[key].title}', index))
|
|
566
|
+
reference_list.append((f'{key}: {self.datasets[key].title}', index))
|
|
567
|
+
|
|
568
|
+
self.sidebar[0,0].options = spectrum_list
|
|
569
|
+
self.sidebar[9,0].options = reference_list
|
|
570
|
+
self.key = list(self.datasets)[dataset_index]
|
|
571
|
+
self.dataset = self.datasets[self.key]
|
|
572
|
+
if 'SPECTRUM' in self.dataset.data_type.name:
|
|
573
|
+
for i in range(14, 17):
|
|
574
|
+
self.sidebar[i, 0].layout.display = "none"
|
|
575
|
+
else:
|
|
576
|
+
for i in range(14, 17):
|
|
577
|
+
self.sidebar[i, 0].layout.display = "flex"
|
|
578
|
+
#self.sidebar[0,0].value = dataset_index #f'{self.key}: {self.datasets[self.key].title}'
|
|
579
|
+
self.sidebar[2,0].value = np.round(self.datasets[self.key].energy_loss[0], 3)
|
|
580
|
+
self.sidebar[3,0].value = np.round(self.datasets[self.key].energy_loss[1] - self.datasets[self.key].energy_loss[0], 4)
|
|
581
|
+
self.sidebar[5,0].value = np.round(self.datasets[self.key].metadata['experiment']['convergence_angle'], 1)
|
|
582
|
+
self.sidebar[6,0].value = np.round(self.datasets[self.key].metadata['experiment']['collection_angle'], 1)
|
|
583
|
+
self.sidebar[7,0].value = np.round(self.datasets[self.key].metadata['experiment']['acceleration_voltage']/1000, 1)
|
|
584
|
+
self.sidebar[10,0].value = np.round(self.datasets[self.key].metadata['experiment']['exposure_time'], 4)
|
|
585
|
+
if 'flux_ppm' not in self.datasets[self.key].metadata['experiment']:
|
|
586
|
+
self.datasets[self.key].metadata['experiment']['flux_ppm'] = 0
|
|
587
|
+
self.sidebar[11,0].value = self.datasets[self.key].metadata['experiment']['flux_ppm']
|
|
588
|
+
if 'count_conversion' not in self.datasets[self.key].metadata['experiment']:
|
|
589
|
+
self.datasets[self.key].metadata['experiment']['count_conversion'] = 1
|
|
590
|
+
self.sidebar[12,0].value = self.datasets[self.key].metadata['experiment']['count_conversion']
|
|
591
|
+
if 'beam_current' not in self.datasets[self.key].metadata['experiment']:
|
|
592
|
+
self.datasets[self.key].metadata['experiment']['beam_current'] = 0
|
|
593
|
+
self.sidebar[13,0].value = self.datasets[self.key].metadata['experiment']['beam_current']
|
|
594
|
+
|
|
595
|
+
self.view = SIPlot(self.dataset)
|
|
596
|
+
|
|
597
|
+
self.y_scale = 1.0
|
|
598
|
+
self.change_y_scale = 1.0
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def cursor2energy_scale(self, value):
|
|
602
|
+
|
|
603
|
+
dispersion = (self.end_cursor.value - self.start_cursor.value) / (self.end_channel - self.start_channel)
|
|
604
|
+
self.datasets[self.key].energy_loss *= (self.sidebar[3, 0].value/dispersion)
|
|
605
|
+
self.sidebar[3, 0].value = dispersion
|
|
606
|
+
offset = self.start_cursor.value - self.start_channel * dispersion
|
|
607
|
+
self.datasets[self.key].energy_loss += (self.sidebar[2, 0].value-self.datasets[self.key].energy_loss[0])
|
|
608
|
+
self.sidebar[2, 0].value = offset
|
|
609
|
+
self.plot()
|
|
610
|
+
|
|
611
|
+
def set_energy_scale(self, value):
|
|
612
|
+
dispersion = self.datasets[self.key].energy_loss[1] - self.datasets[self.key].energy_loss[0]
|
|
613
|
+
self.datasets[self.key].energy_loss *= (self.sidebar[3, 0].value/dispersion)
|
|
614
|
+
self.datasets[self.key].energy_loss += (self.sidebar[2, 0].value-self.datasets[self.key].energy_loss[0])
|
|
615
|
+
self.plot()
|
|
616
|
+
|
|
617
|
+
def set_y_scale(self, value):
|
|
618
|
+
self.change_y_scale = 1/self.y_scale
|
|
619
|
+
if self.sidebar[9,2].value:
|
|
620
|
+
dispersion = self.datasets[self.key].energy_loss[1] - self.datasets[self.key].energy_loss[0]
|
|
621
|
+
self.y_scale = 1/self.datasets[self.key].metadata['experiment']['flux_ppm'] * dispersion
|
|
622
|
+
else:
|
|
623
|
+
self.y_scale = 1.0
|
|
624
|
+
|
|
625
|
+
self.change_y_scale *= self.y_scale
|
|
626
|
+
self.plot()
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def set_flux(self, value):
|
|
630
|
+
self.datasets[self.key].metadata['experiment']['exposure_time'] = self.sidebar[10,0].value
|
|
631
|
+
if self.sidebar[9,0].value < 0:
|
|
632
|
+
self.datasets[self.key].metadata['experiment']['flux_ppm'] = 0.
|
|
633
|
+
else:
|
|
634
|
+
key = list(self.datasets.keys())[self.sidebar[9,0].value]
|
|
635
|
+
self.datasets[self.key].metadata['experiment']['flux_ppm'] = (np.array(self.datasets[key])*1e-6).sum() / self.datasets[key].metadata['experiment']['exposure_time']
|
|
636
|
+
self.datasets[self.key].metadata['experiment']['flux_ppm'] *= self.datasets[self.key].metadata['experiment']['exposure_time']
|
|
637
|
+
self.sidebar[11,0].value = np.round(self.datasets[self.key].metadata['experiment']['flux_ppm'], 2)
|
|
638
|
+
|
|
639
|
+
def set_microscope_parameter(self, value):
|
|
640
|
+
self.datasets[self.key].metadata['experiment']['convergence_angle'] = self.sidebar[5,0].value
|
|
641
|
+
self.datasets[self.key].metadata['experiment']['collection_angle'] = self.sidebar[6,0].value
|
|
642
|
+
self.datasets[self.key].metadata['experiment']['acceleration_voltage'] = self.sidebar[7,0].value*1000
|
|
643
|
+
|
|
644
|
+
def set_binning(self, value):
|
|
645
|
+
if 'SPECTRAL' in self.dataset.data_type.name:
|
|
646
|
+
bin_x = self.sidebar[15,0].value
|
|
647
|
+
bin_y = self.sidebar[16,0].value
|
|
648
|
+
self.dataset.view.set_bin([bin_x, bin_y])
|
|
649
|
+
self.datasets[self.key].metadata['experiment']['SI_bin_x'] = bin_x
|
|
650
|
+
self.datasets[self.key].metadata['experiment']['SI_bin_y'] = bin_y
|
|
651
|
+
|
|
652
|
+
def set_action(self):
|
|
653
|
+
self.sidebar[0,0].observe(self.set_dataset)
|
|
654
|
+
self.sidebar[1,0].on_click(self.cursor2energy_scale)
|
|
655
|
+
self.sidebar[2,0].observe(self.set_energy_scale, names='value')
|
|
656
|
+
self.sidebar[3,0].observe(self.set_energy_scale, names='value')
|
|
657
|
+
self.sidebar[5,0].observe(self.set_microscope_parameter)
|
|
658
|
+
self.sidebar[6,0].observe(self.set_microscope_parameter)
|
|
659
|
+
self.sidebar[7,0].observe(self.set_microscope_parameter)
|
|
660
|
+
self.sidebar[9,0].observe(self.set_flux)
|
|
661
|
+
self.sidebar[9,2].observe(self.set_y_scale)
|
|
662
|
+
self.sidebar[10,0].observe(self.set_flux)
|
|
663
|
+
self.sidebar[15,0].observe(self.set_binning)
|
|
664
|
+
self.sidebar[16,0].observe(self.set_binning)
|
|
665
|
+
|