Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    def test_otf2psf(self):
        """
        Test otf2psf() by verifying that the ideal circular aperture otf obtained with circ_aperture_otf() produces
        the correct psf, obtained by airy_fn()
        :return:
        """
        na = 1.3
        wavelength = 0.465
        dx = 0.061
        nx = 101
        dy = dx
        ny = nx

        fxs = tools.get_fft_frqs(nx, dx)
        fys = tools.get_fft_frqs(ny, dy)
        dfx = fxs[1] - fxs[0]
        dfy = fys[1] - fys[0]

        otf = fit_psf.circ_aperture_otf(fxs[None, :], fys[:, None], na, wavelength)
        psf, (ys, xs) = fit_psf.otf2psf(otf, (dfy, dfx))
        psf = psf / psf.max()

        psf_true = fit_psf.airy_fn(xs[None, :], ys[:, None], [1, 0, 0, na, 0], wavelength)

        self.assertAlmostEqual(np.max(np.abs(psf - psf_true)), 0, 4)
Ejemplo n.º 3
0
    def test_fft_frqs(self):

        dt = 0.46436
        for n in [2, 3, 4, 3634, 581]:
            frqs_np = numpy.fft.fftfreq(n, dt)

            # positive and negative frequencies, with f=0 at edge
            frqs_e = tools.get_fft_frqs(n,
                                        dt,
                                        centered=False,
                                        mode="symmetric")
            self.assertAlmostEqual(np.abs(np.max(frqs_e - frqs_np)),
                                   0,
                                   places=14)

            # positive and negative frequencies, with f=0 at center
            frqs_c = tools.get_fft_frqs(n, dt, centered=True, mode="symmetric")
            self.assertAlmostEqual(np.abs(
                np.max(numpy.fft.fftshift(frqs_np) - frqs_c)),
                                   0,
                                   places=14)

            # positive frequencies with f=0 at edge
            frqs_e_pos = tools.get_fft_frqs(n,
                                            dt,
                                            centered=False,
                                            mode="positive")
            frqs_np_pos = np.array(frqs_np, copy=True)
            frqs_np_pos[
                frqs_np_pos < 0] = frqs_np_pos[frqs_np_pos < 0] + 1 / dt
            self.assertAlmostEqual(np.abs(np.max(frqs_e_pos - frqs_np_pos)),
                                   0,
                                   places=14)
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
    ppath = dmd_pattern_data_fpath[kk]
    xform = affine_xforms[kk]

    with open(ppath, 'rb') as f:
        pattern_data = pickle.load(f)

    # DMD intensity frequency and phase (twice electric field frq/phase)
    frqs_dmd[kk] = 2 * pattern_data['frqs']
    phases_dmd[kk] = 2 * pattern_data['phases']
    dmd_nx = pattern_data['nx']
    dmd_ny = pattern_data['ny']

for kk in range(ncolors):
    # otf matrix
    fmax = 1 / (0.5 * emission_wavelengths[kk] / na)
    fx = tools.get_fft_frqs(nx_roi, pixel_size)
    fy = tools.get_fft_frqs(ny_roi, pixel_size)
    ff = np.sqrt(fx[None, :]**2 + fy[:, None]**2)
    otf = otf_fn(ff, fmax)
    otf[ff >= fmax] = 0

    # guess frequencies/phases
    frqs_guess = np.zeros((nangles, 2))
    phases_guess = np.zeros((nangles, nphases))
    for ii in range(nangles):
        for jj in range(nphases):
            # estimate frequencies based on affine_xform
            frqs_guess[ii, 0], frqs_guess[ii, 1], phases_guess[ii, jj] = \
                affine.xform_sinusoid_params_roi(frqs_dmd[kk, ii, 0], frqs_dmd[kk, ii, 1],
                                                 phases_dmd[kk, ii, jj], [dmd_ny, dmd_nx], roi, xform)
