Пример #1
0
def circular_sample(no=[10, 10],
                    radius=10.,
                    center=[0., 0., 0.],
                    angles=[0., 0., 0.]):
    """
    Definition to generate samples inside a circle over a surface.

    Parameters
    ----------
    no          : list
                  Number of samples.
    radius      : float
                  Radius of the circle.
    center      : list
                  Center location of the surface.
    angles      : list
                  Tilt of the surface.

    Returns
    ----------
    samples     : ndarray
                  Samples generated.
    """
    samples = np.zeros((no[0] + 1, no[1] + 1, 3))
    r_angles, r = np.mgrid[0:no[0] + 1, 0:no[1] + 1]
    r = r / np.amax(r) * radius
    r_angles = r_angles / np.amax(r_angles) * np.pi * 2
    samples[:, :, 0] = r * np.cos(r_angles)
    samples[:, :, 1] = r * np.sin(r_angles)
    samples = samples[1:no[0] + 1, 1:no[1] + 1, :]
    samples = samples.reshape(
        (samples.shape[0] * samples.shape[1], samples.shape[2]))
    samples = rotate_points(samples, angles=angles, offset=center)
    return samples
Пример #2
0
def adaptive_sampling_angular_spectrum(field,k,distance,dx,wavelength):
    """
    A definition to calculate adaptive sampling angular spectrum based beam propagation. For more Zhang, Wenhui, Hao Zhang, and Guofan Jin. "Adaptive-sampling angular spectrum method with full utilization of space-bandwidth product." Optics Letters 45.16 (2020): 4416-4419.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    raise Exception("Adaptive sampling angular spectrum method is not yet stable. See issue https://github.com/kunguz/odak/issues/13")
    iflag = -1
    eps   = 10**(-12)
    nu,nv = field.shape
    l     = nu*dx
    x     = np.linspace(-l/2,l/2,nu)
    y     = np.linspace(-l/2,l/2,nv)
    X,Y   = np.meshgrid(x,y)
    fx    = np.linspace(-1./2./dx,1./2./dx,nu)
    fy    = np.linspace(-1./2./dx,1./2./dx,nv)
    FX,FY = np.meshgrid(fx,fy)
    forig = 1./2./dx
    fc2   = 1./2*(nu/wavelength/np.abs(distance))**0.5
    ss    = np.abs(fc2)/forig
    zc    = nu*dx**2/wavelength
    K     = nu/2/np.amax(np.abs(fx))
    if np.abs(distance) <= zc*2:
        nnu2  = nu
        nnv2  = nv
        fxn   = np.linspace(-1./2./dx,1./2./dx,nnu2)
        fyn   = np.linspace(-1./2./dx,1./2./dx,nnv2)
    else:
        nnu2  = nu
        nnv2  = nv
        fxn   = np.linspace(-fc2,fc2,nnu2)
        fyn   = np.linspace(-fc2,fc2,nnv2)
    FXN,FYN   = np.meshgrid(fxn,fxn)
    Hn        = np.exp(1j*k*distance*(1-(FXN*wavelength)**2-(FYN*wavelength)**2)**0.5)
    FX        = FX/np.amax(FX)*np.pi
    FY        = FY/np.amax(FY)*np.pi
    t_2       = nufft2(field,FX*ss,FY*ss,size=[nnu2,nnv2],sign=iflag,eps=eps)
    FX        = FXN/np.amax(FXN)*np.pi
    FY        = FYN/np.amax(FYN)*np.pi
    result    = nufft2(Hn*t_2,FX*ss,FY*ss,size=[nu,nv],sign=-iflag,eps=eps)
    return result
Пример #3
0
def band_extended_angular_spectrum(field,k,distance,dx,wavelength):
    """
    A definition to calculate bandextended angular spectrum based beam propagation. For more Zhang, Wenhui, Hao Zhang, and Guofan Jin. "Band-extended angular spectrum method for accurate diffraction calculation in a wide propagation range." Optics Letters 45.6 (2020): 1543-1546.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    iflag = -1
    eps   = 10**(-12)
    nv,nu = field.shape
    l     = nu*dx
    x     = np.linspace(-l/2,l/2,nu)
    y     = np.linspace(-l/2,l/2,nv)
    X,Y   = np.meshgrid(x,y)
    Z     = X**2+Y**2
    fx    = np.linspace(-1./2./dx,1./2./dx,nu)
    fy    = np.linspace(-1./2./dx,1./2./dx,nv)
    FX,FY = np.meshgrid(fx,fy)
    K     = nu/2/np.amax(fx)
    fcn   = 1./2*(nu/wavelength/np.abs(distance))**0.5
    ss    = np.abs(fcn)/np.amax(np.abs(fx))
    zc    = nu*dx**2/wavelength
    if np.abs(distance) < zc:
        fxn = fx
        fyn = fy
    else:
        fxn = fx*ss
        fyn = fy*ss
    FXN,FYN   = np.meshgrid(fxn,fyn)
    Hn        = np.exp(1j*k*distance*(1-(FXN*wavelength)**2-(FYN*wavelength)**2)**0.5)
    X         = X/np.amax(X)*np.pi
    Y         = Y/np.amax(Y)*np.pi
    t_asmNUFT = nufft2(field,X*ss,Y*ss,sign=iflag,eps=eps)
    result    = nuifft2(Hn*t_asmNUFT,X*ss,Y*ss,sign=-iflag,eps=eps)
    return result
