예제 #1
0
    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)
예제 #2
0
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
예제 #3
0
    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)
예제 #4
0
    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)