def test_fft_pos(self): dt = 0.46436 for n in [2, 3, 4, 5, 9, 10, 481, 5468]: pos = np.arange(n) * dt pos_e = tools.get_fft_pos(n, dt, centered=False, mode="positive") self.assertAlmostEqual(np.max(np.abs(pos - pos_e)), 0, places=12) pos_c = tools.get_fft_pos(n, dt, centered=True, mode="positive") self.assertAlmostEqual(np.max(np.abs(fft.fftshift(pos) - pos_c)), 0, places=12) pos = fft.fftshift(pos) ind, = np.where(pos == 0) pos[:ind[0]] = pos[:ind[0]] - n * dt pos_c_symm = tools.get_fft_pos(n, dt, centered=True, mode="symmetric") self.assertAlmostEqual(np.max(np.abs(pos - pos_c_symm)), 0, places=12)
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 phase_fft2edge(frq, phase, img_shape, dx=1): """ :param list[float] or np.array frq: :param float phase: :param tuple or list img_shape: :param float dx: :return phase_edge: """ xft = tools.get_fft_pos(img_shape[1], dt=dx, centered=True, mode="symmetric") yft = tools.get_fft_pos(img_shape[0], dt=dx, centered=True, mode="symmetric") phase_edge = xform_phase_translation(frq[0], frq[1], phase, [xft[0], yft[0]]) return phase_edge
def test_fit_phase_realspace(self): """ Test fit_phase_realspace() :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 at edge x_edge = dx * np.arange(nx) y_edge = x_edge xx_edge, yy_edge = np.meshgrid(x_edge, y_edge) m_edge = 1 + 0.2 * np.cos(2 * np.pi * (frqs[0] * xx_edge + frqs[1] * yy_edge) + phi) phase_guess_edge = sim.get_phase_realspace(m_edge, frqs, dx, phase_guess=0, origin="edge") self.assertAlmostEqual(phi, float(phase_guess_edge), places=5) # 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) phase_guess_center = sim.get_phase_realspace(m_center, frqs, dx, phase_guess=0, origin="center") self.assertAlmostEqual(phi, float(phase_guess_center), places=5)
def xform_sinusoid_params_roi(fx, fy, phase, object_size, img_roi, affine_mat, input_origin="fft", output_origin="fft"): """ Transform sinusoid parameter from object space to a region of interest in image space. # todo: would it be more appropriate to put this function in sim_reconstruction.py? This is an unfortunately complicated function because we have five coordinate systems to worry about o: object space coordinates with origin at the corner of the DMD pattern o': object space coordinates assumed by fft functions i: image space coordinates, with origin at corner of the camera r: roi coordinates with origin at the edge of the roi r': roi coordinates, with origin near the center of the roi (coordinates for fft) The frequencies don't care about the coordinate origin, but the phase does :param float fx: x-component of frequency in object space :param float fy: y-component of frequency in object space :param float phase: phase of pattern in object space coordinates system o or o'. :param list[int] object_size: [sy, sx], size of object space, required to define origin of o' :param list[int] img_roi: [ystart, yend, xstart, xend], region of interest in image space. Note: this region does not include the pixels at yend and xend! In coordinates with integer values the pixel centers, it is the area [ystart - 0.5*dy, yend-0.5*dy] x [xstart -0.5*dx, xend - 0.5*dx] :param np.array affine_mat: affine transformation matrix, which takes points from o -> i :param str input_origin: "fft" if phase is provided in coordinate system o', or "edge" if provided in coordinate sysem o :param str output_origin: "fft" if output phase should be in coordinate system r' or "edge" if in coordinate system r :return fx_xform: x-component of frequency in coordinate system r' :return fy_xform: y-component of frequency in coordinates system r' :return phi_xform: phase in coordinates system r or r' (depending on the value of output_origin) """ if input_origin == "fft": phase_o = phase_fft2edge([fx, fy], phase, object_size, dx=1) # xft = tools.get_fft_pos(object_size[1]) # yft = tools.get_fft_pos(object_size[0]) # phase_o = xform_phase_translation(fx, fy, phase, [xft[0], yft[0]]) elif input_origin == "edge": phase_o = phase else: raise ValueError("input origin must be 'fft' or 'edge' but was '%s'" % input_origin) # affine transformation, where here we take coordinate origins at the corners fx_xform, fy_xform, phase_i = xform_sinusoid_params( fx, fy, phase_o, affine_mat) if output_origin == "edge": phase_r = xform_phase_translation(fx_xform, fy_xform, phase_i, [img_roi[2], img_roi[0]]) phase_xform = phase_r elif output_origin == "fft": # transform so that phase is relative to center of ROI ystart, yend, xstart, xend = img_roi x_rp = tools.get_fft_pos(xend - xstart, dt=1, centered=True, mode="symmetric") y_rp = tools.get_fft_pos(yend - ystart, dt=1, centered=True, mode="symmetric") # origin of rp-coordinate system, written in the i-coordinate system cx = xstart - x_rp[0] cy = ystart - y_rp[0] phase_rp = xform_phase_translation(fx_xform, fy_xform, phase_i, [cx, cy]) phase_xform = phase_rp else: raise ValueError("output_origin must be 'fft' or 'edge' but was '%s'" % output_origin) return fx_xform, fy_xform, phase_xform
def test_kmat(self): """ Test that the real-space and Fourier-space pattern generation models agree. i.e. that D_i(x) = amp * (1 + m * cos(2*pi*f + phi_i)) * S(r) matches the result given using the fourier space matrix [[D_1(k)], [D_2(k)], [D_3(k)]] = M * [[S(k)], [S(k-p)], [S(k+p)]] :return: """ # set options dx = 0.065 sim_options = {'pixel_size': 0.065, 'wavelength': 0.5, 'na': 1.3} # set values for SIM images frqs = np.array([[3.5785512, 2.59801082]]) phases = np.array([[0, 2 * np.pi / 3, 3 * np.pi / 3]]) mods = np.array([[0.85, 0.26, 0.19]]) amps = np.array([[1.11, 1.23, 0.87]]) nangles, nphases = phases.shape # ground truth image ny = 512 nx = ny gt = np.random.rand(ny, nx) # calculate sim patterns using real space method x = tools.get_fft_pos(nx, dx) y = tools.get_fft_pos(ny, dx) xx, yy = np.meshgrid(x, y) sim_rs = np.zeros((nangles, nphases, ny, nx)) sim_rs_ft = np.zeros((nangles, nphases, ny, nx), dtype=np.complex) for ii in range(nangles): for jj in range(nphases): pattern = amps[ii, jj] * (1 + mods[ii, jj] * np.cos( 2 * np.pi * (xx * frqs[ii, 0] + yy * frqs[ii, 1]) + phases[ii, jj])) sim_rs[ii, jj] = gt * pattern sim_rs_ft[ii, jj] = fft.fftshift( fft.fft2(fft.ifftshift(sim_rs[ii, jj]))) # calculate SIM patterns using Fourier space method # frq shifted gt images gt_ft_shifted = np.zeros((nangles, nphases, ny, nx), dtype=np.complex) for ii in range(nangles): gt_ft_shifted[ii, 0] = fft.fftshift(fft.fft2(fft.ifftshift(gt))) gt_ft_shifted[ii, 1] = tools.translate_ft(gt_ft_shifted[ii, 0], -frqs[ii], dx) gt_ft_shifted[ii, 2] = tools.translate_ft(gt_ft_shifted[ii, 0], frqs[ii], dx) sim_fs_ft = np.zeros(gt_ft_shifted.shape, dtype=np.complex) for ii in range(nangles): kmat = sim.get_band_mixing_matrix(phases[ii], mods[ii], amps[ii]) sim_fs_ft[ii] = sim.image_times_matrix(gt_ft_shifted[ii], kmat) sim_fs_rs = np.zeros(gt_ft_shifted.shape) for ii in range(nangles): for jj in range(nphases): sim_fs_rs[ii, jj] = fft.fftshift( fft.ifft2(fft.ifftshift(sim_fs_ft[ii, jj]))).real # fx = tools.get_fft_frqs(nx, dx) # dfx = fx[1] - fx[0] # fy = tools.get_fft_frqs(ny, dx) # extent = sim.get_extent(fy, fx) # # fig, fig_ax = plt.subplots(ncols=2, nrows=2, constrained_layout=True) # ii = 0 # jj = 1 # # fig_ax[0][0].imshow(np.abs(sim_rs_ft[ii, jj])**2, norm=PowerNorm(gamma=0.1), extent=extent) # plt.title('calculated in real space') # # fig_ax[0][1].imshow(np.abs(sim_fs_ft[ii, jj])**2, norm=PowerNorm(gamma=0.1), extent=extent) # plt.title('calculated in fourier space') # # fig_ax[1][0].imshow(sim_rs[ii, jj]) # plt.title('real space') # # fig_ax[1][1].imshow(sim_fs_rs[ii, jj]) # plt.title('real space (calculated in Fourier space)') np.testing.assert_allclose(sim_fs_ft, sim_rs_ft, atol=1e-10) np.testing.assert_allclose(sim_fs_rs, sim_rs, atol=1e-12)