Пример #4
0
def batch_of_rays(entry, exit):
    """
    Definition to generate a batch of rays with given entry point(s) and exit point(s). Note that the mapping is one to one, meaning nth item in your entry points list will exit from nth item in your exit list and generate that particular ray. Note that you can have a combination like nx3 points for entry or exit and 1 point for entry or exit. But if you have multiple points both for entry and exit, the number of points have to be same both for entry and exit.

    Parameters
    ----------
    entry      : ndarray
                 Either a single point with size of 3 or multiple points with the size of nx3.
    exit       : ndarray
                 Either a single point with size of 3 or multiple points with the size of nx3.

    Returns
    ----------
    rays       : ndarray
                 Generated batch of rays.
    """
    norays = np.array([0, 0])
    if len(entry.shape) == 1:
        entry = entry.reshape((1, 3))
    if len(exit.shape) == 1:
        exit = exit.reshape((1, 3))
    norays = np.amax(np.asarray([entry.shape[0], exit.shape[0]]))
    if norays > exit.shape[0]:
        exit = np.repeat(exit, norays, axis=0)
    elif norays > entry.shape[0]:
        entry = np.repeat(entry, norays, axis=0)
    rays = []
    norays = int(norays)
    for i in range(norays):
        rays.append(create_ray_from_two_points(entry[i], exit[i]))
    rays = np.asarray(rays)
    return rays
Пример #5
0
def main():
    # Variables to be set.
    wavelength = 0.5 * pow(10, -6)
    pixeltom = 6 * pow(10, -6)
    distance = 0.1
    propagation_type = 'Fraunhofer'
    k = wavenumber(wavelength)
    sample_field = np.zeros((150, 150), dtype=np.complex64)
    sample_field[40:60, 40:60] = 10
    sample_field = add_random_phase(sample_field)
    hologram = propagate_beam(sample_field, k, distance, pixeltom, wavelength,
                              propagation_type)

    if propagation_type == 'Fraunhofer':
        distance = np.abs(distance)
        reconstruction = propagate_beam(hologram, k, distance, pixeltom,
                                        wavelength, 'Fraunhofer Inverse')

    from odak.visualize.plotly import detectorshow
    detector = detectorshow()
    detector.add_field(sample_field)
    detector.show()
    detector.add_field(hologram)
    detector.show()
    detector.add_field(reconstruction / np.amax(np.abs(reconstruction)))
    detector.show()
    assert True == True
Пример #6
0
def reflect(input_ray, normal):
    """ 
    Definition to reflect an incoming ray from a surface defined by a surface normal. Used method described in G.H. Spencer and M.V.R.K. Murty, "General Ray-Tracing Procedure", 1961.

    Parameters
    ----------
    input_ray    : ndarray
                   A vector/ray (2x3). It can also be a list of rays (nx2x3).
    normal       : ndarray
                   A surface normal (2x3). It also be a list of normals (nx2x3).

    Returns
    ----------
    output_ray   : ndarray
                   Array that contains starting points and cosines of a reflected ray.
    """
    input_ray = np.asarray(input_ray)
    normal = np.asarray(normal)
    if len(input_ray.shape) == 2:
        input_ray = input_ray.reshape((1, 2, 3))
    if len(normal.shape) == 2:
        normal = normal.reshape((1, 2, 3))
    mu = 1
    div = normal[:, 1, 0]**2 + normal[:, 1, 1]**2 + normal[:, 1, 2]**2
    a = mu * (input_ray[:, 1, 0] * normal[:, 1, 0] + input_ray[:, 1, 1] *
              normal[:, 1, 1] + input_ray[:, 1, 2] * normal[:, 1, 2]) / div
    n = np.int(np.amax(np.array([normal.shape[0], input_ray.shape[0]])))
    output_ray = np.zeros((n, 2, 3))
    output_ray[:, 0] = normal[:, 0]
    output_ray[:, 1] = input_ray[:, 1] - 2 * a * normal[:, 1]
    if output_ray.shape[0] == 1:
        output_ray = output_ray.reshape((2, 3))
    return output_ray
