Esempio n. 1
0
    def statistics(self,
                   sb_position=None,
                   sb='lower',
                   high_cf=False,
                   fringe_contrast_algorithm='statistical',
                   apodization='hanning',
                   single_values=True,
                   show_progressbar=False,
                   parallel=None):
        """
        Calculates following statistics for off-axis electron holograms:

        1. Fringe contrast using either statistical definition or
        Fourier space approach (see description of `fringe_contrast_algorithm` parameter)
        2. Fringe sampling (in pixels)
        3. Fringe spacing (in calibrated units)
        4. Carrier frequency (in calibrated units, radians and 1/px)

        Parameters
        ----------
        sb_position : tuple, :class:`~hyperspy.signals.Signal1D, None
            The sideband position (y, x), referred to the non-shifted FFT.
            It has to be tuple or to have the same dimensionality as the hologram.
            If None, sideband is determined automatically from FFT.
        sb : str, None
            Select which sideband is selected. 'upper', 'lower', 'left' or 'right'.
        high_cf : bool, optional
            If False, the highest carrier frequency allowed for the sideband location is equal to
            half of the Nyquist frequency (Default: False).
        fringe_contrast_algorithm : str
            Select fringe contrast algorithm between:

            'fourier'
                fringe contrast is estimated as:
                2 * <I(k_0)> / <I(0)>,
                where I(k_0) is intensity of sideband and I(0) is the intensity of central band (FFT origin).
                This method delivers also reasonable estimation if
                interference pattern do not cover full field of view.
            'statistical'
                fringe contrast is estimated by dividing standard deviation by mean
                of the hologram intensity in real space. This algorithm relays on that the fringes are regular and
                covering entire field of view.

            (Default: 'statistical')
        apodization: str or None, optional
            Used with `fringe_contrast_algorithm='fourier'`. If 'hanning' or 'hamming' apodization window
            will be applied in real space before FFT for estimation of fringe contrast.
            Apodization is typically needed to suppress striking  due to sharp edges of the image,
            which often results in underestimation of the fringe contrast. (Default: 'hanning')
        single_values : bool, optional
            If True calculates statistics only for the first navigation pixels and
            returns the values as single floats (Default: True)
        %s
        %s

        Returns
        -------
        statistics_dict :
            Dictionary with the statistics

        Examples
        --------
        >>> import hyperspy.api as hs
        >>> s = hs.datasets.example_signals.reference_hologram()
        >>> sb_position = s.estimate_sideband_position(high_cf=True)
        >>> s.statistics(sb_position=sb_position)
        {'Fringe spacing (nm)': 3.4860442674236256,
        'Carrier frequency (1/px)': 0.26383819985575441,
        'Carrier frequency (mrad)': 0.56475154609203482,
        'Fringe contrast': 0.071298357213623778,
        'Fringe sampling (px)': 3.7902017241882331,
        'Carrier frequency (1 / nm)': 0.28685808994016415}
        """

        # Testing match of navigation axes of reference and self
        # (exception: reference nav_dim=1):

        # Parsing sideband position:
        (sb_position,
         sb_position_temp) = _parse_sb_position(self, None, sb_position, sb,
                                                high_cf, parallel)

        # Calculate carrier frequency in 1/px and fringe sampling:
        fourier_sampling = 1. / np.array(self.axes_manager.signal_shape)
        if single_values:
            carrier_freq_px = calculate_carrier_frequency(
                _first_nav_pixel_data(self),
                sb_position=_first_nav_pixel_data(sb_position),
                scale=fourier_sampling)
        else:
            carrier_freq_px = self.map(calculate_carrier_frequency,
                                       sb_position=sb_position,
                                       scale=fourier_sampling,
                                       inplace=False,
                                       ragged=False,
                                       show_progressbar=show_progressbar,
                                       parallel=parallel)
        fringe_sampling = np.divide(1., carrier_freq_px)

        ureg = UnitRegistry()
        try:
            units = ureg.parse_expression(
                str(self.axes_manager.signal_axes[0].units))
        except UndefinedUnitError:
            raise ValueError('Signal axes units should be defined.')

        # Calculate carrier frequency in 1/units and fringe spacing in units:
        f_sampling_units = np.divide(1., [
            a * b for a, b in zip(self.axes_manager.signal_shape, (
                self.axes_manager.signal_axes[0].scale,
                self.axes_manager.signal_axes[1].scale))
        ])
        if single_values:
            carrier_freq_units = calculate_carrier_frequency(
                _first_nav_pixel_data(self),
                sb_position=_first_nav_pixel_data(sb_position),
                scale=f_sampling_units)
        else:
            carrier_freq_units = self.map(calculate_carrier_frequency,
                                          sb_position=sb_position,
                                          scale=f_sampling_units,
                                          inplace=False,
                                          ragged=False,
                                          show_progressbar=show_progressbar,
                                          parallel=parallel)
        fringe_spacing = np.divide(1., carrier_freq_units)

        # Calculate carrier frequency in mrad:
        try:
            ht = self.metadata.Acquisition_instrument.TEM.beam_energy
        except BaseException:
            raise AttributeError("Please define the beam energy."
                                 "You can do this e.g. by using the "
                                 "set_microscope_parameters method.")

        momentum = 2 * constants.m_e * constants.elementary_charge * ht * \
            1000 * (1 + constants.elementary_charge * ht *
                    1000 / (2 * constants.m_e * constants.c ** 2))
        wavelength = constants.h / np.sqrt(momentum) * 1e9  # in nm
        carrier_freq_quantity = wavelength * \
            ureg('nm') * carrier_freq_units / units * ureg('rad')
        carrier_freq_mrad = carrier_freq_quantity.to('mrad').magnitude

        # Calculate fringe contrast:
        if fringe_contrast_algorithm == 'fourier':
            if single_values:
                fringe_contrast = estimate_fringe_contrast_fourier(
                    _first_nav_pixel_data(self),
                    sb_position=_first_nav_pixel_data(sb_position),
                    apodization=apodization)
            else:
                fringe_contrast = self.map(estimate_fringe_contrast_fourier,
                                           sb_position=sb_position,
                                           apodization=apodization,
                                           inplace=False,
                                           ragged=False,
                                           show_progressbar=show_progressbar,
                                           parallel=parallel)
        elif fringe_contrast_algorithm == 'statistical':
            if single_values:
                fringe_contrast = _first_nav_pixel_data(
                    self).std() / _first_nav_pixel_data(self).mean()
            else:
                fringe_contrast = _estimate_fringe_contrast_statistical(self)
        else:
            raise ValueError(
                "fringe_contrast_algorithm can only be set to fourier or statistical."
            )

        return {
            'Fringe contrast': fringe_contrast,
            'Fringe sampling (px)': fringe_sampling,
            'Fringe spacing ({:~})'.format(units.units): fringe_spacing,
            'Carrier frequency (1/px)': carrier_freq_px,
            'Carrier frequency ({:~})'.format((1. / units).units):
            carrier_freq_units,
            'Carrier frequency (mrad)': carrier_freq_mrad
        }
