예제 #1
0
def test_shift_rotation_consistency(npix=30, grid_size = 1.5, angle=35, display=False):
    """Test shifting & rotation together for FITS and Analytic optics
    Do we get consistent behavior from each? Are the signs and
    order of operations consistent?

    Tests the fix for issue #275.
    """
    import poppy
    if npix < 30:
        raise ValueError("Need npix>=30 for enough resolution for this test")

    # Create rectangle, rotated
    rect = poppy.RectangleAperture(shift_x=0.25, rotation=angle)
    rect_samp = rect.sample(grid_size=grid_size, npix=npix)

    # Create rectangle, turn into FITS, then rotate
    rect_fits = poppy.RectangleAperture().to_fits(grid_size=grid_size, npix=npix)
    rect2 = poppy.FITSOpticalElement(transmission=rect_fits, shift_x=0.25, rotation=angle)

    # Compare that they are consistent enough, meaning
    # no more than 1% pixel difference. That tolerance allows for the
    # imprecision of rotating low-res binary masks.
    diff = np.round(rect2.amplitude)-rect_samp
    assert np.abs(diff).sum() <= 0.01*rect_samp.sum(), "Shift and rotations differ unexpectedly"

    if display:
        plt.figure()
        plt.subplot(131)
        plt.imshow(rect_samp)
        plt.subplot(132)
        plt.imshow(np.round(rect2.amplitude))
        plt.subplot(133)
        plt.imshow(diff)
예제 #2
0
def test_FITSOpticalElement(tmpdir):
    circ_fits = poppy.CircularAperture().to_fits(grid_size=3, npix=10)
    fn = str(tmpdir / "circle.fits")
    circ_fits.writeto(fn, overwrite=True)

    # Test passing aperture via file on disk
    foe = poppy.FITSOpticalElement(transmission=fn)
    assert foe.amplitude_file == fn
    assert np.allclose(foe.amplitude, circ_fits[0].data)

    # Test passing OPD via FITS object, along with unit conversion
    circ_fits[0].header['BUNIT'] = 'micron'  # need unit for OPD
    foe = poppy.FITSOpticalElement(opd=circ_fits)
    assert foe.opd_file == 'supplied as fits.HDUList object'
    assert np.allclose(foe.opd, circ_fits[0].data * 1e-6)

    # make a cube
    rect_mask = poppy.RectangleAperture().sample(grid_size=3, npix=10)
    circ_mask = circ_fits[0].data
    circ_fits[0].data = np.stack([circ_mask, rect_mask])
    circ_fits[0].header['BUNIT'] = 'nm'  # need unit for OPD
    fn2 = str(tmpdir / "cube.fits")
    circ_fits.writeto(fn2, overwrite=True)

    # Test passing OPD as cube, with slice default, units of nanometers
    foe = poppy.FITSOpticalElement(opd=fn2)
    assert foe.opd_file == fn2
    assert foe.opd_slice == 0
    assert np.allclose(foe.opd, circ_mask * 1e-9)

    # Same cube but now we ask for the next slice
    foe = poppy.FITSOpticalElement(opd=(fn2, 1))
    assert foe.opd_file == fn2
    assert foe.opd_slice == 1
    assert np.allclose(foe.opd, rect_mask * 1e-9)
def rectangle_intensity(rot=0, wd=2, ht=1, xshi=0, yshi=0):
    """Calculate light intensity for light going through a rectangular slit
    rectangle_intensity(rot = 0, wd= 2,ht = 1, xshi = 0,yshi = 0)
    
    rot : rotation in degrees
    
    wd : width in m
    
    ht : height in m
    
    xshi : x axis shift in m
    
    yshi : y axis shift in m
    
    """
    ap = poppy.RectangleAperture(rotation=rot,
                                 width=wd,
                                 height=ht,
                                 shift_x=xshi,
                                 shift_y=yshi)
    ap.display(colorbar=False)

    osys = poppy.OpticalSystem()
    osys.add_pupil(ap)
    osys.add_detector(pixelscale=0.05, fov_arcsec=2.0)
    psf = osys.calc_psf(1e-6)

    plt.figure(figsize=(12, 8))
    poppy.display_psf(psf, title="Diffraction Pattern")
