Exemple #1
0
    def crop(self):
        """Crop data to rectangle bounding non-NaN region."""
        nans = m.isfinite(self.phase)
        nancols = m.any(nans, axis=0)
        nanrows = m.any(nans, axis=1)

        left, right = nanrows.argmax(), nanrows[::-1].argmax()
        top, bottom = nancols.argmax(), nancols[::-1].argmax()
        if left == right == top == bottom == 0:
            return self

        if left == 0 and bottom == 0:
            lr = slice(None)
        if left == 0:
            lr = slice(-right)
        elif right == 0:
            lr = slice(left, self.phase.shape[0])
        else:
            lr = slice(left, -right)

        if top == 0 and bottom == 0:
            tb = slice(None)
        elif top == 0:
            tb = slice(-bottom)
        elif bottom == 0:
            tb = slice(top, self.phase.shape[1])
        else:
            tb = slice(top, -bottom)

        self.phase = self.phase[lr, tb]
        self.unit_y, self.unit_x = self.unit_y[lr], self.unit_x[tb]
        self.unit_x -= self.unit_x[0]
        self.unit_y -= self.unit_y[0]
        return self
Exemple #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
Exemple #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
Exemple #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
Exemple #5
0
def pv(array):
    """Return the PV value of the valid elements of an array.

    Parameters
    ----------
    array : `numpy.ndarray`
        array of values

    Returns
    -------
    `float`
        PV of the array
    """
    non_nan = m.isfinite(array)
    return array[non_nan].max() - array[non_nan].min()
Exemple #6
0
def mean(array):
    """Return the mean value of the valid elements of an array.

    Parameters
    ----------
    array : `numpy.ndarray`
        array of values

    Returns
    -------
    `float`
        mean value

    """
    non_nan = m.isfinite(array)
    return array[non_nan].mean()
Exemple #7
0
def rms(array):
    """Return the RMS value of the valid elements of an array.

    Parameters
    ----------
    array : `numpy.ndarray`
        array of values

    Returns
    -------
    `float`
        RMS of the array

    """
    non_nan = m.isfinite(array)
    return m.sqrt((array[non_nan]**2).mean())
Exemple #8
0
def Sa(array):
    """Return the Ra value for the valid elements of an array.

    Parameters
    ----------
    array: `numpy.ndarray`
        array of values

    Returns
    -------
    `float`
        Ra of the array
    """
    non_nan = m.isfinite(array)
    ary = array[non_nan]
    mean = ary.mean()
    return abs(ary - mean).sum() / ary.size
Exemple #9
0
    def fill(self, _with=0):
        """Fill invalid (NaN) values.

        Parameters
        ----------
        _with : `float`, optional
            value to fill with

        Returns
        -------
        `Interferogram`
            self

        """
        nans = ~m.isfinite(self.phase)
        self.phase[nans] = _with
        return self
Exemple #10
0
def std(array):
    """Return the standard deviation of the valid elements of an array.

    Parameters
    ----------
    array: `numpy.ndarray`
        array of values

    Returns
    -------
    `float`
        std of the array

    """
    non_nan = m.isfinite(array)
    ary = array[non_nan]
    return ary.std()
Exemple #11
0
    def crop(self):
        """Crop data to rectangle bounding non-NaN region."""
        nans = m.isfinite(self.phase)
        nancols = m.any(nans, axis=0)
        nanrows = m.any(nans, axis=1)

        left, right = nanrows.argmax(), nanrows[::-1].argmax()
        top, bottom = nancols.argmax(), nancols[::-1].argmax()
        if left == right == top == bottom == 0:
            return self

        self.phase = self.phase[left:-right, top:-bottom]
        self.unit_y, self.unit_x = self.unit_y[left:-right], self.unit_x[
            top:-bottom]
        self.unit_x -= self.unit_x[0]
        self.unit_y -= self.unit_y[0]
        return self
Exemple #12
0
def fit(data, num_terms=16, rms_norm=False, round_at=6):
    ''' Fits a number of Zernike coefficients to provided data by minimizing
        the root sum square between each coefficient and the given data.  The
        data should be uniformly sampled in an x,y grid.

    Args:

        data (`numpy.ndarray`): data to fit to.

        num_terms (`int`): number of terms to fit, fits terms 0~num_terms.

        rms_norm (`bool`): if true, normalize coefficients to unit RMS value.

        round_at (`int`): decimal place to round values at.

    Returns:
        numpy.ndarray: an array of coefficients matching the input data.

    '''
    if num_terms > len(zernmap):
        raise ValueError(f'number of terms must be less than {len(zernmap)}')

    # precompute the valid indexes in the original data
    pts = m.isfinite(data)

    # set up an x/y rho/phi grid to evaluate Zernikes on
    x, y = m.linspace(-1, 1, data.shape[1]), m.linspace(-1, 1, data.shape[0])
    xx, yy = m.meshgrid(x, y)
    rho = m.sqrt(xx**2 + yy**2)[pts].flatten()
    phi = m.arctan2(xx, yy)[pts].flatten()

    # compute each Zernike term
    zernikes = []
    for i in range(num_terms):
        func = z.zernikes[zernmap[i]]
        base_zern = func(rho, phi)
        if rms_norm:
            base_zern *= func.norm
        zernikes.append(base_zern)
    zerns = m.asarray(zernikes).T

    # use least squares to compute the coefficients
    coefs = m.lstsq(zerns, data[pts].flatten(), rcond=None)[0]
    return coefs.round(round_at)
