<jupyter_start><jupyter_text>Can we develop phase retrieval code that can work with less than Nyquist data? We'll clearly be unable to estimate high frequency abberations (high frequency zernike modes) but we should be able to estimate the lower order ones which could still be useful for characterizing commercial / core scopes with hard to change magnifications.<jupyter_code>%pylab inline
import pyotf.otf
class HanserPSFSubsample(object):
    """A class defining the pupil function and its closely related methods.

    Based on the following work

    [(1) <NAME>.; <NAME>.; <NAME>.; <NAME>.
    Phase-Retrieved Pupil Functions in Wide-Field Fluorescence Microscopy.
    Journal of Microscopy 2004, 216 (1), 32–48.](dx.doi.org/10.1111/j.0022-2720.2004.01393.x)
    [(2) <NAME>.; <NAME>.; <NAME>.; <NAME>.
    Phase Retrieval for High-Numerical-Aperture Optical Systems.
    Optics Letters 2003, 28 (10), 801.](dx.doi.org/10.1364/OL.28.000801)
    """

    def __init__(self, wl, na, ni, res, size, **kwargs):
        """zrange : array-like
            An alternate way to specify the z range for the calculation
            must be expressed in the same units as wavelength
        """
        nyquist_res = 1 / (2 * self.na / self.wl) / 2
        self.ratio = np.ceil(res / nyquist_res).astype(int)
        self.res = res
        self.size = size
        self.nyquist = pyotf.otf.HanserPSF(wl, na, ni, res / ratio, size * ratio, **kwargs)
        
    # do a general getter/setter that redirects to wrapped class for everything
    # aside from res

    def _gen_kr(self):
        """Internal utiltiy to generate coordinate system and other internal
        parameters"""
        k = fftfreq(self.size, self.res)
        kxx, kyy = np.meshgrid(k, k)
        self._kr, self._phi = cart2pol(kyy, kxx)
        # kmag is the radius of the spherical shell of the OTF
        self._kmag = self.ni / self.wl
        # because the OTF only exists on a spherical shell we can calculate
        # a kz value for any pair of kx and ky values
        self._kz = psqrt(self._kmag**2 - self._kr**2)

    def _gen_pupil(self):
        """Generate an ideal pupil"""
        kr = self._kr
        # define the diffraction limit
        # remember we"re working with _coherent_ data _not_ intensity,
        # so drop the factor of 2
        diff_limit = self._na / self._wl
        # return a circle of intensity 1 over the ideal passband of the
        # objective make sure data is complex
        return (kr < diff_limit).astype(complex)

    def _calc_defocus(self):
        """Calculate the defocus to apply to the base pupil"""
        kz = self._kz
        return np.exp(2 * np.pi * 1j * kz *
                      self.zrange[:, np.newaxis, np.newaxis])

    def _gen_psf(self, pupil_base=None):
        """An internal utility that generates the PSF
        Kwargs
        ------
        pupil_base : ndarray
            provided so that phase retrieval algorithms can hook into this
            method.

        NOTE: that the internal state is created with fftfreq, which creates
        _unshifted_ frequences"""
        
        self._PSFa = self.nyquist.PSFa

    # Because the _attribute_changed() method sets all the internal OTFs and
    # PSFs None we can recalculate them only when needed
    @property
    def OTFa(self):
        if self._OTFa is None:
            self._OTFa = easy_fft(self.PSFa, axes=(1, 2, 3))
        return self._OTFa

    @property
    def PSFa(self):
        if self._PSFa is None:
            self._gen_psf()
        return self._PSFa
import scipy.ndimage as ndi
ndi.zoom?<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08940852819807428, 'fraction_numerical': 0.02063273727647868, 'mean_word_length': 2.3917910447761193, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '158873', 'license': 'Apache-2.0', 'n_tokens_mistral': 1183, 'n_tokens_neox': 1078, 'n_words': 397, 'path': 'notebooks/Phase Retrieval Development Tests/Phase Retrieval for less than Nyquist.ipynb', 'repo_name': 'jaimergp/pyotf', 'size': '3693'}