예제 #1
0
파일: geometry.py 프로젝트: chllym/prysm
def rotated_ellipse(width_major, width_minor, major_axis_angle=0, samples=128):
    """Generate a binary mask for an ellipse, centered at the origin.

    The major axis will notionally extend to the limits of the array, but this
    will not be the case for rotated cases.

    Parameters
    ----------
    width_major : `float`
        width of the ellipse in its major axis
    width_minor : `float`
        width of the ellipse in its minor axis
    major_axis_angle : `float`
        angle of the major axis w.r.t. the x axis, degrees
    samples : `int`
        number of samples

    Returns
    -------
    `numpy.ndarray`
        An ndarray of shape (samples,samples) of value 0 outside the ellipse,
        and value 1 inside the ellipse

    Notes
    -----
    The formula applied is:
         ((x-h)cos(A)+(y-k)sin(A))^2      ((x-h)sin(A)+(y-k)cos(A))^2
        ______________________________ + ______________________________ 1
                     a^2                               b^2
    where x and y are the x and y dimensions, A is the rotation angle of the
    major axis, h and k are the centers of the the ellipse, and a and b are
    the major and minor axis widths.  In this implementation, h=k=0 and the
    formula simplifies to:
            (x*cos(A)+y*sin(A))^2             (x*sin(A)+y*cos(A))^2
        ______________________________ + ______________________________ 1
                     a^2                               b^2

    see SO:
    https://math.stackexchange.com/questions/426150/what-is-the-general-equation-of-the-ellipse-that-is-not-in-the-origin-and-rotate

    Raises
    ------
    ValueError
        Description

    """
    if width_minor > width_major:
        raise ValueError(
            'By definition, major axis must be larger than minor.')

    arr = m.ones((samples, samples))
    lim = width_major
    x, y = m.linspace(-lim, lim, samples), m.linspace(-lim, lim, samples)
    xv, yv = m.meshgrid(x, y)
    A = m.radians(-major_axis_angle)
    a, b = width_major, width_minor
    major_axis_term = ((xv * m.cos(A) + yv * m.sin(A))**2) / a**2
    minor_axis_term = ((xv * m.sin(A) - yv * m.cos(A))**2) / b**2
    arr[major_axis_term + minor_axis_term > 1] = 0
    return arr
예제 #2
0
def fit_plane(x, y, z):
    xx, yy = m.meshgrid(x, y)
    pts = m.isfinite(z)
    xx_, yy_ = xx[pts].flatten(), yy[pts].flatten()
    flat = m.ones(xx_.shape)

    coefs = m.lstsq(m.stack([xx_, yy_, flat]).T, z[pts].flatten(),
                    rcond=None)[0]
    plane_fit = coefs[0] * xx + coefs[1] * yy + coefs[2]
    return plane_fit
예제 #3
0
def fit_sphere(z):
    x, y = m.linspace(-1, 1, z.shape[1]), m.linspace(-1, 1, z.shape[0])
    xx, yy = m.meshgrid(x, y)
    pts = m.isfinite(z)
    xx_, yy_ = xx[pts].flatten(), yy[pts].flatten()
    rho, phi = cart_to_polar(xx_, yy_)
    focus = defocus(rho, phi)

    coefs = m.lstsq(m.stack([focus, m.ones(focus.shape)]).T, z[pts].flatten(), rcond=None)[0]
    rho, phi = cart_to_polar(xx, yy)
    sphere = defocus(rho, phi) * coefs[0]
    return sphere
예제 #4
0
def fit_plane(x, y, z):
    pts = m.isfinite(z)
    if len(z.shape) > 1:
        x, y = m.meshgrid(x, y)
        xx, yy = x[pts].flatten(), y[pts].flatten()
    else:
        xx, yy = x, y

    flat = m.ones(xx.shape)

    coefs = m.lstsq(m.stack([xx, yy, flat]).T, z[pts].flatten(), rcond=None)[0]
    plane_fit = coefs[0] * x + coefs[1] * y + coefs[2]
    return plane_fit
