Skip to content
Snippets Groups Projects
Commit c4140767 authored by Jan Caron's avatar Jan Caron
Browse files

New folder structure (tests now in pyramid), deleted semper module (now ercpy)!

colormap: Added TransparentColormap.
magdata: Added get_magslice method, to_emd and from_emd.
phasemap: Added to_emd and from_emd.
gui: New GUI "PhaseMap Creator".
tests: Moved from ./tests to ./pyramid/tests. Not a package anymore!
parent df7d8cd4
No related branches found
No related tags found
No related merge requests found
Showing
with 259 additions and 70 deletions
syntax: glob
*~
.spyderworkspace
.spyderproject
.cproject
.pyramid.egg-info
*.pyc
*.pyd
*.so
......
[.ShellClassInfo]
IconResource=C:\Users\Jan\Home\PhD Thesis\Pyramid\docs\icon.ico,0
IconResource=C:\Users\Jan\Home\PhD Thesis\Spyder\Pyramid\docs\icon.ico,0
[ViewState]
Mode=
Vid=
......
......@@ -40,8 +40,6 @@ colormap
Class which implements a custom direction encoding colormap.
fft
Class for custom FFT functions using numpy or FFTW.
semper
Class for compatibility with a readable / writable Semper format.
Subpackages
-----------
......@@ -67,7 +65,6 @@ from .projector import * # analysis:ignore
from .regularisator import * # analysis:ignore
from .ramp import * # analysis:ignore
from .quaternion import * # analysis:ignore
from .semper import * # analysis:ignore
from .colormap import * # analysis:ignore
from .config import * # analysis:ignore
from .version import version as __version__
......@@ -75,7 +72,7 @@ from .version import hg_revision as __hg_revision__
import logging
_log = logging.getLogger(__name__)
_log.info("Starting PYRAMID V{} HG{}".format(__version__, __hg_revision__))
_log.info("Starting Pyramid V{} HG{}".format(__version__, __hg_revision__))
del logging
__all__ = ['analytic', 'magcreator', 'reconstruction', 'fft']
......@@ -91,5 +88,4 @@ __all__.extend(projector.__all__)
__all__.extend(regularisator.__all__)
__all__.extend(ramp.__all__)
__all__.extend(quaternion.__all__)
__all__.extend(semper.__all__)
__all__.extend(colormap.__all__)
......@@ -15,12 +15,12 @@ from numbers import Number
import logging
__all__ = ['DirectionalColormap']
__all__ = ['DirectionalColormap', 'TransparentColormap']
class DirectionalColormap(mpl.colors.LinearSegmentedColormap):
'''Colormap subclass for encoding 3D-directions with colors..
'''Colormap subclass for encoding 3D-directions with colors.
This class is a subclass of the :class:`~matplotlib.pyplot.colors.LinearSegmentedColormap`
class with a few classmethods which can be used for convenience. The
......@@ -220,3 +220,37 @@ class DirectionalColormap(mpl.colors.LinearSegmentedColormap):
r = np.sqrt(x**2 + y**2 + z**2)
theta = np.arccos(z / (r+1E-30))
return cls.rgb_from_angles(phi, theta)
class TransparentColormap(mpl.colors.LinearSegmentedColormap):
'''Colormap subclass for including transparency.
This class is a subclass of the :class:`~matplotlib.pyplot.colors.LinearSegmentedColormap`
class with integrated support for transparency. The colormap is unicolor and varies only in
transparency.
Attributes
----------
r: float, optional
Intensity of red in the colormap. Has to be between 0. and 1.
g: float, optional
Intensity of green in the colormap. Has to be between 0. and 1.
b: float, optional
Intensity of blue in the colormap. Has to be between 0. and 1.
alpha_range : list (N=2) of float, optional
Start and end alpha value. Has to be between 0. and 1.
'''
_log = logging.getLogger(__name__+'.TransparentColormap')
def __init__(self, r=1., g=0., b=0., alpha_range=[0., 1.]):
self._log.debug('Calling __create_directional_colormap')
red = [(0., 0., r), (1., r, 1.)]
green = [(0., 0., g), (1., g, 1.)]
blue = [(0., 0., b), (1., b, 1.)]
alpha = [(0., 0., alpha_range[0]), (1., alpha_range[1], 1.)]
cdict = {'red': red, 'green': green, 'blue': blue, 'alpha': alpha}
super(TransparentColormap, self).__init__('transparent_colormap', cdict, N=256)
self._log.debug('Created '+str(self))
......@@ -263,7 +263,7 @@ class DataSet(object):
'''
self._log.debug('Calling display_mask')
if self.mask is not None:
from mayavi import mlab # TODO: Supress annoying warning from traits!
from mayavi import mlab
zz, yy, xx = np.indices(self.dim)
ad = ar_dens
zz = zz[::ad, ::ad, ::ad].flatten()
......
......@@ -178,7 +178,7 @@ class DistributedForwardModel(ForwardModel):
Subclass of the :class:`~.ForwardModel` class which implements multiprocessing strategies
to speed up the calculations. The interface is the same, internally, the processes and one
ForwardModel operating on a subset of the DataSet per process are created during construction.
Ramps are calculated in the main thread. The :method:`~.finalize` method can be used to force
Ramps are calculated in the main thread. The :func:`~.finalize` method can be used to force
the processes to join if the class is no longer used.
Attributes
......
......@@ -362,6 +362,49 @@ class MagData(object):
else:
self.mag_vec = vector
def get_magslice(self, ax_slice=0, proj_axis='z', mode='complex'):
'''Extract a slice from the :class:`~.MagData` object.
Parameters
----------
proj_axis : {'z', 'y', 'x'}, optional
The axis, from which the slice is taken. The default is 'z'.
ax_slice : int, optional
The slice-index of the axis specified in `proj_axis`. Defaults to zero (first slice).
mode : {'complex', 'amplitude'}, optional
Determines if the 2D magnetization is returned as complex values or if the amplitude
of the two components is calculated.
Returns
-------
mag_slice : :class:`~numpy.ndarray` (N=2)
The extracted magnetization slice.
'''
self._log.debug('Calling get_mag_slice')
# Find slice:
assert proj_axis == 'z' or proj_axis == 'y' or proj_axis == 'x', \
'Axis has to be x, y or z (as string).'
if proj_axis == 'z': # Slice of the xy-plane with z = ax_slice
self._log.debug('proj_axis == z')
u_mag = np.copy(self.magnitude[0][ax_slice, ...]) # x-component
v_mag = np.copy(self.magnitude[1][ax_slice, ...]) # y-component
elif proj_axis == 'y': # Slice of the xz-plane with y = ax_slice
self._log.debug('proj_axis == y')
u_mag = np.copy(self.magnitude[0][:, ax_slice, :]) # x-component
v_mag = np.copy(self.magnitude[2][:, ax_slice, :]) # z-component
elif proj_axis == 'x': # Slice of the yz-plane with x = ax_slice
self._log.debug('proj_axis == x')
u_mag = np.swapaxes(np.copy(self.magnitude[2][..., ax_slice]), 0, 1) # z-component
v_mag = np.swapaxes(np.copy(self.magnitude[1][..., ax_slice]), 0, 1) # y-component
# Create data field:
if mode == 'complex':
return u_mag + 1j*v_mag
elif mode == 'amplitude':
return np.hypot(u_mag, v_mag)
else:
raise ValueError('Given mode not understood!')
def flip(self, axis='x'):
'''Flip/mirror the magnetization around the specified axis.
......@@ -623,34 +666,86 @@ class MagData(object):
# Write the tree into the file in pretty print format:
tree.write(filename, pretty_print=True)
def to_hyperspy(self):
'''Return a :class:`~.hyperspy.signals.Spectrum` class instance. Useful for file exports.
def to_emd(self, user={}, microscope={}, sample={}, comments={}):
'''Convert :class:`~.MagData` data into ERCpys EMD-format.
Parameters
----------
None
user: dict, optional
Dictionary defining user metadata.
microscope: dict, optional
Dictionary defining microscope metadata.
sample: dict, optional
Dictionary defining sample metadata.
comments: dict, optional
Dictionary defining comment metadata.
Returns
-------
emd: :class:`~ercpy.EMD`
Representation of the :class:`~.MagData` object as an :class:`~ercpy.EMD` object.
Notes
-----
This method recquires the ercpy package!
'''
self._log.debug('Calling to_emd')
# Try importing ERCpy:
try:
import ercpy
import hyperspy.api as hp
except ImportError:
self._log.error('Could not load ercpy package!')
return
# Create signals:
magnitude = hp.signals.Signal(np.rollaxis(self.magnitude, 0, 4))
# Set axes:
magnitude.axes_manager[0].name = 'x-axis'
magnitude.axes_manager[0].units = 'nm'
magnitude.axes_manager[0].scale = self.a
magnitude.axes_manager[1].name = 'y-axis'
magnitude.axes_manager[1].units = 'nm'
magnitude.axes_manager[1].scale = self.a
magnitude.axes_manager[2].name = 'z-axis'
magnitude.axes_manager[2].units = 'nm'
magnitude.axes_manager[2].scale = self.a
magnitude.axes_manager[3].name = 'components (x,y,z)'
magnitude.axes_manager[3].units = ''
# Set metadata:
magnitude.metadata.Signal.add_dictionary({'name': 'magnitude'})
# Create and return EMD:
signals = {'magnitude': magnitude}
return ercpy.EMD(signals, user, microscope, sample, comments)
@classmethod
def from_emd(cls, emd):
'''Convert a :class:`~ercpy.EMD` object to a :class:`~.MagData` object.
Parameters
----------
emd: :class:`~ercpy.EMD`
The :class:`~ercpy.EMD` object which should be converted to :class:`~.MagData`.
Returns
-------
hyperspy_spectrum : :class:`~.hyperspy.signals.Spectrum`
Hyperspy Spectrum class which can be further used with the hyperspy package.
mag_data: :class:`~.MagData`
A :class:`~.MagData` object containing the loaded data.
Notes
-----
This method recquires the ercpy package!
'''
self._log.debug('Calling to_hyperspy')
import hyperspy.hspy as hp
# Last axis is used as signal per default, which is wrong (c, z, y, x) -> (y, z, c | x)
# Signal (and Spectrum) use the last axis, Image the TWO last axes!
mag_data_hp = hp.signals.Signal(self.magnitude).as_spectrum(spectral_axis=2) # components!
mag_data_hp.axes_manager[0].name = 'X'
mag_data_hp.axes_manager[0].units = 'nm'
mag_data_hp.axes_manager[0].scale = self.a
mag_data_hp.axes_manager[1].name = 'Y'
mag_data_hp.axes_manager[1].units = 'nm'
mag_data_hp.axes_manager[1].scale = self.a
mag_data_hp.axes_manager[2].name = 'Z'
mag_data_hp.axes_manager[2].units = 'nm'
mag_data_hp.axes_manager[2].scale = self.a
return mag_data_hp
cls._log.debug('Calling to_emd')
# Extract signals:
try:
magnitude = np.rollaxis(emd['magnitude'], 3, 0)
except KeyError as e:
cls._log.error(str(e))
# Extract properties:
a = emd.signals['magnitude'].axes_manager[0].scale
return MagData(a, magnitude)
def quiver_plot(self, title='Magnetization Distribution', axis=None, proj_axis='z',
coloring='angle', ar_dens=1, ax_slice=None, log=False, scaled=True,
......@@ -818,7 +913,7 @@ class MagData(object):
'''
self._log.debug('Calling quiver_plot3D')
from mayavi import mlab # TODO: Supress annoying warning from traits!
from mayavi import mlab
a = self.a
dim = self.dim
if limit is None:
......@@ -834,13 +929,7 @@ class MagData(object):
z_mag = self.magnitude[2][::ad, ::ad, ::ad].flatten()
# Plot them as vectors:
mlab.figure(size=(750, 700))
import warnings
with warnings.catch_warnings(record=True) as w: # Catch annoying warning
plot = mlab.quiver3d(xx, yy, zz, x_mag, y_mag, z_mag, mode=mode, colormap=cmap)
if len(w) != 0: # If a warning occurs make sure it is (only) the expected one!
assert len(w) == 1
assert issubclass(w[0].category, FutureWarning)
assert 'comparison' in str(w[0].message)
plot = mlab.quiver3d(xx, yy, zz, x_mag, y_mag, z_mag, mode=mode, colormap=cmap)
if coloring == 'angle': # Encodes the full angle via colorwheel and saturation
self._log.debug('Encoding full 3D angles')
from tvtk.api import tvtk
......
......@@ -16,7 +16,7 @@ import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.ticker import MaxNLocator, FuncFormatter
from pyramid.colormap import DirectionalColormap
from pyramid.colormap import DirectionalColormap, TransparentColormap
import logging
......@@ -408,7 +408,7 @@ class PhaseMap(object):
----------
filename : string
The name of the file in which to store the phase map data.
The default is '..\output\phasemap_output.txt'.
The default is '..\output\phasemap.txt'.
skip_header : boolean, optional
Determines if the header, should be skipped (useful for some other programs).
Default is False.
......@@ -475,7 +475,7 @@ class PhaseMap(object):
----------
filename : string, optional
The name of the NetCDF4-file in which to store the phase data.
The default is '..\output\phasemap_output.nc'.
The default is '..\output\phasemap.nc'.
Returns
-------
......@@ -543,36 +543,92 @@ class PhaseMap(object):
phase_file.close()
return PhaseMap(a, phase, mask, confidence)
def to_hyperspy(self):
'''Return a :class:`~.hyperspy.signals.Image` class instance. Useful for file exports.
def to_emd(self, user={}, microscope={}, sample={}, comments={}):
'''Convert :class:`~.PhaseMap` data into ERCpys EMD-format.
Parameters
----------
None
user: dict, optional
Dictionary defining user metadata.
microscope: dict, optional
Dictionary defining microscope metadata.
sample: dict, optional
Dictionary defining sample metadata.
comments: dict, optional
Dictionary defining comment metadata.
Returns
-------
emd: :class:`~ercpy.EMD`
Representation of the :class:`~.PhaseMap` object as an :class:`~ercpy.EMD` object.
Notes
-----
This method recquires the ercpy package!
'''
self._log.debug('Calling to_emd')
# Try importing ERCpy:
try:
import ercpy
import hyperspy.api as hp
except ImportError:
self._log.error('Could not load ercpy package!')
return
# Create signals:
phase = hp.signals.Image(self.phase)
mask = hp.signals.Image(self.mask)
conf = hp.signals.Image(self.confidence)
# Set axes:
for signal in (phase, mask, conf):
signal.axes_manager[0].name = 'x-axis'
signal.axes_manager[0].units = 'nm'
signal.axes_manager[0].scale = self.a
signal.axes_manager[1].name = 'y-axis'
signal.axes_manager[1].units = 'nm'
signal.axes_manager[1].scale = self.a
# Set metadata:
phase.metadata.Signal.add_dictionary({'name': 'phase', 'units': self.unit})
mask.metadata.Signal.add_dictionary({'name': 'mask'})
conf.metadata.Signal.add_dictionary({'name': 'confidence'})
# Create and return EMD:
signals = {'phase': phase, 'mask': mask, 'confidence': conf}
return ercpy.EMD(signals, user, microscope, sample, comments)
@classmethod
def from_emd(cls, emd):
'''Convert a :class:`~ercpy.EMD` object to a :class:`~.PhaseMap` object.
Parameters
----------
emd: :class:`~ercpy.EMD`
The :class:`~ercpy.EMD` object which should be converted to :class:`~.PhaseMap`.
Returns
-------
hyperspy_image : :class:`~.hyperspy.signals.Image`
Hyperspy Image class which can be further used with the hyperspy package.
phase_map: :class:`~.PhaseMap`
A :class:`~.PhaseMap` object containing the loaded data.
Notes
-----
Does not save the unit and mask of the original phase map.
This method recquires the ercpy package!
'''
self._log.debug('Calling to_hyperspy')
import hyperspy.hspy as hp
phase_map_hp = hp.signals.Image(self.phase)
phase_map_hp.axes_manager[0].name = 'X'
phase_map_hp.axes_manager[0].units = 'nm'
phase_map_hp.axes_manager[0].scale = self.a
phase_map_hp.axes_manager[1].name = 'Y'
phase_map_hp.axes_manager[1].units = 'nm'
phase_map_hp.axes_manager[1].scale = self.a
return phase_map_hp
cls._log.debug('Calling to_emd')
# Extract signals:
try:
phase = emd['pahse']
mask = emd['mask']
confidence = emd['confidence']
except KeyError as e:
cls._log.error(str(e))
# Extract properties:
a = emd.signals['phase'].axes_manager[0].scale
unit = emd.signals['phase'].metadata.Signal.as_dictionary().get('units', 'rad')
return PhaseMap(a, phase, mask, confidence, unit)
def display_phase(self, title='Phase Map', cmap='RdBu', limit=None,
norm=None, axis=None, cbar=True, show_mask=True):
norm=None, axis=None, cbar=True, show_mask=True, show_conf=True):
'''Display the phasemap as a colormesh.
Parameters
......@@ -592,7 +648,10 @@ class PhaseMap(object):
Axis on which the graph is plotted. Creates a new figure if none is specified.
cbar : bool, optional
A switch determining if the colorbar should be plotted or not. Default is True.
show_mask : bool, optional
A switch determining if the mask should be plotted or not. Default is True.
show_conf : float, optional
A switch determining if the confidence should be plotted or not. Default is True.
Returns
-------
axis, cbar: :class:`~matplotlib.axes.AxesSubplot`
......@@ -611,12 +670,14 @@ class PhaseMap(object):
axis.set_aspect('equal')
# Plot the phasemap:
im = axis.pcolormesh(phase, cmap=cmap, vmin=-limit, vmax=limit, norm=norm)
if show_mask and not np.all(self.mask): # Plot mask if desired and not trivial!
if show_mask or show_conf:
vv, uu = np.indices(self.dim_uv) + 0.5
if show_mask and not np.all(self.mask): # Plot mask if desired and not trivial!
axis.contour(uu, vv, self.mask, levels=[0.5], colors='k', linestyles='dotted')
if show_conf and not np.all(self.confidence == 1.0):
vv, uu = np.indices(self.dim_uv) + 0.5
axis.pcolormesh(self.confidence, cmap=TransparentColormap(0.2, 0.3, 0.2, [0.2, 0.]))
# Set the axes ticks and labels:
axis.set_xlim(0, self.dim_uv[1])
axis.set_ylim(0, self.dim_uv[0])
if self.dim_uv[0] >= self.dim_uv[1]:
u_bin, v_bin = np.max((2, np.floor(9*self.dim_uv[1]/self.dim_uv[0]))), 9
else:
......@@ -627,6 +688,8 @@ class PhaseMap(object):
axis.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:g}'.format(x*self.a)))
axis.tick_params(axis='both', which='major', labelsize=14)
axis.set_title(title, fontsize=18)
axis.set_xlim(0, self.dim_uv[1])
axis.set_ylim(0, self.dim_uv[0])
axis.set_xlabel('u-axis [nm]', fontsize=15)
axis.set_ylabel('v-axis [nm]', fontsize=15)
# Add colorbar:
......@@ -757,7 +820,8 @@ class PhaseMap(object):
return axis
def display_combined(self, title='Combined Plot', cmap='RdBu', limit=None, norm=None,
gain='auto', interpolation='none', grad_encode='bright'):
gain='auto', interpolation='none', grad_encode='bright',
cbar=True, show_mask=True, show_conf=True):
'''Display the phase map and the resulting color coded holography image in one plot.
Parameters
......@@ -784,6 +848,12 @@ class PhaseMap(object):
encodes the direction (without gradient strength), 'dark' modulates the gradient
strength with a factor between 0 and 1 and 'bright' (which is the default) encodes
the gradient strength with color saturation.
cbar : bool, optional
A switch determining if the colorbar should be plotted or not. Default is True.
show_mask : bool, optional
A switch determining if the mask should be plotted or not. Default is True.
show_conf : float, optional
A switch determining if the confidence should be plotted or not. Default is True.
Returns
-------
......@@ -802,6 +872,7 @@ class PhaseMap(object):
# Plot phase map:
phase_axis = fig.add_subplot(1, 2, 2, aspect='equal')
fig.subplots_adjust(right=0.85)
self.display_phase(cmap='RdBu', limit=limit, norm=norm, axis=phase_axis)
self.display_phase(cmap='RdBu', limit=limit, norm=norm, axis=phase_axis,
cbar=cbar, show_mask=show_mask, show_conf=show_conf)
# Return the plotting axes:
return phase_axis, holo_axis
File moved
# -*- coding: utf-8 -*-
"""Testcase for the magcreator module."""
"""Testcase for the compliance with PEP8."""
import os
......@@ -14,7 +14,8 @@ import pep8
class TestCaseCompliance(unittest.TestCase):
"""TestCase for checking the pep8 compliance of the pyramid package."""
path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] # Pyramid dir
# Pyramid directory:
path = os.path.abspath(os.path.join(os.path.split(os.path.realpath(__file__))[0], '..', '..'))
def get_files_to_check(self, rootdir):
filepaths = []
......@@ -29,13 +30,13 @@ class TestCaseCompliance(unittest.TestCase):
'''Test for pep8 compliance.'''
files = self.get_files_to_check(os.path.join(self.path, 'pyramid')) \
+ self.get_files_to_check(os.path.join(self.path, 'scripts')) \
+ self.get_files_to_check(os.path.join(self.path, 'tests'))
+ self.get_files_to_check(os.path.join(self.path, 'pyramid', 'tests'))
ignores = ('E125', 'E226', 'E228')
pep8.MAX_LINE_LENGTH = 99
pep8style = pep8.StyleGuide(quiet=False)
pep8style.options.ignore = ignores
stdout_buffer = sys.stdout
with open(os.path.join(self.path, 'output', 'pep8_log.txt'), 'w') as sys.stdout:
with open(os.path.join(self.path, 'pyramid', 'tests', 'pep8_log.txt'), 'w+') as sys.stdout:
print '<<< PEP8 LOGFILE >>>'
print 'RUN:', datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print 'IGNORED RULES:', ', '.join(ignores)
......
File moved
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment