# Copyright 2015 Joshua Taillon
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# #########################################################################
# This file contains the code necessary to load a DM survey image and plot
# it with Hyperspy, adding markers as necessary to show the spatial extents
# of the spectrum image, beam location, and spatial drift box
# #########################################################################
import seaborn as _sns
import matplotlib
from matplotlib.patches import Rectangle as _Rect
from mpl_toolkits.axes_grid1 import make_axes_locatable as _mal
import hyperspy
import hyperspy.api as _hs
import hyperspy.io_plugins.digital_micrograph as _dm
__all__ = ['fit_peak',
'add_colored_outlines',
'add_custom_colorbars',
'plot_dm3_survey_with_markers']
[docs]def fit_peak(sig, lower_bound, upper_bound, factor_num=None,):
"""
Fit a Gaussian peak in a range to a signal
Parameters
----------
sig: ~hyperspy.signal.Signal
Signal to fit
lower_bound: float
Lower bound of values in which to fit
upper_bound: float
Upper bound of values in which to fit
factor_num: int
if given, the fit will be performed on a decomposition component of
the signal (given by ``factor_num``), rather than the signal itself
Returns
-------
centre: float
center of the fitted Gaussian
"""
if factor_num is not None:
c1 = sig.get_decomposition_factors()[factor_num]
else:
c1 = sig
# noinspection PyUnresolvedReferences,PyProtectedMember
if isinstance(sig, hyperspy._signals.spectrum.Spectrum):
is_eels = False
else:
is_eels = True
if is_eels:
c1.set_microscope_parameters(beam_energy=200,
convergence_angle=12,
collection_angle=29)
m1 = c1.create_model(auto_background=False)
else:
m1 = c1.create_model()
g1 = _hs.model.components.Gaussian(centre=((float(lower_bound) +
upper_bound) / 2.0))
m1.append(g1)
m1.set_signal_range(lower_bound, upper_bound)
m1.fit()
m1.plot()
centre = g1.centre.value
return centre
[docs]def add_colored_outlines(fig,
signal,
num_images,
color_palette=_sns.color_palette(),
border=0.0,
lw=15):
"""
Add outlines to a matplotlib figure to make it easy to visualize with
the plotted spectra
Parameters
----------
fig: matplotlib.figure
figure to which to add outlines (this should not have colorbars,
add them later)
signal: ~hyperspy.signal.Signal
signal that has calibrated axes (in order to set bounds of rectangle)
num_images: int
number of images in figure
color_palette: list
list of colors to use for outlines
border: float
offset of rectangle from edge of data
lw: float
width of rectangle lines to plot
"""
x, y = signal.axes_manager[0].high_value, signal.axes_manager[1].high_value
for i in range(num_images):
ax = fig.get_axes()[num_images - (1 + i)]
# noinspection PyUnresolvedReferences
r = _Rect((border, border),
x - 1.5 * border,
y - 1.5 * border,
fill=False,
edgecolor=color_palette[num_images - (1 + i)],
alpha=1,
linewidth=lw)
ax.add_patch(r)
[docs]def add_custom_colorbars(fig,
tick_list=None):
"""
Add custom colorbars with ticks at specified values
Parameters
----------
fig: matplotlib.figure
figure to which to add colorbars (should not currently have colorbars)
tick_list: list
nested list with the position of ticks to be added to colorbars;
should have a length equal to the number of images in the figure
Example for a four plot figure:
>>> tick_list = [[120,200,280],
... [4,20,36],
... [0,8,16],
... [0,22,44]]
"""
for i, a in enumerate(fig.axes):
# if i == 2:
# a.get_images()[0].set_clim(0,16)
div = _mal(a)
cax = div.append_axes("right", size="5%", pad=0.05)
if tick_list is None:
_ = fig.colorbar(a.get_images()[0], cax=cax)
else:
# noinspection PyUnresolvedReferences
_ = fig.colorbar(a.get_images()[0], cax=cax, ticks=tick_list[i])
[docs]def plot_dm3_survey_with_markers(fname,
add_text=True,
plot_beam=True,
plot_si=True,
plot_drift=True,
x_offset=1.0,
y_offset=1.0,
im_scale=1.0,
text_size='xx-small',
**kwargs):
"""
Plot a hyperspy signal with the markers from digital micrograph enabled
Parameters
----------
fname : str
Name of .dm3 file to load
add_text : bool
Switch to control if labels for the markers are added to the plot
plot_beam : bool
Switch to control if beam point is plotted (if present)
plot_si : bool
Switch to control if spectrum image box or line is plotted (if present)
plot_drift : bool
Switch to control if spatial drift box is plotted (if present)
x_offset : float
multiplier to control how far the text will be offset from its
default position (in the x-direction)
y_offset : float
multiplier to control how far the text will be offset from its
default position (in the y-direction)
im_scale : float
will scale the survey image by a given factor (useful for correcting
image scale bars if the calibration is incorrect)
text_size : str or float
size of the text that will be written on the image (follows same
convention as the `Text
<http://matplotlib.org/1.3.0/api/artist_api.html#matplotlib.text.
Text.set_size>`_ matplotlib Artist
**kwargs
Other keyword arguments are passed to hs.signal.plot()
Returns
-------
im: hyperspy._signals.image.Image
HyperSpy signal with markers added. While the figure is open,
the figure can be saved with a call such as
``im._plot.signal_plot.figure.savefig()``
"""
def _add_beam(image, location, annotationtype):
if plot_beam:
beam_m = _hs.plot.markers.point(x=location[1],
y=location[0],
color='red')
image.add_marker(beam_m)
if add_text:
beam_text_m = _hs.plot.markers.text(x=location[1] - (0.5 *
x_offset),
y=location[0] - (1.5 *
y_offset),
color='red',
text='Beam',
size=text_size)
image.add_marker(beam_text_m)
else:
pass
def _add_si(image, location, annotationtype):
# adds a green rectangle (or line, if the coordinates are such) to
# image
if plot_si:
if annotationtype == 23: # Map
si_m = _hs.plot.markers.rectangle(x1=location[1],
y1=location[0],
x2=location[3],
y2=location[2],
color='#13FF00')
elif annotationtype == 25: # Line profile
si_m = _hs.plot.markers.line_segment(x1=location[1],
y1=location[0],
x2=location[3],
y2=location[2],
color='#13FF00')
else:
raise Exception("No spectrum image annotation found")
image.add_marker(si_m)
if add_text:
si_text_m = _hs.plot.markers.text(x=location[1],
y=location[0] - (0.5 *
y_offset),
color='#13FF00',
text='Spectrum Image',
size=text_size)
image.add_marker(si_text_m)
else:
pass
def _add_drift(image, location, annotationtype):
if plot_drift:
drift_m = _hs.plot.markers.rectangle(x1=location[1],
y1=location[0],
x2=location[3],
y2=location[2],
color='yellow')
image.add_marker(drift_m)
if add_text:
drift_text_m = _hs.plot.markers.text(x=location[1],
y=location[0] - (0.5 *
y_offset),
color='yellow',
text='Spatial Drift',
size=text_size)
image.add_marker(drift_text_m)
else:
pass
im = _hs.load(fname)
flist = _dm.file_reader(fname)
annotation_list = flist[0]['original_metadata']['DocumentObjectList'][
'TagGroup0']['AnnotationGroupList']
im.axes_manager[0].scale *= im_scale
im.axes_manager[1].scale *= im_scale
scale = im.axes_manager[0].scale
mapping = {
'Beam': _add_beam,
'Spectrum Image': _add_si,
'Spatial Drift': _add_drift
}
im.plot(cmap='gist_gray', **kwargs)
for i in range(len(annotation_list)):
try:
label = annotation_list['TagGroup' + str(i)]['Label']
loc = annotation_list['TagGroup' + str(i)]['Rectangle']
scaled_loc = [scale * i for i in loc]
annotationtype = annotation_list['TagGroup' + str(i)]['AnnotationType']
mapping[label](im, scaled_loc, annotationtype)
except KeyError:
pass
return im # Returns a hyperspy._signals.image.Image, and with it all
# editing and saving capabilities.
def plot_decomposition_results(signal,
list_of_comps,
labels=None,
factor_style='cascade',
loadings_cmap='cubehelix',
mva_type='decomposition',
loadings_kwargs={},
factors_kwargs={}):
"""
Plot the results of a area SI decomposition (components and factors)
Parameters
----------
signal: ~hyperspy.signal.Signal
2D signal for which to plot decomposition results
list_of_comps: list
should be a list of ints that defines which factors to plot. Can also
have a tuple (or another iterable) in place of an int, in which case
the prescribed components will be summed in the output
labels: list
list of labels to use for components
factor_style: string
string defining which plot_spectra style to use
loadings_cmap: string
colormap to use for the loadings plots
mva_type: string
'decomposition' or 'bss'. Defines which HyperSpy method to use
loadings_kwargs: dict
other keyword arguments passed to plot_images for the loadings plot
factors_kwargs: dict
other keyword arguments passed to plot_spectra for the factors plot
"""
if mva_type == 'decomposition':
l = signal.get_decomposition_loadings()
f = signal.get_decomposition_factors()
elif mva_type == 'bss':
l = signal.get_bss_loadings()
f = signal.get_bss_factors()
else:
raise ValueError('Did not understand mva_type: {}'.format(mva_type))
l_to_plot = []
f_to_plot = []
for i in list_of_comps:
try:
l_x = [l.inav[j] for j in iter(i)]
f_x = [f.inav[j] for j in iter(i)]
summed_comp = l_x[0]
summed_fact = f_x[0]
for j in range(1, len(l_x)):
summed_comp += l_x[j]
summed_fact += f_x[j]
l_to_plot.append(summed_comp)
f_to_plot.append(summed_fact)
except:
l_to_plot.append(l.inav[i])
f_to_plot.append(f.inav[i])
_hs.plot.plot_images(l_to_plot,
cmap=loadings_cmap,
label=labels,
**loadings_kwargs)
_hs.plot.plot_spectra(f_to_plot,
style=factor_style,
legend=labels,
**factors_kwargs)