예제 #5
0
파일: geometry.py 프로젝트: chllym/prysm
def inverted_circle(samples=128, radius=1):
    """ Create an inverted circular mask (obscuration).

    Parameters
    ----------
    samples : `int`, optional
        number of samples in the square output array

    Returns
    ------
    `numpy.ndarray`
        binary ndarray representation of the mask

    """
    if radius is 0:
        return m.ones((samples, samples))
    else:
        x = m.linspace(-1, 1, samples)
        y = x
        xx, yy = m.meshgrid(x, y)
        rho, phi = cart_to_polar(xx, yy)
        mask = m.ones(rho.shape)
        mask[rho < radius] = 0
        return mask
예제 #6
0
파일: geometry.py 프로젝트: chllym/prysm
def square(samples=128):
    """Create a square mask.

    Parameters
    ----------
    samples : `int`, optional
        number of samples in the square output array

    Returns
    -------
    `numpy.ndarray`
        binary ndarray representation of the mask

    """
    return m.ones((samples, samples), dtype=bool)
예제 #7
0
파일: geometry.py 프로젝트: samkberry/prysm
def square(samples=128, radius=1):
    """Create a square mask.

    Parameters
    ----------
    samples : `int`, optional
        number of samples in the square output array
    radius : `float`, optional
        radius of the shape in the square output array.  radius=1 will fill the
        x

    Returns
    -------
    `numpy.ndarray`
        binary ndarray representation of the mask

    """
    return m.ones((samples, samples), dtype=bool)
예제 #8
0
파일: objects.py 프로젝트: samkberry/prysm
    def __init__(self,
                 angle=4,
                 background='white',
                 sample_spacing=2,
                 samples=256):
        """Create a new TitledSquare instance.

        Parameters
        ----------
        angle : `float`
            angle in degrees to tilt w.r.t. the x axis
        background : `string`
            white or black; the square will be the opposite color of the background
        sample_spacing : `float`
            spacing of samples
        samples : `int`
            number of samples

        """
        radius = 0.3
        if background.lower() == 'white':
            arr = m.ones((samples, samples))
            fill_with = 0
        else:
            arr = m.zeros((samples, samples))
            fill_with = 1

        # TODO: optimize by working with index numbers directly and avoid
        # creation of X,Y arrays for performance.
        x = m.linspace(-0.5, 0.5, samples)
        y = m.linspace(-0.5, 0.5, samples)
        xx, yy = m.meshgrid(x, y)
        sf = samples * sample_spacing

        # TODO: convert inline operation to use of rotation matrix
        angle = m.radians(angle)
        xp = xx * m.cos(angle) - yy * m.sin(angle)
        yp = xx * m.sin(angle) + yy * m.cos(angle)
        mask = (abs(xp) < radius) * (abs(yp) < radius)
        arr[mask] = fill_with
        super().__init__(data=arr,
                         unit_x=x * sf,
                         unit_y=y * sf,
                         has_analytic_ft=False)
예제 #9
0
def psd(height, sample_spacing):
    """Compute the power spectral density of a signal.

    Parameters
    ----------
    height : `numpy.ndarray`
        height or phase data
    sample_spacing : `float`
        spacing of samples in the input data

    Returns
    -------
    unit_x : `numpy.ndarray`
        ordinate x frequency axis
    unit_y : `numpy.ndarray`
        ordinate y frequency axis
    psd : `numpy.ndarray`
        power spectral density

    """
    s = height.shape

    window = window_2d_welch(
        m.arange(s[1]) * sample_spacing,
        m.arange(s[0]) * sample_spacing)
    window = m.ones(height.shape)
    psd = prop_pupil_plane_to_psf_plane(height * window, norm='ortho')
    ux = forward_ft_unit(sample_spacing, int(round(height.shape[1], 0)))
    uy = forward_ft_unit(sample_spacing, int(round(height.shape[0], 0)))

    psd /= height.size

    # adjustment for 2D welch window bias, there is room for improvement to this
    # approximate value from:
    # Power Spectral Density Specification and Analysis of Large Optical Surfaces
    # E. Sidick, JPL
    psd /= 0.925
    return ux, uy, psd
