Example #1
0
def prism_phase_function(nx, ny, k, angle, dx=0.001, axis='x'):
    """
    A definition to generate 2D phase function that represents a prism. See Goodman's Introduction to Fourier Optics book for more.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    angle      : float
                 Tilt angle of the prism in degrees.
    dx         : float
                 Pixel pitch.
    axis       : str
                 Axis of the prism.

    Returns
    ----------
    prism      : ndarray
                 Generated phase function for a prism.
    """
    angle = np.radians(angle)
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    if axis == 'y':
        prism = np.exp(-1j * k * np.sin(angle) * Y)
    elif axis == 'x':
        prism = np.exp(-1j * k * np.sin(angle) * X)
    return prism
Example #2
0
def sphere_sample(no=[10, 10], radius=1., center=[0., 0., 0.], k=[1, 2]):
    """
    Definition to generate a regular sample set on the surface of a sphere using polar coordinates.

    Parameters
    ----------
    no          : list
                  Number of samples.
    radius      : float
                  Radius of a sphere.
    center      : list
                  Center of a sphere.
    k           : list
                  Multipliers for gathering samples. If you set k=[1,2] it will draw samples from a perfect sphere.

    Returns
    ----------
    samples     : ndarray
                  Samples generated.
    """
    samples = np.zeros((no[0], no[1], 3))
    psi, teta = np.mgrid[0:no[0], 0:no[1]]
    psi = k[0] * np.pi / no[0] * psi
    teta = k[1] * np.pi / no[1] * teta
    samples[:, :, 0] = center[0] + radius * np.sin(psi) * np.cos(teta)
    samples[:, :, 1] = center[0] + radius * np.sin(psi) * np.sin(teta)
    samples[:, :, 2] = center[0] + radius * np.cos(psi)
    samples = samples.reshape((no[0] * no[1], 3))
    return samples
Example #3
0
def linearpolarizer(field, rotation=0):
    """
    Definition that represents a linear polarizer.

    Parameters
    ----------
    field        : ndarray
                   Polarization vector of an input beam.
    rotation     : float
                   Represents rotation of the polarizer along propagation direction in angles (couter-clockwise).

    Returns
    ----------
    result       : ndarray
                   Polarization vector of an output beam.
    """
    rotation = np.radians(rotation)
    rotmat = np.array([[float(np.cos(rotation)),
                        float(np.sin(rotation))],
                       [float(-np.sin(rotation)),
                        float(np.cos(rotation))]])
    linearpolarizer = np.array([[1, 0], [0, 0]])
    linearpolarizer = np.dot(rotmat.transpose(),
                             np.dot(linearpolarizer, rotmat))
    result = np.dot(linearpolarizer, field)
    return result
Example #4
0
def circular_uniform_sample(no=[10, 50],
                            radius=10.,
                            center=[0., 0., 0.],
                            angles=[0., 0., 0.]):
    """
    Definition to generate sample inside a circle uniformly.

    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.empty((0, 3))
    for i in range(0, no[0]):
        r = i / no[0] * radius
        ang_no = no[1] * i / no[0]
        for j in range(0, int(no[1] * i / no[0])):
            angle = j / ang_no * 2 * np.pi
            point = np.array(
                [float(r * np.cos(angle)),
                 float(r * np.sin(angle)), 0])
            samples = np.vstack((samples, point))
    samples = rotate_points(samples, angles=angles, offset=center)
    return samples
Example #5
0
def circular_uniform_random_sample(no=[10, 50],
                                   radius=10.,
                                   center=[0., 0., 0.],
                                   angles=[0., 0., 0.]):
    """ 
    Definition to generate sample inside a circle uniformly but randomly.

    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.empty((0, 3))
    rs = np.sqrt(np.random.uniform(0, 1, no[0]))
    angs = np.random.uniform(0, 2 * np.pi, no[1])
    for i in rs:
        for angle in angs:
            r = radius * i
            point = np.array(
                [float(r * np.cos(angle)),
                 float(r * np.sin(angle)), 0])
            samples = np.vstack((samples, point))
    samples = rotate_points(samples, angles=angles, offset=center)
    return samples
Example #6
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
Example #7
0
def main():
    # Variables to be set.
    wavelength = 0.5 * pow(10, -6)
    pixeltom = 6 * pow(10, -6)
    distance = 0.2
    propagation_type = 'IR Fresnel'
    k = wavenumber(wavelength)
    sample_field = np.zeros((500, 500), dtype=np.complex64)
    sample_field[240:260, 240:260] = 1000
    random_phase = np.pi * np.random.random(sample_field.shape)
    sample_field = sample_field * np.cos(
        random_phase) + 1j * sample_field * np.sin(random_phase)
    sample_field = torch.from_numpy(sample_field)
    hologram = propagate_beam_torch(sample_field, k, distance, pixeltom,
                                    wavelength, propagation_type)
    reconstruction = propagate_beam_torch(hologram, k, -distance, pixeltom,
                                          wavelength, propagation_type)
    # 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)
    # detector.show()
    assert True == True