Ejemplo n.º 7
0
def simulated_img(ground_truth,
                  max_photons,
                  cam_gains,
                  cam_offsets,
                  cam_readout_noise_sds,
                  pix_size=None,
                  otf=None,
                  na=1.3,
                  wavelength=0.5,
                  photon_shot_noise=True,
                  bin_size=1,
                  use_otf=False):
    """
    Convert ground truth image (with values between 0-1) to simulated camera image, including the effects of
    photon shot noise and camera readout noise.

    :param use_otf:
    :param ground_truth: Relative intensity values of image
    :param max_photons: Mean photons emitted by ber of photons will be different than expected. Furthermore, due to
    the "blurring" of the point spread function and possible binning of the image, no point in the image
     may realize "max_photons"
    :param cam_gains: gains at each camera pixel
    :param cam_offsets: offsets of each camera pixel
    :param cam_readout_noise_sds: standard deviation characterizing readout noise at each camera pixel
    :param pix_size: pixel size of ground truth image in ums. Note that the pixel size of the output image will be
    pix_size * bin_size
    :param otf: optical transfer function. If None, use na and wavelength to set values
    :param na: numerical aperture. Only used if otf=None
    :param wavelength: wavelength in microns. Only used if otf=None
    :param photon_shot_noise: turn on/off photon shot-noise
    :param bin_size: bin pixels before applying Poisson/camera noise. This is to allow defining a pattern on a
    finer pixel grid.

    :return img:
    :return snr:
    :return max_photons_real:
    """
    if np.any(ground_truth > 1) or np.any(ground_truth < 0):
        warnings.warn(
            'ground_truth image values should be in the range [0, 1] for max_photons to be correct'
        )

    img_size = ground_truth.shape

    if use_otf:
        # get OTF
        if otf is None:
            fx = tools.get_fft_frqs(img_size[1], pix_size)
            fy = tools.get_fft_frqs(img_size[0], pix_size)
            otf = psf.circ_aperture_otf(fx[None, :], fy[:, None], na,
                                        wavelength)

        # blur image with otf/psf
        # todo: maybe should add an "imaging forward model" function to fit_psf.py and call it here.
        gt_ft = fft.fftshift(fft.fft2(fft.ifftshift(ground_truth)))
        img_blurred = max_photons * fft.fftshift(
            fft.ifft2(fft.ifftshift(gt_ft * otf))).real
        img_blurred[img_blurred < 0] = 0
    else:
        img_blurred = max_photons * ground_truth

    # resample image by binning
    img_blurred = tools.bin(img_blurred, (bin_size, bin_size), mode='sum')

    max_photons_real = img_blurred.max()

    # add shot noise
    if photon_shot_noise:
        img_shot_noise = np.random.poisson(img_blurred)
    else:
        img_shot_noise = img_blurred

    # add camera noise and convert from photons to ADU
    readout_noise = np.random.standard_normal(
        img_shot_noise.shape) * cam_readout_noise_sds

    img = cam_gains * img_shot_noise + readout_noise + cam_offsets

    # signal to noise ratio
    sig = cam_gains * img_blurred
    # assuming photon number large enough ~gaussian
    noise = np.sqrt(cam_readout_noise_sds**2 + cam_gains**2 * img_blurred)
    snr = sig / noise

    return img, snr, max_photons_real
