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 plot_pattern(img, va, vb, frq_vects, fmax_img, pixel_size_um, dmd_size, affine_xform, roi, nphases, phase_index, fmax_in=None, peak_int_exp=None, peak_int_exp_unc=None, peak_int_theory=None, otf=None, otf_unc=None, to_use=None, figsize=(20, 10)): """ plot image and affine xformed pattern it corresponds to :param img: image ny x nx :param va: [vx, vy] :param vb: [vx, vy] :param frq_vects: vecs1 x nvecs2 x 2. in 1/um :param float fmax_img: in 1/um :param float pixel_size_um: :param dmd_size: [] :param affine_xform: affine transformation between DMD space and camera space :param roi: [ystart, yend, xstart, xend] region of interest in image :param nphases: number of phaseshifts used to generate the DMD pattern. Needed in addition to va/vb to specify pattern :param phase_index: index of phaseshift. Needed in addition to va, vb, nphases to specify pattern :return fig_handle: """ if to_use is None: to_use = np.ones(peak_int_exp.shape, dtype=np.bool) fmags = np.linalg.norm(frq_vects, axis=-1) n1max = int(np.round(0.5 * (fmags.shape[0] - 1))) n2max = int(np.round(0.5 * (fmags.shape[1] - 1))) # generate DMD pattern pattern, _ = dmd_patterns.get_sim_pattern(dmd_size, va, vb, nphases, phase_index) # crop image img_roi = img[roi[0]:roi[1], roi[2]:roi[3]] ny, nx = img_roi.shape # transform pattern using affine transformation xform_roi = affine.xform_shift_center(affine_xform, cimg_new=(roi[2], roi[0])) img_coords = np.meshgrid(range(nx), range(ny)) pattern_xformed = affine.affine_xform_mat(pattern, xform_roi, img_coords, mode="interp") # pattern_xformed_ft = fft.fftshift(fft.fft2(fft.ifftshift(pattern_xformed))) # get fourier transform of image fxs = tools.get_fft_frqs(nx, pixel_size_um) dfx = fxs[1] - fxs[0] fys = tools.get_fft_frqs(ny, pixel_size_um) dfy = fys[1] - fys[0] extent = [ fxs[0] - 0.5 * dfx, fxs[-1] + 0.5 * dfx, fys[-1] + 0.5 * dfy, fys[0] - 0.5 * dfy ] window = scipy.signal.windows.hann(nx)[ None, :] * scipy.signal.windows.hann(ny)[:, None] img_ft = fft.fftshift(fft.fft2(fft.ifftshift(img_roi * window))) # plot results figh = plt.figure(figsize=figsize) grid = plt.GridSpec(2, 6) period = dmd_patterns.get_sim_period(va, vb) angle = dmd_patterns.get_sim_angle(va, vb) frq_main_um = frq_vects[n1max, n2max + 1] plt.suptitle( "DMD period=%0.3f mirrors, angle=%0.2fdeg\n" "Camera period=%0.1fnm = 1/%0.3f um, angle=%0.2fdeg\n" "va=(%d, %d); vb=(%d, %d)" % (period, angle * 180 / np.pi, 1 / np.linalg.norm(frq_main_um) * 1e3, np.linalg.norm(frq_main_um), np.mod(np.angle(frq_main_um[0] + 1j * frq_main_um[1]), 2 * np.pi) * 180 / np.pi, va[0], va[1], vb[0], vb[1])) plt.subplot(grid[0, 0:2]) plt.imshow(img_roi) plt.title('image') plt.subplot(grid[0, 2:4]) plt.imshow(pattern_xformed) plt.title('pattern after affine xform') ax = plt.subplot(grid[0, 4:6]) plt.title('image FFT') plt.imshow(np.abs(img_ft)**2, norm=PowerNorm(gamma=0.1), extent=extent) # to_plot = np.logical_not(np.isnan(intensity_exp_norm)) nmax = int(np.round((frq_vects.shape[1] - 1) * 0.5)) plt.scatter(frq_vects[to_use, 0].ravel(), frq_vects[to_use, 1].ravel(), facecolor='none', edgecolor='r') plt.scatter(-frq_vects[to_use, 0].ravel(), -frq_vects[to_use, 1].ravel(), facecolor='none', edgecolor='m') # plt.scatter(frq_vects[nmax, nmax + 1, 0], frq_vects[nmax, nmax + 1, 1], facecolor="none", edgecolor='k') # plt.scatter(frq_vects[nmax, nmax - 1, 0], frq_vects[nmax, nmax - 1, 1], facecolor="none", edgecolor='k') # plt.scatter(frq_vects[nmax, nmax + 2, 0], frq_vects[nmax, nmax + 2, 1], facecolor="none", edgecolor='k') # plt.scatter(frq_vects[nmax, nmax - 2, 0], frq_vects[nmax, nmax - 2, 1], facecolor="none", edgecolor='k') circ = matplotlib.patches.Circle((0, 0), radius=fmax_img, color='k', fill=0, ls='--') ax.add_artist(circ) if fmax_in is not None: circ2 = matplotlib.patches.Circle((0, 0), radius=(fmax_in / 2), color='r', fill=0, ls='--') ax.add_artist(circ2) plt.xlim([-fmax_img, fmax_img]) plt.ylim([fmax_img, -fmax_img]) ax = plt.subplot(grid[1, :2]) plt.title("peaks amp expt/theory") plt.xlabel("Frequency (1/um)") plt.ylabel("Intensity") plt.plot([fmax_img, fmax_img], [0, 1], 'k') phs = [] legend_entries = [] if peak_int_theory is not None: ph, = ax.plot( fmags[to_use], np.abs(peak_int_theory[to_use]).ravel() / np.nanmax(np.abs(peak_int_theory[to_use])), '.') phs.append(ph) legend_entries.append("theory") if peak_int_exp is not None: if peak_int_exp_unc is None: peak_int_exp_unc = np.zeros(peak_int_exp.shape) ph = ax.errorbar(fmags[to_use], np.abs(peak_int_exp[to_use]).ravel() / np.nanmax(np.abs(peak_int_exp)), yerr=peak_int_exp_unc[to_use].ravel() / np.nanmax(np.abs(peak_int_exp)), fmt='x') phs.append(ph) legend_entries.append("experiment") ax.set_ylim([1e-4, 1.2]) ax.set_xlim([-0.1 * fmax_img, 1.1 * fmax_img]) ax.set_yscale('log') plt.legend(phs, legend_entries) # plot phase ax = plt.subplot(grid[1, 2:4]) plt.title("peaks phase expt/theory") plt.xlabel("Frequency (1/um)") plt.ylabel("phase") plt.plot([fmax_img, fmax_img], [-np.pi, np.pi], 'k') phs = [] legend_entries = [] if peak_int_theory is not None: ph, = ax.plot(fmags[to_use], np.angle(peak_int_theory[to_use]).ravel(), '.') phs.append(ph) legend_entries.append("theory") if peak_int_exp is not None: ph, = ax.plot(fmags[to_use], np.angle(peak_int_exp[to_use]).ravel(), 'x') phs.append(ph) legend_entries.append("experiment") ax.set_xlim([-0.1 * fmax_img, 1.1 * fmax_img]) plt.legend(phs, legend_entries) # plot otf ax = plt.subplot(grid[1, 4:]) plt.title("otf") plt.xlabel("Frequency (1/um)") plt.ylabel("otf") ax.plot([0, fmax_img], [0, 0], 'k') ax.plot([fmax_img, fmax_img], [0, 1], 'k') if otf is not None: if otf_unc is None: otf_unc = np.zeros(otf.shape) ax.errorbar(fmags[to_use], np.abs(otf[to_use]).ravel(), yerr=otf_unc[to_use].ravel(), fmt='.') ax.set_ylim([-0.05, 1.2]) ax.set_xlim([-0.1 * fmax_img, 1.1 * fmax_img]) return figh
def test_patterns_main_phase(self): """ Test determination of DMD pattern phase by comparing with FFT phase for dominant frequency component of given pattern. This compares get_sim_phase() with directly producing a pattern via get_sim_pattern() and numerically determining the phase. Unlike test_phase_vs_phase_index(), test many different lattice vectors, but only a single phase index :return: """ # dmd_size = [1920, 1080] dmd_size = [192, 108] nphases = 3 phase_index = 0 nx, ny = dmd_size # va_comps = np.arange(-15, 15) # vb_comps = np.arange(-30, 30, 3) va_comps = np.array([-15, -7, -3, 0, 4, 8, 17]) vb_comps = np.array([-30, -15, -6, -3, 12, 18]) # va_comps = np.array([1, 3, 10]) # vb_comps = np.array([-3, 0, 3]) # generate all lattice vectors vas_x, vas_y = np.meshgrid(va_comps, va_comps) vas = np.concatenate((vas_x.ravel()[:, None], vas_y.ravel()[:, None]), axis=1) vas = vas[np.linalg.norm(vas, axis=1) != 0] vbs_x, vbs_y = np.meshgrid(vb_comps, vb_comps) vbs = np.concatenate((vbs_x.ravel()[:, None], vbs_y.ravel()[:, None]), axis=1) vbs = vbs[np.linalg.norm(vbs, axis=1) != 0] for ii in range(len(vas)): for jj in range(len(vbs)): print("pattern %d/%d" % (len(vbs) * ii + jj + 1, len(vas) * len(vbs))) vec_a = vas[ii] vec_b = vbs[jj] try: recp_va, recp_vb = dmd.get_reciprocal_vects(vec_a, vec_b) except: continue # normal phase function phase = dmd.get_sim_phase(vec_a, vec_b, nphases, phase_index, dmd_size) # estimate phase from FFT pattern, _ = dmd.get_sim_pattern(dmd_size, vec_a, vec_b, nphases, phase_index) window = scipy.signal.windows.hann(nx)[ None, :] * scipy.signal.windows.hann(ny)[:, None] pattern_ft = fft.fftshift( fft.fft2(fft.ifftshift(pattern * window))) fx = tools.get_fft_frqs(nx, 1) fy = tools.get_fft_frqs(ny, 1) try: phase_direct = np.angle( tools.get_peak_value(pattern_ft, fx, fy, recp_vb, peak_pixel_size=2)) except: # recp_vb too close to edge of pattern continue phase_diff = np.min([ np.abs(phase - phase_direct), np.abs(phase - phase_direct - 2 * np.pi) ]) # assert np.round(phase_diff, 1) == 0 self.assertAlmostEqual(phase_diff, 0, 1)
def test_affine_phase_xform(self): """ Test transforming pattern and phases through affine xform :return: """ # get pattern dmd_size = [1920, 1080] #[nx, ny] nphases = 3 phase_index = 0 nmax = 40 # roi = tools.get_centered_roi([1024, 1024], [600, 800]) roi = tools.get_centered_roi([1024, 1024], [500, 500]) nx = roi[3] - roi[2] ny = roi[1] - roi[0] # vec_a = np.array([8, 17]) # vec_b = np.array([3, -6]) vec_a = [1, -117] vec_b = [6, -117] pattern, _ = dmd.get_sim_pattern(dmd_size, vec_a, vec_b, nphases, phase_index) unit_cell, xc, yc = dmd.get_sim_unit_cell(vec_a, vec_b, nphases) # get affine matrix # affine_mat = affine.params2xform([2, 15 * np.pi/180, 15, 1.7, -13 * np.pi/180, 3]) affine_mat = np.array( [[-1.01788979e+00, -1.04522661e+00, 2.66353915e+03], [9.92641451e-01, -9.58516962e-01, 7.83771959e+02], [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]) # get affine matrix for our ROI affine_xform_roi = affine.xform_shift_center(affine_mat, cimg_new=(roi[2], roi[0])) # get transformed phase from affine xform # transform pattern phase efields, ns, ms, vecs = dmd.get_efield_fourier_components(unit_cell, xc, yc, vec_a, vec_b, nphases, phase_index, dmd_size, nmax=nmax, origin="fft") efields = efields / np.max(np.abs(efields)) vecs_xformed = np.zeros(vecs.shape) vecs_xformed[..., 0], vecs_xformed[ ..., 1], phases = affine.xform_sinusoid_params_roi(vecs[..., 0], vecs[..., 1], np.angle(efields), pattern.shape, roi, affine_mat, input_origin="fft", output_origin="fft") efields_xformed = np.abs(efields) * np.exp(1j * phases) # get pattern phase directly from fft # transform pattern to image space img_coords = np.meshgrid(range(nx), range(ny)) # interpolation preserves phases but can distort Fourier components pattern_xform = affine.affine_xform_mat(pattern, affine_xform_roi, img_coords, mode="interp") # taking nearest pixel does a better job with amplitudes, but can introduce fourier components that did not exist before # pattern_xform_nearest = affine.affine_xform_mat(pattern, affine_xform_roi, img_coords, mode="nearest") window = scipy.signal.windows.hann(nx)[ None, :] * scipy.signal.windows.hann(ny)[:, None] pattern_ft = fft.fftshift( fft.fft2(fft.ifftshift(pattern_xform * window))) fx = tools.get_fft_frqs(nx, 1) fy = tools.get_fft_frqs(ny, 1) efields_direct = np.zeros(efields_xformed.shape, dtype=np.complex) for ii in range(efields.shape[0]): for jj in range(efields.shape[1]): if np.abs(vecs_xformed[ii, jj][0]) > 0.5 or np.abs( vecs_xformed[ii, jj][1]) > 0.5: efields_direct[ii, jj] = np.nan else: try: efields_direct[ii, jj] = tools.get_peak_value( pattern_ft, fx, fy, vecs_xformed[ii, jj], peak_pixel_size=2) except: efields_direct[ii, jj] = np.nan efields_direct = efields_direct / np.nanmax(np.abs(efields_direct)) to_compare = np.abs(efields_xformed) > 0.05 # check phases self.assertTrue( np.max( np.abs( np.angle(efields_xformed[to_compare]) - np.angle(efields_direct[to_compare]))) < 0.003) # check amplitudes self.assertTrue( np.nanmax( np.abs(efields_xformed[to_compare] - efields_direct[to_compare])) < 0.07) # check relative amplitudes self.assertTrue( np.nanmax( np.abs(efields_xformed[to_compare] - efields_direct[to_compare]) / np.abs(efields_xformed[to_compare])) < 0.25)