Exemple #13
0
    def bandreject(self, wllow, wlhigh):
        """Apply a band-rejection filter to the phase (height) data.

        Parameters
        ----------
        wllow : `float`
            low wavelength (spatial period), units of self.scale
        wlhigh : `float`
            high wavelength (spatial period), units of self.scale

        Returns
        -------
        `Interferogram`
            in-place modified instance of self

        """
        new_phase = bandreject_filter(self.phase, self.sample_spacing, wllow,
                                      wlhigh)
        new_phase[~m.isfinite(self.phase)] = m.nan
        self.phase = new_phase
        return self
Exemple #14
0
def bandreject_filter(array, sample_spacing, wllow, wlhigh):
    sy, sx = array.shape

    # compute the bandpass in sample coordinates
    ux, uy = forward_ft_unit(sample_spacing,
                             sx), forward_ft_unit(sample_spacing, sy)
    fhigh, flow = 1 / wllow, 1 / wlhigh

    # make an ordinate array in frequency space and use it to make a mask
    uxx, uyy = m.meshgrid(ux, uy)
    highpass = ((uxx < -fhigh) | (uxx > fhigh)) | ((uyy < -fhigh) |
                                                   (uyy > fhigh))
    lowpass = ((uxx > -flow) & (uxx < flow)) & ((uyy > -flow) & (uyy < flow))
    mask = highpass | lowpass

    # adjust NaNs and FFT
    work = array.copy()
    work[~m.isfinite(work)] = 0
    fourier = m.fftshift(m.fft2(m.ifftshift(work)))
    fourier[mask] = 0
    out = m.fftshift(m.ifft2(m.ifftshift(fourier)))
    return out.real
Exemple #15
0
def zernikefit(data, x=None, y=None, rho=None, phi=None, terms=16, norm=False, residual=False, round_at=6, map_='fringe'):
    """Fits a number of Zernike coefficients to provided data.

    Works by minimizing the mean square error  between each coefficient and the
    given data.  The data should be uniformly sampled in an x,y grid.

    Parameters
    ----------
    data : `numpy.ndarray`
        data to fit to.
    x : `numpy.ndarray`, optional
        x coordinates, same shape as data
    y : `numpy.ndarray`, optional
        y coordinates, same shape as data
    rho : `numpy.ndarray`, optional
        radial coordinates, same shape as data
    phi : `numpy.ndarray`, optional
        azimuthal coordinates, same shape as data
    terms : `int`, optional
        number of terms to fit, fits terms 0~terms
    norm : `bool`, optional
        if True, normalize coefficients to unit RMS value
    residual : `bool`, optional
        if True, return a tuple of (coefficients, residual)
    round_at : `int`, optional
        decimal place to round values at.
    map_ : `str`, optional, {'fringe', 'noll'}
        which ordering of Zernikes to use

    Returns
    -------
    coefficients : `numpy.ndarray`
        an array of coefficients matching the input data.
    residual : `float`
        RMS error between the input data and the fit.

    Raises
    ------
    ValueError
        too many terms requested.

    """
    map_ = maps[map_]
    if terms > len(fringemap):
        raise ValueError(f'number of terms must be less than {len(fringemap)}')

    data = data.T  # transpose to mimic transpose of zernikes

    # precompute the valid indexes in the original data
    pts = m.isfinite(data)

    if x is None and rho is None:
        # set up an x/y rho/phi grid to evaluate Zernikes on
        rho, phi = make_rho_phi_grid(*reversed(data.shape))
        rho = rho[pts].flatten()
        phi = phi[pts].flatten()
    elif rho is None:
        rho, phi = cart_to_polar(x, y)
        rho, phi = rho[pts].flatten(), phi[pts].flatten()

    # compute each Zernike term
    zerns_raw = []
    for i in range(terms):
        func = zernikes[map_[i]]
        base_zern = func(rho, phi)
        if norm:
            base_zern *= func.norm
        zerns_raw.append(base_zern)
    zerns = m.asarray(zerns_raw).T

    # use least squares to compute the coefficients
    meas_pts = data[pts].flatten()
    coefs = m.lstsq(zerns, meas_pts, rcond=None)[0]
    if round_at is not None:
        coefs = coefs.round(round_at)

    if residual is True:
        components = []
        for zern, coef in zip(zerns_raw, coefs):
            components.append(coef * zern)

        _fit = m.asarray(components)
        _fit = _fit.sum(axis=0)
        rmserr = rms(data[pts].flatten() - _fit)
        return coefs, rmserr
    else:
        return coefs
Exemple #16
0
 def dropout_percentage(self):
     """Percentage of pixels in the data that are invalid (NaN)."""
     return m.count_nonzero(~m.isfinite(self.phase)) / self.phase.size * 100
Exemple #17
0
 def remove_piston(self):
     """Remove piston from the data by subtracting the mean value."""
     self.phase -= self.phase[m.isfinite(self.phase)].mean()
     return self