From cfea0d77947c3b0b682a39a2aa8a7732bbb22d25 Mon Sep 17 00:00:00 2001 From: Jan Caron <j.caron@fz-juelich.de> Date: Fri, 28 Nov 2014 13:59:27 +0100 Subject: [PATCH] Implementation of FFTW and covariance matrix functionality added Se_inv: is now handled differently, defaults to eye-matrix, but can be set later (added a few convenience functions to DataSet) utility: (re-)added utility module for index conversion kernel/phasemapper: can now use FFTW library and operate on pre-allocated arrays dtype: set dtype of arrays to np.float32/np.complex64 (halves memory size), also applied to numcore routines! magdata: quiver_plot can now plot every n-th arrow (yet untested!) reconstruction scripts: modified several reconstruction scripts. --- pyramid/__init__.py | 2 + pyramid/costfunction.py | 6 +- pyramid/dataset.py | 31 ++-- pyramid/kernel.py | 55 +++--- pyramid/magdata.py | 9 +- pyramid/numcore/phasemapper_core.pyx | 20 +-- pyramid/phasemap.py | 6 +- pyramid/phasemapper.py | 168 +++++++----------- pyramid/reconstruction.py | 2 +- pyramid/util.py | 131 ++++++++++++++ pyramid/version.py | 2 +- .../patrick_nanowire_reconstruction.py | 60 +++++-- .../reconstruction_linear_test.py | 16 +- .../reconstruction_linear_test_batch.py | 45 +++-- scripts/test methods/compare_methods.py | 35 ++-- 15 files changed, 376 insertions(+), 212 deletions(-) create mode 100644 pyramid/util.py diff --git a/pyramid/__init__.py b/pyramid/__init__.py index aed6a3d..dace4b4 100644 --- a/pyramid/__init__.py +++ b/pyramid/__init__.py @@ -51,6 +51,7 @@ from .phasemap import * from .phasemapper import * from .projector import * from .regularisator import * +from .util import * from .version import version as __version__ from .version import hg_revision as __hg_revision__ @@ -68,3 +69,4 @@ __all__.extend(phasemap.__all__) __all__.extend(phasemapper.__all__) __all__.extend(projector.__all__) __all__.extend(regularisator.__all__) +__all__.extend(util.__all__) diff --git a/pyramid/costfunction.py b/pyramid/costfunction.py index 0859ae3..9f36779 100644 --- a/pyramid/costfunction.py +++ b/pyramid/costfunction.py @@ -5,6 +5,7 @@ the so called `cost` of a threedimensional magnetization distribution.""" import numpy as np +from scipy.sparse import eye as sparse_eye from scipy.sparse.linalg import LinearOperator from pyramid.forwardmodel import ForwardModel @@ -60,9 +61,12 @@ class Costfunction(object): self.regularisator = NoneRegularisator() # Extract important information: self.y = data_set.phase_vec - self.Se_inv = data_set.Se_inv self.n = data_set.n self.m = data_set.m + if data_set.Se_inv is not None: + self.Se_inv = data_set.Se_inv + else: + self.Se_inv = sparse_eye(self.m) self._log.debug('Created '+str(self)) def __repr__(self): diff --git a/pyramid/dataset.py b/pyramid/dataset.py index 9559d59..1e3ad43 100644 --- a/pyramid/dataset.py +++ b/pyramid/dataset.py @@ -6,7 +6,7 @@ and additional data like corresponding projectors.""" import numpy as np from numbers import Number -from scipy.sparse import eye as sparse_eye +from scipy import sparse import matplotlib.pyplot as plt @@ -62,11 +62,6 @@ class DataSet(object): def m(self): return np.sum([len(p.phase_vec) for p in self.phase_maps]) - @property - def Se_inv(self): - # TODO: better implementation, maybe get-method? more flexible? input in append? - return sparse_eye(self.m) - @property def phase_vec(self): return np.concatenate([p.phase_vec for p in self.phase_maps]) @@ -80,11 +75,13 @@ class DataSet(object): @property def phase_mappers(self): - dim_uv_list = np.unique([p.dim_uv for p in self.projectors]) - kernel_list = [Kernel(self.a, tuple(dim_uv)) for dim_uv in dim_uv_list] + dim_uv_set = set([p.dim_uv for p in self.projectors]) + kernel_list = [Kernel(self.a, dim_uv, use_fftw=self.use_fftw, threads=self.threads) + for dim_uv in dim_uv_set] return {kernel.dim_uv: PhaseMapperRDFC(kernel) for kernel in kernel_list} - def __init__(self, a, dim, b_0=1, mask=None): + def __init__(self, a, dim, b_0=1, mask=None, Se_inv=None, use_fftw=True, threads=1): + # TODO: document! self._log.debug('Calling __init__') assert isinstance(a, Number), 'Grid spacing has to be a number!' assert a >= 0, 'Grid spacing has to be a positive number!' @@ -99,6 +96,9 @@ class DataSet(object): self.dim = dim self.b_0 = b_0 self.mask = mask + self.Se_inv = Se_inv + self.use_fftw = use_fftw + self.threads = threads self.phase_maps = [] self.projectors = [] self._log.debug('Created: '+str(self)) @@ -111,7 +111,7 @@ class DataSet(object): self._log.debug('Calling __str__') return 'DataSet(a=%s, dim=%s, b_0=%s)' % (self.a, self.dim, self.b_0) - def append(self, phase_map, projector): # TODO: include Se_inv or 2D mask?? + def append(self, phase_map, projector): '''Appends a data pair of phase map and projection infos to the data collection.` Parameters @@ -152,6 +152,15 @@ class DataSet(object): return [self.phase_mappers[projector.dim_uv](projector(mag_data)) for projector in self.projectors] + def set_Se_inv_block_diag(self, cov_list): + # TODO: Document! + self.Se_inv = sparse.block_diag(cov_list).tocsr() + + def set_Se_inv_diag_with_masks(self, mask_list): + # TODO: Document! + cov_list = [sparse.diags(m.flatten(), 0) for m in mask_list] + self.set_Se_inv_block_diag(cov_list) + def display_phase(self, mag_data=None, title='Phase Map', cmap='RdBu', limit=None, norm=None): '''Display all phasemaps saved in the :class:`~.DataSet` as a colormesh. @@ -235,5 +244,3 @@ class DataSet(object): cmap, limit, norm, gain, interpolation, grad_encode) for (i, phase_map) in enumerate(phase_maps)] plt.show() - -# TODO: method for constructing 3D mask from 2D masks? diff --git a/pyramid/kernel.py b/pyramid/kernel.py index cb237b0..c2b1664 100644 --- a/pyramid/kernel.py +++ b/pyramid/kernel.py @@ -53,7 +53,7 @@ class Kernel(object): A tuple of :class:`slice` objects to extract the original field of view from the increased size (`size_fft`) of the grid for the FFT-convolution. - ''' # TODO: overview what all dim_??? mean! and use_fftw + ''' # TODO: overview what all dim_??? mean! and use_fftw, slice(_fft), etc. _log = logging.getLogger(__name__+'.Kernel') @@ -66,32 +66,36 @@ class Kernel(object): self.a = a self.geometry = geometry # Set up FFT: - if use_fftw: + if not use_fftw: + self.dim_pad = tuple(2**np.ceil(np.log2(2*np.array(dim_uv))).astype(int)) # pow(2) + self.use_fftw = False + else: try: import pyfftw + self.dim_pad = tuple(2*np.array(dim_uv)) # is at least even (not nec. power of 2) + self.use_fftw = True except ImportError: - use_fftw = False + self.dim_pad = tuple(2**np.ceil(np.log2(2*np.array(dim_uv))).astype(int)) # pow(2) + self.use_fftw = False self._log.info('pyFFTW could not be imported, using numpy instead!') - if use_fftw: # use pyfftw (FFTW wrapper for python) - self.dim_pad = tuple(2*np.array(dim_uv)) # is at least even (not nec. power of 2) - self.dim_fft = (self.dim_pad[0], self.dim_pad[1]/2.+1) # last axis is real - n = pyfftw.simd_alignment - self.u = pyfftw.n_byte_align_empty(self.dim_kern, n, dtype='float32') - self.v = pyfftw.n_byte_align_empty(self.dim_kern, n, dtype='float32') - self.u_fft = pyfftw.n_byte_align_empty(self.dim_fft, n, dtype='complex64') - self.v_fft = pyfftw.n_byte_align_empty(self.dim_fft, n, dtype='complex64') + self.dim_fft = (self.dim_pad[0], self.dim_pad[1]/2+1) # last axis is real + self.slice_phase = (slice(dim_uv[0]-1, self.dim_kern[0]), # Shift because kernel center + slice(dim_uv[1]-1, self.dim_kern[1])) # is not at (0, 0)! + self.slice_mag = (slice(0, dim_uv[0]), # Magnetization is padded on the far end! + slice(0, dim_uv[1])) # (Phase cutout is shifted as listed above) + self.u = np.zeros(self.dim_kern, dtype=np.float32) + self.v = np.zeros(self.dim_kern, dtype=np.float32) + self.u_fft = np.zeros(self.dim_fft, dtype=np.complex64) + self.v_fft = np.zeros(self.dim_fft, dtype=np.complex64) + if self.use_fftw: # use pyfftw (FFTW wrapper for python) + self.u = pyfftw.n_byte_align(self.u, pyfftw.simd_alignment) + self.v = pyfftw.n_byte_align(self.v, pyfftw.simd_alignment) + self.u_fft = pyfftw.n_byte_align(self.u_fft, pyfftw.simd_alignment) + self.v_fft = pyfftw.n_byte_align(self.v_fft, pyfftw.simd_alignment) rfftn = pyfftw.builders.rfftn(self.u, self.dim_pad, threads=threads) self.threads = threads - self.use_fftw = True else: # otherwise use numpy - self.dim_pad = tuple(2**np.ceil(np.log2(2*np.array(dim_uv))).astype(int)) # pow(2) - self.dim_fft = (self.dim_pad[0], self.dim_pad[1]/2+1) # last axis is real - self.u = np.empty(self.dim_kern, dtype=float) - self.v = np.empty(self.dim_kern, dtype=float) - self.u_fft = np.empty(self.dim_fft, dtype=complex) - self.v_fft = np.empty(self.dim_fft, dtype=complex) rfftn = lambda x: np.fft.rfftn(x, self.dim_pad) - self.use_fftw = False # Calculate kernel (single pixel phase): coeff = b_0 * a**2 / (2*PHI_0) # Minus is gone because of negative z-direction v_dim, u_dim = dim_uv @@ -101,7 +105,6 @@ class Kernel(object): self.u[...] = coeff * self._get_elementary_phase(geometry, uu, vv, a) self.v[...] = coeff * self._get_elementary_phase(geometry, vv, uu, a) # Calculate Fourier trafo of kernel components: - self.slice_fft = (slice(dim_uv[0]-1, 2*dim_uv[0]-1), slice(dim_uv[1]-1, 2*dim_uv[1]-1)) self.u_fft[...] = rfftn(self.u) self.v_fft[...] = rfftn(self.v) self._log.debug('Created '+str(self)) @@ -137,5 +140,13 @@ class Kernel(object): return 0.5 * (F_a(n-0.5, m-0.5) - F_a(n+0.5, m-0.5) - F_a(n-0.5, m+0.5) + F_a(n+0.5, m+0.5)) - def get_info(self): - pass + def print_info(self): + print 'Shape of the FOV :', self.dim_uv + print 'Shape of the Kernel:', self.dim_kern + print 'Zero-padded shape :', self.dim_pad + print 'Shape of the FFT :', self.dim_fft + print 'Slice for the phase:', self.slice_phase + print 'Slice for the magn.:', self.slice_mag + print 'Grid spacing: {} nm'.format(self.a) + print 'Geometry:', self.geometry + print 'Use FFTW: {}; with {} thread(s)'.format(self.use_fftw, self.threads) diff --git a/pyramid/magdata.py b/pyramid/magdata.py index 0c8e2fc..f24686d 100644 --- a/pyramid/magdata.py +++ b/pyramid/magdata.py @@ -73,7 +73,7 @@ class MagData(object): assert isinstance(magnitude, np.ndarray), 'Magnitude has to be a numpy array!' assert len(magnitude.shape) == 4, 'Magnitude has to be 4-dimensional!' assert magnitude.shape[0] == 3, 'First dimension of the magnitude has to be 3!' - self._magnitude = magnitude + self._magnitude = np.asarray(magnitude, dtype=np.float32) self._dim = magnitude.shape[1:] @property @@ -419,7 +419,7 @@ class MagData(object): return MagData(a, magnitude) def quiver_plot(self, title='Magnetization Distribution', axis=None, proj_axis='z', - ax_slice=None, log=False, scaled=True): + ar_dens=1, ax_slice=None, log=False, scaled=True): # TODO: Doc ar_dens '''Plot a slice of the magnetization as a quiver plot. Parameters @@ -494,8 +494,9 @@ class MagData(object): if scaled: plot_u /= np.hypot(plot_u, plot_v).max() plot_v /= np.hypot(plot_u, plot_v).max() - axis.quiver(plot_u, plot_v, pivot='middle', units='xy', angles=angles, - scale_units='xy', scale=1, headwidth=6, headlength=7) + ad = ar_dens + axis.quiver(plot_u[::ad, ::ad], plot_v[::ad, ::ad], pivot='middle', units='xy', + angles=angles, scale_units='xy', scale=1, headwidth=6, headlength=7) axis.set_xlim(-1, np.shape(plot_u)[1]) axis.set_ylim(-1, np.shape(plot_u)[0]) axis.set_title(title, fontsize=18) diff --git a/pyramid/numcore/phasemapper_core.pyx b/pyramid/numcore/phasemapper_core.pyx index 7ea34d9..d7b1f04 100644 --- a/pyramid/numcore/phasemapper_core.pyx +++ b/pyramid/numcore/phasemapper_core.pyx @@ -19,9 +19,9 @@ cimport numpy as np @cython.wraparound(False) def phasemapper_real_convolve( unsigned int v_dim, unsigned int u_dim, - double[:, :] v_phi, double[:, :] u_phi, - double[:, :] v_mag, double[:, :] u_mag, - double[:, :] phase, float threshold): + float[:, :] v_phi, float[:, :] u_phi, + float[:, :] v_mag, float[:, :] u_mag, + float[:, :] phase, float threshold): '''Numerical core routine for the phase calculation using the real space approach. Parameters @@ -43,7 +43,7 @@ def phasemapper_real_convolve( ''' cdef unsigned int i, j, p, q, p_c, q_c - cdef double u_m, v_m + cdef float u_m, v_m for j in range(v_dim): for i in range(u_dim): u_m = u_mag[j, i] @@ -64,9 +64,9 @@ def phasemapper_real_convolve( @cython.wraparound(False) def jac_dot_real_convolve( unsigned int v_dim, unsigned int u_dim, - double[:, :] u_phi, double[:, :] v_phi, - double[:] vector, - double[:] result): + float[:, :] u_phi, float[:, :] v_phi, + float[:] vector, + float[:] result): '''Numerical core routine for the Jacobi matrix multiplication for the phase mapper. Parameters @@ -113,9 +113,9 @@ def jac_dot_real_convolve( @cython.wraparound(False) def jac_T_dot_real_convolve( unsigned int v_dim, unsigned int u_dim, - double[:, :] u_phi, double[:, :] v_phi, - double[:] vector, - double[:] result): + float[:, :] u_phi, float[:, :] v_phi, + float[:] vector, + float[:] result): '''Core routine for the transposed Jacobi multiplication for the phase mapper. Parameters diff --git a/pyramid/phasemap.py b/pyramid/phasemap.py index b33ab8c..4b962dd 100644 --- a/pyramid/phasemap.py +++ b/pyramid/phasemap.py @@ -121,7 +121,7 @@ class PhaseMap(object): def phase(self, phase): assert isinstance(phase, np.ndarray), 'Phase has to be a numpy array!' assert len(phase.shape) == 2, 'Phase has to be 2-dimensional!' - self._phase = phase + self._phase = np.asarray(phase, dtype=np.float32) self._dim_uv = phase.shape @property @@ -344,8 +344,8 @@ class PhaseMap(object): axis.set_ylim(0, self.dim_uv[0]) axis.xaxis.set_major_locator(MaxNLocator(nbins=9, integer=True)) axis.yaxis.set_major_locator(MaxNLocator(nbins=9, integer=True)) - axis.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:g}'.format(x*self.a))) - axis.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:g}'.format(x*self.a))) + axis.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:.3g}'.format(x*self.a))) + axis.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{:.3g}'.format(x*self.a))) axis.tick_params(axis='both', which='major', labelsize=14) axis.set_title(title, fontsize=18) axis.set_xlabel('u-axis [nm]', fontsize=15) diff --git a/pyramid/phasemapper.py b/pyramid/phasemapper.py index 2c2c0e9..32387dd 100644 --- a/pyramid/phasemapper.py +++ b/pyramid/phasemapper.py @@ -100,32 +100,29 @@ class PhaseMapperRDFC(PhaseMapper): self.kernel = kernel self.m = np.prod(kernel.dim_uv) self.n = 2 * self.m + self.u_mag = np.zeros(kernel.dim_pad, dtype=np.float32) + self.v_mag = np.zeros(kernel.dim_pad, dtype=np.float32) + self.mag_adj = np.zeros(kernel.dim_pad, dtype=np.float32) + self.u_mag_fft = np.zeros(kernel.dim_fft, dtype=np.complex64) + self.v_mag_fft = np.zeros(kernel.dim_fft, dtype=np.complex64) + self.phase_fft = np.zeros(kernel.dim_fft, dtype=np.complex64) if self.kernel.use_fftw: # if possible use FFTW import pyfftw - n = pyfftw.simd_alignment - self.u_mag = pyfftw.n_byte_align_empty(self.kernel.dim_uv, n, dtype='float32') - self.v_mag = pyfftw.n_byte_align_empty(self.kernel.dim_uv, n, dtype='float32') -# self.phase = pyfftw.n_byte_align_empty(self.kernel.dim_uv, n, dtype='float32') - self.u_mag_fft = pyfftw.n_byte_align_empty(self.kernel.dim_fft, n, dtype='complex64') - self.v_mag_fft = pyfftw.n_byte_align_empty(self.kernel.dim_fft, n, dtype='complex64') - self.phase_fft = pyfftw.n_byte_align_empty(self.kernel.dim_fft, n, dtype='complex64') - self._rfft2 = pyfftw.builders.rfftn(self.u_mag, self.kernel.dim_pad, - threads=self.kernel.threads) - self._irfft2 = pyfftw.builders.irfftn(self.phase_fft, self.kernel.dim_pad, - threads=self.kernel.threads) + self.u_mag = pyfftw.n_byte_align(self.u_mag, pyfftw.simd_alignment) + self.v_mag = pyfftw.n_byte_align(self.v_mag, pyfftw.simd_alignment) + self.mag_adj = pyfftw.n_byte_align(self.mag_adj, pyfftw.simd_alignment) + self.u_mag_fft = pyfftw.n_byte_align(self.u_mag_fft, pyfftw.simd_alignment) + self.v_mag_fft = pyfftw.n_byte_align(self.v_mag_fft, pyfftw.simd_alignment) + self.phase_fft = pyfftw.n_byte_align(self.phase_fft, pyfftw.simd_alignment) + self._rfft2 = pyfftw.builders.rfftn(self.u_mag, threads=kernel.threads) + self._irfft2 = pyfftw.builders.irfftn(self.phase_fft, threads=kernel.threads) self._rfft2_adj = self._rfft2_adj_fftw self._irfft2_adj = self._irfft2_adj_fftw else: # otherwise use numpy - self.u_mag = np.empty(self.kernel.dim_uv, dtype=float) - self.v_mag = np.empty(self.kernel.dim_uv, dtype=float) -# self.phase = np.empty(self.kernel.dim_uv, dtype=float) - self.u_mag_fft = np.empty(self.kernel.dim_fft, dtype=complex) - self.v_mag_fft = np.empty(self.kernel.dim_fft, dtype=complex) - self.phase_fft = np.empty(self.kernel.dim_fft, dtype=complex) - self._rfft2 = lambda x: np.fft.rfftn(x, self.kernel.dim_pad) - self._irfft2 = lambda x: np.fft.irfftn(x, self.kernel.dim_pad) - self._rfft2_adj = jfft.irfft2_adj - self._rfft2_adj = jfft.irfft2_adj + self._rfft2 = np.fft.rfftn + self._irfft2 = np.fft.irfftn + self._rfft2_adj = lambda x: jfft.rfft2_adj(x, self.kernel.dim_pad[1]) + self._irfft2_adj = jfft.irfft2_adj self._log.debug('Created '+str(self)) def __repr__(self): @@ -143,7 +140,8 @@ class PhaseMapperRDFC(PhaseMapper): assert mag_data.dim[0] == 1, 'Magnetic distribution must be 2-dimensional!' assert mag_data.dim[1:3] == self.kernel.dim_uv, 'Dimensions do not match!' # Process input parameters: - self.u_mag[...], self.v_mag[...] = mag_data.magnitude[0:2, 0, ...] + self.u_mag[self.kernel.slice_mag] = mag_data.magnitude[0, 0, ...] # u-component + self.v_mag[self.kernel.slice_mag] = mag_data.magnitude[1, 0, ...] # v-component return PhaseMap(mag_data.a, self._convolve()) def _convolve(self): @@ -153,7 +151,7 @@ class PhaseMapperRDFC(PhaseMapper): # Convolve the magnetization with the kernel in Fourier space: self.phase_fft[...] = self.u_mag_fft*self.kernel.u_fft - self.v_mag_fft*self.kernel.v_fft # Return the result: - return self._irfft2(self.phase_fft)[self.kernel.slice_fft] + return self._irfft2(self.phase_fft)[self.kernel.slice_phase] def jac_dot(self, vector): '''Calculate the product of the Jacobi matrix with a given `vector`. @@ -174,8 +172,9 @@ class PhaseMapperRDFC(PhaseMapper): # self._log.debug('Calling jac_dot') # TODO: Profiler says this was slow... assert len(vector) == self.n, \ 'vector size not compatible! vector: {}, size: {}'.format(len(vector), self.n) - self.u_mag[...], self.v_mag[...] = np.reshape(vector, (2,)+self.kernel.dim_uv) - result = self._convolve().reshape(-1) + self.u_mag[self.kernel.slice_mag], self.v_mag[self.kernel.slice_mag] = \ + np.reshape(vector, (2,)+self.kernel.dim_uv) + result = self._convolve().flatten() return result def jac_T_dot(self, vector): @@ -197,81 +196,50 @@ class PhaseMapperRDFC(PhaseMapper): # self._log.debug('Calling jac_T_dot') # TODO: Profiler says this was slow... assert len(vector) == self.m, \ 'vector size not compatible! vector: {}, size: {}'.format(len(vector), self.m) - phase = vector.reshape(self.kernel.dim_uv) - p0 = self.kernel.dim_pad[0]-self.kernel.dim_uv[0] - p1 = self.kernel.dim_pad[1]-self.kernel.dim_uv[1] - phase = np.pad(phase, ((0, p0), (0, p1)), 'constant') - phase_fft = jfft.irfft2_adj(phase) - u_mag_fft = phase_fft * self.kernel.u_fft - v_mag_fft = phase_fft * -self.kernel.v_fft - u_mag = jfft.rfft2_adj(u_mag_fft, self.kernel.dim_pad[1])[self.kernel.slice_fft] - v_mag = jfft.rfft2_adj(v_mag_fft, self.kernel.dim_pad[1])[self.kernel.slice_fft] - return -np.concatenate((u_mag.flatten(), v_mag.flatten())) # TODO: why the minus? - -# TODO: save arrays in PADDED form (kernel and the self.u/v/... stuff) to save padding time -# TODO: also gets rid of (..., self.kernel.dim_pad) argument -# if a.shape[axis] != n: -# s = list(a.shape) -# if s[axis] > n: -# index = [slice(None)]*len(s) -# index[axis] = slice(0, n) -# a = a[index] -# else: -# index = [slice(None)]*len(s) -# index[axis] = slice(0, s[axis]) -# s[axis] = n -# z = zeros(s, a.dtype.char) -# z[index] = a -# a = z - - def _rfft2_adj_fftw(x, n=None): - """ - Adjoint of the 2-D Real Fast Fourier Transform, that is the product of the adjoint - Jacobian of the numpy rfftn with vector x. - - Parameters - ---------- - x : array_like - n : int, optional - Length of second dimension of original array (the one, the size of which is - changed by employing the rfft2) - - Returns - ------- - array_like - """ - print 'rfftn2_adj input:', x.shape - if n is None: - n = 2 * (x.shape[1] - 1) - xp = np.zeros((x.shape[0], n), dtype=x.dtype) + self.mag_adj[self.kernel.slice_mag] = vector.reshape(self.kernel.dim_uv) + mag_adj_fft = self._irfft2_adj(self.mag_adj) + self.u_mag_fft[...] = mag_adj_fft * self.kernel.u_fft # u/v_mag_fft is just used as cache + self.v_mag_fft[...] = mag_adj_fft * -self.kernel.v_fft # they are really 'adjoint' phases + u_phase_adj = self._rfft2_adj(self.u_mag_fft)[self.kernel.slice_phase] + v_phase_adj = self._rfft2_adj(self.v_mag_fft)[self.kernel.slice_phase] + return -np.concatenate((u_phase_adj.flatten(), v_phase_adj.flatten())) # TODO: why minus? + + def _rfft2_adj_fftw(self, phase_adj): + x = phase_adj + xp = np.zeros(self.kernel.dim_pad, dtype=np.complex64) xp[:, :x.shape[1]] = x - print 'rfftn2_adj output:', np.fft.ifftn(xp).real * x.shape[0] * n - return np.fft.ifftn(xp).real * x.shape[0] * n - - def _irfft2_adj_fftw(x): - """ - Adjoint of the 2-D Inverse Real Fast Fourier Transform, that is the product of the - adjoint Jacobian of the numpy irfftn with vector x. - - Parameters - ---------- - x : array_like - - Returns - ------- - array_like - """ - print 'irfftn2_adj input:', x.shape - n_out = x.shape[1] / 2 + 1 - xp = np.fft.fft(x, axis=1) / x.shape[1] - if x.shape[1] % 2 == 0: - xp[:, 1:n_out - 1] += np.conj(xp[:, :n_out-1:-1]) - # xp[:, n_out - 1] = xp[:, n_out - 1].real - else: - xp[:, 1:n_out] += np.conj(xp[:, :n_out-1:-1]) - # xp[:, 0] = xp[:, 0].real - print 'rfftn2_adj output:', np.fft.ifftn(xp).real * x.shape[0] * n - return np.fft.fft(xp[:, :n_out], axis=0) / xp.shape[0] + phase_adj_fft = np.fft.ifftn(xp).real * np.prod(self.kernel.dim_pad) # TODO: Convert to FFTW + return phase_adj_fft + import pyfftw + kernel = self.kernel + self.phase_adj = np.zeros(kernel.dim_pad, dtype=np.complex64) + self.phase_adj = pyfftw.n_byte_align(self.phase_adj, pyfftw.simd_alignment) + self.phase_adj[:, :phase_adj.shape[1]] = phase_adj + self._ifft2 = pyfftw.builders.ifftn(self.u_mag, threads=kernel.threads) + phase_adj_fft_TEST = self._ifft2(self.phase_adj).real * np.prod(self.kernel.dim_pad) + import pdb; pdb.set_trace() + return phase_adj_fft + + def _irfft2_adj_fftw(self, mag_adj_fft): + x = mag_adj_fft + n_out = self.kernel.dim_fft[1] + xp = np.zeros(self.kernel.dim_pad, dtype=np.complex64) + xp[...] = np.fft.fft(x, axis=1) / self.kernel.dim_pad[1] # TODO: Convert to FFTW + xp[:, 1:n_out - 1] += np.conj(xp[:, :n_out-1:-1]) # if even (guaranteed for dim_pad) + mag_adj = np.fft.fft(xp[:, :n_out], axis=0) / self.kernel.dim_pad[0] # TODO: Convert to FFTW + return mag_adj + import pyfftw + kernel = self.kernel + self.mag_adj = np.zeros(kernel.dim_pad, dtype=np.complex64) + self.mag_adj = pyfftw.n_byte_align(self.mag_adj, pyfftw.simd_alignment) + self._fft_ax0 = pyfftw.FFTW(self.mag_adj, threads=kernel.threads) + self._fft_ax1 = pyfftw.FFTW(self.mag_adj, threads=kernel.threads) + n_out = self.kernel.dim_fft[1] + self.mag_adj[...] = np.fft.fft(phase_adj, axis=1) / self.kernel.dim_pad[1] # TODO: Convert to FFTW + self.mag_adj[:, 1:n_out - 1] += np.conj(self.mag_adj[:, :n_out-1:-1]) # if even (guaranteed for dim_pad) + mag_adj_TEST = np.fft.fft(self.mag_adj[:, :n_out], axis=0) / self.kernel.dim_pad[0] # TODO: Convert to FFTW + import pdb; pdb.set_trace() + return mag_adj class PhaseMapperRDRC(PhaseMapper): @@ -339,7 +307,7 @@ class PhaseMapperRDRC(PhaseMapper): u_phi = self.kernel.u v_phi = self.kernel.v # Calculation of the phase: - phase = np.zeros(dim_uv) + phase = np.zeros(dim_uv, dtype=np.float32) if self.numcore: nc.phasemapper_real_convolve(dim_uv[0], dim_uv[1], v_phi, u_phi, v_mag, u_mag, phase, self.threshold) diff --git a/pyramid/reconstruction.py b/pyramid/reconstruction.py index 62812db..4da85c7 100644 --- a/pyramid/reconstruction.py +++ b/pyramid/reconstruction.py @@ -103,7 +103,7 @@ def optimize_linear(data, regularisator=None, max_iter=None, info=None): mag_data : :class:`~pyramid.magdata.MagData` The reconstructed magnetic distribution as a :class:`~.MagData` object. - ''' + ''' # TODO: info document! import jutil.cg as jcg _log.debug('Calling optimize_linear') # Set up necessary objects: diff --git a/pyramid/util.py b/pyramid/util.py new file mode 100644 index 0000000..40ce45a --- /dev/null +++ b/pyramid/util.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Nov 26 10:52:32 2014 + +@author: Jan +""" + + +import numpy as np + + +__all__ = ['IndexConverter3D', 'IndexConverter2D'] + + +class IndexConverter3D(object): + + # TODO: Document everything! 3comp: 3 components! + + def __init__(self, dim): + assert len(dim) == 3, 'Dimensions have to be two-dimensioal!' + self.dim = dim + self.size_3d = np.prod(dim) + self.size_2d = dim[2]*dim[1] + + def ind_to_coord(self, ind): + assert ind < self.size_3d, 'Index out of range!' + z, remain = int(ind/self.size_2d), ind % self.size_2d + y, x = int(remain/self.dim[2]), remain % self.dim[2] + return (z, y, x) + + def ind3comp_to_coord(self, ind): + assert ind < 3 * self.size_3d, 'Index out of range!' + c, remain = int(ind/self.size_3d), ind % self.size_3d + z, remain = int(remain/self.size_2d), remain % self.size_2d + y, x = int(remain/self.dim[2]), remain % self.dim[2] + return (c, z, y, x) + + def coord_to_ind(self, coord): + if coord is None: + return None + z, y, x = coord + ind = z*self.size_2d + y*self.dim[2] + x + assert ind < self.size_3d, 'Index out of range!' + return ind + + def coord_to_ind3comp(self, coord): + if coord is None: + return [None, None, None] + z, y, x = coord + ind = [c*self.size_3d + z*self.size_2d + y*self.dim[2] + x for c in range(3)] + assert ind[-1] < 3 * self.size_3d, 'Index out of range!' + return ind + + def get_neighbour_coord(self, coord): + def validate_coord(coord): + dim = self.dim_uv + if (0 <= coord[0] < dim[0]) and (0 <= coord[1] < dim[1]) and (0 <= coord[2] < dim[2]): + return coord + else: + return None + + z, y, x = coord + t, d = (z-1, y, x), (z+1, y, x) # t: top, d: down + f, b = (z, y-1, x), (z, y+1, x) # f: front, b: back + l, r = (z, y, x-1), (z, y, x+1) # l: left, r: right + return [validate_coord(i) for i in [t, d, f, b, l, r]] + + def get_neighbour_ind(self, coord): + neighbours = [self.coord_to_ind(i) for i in self.get_neighbour_coord(coord)] + return np.reshape(neighbours, (3, 2)) + + def get_neighbour_ind3comp(self, coord): + neighbours = [self.coord_to_ind3comp(i) for i in self.get_neighbour_coord(coord)] + return np.reshape(np.swapaxes(neighbours, 0, 1), (3, 3, 2)) + + +class IndexConverter2D(object): + + def __init__(self, dim_uv): + assert len(dim_uv) == 2, 'Dimensions have to be two-dimensioal!' + self.dim_uv = dim_uv + self.size_2d = np.prod(dim_uv) + + def ind_to_coord(self, ind): + assert ind < self.size_2d, 'Index out of range!' + v, u = int(ind/self.dim_uv[1]), ind % self.dim_uv[1] + return v, u + + def ind2comp_to_coord(self, ind): + assert ind < 2 * self.size_2d, 'Index out of range!' + c, remain = int(ind/self.size_2d), ind % self.size_2d + v, u = int(remain/self.dim_uv[1]), remain % self.dim_uv[1] + return c, v, u + + def coord_to_ind(self, coord): + if coord is None: + return None + v, u = coord + ind = v*self.dim_uv[1] + u + assert ind < self.size_2d, 'Index out of range!' + return ind + + def coord_to_ind2comp(self, coord): + if coord is None: + return [None, None] + v, u = coord + ind = [i*self.size_2d + v*self.dim_uv[1] + u for i in range(2)] + assert ind[-1] < 2 * self.size_2d, 'Index out of range!' + return ind + + def get_neighbour_coord(self, coord): + def validate_coord(coord): + if (0 <= coord[0] < self.dim_uv[0]) and (0 <= coord[1] < self.dim_uv[1]): + return coord + else: + return None + v, u = coord + f, b = (v-1, u), (v+1, u) # f: front, b: back + l, r = (v, u-1), (v, u+1) # l: left, r: right + return [validate_coord(i) for i in [f, b, l, r]] + + def get_neighbour_ind(self, coord): + neighbours = [self.coord_to_ind(i) for i in self.get_neighbour_coord(coord)] + return np.reshape(neighbours, (2, 2)) + + def get_neighbour_ind2comp(self, coord): + neighbours = [self.coord_to_ind2comp(i) for i in self.get_neighbour_coord(coord)] + return np.reshape(np.swapaxes(neighbours, 0, 1), (2, 2, 2)) + + +# TODO: method for constructing 3D mask from 2D masks? diff --git a/pyramid/version.py b/pyramid/version.py index 049ee68..2a9ac58 100644 --- a/pyramid/version.py +++ b/pyramid/version.py @@ -1,3 +1,3 @@ # THIS FILE IS GENERATED FROM THE PYRAMID SETUP.PY version="0.1.0-dev" -hg_revision="aa1763114c1c+" +hg_revision="ac57353bbe0b+" diff --git a/scripts/collaborations/patrick_nanowire_reconstruction.py b/scripts/collaborations/patrick_nanowire_reconstruction.py index 03f31ed..7a60887 100644 --- a/scripts/collaborations/patrick_nanowire_reconstruction.py +++ b/scripts/collaborations/patrick_nanowire_reconstruction.py @@ -9,7 +9,7 @@ from PIL import Image import numpy as np from jutil.taketime import TakeTime -from pyramid import * +from pyramid import * # analysis:ignore import logging import logging.config @@ -21,36 +21,68 @@ LOGGING_CONF = os.path.join(os.path.dirname(os.path.realpath(pyramid.__file__)), logging.config.fileConfig(LOGGING_CONF, disable_existing_loggers=False) ################################################################################################### -a = 1.0 # in nm +a = 1.455 # in nm gain = 5 b_0 = 1 lam = 1E-6 PATH = '../../output/patrick/' -dim_uv = (128, 128) +PHASE = '-10_pha' +MASK = 'mask_-10_elong_0' +longFOV = True +longFOV_string = np.where(longFOV, 'longFOV', 'normalFOV') +IMAGENAME = '{}_{}_{}_'.format(MASK, PHASE, longFOV_string) +PHASE_MAX = 0.92 # -10°: 0.92, 30°: 7.68 +PHASE_MIN = -16.85 # -10°: -16.85, 30°: -18.89 +PHASE_DELTA = PHASE_MAX - PHASE_MIN ################################################################################################### # Read in files: -im_mask = Image.open(PATH+'min102mask.tif') -im_mask.thumbnail(dim_uv, Image.ANTIALIAS) +im_mask = Image.open(PATH+MASK+'.tif') +#im_mask.thumbnail((125, 175), Image.ANTIALIAS) +im_phase = Image.open(PATH+PHASE+'.tif') +#im_phase.thumbnail((125, 125), Image.ANTIALIAS) + mask = np.array(np.array(im_mask)/255, dtype=bool) +dim_uv = mask.shape +phase = (np.array(im_phase)/255.-0.5) * PHASE_DELTA +pad = dim_uv[0] - phase.shape[0] +phase_pad = np.zeros(dim_uv) +phase_pad[pad:, :] = phase + mask = np.expand_dims(mask, axis=0) -im_phase = Image.open(PATH+'min102.tif') -im_phase.thumbnail(dim_uv, Image.ANTIALIAS) -phase = np.array(im_phase)/255. - 0.5#*(18.19977+8.057761) - 8.057761 -phase_map = PhaseMap(a, phase) dim = mask.shape +phase_map = PhaseMap(a, phase) +phase_map_pad = PhaseMap(a, phase_pad) + data_set = DataSet(a, dim, b_0, mask) -data_set.append(phase_map, SimpleProjector(dim)) +data_set.append(phase_map_pad, SimpleProjector(dim)) + +# Create Se_inv +if longFOV: + mask_Se = np.ones(dim_uv) + mask_Se[:pad, :] = 0 + data_set.set_Se_inv_diag_with_masks([mask_Se]) regularisator = FirstOrderRegularisator(mask, lam, p=2) with TakeTime('reconstruction time'): mag_data_rec = rc.optimize_linear(data_set, regularisator=regularisator, max_iter=50) +phase_map_rec_pad = pm(mag_data_rec) +phase_map_rec = PhaseMap(a, phase_map_rec_pad.phase[pad:, :]) +phase_map_diff = phase_map_rec - phase_map + # Display the reconstructed phase map and holography image: -phase_map.display_combined('Input PhaseMap', gain=40) +phase_map.display_combined('Input PhaseMap', gain=4) +plt.savefig(PATH+IMAGENAME+'ORIGINAL.png') +phase_map_pad.display_combined('Input PhaseMap (padded)', gain=4) +phase_map_rec_pad.display_combined('Reconstr. Distribution (padded)', gain=4) +plt.savefig(PATH+IMAGENAME+'RECONSTRUCTION_PADDED.png') +phase_map_rec.display_combined('Reconstr. Distribution', gain=4) +plt.savefig(PATH+IMAGENAME+'RECONSTRUCTION.png') +phase_map_diff.display_combined('Difference') +plt.savefig(PATH+IMAGENAME+'DIFFERENCE.png') +mag_data_rec.scale_down(4) mag_data_rec.quiver_plot(log=True) -phase_map_rec = pm(mag_data_rec) -phase_map_rec.display_combined('Reconstr. Distribution', gain=40) -#plt.savefig(dirname + "/reconstr.png") +plt.savefig(PATH+IMAGENAME+'MAGNETIZATION_DOWNSCALE4.png') diff --git a/scripts/reconstruction/reconstruction_linear_test.py b/scripts/reconstruction/reconstruction_linear_test.py index 2b29a26..e6a74cc 100644 --- a/scripts/reconstruction/reconstruction_linear_test.py +++ b/scripts/reconstruction/reconstruction_linear_test.py @@ -36,6 +36,7 @@ dim = (32, 32, 32) dim_uv = dim[1:3] count = 16 lam = 1E-4 +use_fftw = True center = (dim[0]/2-0.5, int(dim[1]/2)-0.5, int(dim[2]/2)-0.5) radius_core = dim[1]/8 radius_shell = dim[1]/4 @@ -51,9 +52,9 @@ shape = mc.Shapes.disc(dim, center, radius_shell, height) magnitude = mc.create_mag_dist_vortex(shape) mag_data = MagData(a, magnitude) -#mag_data.quiver_plot('z-projection', proj_axis='z') -#mag_data.quiver_plot('y-projection', proj_axis='y') -#mag_data.quiver_plot3d('Original distribution') +mag_data.quiver_plot('z-projection', proj_axis='z') +mag_data.quiver_plot('y-projection', proj_axis='y') +mag_data.quiver_plot3d('Original distribution') tilts_full = np.linspace(-pi/2, pi/2, num=count/2, endpoint=False) tilts_miss = np.linspace(-pi/3, pi/3, num=count/2, endpoint=False) @@ -76,7 +77,7 @@ noise = 0 print('--Setting up data collection') mask = mag_data.get_mask() -data = DataSet(a, dim, b_0, mask) +data = DataSet(a, dim, b_0, mask, use_fftw=use_fftw) data.projectors = projectors data.phase_maps = data.create_phase_maps(mag_data) @@ -89,12 +90,13 @@ if noise != 0: print('--Reconstruction') reg = FirstOrderRegularisator(mask, lam, p=2) +info = [] with TakeTime('reconstruction'): - mag_data_opt = rc.optimize_linear(data, regularisator=reg, max_iter=100) + mag_data_opt = rc.optimize_linear(data, regularisator=reg, max_iter=100, info=info) ################################################################################################### print('--Plot stuff') mag_data_opt.quiver_plot3d('Reconstructed distribution') -#(mag_data_opt - mag_data).quiver_plot3d('Difference') -#phase_maps_opt = data.create_phase_maps(mag_data_opt) +(mag_data_opt - mag_data).quiver_plot3d('Difference') +phase_maps_opt = data.create_phase_maps(mag_data_opt) diff --git a/scripts/reconstruction/reconstruction_linear_test_batch.py b/scripts/reconstruction/reconstruction_linear_test_batch.py index 38a25fc..3f310a8 100644 --- a/scripts/reconstruction/reconstruction_linear_test_batch.py +++ b/scripts/reconstruction/reconstruction_linear_test_batch.py @@ -11,12 +11,7 @@ import itertools from mayavi import mlab -import pyramid -from pyramid.magdata import MagData -from pyramid.phasemap import PhaseMap -from pyramid.projector import YTiltProjector, XTiltProjector -from pyramid.dataset import DataSet -from pyramid.regularisator import ZeroOrderRegularisator, FirstOrderRegularisator +from pyramid import * #analysis:ignore import pyramid.magcreator as mc import pyramid.reconstruction as rc @@ -32,7 +27,7 @@ import logging.config LOGGING_CONF = os.path.join(os.path.dirname(os.path.realpath(pyramid.__file__)), 'logging.ini') -PATH = '../../output/3d reconstruction/' +PATH = '../../output/3d reconstruction/Phase 1/' logging.config.fileConfig(LOGGING_CONF, disable_existing_loggers=False) if not os.path.exists(PATH): @@ -48,36 +43,38 @@ b_0 = 1. print('--Magnetization distributions') dim = (32, 32, 32) -center = (dim[0]/2-0.5, int(dim[1]/2)-0.5, int(dim[2]/2)-0.5) +center = (dim[0]/2-0.5, dim[1]/2-0.5, dim[2]/2-0.5) radius_core = dim[1]/8 radius_shell = dim[1]/4 height = dim[0]/2 +width_cube = (dim[0]/4, dim[1]/4, dim[2]/4) +width_slab = (dim[0]/4, dim[1]/4, dim[2]/2) # Vortex: mag_shape_disc = mc.Shapes.disc(dim, center, radius_shell, height) mag_data_vortex = MagData(a, mc.create_mag_dist_vortex(mag_shape_disc)) -# Sphere: -mag_shape_sphere = mc.Shapes.sphere(dim, center, radius_shell) -mag_data_sphere = MagData(a, mc.create_mag_dist_homog(mag_shape_sphere, phi=pi/4, theta=pi/4)) # Core-Shell: mag_shape_core = mc.Shapes.disc(dim, center, radius_core, height) mag_shape_shell = np.logical_xor(mag_shape_disc, mag_shape_core) mag_data_core_shell = MagData(a, mc.create_mag_dist_vortex(mag_shape_shell, magnitude=0.75)) mag_data_core_shell += MagData(a, mc.create_mag_dist_homog(mag_shape_core, phi=0, theta=0)) - -# TODO: cubic bar magnet, rectangular bar magnet, -sphere +# Cubic slab: +mag_shape_cube = mc.Shapes.slab(dim, center, width_cube) +mag_data_cube = MagData(a, mc.create_mag_dist_homog(mag_shape_cube, 0)) +# Rectangular slab: +mag_shape_slab = mc.Shapes.slab(dim, center, width_slab) +mag_data_slab = MagData(a, mc.create_mag_dist_homog(mag_shape_slab, 0)) ################################################################################################### print('--Set up configurations') -mag_datas = {'vortex_disc': mag_data_vortex, 'homog_sphere': mag_data_sphere, - 'core_shell': mag_data_core_shell} -masks = {'mask': True} -xy_tilts = {'xy': [True, True]} +mag_datas = {'rect_slab': mag_data_slab} +masks = {'mask': True, 'nomask': False} +xy_tilts = {'y': [False, True]} max_tilts = [60] tilt_steps = [10] noises = [0] orders = [1] -lambdas = [1E-4] +lambdas = [1E-2, 1E-3, 1E-4, 1E-5, 1E-6] # Combining everything: config_list = [mag_datas.keys(), masks.keys(), xy_tilts.keys(), max_tilts, tilt_steps, noises, orders, lambdas] @@ -93,13 +90,15 @@ for key in mag_datas: ################################################################################################### print('--Reconstruction') -print 'Number of configurations:', len(list(itertools.product(*config_list))) +total_number = len(list(itertools.product(*config_list))) +print 'Number of configurations:', total_number -for configuration in itertools.product(*config_list): +for i, configuration in enumerate(itertools.product(*config_list)): # Extract keys: mag_key, mask_key, xy_key, max_tilt, tilt_step, noise, order, lam = configuration - print '----CONFIG:', configuration - name = '{}_{}_{}tilts_max{}_in{}steps_{}noise_{}order_{}lam'.format(*configuration) + print '----CONFIG {} of {}:'.format(i+1, total_number) + print configuration + name = '{}_{}_{}tilts_max{}_in{}steps_{}noise_{}order_{:.0E}lam'.format(*configuration) dim = mag_datas[mag_key].dim # Mask: if masks[mask_key]: @@ -140,7 +139,7 @@ for configuration in itertools.product(*config_list): (mag_data_rec - mag_datas[mag_key]).quiver_plot3d('Difference ({})'.format(mag_key)) mlab.savefig('{}{}_DIFF.png'.format(PATH, name), size=(800, 800)) mlab.close(all=True) - data_shelve = shelve.open(PATH+'/3d_shelve') + data_shelve = shelve.open(PATH+'3d_shelve') data_shelve[name] = info data_shelve.close() print 'chisq = {:.6f}, chisq_m = {:.6f}, chisq_a = {:.6f}'.format(*info) diff --git a/scripts/test methods/compare_methods.py b/scripts/test methods/compare_methods.py index a164ac9..dabd9f3 100644 --- a/scripts/test methods/compare_methods.py +++ b/scripts/test methods/compare_methods.py @@ -30,10 +30,8 @@ logging.config.fileConfig(LOGGING_CONF, disable_existing_loggers=False) b_0 = 1.0 # in T a = 1.0 # in nm phi = pi/4 -numcore = False -padding = 1 gain = 'auto' -dim = (32, 256, 256) # in px (z, y, x) +dim = (16, 128, 128) # in px (z, y, x) # Create magnetic shape: geometry = 'disc' @@ -59,23 +57,32 @@ mag_data = MagData(a, mc.create_mag_dist_homog(mag_shape, phi)) projector = SimpleProjector(dim) projection = projector(mag_data) # Construct PhaseMapper objects: -pm_real = PhaseMapperRDRC(Kernel(a, projector.dim_uv, b_0), numcore=numcore) -pm_conv = PhaseMapperRDFC(Kernel(a, projector.dim_uv, b_0)) -pm_four = PhaseMapperFDFC(a, projector.dim_uv, b_0, padding=padding) +pm_real_slow = PhaseMapperRDRC(Kernel(a, projector.dim_uv, b_0), numcore=False) +pm_real_fast = PhaseMapperRDRC(Kernel(a, projector.dim_uv, b_0), numcore=True) +pm_conv_slow = PhaseMapperRDFC(Kernel(a, projector.dim_uv, b_0, use_fftw=False)) +pm_conv_fast = PhaseMapperRDFC(Kernel(a, projector.dim_uv, b_0, use_fftw=True)) +pm_four_pad0 = PhaseMapperFDFC(a, projector.dim_uv, b_0, padding=0) +pm_four_pad1 = PhaseMapperFDFC(a, projector.dim_uv, b_0, padding=1) # Get times for different approaches: start_time = time.time() -phase_map_real = pm_real(projection) -print 'Time for RDRC: ', time.time() - start_time +phase_map_real = pm_real_slow(projection) +print 'Time for RDRC no numcore :', time.time() - start_time start_time = time.time() -phase_map_conv = pm_conv(projection) -print 'Time for RDFC: ', time.time() - start_time +phase_map_real = pm_real_fast(projection) +print 'Time for RDRC numcore :', time.time() - start_time start_time = time.time() -phase_map_conv = pm_conv(projection) -print 'Time for RDFC: ', time.time() - start_time +phase_map_conv = pm_conv_slow(projection) +print 'Time for RDFC numpy conv.:', time.time() - start_time start_time = time.time() -phase_map_four = pm_four(projection) -print 'Time for FDFC: ', time.time() - start_time +phase_map_conv = pm_conv_fast(projection) +print 'Time for RDFC FFTW conv.:', time.time() - start_time +start_time = time.time() +phase_map_four = pm_four_pad0(projection) +print 'Time for FDFC padding 0 :', time.time() - start_time +start_time = time.time() +phase_map_four = pm_four_pad1(projection) +print 'Time for FDFC padding 1 :', time.time() - start_time # Display the combinated plots with phasemap and holography image: phase_map_ana.display_combined('Analytic Solution', gain=gain) -- GitLab