Пример #1
0
def general_gamma_binary(x, y, i1, i2):
    k = 2 * np.pi / wavelength
    beta = mas2rad(sep)
    th = np.deg2rad(theta)
    phi1 = k * x * beta * np.cos(th) / 2
    phi2 = k * y * beta * np.sin(th) / 2
    out = i1 * np.exp(-1j * (phi1 + phi2))
    out += i2 * np.exp(1j * (phi1 + phi2))
    return out / (i1 + i2)
Пример #2
0
def test_pynfft():

    pixels = 128
    wavel = 1.5e-6
    plate_scale = 1.2  # mas / pixel
    x = np.arange(pixels) - pixels // 2
    xx, yy = np.meshgrid(x, x)
    image = np.zeros_like(xx)
    rho = np.sqrt(xx ** 2 + yy ** 2)
    image = image + 1.0 * (rho < 5)

    mask = np.random.normal(0, 6, [100, 2])

    def uv_samples(mask):
        N = mask.shape[0]
        uv = np.zeros([N * (N - 1) // 2, 2])
        k = 0
        for i in range(N):
            for j in range(i + 1, N):
                uv[k, 0] = mask[i, 0] - mask[j, 0]
                uv[k, 1] = mask[i, 1] - mask[j, 1]
                k += 1
        return uv

    uv = uv_samples(mask)
    start = time.time()
    ndft = NDFTM(uv, wavel, pixels, plate_scale)
    vis1 = np.einsum("ij, j -> i", ndft, np.ravel(image))
    end = time.time() - start
    print(f"Took {end:.4f} second to compute NDFTM")

    phase = np.exp(-1j * np.pi / wavel * mas2rad(plate_scale) * (uv[:, 0] + uv[:, 1]))
    start = time.time()
    plan = NFFT([pixels, pixels], uv.shape[0], n=[pixels, pixels])
    plan.x = uv / wavel * mas2rad(plate_scale)
    plan.f_hat = image
    plan.precompute()
    plan.trafo()
    vis = plan.f.copy() * phase
    end = time.time() - start
    print(f"Took {end:.4f} seconds to compute NFFT")
    assert np.allclose(np.abs(vis), np.abs(vis1), rtol=1e-5)
    assert np.allclose(np.sin(np.angle(vis) - np.angle(vis1)), np.zeros_like(vis), atol=1e-5)
Пример #3
0
def NDFTM(coords, wavelength, pixels, plate_scale, inv=False, dprec=True):
    '''
    (modified from F. Martinache Xara project)

    Computes the Non uniform 2D Discrete Fourier Transform Matrix to transform flattened 2D image into complex
    Fourier coefficient.

    parameters:
    ----------
    - coords : vector of baseline (u,v) coordinates where to compute the FT (usually coords=Baselines.UVC)
    - wavelength: wavelength of light observed (in meters)
    - pixels: number of pixels of mage grid (on a side)
    - plate_scale: Plate scale of the camera in mas/pixel (that is 206265/(1000 * f[mm] * pixel_density[pixel/mm])
        or simply FOV [mas] / pixels where FOV stands for field of view and f is the focal length)

    Options:
    ------
    - inv    : Boolean (default=False) : True -> computes inverse DFT matrix
    - dprec  : double precision (default=True)
    For an image of size pixels^2, the computation requires what can be a
    fairly large (N_UV x pixels^2) auxilliary matrix. Consider using pyNFFT (or equivalent)
    for Non uniform Fast Fourier Transform.

    Example:
    --------
    >> B = exorim.operators.Baselines(mask_coordinates)
    >> A = NDFTM(B.UVC, wavelength, pixels, FOV / pixels)
    '''
    m2pix = mas2rad(plate_scale) * pixels / wavelength  # meter to pixels
    i2pi = 1j * 2 * np.pi
    mydtype = np.complex64
    if dprec is True:
        mydtype = np.complex128
    xx, yy = pixel_grid(pixels)
    uvc = coords * m2pix  # scale correctly uv and xy so that ux + vy is in RAD
    nuv = uvc.shape[0]
    if inv is True:
        WW = np.zeros((pixels**2, nuv), dtype=mydtype)
        for i in range(nuv):
            # Inverse is scaled correctly with the 4 pi^2 (2D transform)
            WW[:, i] = 1. / 4. / np.pi**2 * np.exp(
                i2pi * (uvc[i, 0] * xx.flatten() + uvc[i, 1] * yy.flatten()) /
                float(pixels))
    else:
        WW = np.zeros((nuv, pixels**2), dtype=mydtype)

        for i in range(nuv):
            WW[i] = np.exp(
                -i2pi * (uvc[i, 0] * xx.flatten() + uvc[i, 1] * yy.flatten()) /
                float(pixels))
    return WW
Пример #4
0
def gamma_binary(x, y, i1, i2):
    k = 2 * np.pi / wavelength
    beta = mas2rad(sep)
    out = (i1 - i2) / (i1 + i2) * np.exp(1j * k * x * beta / 2)
    out += 2 * i2 / (i1 + i2) * np.cos(np.pi * k * x * beta / 2)
    return out
 def scale2freq(x):
     return 1/mas2rad(x)
def main():
    # Defines physical variables
    np.random.seed(42)
    N = 21
    L = 6
    radius = 20 # pixels
    lam_circle = 4*radius
    pixels = 64
    wavel = 0.5e-6
    var = 5

    # mask
    x = (L + np.random.normal(0, var, N)) * np.cos(2 * np.pi * np.arange(N) / N)
    y = (L + np.random.normal(0, var, N)) * np.sin(2 * np.pi * np.arange(N) / N)
    circle_mask = np.array([x, y]).T
    mask = np.random.normal(0, L, (N, 2))
    # selected_mask = circle_mask
    selected_mask = mask

    B = Operators(selected_mask, wavel)
    rho = np.sqrt(B.UVC[:, 0]**2 + B.UVC[:, 1]**2)/wavel

    theta = rad2mas(1/rho)
    print(mode(theta)[0][0])
    print(np.std(theta))
    # this favors high frequencies over large ones.
    # plate_scale = (mode(theta)[0][0] + 2*np.std(theta))/pixels
    plate_scale = (np.median(theta) + 1*np.std(theta))/pixels
    # plate_scale = np.max(theta)/pixels
    # plate_scale = 5*np.std(theta)/pixels
    # plate_scale = np.min(theta)/10
    print(plate_scale)
    d_theta = mas2rad(plate_scale)

    sampled_scales = rad2mas(1/rho) #/plate_scale
    print(f"Largest structure: {sampled_scales.max():.0f}")
    print(f"Smallest structure: {sampled_scales.min():.0f}")
    print(f"xi = {(rad2mas(1/2/rho.min())/pixels)}")
    sampling_frequency = 1/plate_scale

    signal_frequency = 1/(mas2rad(plate_scale*lam_circle))
    nyquist_frequency = 0.5*signal_frequency # half of pixel/RAD, plate_scale is the sampling rate
    # Estimated sampling rate
    estimated_rate = np.diff(np.sort(rho), 1).mean()
    rho_th = np.linspace(rho.min(), rho.max(), 1000)

    image_coords = np.arange(pixels) - pixels / 2.
    xx, yy = np.meshgrid(image_coords, image_coords)
    image1 = np.zeros_like(xx)
    rho_squared = (xx) ** 2 + (yy) ** 2
    a = radius * mas2rad(plate_scale)
    image1 += np.sqrt(rho_squared) < radius

    A = NDFTM(B.UVC, wavel, pixels, plate_scale) * d_theta**2
    V = A.dot(image1.ravel())
    x = 2*np.pi*a*rho_th
    V_th = j1(x)/x * 2*np.pi * a**2
    V_th_f = interp1d(rho_th, np.abs(V_th))

    def freq2scale(x):
        return rad2mas(1/ x)#/plate_scale

    def scale2freq(x):
        return 1/mas2rad(x)

    fig = plt.figure(figsize=(10, 8))
    fig.suptitle(r"Comparison between analytic and discrete FT of circ($2\rho/a$)")
    frame1 = fig.add_axes((.1, .3, .8, .6))
    _frame1 = frame1.twiny()
    _frame1.set_xlabel(r"$\theta$ [pixels]")
    _frame1.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: f"{freq2scale(x)/plate_scale:.1f}"))
    frame1.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: f"{x/0.5/a**2/2/np.pi:.1f}"))
    plt.plot(rho, np.abs(V), "ko", label=r"$\mathcal{F} X$")
    plt.plot(rho_th, np.abs(V_th), "r-", label=r"$a J_1(2\pi a \rho) / \rho$")
    # frame1.axvline(signal_frequency, color="b")
    plt.axvline(signal_frequency, color="b")
    plt.annotate(f"Circle radius = a = {rad2mas(a):.2f} mas", xy=(0.6, 0.8), xycoords="axes fraction", color="k")
    plt.annotate(r"Sampling frequency = %.2f mas$^{-1}$" % (sampling_frequency), xy=(0.6, 0.65), xycoords="axes fraction", color="b")
    plt.annotate(r"Nyquist frequency = %.2f mas$^{-1}$" % (nyquist_frequency/rad2mas(1)), xy=(0.6, 0.6), xycoords="axes fraction", color="k")
    frame1.set_ylabel(r"$|\gamma| = \frac{|V|}{a^2\pi }$")
    frame1.set_xticklabels([])  # Remove x-tic labels for the first frame
    # plt.annotate("")
    plt.xlim(rho.min(), rho.max())
    plt.legend()

    frame2 = fig.add_axes((.1, .1, .8, .2))
    frame2.xaxis.set_major_formatter(FuncFormatter(lambda x,pos: f"{x/rad2mas(1):.2f}"))
    plt.plot(rho, np.abs(np.abs(V) - V_th_f(rho))/V_th_f(rho) * 100, 'ok')
    plt.yscale("log")
    plt.ylabel("Error %")
    plt.xlabel(r"$\rho$ (mas$^{-1}$)")
    # plt.xlabel(r"Baseline (m)")

    plt.xlim(rho.min(), rho.max())
    # plt.savefig(os.path.join(results_dir, "test_fourier_transform_circle.png"), bbox_inches="tight")

    # it is the baselines that are distributed with this pdf, so we change scale
    def rho_pdf(rho):
        x = rho*wavel
        return x/L * np.exp(-x**2/L**2/4) / 2

    plt.figure()
    # theta = sampled_scales #/plate_scale
    plt.hist(np.sort(sampled_scales)[:-3], bins=50, density=True)
    # print(f"mean theta = {freq2scale(2*gamma(3/2)/wavel)/plate_scale:.2f}")
    # print(f"var theta = {freq2scale((4 - 4*gamma(3/2)**2)/wavel)/plate_scale:.2f}")
    # print(f"skewness theta = {freq2scale(8*gamma(5/2)/wavel)/plate_scale:.2f}")
    # plt.plot(theta, rho_pdf(rho/2), "ko")
    plt.axvline(rad2mas(2*a), color="r")
    plt.axvline(pixels*plate_scale, color="g")
    plt.axvline(plate_scale, color="g")
    plt.xlabel(r"$\Delta \theta$ sampled [mas]")
    plt.annotate(f"Circle diameter = {rad2mas(2*a):.2f} mas", xy=(0.65, 0.9), xycoords="axes fraction", color="r")
    plt.annotate(f"Image size = {pixels} pixels", xy=(0.65, 0.5), xycoords="axes fraction", color="g")
    plt.annotate(f"Median = {np.median(theta):.2f} mas", xy=(0.65, 0.8), xycoords="axes fraction", color="k")
    plt.annotate(f"Mean = {np.mean(theta):.2f} mas", xy=(0.65, 0.7), xycoords="axes fraction", color="k")
    plt.annotate(f"std = {np.std(theta):.2f} mas", xy=(0.65, 0.6), xycoords="axes fraction", color="k")
    plt.title("Baseline sampling of image dimensions in pixels")
    # plt.savefig(os.path.join(results_dir, "image_pixels_sampling.png"))

    plt.show()
Пример #7
0
    return uv


def pulse(x, y, pdim):
    rm = 0.5 * pdim
    return j1(rm * np.sqrt(x**2 + y**2)) / np.sqrt(x**2 + y**2) / rm**2


uv = uv_samples(mask)
start = time.time()
ndft = NDFTM(uv, wavel, pixels, plate_scale)
vis1 = np.einsum("ij, j -> i", ndft, np.ravel(image))
end = time.time() - start
print(f"Took {end:.4f} second to compute NDFTM")

m2pix = mas2rad(plate_scale) * pixels / wavel
phase = np.exp(-1j * np.pi / wavel * mas2rad(plate_scale) *
               (uv[:, 0] + uv[:, 1]))
start = time.time()
plan = NFFT([pixels, pixels], uv.shape[0], n=[pixels, pixels])
plan.x = uv / wavel * mas2rad(plate_scale)
plan.f_hat = image
plan.precompute()
plan.trafo()
vis = plan.f.copy() * phase
end = time.time() - start
print(
    f"Took {end:.4f} seconds to compute NFFT"
)  # usually at least 5x faster, scales better with large number of pixels
# print(np.abs(vis))
# print(np.abs(vis1))