Пример #1
0
def _padshape(array, Q):
    y, x = array.shape
    out_x = int(m.ceil(x * Q))
    out_y = int(m.ceil(y * Q))
    factor_x = (out_x - x) / 2
    factor_y = (out_y - y) / 2
    return (
        (int(m.floor(factor_y)), int(m.ceil(factor_y))),
        (int(m.floor(factor_x)), int(m.ceil(factor_x)))), out_x, out_y
Пример #2
0
def prop_pupil_plane_to_psf_plane_units(wavefunction, input_sample_spacing,
                                        prop_dist, wavelength, Q):
    """Compute the ordinate axes for a pupil plane to PSF plane propagation.

    Parameters
    ----------
    wavefunction : `numpy.ndarray`
        the pupil wavefunction
    input_sample_spacing : `float`
        spacing between samples in the pupil plane
    prop_dist : `float`
        propagation distance along the z distance
    wavelength : `float`
        wavelength of light
    Q : `float`
        oversampling / padding factor

    Returns
    -------
    unit_x : `numpy.ndarray`
        x axis unit, 1D ndarray
    unit_y : `numpy.ndarray`
        y axis unit, 1D ndarray

    """
    s = wavefunction.shape
    samples_x, samples_y = s[1] * Q, s[0] * Q
    sample_spacing_x = pupil_sample_to_psf_sample(
        pupil_sample=input_sample_spacing,  # factor of
        samples=samples_x,  # 1e3 corrects
        wavelength=wavelength,  # for unit
        efl=prop_dist) / 1e3  # translation
    sample_spacing_y = pupil_sample_to_psf_sample(
        pupil_sample=input_sample_spacing,  # factor of
        samples=samples_y,  # 1e3 corrects
        wavelength=wavelength,  # for unit
        efl=prop_dist) / 1e3  # translation
    unit_x = m.arange(-1 * int(m.ceil(samples_x / 2)),
                      int(m.floor(samples_x / 2))) * sample_spacing_x
    unit_y = m.arange(-1 * int(m.ceil(samples_y / 2)),
                      int(m.floor(samples_y / 2))) * sample_spacing_y
    return unit_x, unit_y
Пример #3
0
def matrix_dft(f, alpha, npix, shift=None, unitary=False):
    '''
    A technique shamelessly stolen from Andy Kee @ NASA JPL
    Is it magic or math?
    '''
    if np.isscalar(alpha):
        ax = ay = alpha
    else:
        ax = ay = np.asarray(alpha)

    f = np.asarray(f)
    m, n = f.shape

    if np.isscalar(npix):
        M = N = npix
    else:
        M = N = np.asarray(npix)

    if shift is None:
        sx = sy = 0
    else:
        sx = sy = np.asarray(shift)

    # Y and X are (r,c) coordinates in the (m x n) input plane, f
    # V and U are (r,c) coordinates in the (M x N) output plane, F
    X = np.arange(n) - floor(n / 2) - sx
    Y = np.arange(m) - floor(m / 2) - sy
    U = np.arange(N) - floor(N / 2) - sx
    V = np.arange(M) - floor(M / 2) - sy

    E1 = exp(1j * -2 * np.pi * (ay / m) * np.outer(Y, V).T)
    E2 = exp(1j * -2 * np.pi * (ax / m) * np.outer(X, U))

    F = E1.dot(f).dot(E2)

    if unitary is True:
        norm_coef = sqrt((ay * ax) / (m * n * M * N))
        return F * norm_coef
    else:
        return F
Пример #4
0
def pad2d(array, Q=2, value=0):
    """Symmetrically pads a 2D array with a value.

    Parameters
    ----------
    array : `numpy.ndarray`
        source array
    Q : `float` or `int`
        oversampling factor; ratio of input to output array widths
    value : `float` or `int`
        value with which to pad the array

    Returns
    -------
    `numpy.ndarray`
        padded array

    Notes
    -----
    padding will be symmetric.

    """
    if Q is 1:
        return array
    else:
        y, x = array.shape
        out_x = int(x * Q)
        out_y = int(y * Q)
        factor_x = (out_x - x) / 2
        factor_y = (out_y - y) / 2
        pad_shape = ((int(m.floor(factor_y)), int(m.ceil(factor_y))),
                     (int(m.floor(factor_x)), int(m.ceil(factor_x))))
        if value is 0:
            out = m.zeros((out_y, out_x), dtype=array.dtype)
        else:
            out = m.zeros((out_y, out_x), dtype=array.dtype) + value
        yy, xx = pad_shape
        out[yy[0]:yy[0] + y, xx[0]:xx[0] + x] = array
        return out