Ejemplo n.º 8
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
Ejemplo n.º 9
0
def get_all_fourier_exp(imgs,
                        frq_vects_theory,
                        roi,
                        pixel_size_um,
                        fmax_img,
                        to_use=None,
                        use_guess_frqs=True,
                        max_frq_shift_pix=1.5,
                        force_start_from_guess=True,
                        peak_pix=2,
                        bg=100):
    """
    Calculate Fourier components from a set of images.

    :param imgs: nimgs x ny x nx
    :param vects: nimgs x nvecs1 x nvecs2 x 2
    :param float pixel_size_um:
    :param bool use_guess_frqs: if True, use guess frequencies computed from frq_vects_theory, if False use fitting
    procedure to find peak
    :param int peak_pix: number of pixels to use when calculating peak. Typically 2.
    :param float bg:

    :return intensity:
    :return intensity_unc:
    :return frq_vects_expt:
    """
    if to_use is None:
        to_use = np.ones(frq_vects_theory[:, :, :, 0].shape, dtype=np.int)

    nimgs = frq_vects_theory.shape[0]
    n1_vecs = frq_vects_theory.shape[1]
    n2_vecs = frq_vects_theory.shape[2]

    intensity = np.zeros(frq_vects_theory.shape[:-1],
                         dtype=np.complex) * np.nan
    intensity_unc = np.zeros(intensity.shape) * np.nan

    # apodization, 2D window from broadcasting
    nx_roi = roi[3] - roi[2]
    ny_roi = roi[1] - roi[0]
    window = scipy.signal.windows.hann(nx_roi)[
        None, :] * scipy.signal.windows.hann(ny_roi)[:, None]

    # generate frequency data for image FT's
    fxs = tools.get_fft_frqs(nx_roi, pixel_size_um)
    dfx = fxs[1] - fxs[0]
    fys = tools.get_fft_frqs(ny_roi, pixel_size_um)
    dfy = fys[1] - fys[0]

    if imgs.shape[0] == nimgs:
        multiple_images = True
    elif imgs.shape[0] == 1:
        multiple_images = False
        icrop = imgs[0, roi[0]:roi[1], roi[2]:roi[3]]

        img = icrop - bg
        img[img < 0] = 1e-6

        img_ft = fft.fftshift(fft.fft2(fft.ifftshift(img * window)))
        noise_power = sim_reconstruction.get_noise_power(
            img_ft, fxs, fys, fmax_img)
    else:
        raise Exception()

    frq_vects_expt = np.zeros(frq_vects_theory.shape)
    tstart = time.process_time()
    for ii in range(nimgs):
        tnow = time.process_time()
        print("%d/%d, %d peaks, elapsed time = %0.2fs" %
              (ii + 1, nimgs, np.sum(to_use[ii]), tnow - tstart))

        if multiple_images:
            # subtract background and crop to ROI
            # img = img[0, roi[0]:roi[1], roi[2]:roi[3]] - bg
            # img[img < 0] = 1e-6
            icrop = imgs[ii, roi[0]:roi[1], roi[2]:roi[3]]
            img = icrop - bg
            img[img < 0] = 1e-6

            # fft
            img_ft = fft.fftshift(fft.fft2(fft.ifftshift(img * window)))
            # get noise
            noise_power = sim_reconstruction.get_noise_power(
                img_ft, fxs, fys, fmax_img)

        # minimimum separation between reciprocal lattice vectors
        vnorms = np.linalg.norm(frq_vects_theory[ii], axis=2)
        min_sep = np.min(vnorms[vnorms > 0])

        # get experimental weights of fourier components
        for aa in range(n1_vecs):
            for bb in range(n2_vecs):

                frq_vects_expt[ii, aa, bb] = frq_vects_theory[ii, aa, bb]

                # only do fitting if peak size exceeds tolerance, and only fit one of a peak and its compliment
                if not to_use[ii, aa, bb]:
                    continue

                max_frq_shift = np.min([
                    max_frq_shift_pix * dfx, 0.5 * vnorms[aa, bb],
                    0.5 * min_sep
                ])

                # get experimental frequency
                if (max_frq_shift /
                        dfx) < 1 or use_guess_frqs or np.linalg.norm(
                            frq_vects_expt[ii, aa, bb]) == 0:
                    # if can't get large enough ROI, then use our guess
                    pass
                else:
                    # fit real fourier component in image space
                    # only need wavelength and na to get fmax
                    frq_vects_expt[
                        ii, aa,
                        bb], mask = sim_reconstruction.fit_modulation_frq(
                            img_ft,
                            img_ft,
                            pixel_size_um,
                            fmax_img,
                            frq_guess=frq_vects_theory[ii, aa, bb],
                            max_frq_shift=max_frq_shift,
                            force_start_from_guess=force_start_from_guess)

                    sim_reconstruction.plot_correlation_fit(
                        img_ft,
                        img_ft,
                        frq_vects_expt[ii, aa, bb],
                        pixel_size_um,
                        fmax_img,
                        frqs_guess=frq_vects_theory[ii, aa, bb],
                        roi_size=3)

                try:
                    # get peak value and phase
                    intensity[ii, aa, bb] = tools.get_peak_value(
                        img_ft,
                        fxs,
                        fys,
                        frq_vects_expt[ii, aa, bb],
                        peak_pixel_size=peak_pix)

                    intensity_unc[ii, aa,
                                  bb] = np.sqrt(noise_power) * peak_pix**2

                    # handle complimentary point with aa > bb
                    aa_neg = n1_vecs - 1 - aa
                    bb_neg = n2_vecs - 1 - bb
                    intensity[ii, aa_neg, bb_neg] = intensity[ii, aa,
                                                              bb].conj()
                    intensity_unc[ii, aa_neg, bb_neg] = intensity_unc[ii, aa,
                                                                      bb]

                except:
                    pass

    return intensity, intensity_unc, frq_vects_expt