Esempio n. 2
0
    def statistics(self,
                   sb_position=None,
                   sb='lower',
                   high_cf=False,
                   fringe_contrast_algorithm='statistical',
                   apodization='hanning',
                   single_values=True,
                   show_progressbar=False,
                   parallel=None):
        """
        Calculates following statistics for off-axis electron holograms:

        1. Fringe contrast using either statistical definition or
        Fourier space approach (see description of `fringe_contrast_algorithm` parameter)
        2. Fringe sampling (in pixels)
        3. Fringe spacing (in calibrated units)
        4. Carrier frequency (in calibrated units, radians and 1/px)

        Parameters
        ----------
        sb_position : tuple, :class:`~hyperspy.signals.Signal1D, None
            The sideband position (y, x), referred to the non-shifted FFT.
            It has to be tuple or to have the same dimensionality as the hologram.
            If None, sideband is determined automatically from FFT.
        sb : str, None
            Select which sideband is selected. 'upper', 'lower', 'left' or 'right'.
        high_cf : bool, optional
            If False, the highest carrier frequency allowed for the sideband location is equal to
            half of the Nyquist frequency (Default: False).
        fringe_contrast_algorithm : str
            Select fringe contrast algorithm between:

            'fourier'
                fringe contrast is estimated as:
                2 * <I(k_0)> / <I(0)>,
                where I(k_0) is intensity of sideband and I(0) is the intensity of central band (FFT origin).
                This method delivers also reasonable estimation if
                interference pattern do not cover full field of view.
            'statistical'
                fringe contrast is estimated by dividing standard deviation by mean
                of the hologram intensity in real space. This algorithm relays on that the fringes are regular and
                covering entire field of view.

            (Default: 'statistical')
        apodization: str or None, optional
            Used with `fringe_contrast_algorithm='fourier'`. If 'hanning' or 'hamming' apodization window
            will be applied in real space before FFT for estimation of fringe contrast.
            Apodization is typically needed to suppress striking  due to sharp edges of the image,
            which often results in underestimation of the fringe contrast. (Default: 'hanning')
        single_values : bool, optional
            If True calculates statistics only for the first navigation pixels and
            returns the values as single floats (Default: True)
        show_progressbar : bool, optional
            Shows progressbar while iterating over different slices of the
            signal (passes the parameter to map method). (Default: False)
        parallel : bool, None, optional
            Run the reconstruction in parallel

        Returns
        -------
        statistics_dict :
            Dictionary with the statistics

        Examples
        --------
        >>> import hyperspy.api as hs
        >>> s = hs.datasets.example_signals.reference_hologram()
        >>> sb_position = s.estimate_sideband_position(high_cf=True)
        >>> s.statistics(sb_position=sb_position)
        {'Fringe spacing (nm)': 3.4860442674236256,
        'Carrier frequency (1/px)': 0.26383819985575441,
        'Carrier frequency (mrad)': 0.56475154609203482,
        'Fringe contrast': 0.071298357213623778,
        'Fringe sampling (px)': 3.7902017241882331,
        'Carrier frequency (1 / nm)': 0.28685808994016415}
        """

        # Testing match of navigation axes of reference and self
        # (exception: reference nav_dim=1):

        # Parsing sideband position:
        (sb_position, sb_position_temp) = _parse_sb_position(
            self, None, sb_position, sb, high_cf, parallel)

        # Calculate carrier frequency in 1/px and fringe sampling:
        fourier_sampling = 1. / np.array(self.axes_manager.signal_shape)
        if single_values:
            carrier_freq_px = calculate_carrier_frequency(_first_nav_pixel_data(self),
                                                          sb_position=_first_nav_pixel_data(
                                                              sb_position),
                                                          scale=fourier_sampling)
        else:
            carrier_freq_px = self.map(calculate_carrier_frequency,
                                       sb_position=sb_position,
                                       scale=fourier_sampling,
                                       inplace=False,
                                       ragged=False,
                                       show_progressbar=show_progressbar,
                                       parallel=parallel)
        fringe_sampling = np.divide(1., carrier_freq_px)

        ureg = UnitRegistry()
        try:
            units = ureg.parse_expression(
                str(self.axes_manager.signal_axes[0].units))
        except UndefinedUnitError:
            raise ValueError('Signal axes units should be defined.')

        # Calculate carrier frequency in 1/units and fringe spacing in units:
        f_sampling_units = np.divide(
            1.,
            [a * b for a, b in
             zip(self.axes_manager.signal_shape,
                 (self.axes_manager.signal_axes[0].scale,
                  self.axes_manager.signal_axes[1].scale))]
        )
        if single_values:
            carrier_freq_units = calculate_carrier_frequency(_first_nav_pixel_data(self),
                                                             sb_position=_first_nav_pixel_data(
                                                                 sb_position),
                                                             scale=f_sampling_units)
        else:
            carrier_freq_units = self.map(calculate_carrier_frequency,
                                          sb_position=sb_position,
                                          scale=f_sampling_units,
                                          inplace=False,
                                          ragged=False,
                                          show_progressbar=show_progressbar,
                                          parallel=parallel)
        fringe_spacing = np.divide(1., carrier_freq_units)

        # Calculate carrier frequency in mrad:
        try:
            ht = self.metadata.Acquisition_instrument.TEM.beam_energy
        except BaseException:
            raise AttributeError("Please define the beam energy."
                                 "You can do this e.g. by using the "
                                 "set_microscope_parameters method.")

        momentum = 2 * constants.m_e * constants.elementary_charge * ht * \
            1000 * (1 + constants.elementary_charge * ht *
                    1000 / (2 * constants.m_e * constants.c ** 2))
        wavelength = constants.h / np.sqrt(momentum) * 1e9  # in nm
        carrier_freq_quantity = wavelength * \
            ureg('nm') * carrier_freq_units / units * ureg('rad')
        carrier_freq_mrad = carrier_freq_quantity.to('mrad').magnitude

        # Calculate fringe contrast:
        if fringe_contrast_algorithm == 'fourier':
            if single_values:
                fringe_contrast = estimate_fringe_contrast_fourier(_first_nav_pixel_data(self),
                                                                   sb_position=_first_nav_pixel_data(
                                                                       sb_position),
                                                                   apodization=apodization)
            else:
                fringe_contrast = self.map(estimate_fringe_contrast_fourier,
                                           sb_position=sb_position,
                                           apodization=apodization,
                                           inplace=False,
                                           ragged=False,
                                           show_progressbar=show_progressbar,
                                           parallel=parallel)
        elif fringe_contrast_algorithm == 'statistical':
            if single_values:
                fringe_contrast = _first_nav_pixel_data(
                    self).std() / _first_nav_pixel_data(self).mean()
            else:
                fringe_contrast = _estimate_fringe_contrast_statistical(self)
        else:
            raise ValueError(
                "fringe_contrast_algorithm can only be set to fourier or statistical.")

        return {'Fringe contrast': fringe_contrast,
                'Fringe sampling (px)': fringe_sampling,
                'Fringe spacing ({:~})'.format(units.units): fringe_spacing,
                'Carrier frequency (1/px)': carrier_freq_px,
                'Carrier frequency ({:~})'.format((1. / units).units): carrier_freq_units,
                'Carrier frequency (mrad)': carrier_freq_mrad}