Пример #5
0
    def mask(self, shape_or_mask, diameter=None):
        """Mask the signal.

        The mask will be inscribed in the axis with fewer pixels.  I.e., for
        a interferogram with 1280x1000 pixels, the mask will be 1000x1000 at
        largest.

        Parameters
        ----------
        shape_or_mask : `str` or `numpy.ndarray`
            valid shape from prysm.geometry
        diameter : `float`
            diameter of the mask, in self.spatial_units
        mask : `numpy.ndarray`
            user-provided mask

        Returns
        -------
        self
            modified Interferogram instance.

        """
        if isinstance(shape_or_mask, str):
            if diameter is None:
                diameter = self.diameter
            mask = mcache(shape_or_mask, min(self.shape), radius=diameter / min(self.diameter_x, self.diameter_y))
            base = m.zeros(self.shape, dtype=config.precision)
            difference = abs(self.shape[0] - self.shape[1])
            l, u = int(m.floor(difference / 2)), int(m.ceil(difference / 2))
            if u is 0:  # guard against nocrop scenario
                _slice = slice(None)
            else:
                _slice = slice(l, -u)
            if self.shape[0] < self.shape[1]:
                base[:, _slice] = mask
            else:
                base[_slice, :] = mask

            mask = base
        else:
            mask = shape_or_mask

        hitpts = mask == 0
        self.phase[hitpts] = m.nan
        return self
