Demo 3): File IO and basic field manipulation

In this demo, you’ll learn how to import your own data, more plotting routines and how to postprocess data with the inherent functionality of the Field class.

Import:

[1]:
import empyre as emp
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)


Loading data

You can use the io module (documentation will follow soon) to load a variety of different data formats. It uses the HyperSpy package (see here for more info) for a lot of the underlying work, but also has some routines for other formats as well. In general, all image files should work, as well as numpy data and others. The example here is the magnetic structure of a litographic Cobalt pattern. Note how we have to specify, that we want to load a vector field by setting vector=True. For vector fields, we can also set, which axis should be interpreted as the component axis (default is comp_pos=-1 for the last axis).

[2]:
field_io = emp.io.load_field(os.path.join('files', 'magdata.hdf5'), vector=True, comp_pos=0)
print(field_io)

WARNING:hyperspy.api:The ipywidgets GUI elements are not available, probably because the hyperspy_gui_ipywidgets package is not installed.
WARNING:hyperspy.api:The traitsui GUI elements are not available, probably because the hyperspy_gui_traitsui package is not installed.
Field(dim=(1, 512, 512), scale=(2.3397676944732666, 2.3397676944732666, 2.3397676944732666), vector=True, ncomp=3)

Scaling data

Note how the loaded field is quite large (512 x 512 pixels). You can manipulate the size of your fields with the bin function, to bin over a certain number of pixels, or use zoom to interpolate to higher numbers of pixels. Here, we’ll do the former. Note how the scale automatically changes after binning.

[3]:
field = field_io.bin(3)
print(field)


Field(dim=(1, 171, 171), scale=(7.0193030834198, 7.0193030834198, 7.0193030834198), vector=True, ncomp=3)

Plot the loaded field

We now want to plot our loaded data. Note how the scalebar always tries to show “nice” numbers, even though the scale itself had long floating points. We are not content with the quiver plot though.

[4]:
emp.vis.colorvec(field)
emp.vis.quiver(field)
emp.vis.colorwheel()
emp.vis.scalebar()


[4]:
<mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar at 0x1753efd6048>
../_images/demos_demo3_8_1.png

Nicer plot

The default binning size of quiver is determined in a way that tries to show 16 arrows across the whole field of view. In this case, we want to tweak this number to show more, but smaller arrows, which can be done with the n_bin parameter.

[5]:
emp.vis.colorvec(field)
emp.vis.quiver(field, n_bin=4)
emp.vis.colorwheel()
emp.vis.scalebar()


[5]:
<mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar at 0x1753f0afd68>
../_images/demos_demo3_10_1.png

Split vector fields into its components as scalar fields

Furthermore, we can split our vector fields into its scalar components (also returned as fields), by accessing the comp attribute. We’ll also plot the in-plane components here.

[6]:
x_field, y_field, z_field = field.comp
print(x_field)
fig, axes = emp.vis.new(1, 2)
emp.vis.imshow(x_field, axis=axes[0])
emp.vis.scalebar(axis=axes[0])
emp.vis.imshow(y_field, axis=axes[1])

Field(dim=(1, 171, 171), scale=(7.0193030834198, 7.0193030834198, 7.0193030834198), vector=False)
c:\users\weber\documents\libertem\empyre\src\empyre\vis\plot2d.py:79: MatplotlibDeprecationWarning:
The DivergingNorm class was deprecated in Matplotlib 3.2 and will be removed two minor releases later. Use TwoSlopeNorm instead.
  kwargs.setdefault('norm', DivergingNorm(0))  # Diverging colormap should have zero at the symmetry point!
c:\users\weber\documents\libertem\empyre\src\empyre\vis\plot2d.py:79: MatplotlibDeprecationWarning:
The DivergingNorm class was deprecated in Matplotlib 3.2 and will be removed two minor releases later. Use TwoSlopeNorm instead.
  kwargs.setdefault('norm', DivergingNorm(0))  # Diverging colormap should have zero at the symmetry point!
[6]:
<matplotlib.image.AxesImage at 0x1753f12ef98>
../_images/demos_demo3_12_3.png

Merge scalars into vector fields

The opposite can be done as well, by using the class method from_scalar_fields of the Field class as an alternate constructor.

[7]:
field_recombined = emp.fields.Field.from_scalar_fields([x_field, y_field, z_field])
print(field_recombined)

Field(dim=(1, 171, 171), scale=(7.0193030834198, 7.0193030834198, 7.0193030834198), vector=True, ncomp=3)

Load more data and combine them

Let’s load more data from different formats, in this case the magnetic phase map that results from the magnetisation distribution from above, as well as a mask that determines the position of the litographic pattern and a “confidence” array that shows which regions of the (experimentally acquired) phase image we want to trust. Note that some loading routines need to be provided with the scale, otherwise they would be defaulting to 1, as not all formats are capable of saving the scale correctly. All fiels are scalar fields and thus don’t need the vector parameter.