Пример #7
0
def roi(image, location=[0, 100, 0, 100], threshold=[0, 1, 0, 1]):
    """
    Definition to get the lines from a target ROI.

    Parameters
    ----------
    image      : ndarray
                 a 2D image to be sliced (nxm).
    location   : ndarray
                 Locations for taking the ROI.
    threshold  : list
                 Threshold below and above these numbers.

    Returns
    -------
    line_x     : ndarray
                 Line slice.
    line_y     : ndarray
                 Line slice.
    """
    img = image[location[0]:location[1], location[2]:location[3]]
    if len(img.shape) == 3:
        img = np.sum(img, axis=2)
    line_x = img[:, int(img.shape[1] / 2)]
    line_y = img[int(img.shape[0] / 2), :]
    line_x = np.asarray(line_x)
    line_y = np.asarray(line_y)
    line_x = line_x - np.amin(line_x)
    line_x = line_x / np.amax(line_x)
    line_y = line_y - np.amin(line_y)
    line_y = line_y / np.amax(line_y)
    line_x[line_x < threshold[0]] = 0
    line_x[line_x > threshold[1]] = 1
    line_y[line_y < threshold[2]] = 0
    line_y[line_y > threshold[3]] = 1
    return line_x, line_y, img
Пример #8
0
def fourier_transform_1d(line):
    """
    Definition to take the 1D fourier transform. This is used only for modulation transfer function calculations.

    Parameters
    ----------
    line         : ndarray
                   1D array.

    Returns
    ----------
    result       : ndarray
                   Positive side of the fourier transform of the given line.
    """
    result = np.fft.fft(line)  #/len(der_x)
    result /= np.amax(result)
    result = result[np.arange(0, int(line.shape[0] / 2))]
    return result
Пример #9
0
def test():
    from odak import np
    from odak.wave import gerchberg_saxton, adjust_phase_only_slm_range, produce_phase_only_slm_pattern, calculate_amplitude
    from odak.tools import save_image
    wavelength = 0.000000532
    dx = 0.0000064
    distance = 2.0
    input_field = np.zeros((500, 500), dtype=np.complex64)
    input_field[0::50, :] += 1
    iteration_number = 200
    hologram, reconstructed = gerchberg_saxton(input_field, iteration_number,
                                               distance, dx, wavelength,
                                               np.pi * 2,
                                               'Bandlimited Angular Spectrum')
    hologram = produce_phase_only_slm_pattern(hologram, 2 * np.pi,
                                              'output_hologram.png')
    amplitude = calculate_amplitude(reconstructed)
    save_image('output_amplitude.png',
               amplitude,
               cmin=0,
               cmax=np.amax(amplitude))
    assert True == True
Пример #10
0
def intersect_w_surface(ray, points):
    """
    Definition to find intersection point inbetween a surface and a ray. For more see: http://geomalgorithms.com/a06-_intersect-2.html

    Parameters
    ----------
    ray          : ndarray
                   A vector/ray.
    points       : ndarray
                   Set of points in X,Y and Z to define a planar surface.

    Returns
    ----------
    normal       : ndarray
                   Surface normal at the point of intersection.
    distance     : float
                   Distance in between starting point of a ray with it's intersection with a planar surface.
    """
    points = np.asarray(points)
    normal = get_triangle_normal(points)
    if len(ray.shape) == 2:
        ray = ray.reshape((1, 2, 3))
    if len(points) == 2:
        points = points.reshape((1, 3, 3))
    if len(normal.shape) == 2:
        normal = normal.reshape((1, 2, 3))
    f = normal[:, 0] - ray[:, 0]
    distance = np.dot(normal[:, 1], f.T) / np.dot(normal[:, 1], ray[:, 1].T)
    n = np.int(np.amax(np.array([ray.shape[0], normal.shape[0]])))
    normal = np.zeros((n, 2, 3))
    normal[:, 0] = ray[:, 0] + distance.T * ray[:, 1]
    distance = np.abs(distance)
    if normal.shape[0] == 1:
        normal = normal.reshape((2, 3))
        distance = distance.reshape((1))
    if distance.shape[0] == 1 and len(distance.shape) > 1:
        distance = distance.reshape((distance.shape[1]))
    return normal, distance