예제 #10
0
파일: geometry.py 프로젝트: samkberry/prysm
def inverted_circle(samples=128, radius=1):
    """Create an inverted circular mask (obscuration).

    Parameters
    ----------
    samples : `int`, optional
        number of samples in the square output array
    radius : `float`, optional
        radius of the shape in the square output array.  radius=1 will fill the
        x

    Returns
    -------
    `numpy.ndarray`
        binary ndarray representation of the mask

    """
    if radius is 0:
        return m.zeros((samples, samples), dtype=config.precision)
    else:
        rho, phi = make_rho_phi_grid(samples, samples)
        mask = m.ones(rho.shape, dtype=config.precision)
        mask[rho < radius] = 0
        return mask
예제 #11
0
    def __init__(self,
                 samples=128,
                 dia=1.0,
                 wavelength=0.55,
                 opd_unit='waves',
                 mask='circle',
                 mask_target='both',
                 ux=None,
                 uy=None,
                 phase=None):
        """Create a new `Pupil` instance.

        Parameters
        ----------
        samples : `int`, optional
            number of samples across the pupil interior
        dia : `float`, optional
            diameter of the pupil, mm
        wavelength : `float`, optional
            wavelength of light, um
        opd_unit : `str`, optional, {'waves', 'um', 'nm'}
            unit used to m.express the OPD.  Equivalent strings may be used to the
            valid options, e.g. 'microns', or 'nanometers'
        mask : `str` or `numpy.ndarray`
            mask used to define the amplitude and boundary of the pupil; any
            regular polygon from `prysm.geometry` as a string, e.g. 'circle' is
            valid.  A user-provided ndarray can also be used.
        mask_target : `str`, {'phase', 'fcn', 'both', None}
            which array to mask during pupil creation; only masking fcn is
            faster for numerical propagations but will make plot2d() and the
            phase array not be truncated properly.  Note that if the mask is not
            binary and `phase` or `both` is used, phase plots will also not be
            correct, as they will be attenuated by the mask.
        ux : `np.ndarray`
            x axis units
        uy : `np.ndarray`
            y axis units
        phase : `np.ndarray`
            phase data

        Notes
        -----
        If ux give, assume uy and phase also given; skip much of the pupil building process
        and simply copy values.

        Raises
        ------
        ValueError
            if the OPD unit given is invalid

        """
        if ux is None:
            # must build a pupil
            self.dia = dia
            ux = m.linspace(-dia / 2, dia / 2, samples)
            uy = m.linspace(-dia / 2, dia / 2, samples)
            self.samples = samples
            need_to_build = True
        else:
            # data already known
            need_to_build = False
        super().__init__(unit_x=ux,
                         unit_y=uy,
                         phase=phase,
                         wavelength=wavelength,
                         phase_unit=opd_unit,
                         spatial_unit='mm')
        self.xaxis_label = 'Pupil ξ'
        self.yaxis_label = 'Pupil η'
        self.zaxis_label = 'OPD'
        self.rho = self.phi = None

        if need_to_build:
            if type(mask) is not m.ndarray:
                mask = mcache(mask, self.samples)

            self._mask = mask
            self.mask_target = mask_target
            self.build()
            self.mask(self._mask, self.mask_target)
        else:
            protomask = m.isnan(phase)
            mask = m.ones(protomask.shape)
            mask[protomask] = 0
            self._mask = mask
            self.mask_target = 'fcn'
예제 #12
0
def Z0(rho, phi):
    return m.ones(rho.shape)
예제 #13
0
def piston(rho, phi):
    """Zernike Piston."""
    return m.ones(rho.shape)
예제 #14
0
def piston(rho, phi):
    return m.ones(rho.shape)