예제 #4
0
파일: optics.py 프로젝트: idiap/cbi_toolbox
def openspim_illumination(wavelength=500e-9,
                          refr_index=1.333,
                          laser_radius=1.2e-3,
                          objective_na=0.3,
                          objective_focal=18e-3,
                          slit_opening=10e-3,
                          pixelscale=635e-9,
                          npix_fov=512,
                          rel_thresh=None,
                          simu_size=2048,
                          oversample=16):
    """
    Compute the illumination function of an OpenSPIM device

    Parameters
    ----------
    wavelength : float, optional
        illumination wavelength in meters, by default 500e-9
    refr_index : float, optional
        imaging medium refraction index, by default 1.333
    laser_radius : float, optional
        source laser radius in meters, by default 1.2e-3
    objective_na : float, optional
        illumination objective NA, by default 0.3
    objective_focal : float, optional
        illumination objective focal length in meters, by default 18e-3
    slit_opening : float, optional
        vertical slit opening in meters, by default 10e-3
    pixelscale : float, optional
        target pixelscale in meters per pixel, by default 1.3e-3/2048
    npix_fov : int, optional
        target size in pixels, by default 512
    rel_thresh: float, optional
        relative threshold to crop the beam thickness
        if a full row is below this theshold, all rows after are removed
        will be computed as compared to the maximum pixel
    simu_size : int, optional
        size of the arrays used for simulation, by default 2048
    oversample : int, optional
        oversampling used for the simulation (must be increased sith simu_size), by default 16

    Returns
    -------
    array [ZXY]
        the illumination function
    """

    pixel_width = 1
    wavelength *= u.m
    laser_radius *= u.m
    objective_focal *= u.m
    pixelscale *= (u.m / u.pixel)
    slit_opening *= u.m

    noop = poppy.ScalarTransmission()
    beam_ratio = 1 / oversample

    fov_pixels = npix_fov * u.pixel
    detector = poppy.FresnelOpticalSystem()
    detector.add_detector(fov_pixels=fov_pixels, pixelscale=pixelscale)

    # We approximate the objective aperture with a square one to make it separable
    # Given the shape of the wavefront, we estimate the generated error to be negligible
    objective_radius = math.tan(math.asin(
        objective_na / refr_index)) * objective_focal
    objective_aperture = poppy.RectangleAperture(name='objective aperture',
                                                 width=2 * objective_radius,
                                                 height=2 * objective_radius)
    objective_lens = poppy.QuadraticLens(f_lens=objective_focal,
                                         name='objective lens')

    obj_aperture = poppy.FresnelOpticalSystem()
    obj_aperture.add_optic(objective_aperture, objective_focal)

    # Implement the objective lens separately to be able to account for refractive index change
    obj_lens = poppy.FresnelOpticalSystem()
    obj_lens.add_optic(objective_lens)

    # Computed as following: going through T1 then CLens then T2
    # is equivalent to going through CLens with focal/4
    # Then the radius is computed as the Fourier transform of the input beam, per 2F lens system
    w0_y = (12.5e-3 * u.m * wavelength) / (2 * np.pi**2 * laser_radius)
    laser_shape_y = poppy.GaussianAperture(w=w0_y, pupil_diam=5 * w0_y)
    path_y = poppy.FresnelOpticalSystem(pupil_diameter=2 * w0_y,
                                        npix=pixel_width,
                                        beam_ratio=beam_ratio)
    path_y.add_optic(laser_shape_y)

    # Going through T1, slit and T2 is equivalent to going through a half-sized slit,
    # then propagating 1/4 the distance
    # Since we use 1D propagation, we can increase oversampling a lot for better results
    laser_shape_z = poppy.GaussianAperture(w=laser_radius,
                                           pupil_diam=slit_opening / 2)
    slit = poppy.RectangleAperture(name='Slit',
                                   width=slit_opening / 2,
                                   height=slit_opening / 2)
    path_z = poppy.FresnelOpticalSystem(pupil_diameter=slit_opening / 2,
                                        npix=pixel_width,
                                        beam_ratio=beam_ratio)
    path_z.add_optic(laser_shape_z)
    path_z.add_optic(slit)
    path_z.add_optic(noop, 0.25 * 100e-3 * u.m)

    # Propagate 1D signals
    wf_z = path_z.input_wavefront(wavelength=wavelength)
    create_wf_1d(wf_z, upsampling=simu_size)
    path_z.propagate(wf_z)

    wf_y = path_y.input_wavefront(wavelength=wavelength)
    create_wf_1d(wf_y, upsampling=simu_size, scale=10)
    path_y.propagate(wf_y)

    obj_aperture.propagate(wf_z)
    obj_aperture.propagate(wf_y)

    wf_z.wavelength /= refr_index
    wf_y.wavelength /= refr_index

    obj_lens.propagate(wf_z)
    obj_lens.propagate(wf_y)

    illumination = np.empty((npix_fov, npix_fov, npix_fov),
                            dtype=wf_z.intensity.dtype)

    # Make sure it is centered even if pixels are odd or even
    offset = 0 if npix_fov % 2 else 0.5

    for pix in range(npix_fov):
        pixel = pix - npix_fov // 2 + offset
        distance = pixel * pixelscale * u.pixel

        psf = poppy.FresnelOpticalSystem()
        psf.add_optic(noop, objective_focal + distance)

        wfc_y = wf_y.copy()
        wfc_z = wf_z.copy()

        psf.propagate(wfc_y)
        psf.propagate(wfc_z)

        resample_wavefront(wfc_y, pixelscale, fov_pixels)
        resample_wavefront(wfc_z, pixelscale, fov_pixels)

        mix = wf_mix(wfc_y, wfc_z)
        mix.normalize()

        illumination[:, pix, :] = mix.intensity

    if rel_thresh is not None:
        illumination = utils.threshold_crop(illumination, rel_thresh, 0)

    return illumination / illumination.sum(0).mean()