pyxem / kikuchipy

Toolbox for analysis of electron backscatter diffraction (EBSD) patterns
https://kikuchipy.org
GNU General Public License v3.0
79 stars 30 forks source link

Add EMsoft ECP/TKD master pattern readers, improve handling of custom properties #564

Closed hakonanes closed 1 year ago

hakonanes commented 1 year ago

Description of the change

This PR adds readers for EMsoft's ECP and TKD master patterns. The former returns a new ECPMasterPattern signal, which is similar to EBSDMasterPattern without get_patterns(), while the latter returns a EBSDMasterPattern.

In the process of generalizing the master pattern class, I reimplemented the private CommonImage class in KikuchipySignal2D and LazyKikuchipySignal2D . The new class handles custom properties (not in HyperSpy), like EBSD.static_background() and EBSDMasterPattern.phase, and wraps calls to methods as_lazy(), change_dtype(), compute(), squeeze() and deepcopy() so that these properties carry over to or are removed in new signals as appropriate. All custom properties are also carried over when switching between ECPMasterPattern and EBSDMasterPattern using set_signal_type().

Finally, logging is added via kikuchipy.set_log_level() (same as in HyperSpy), since I needed this to debug the wrapped Signal2D methods mentioned above.

Replaces #476.

Closes #512, #386 and #16.

Progress of the PR

Minimal example of the bug fix or new feature

>>> import numpy as np
>>> import dask.array as da
>>> import kikuchipy as kp

# Set log level to DEBUG to show new messages added in KikuchipySignal2D

>>> kp.set_log_level("DEBUG")
>>> s = kp.data.nickel_ebsd_small()
>>> s2 = s.deepcopy()
DEBUG:kikuchipy.signals._kikuchipy_signal:Transfer custom properties when deep copying
>>> np.may_share_memory(s.static_background, s2.static_background()
False
>>> kp.set_log_level("WARNING")

# Static background is handled correctly

>>> isinstance(s.static_background, np.ndarray)
True
>>> s3 = s.as_lazy()
>>> isinstance(s3.static_background, da.Array)
True
>>> s3.compute()
[########################################] | 100% Completed | 100.36 ms
>>> isinstance(s3.static_background, np.ndarray)
True
>>> print(s.data.dtype, s.static_background.dtype)
uint8 uint8
>>> s.change_dtype("float32")
>>> print(s.data.dtype, s.static_background.dtype)
float32 float32

# Switching between master patterns carries over properties

>>> mp = kp.data.nickel_ebsd_master_pattern_small()
>>> mp
<EBSDMasterPattern, title: ni_mc_mp_20kv_uint8_gzip_opts9, dimensions: (|401, 401)>
>>> mp.phase
<name: ni. space group: Fm-3m. point group: m-3m. proper point group: 432. color: tab:blue>
>>> mp.set_signal_type("ECPMasterPattern")
>>> mp
<ECPMasterPattern, title: ni_mc_mp_20kv_uint8_gzip_opts9, dimensions: (|401, 401)>
>>> mp.phase
<name: ni. space group: Fm-3m. point group: m-3m. proper point group: 432. color: tab:blue>

For reviewers