[8]:
field_phase = emp.io.load_field(os.path.join('files', 'phase.unf'))
field_mask = emp.io.load_field(os.path.join('files', 'mask.png'), scale=field_phase.scale)
field_conf = emp.io.load_field(os.path.join('files', 'confidence.npy'), scale=field_phase.scale)
# Plots
fig, axes = emp.vis.new(1, 3)
emp.vis.imshow(field_phase, axis=axes[0])
emp.vis.annotate('phase', axis=axes[0])
emp.vis.imshow(field_mask, axis=axes[1])
emp.vis.annotate('mask', axis=axes[1])
emp.vis.imshow(field_conf, axis=axes[2])
emp.vis.annotate('confidence', axis=axes[2])

c:\users\weber\documents\libertem\empyre\src\empyre\vis\plot2d.py:79: MatplotlibDeprecationWarning:
The DivergingNorm class was deprecated in Matplotlib 3.2 and will be removed two minor releases later. Use TwoSlopeNorm instead.
  kwargs.setdefault('norm', DivergingNorm(0))  # Diverging colormap should have zero at the symmetry point!
c:\users\weber\documents\libertem\empyre\src\empyre\vis\plot2d.py:79: MatplotlibDeprecationWarning:
The DivergingNorm class was deprecated in Matplotlib 3.2 and will be removed two minor releases later. Use TwoSlopeNorm instead.
  kwargs.setdefault('norm', DivergingNorm(0))  # Diverging colormap should have zero at the symmetry point!
c:\users\weber\documents\libertem\empyre\src\empyre\vis\plot2d.py:79: MatplotlibDeprecationWarning:
The DivergingNorm class was deprecated in Matplotlib 3.2 and will be removed two minor releases later. Use TwoSlopeNorm instead.
  kwargs.setdefault('norm', DivergingNorm(0))  # Diverging colormap should have zero at the symmetry point!
[8]:
<matplotlib.offsetbox.AnchoredOffsetbox at 0x1753f1a7e10>
../_images/demos_demo3_16_2.png

Combined plot

We now want to plot all the loaded information into the same image. * We plot the phase with imshow, just like before. * We plot the mask as a contour plot. * We plot the confidence as another imshow, but with a special colormap. * We add a colorbar that needs the phase image as an input to work. * We add a scalebar.

[9]:
im = emp.vis.imshow(field_phase)
emp.vis.contour(field_mask)
emp.vis.imshow(field_conf, cmap=emp.vis.colors.cmaps.transparent_confidence)
emp.vis.colorbar(im, label='phase [rad]')
emp.vis.scalebar()

c:\users\weber\documents\libertem\empyre\src\empyre\vis\plot2d.py:79: MatplotlibDeprecationWarning:
The DivergingNorm class was deprecated in Matplotlib 3.2 and will be removed two minor releases later. Use TwoSlopeNorm instead.
  kwargs.setdefault('norm', DivergingNorm(0))  # Diverging colormap should have zero at the symmetry point!
[9]:
<mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar at 0x1753fd511d0>
../_images/demos_demo3_18_2.png

Holographic contour plots

Magnetic phase images are often shown as holographic contour maps, which you get by calculating the curl (or rotation) of the magnetic phase and overlay it with cosine contours (the cosine of the phase, amplified by a gain factor). Due to empyre being rooted in the analysis of magnetic phase images, the according functionality is here as well. First, let’s create the rotation of the phase image by using the curl function. Visualizing it with colorvec shows a problem, though. The strongest saturation is fixed to obviously erroneous pixels and the interesting parts of the image seem faded out and unsaturated.

[10]:
emp.vis.colorvec(field_phase.curl())

[10]:
<matplotlib.image.AxesImage at 0x1753fdc0cc0>
../_images/demos_demo3_20_1.png

The curl and clipping

We can use the clip function to clip the values to all lie in a Gaussian distribution with a certain sigma (the usual numpy functionality with specifying vmin and vmax is available as well). This clips noisy regions where the rotation is very strong due to sharp boundaries or noise.

[11]:
emp.vis.colorvec(field_phase.curl().clip(sigma=2))


[11]:
<matplotlib.image.AxesImage at 0x1753fe13fd0>
../_images/demos_demo3_22_1.png

Cosine contours

Next we look at the cosine contours of the phase, which we can plot via the cosine_contours plot function in vis. Not that normally, a gain factor is determined automatically (default: gain='auto'), but we’ll look at the pure, unaltered cosine here first.

[12]:
emp.vis.cosine_contours(field_phase, gain=1)

[12]:
<matplotlib.image.AxesImage at 0x1753ff88f98>
../_images/demos_demo3_24_1.png

The full contour map

Now let’s combine both plots, add confidence and mask plots again, as well as a colorwheel and a scalebar. The cosine contour now searches for an adequate gain automatically. Also try out other colormaps (e.g. the cyclic_classic one that corresponds to many holographic contour plots in older publications)

[13]:
cmap = emp.vis.colors.cmaps.cyclic_cubehelix
emp.vis.colorvec(field_phase.curl().clip(sigma=2), cmap=cmap)
emp.vis.cosine_contours(field_phase)
emp.vis.contour(field_mask, colors='w')
emp.vis.imshow(field_conf, cmap=emp.vis.colors.cmaps.transparent_confidence)
emp.vis.colorwheel(cmap=cmap)
emp.vis.scalebar()

[13]:
<mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar at 0x1754012cb70>
../_images/demos_demo3_26_1.png