def applyWeights(self,img_red,ws_img): af = self.af af2 = self.af2 nc, ny, nx = ws_img.shape[1:] nc, nyr, nxr = img_red.shape if (ny > nyr): af = round(ny/nyr) af2 = round(nx/nxr) print('Assuming the data is passed without zeros at not sampled lines ......') print('Acceleration factor is af = ' + str(af) + '.....') sig_red = img_red for idx in range(1,3): sig_red = fftshift(fft(fftshift(sig_red,axes=idx),axis=idx,norm='ortho'),axes=idx) sig_new = np.zeros((nc,ny,nx),dtype=np.complex64) sig_new[:,::af,::af2] = sig_red img_red = sig_new for idx in range(1,3): img_red = ifftshift(ifft(ifftshift(img_red,axes=idx),axis=idx,norm='ortho'),axes=idx) recon = np.zeros((nc,ny,nx),dtype=np.complex64) for k in range(0,nc): recon[k,:,:] = np.sum(ws_img[k,:,:,:] * img_red,axis=0) return recon
def ringthickness(self): """ Define indexes for ring_thick """ nmax = np.max(self.n) x = (np.arange(-np.fix(self.nc / 2.0), np.ceil(self.nc / 2.0)) * np.floor(nmax / 2.0) / np.floor(self.nc / 2.0)) y = (np.arange(-np.fix(self.nr / 2.0), np.ceil(self.nr / 2.0)) * np.floor(nmax / 2.0) / np.floor(self.nr / 2.0)) # bring the central pixel to the corners (important for odd array dimensions) x = ifftshift(x) y = ifftshift(y) if self.ndim == 2: # meshgriding X = np.meshgrid(x, y) elif self.ndim == 3: z = (np.arange(-np.fix(self.ns / 2.0), np.ceil(self.ns / 2.0)) * np.floor(nmax / 2.0) / np.floor(self.ns / 2.0)) # bring the central pixel to the corners (important for odd array dimensions) z = ifftshift(z) # meshgriding X = np.meshgrid(y, z, x) # sum of the squares independent of ndim sumsquares = np.zeros_like(X[0]) for ii in range(len(X)): sumsquares += X[ii]**2 index = np.round(np.sqrt(sumsquares)).astype(np.int) return index
def _applyPMD_einsum(field, H, h3): Sf = fft.fftshift(fft.fft(fft.ifftshift(field, axes=1),axis=1), axes=1) SSf = np.einsum('ijk,ik -> ik',H , Sf) SS = fft.fftshift(fft.ifft(fft.ifftshift(SSf, axes=1),axis=1), axes=1) SS = np.dot(h3, SS) try: return field.recreate_from_np_array(SS.astype(field.dtype)) except: return SS.astype(field.dtype)
def _applyPMD_dot(field, theta, t_dgd, omega): Sf = fft.fftshift(fft.fft(fft.ifftshift(field, axes=1),axis=1), axes=1) Sff = rotate_field(Sf, theta) h2 = np.array([np.exp(-1.j*omega*t_dgd/2), np.exp(1.j*omega*t_dgd/2)]) Sn = Sff*h2 Sf2 = rotate_field(Sn, -theta) SS = fft.fftshift(fft.ifft(fft.ifftshift(Sf2, axes=1), axis=1), axes=1) try: return field.recreate_from_np_array(SS.astype(field.dtype)) except: return SS.astype(field.dtype)
def test_expand_fourier_sp_even2d(self): """ test function with even input size """ arr = np.random.rand(100, 100) arr_ft = fft.fftshift(fft.fft2(fft.ifftshift(arr))) arr_ex_ft = tools.resample_bandlimited_ft(arr_ft, (2, 2)) arr_exp = fft.fftshift(fft.ifft2(fft.ifftshift(arr_ex_ft))).real max_err = np.max(np.abs(arr_exp[::2, ::2] - arr)) self.assertTrue(max_err < 1e-14)
def test_expand_fourier_sp_odd1d(self): """ Test function with odd input size """ arr = np.random.rand(151) arr_ft = fft.fftshift(fft.fft(fft.ifftshift(arr))) arr_ex_ft = tools.resample_bandlimited_ft(arr_ft, (2, )) arr_exp = fft.fftshift(fft.ifft(fft.ifftshift(arr_ex_ft))).real max_err = np.max(np.abs(arr_exp[1::2] - arr)) self.assertTrue(max_err < 1e-14)
def test_estimate_phase(self): """ Test estimate_phase() function, which guesses phase from value of image FT :return: """ # set parameters dx = 0.065 nx = 2048 f = 1 / 0.25 angle = 30 * np.pi / 180 frqs = [f * np.cos(angle), f * np.sin(angle)] phi = 0.2377747474 # create sample image with origin in center x_center = tools.get_fft_pos(nx, dx) y_center = x_center xx_center, yy_center = np.meshgrid(x_center, y_center) m_center = 1 + 0.2 * np.cos( 2 * np.pi * (frqs[0] * xx_center + frqs[1] * yy_center) + phi) m_center_ft = fft.fftshift(fft.fft2(fft.ifftshift(m_center))) phase_guess_center = sim.get_phase_ft(m_center_ft, frqs, dx) self.assertAlmostEqual(phi, float(phase_guess_center), places=5)
def test_translate_ft(self): """ Test translate_ft() function :return: """ img = np.random.rand(100, 100) img_ft = fft.fftshift(fft.fft2(fft.ifftshift(img))) dx = 0.065 fx = tools.get_fft_frqs(img.shape[1], dx) fy = tools.get_fft_frqs(img.shape[0], dx) df = fx[1] - fx[0] # x-shifting for n in range(1, 20): img_ft_shifted = tools.translate_ft(img_ft, [n * df, 0], dx) max_err = np.abs(img_ft_shifted[:, :-n] - img_ft[:, n:]).max() self.assertTrue(max_err < 1e-7) # y-shifting for n in range(1, 20): img_ft_shifted = tools.translate_ft(img_ft, [0, n * df], dx) max_err = np.abs(img_ft_shifted[:-n, :] - img_ft[n:, :]).max() self.assertTrue(max_err < 1e-7) # x+y shifting for n in range(1, 20): img_ft_shifted = tools.translate_ft(img_ft, [n * df, n * df], dx) max_err = np.abs(img_ft_shifted[:-n, :-n] - img_ft[n:, n:]).max() self.assertTrue(max_err < 1e-7)
def test_fit_modulation_frq(self): """ Test fit_modulation_frq() function :return: """ # set parameters options = {'pixel_size': 0.065, 'wavelength': 0.5, 'na': 1.3} fmax = 1 / (0.5 * options["wavelength"] / options["na"]) nx = 512 f = 1 / 0.25 angle = 30 * np.pi / 180 frqs = [f * np.cos(angle), f * np.sin(angle)] phi = 0.2377747474 # create sample image x = options['pixel_size'] * np.arange(nx) y = x xx, yy = np.meshgrid(x, y) m = 1 + 0.5 * np.cos(2 * np.pi * (frqs[0] * xx + frqs[1] * yy) + phi) mft = fft.fftshift(fft.fft2(fft.ifftshift(m))) frq_extracted, mask, = sim.fit_modulation_frq(mft, mft, options["pixel_size"], fmax, exclude_res=0.6) self.assertAlmostEqual(np.abs(frq_extracted[0]), np.abs(frqs[0]), places=5) self.assertAlmostEqual(np.abs(frq_extracted[1]), np.abs(frqs[1]), places=5)
def add_dispersion(sig, fs, D, L, wl0=1550e-9): """ Add dispersion to signal. Parameters ---------- sig : array_like input signal fs : flaot sampling frequency of the signal (in SI units) D : float Dispersion factor in s/m/m L : float Length of the dispersion in m wl0 : float,optional center wavelength of the signal Returns ------- sig_out : array_like dispersed signal """ C = 2.99792458e8 N = sig.shape[-1] omega = fft.fftfreq(N, 1/fs)*np.pi*2 beta2 = D * wl0**2 / (C*np.pi*2) H = np.exp(-0.5j * omega**2 * beta2 * L).astype(sig.dtype) sff = fft.fft(fft.ifftshift(sig, axes=-1), axis=-1) sig_out = fft.fftshift(fft.ifft(sff*H)) return sig_out
def test_test_patterns(self): """ Generate SIM test patterns with no noise or OTF blurring, and verify that these can be reconstructed. :return: """ gt = sim.get_test_pattern([600, 600]) otf = np.ones(gt.shape) frqs = np.array([[0.21216974, 3.97739458], [-3.17673383, 1.97618225], [3.24054346, 1.8939568]]) phases = np.zeros((3, 3)) phases[:, 0] = 0 phases[:, 1] = 2 * np.pi / 3 phases[:, 2] = 4 * np.pi / 3 mod_depths = np.array([[0.9, 0.8, 0.87], [1, 0.98, 0.88], [0.89, 0.912, 0.836]]) amps = np.array([[1, 0.7, 1.1], [1, 1.16, 0.79], [1, 0.83, 1.2]]) max_photons = 1 sim_imgs, _ = sim.get_simulated_sim_imgs(gt, frqs, phases, mod_depths, max_photons, 1, 0, 0, 0.065, amps, otf=otf, photon_shot_noise=False) nangles, nphases, ny, nx = sim_imgs.shape imgs_ft = np.zeros(sim_imgs.shape, dtype=np.complex) for ii in range(nangles): for jj in range(nphases): imgs_ft[ii, jj] = fft.fftshift( fft.fft2(fft.ifftshift(sim_imgs[ii, jj]))) separated_components = sim.do_sim_band_separation( imgs_ft, phases, mod_depths, amps) gt_per_angle = np.zeros((nangles, ny, nx)) for ii in range(nangles): gt_per_angle[ii] = fft.fftshift( fft.ifft2(fft.ifftshift(separated_components[ii, 0]))).real np.testing.assert_allclose(gt, gt_per_angle[ii], atol=1e-14)
def __init__(self, psf0, psf1, wvl, dx, dz, fno, phase_guess=None): """Create a new MiselTwoPSF problem. Parameters ---------- psf0 : `numpy.ndarray` array containing the incoherent PSF, |F_0|^2, of shape (m,n) psf1 : `numpy.ndarray` array containing the defocused PSF, |F_1|^2, of shape (m,n) wvl : `float` wavelength, microns dx : `float` inter-sample spacing for both psf0 and psf1, microns dz : `float` longitudinal separation between psf0 and psf1, microns fno : `float` f number, dimmensionless phase_guess : `numpy.ndarray` array containing the guess for the phase, np.angle(g), of shape (m,n) """ # this code is basically identical to _init_iterative_transform if phase_guess is None: phase_guess = np.random.rand(*psf0.shape) absF0 = np.sqrt(psf0) absF1 = np.sqrt(psf1) self.absF0 = fft.ifftshift(absF0) self.absF1 = fft.ifftshift(absF1) phase_guess = fft.ifftshift(phase_guess) self.G0 = self.absF0 * np.exp(1j * phase_guess) self.G1 = self.absF1 # per Misel, initialization of defocused plane is zero phase # only compute the transfer function between 0 -> 1 and 1 -> 0 one time # for efficiency self.tf0to1 = _angular_spectrum_transfer_function( psf0.shape, wvl, dx, dz) self.tf1to0 = _angular_spectrum_transfer_function( psf0.shape, wvl, dx, -dz) self.mse_denom0 = np.sum((self.absF0)**2) self.mse_denom1 = np.sum((self.absF1)**2) self.iter = 0 self.costF0 = [] self.costF1 = []
def test_expand_fourier_sp(self): """ Test expand_fourier_sp() function :return: """ arr = np.array([[1, 2], [3, 4]]) arr_ft = fft.fftshift(fft.fft2(fft.ifftshift(arr))) arr_ft_ex = tools.resample_bandlimited_ft(arr_ft, (2, 2)) arr_ex = fft.fftshift(fft.ifft2(fft.ifftshift(arr_ft_ex))) self.assertTrue( np.array_equal( arr_ex.real, np.array([[1, 1.5, 2, 1.5], [2, 2.5, 3, 2.5], [3, 3.5, 4, 3.5], [2, 2.5, 3, 2.5]])))
def _asm_sfft(cmode_to_propagate, wavelength, nx, ny, xstart, ystart, xend, yend, rx, ry, distance): dx = (xstart - xend) / nx dy = (ystart - yend) / ny fx = np.linspace(-1 / (2 * dx), 1 / (2 * dx), nx) fy = np.linspace(-1 / (2 * dy), 1 / (2 * dy), ny) mesh_fx, mesh_fy = np.meshgrid(fx, fy) impulse = np.exp(-1j * 2 * np.pi * distance * np.sqrt(1 / wavelength**2 - (mesh_fx**2 + mesh_fy**2))) cmode_to_propagate = fft.ifftshift(fft.fft2(cmode_to_propagate)) propagated_cmode = fft.ifft2(fft.ifftshift(impulse * cmode_to_propagate)) return propagated_cmode
def _init_iterative_transform(self, psf, pupil_amplitude, phase_guess=None): """Initialize an instance of an iterative transform type algorithm.""" # python teaching moment -- self as an argument name is only a convention, # and carries no special meaning. This function exists to refactor the # various iterative transform types without subclassing or inheritance if phase_guess is None: phase_guess = np.random.rand(*pupil_amplitude.shape) absF = np.sqrt(psf) absg = pupil_amplitude self.absF = fft.ifftshift(absF) self.absg = fft.ifftshift(absg) phase_guess = fft.ifftshift(phase_guess) self.g = self.absg * np.exp(1j * phase_guess) self.mse_denom = np.sum((self.absF)**2) self.iter = 0 self.costF = []
def calcGfact(self,ws_img,imgref): af = self.af af2 = self.af2 corr_mtx = self.corr_mtx nc, ny, nx = ws_img.shape[1:] ws_img = ws_img/(af*af2) print('Calculating G-factor......') sigref = imgref for idx in range(1,3): sigref = fftshift(fft(fftshift(sigref,axes=idx),axis=idx,norm='ortho'),axes=idx) nc, nyref, nxref = sigref.shape # filter kspace sigref = sigref * np.reshape(tukey(nyref,alpha=1),(1, nyref, 1)) sigref = sigref * np.reshape(tukey(nxref,alpha=1),(1, 1, nxref)) # sigreff = np.zeros((nc,ny,nx),dtype=np.complex64) yidx = slice(int(np.floor((ny-nyref)/2)),int(nyref + np.floor((ny-nyref)/2))) xidx = slice(int(np.floor((nx-nxref)/2)),int(nxref + np.floor((nx-nxref)/2))) sigreff[:,yidx,xidx] = sigref imgref = sigreff for idx in range(1,3): imgref = ifftshift(ifft(ifftshift(imgref,axes=idx),axis=idx,norm='ortho'),axes=idx) g = np.zeros((ny,nx)) for y in range(0,ny): for x in range(0,nx): tmp = imgref[:,y,x] norm_tmp = norm2(tmp,2) W = ws_img[:,:,y,x] # Weights in image space n = tmp.conj().T / norm_tmp # This is the generalized g-factor formulation g[y,x] = np.sqrt(np.abs((n@W)@(corr_mtx@((n@W).conj().T)))) / np.sqrt(np.abs(n@(corr_mtx@(n.conj().T)))) return g
def fft_hankel(integrator, integrator_args, integrator_kwargs, log_limit, npoints, in_ndim=0, is_complex=True, fft=scipy.fft): """Inverse HankelTransform using Fast Fourier Transform""" if hasattr(fft, "next_fast_len"): npoints = fft.next_fast_len(npoints, not is_complex) sp = np.linspace(-log_limit, log_limit, npoints) dt = sp[1] - sp[0] wq = 2 * pi * (fft.fftfreq if is_complex else fft.rfftfreq)(len(sp), dt) dw = wq[1] - wq[0] sp = _expand_first_ndim(sp, in_ndim) wq = _expand_first_ndim(wq, in_ndim) s = np.exp(-sp) r = np.exp(sp) # Weird tricks ahead to reduce memory usage res = np.nan_to_num(integrator(s, *integrator_args, **integrator_kwargs), copy=False) res *= s shifted = fft.ifftshift(res, axes=-1) del res fres = (fft.fft if is_complex else fft.rfft)(shifted, norm="ortho", axis=-1) del shifted fres *= dt fres *= fourierBesselJv(wq) shifted_hres = (fft.ifft if is_complex else fft.irfft)(fres, norm="ortho", axis=-1) del fres hres = fft.ifftshift(shifted_hres, axes=-1) del shifted_hres hres *= sp.size * dw / (2 * pi) hres /= r return sp, hres
def _get_nd_butterworth_filter(shape, factor, order, high_pass, real, dtype=np.float64, squared_butterworth=True): """Create a N-dimensional Butterworth mask for an FFT Parameters ---------- shape : tuple of int Shape of the n-dimensional FFT and mask. factor : float Fraction of mask dimensions where the cutoff should be. order : float Controls the slope in the cutoff region. high_pass : bool Whether the filter is high pass (low frequencies attenuated) or low pass (high frequencies are attenuated). real : bool Whether the FFT is of a real (True) or complex (False) image squared_butterworth : bool, optional When True, the square of the Butterworth filter is used. Returns ------- wfilt : ndarray The FFT mask. """ ranges = [] for i, d in enumerate(shape): # start and stop ensures center of mask aligns with center of FFT axis = np.arange(-(d - 1) // 2, (d - 1) // 2 + 1) / (d * factor) ranges.append(fft.ifftshift(axis**2)) # for real image FFT, halve the last axis if real: limit = d // 2 + 1 ranges[-1] = ranges[-1][:limit] # q2 = squared Euclidian distance grid q2 = functools.reduce(np.add, np.meshgrid(*ranges, indexing="ij", sparse=True)) q2 = q2.astype(dtype) q2 = np.power(q2, order) wfilt = 1 / (1 + q2) if high_pass: wfilt *= q2 if not squared_butterworth: np.sqrt(wfilt, out=wfilt) return wfilt
def _fresnel_old_dfft(cmode_to_propagate, wavelength, nx, ny, xstart, ystart, xend, yend, rx, ry, distance): # Warning! The fft2 of impulse function and ic should be done together with # numpy fft fft2. Or some speckle will apear. """ Double FFT Fresnel propagation of coherent mode. Args: cmode_to_propagate - the coherent mode to propagate. cmode_mask - the mask of coherent mode. wavelength - the wavelength of light field. nx - the dim of aixs x. ny - the dim of axis y. xstart - location of start along axis x. ystart - location of start along axis y. xend - location of end along axis x. yend - lcoation of end along axis y. rx - the range of x. ry - the range of y. distance - the distance of the propagation. Return: propagated coherent mode. """ # cmode_to_propagate = cmode_to_propagate # wave number k wave_num = 2 * np.pi / wavelength # the axis in frequency space x0 = np.linspace(xstart, xend, nx) y0 = np.linspace(ystart, yend, ny) mesh_x, mesh_y = np.meshgrid(x0, y0) # propagation function impulse = (np.exp(1j * wave_num * distance) * np.exp(-1j * wave_num * (mesh_x**2 + mesh_y**2) / (2 * distance)) / (1j * wavelength * distance)) # the multiply of coherent mode and propagation function propagated_cmode = fft.ifftshift( fft.ifft2(fft.fft2(cmode_to_propagate) * fft.fft2(impulse))) return propagated_cmode
def __init__(self, psf, pupil_amplitude, phase_guess=None, beta=1): """Create a new Hybrid Input-Output problem. Parameters ---------- psf : `numpy.ndarray` array containing the incoherent PSF, |G|^2, of shape (m,n) pupil_amplitude : `numpy.ndarray` array containing the amplitude of the pupil, |g|, of shape (m,n) phase_guess : `numpy.ndarray` array containing the guess for the phase, np.angle(g), of shape (m,n) beta : `bool`, optional gain parameter Notes ----- psf and pupil_amplitude should both be centered with the origin in the middle of the array. """ if phase_guess is None: phase_guess = np.random.rand(*pupil_amplitude.shape) absF = np.sqrt(psf) absg = pupil_amplitude self.absF = fft.ifftshift(absF) self.absg = fft.ifftshift(absg) phase_guess = fft.ifftshift(phase_guess) self.g = self.absg * np.exp(1j * phase_guess) self.supportmask = absg < 1e-6 self.iter = 0 self.beta = beta self.costF = [] self.costf = []
def hybrid(low, high, factor=.8, cutoff_low=25, cutoff_high=20): low_pass_input_image, low_std = low high_pass_input_image, high_std = high # get low fft low_fft = low_pass_filter(low_pass_input_image, low_std, cutoff_low) # save low image frequency domain # low_fft_image = 8 * np.log(np.abs(low_fft)) # cv.imwrite('q4_12_lowpassed.jpg', low_fft_image, ) # calculate low image in spatial domain low = np.abs(fft.ifft2(fft.ifftshift(low_fft))) ############## # get high fft high_fft = high_pass_filter(high_pass_input_image, high_std, cutoff_high) # save high image frequency domain # high_fft_image = 8 * np.log(np.abs(high_fft)) # cv.imwrite('q4_11_highpassed.jpg', high_fft_image) # calculate low image in spatial domain high = np.abs(fft.ifft2(fft.ifftshift(high_fft))) # calculate hybrid fft hybrid_fft = factor * low_fft + (1 - factor) * high_fft # save hybrid image frequency domain # hybrid_fft_image = 8 * np.log(np.abs(hybrid_fft)) # cv.imwrite('q4_13_hybrid_frequency.jpg', hybrid_fft_image) hybrid_result = np.abs(fft.ifft2(hybrid_fft)) return hybrid_result, low, high
def getWeights(ws_kernel,ny,nx): nc, nky, nkx = ws_kernel.shape[1:4] ws_k = np.zeros((nc,nc,ny,nx),dtype=np.complex64) ystart = int(np.ceil((ny-nky)/2)) ystop = int(np.ceil((ny+nky)/2)) yrange = slice(ystart,ystop) xstart = int(np.ceil((nx-nkx)/2)) xstop = int(np.ceil((nx+nkx)/2)) xrange = slice(xstart,xstop) ws_k[:,:,yrange,xrange] = ws_kernel #put reconstruction kernel in the center of matrix tmp0 = ifftshift(ws_k,axes=2) # shift in phase tmp1 = ifftshift(tmp0,axes=3) # shift in read tmp0 = ifft(tmp1,axis=2,norm='ortho') # ifft in phase tmp1 = ifft(tmp0,axis=3,norm='ortho') # ifft in read tmp0 = ifftshift(tmp1,axes=2) # shift in phase tmp1 = ifftshift(tmp0,axes=3) # shift in read ws_img = np.sqrt(ny*nx)*tmp1 return ws_img
def reconvolve_gaussian_kernel(img, old_maj, old_min, old_pa, new_maj, new_min, new_pa): """ convolve image with a gaussian kernel without FFTing it bmaj, bmin -- in pixels, bpa -- in degrees from top clockwise (like in Beam) inverse -- use True to deconvolve. NOTE: yet works for square image without NaNs """ size = len(img) imean = img.mean() img -= imean fimg = np.fft.fft2(img) krel = fft_psf(new_maj, new_min, new_pa, size) / fft_psf(old_maj, old_min, old_pa, size) fconv = fimg * ifftshift(krel) return ifft2(fconv).real + imean
def test_xform_sinusoid_params_roi(self): """ Test function xform_sinusoid_params_roi() by constructing sinusoid pattern and passing through an affine transformation. Compare the resulting frequency determined numerically with the resulting frequency determined from the initial frequency + affine parameters :return: """ # define object space parameters # roi_img = [0, 2048, 0, 2048] roi_img = [512, 788, 390, 871] fobj = np.array([0.08333333, 0.08333333]) phase_obj = 5.497787143782089 fn = lambda x, y: 1 + np.cos(2 * np.pi * (fobj[0] * x + fobj[1] * y) + phase_obj) # define affine transform xform = affine.params2xform([1.4296003114502853, 2.3693263411981396, 2671.39109, 1.4270495211450602, 2.3144621088632635, 790.402632]) # sinusoid parameter transformation fxi, fyi, phase_roi = affine.xform_sinusoid_params_roi(fobj[0], fobj[1], phase_obj, None, roi_img, xform, input_origin="edge", output_origin="edge") fimg = np.array([fxi, fyi]) # FFT phase _, _, phase_roi_ft = affine.xform_sinusoid_params_roi(fobj[0], fobj[1], phase_obj, None, roi_img, xform, input_origin="edge", output_origin="fft") # compared with phase from fitting image directly out_coords = np.meshgrid(range(roi_img[2], roi_img[3]), range(roi_img[0], roi_img[1])) img = affine.xform_fn(fn, xform, out_coords) phase_fit_roi = float(sim.get_phase_realspace(img, fimg, 1, phase_guess=phase_roi, origin="edge")) # phase FFT ny, nx = img.shape window = scipy.signal.windows.hann(nx)[None, :] * scipy.signal.windows.hann(ny)[:, None] img_ft = fft.fftshift(fft.fft2(fft.ifftshift(img * window))) fx = tools.get_fft_frqs(nx, 1) fy = tools.get_fft_frqs(ny, 1) peak = tools.get_peak_value(img_ft, fx, fy, fimg, 2) phase_fit_roi_ft = np.mod(np.angle(peak), 2*np.pi) # accuracy is limited by frequency fitting routine... self.assertAlmostEqual(phase_roi, phase_fit_roi, 1) # probably limited by peak height finding routine self.assertAlmostEqual(phase_roi_ft, phase_fit_roi_ft, 3)
def test_buffer(): """Test that CPU & GPU outputs match for `modulated=True` & `=False`, and that `modulated=True` matches `ifftshift(buffer(modulated=False))`. Also that single- & multi-thread CPU outputs agree. Test both single and batched input. """ N = 128 tsigs = TestSignals(N=N) for dtype in ('float64', 'float32'): for ndim in (1, 2): x = (tsigs.cosine()[0].astype(dtype) if ndim == 1 else np.random.randn(4, N)) xt = torch.as_tensor(x, device='cuda') if CAN_GPU else 0 for modulated in (False, True): for seg_len in (N // 2, N // 2 - 1): for n_overlap in (N // 2 - 1, N // 2 - 2, N // 2 - 3): if seg_len == n_overlap: continue out0 = buffer(x, seg_len, n_overlap, modulated, parallel=True) if modulated: out00 = buffer(x, seg_len, n_overlap, modulated=False, parallel=False) out00 = ifftshift(out00, axes=0 if ndim == 1 else 1) if CAN_GPU: out1 = buffer(xt, seg_len, n_overlap, modulated).cpu().numpy() assert_params = (dtype, modulated, seg_len, n_overlap) if modulated: adiff000 = np.abs(out0 - out00).mean() assert adiff000 == 0, (*assert_params, adiff000) if CAN_GPU: adiff01 = np.abs(out0 - out1).mean() assert adiff01 == 0, (*assert_params, adiff01)
def fourier_grid(n_pixels, n_pad_pixels): pi = np.pi n_elements = int(np.floor((n_pixels + 2 * n_pad_pixels)/2 + (n_pixels + 2 * n_pad_pixels)/2)) # Create real grid from linspace x = np.linspace(-(n_pixels + 2 * n_pad_pixels)/2, (n_pixels + 2 * n_pad_pixels)/2 - 1, num=n_elements) y = x XSI1, XSI2 = np.meshgrid( x, y ) # Normalize XSI1 = (2 * pi) / (n_pixels + 2 * n_pad_pixels) * XSI1 XSI2 = (2 * pi) / (n_pixels + 2 * n_pad_pixels) * XSI2 # Create Fourier grid XSISQ = ifftshift(XSI1 ** 2 + XSI2 ** 2) return XSISQ
def _fresnel_dfft(cmode_to_propagate, wavelength, nx, ny, xstart, ystart, xend, yend, rx, ry, distance): # Warning! The fft2 of impulse function and ic should be done together with # numpy fft fft2. Or some speckle will apear. """ Double FFT Fresnel propagation of coherent mode. Args: cmode_to_propagate - the coherent mode to propagate. cmode_mask - the mask of coherent mode. wavelength - the wavelength of light field. nx - the dim of aixs x. ny - the dim of axis y. xstart - location of start along axis x. ystart - location of start along axis y. xend - location of end along axis x. yend - location of end along axis y. rx - the range of x. ry - the range of y. distance - the distance of the propagation. Return: propagated coherent mode. """ # wave number k wave_num = 2 * np.pi / wavelength # the axis in frequency space qx = np.linspace(0.25 / xstart, 0.25 / xend, nx) * nx qy = np.linspace(0.25 / ystart, 0.25 / yend, ny) * ny mesh_qx, mesh_qy = np.meshgrid(qx, qy) # propagation function impulse_q = np.exp((-1j * wave_num * distance) * (1 - wavelength**2 * (mesh_qx**2 + mesh_qy**2)) / 2) # the multiply of coherent mode and propagation function propagated_cmode = fft.ifft2( fft.fft2(cmode_to_propagate) * fft.ifftshift(impulse_q)) return propagated_cmode
def wiener(data, impulse_response=None, filter_params={}, K=0.25, predefined_filter=None): """Minimum Mean Square Error (Wiener) inverse filter. Parameters ---------- data : (M,N) ndarray Input data. K : float or (M,N) ndarray Ratio between power spectrum of noise and undegraded image. impulse_response : callable `f(r, c, **filter_params)` Impulse response of the filter. See LPIFilter2D.__init__. filter_params : dict Additional keyword parameters to the impulse_response function. Other Parameters ---------------- predefined_filter : LPIFilter2D If you need to apply the same filter multiple times over different images, construct the LPIFilter2D and specify it here. """ check_nD(data, 2, 'data') if not isinstance(K, float): check_nD(K, 2, 'K') if predefined_filter is None: filt = LPIFilter2D(impulse_response, **filter_params) else: filt = predefined_filter F, G = filt._prepare(data) _min_limit(F, val=np.finfo(F.real.dtype).eps) H_mag_sqr = np.abs(F) ** 2 F = 1 / F * H_mag_sqr / (H_mag_sqr + K) return _centre(np.abs(fft.ifftshift(fft.ifftn(G * F))), data.shape)
def test_pattern_main_phase_vs_phase_index(self): """ Test get_sim_phase() on the main frequency component for one SIM pattern. Ensure works for all phase indices. Tests only a single pattern vector :return: """ nx = 1920 ny = 1080 nphases = 3 fx = tools.get_fft_frqs(nx) fy = tools.get_fft_frqs(ny) window = scipy.signal.windows.hann(nx)[ None, :] * scipy.signal.windows.hann(ny)[:, None] va = [-3, 11] vb = [3, 12] # va = [-1, 117] # vb = [-6, 117] rva, rvb = dmd.get_reciprocal_vects(va, vb) for jj in range(nphases): phase = dmd.get_sim_phase(va, vb, nphases, jj, [nx, ny], origin="fft") pattern, _ = dmd.get_sim_pattern([nx, ny], va, vb, nphases, jj) pattern_ft = fft.fftshift(fft.fft2(fft.ifftshift(pattern * window))) pattern_phase_est = np.mod( np.angle(tools.get_peak_value(pattern_ft, fx, fy, rvb, 2)), 2 * np.pi) # assert np.round(np.abs(pattern_phase_est - float(phase)), 3) == 0 self.assertAlmostEqual(pattern_phase_est, float(phase), 3)
def inverse(data, impulse_response=None, filter_params={}, max_gain=2, predefined_filter=None): """Apply the filter in reverse to the given data. Parameters ---------- data : (M,N) ndarray Input data. impulse_response : callable `f(r, c, **filter_params)` Impulse response of the filter. See LPIFilter2D.__init__. filter_params : dict Additional keyword parameters to the impulse_response function. max_gain : float Limit the filter gain. Often, the filter contains zeros, which would cause the inverse filter to have infinite gain. High gain causes amplification of artefacts, so a conservative limit is recommended. Other Parameters ---------------- predefined_filter : LPIFilter2D If you need to apply the same filter multiple times over different images, construct the LPIFilter2D and specify it here. """ check_nD(data, 2, 'data') if predefined_filter is None: filt = LPIFilter2D(impulse_response, **filter_params) else: filt = predefined_filter F, G = filt._prepare(data) _min_limit(F, val=np.finfo(F.real.dtype).eps) F = 1 / F mask = np.abs(F) > max_gain F[mask] = np.sign(F[mask]) * max_gain return _centre(np.abs(fft.ifftshift(fft.ifftn(G * F))), data.shape)