Пример #6
0
def regular_polygon(sides, samples, radius=1):
    """Generate a regular polygon mask with the given number of sides and samples in the mask array.

    Parameters
    ----------
    sides : `int`
        number of sides to the polygon
    samples : `int`
        number of samples in the output polygon
    radius : `float`, optional
        radius of the regular polygon.  For R=1, will fill the x and y extent

    Returns
    -------
    `numpy.ndarray`
        mask for regular polygon with radius equal to the array radius

    """
    verts = generate_vertices(sides, int(m.floor((samples // 2) * radius)))
    verts[:, 0] += samples // 2  # shift y to center
    verts[:, 1] += samples // 2  # shift x to center
    return generate_mask(verts, samples).astype(config.precision)
Пример #7
0
    def plot2d(self,
               axlim=25,
               power=1,
               clim=(None, None),
               interp_method='lanczos',
               pix_grid=None,
               cmap=config.image_colormap,
               fig=None,
               ax=None,
               show_axlabels=True,
               show_colorbar=True,
               circle_ee=None,
               circle_ee_lw=None):
        """Create a 2D plot of the PSF.

        Parameters
        ----------
        axlim : `float`
            limits of axis, symmetric. xlim=(-axlim,axlim), ylim=(-axlim, axlim)
        power : `float`
            power to stretch the data by for plotting
        clim : iterable
            limits to use for log color scaling.  If power != 1 and
            clim != (None, None), clim (log axes) takes precedence
        interp_method : `string`
            method used to interpolate the image between samples of the PSF
        pix_grid : `float`
            if not None, overlays gridlines with spacing equal to pix_grid.
            Intended to show the collection into camera pixels while still in
            the oversampled domain
        cmap : `str`, optional
            colormap, passed directly to matplotlib
        fig : `matplotlib.figure.Figure`, optional:
            Figure containing the plot
        ax : `matplotlib.axes.Axis`, optional:
            Axis containing the plot
        show_axlabels : `bool`
            whether or not to show the axis labels
        show_colorbar : `bool`
            whether or not to show the colorbar
        circle_ee : `float`, optional
            relative encircled energy to draw a circle at, in addition to
            diffraction limited airy radius (1.22*λ*F#).  First airy zero occurs
            at circle_ee=0.8377850436212378
        circle_ee_lw : `float`, optional
            linewidth passed to matplotlib for the encircled energy circles

        Returns
        -------
        fig : `matplotlib.figure.Figure`, optional
            Figure containing the plot
        ax : `matplotlib.axes.Axis`, optional
            Axis containing the plot

        """
        from matplotlib import colors, patches

        label_str = 'Normalized Intensity [a.u.]'

        left, right = self.unit_x[0], self.unit_x[-1]
        bottom, top = self.unit_y[0], self.unit_y[-1]

        fig, ax = share_fig_ax(fig, ax)

        plt_opts = {
            'extent': [left, right, bottom, top],
            'origin': 'lower',
            'cmap': cmap,
            'interpolation': interp_method,
        }
        cb_opts = {}
        if power is not 1:
            plt_opts['norm'] = colors.PowerNorm(1 / power)
            plt_opts['clim'] = (0, 1)
        elif clim[1] is not None:
            plt_opts['norm'] = colors.LogNorm(*clim)
            cb_opts = {'extend': 'both'}

        im = ax.imshow(self.data, **plt_opts)
        if show_colorbar:
            cb = fig.colorbar(im,
                              label=label_str,
                              ax=ax,
                              fraction=0.046,
                              **cb_opts)
            cb.outline.set_edgecolor('k')
            cb.outline.set_linewidth(0.5)
        if show_axlabels:
            ax.set(xlabel='Image Plane x [μm]', ylabel='Image Plane y [μm]')

        ax.set(xlim=(-axlim, axlim), ylim=(-axlim, axlim))

        if pix_grid is not None:
            # if pixel grid is desired, add it
            mult = m.floor(axlim / pix_grid)
            gmin, gmax = -mult * pix_grid, mult * pix_grid
            pts = m.arange(gmin, gmax, pix_grid)
            ax.set_yticks(pts, minor=True)
            ax.set_xticks(pts, minor=True)
            ax.yaxis.grid(True, which='minor', color='white', alpha=0.25)
            ax.xaxis.grid(True, which='minor', color='white', alpha=0.25)

        if circle_ee is not None:
            if self.fno is None:
                raise ValueError(
                    'F/# must be known to compute EE, set self.fno')
            elif self.wavelength is None:
                raise ValueError(
                    'wavelength must be known to compute EE, set self.wavelength'
                )

            radius = self.ee_radius(circle_ee)
            analytic = _inverse_analytic_encircled_energy(
                self.fno, self.wavelength, circle_ee)

            c_diff = patches.Circle((0, 0),
                                    analytic,
                                    fill=False,
                                    color='r',
                                    ls='--',
                                    lw=circle_ee_lw)
            c_true = patches.Circle((0, 0),
                                    radius,
                                    fill=False,
                                    color='r',
                                    lw=circle_ee_lw)
            ax.add_artist(c_diff)
            ax.add_artist(c_true)
            ax.legend([c_diff, c_true], ['Diff. Lim.', 'Actual'], ncol=2)

        return fig, ax
Пример #8
0
def bindown(array, nsamples_x, nsamples_y=None, mode='avg'):
    """Bin (resample) an array.

    Parameters
    ----------
    array : `numpy.ndarray`
        array of values
    nsamples_x : `int`
        number of samples in x axis to bin by
    nsamples_y : `int`
        number of samples in y axis to bin by.  If None, duplicates value from nsamples_x
    mode : `str`, {'avg', 'sum'}
        sum or avg, how to adjust the output signal

    Returns
    -------
    `numpy.ndarray`
        ndarray binned by given number of samples

    Notes
    -----
    Array should be 2D.  TODO: patch to allow 3D data.

    If the size of `array` is not evenly divisible by the number of samples,
    the algorithm will trim around the border of the array.  If the trim
    length is odd, one extra sample will be lost on the left side as opposed
    to the right side.

    Raises
    ------
    ValueError
        invalid mode

    """
    if nsamples_y is None:
        nsamples_y = nsamples_x

    if nsamples_x == 1 and nsamples_y == 1:
        return array

    # determine amount we need to trim the array
    samples_x, samples_y = array.shape
    total_samples_x = samples_x // nsamples_x
    total_samples_y = samples_y // nsamples_y
    final_idx_x = total_samples_x * nsamples_x
    final_idx_y = total_samples_y * nsamples_y

    residual_x = int(samples_x - final_idx_x)
    residual_y = int(samples_y - final_idx_y)

    # if the amount to trim is symmetric, trim symmetrically.
    if not is_odd(residual_x) and not is_odd(residual_y):
        samples_to_trim_x = residual_x // 2
        samples_to_trim_y = residual_y // 2
        trimmed_data = array[samples_to_trim_x:final_idx_x + samples_to_trim_x,
                             samples_to_trim_y:final_idx_y + samples_to_trim_y]
    # if not, trim more on the left.
    else:
        samples_tmp_x = (samples_x - final_idx_x) // 2
        samples_tmp_y = (samples_y - final_idx_y) // 2
        samples_top = int(m.floor(samples_tmp_y))
        samples_bottom = int(m.ceil(samples_tmp_y))
        samples_left = int(m.ceil(samples_tmp_x))
        samples_right = int(m.floor(samples_tmp_x))
        trimmed_data = array[samples_left:final_idx_x + samples_right,
                             samples_bottom:final_idx_y + samples_top]

    intermediate_view = trimmed_data.reshape(total_samples_x, nsamples_x,
                                             total_samples_y, nsamples_y)

    if mode.lower() in ('avg', 'average', 'mean'):
        output_data = intermediate_view.mean(axis=(1, 3))
    elif mode.lower() == 'sum':
        output_data = intermediate_view.sum(axis=(1, 3))
    else:
        raise ValueError('mode must be average of sum.')

    # trim as needed to make even number of samples.
    # TODO: allow work with images that are of odd dimensions
    px_x, px_y = output_data.shape
    trim_x, trim_y = 0, 0
    if is_odd(px_x):
        trim_x = 1
    if is_odd(px_y):
        trim_y = 1

    return output_data[:px_y - trim_y, :px_x - trim_x]
Пример #9
0
    def plot2d(self,
               axlim=25,
               interp_method='lanczos',
               pix_grid=None,
               fig=None,
               ax=None):
        '''Create a 2D color plot of the PSF.

        Parameters
        ----------
        axlim : `float`
            limits of axis, symmetric. xlim=(-axlim,axlim), ylim=(-axlim, axlim)
        interp_method : `str`
            method used to interpolate the image between samples of the PSF
        pix_grid : `float`
            if not None, overlays gridlines with spacing equal to pix_grid.
            Intended to show the collection into camera pixels while still in
            the oversampled domain
        fig : `matplotlib.figure.Figure`, optional
            Figure containing the plot
        ax : `matplotlib.axes.Axis`, optional:
            Axis containing the plot

        Returns
        -------
        fig : `matplotlib.figure.Figure`, optional
            Figure containing the plot
        ax : `matplotlib.axes.Axis`, optional:
            Axis containing the plot

        '''
        dat = m.empty((self.samples_x, self.samples_y, 3))
        dat[:, :, 0] = self.R
        dat[:, :, 1] = self.G
        dat[:, :, 2] = self.B

        left, right = self.unit_y[0], self.unit_y[-1]
        bottom, top = self.unit_x[0], self.unit_x[-1]

        fig, ax = share_fig_ax(fig, ax)

        ax.imshow(dat,
                  extent=[left, right, bottom, top],
                  interpolation=interp_method,
                  origin='lower')
        ax.set(xlabel=r'Image Plane X [$\mu m$]',
               ylabel=r'Image Plane Y [$\mu m$]',
               xlim=(-axlim, axlim),
               ylim=(-axlim, axlim))

        if pix_grid is not None:
            # if pixel grid is desired, add it
            mult = m.floor(axlim / pix_grid)
            gmin, gmax = -mult * pix_grid, mult * pix_grid
            pts = m.arange(gmin, gmax, pix_grid)
            ax.set_yticks(pts, minor=True)
            ax.set_xticks(pts, minor=True)
            ax.yaxis.grid(True, which='minor')
            ax.xaxis.grid(True, which='minor')

        return fig, ax
Пример #10
0
    def plot2d(self,
               axlim=25,
               power=1,
               interp_method='lanczos',
               pix_grid=None,
               fig=None,
               ax=None,
               show_axlabels=True,
               show_colorbar=True,
               circle_ee=None):
        """Create a 2D plot of the PSF.

        Parameters
        ----------
        axlim : `float`
            limits of axis, symmetric. xlim=(-axlim,axlim), ylim=(-axlim, axlim)
        power : `float`
            power to stretch the data by for plotting
        interp_method : `string`
            method used to interpolate the image between samples of the PSF
        pix_grid : `float`
            if not None, overlays gridlines with spacing equal to pix_grid.
            Intended to show the collection into camera pixels while still in
            the oversampled domain
        fig : `matplotlib.figure.Figure`, optional:
            Figure containing the plot
        ax : `matplotlib.axes.Axis`, optional:
            Axis containing the plot
        show_axlabels : `bool`
            whether or not to show the axis labels
        show_colorbar : `bool`
            whether or not to show the colorbar
        circle_ee : `float`, optional
            relative encircled energy to draw a circle at, in addition to
            diffraction limited airy radius (1.22*λ*F#).  First airy zero occurs
            at circle_ee=0.8377850436212378

        Returns
        -------
        fig : `matplotlib.figure.Figure`, optional
            Figure containing the plot
        ax : `matplotlib.axes.Axis`, optional
            Axis containing the plot

        """
        label_str = 'Normalized Intensity [a.u.]'
        lims = (0, 1)

        left, right = self.unit_x[0], self.unit_x[-1]
        bottom, top = self.unit_y[0], self.unit_y[-1]

        fig, ax = share_fig_ax(fig, ax)

        im = ax.imshow(self.data,
                       extent=[left, right, bottom, top],
                       origin='lower',
                       cmap='Greys_r',
                       norm=colors.PowerNorm(1 / power),
                       interpolation=interp_method,
                       clim=lims)
        if show_colorbar:
            cb = fig.colorbar(im, label=label_str, ax=ax, fraction=0.046)
            cb.outline.set_edgecolor('k')
            cb.outline.set_linewidth(0.5)
        if show_axlabels:
            ax.set(xlabel=r'Image Plane $x$ [$\mu m$]',
                   ylabel=r'Image Plane $y$ [$\mu m$]')

        ax.set(xlim=(-axlim, axlim), ylim=(-axlim, axlim))

        if pix_grid is not None:
            # if pixel grid is desired, add it
            mult = m.floor(axlim / pix_grid)
            gmin, gmax = -mult * pix_grid, mult * pix_grid
            pts = m.arange(gmin, gmax, pix_grid)
            ax.set_yticks(pts, minor=True)
            ax.set_xticks(pts, minor=True)
            ax.yaxis.grid(True, which='minor', color='white', alpha=0.25)
            ax.xaxis.grid(True, which='minor', color='white', alpha=0.25)

        if circle_ee is not None:
            if self.fno is None:
                raise ValueError(
                    'F/# must be known to compute EE, set self.fno')
            elif self.wavelength is None:
                raise ValueError(
                    'wavelength must be known to compute EE, set self.wavelength'
                )

            radius = self.ee_radius(circle_ee)
            analytic = _inverse_analytic_encircled_energy(
                self.fno, self.wavelength, circle_ee)
            c_diff = patches.Circle((0, 0),
                                    analytic,
                                    fill=False,
                                    color='r',
                                    ls='--')
            c_true = patches.Circle((0, 0), radius, fill=False, color='r')
            ax.add_artist(c_diff)
            ax.add_artist(c_true)

        return fig, ax
Пример #11
0
    def trace_focus(self, algorithm='avg'):
        ''' finds the focus position in each field.  This is, in effect, the
            "field curvature" for this azimuth.

        Args:
            algorithm (`str): algorithm to use to trace focus, currently only
                supports '0.5', see notes for a description of this technique.

        Returns:
            `numpy.ndarray`: focal surface sag, in microns, vs field.

        Notes:
            Algorithm '0.5' uses the frequency that has its peak closest to 0.5
            on-axis to estimate the focus coresponding to the minimum RMS WFE
            condition.  This is based on the following assumptions:
                * Any combination of third, fifth, and seventh order spherical
                    aberration will produce a focus shift that depends on
                    frequency, and this dependence can be well fit by an
                    equation of the form y(x) = ax^2 + bx + c.  If this is true,
                    then the frequency which peaks at 0.5 will be near the
                    vertex of the quadratic, which converges to the min RMS WFE
                    condition.

                * Coma, while it enhances depth of field, does not shift the
                    focus peak.

                * Astigmatism and field curvature are the dominant cause of any
                    shift in best focus with field.

                * Chromatic aberrations do not influence the thru-focus MTF peak
                    in a way that varies with field.

        '''
        if algorithm == '0.5':
            # locate the frequency index on axis
            idx_axis = np.searchsorted(self.field, 0)
            idx_freq = abs(self.data[:, idx_axis, :].max(axis=0) -
                           0.5).argmin(axis=1)
            focus_idx = self.data[:,
                                  np.arange(self.data.shape[1]),
                                  idx_freq].argmax(axis=0)
            return self.focus[focus_idx], self.field
        elif algorithm.lower() in ('avg', 'average'):
            if self.freq[0] == 0:
                # if the zero frequency is included, exclude it from our calculations
                avg_idxs = self.data.argmax(axis=0)[:, 1:].mean(axis=1)
            else:
                avg_idxs = self.data.argmax(axis=0).mean(axis=1)

            # account for fractional indexes
            focus_out = avg_idxs.copy()
            for i, idx in enumerate(avg_idxs):
                li, ri = floor(idx), ceil(idx)
                lf, rf = self.focus[li], self.focus[ri]
                diff = rf - lf
                part = idx % 1
                focus_out[i] = lf + diff * part

            return focus_out, self.field
        else:
            raise ValueError('0.5 is only algorithm supported')
Пример #12
0
    def plot2d(self, log=False, axlim=25, interp_method='lanczos',
               pix_grid=None, fig=None, ax=None):
        ''' Creates a 2D color plot of the PSF.

        Args:
            log (`bool`): if true, plot in log scale.  If false, plot in linear
                scale.

            axlim (`float`): limits of axis, symmetric.
                xlim=(-axlim,axlim), ylim=(-axlim, axlim).

            interp_method (`string`): method used to interpolate the image between
                samples of the PSF.

            pix_grid (`float`): if not None, overlays gridlines with spacing equal
                to pix_grid.  Intended to show the collection into camera pixels
                while still in the oversampled domain.

            fig (pyplot.figure): figure to plot in.

            ax (pyplot.axis): axis to plot in.

        Returns:
            pyplot.fig, pyplot.axis.  Figure and axis containing the plot.

        Notes:
            Largely a copy-paste of plot2d() from the PSF class.  Some  refactoring
                could be done to make the code more succinct and unified.

        '''
        dat = np.empty((self.samples_x, self.samples_y, 3))
        dat[:, :, 0] = self.R
        dat[:, :, 1] = self.G
        dat[:, :, 2] = self.B

        if log:
            fcn = 20 * np.log10(1e-100 + dat)
            lims = (-100, 0)  # show first 100dB -- range from (1e-6, 1) in linear scale
        else:
            fcn = correct_gamma(dat)
            lims = (0, 1)

        left, right = self.unit_x[0], self.unit_x[-1]
        bottom, top = self.unit_y[0], self.unit_y[-1]

        fig, ax = share_fig_ax(fig, ax)

        ax.imshow(fcn,
                  extent=[left, right, bottom, top],
                  interpolation=interp_method,
                  origin='lower')
        ax.set(xlabel=r'Image Plane X [$\mu m$]',
               ylabel=r'Image Plane Y [$\mu m$]',
               xlim=(-axlim, axlim),
               ylim=(-axlim, axlim))

        if pix_grid is not None:
            # if pixel grid is desired, add it
            mult = floor(axlim / pix_grid)
            gmin, gmax = -mult * pix_grid, mult * pix_grid
            pts = np.arange(gmin, gmax, pix_grid)
            ax.set_yticks(pts, minor=True)
            ax.set_xticks(pts, minor=True)
            ax.yaxis.grid(True, which='minor')
            ax.xaxis.grid(True, which='minor')

        return fig, ax
Пример #13
0
    def plot2d(self, axlim=25, power=1, interp_method='lanczos',
               pix_grid=None, fig=None, ax=None,
               show_axlabels=True, show_colorbar=True):
        ''' Creates a 2D plot of the PSF.

        Args:

            axlim (`float`): limits of axis, symmetric.
                xlim=(-axlim,axlim), ylim=(-axlim, axlim).

            power (`float`): power to stretch the data by for plotting.

            interp_method (`string`): method used to interpolate the image between
                samples of the PSF.

            pix_grid (`float`): if not None, overlays gridlines with spacing equal
                to pix_grid.  Intended to show the collection into camera pixels
                while still in the oversampled domain.

            fig (pyplot.figure): figure to plot in.

            ax (pyplot.axis): axis to plot in.

            show_axlabels (`bool`): whether or not to show the axis labels.

            show_colorbar (`bool`): whether or not to show the colorbar.

        Returns:
            pyplot.fig, pyplot.axis.  Figure and axis containing the plot.

        '''
        fcn = correct_gamma(self.data ** power)
        label_str = 'Normalized Intensity [a.u.]'
        lims = (0, 1)

        left, right = self.unit_x[0], self.unit_x[-1]
        bottom, top = self.unit_y[0], self.unit_y[-1]

        fig, ax = share_fig_ax(fig, ax)

        im = ax.imshow(fcn,
                       extent=[left, right, bottom, top],
                       origin='lower',
                       cmap='Greys_r',
                       interpolation=interp_method,
                       clim=lims)
        if show_colorbar:
            cb = fig.colorbar(im, label=label_str, ax=ax, fraction=0.046)
            cb.outline.set_edgecolor('k')
            cb.outline.set_linewidth(0.5)
        if show_axlabels:
            ax.set(xlabel=r'Image Plane $x$ [$\mu m$]',
                   ylabel=r'Image Plane $y$ [$\mu m$]')

        ax.set(xlim=(-axlim, axlim),
               ylim=(-axlim, axlim))

        if pix_grid is not None:
            # if pixel grid is desired, add it
            mult = floor(axlim / pix_grid)
            gmin, gmax = -mult * pix_grid, mult * pix_grid
            pts = np.arange(gmin, gmax, pix_grid)
            ax.set_yticks(pts, minor=True)
            ax.set_xticks(pts, minor=True)
            ax.yaxis.grid(True, which='minor', color='white', alpha=0.25)
            ax.xaxis.grid(True, which='minor', color='white', alpha=0.25)

        return fig, ax
Пример #14
0
    make_focus_range_realistic_number_of_microns,
    prepare_document_local,
    prepare_document_global,
)
from iris.recipes import opt_routine_lbfgsb, opt_routine_basinhopping
from iris.core import config_codex_params_to_pupil
from iris.rings import W1

efl, fno, lambda_ = 50, 2, 0.55
extinction = 1000 / (fno * lambda_)
DEFAULT_CONFIG = SimulationConfig(
    efl=efl,
    fno=fno,
    wvl=lambda_,
    samples=128,
    freqs=tuple(range(10, floor(extinction), 10)),
    focus_range_waves=1 / 2 * sqrt(3),  # waves / Zernike/Hopkins / norm(Z4)
    focus_zernike=True,
    focus_normed=True,
    focus_planes=21)
DEFAULT_CONFIG = make_focus_range_realistic_number_of_microns(
    DEFAULT_CONFIG, 5)


def run_simulation(truth=(0, 0.125, 0, 0),
                   guess=(0, 0.0, 0, 0),
                   cfg=None,
                   solver='global',
                   decoder_ring=None,
                   solver_opts=None,
                   core_opts=None):