Ejemplo n.º 10
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)
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
    def test_pattern_all_phases(self):
        """
        Test that can predict phases for ALL reciprocal lattice vectors in pattern using the
        get_efield_fourier_components() function.

        Do this for a few lattice vectors
        :return:
        """
        dmd_size = [1920, 1080]
        nx, ny = dmd_size
        nphases = 3
        phase_index = 0

        # list of vectors to test
        vec_as = [[-7, 7], [-5, 5]]
        vec_bs = [[3, 9], [3, 9]]
        # vec_as = [[-1, 117]]
        # vec_bs = [[-6, 117]]

        # other unit vectors with larger unit cells that don't perfectly tile DMD do not work as well.
        # close, but larger errors
        # [12, -12]
        # [0, 18]

        for vec_a, vec_b in zip(vec_as, vec_bs):

            pattern, _, _, angles, frqs, periods, phases, recp_vects_a, recp_vects_b, min_leakage_angle = \
                dmd.vects2pattern_data(dmd_size, [vec_a], [vec_b], nphases=nphases)

            pattern = pattern[0, 0]
            unit_cell, xc, yc = dmd.get_sim_unit_cell(vec_a, vec_b, nphases)

            # get ft
            window = scipy.signal.windows.hann(nx)[
                None, :] * scipy.signal.windows.hann(ny)[:, None]
            pattern_ft = fft.fftshift(fft.fft2(fft.ifftshift(pattern *
                                                             window)))
            fxs = tools.get_fft_frqs(nx)
            dfx = fxs[1] - fxs[0]
            fys = tools.get_fft_frqs(ny)
            dfy = fys[1] - fys[0]

            # get expected pattern components
            efield, ns, ms, vecs = dmd.get_efield_fourier_components(
                unit_cell,
                xc,
                yc,
                vec_a,
                vec_b,
                nphases,
                phase_index,
                dmd_size,
                nmax=40)
            # divide by size of DC component
            efield = efield / np.max(np.abs(efield))

            # get phase from fft
            efield_img = np.zeros(efield.shape, dtype=np.complex)
            for ii in range(len(ns)):
                for jj in range(len(ms)):
                    if np.abs(vecs[ii, jj][0]) > 0.5 or np.abs(
                            vecs[ii, jj][1]) > 0.5:
                        efield_img[ii, jj] = np.nan
                        continue

                    try:
                        efield_img[ii, jj] = tools.get_peak_value(
                            pattern_ft, fxs, fys, vecs[ii, jj], 2)
                    except:
                        efield_img[ii, jj] = np.nan

            # divide by size of DC component
            efield_img = efield_img / np.nanmax(np.abs(efield_img))

            # import matplotlib.pyplot as plt
            # from matplotlib.colors import PowerNorm
            # plt.figure()
            # fs = np.linalg.norm(vecs, axis=2)
            #
            # xlim = [-0.05, 1.2*np.max([fxs.max(), fys.max()])]
            # to_use = np.logical_and(np.logical_not(np.isnan(efield_img)), np.abs(efield) > 1e-8)
            #
            # plt.subplot(2, 2, 1)
            # plt.semilogy(fs[to_use], np.abs(efield_img[to_use]), 'r.')
            # plt.semilogy(fs[to_use], np.abs(efield[to_use]), 'bx')
            # plt.xlim(xlim)
            # plt.ylabel('amplitude')
            # plt.xlabel('Frq 1/mirrors')
            # plt.legend(['FFT', 'Prediction'])
            #
            # plt.subplot(2, 2, 2)
            # plt.plot(fs[to_use], np.abs(efield_img[to_use]), 'r.')
            # plt.plot(fs[to_use], np.abs(efield[to_use]), 'bx')
            # plt.xlim(xlim)
            # plt.ylabel('amplitude')
            # plt.xlabel('Frq 1/mirrors')
            #
            # plt.subplot(2, 2, 3)
            # plt.plot(fs[to_use], np.mod(np.angle(efield_img[to_use]), 2*np.pi), 'r.')
            # plt.plot(fs[to_use], np.mod(np.angle(efield[to_use]), 2*np.pi), 'bx')
            # plt.xlim(xlim)
            # plt.ylabel('phases')
            # plt.xlabel('Frq 1/mirrors')
            #
            # plt.subplot(2, 2, 4)
            # extent = [fxs[0] - 0.5 * dfx, fxs[-1] + 0.5 * dfx, fys[-1] + 0.5 * dfy, fys[0] - 0.5 * dfy]
            # plt.imshow(np.abs(pattern_ft), extent=extent, norm=PowerNorm(gamma=0.1))
            #
            # assert np.round(np.nanmax(np.abs(efield_img - efield)), 12) == 0
            self.assertAlmostEqual(np.nanmax(np.abs(efield_img - efield)), 0,
                                   12)