Example #8
0
def compare():
    wavelength = 0.5 * pow(10, -6)
    pixeltom = 6 * pow(10, -6)
    distance = 0.2
    propagation_type = 'IR Fresnel'
    k = wavenumber(wavelength)
    sample_field = np.zeros((500, 500), dtype=np.complex64)
    sample_field[240:260, 240:260] = 1000
    random_phase = np.pi * np.random.random(sample_field.shape)
    sample_field = sample_field * np.cos(
        random_phase) + 1j * sample_field * np.sin(random_phase)

    sample_field_torch = torch.from_numpy(sample_field)

    ## Propagate and reconstruct using torch.
    hologram_torch = propagate_beam_torch(sample_field_torch, k, distance,
                                          pixeltom, wavelength,
                                          propagation_type)
    reconstruction_torch = propagate_beam_torch(hologram_torch, k, -distance,
                                                pixeltom, wavelength,
                                                propagation_type)

    ## Propagate and reconstruct using np.
    hologram = propagate_beam(sample_field, k, distance, pixeltom, wavelength,
                              propagation_type)
    reconstruction = propagate_beam(hologram, k, -distance, pixeltom,
                                    wavelength, propagation_type)

    np.testing.assert_array_almost_equal(hologram_torch.numpy(), hologram, 3)
Example #9
0
def freeform(nx, ny, k, distances, dx=0.001):
    """
    A definition to generate a freeform field pattern from a depth map.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    distances  : ndarray
                 Depth map.
    dx         : float
                 Pixel pitch.

    Returns
    ---------
    field      : ndarray
                 Generated pattern.
    """
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    field = np.exp(1j * k * 0.5 * np.sin(Z / distances))
    return field
Example #10
0
def quadratic_phase_function(nx, ny, k, focal=0.4, dx=0.001):
    """ 
    A definition to generate 2D quadratic phase function, which is typically use to represent lenses.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    focal      : float
                 Focal length of the quadratic phase function.
    dx         : float
                 Pixel pitch.

    Returns
    ---------
    function   : ndarray
                 Generated quadratic phase function.
    """
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    qwf = np.exp(1j * k * 0.5 * np.sin(Z / focal))
    return qwf
Example #11
0
def produce_phase_only_slm_pattern(hologram,slm_range):
    """
    Definition for producing a pattern for a phase only Spatial Light Modulator (SLM) using a given field.

    Parameters
    ==========
    hologram           : torch.cfloat
                         Input holographic field.
    slm_range          : float
                         Range of the phase only SLM in radians for a working wavelength (i.e. two pi). See odak.wave.adjust_phase_only_slm_range() for more.
    filename           : str
                         Optional variable, if provided the patterns will be save to given location.

    Returns
    ==========
    pattern            : torch.cfloat
                         Adjusted phase only pattern.
    """
    hologram_phase                            = calculate_phase(hologram) % (2*np.pi)
    hologram_phase[hologram_phase>slm_range]  = slm_range
    hologram_phase                           /= slm_range
    hologram_phase                           *= 255
    hologram_phase                            = hologram_phase.int()
    hologram_phase                            = hologram_phase.float()
    hologram_phase                           *= slm_range/255.
    return np.cos(hologram_phase)+1j*np.sin(hologram_phase)
Example #12
0
def plane_tilt(nx, ny, k, focals, dx=0.001, axis='x'):
    """
    A definition to tilt a complex field.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    focals     : list
                 Focus ranges, two for X and two for Y, in total four numbers. Make sure to pass nonzero numbers.
    dx         : float
                 Pixel pitch.
    axis       : str
                 Which state to tilt, x, y or xy.

    Returns
    ----------
    field      : ndarray
                 Field to tilt a plane.
    """
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    if np.all((focals == 0)):
        raise Exception("Focals must be non zero.")
    focal_x = np.geomspace(focals[0], focals[1], size[0])
    focal_y = np.geomspace(focals[2], focals[3], size[1])
    FX, FY = np.meshgrid(focal_x, focal_y)
    field = np.ones((nx, ny), dtype=np.complex64)
    if axis == 'x' or axis == 'xy':
        field *= np.exp(1j * k * 0.5 * np.sin(Z / FX))
    if axis == 'y' or axis == 'xy':
        field *= np.exp(1j * k * 0.5 * np.sin(Z / FY))
    return field
Example #13
0
def main():
    # Variables to be set.
    wavelength                 = 0.5*pow(10,-6)
    pixeltom                   = 6*pow(10,-6)
    distance                   = 0.2
    propagation_type           = 'Bandlimited Angular Spectrum'
    k                          = wavenumber(wavelength)
    sample_field               = np.zeros((500,500),dtype=np.complex64)
    sample_field[
                 240:260,
                 240:260
                ]              = 1000
    random_phase               = np.pi*np.random.random(sample_field.shape)
    sample_field               = sample_field*np.cos(random_phase)+1j*sample_field*np.sin(random_phase)
    criterion = torch.nn.MSELoss()

    if np.__name__ == 'cupy':
        sample_field = np.asnumpy(sample_field)

    sample_field               = torch.from_numpy(sample_field)

    hologram                   = propagate_beam_torch(
                                                sample_field,
                                                k,
                                                distance,
                                                pixeltom,
                                                wavelength,
                                                propagation_type
                                               )
    hologram.requires_grad = True
    reconstruction             = propagate_beam_torch(
                                                hologram,
                                                k,
                                                -distance,
                                                pixeltom,
                                                wavelength,
                                                propagation_type
                                               )
    loss = criterion(torch.abs(sample_field), torch.abs(reconstruction))
    loss.backward()
    print(hologram.grad)
    print('backward successfully')

    #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)
    #detector.show()
    assert True==True
Example #14
0
def set_amplitude(field, amplitude):
    """
    Definition to keep phase as is and change the amplitude of a given field.
    Parameters
    ----------
    field        : np.complex64
                   Complex field.
    amplitude    : np.array or np.complex64
                   Amplitudes.
    Returns
    ----------
    new_field    : np.complex64
                   Complex field.
    """
    amplitude = calculate_amplitude(amplitude)
    phase = calculate_phase(field)
    new_field = amplitude * np.cos(phase) + 1j * amplitude * np.sin(phase)
    return new_field