def potential(self, q, steps): hx = steps[0] hy = steps[1] hz = steps[2] Nx = q.shape[0] Ny = q.shape[1] Nz = q.shape[2] out = np.zeros((2*Nx-1, 2*Ny-1, 2*Nz-1)) out[:Nx, :Ny, :Nz] = q K1 = self.sym_kernel(q.shape, steps) K2 = np.zeros((2*Nx-1, 2*Ny-1, 2*Nz-1)) K2[0:Nx, 0:Ny, 0:Nz] = K1 K2[0:Nx, 0:Ny, Nz:2*Nz-1] = K2[0:Nx, 0:Ny, Nz-1:0:-1] #z-mirror K2[0:Nx, Ny:2*Ny-1,:] = K2[0:Nx, Ny-1:0:-1, :] #y-mirror K2[Nx:2*Nx-1, :, :] = K2[Nx-1:0:-1, :, :] #x-mirror t0 = time() if pyfftw_flag: nthread = multiprocessing.cpu_count() K2_fft = pyfftw.builders.fftn(K2, axes=None, overwrite_input=False, planner_effort='FFTW_ESTIMATE', threads=nthread, auto_align_input=False, auto_contiguous=False, avoid_copy=True) out_fft = pyfftw.builders.fftn(out, axes=None, overwrite_input=False, planner_effort='FFTW_ESTIMATE', threads=nthread, auto_align_input=False, auto_contiguous=False, avoid_copy=True) out_ifft = pyfftw.builders.ifftn(out_fft()*K2_fft(), axes=None, overwrite_input=False, planner_effort='FFTW_ESTIMATE', threads=nthread, auto_align_input=False, auto_contiguous=False, avoid_copy=True) out = np.real(out_ifft()) else: out = np.real(ifftn(fftn(out)*fftn(K2))) t1 = time() if self.debug: print( 'fft time:', t1-t0, ' sec') out[:Nx, :Ny, :Nz] = out[:Nx,:Ny,:Nz]/(4*pi*epsilon_0*hx*hy*hz) return out[:Nx, :Ny, :Nz]
def convolve(arr1, arr2, dx=None, axes=None): """ Performs a centred convolution of input arrays Parameters ---------- arr1, arr2 : `numpy.ndarray` Arrays to be convolved. If dimensions are not equal then 1s are appended to the lower dimensional array. Otherwise, arrays must be broadcastable. dx : float > 0, list of float, or `None` , optional Grid spacing of input arrays. Output is scaled by `dx**max(arr1.ndim, arr2.ndim)`. default=`None` applies no scaling axes : tuple of ints or `None`, optional Choice of axes to convolve. default=`None` convolves all axes """ if arr2.ndim > arr1.ndim: arr1, arr2 = arr2, arr1 if axes is None: axes = range(arr2.ndim) arr2 = arr2.reshape(arr2.shape + (1, ) * (arr1.ndim - arr2.ndim)) if dx is None: dx = 1 elif isscalar(dx): dx = dx**(len(axes) if axes is not None else arr1.ndim) else: dx = prod(dx) arr1 = fftn(arr1, axes=axes) arr2 = fftn(ifftshift(arr2), axes=axes) out = ifftn(arr1 * arr2, axes=axes) * dx return require(out, requirements="CA")
def fftconvolve(in1, in2, mode="same", threads=1): """Same as above but with pyfftw added in""" in1 = np.asarray(in1) in2 = np.asarray(in2) if in1.ndim == in2.ndim == 0: # scalar inputs return in1 * in2 elif not in1.ndim == in2.ndim: raise ValueError("in1 and in2 should have the same dimensionality") elif in1.size == 0 or in2.size == 0: # empty arrays return np.array([]) s1 = np.array(in1.shape) s2 = np.array(in2.shape) complex_result = (np.issubdtype(in1.dtype, complex) or np.issubdtype(in2.dtype, complex)) shape = s1 + s2 - 1 # Check that input sizes are compatible with 'valid' mode if sig._inputs_swap_needed(mode, s1, s2): # Convolution is commutative; order doesn't have any effect on output in1, s1, in2, s2 = in2, s2, in1, s1 # Speed up FFT by padding to optimal size for FFTPACK fshape = [sig.fftpack.helper.next_fast_len(int(d)) for d in shape] fslice = tuple([slice(0, int(sz)) for sz in shape]) # Pre-1.9 NumPy FFT routines are not threadsafe. For older NumPys, make # sure we only call rfftn/irfftn from one thread at a time. if not complex_result and (sig._rfft_mt_safe or sig._rfft_lock.acquire(False)): try: sp1 = rfftn(in1, fshape, threads=threads) sp2 = rfftn(in2, fshape, threads=threads) ret = (irfftn(sp1 * sp2, fshape, threads=threads)[fslice].copy()) finally: if not sig._rfft_mt_safe: sig._rfft_lock.release() else: # If we're here, it's either because we need a complex result, or we # failed to acquire _rfft_lock (meaning rfftn isn't threadsafe and # is already in use by another thread). In either case, use the # (threadsafe but slower) SciPy complex-FFT routines instead. sp1 = fftn(in1, fshape, threads=threads) sp2 = fftn(in2, fshape, threads=threads) ret = ifftn(sp1 * sp2, threads=threads)[fslice].copy() if not complex_result: ret = ret.real if mode == "full": return ret elif mode == "same": return sig._centered(ret, s1) elif mode == "valid": return sig._centered(ret, s1 - s2 + 1) else: raise ValueError("Acceptable mode flags are 'valid'," " 'same', or 'full'.")
def fourier_shell_correlation(obj, ref, step_size=1, save_path='fsc', save_mask=True): if not os.path.exists(save_path): os.makedirs(save_path) radius_max = int(min(obj.shape) / 2) f_obj = np_fftshift(fftn(obj)) f_ref = np_fftshift(fftn(ref)) f_prod = f_obj * np.conjugate(f_ref) f_obj_2 = np.real(f_obj * np.conjugate(f_obj)) f_ref_2 = np.real(f_ref * np.conjugate(f_ref)) radius_ls = np.arange(1, radius_max, step_size) fsc_ls = [] np.save(os.path.join(save_path, 'radii.npy'), radius_ls) for rad in radius_ls: print(rad) if os.path.exists( os.path.join(save_path, 'mask_rad_{:04d}.tiff'.format(int(rad)))): mask = dxchange.read_tiff( os.path.join(save_path, 'mask_rad_{:04d}.tiff'.format(int(rad)))) else: mask = generate_shell(obj.shape, rad, anti_aliasing=2) if save_mask: dxchange.write_tiff( mask, os.path.join(save_path, 'mask_rad_{:04d}.tiff'.format(int(rad))), dtype='float32', overwrite=True) fsc = abs(np.sum(f_prod * mask)) fsc /= np.sqrt(np.sum(f_obj_2 * mask) * np.sum(f_ref_2 * mask)) fsc_ls.append(fsc) np.save(os.path.join(save_path, 'fsc.npy'), fsc_ls) matplotlib.rcParams['pdf.fonttype'] = 'truetype' fontProperties = { 'family': 'serif', 'serif': ['Times New Roman'], 'weight': 'normal', 'size': 12 } plt.rc('font', **fontProperties) plt.plot(radius_ls.astype(float) / radius_ls[-1], fsc_ls) plt.xlabel('Spatial frequency (1 / Nyquist)') plt.ylabel('FSC') plt.savefig(os.path.join(save_path, 'fsc.pdf'), format='pdf')
def template_generation(imgs): avgimg = np.median(imgs, 0) avgimg_fft = fftn(avgimg) # Estimation of the difference for few trials in order to create a new template datafft = fftn(imgs, axes=(1, 2)) mov = Parallel(n_jobs=4)(delayed(register_translation)(avgimg_fft, datafft[ i, :, :], 100, 'fourier') for i in np.arange(len(imgs))) x = [element[0][0] for element in mov] y = [element[0][1] for element in mov] # Creation of the new template newimgs = shifts(imgs, x, y) # Run the algorythm to estimate registration between frames template = np.median(newimgs, 0) return template
def __init__( self, modulus, support, beta=0.9, binning=1, # for high-energy CDI. Set to 1 for regular phase retrieval. gpu=False, random_start=True): self._modulus = fftshift(modulus) self._support = support self._beta = beta # self._modulus_sum = modulus.sum() self._support_comp = 1. - support if random_start: self._cImage = np.exp(2.j * np.pi * np.random.rand( binning * self._modulus.shape[0], binning * self._modulus.shape[1], self._modulus.shape[2])) * self._support else: self._cImage = 1. * support self._cachedImage = np.zeros(self._cImage.shape).astype(complex) self._cImage_fft_mod = np.absolute(fftn(self._cImage)) self._error = [] self._UpdateError() self.generateAlgoDict() if gpu == True: self.gpusolver = accelerator.Solver(self.generateGPUPackage()) return
def fftn(a, axes=None): if _use_fftw: from pyfftw.interfaces.numpy_fft import fftn _init_pyfftw() return fftn(a, axes=axes, **_fft_extra_args) else: return np.fft.fftn(a, axes=axes)
def fft2d(arr2d, mode, numpy_fft=pyfftw.interfaces.numpy_fft, only_real=False, batch=False): ''' we apply an alterating +1/-1 multiplicative before we go to/from Fourier space. Later we apply this again to the transform. TODO: look into pyfftw.interfaces.numpy_fft.irfftn ''' assert (arr2d.ndim == 2 and not batch) or (batch and arr2d.ndim == 3) n1, n2 = arr2d.shape[-2:] assert n1 == n2 arr2d = neg_pos_2d(arr2d.reshape(-1, n1, n1).copy()) if mode == 'f': arr2d_f = numpy_fft.fftn(arr2d, axes=(-2, -1)) arr2d_f /= n1 elif mode == 'i': arr2d_f = numpy_fft.ifftn(arr2d, axes=(-2, -1)) arr2d_f *= n1 if only_real: arr2d_f = arr2d_f.real arr2d_f = neg_pos_2d(arr2d_f.copy()) if not batch: arr2d_f = arr2d_f.reshape(n1, n2) return (arr2d_f)
def cryo_conv_vol(x, kernel_f): n = x.shape[0] n_ker = kernel_f.shape[0] if np.any(np.array(x.shape) != n): raise ValueError('Volume in `x` must be cubic') if np.any(np.array(kernel_f.shape) != n_ker): raise ValueError('Convolution kernel in `kernel_f` must be cubic') is_singleton = len(x.shape) == 3 shifted_kernel_f = np.fft.ifftshift(np.fft.ifftshift(np.fft.ifftshift(kernel_f, 0), 1), 2) if is_singleton: x = numpy_fft.fftn(x, [n_ker] * 3) else: x = numpy_fft.fft(x, n=n_ker, axis=0) x = numpy_fft.fft(x, n=n_ker, axis=1) x = numpy_fft.fft(x, n=n_ker, axis=2) x *= shifted_kernel_f if is_singleton: x = numpy_fft.ifftn(x) x = x[:n, :n, :n] else: x = numpy_fft.ifft(x, axis=0) x = numpy_fft.ifft(x, axis=1) x = numpy_fft.ifft(x, axis=2) x = x.real return x
def ufftn(inarray, dim=None): """N-dimensional unitary Fourier transform. Parameters ---------- inarray : ndarray The array to transform. dim : int, optional The last axis along which to compute the transform. All axes by default. Returns ------- outarray : ndarray (same shape than inarray) The unitary N-D Fourier transform of ``inarray``. Examples -------- >>> input = np.ones((3, 3, 3)) >>> output = ufftn(input) >>> np.allclose(np.sum(input) / np.sqrt(input.size), output[0, 0, 0]) True >>> output.shape (3, 3, 3) """ if dim is None: dim = inarray.ndim outarray = fftn(inarray, axes=range(-dim, 0)) return outarray / np.sqrt(np.prod(inarray.shape[-dim:]))
def registerPP(data, template=[], size=30): """ Register multiple images according to a template, if the template does not exist create one with the first SIZE images. """ #size of the first bunch of time to create the template nt, nx, ny = data.shape datasmooth = gaussian_filter(data, (0, 2, 2)) # First approximation of the template if len(template) == 0: template = template_generation(data[:size, :, :]) template_fft = fftn(template) mov = Parallel(n_jobs=4)(delayed(register_translation)( template_fft, fftn(data[i, :, :]), 100, 'fourier') for i in np.arange(nt)) x = [element[0][0] for element in mov] y = [element[0][1] for element in mov] return x, y
def easy_fft(data, axes=None): """utility method that includes fft shifting""" return fftshift( fftn( ifftshift( data, axes=axes ), axes=axes ), axes=axes)
def removePhaseRamps( img ): #... by centering the Bragg peak in the array fimg = fftshift( fftn( fftshift( img ) ) ) intens = np.absolute( fimg )**2 maxHere = np.where( intens==intens.max() ) for n in [ 0, 1, 2 ]: fimg = np.roll( fimg, fimg.shape[n]//2-maxHere[n], axis=n ) imgout = fftshift( ifftn( fftshift( fimg ) ) ) return imgout
def fftdeconvolve(image, psf): """ De-convolution by directly dividing the DFT... may not be numerically desirable for many applications. Noise could be an issue. Use scipy.fftpack for now; will re-write for anfft later... Taken from this post on stackoverflow.com: http://stackoverflow.com/questions/17473917/is-there-a-equivalent-of-scipy-signal-deconvolve-for-2d-arrays """ if not _pyfftw: raise NotImplementedError image = image.astype('float') psf = psf.astype('float') # image_fft = fftpack.fftshift(fftpack.fftn(image)) # psf_fft = fftpack.fftshift(fftpack.fftn(psf)) image_fft = fftshift(fftn(image)) psf_fft = fftshift(fftn(psf)) kernel = fftshift(ifftn(ifftshift(image_fft / psf_fft))) return kernel
def __init__( self, measured_intensity, gpack ): self._shape = gpack[ 'array_shape' ] self._modulus_measured = tf.constant( np.sqrt( measured_intensity ), dtype=tf.float32 ) pts = self._setupDomain( gpack=gpack ) self._setupConstants( pts ) self._setCoherentEstimate( np.absolute( fftn( gpack[ 'cImage' ] ) )**2 ) self._setupVariables() self._setupAuxiliary() self._updateBlurKernel() self._setupOptimizer( learning_rate=0.01, momentum=0.99 ) return
def realign_image_1d(arr, shift, angle=0): # if both shifts are integers, do circular shift; otherwise perform Fourier shift. if np.abs(np.array(shift) - np.round(shift)) < 0.01: temp = np.roll(arr, int(shift)) temp = temp.astype('float32') else: temp = fourier_shift(fftn(arr), shift) temp = ifftn(temp) temp = np.abs(temp).astype('float32') return temp
def cfftn(data, axes): """ Centered fast fourier transform, n-dimensional. :param data: Complex input data. :param axes: Axes along which to shift and transform. :return: Fourier transformed data. """ return fft.fftshift(fft.fftn(fft.ifftshift(data, axes=axes), axes=axes, norm='ortho'), axes=axes)
def fftw(E): if np.any(np.array(E.shape[2:]) > 1): assert(E.ndim-2 == self.data_shape.size and np.all(E.shape[2:] == self.data_shape)) if np.all(E.shape == fft_vec_object.input_shape): result_array = self.__empty_word_aligned(E.shape, dtype=np.complex128) fft_vec_object(E, result_array) else: log.debug('Fourier Transform: Array shape not standard, falling back to default interface.') result_array = ft.fftn(E, axes=ft_axes) return result_array else: return E.copy()
def plan_fft(A, n=None, axis=None, norm=None, **_): """ Plans an fft for repeated use. Parameters are the same as for `pyfftw`'s `fftn` which are, where possible, the same as the `numpy` equivalents. Note that some functionality is only possible when using the `pyfftw` backend. Parameters ---------- A : `numpy.ndarray`, of dimension `d` Array of same shape to be input for the fft n : iterable or `None`, `len(n) == d`, optional The output shape of fft (default=`None` is same as `A.shape`) axis : `int`, iterable length `d`, or `None`, optional The axis (or axes) to transform (default=`None` is all axes) overwrite : `bool`, optional Whether the input array can be overwritten during computation (default=False) planner : {0, 1, 2, 3}, optional Amount of effort put into optimising Fourier transform where 0 is low and 3 is high (default=`1`). threads : `int`, `None` Number of threads to use (default=`None` is all threads) auto_align_input : `bool`, optional If `True` then may re-align input (default=`True`) auto_contiguous : `bool`, optional If `True` then may re-order input (default=`True`) avoid_copy : `bool`, optional If `True` then may over-write initial input (default=`False`) norm : {None, 'ortho'}, optional Indicate whether fft is normalised (default=`None`) Returns ------- plan : function Returns the Fourier transform of `B`, `plan() == fftn(B)` B : `numpy.ndarray`, `A.shape` Array which should be modified inplace for fft to be computed. If possible, `B is A`. Example ------- A = numpy.zeros((8,16)) plan, B = plan_fft(A) B[:,:] = numpy.random.rand(8,16) numpy.fft.fftn(B) == plan() B = numpy.random.rand(8,16) numpy.fft.fftn(B) != plan() """ return lambda: fftn(A, n, axis, norm), A
def ftrg(fr, grid): """Fourier transform function fr from R space to G space. Args: fr (np.ndarray): R space function. shape == (grid.n1, grid.n2, grid.n3). grid (FFTGrid): FFT grid on which fr is defined. Returns: G space function. """ assert fr.shape == (grid.n1, grid.n2, grid.n3) return (1. / grid.N) * fftn(fr)
def forward(self, fr): """Fourier forward transform a function. Args: fr (np.ndarray): function in R space (3D array) Returns: function in G space (with same grid size) """ assert fr.ndim == 3 and fr.shape == (self.n1, self.n2, self.n3) fg = (1./self.N) * fftn(fr) return fg
def potential(self, q, steps): hx = steps[0] hy = steps[1] hz = steps[2] Nx = q.shape[0] Ny = q.shape[1] Nz = q.shape[2] out = np.zeros((2*Nx-1, 2*Ny-1, 2*Nz-1)) out[:Nx, :Ny, :Nz] = q K1 = self.sym_kernel(q.shape, steps) K2 = np.zeros((2*Nx-1, 2*Ny-1, 2*Nz-1)) K2[0:Nx, 0:Ny, 0:Nz] = K1 K2[0:Nx, 0:Ny, Nz:2*Nz-1] = K2[0:Nx, 0:Ny, Nz-1:0:-1] #z-mirror K2[0:Nx, Ny:2*Ny-1,:] = K2[0:Nx, Ny-1:0:-1, :] #y-mirror K2[Nx:2*Nx-1, :, :] = K2[Nx-1:0:-1, :, :] #x-mirror t0 = time() out = np.real(ifftn(fftn(out)*fftn(K2))) t1 = time() if self.debug: print( 'fft time:', t1-t0, ' sec') out[:Nx, :Ny, :Nz] = out[:Nx,:Ny,:Nz]/(4*pi*epsilon_0*hx*hy*hz) return out[:Nx, :Ny, :Nz]
def _initializeSupport(self, sigma=0.575): temp = np.log10(np.absolute(fftshift(fftn(self._modulus)))) mask = (temp > sigma * temp.max()).astype(float) labeled, features = label(mask) support_label = list( dict( sorted(collections.Counter(labeled.ravel()).items(), key=lambda item: -item[1])).keys())[1] self._support = np.zeros(self._arraySize) self._support[np.where(labeled == support_label)] = 1. self._support = fftshift(self._support) # self.BinaryErosion( 1 ) return
def __init__( self, modulus, support=None, # if None, use default support beta=0.9, binning=1, # for high-energy CDI. Set to 1 for regular phase retrieval. gpu=False, pcc=False, pcc_params=None, # user-defined coherence function parameters random_start=True ): self.BEStruct = np.ones( ( 3, 3, 3 ) ) # default structuring element for 3D binary erosion self.BinaryErosion = self.__CPUErosion__ self._modulus = fftshift( modulus ) self._arraySize = tuple( this*shp for this, shp in zip( [ binning, binning, 1 ], self._modulus.shape ) ) if support is None: self._initializeSupport() else: self._support = fftshift( support ) self._support_comp = 1. - self._support self._beta = beta if random_start: self._cImage = np.exp( 2.j * np.pi * np.random.random_sample( self._arraySize ) ) * self._support else: self._cImage = 1. * self._support self._cachedImage = np.zeros( self._cImage.shape ).astype( complex ) self._cImage_fft_mod = np.absolute( fftn( self._cImage ) ) self._error = [] self._UpdateError() self.generateAlgoDict() if gpu==True: gpack = self.generateGPUPackage( pcc=pcc, pcc_params=pcc_params ) self.gpusolver = accelerator.Solver( gpack ) #if pcc==True: # self._pccSolver = PCSolver( np.absolute( self._modulus )**2, gpack ) # self._kernel_f = self._pccSolver.getBlurKernel() # self._ModProject = self._ModProjectPC return
def fftw_inplace(E): if np.any(np.array(E.shape[2:]) > 1): strides_not_identical = np.any(E.strides != fftw_vec_array.strides) if strides_not_identical: log.debug('In-place Fourier Transform: strides not identical.') E = self.__word_align(E.copy()) if not pyfftw.is_n_byte_aligned(E, pyfftw.simd_alignment): log.debug('In-place Fourier Transform: Input/Output array not %d-byte word aligned, aligning now.' % pyfftw.simd_alignment) E = self.__word_align(E) assert(E.ndim-2 == self.data_shape.size and np.all(E.shape[2:] == self.data_shape)) if np.all(E.shape == fft_vec_object.input_shape) \ and pyfftw.is_n_byte_aligned(E, pyfftw.simd_alignment): fft_vec_object(E, E) # E should be in SIMD-word-aligned memory zone else: log.debug('Fourier Transform: Array shape not standard, falling back to default interface.') E = ft.fftn(E, axes=ft_axes) return E
def fft3d( arr3d, mode, neg_pos_3d=None, numpy_fft=pyfftw.interfaces.numpy_fft, only_real=False ): if neg_pos_3d is None: neg_pos_3d = make_neg_pos_3d(arr3d) if arr3d.shape[0] % 4 != 0: neg_pos_3d *= -1 if mode == "f": arr3d_f = numpy_fft.fftn(neg_pos_3d * arr3d) arr3d_f /= np.sqrt(np.prod(arr3d_f.shape)) elif mode == "i": arr3d_f = numpy_fft.ifftn(neg_pos_3d * arr3d) arr3d_f *= np.sqrt(np.prod(arr3d_f.shape)) if only_real: arr3d_f = arr3d_f.real arr3d_f *= neg_pos_3d return arr3d_f
def fft2d(arr2d, mode, numpy_fft=pyfftw.interfaces.numpy_fft, only_real=True): ''' we apply an alterating +1/-1 multiplicative before we go to/from Fourier space. Later we apply this again to the transform. ''' assert arr2d.ndim == 2 n1, n2 = arr2d.shape assert n1 == n2 arr2d = neg_pos(arr2d.copy()) arr2d_f = numpy_fft.fftn(arr2d.reshape(-1, n1, n1), axes=(-2, -1)) if mode == 'f': arr2d_f /= n1 elif mode == 'i': arr2d_f *= n1 if only_real: arr2d_f = arr2d_f.real arr2d_f = neg_pos(arr2d_f.reshape(n1, n1).copy()) return (arr2d_f)
def DFT(fx, axes=None): """ Discrete Fourier transform Parameters ---------- fx : `numpy.ndarray` Array defining a function evaluated on a mesh. axes : `int`or list of `int` , optional Specification of which axes to transform. default=`None` transforms all. Returns ------- fy : `numpy.ndarray` The Fourier transform of `fx` evaluated on a mesh """ NDIM = fx.ndim if axes is None: axes = [NDIM + i for i in range(-ndim, 0)] elif not hasattr(axes, "__iter__"): axes = (axes, ) axes = array(axes) axes.sort() FT = fftshift(fftn(fx, axes=axes), axes=axes) if NDIM != 3: # This is not typically a bottle-neck in <3D for i in axes: sz = [1] * NDIM sz[axes[i]] = -1 FT *= exp(-xmin[i] * Y[i].reshape(sz) * 1j) * (dx[i] if dx[i] != 0 else 1) else: F = [ exp(-xmin[i] * Y[i] * 1j) * (dx[i] if dx[i] != 0 else 1) for i in range(NDIM) ] apply_phase_3D(FT, *F) return FT
def interp(self, fr, n1, n2, n3): """Fourier interpolate a function to a smoother grid. Args: fr: function to be interpolated n1, n2, n3 (int): new grid size Returns: interpolated function (3D array of size n1 by n2 by n3) """ assert fr.ndim == 3 and fr.shape == (self.n1, self.n2, self.n3) assert n1 <= self.n1 and n2 <= self.n2 and n3 <= n3 if (n1, n2, n3) == (self.n1, self.n2, self.n3): return fr fg = fftn(fr) sfg = fftshift(fg) newsfg = sfg[(self.n1-n1-1)//2+1:(self.n1-n1-1)//2+1+n1, (self.n2-n2-1)//2+1:(self.n2-n2-1)//2+1+n2, (self.n3-n3-1)//2+1:(self.n3-n3-1)//2+1+n3, ] newfg = ifftshift(newsfg) newfr = (float(n1*n2*n3)/float(self.N)) * ifftn(newfg) return newfr
def ft(phi): """Go from physical space to spectral space.""" return fftn(phi, axes=(0,1))
def ir2fr(imp_resp, shape, center=None, real=True): """Return the frequency response from impulsionnal responses This function make the necessary correct zero-padding, zero convention, correct DFT etc. to compute the frequency response from impulsionnal responses (IR). The IR array is supposed to have the origin in the middle of the array. The Fourier transform is performed on the last `len(shape)` dimensions. Parameters ---------- imp_resp : ndarray The impulsionnal responses. shape : tuple of int A tuple of integer corresponding to the target shape of the frequency responses, without hermitian property. center : tuple of int, optional The origin index of the impulsionnal response. The middle by default. real : boolean (optionnal, default True) If True, imp_resp is supposed real, the hermissian property is used with rfftn DFT and the output has `shape[-1] / 2 + 1` elements on the last axis. Returns ------- y : ndarray The frequency responses of shape `shape` on the last `len(shape)` dimensions. Notes ----- - For convolution, the result have to be used with unitary discrete Fourier transform for the signal (udftn or equivalent). - DFT are always peformed on last axis for efficiency. - Results is always C-contiguous. See Also -------- udftn, uidftn, urdftn, uirdftn """ if len(shape) > imp_resp.ndim: raise ValueError("length of shape must inferior to imp_resp.ndim") if not center: center = [int(np.floor(length / 2)) for length in imp_resp.shape] if len(center) != len(shape): raise ValueError("center and shape must have the same length") # Place the provided IR at the beginning of the array irpadded = np.zeros(shape) irpadded[tuple([slice(0, s) for s in imp_resp.shape])] = imp_resp # Roll, or circshift to place the origin at 0 index, the # hypothesis of the DFT for axe, shift in enumerate(center): irpadded = np.roll(irpadded, -shift, imp_resp.ndim - len(shape) + axe) # Perform the DFT on the last axes if real: return np.ascontiguousarray( rfftn(irpadded, axes=list(range(imp_resp.ndim - len(shape), imp_resp.ndim)))) else: return np.ascontiguousarray( fftn(irpadded, axes=list(range(imp_resp.ndim - len(shape), imp_resp.ndim))))
def register_mean_offsets(frames2reg, max_iters=-1, block_frame_length=-1, include_shift=False, to_truncate=False, float_type=numpy.dtype(float).type): """ This algorithm registers the given image stack against its mean projection. This is done by computing translations needed to put each frame in alignment. Then the translation is performed and new translations are computed. This is repeated until no further improvement can be made. The code for translations can be found in find_mean_offsets. Notes: Adapted from code provided by Wenzhi Sun with speed improvements provided by Uri Dubin. Args: frames2reg(numpy.ndarray): Image stack to register (time is the first dimension uses C-order tyx or tzyx). max_iters(int): Number of iterations to allow before forcing termination if stable point is not found yet. Set to -1 if no limit. (Default -1) block_frame_length(int): Number of frames to work with at a time. By default all. (Default -1) include_shift(bool): Whether to return the shifts used, as well. (Default False) to_truncate(bool): Whether to truncate the frames to remove all masked portions. (Default False) float_type(type): Type of float to use for calculation. (Default numpy.float64). Returns: (numpy.ndarray): an array containing the translations to apply to each frame. Examples: >>> a = numpy.zeros((5, 3, 4)); a[:,0] = 1; a[2,0] = 0; a[2,2] = 1 >>> a array([[[ 1., 1., 1., 1.], [ 0., 0., 0., 0.], [ 0., 0., 0., 0.]], <BLANKLINE> [[ 1., 1., 1., 1.], [ 0., 0., 0., 0.], [ 0., 0., 0., 0.]], <BLANKLINE> [[ 0., 0., 0., 0.], [ 0., 0., 0., 0.], [ 1., 1., 1., 1.]], <BLANKLINE> [[ 1., 1., 1., 1.], [ 0., 0., 0., 0.], [ 0., 0., 0., 0.]], <BLANKLINE> [[ 1., 1., 1., 1.], [ 0., 0., 0., 0.], [ 0., 0., 0., 0.]]]) >>> register_mean_offsets(a, include_shift=True) (masked_array(data = [[[1.0 1.0 1.0 1.0] [0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0]] <BLANKLINE> [[1.0 1.0 1.0 1.0] [0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0]] <BLANKLINE> [[-- -- -- --] [0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0]] <BLANKLINE> [[1.0 1.0 1.0 1.0] [0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0]] <BLANKLINE> [[1.0 1.0 1.0 1.0] [0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0]]], mask = [[[False False False False] [False False False False] [False False False False]] <BLANKLINE> [[False False False False] [False False False False] [False False False False]] <BLANKLINE> [[ True True True True] [False False False False] [False False False False]] <BLANKLINE> [[False False False False] [False False False False] [False False False False]] <BLANKLINE> [[False False False False] [False False False False] [False False False False]]], fill_value = 0.0) , array([[0, 0], [0, 0], [1, 0], [0, 0], [0, 0]])) """ float_type = numpy.dtype(float_type).type # Must be of type float and must be at least 32-bit (smallest complex type # uses two 32-bit floats). assert issubclass(float_type, numpy.floating) assert numpy.dtype(float_type).itemsize >= 4 # Sadly, there is no easier way to map the two types; so, this is it. float_complex_mapping = { numpy.float32 : numpy.complex64, numpy.float64 : numpy.complex128, numpy.float128 : numpy.complex256 } complex_type = float_complex_mapping[float_type] if block_frame_length == -1: block_frame_length = len(frames2reg) tempdir_name = "" temporaries_filename = "" if isinstance(frames2reg, h5py.Dataset): tempdir_name, temporaries_filename = os.path.split( os.path.abspath(frames2reg.file.filename) ) temporaries_filename = os.path.splitext(temporaries_filename)[0] temporaries_filename += "_".join( [ frames2reg.name.replace("/", "_"), "temporaries.h5" ] ) temporaries_filename = os.path.join( tempdir_name, temporaries_filename ) elif (block_frame_length != len(frames2reg)): tempdir_name = tempfile.mkdtemp() temporaries_filename = os.path.join(tempdir_name, "temporaries.h5") frames2reg_fft = None space_shift = None this_space_shift = None if tempdir_name: temporaries_file = h5py.File(temporaries_filename, "w") frames2reg_fft = temporaries_file.create_dataset( "frames2reg_fft", shape=frames2reg.shape, dtype=complex_type ) space_shift = temporaries_file.create_dataset( "space_shift", shape=(len(frames2reg), len(frames2reg.shape)-1), dtype=int ) this_space_shift = temporaries_file.create_dataset( "this_space_shift", shape=space_shift.shape, dtype=space_shift.dtype ) else: frames2reg_fft = numpy.empty(frames2reg.shape, dtype=complex_type) space_shift = numpy.zeros( (len(frames2reg), len(frames2reg.shape)-1), dtype=int ) this_space_shift = numpy.empty_like(space_shift) for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): frames2reg_fft[i:j] = fft.fftn( frames2reg[i:j], axes=range(1, len(frames2reg.shape))) template_fft = numpy.empty(frames2reg.shape[1:], dtype=complex_type) negative_wave_vector = numpy.asarray(template_fft.shape, dtype=float_type) numpy.reciprocal(negative_wave_vector, out=negative_wave_vector) negative_wave_vector *= 2*numpy.pi numpy.negative(negative_wave_vector, out=negative_wave_vector) template_fft_indices = xnumpy.cartesian_product( [numpy.arange(_) for _ in template_fft.shape]) unit_space_shift_fft = template_fft_indices * negative_wave_vector unit_space_shift_fft = unit_space_shift_fft.T.copy() unit_space_shift_fft = unit_space_shift_fft.reshape( (template_fft.ndim,) + template_fft.shape) negative_wave_vector = None template_fft_indices = None # Repeat shift calculation until there is no further adjustment. num_iters = 0 squared_magnitude_delta_space_shift = 1.0 while (squared_magnitude_delta_space_shift != 0.0): squared_magnitude_delta_space_shift = 0.0 template_fft[:] = 0 for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): frames2reg_shifted_fft_ij = numpy.exp( 1j * numpy.tensordot( space_shift[i:j], unit_space_shift_fft, axes=[-1, 0] ) ) frames2reg_shifted_fft_ij *= frames2reg_fft[i:j] template_fft += numpy.sum(frames2reg_shifted_fft_ij, axis=0) template_fft /= len(frames2reg) for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): this_space_shift[i:j] = find_offsets( frames2reg_fft[i:j], template_fft ) # Remove global shifts. this_space_shift_mean = numpy.zeros( this_space_shift.shape[1:], dtype=this_space_shift.dtype) for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): this_space_shift_mean = this_space_shift[i:j].sum(axis=0) this_space_shift_mean = numpy.round( this_space_shift_mean.astype(float_type) / len(this_space_shift) ).astype(int) for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): this_space_shift[i:j] = xnumpy.find_relative_offsets( this_space_shift[i:j], this_space_shift_mean ) # Find the shortest roll possible (i.e. if it is going over halfway # switch direction so it will go less than half). # Note all indices by definition were positive semi-definite and upper # bounded by the shape. This change will make them bound by # the half shape, but with either sign. for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): this_space_shift[i:j] = xnumpy.find_shortest_wraparound( this_space_shift[i:j], frames2reg_fft.shape[1:] ) for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): delta_space_shift_ij = this_space_shift[i:j] - space_shift[i:j] squared_magnitude_delta_space_shift += numpy.dot( delta_space_shift_ij, delta_space_shift_ij.T ).sum() for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): space_shift[i:j] = this_space_shift[i:j] num_iters += 1 logger.info( "Completed iteration, %i, " % num_iters + "where the L_2 norm squared of the relative shift was, %f." % squared_magnitude_delta_space_shift ) if max_iters != -1: if num_iters >= max_iters: logger.info("Hit maximum number of iterations.") break reg_frames_shape = frames2reg.shape if to_truncate: space_shift_max = numpy.zeros( space_shift.shape[1:], dtype=space_shift.dtype ) space_shift_min = numpy.zeros( space_shift.shape[1:], dtype=space_shift.dtype ) for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): numpy.maximum( space_shift_max, space_shift[i:j].max(axis=0), out=space_shift_max ) numpy.minimum( space_shift_min, space_shift[i:j].min(axis=0), out=space_shift_min ) reg_frames_shape = numpy.asarray(reg_frames_shape) reg_frames_shape[1:] -= space_shift_max reg_frames_shape[1:] += space_shift_min reg_frames_shape = tuple(reg_frames_shape) space_shift_max = tuple(space_shift_max) space_shift_min = space_shift_min.astype(object) space_shift_min[space_shift_min == 0] = None space_shift_min = tuple(space_shift_min) reg_frames_slice = tuple( slice(_1, _2) for _1, _2 in itertools.izip( space_shift_max, space_shift_min ) ) # Adjust the registered frames using the translations found. # Mask rolled values. reg_frames = None if tempdir_name: if to_truncate: reg_frames = temporaries_file.create_dataset( "reg_frames", shape=reg_frames_shape, dtype=frames2reg.dtype, chunks=True ) else: reg_frames = temporaries_file.create_group("reg_frames") reg_frames = hdf5.serializers.HDF5MaskedDataset( reg_frames, shape=frames2reg.shape, dtype=frames2reg.dtype ) else: if to_truncate: reg_frames = numpy.empty(reg_frames_shape, dtype=frames2reg.dtype) else: reg_frames = numpy.ma.empty_like(frames2reg) reg_frames.mask = numpy.ma.getmaskarray(reg_frames) reg_frames.set_fill_value(reg_frames.dtype.type(0)) for i, j in iters.lagged_generators_zipped( itertools.chain( xrange(0, len(frames2reg), block_frame_length), [len(frames2reg)] ) ): for k in xrange(i, j): if to_truncate: reg_frames[k] = xnumpy.roll( frames2reg[k], space_shift[k])[reg_frames_slice] else: reg_frames[k] = xnumpy.roll( frames2reg[k], space_shift[k], to_mask=True) result = None results_filename = "" if tempdir_name: result = results_filename results_filename = os.path.join(tempdir_name, "results.h5") results_file = h5py.File(results_filename, "w") if to_truncate: temporaries_file.copy(reg_frames.name, results_file) else: temporaries_file.copy(reg_frames.group, results_file) if include_shift: temporaries_file.copy(space_shift, results_file) frames2reg_fft = None reg_frames = None space_shift = None this_space_shift = None temporaries_file.close() os.remove(temporaries_filename) temporaries_filename = "" result = results_filename else: result = reg_frames if include_shift: result = (reg_frames, space_shift) if tempdir_name: results_file.close() results_file = None return(result)
def ir2tf(imp_resp, shape, dim=None, is_real=True): """Compute the transfer function of an impulse response (IR). This function makes the necessary correct zero-padding, zero convention, correct fft2, etc... to compute the transfer function of IR. To use with unitary Fourier transform for the signal (ufftn or equivalent). Parameters ---------- imp_resp : ndarray The impulse responses. shape : tuple of int A tuple of integer corresponding to the target shape of the transfer function. dim : int, optional The last axis along which to compute the transform. All axes by default. is_real : boolean (optional, default True) If True, imp_resp is supposed real and the Hermitian property is used with rfftn Fourier transform. Returns ------- y : complex ndarray The transfer function of shape ``shape``. See Also -------- ufftn, uifftn, urfftn, uirfftn Examples -------- >>> np.all(np.array([[4, 0], [0, 0]]) == ir2tf(np.ones((2, 2)), (2, 2))) True >>> ir2tf(np.ones((2, 2)), (512, 512)).shape == (512, 257) True >>> ir2tf(np.ones((2, 2)), (512, 512), is_real=False).shape == (512, 512) True Notes ----- The input array can be composed of multiple-dimensional IR with an arbitrary number of IR. The individual IR must be accessed through the first axes. The last ``dim`` axes contain the space definition. """ if not dim: dim = imp_resp.ndim # Zero padding and fill irpadded = np.zeros(shape) irpadded[tuple([slice(0, s) for s in imp_resp.shape])] = imp_resp # Roll for zero convention of the fft to avoid the phase # problem. Work with odd and even size. for axis, axis_size in enumerate(imp_resp.shape): if axis >= imp_resp.ndim - dim: irpadded = np.roll(irpadded, shift=-int(np.floor(axis_size / 2)), axis=axis) if is_real: return rfftn(irpadded, axes=range(-dim, 0)) else: return fftn(irpadded, axes=range(-dim, 0))
def _register_translation(src_image, target_image, upsample_factor=1, space="real"): """ ***************************************** From skimage.feature.register_translation ***************************************** Efficient subpixel image translation registration by cross-correlation. This code gives the same precision as the FFT upsampled cross-correlation in a fraction of the computation time and with reduced memory requirements. It obtains an initial estimate of the cross-correlation peak by an FFT and then refines the shift estimation by upsampling the DFT only in a small neighborhood of that estimate by means of a matrix-multiply DFT. Parameters ---------- src_image : ndarray Reference image. target_image : ndarray Image to register. Must be same dimensionality as ``src_image``. upsample_factor : int, optional Upsampling factor. Images will be registered to within ``1 / upsample_factor`` of a pixel. For example ``upsample_factor == 20`` means the images will be registered within 1/20th of a pixel. Default: 1 (no upsampling). space : string, one of "real" or "fourier" Defines how the algorithm interprets input data. "real" means data will be FFT'd to compute the correlation, while "fourier" data will bypass FFT of input data. Case insensitive. Default: "real". Returns ------- shifts : ndarray Shift vector (in pixels) required to register ``target_image`` with ``src_image``. Axis ordering is consistent with numpy (e.g. Z, Y, X) error : float Translation invariant normalized RMS error between ``src_image`` and ``target_image``. phasediff : float Global phase difference between the two images (should be zero if images are non-negative). References ---------- .. [1] Manuel Guizar-Sicairos, Samuel T. Thurman, and James R. Fienup, "Efficient subpixel image registration algorithms," Optics Letters 33, 156-158 (2008). """ # images must be the same shape if src_image.shape != target_image.shape: raise ValueError("Error: images must be same size for " "register_translation") # only 2D data makes sense right now if src_image.ndim != 2 and upsample_factor > 1: raise NotImplementedError("Error: register_translation only supports " "subpixel registration for 2D images") # assume complex data is already in Fourier space if space.lower() == 'fourier': src_freq = src_image target_freq = target_image # real data needs to be fft'd. elif space.lower() == 'real': src_image = np.array(src_image, dtype=np.complex128, copy=False) target_image = np.array(target_image, dtype=np.complex128, copy=False) src_freq = fftn(src_image) target_freq = fftn(target_image) else: raise ValueError("Error: register_translation only knows the \"real\" " "and \"fourier\" values for the ``space`` argument.") # Whole-pixel shift - Compute cross-correlation by an IFFT shape = src_freq.shape image_product = src_freq * target_freq.conj() cross_correlation = ifftn(image_product) # Locate maximum maxima = np.unravel_index(np.argmax(np.abs(cross_correlation)), cross_correlation.shape) midpoints = np.array([np.fix(axis_size / 2) for axis_size in shape]) shifts = np.array(maxima, dtype=np.float64) shifts[shifts > midpoints] -= np.array(shape)[shifts > midpoints] if upsample_factor == 1: src_amp = np.sum(np.abs(src_freq) ** 2) / src_freq.size target_amp = np.sum(np.abs(target_freq) ** 2) / target_freq.size # CCmax = cross_correlation.max() # If upsampling > 1, then refine estimate with matrix multiply DFT else: # Initial shift estimate in upsampled grid shifts = np.round(shifts * upsample_factor) / upsample_factor upsampled_region_size = np.ceil(upsample_factor * 1.5) # Center of output array at dftshift + 1 dftshift = np.fix(upsampled_region_size / 2.0) upsample_factor = np.array(upsample_factor, dtype=np.float64) normalization = (src_freq.size * upsample_factor ** 2) # Matrix multiply DFT around the current shift estimate sample_region_offset = dftshift - shifts * upsample_factor cross_correlation = _upsampled_dft(image_product.conj(), upsampled_region_size, upsample_factor, sample_region_offset).conj() cross_correlation /= normalization # Locate maximum and map back to original pixel grid maxima = np.array(np.unravel_index( np.argmax(np.abs(cross_correlation)), cross_correlation.shape), dtype=np.float64) maxima -= dftshift shifts = shifts + maxima / upsample_factor # CCmax = cross_correlation.max() src_amp = _upsampled_dft(src_freq * src_freq.conj(), 1, upsample_factor)[0, 0] src_amp /= normalization target_amp = _upsampled_dft(target_freq * target_freq.conj(), 1, upsample_factor)[0, 0] target_amp /= normalization # If its only one row or column the shift along that dimension has no # effect. We set to zero. for dim in range(src_freq.ndim): if shape[dim] == 1: shifts[dim] = 0 return shifts
import numpy as np import pyfftw.builders from pyfftw.interfaces.numpy_fft import fftn r = np.random.randn(32, 32, 32) # the following transform will fail if MKL FFT routines are being linked to # instead of the FFTW ones. (MKL doesn't support FFT of only 1 # dimension of a 3D array). # see: https://github.com/pyFFTW/pyFFTW/issues/40 fftn(r, axes=(0, ))