def filter_vectors_detector_edge(self, exclude_width, *args, **kwargs):
        """Filter the diffraction vectors to accept only those not within a
        user specified proximity to the detector edge.

        Parameters
        ----------
        exclude_width : int
            The width of the region adjacent to the detector edge from which
            vectors will be excluded.
        *args:
            Arguments to be passed to map().
        **kwargs:
            Keyword arguments to map().

        Returns
        -------
        filtered_vectors : DiffractionVectors
            Diffraction vectors within allowed detector region.
        """
        x_threshold = self.pixel_calibration * (
            self.detector_shape[0] /
            2) - self.pixel_calibration * exclude_width
        y_threshold = self.pixel_calibration * (
            self.detector_shape[1] /
            2) - self.pixel_calibration * exclude_width
        # If ragged the signal axes will not be defined
        if len(self.axes_manager.signal_axes) == 0:
            filtered_vectors = self.map(filter_vectors_edge_ragged,
                                        x_threshold=x_threshold,
                                        y_threshold=y_threshold,
                                        inplace=False,
                                        *args,
                                        **kwargs)
            # Type assignment to DiffractionVectors for return
            filtered_vectors = DiffractionVectors(filtered_vectors)
            filtered_vectors.axes_manager.set_signal_dimension(0)
        # Otherwise easier to calculate.
        else:
            x_inbounds = np.absolute(
                self.data.T[0]) < x_threshold  #True if vector is good to go
            y_inbounds = np.absolute(self.data.T[1]) < y_threshold
            filtered_vectors = self.data[np.logical_and(
                x_inbounds, y_inbounds)]
            # Type assignment to DiffractionVectors for return
            filtered_vectors = DiffractionVectors(filtered_vectors)
            filtered_vectors.axes_manager.set_signal_dimension(1)

        transfer_navigation_axes(filtered_vectors, self)

        return filtered_vectors
Exemple #2
0
    def as_polar(self, dr=1.0, dt=None, jacobian=True, **kwargs):
        """Reprojects two-dimensional diffraction data from cartesian to polar
        coordinates.

        Parameters
        ----------
        dr : float
            Radial coordinate spacing for the grid interpolation
            tests show that there is not much point in going below 0.5
        dt : float
            Angular coordinate spacing (in radians). If ``dt=None``, dt is set
            such that the number of theta values is equal to the largest
            dimension of the data array.
        jacobian : boolean
            Include ``r`` intensity scaling in the coordinate transform.
            This should be included to account for the changing pixel size that
            occurs during the transform.
        **kwargs : keyord arguments
            Keyword arguments passed to the hyperspy map function.

        Returns
        -------
        polar : PolarDiffraction2D
            Two-dimensional diffraction data in polar coordinates (k, theta).

        """
        polar = self.map(reproject_polar,
                         dr=dr,
                         dt=dt,
                         jacobian=jacobian,
                         inplace=False,
                         **kwargs)
        # Assign to appropriate signal
        polar.set_signal_type("polar_diffraction")
        # Transfer navigation_axes
        transfer_navigation_axes(polar, self)
        # Set signal axes parameters (Theta)
        polar_t_axis = polar.axes_manager.signal_axes[0]
        polar_t_axis.name = "theta"
        polar_t_axis.scale = 2 * np.pi / polar_t_axis.size
        polar_t_axis.units = "$rad$"
        # Set signal axes parameters (magnitude)
        polar_k_axis = polar.axes_manager.signal_axes[1]
        polar_k_axis.name = "k"
        polar_k_axis.scale = 2 * np.pi / polar_k_axis.size
        polar_k_axis.units = "$rad$"

        return polar
Exemple #3
0
    def get_crystallographic_map(self, *args, **kwargs):
        """Obtain a crystallographic map specifying the best matching phase and
        orientation at each probe position with corresponding metrics.

        Returns
        -------
        cryst_map : CrystallographicMap
            Crystallographic mapping results containing the best matching phase
            and orientation at each navigation position with associated metrics.

            The Signal at each navigation position is an array of,

                            [phase, np.array((z,x,z)), dict(metrics)]

            which defines the phase, orientation as Euler angles in the zxz
            convention and metrics associated with the matching.

            Metrics for template matching results are
                'correlation'
                'orientation_reliability'
                'phase_reliability'

        """
        # TODO: Add alternative methods beyond highest correlation score.
        crystal_map = self.map(crystal_from_template_matching,
                               inplace=False,
                               *args,
                               **kwargs)

        cryst_map = CrystallographicMap(crystal_map)
        cryst_map = transfer_navigation_axes(cryst_map, self)
        cryst_map.method = 'template_matching'

        return cryst_map
Exemple #4
0
    def get_radial_profile(self, inplace=False, **kwargs):
        """Return the radial profile of the diffraction variance signals.

        Returns
        -------
        DiffractionVariance1D
            radial_profile: :obj:`pyxem.signals.DiffractionVariance1D`
            The radial profile of each diffraction variance pattern in the
            DiffractionVariance2D signal.

        See also
        --------
        :func:`pyxem.utils.expt_utils.radial_average`

        Examples
        --------
        .. code-block:: python
            profiles = ed.get_radial_profile()
            profiles.plot()
        """
        radial_profiles = self.map(radial_average, inplace=inplace, **kwargs)

        radial_profiles.axes_manager.signal_axes[0].offset = 0
        signal_axis = radial_profiles.axes_manager.signal_axes[0]

        rp = DiffractionVariance1D(radial_profiles.as_signal1D(signal_axis))
        rp = transfer_navigation_axes(rp, self)
        rp_axis = rp.axes_manager.signal_axes[0]
        rp_axis.name = 'q'
        rp_axis.scale = self.axes_manager.signal_axes[0].scale
        rp_axis.units = '$A^{-1}$'

        return rp
Exemple #5
0
    def get_reduced_intensity(self):
        """Obtains a reduced intensity profile from the radial profile.

        Parameters
        ----------
        s_cutoff : list of float
                    A list of the form [s_min, s_max] to change the s_cutoff
                    from the fit.
        Returns
        -------
        ri : ReducedIntensity1D
        """

        s_scale = self.signal.axes_manager.signal_axes[0].scale
        s = np.arange(self.signal.axes_manager.signal_axes[0].size,
                      dtype='float64')
        s *= self.signal.axes_manager.signal_axes[0].scale

        reduced_intensity = (2 * np.pi * s * np.divide(
            (self.signal.data - self.background_fit), self.normalisation))

        ri = ReducedIntensity1D(reduced_intensity)
        ri = transfer_navigation_axes(ri, self.signal)
        ri = transfer_signal_axes(ri, self.signal)

        return ri
Exemple #6
0
    def get_crystallographic_map(self, *args, **kwargs):
        """Obtain a crystallographic map specifying the best matching phase and
        orientation at each probe position with corresponding metrics.

        Returns
        -------
        cryst_map : Signal2D
            Crystallographic mapping results containing the best matching phase
            and orientation at each navigation position with associated metrics.
            The Signal at each navigation position is an array of,
                            [phase, np.array((z,x,z)), dict(metrics)]
            which defines the phase, orientation as Euler angles in the zxz
            convention and metrics associated with the matching.
            Metrics for template matching results are
                'match_rate'
                'total_error'
                'orientation_reliability'
                'phase_reliability'
        """
        crystal_map = self.map(crystal_from_vector_matching,
                               inplace=False,
                               *args,
                               **kwargs)

        crystal_map = transfer_navigation_axes(crystal_map, self)
        return crystal_map
Exemple #7
0
    def get_pdf(self,
                s_min,
                s_max=None,
                r_min=0,
                r_max=20,
                r_increment=0.01
                ):
        """ Calculates the pdf from the reduced intensity signal.

        Parameters
        ----------
        s_min : float
                    Minimum scattering vector s for the pdf calculation. Note that s is
                    defined here as s = 2 sin(theta)/lambda = 1/d.
        s_max : float
                    Maximum scattering vector s for the pdf calculation. Note that s is
                    defined here as s = 2 sin(theta)/lambda = 1/d.
        r_cutoff : list of float
                    A list with the format [<r_min>, <r_max>], which sets the
                    limits of the real space axis in the calculated PDF.
        r_increment : float
                    Step size in r in the extracted PDF.
        Returns
        -------
        pdf : PDF1D
                    A signal of pair distribution functions.
        """
        s_scale = self.signal.axes_manager.signal_axes[0].scale
        if s_max is None:
            s_max = self.signal.axes_manager.signal_axes[0].size * s_scale
            print('s_max set to maximum of signal.')

        r_values = np.arange(r_min, r_max, r_increment)
        r_values = r_values.reshape(1, r_values.size)
        s_limits = [int(s_min / s_scale), int(s_max / s_scale)]

        #check that these aren't out of bounds
        if s_limits[1] > self.signal.axes_manager.signal_axes[0].size:
            raise ValueError('User specified s_max is larger than the maximum '
                    'scattering vector magnitude in the data. Please reduce '
                    's_max or use s_max=None to use the full scattering range.')
        s_values = np.arange(s_limits[0], s_limits[1], 1) * s_scale
        s_values = s_values.reshape(s_values.size, 1)  # column vector

        limited_red_int = self.signal.isig[s_limits[0]:s_limits[1]].data

        pdf_sine = np.sin(2 * np.pi * np.matmul(s_values,r_values))
        # creates a vector of the pdf
        rpdf = PairDistributionFunction1D(8 * np.pi * s_scale
                                    * np.matmul(limited_red_int,pdf_sine))

        signal_axis = rpdf.axes_manager.signal_axes[0]
        pdf_scaling = r_increment
        signal_axis.scale = pdf_scaling
        signal_axis.name = 'Radius r'
        signal_axis.units = '$Å$'
        rpdf = transfer_navigation_axes(rpdf,self.signal)

        return rpdf
    def filter_vectors_magnitudes(self, min_magnitude, max_magnitude, *args,
                                  **kwargs):
        """Filter the diffraction vectors to accept only those with magnitudes
        within a user specified range.

        Parameters
        ----------
        min_magnitude : float
            Minimum allowed vector magnitude.
        max_magnitude : float
            Maximum allowed vector magnitude.
        *args:
            Arguments to be passed to map().
        **kwargs:
            Keyword arguments to map().

        Returns
        -------
        filtered_vectors : DiffractionVectors
            Diffraction vectors within allowed magnitude tolerances.
        """
        # If ragged the signal axes will not be defined
        if len(self.axes_manager.signal_axes) == 0:
            filtered_vectors = self.map(filter_vectors_ragged,
                                        min_magnitude=min_magnitude,
                                        max_magnitude=max_magnitude,
                                        inplace=False,
                                        *args,
                                        **kwargs)
            # Type assignment to DiffractionVectors for return
            filtered_vectors = DiffractionVectors(filtered_vectors)
            filtered_vectors.axes_manager.set_signal_dimension(0)
        # Otherwise easier to calculate.
        else:
            magnitudes = self.get_magnitudes()
            magnitudes.data[magnitudes.data < min_magnitude] = 0
            magnitudes.data[magnitudes.data > max_magnitude] = 0
            filtered_vectors = self.data[np.where(magnitudes)]
            # Type assignment to DiffractionVectors for return
            filtered_vectors = DiffractionVectors(filtered_vectors)
            filtered_vectors.axes_manager.set_signal_dimension(1)

        transfer_navigation_axes(filtered_vectors, self)

        return filtered_vectors
    def index_vectors(self, mag_tol, angle_tol, index_error_tol,
                      n_peaks_to_index, n_best, *args, **kwargs):
        """Assigns hkl indices to diffraction vectors.

        Parameters
        ----------
        mag_tol : float
            The maximum absolute error in diffraction vector magnitude, in units
            of reciprocal Angstroms, allowed for indexation.
        angle_tol : float
            The maximum absolute error in inter-vector angle, in units of
            degrees, allowed for indexation.
        index_error_tol : float
            Max allowed error in peak indexation for classifying it as indexed,
            calculated as :math:`|hkl_calculated - round(hkl_calculated)|`.
        n_peaks_to_index : int
            The maximum number of peak to index.
        n_best : int
            The maximum number of good solutions to be retained.
        *args : arguments
            Arguments passed to the map() function.
        **kwargs : arguments
            Keyword arguments passed to the map() function.

        Returns
        -------
        indexation_results : VectorMatchingResults
            Navigation axes of the diffraction vectors signal containing vector
            indexation results for each probe position.
        """
        vectors = self.vectors
        library = self.library

        matched = vectors.cartesian.map(match_vectors,
                                        library=library,
                                        mag_tol=mag_tol,
                                        angle_tol=np.deg2rad(angle_tol),
                                        index_error_tol=index_error_tol,
                                        n_peaks_to_index=n_peaks_to_index,
                                        n_best=n_best,
                                        inplace=False,
                                        *args,
                                        **kwargs)
        indexation = matched.isig[0]
        rhkls = matched.isig[1].data

        indexation_results = VectorMatchingResults(indexation)
        indexation_results.vectors = vectors
        indexation_results.hkls = rhkls
        indexation_results = transfer_navigation_axes(indexation_results,
                                                      vectors.cartesian)

        vectors.hkls = rhkls

        return indexation_results
    def correlate(self, n_largest=5, mask=None, *args, **kwargs):
        """Correlates the library of simulated diffraction patterns with the
        electron diffraction signal.

        Parameters
        ----------
        n_largest : int
            The n orientations with the highest correlation values are returned.
        mask : Array
            Array with the same size as signal (in navigation) or None
        *args : arguments
            Arguments passed to map().
        **kwargs : arguments
            Keyword arguments passed map().

        Returns
        -------
        matching_results : TemplateMatchingResults
            Navigation axes of the electron diffraction signal containing
            correlation results for each diffraction pattern, in the form
            [Library Number , [z, x, z], Correlation Score]

        """
        signal = self.signal
        library = self.library

        if mask is None:
            # Index at all real space pixels
            mask = 1

        # TODO: Add extra methods
        no_extra_methods_yet = True
        if no_extra_methods_yet:
            # adds a normalisation to library
            for phase in library.keys():
                norm_array = np.ones(
                    library[phase]
                    ['intensities'].shape[0])  # will store the norms
                for i, intensity_array in enumerate(
                        library[phase]['intensities']):
                    norm_array[i] = np.linalg.norm(intensity_array)
                library[phase][
                    'pattern_norms'] = norm_array  # puts this normalisation into the library

            matches = signal.map(correlate_library,
                                 library=library,
                                 n_largest=n_largest,
                                 mask=mask,
                                 inplace=False,
                                 **kwargs)

        matching_results = TemplateMatchingResults(matches)
        matching_results = transfer_navigation_axes(matching_results, signal)

        return matching_results
Exemple #11
0
    def calculate_cartesian_coordinates(self, accelerating_voltage,
                                        camera_length, *args, **kwargs):
        """Get cartesian coordinates of the diffraction vectors.

        Parameters
        ----------
        accelerating_voltage : float
            The acceleration voltage with which the data was acquired.
        camera_length : float
            The camera length in meters.
        """
        # Imported here to avoid circular dependency
        from diffsims.utils.sim_utils import get_electron_wavelength
        wavelength = get_electron_wavelength(accelerating_voltage)
        self.cartesian = self.map(
            detector_to_fourier,
            wavelength=wavelength,
            camera_length=camera_length * 1e10,
            inplace=False,
            parallel=False,  # TODO: For testing
            *args,
            **kwargs)
        transfer_navigation_axes(self.cartesian, self)
    def refine_n_best_orientations(
        self,
        orientations,
        accelarating_voltage,
        camera_length,
        n_best=0,
        rank=0,
        index_error_tol=0.2,
        vary_angles=True,
        vary_center=False,
        vary_scale=False,
        method="leastsq",
    ):
        """Refines the best orientation and assigns hkl indices to diffraction vectors.

        Parameters
        ----------
        orientations : VectorMatchingResults
            List of orientations to refine, must be an instance of `VectorMatchingResults`.
        accelerating_voltage : float
            The acceleration voltage with which the data was acquired.
        camera_length : float
            The camera length in meters.
        n_best : int
            Refine the best `n` orientations starting from `rank`.
            With `n_best=0` (default), all orientations are refined.
        rank : int
            The rank of the solution to start from.
        index_error_tol : float
            Max allowed error in peak indexation for classifying it as indexed,
            calculated as :math:`|hkl_calculated - round(hkl_calculated)|`.
        method : str
            Minimization algorithm to use, choose from:
            'leastsq', 'nelder', 'powell', 'cobyla', 'least-squares'.
            See `lmfit` documentation (https://lmfit.github.io/lmfit-py/fitting.html)
            for more information.
        vary_angles : bool,
            Free the euler angles (rotation matrix) during the refinement.
        vary_center : bool
            Free the center of the diffraction pattern (beam center) during the refinement.
        vary_scale : bool
            Free the scale (i.e. pixel size) of the diffraction vectors during refinement.

        Returns
        -------
        indexation_results : VectorMatchingResults
            Navigation axes of the diffraction vectors signal containing vector
            indexation results for each probe position.
        """
        vectors = self.vectors
        library = self.library

        matched = orientations.map(
            _refine_best_orientations,
            vectors=vectors,
            library=library,
            accelarating_voltage=accelarating_voltage,
            camera_length=camera_length,
            n_best=n_best,
            rank=rank,
            method="leastsq",
            verbose=False,
            vary_angles=vary_angles,
            vary_center=vary_center,
            vary_scale=vary_scale,
            inplace=False,
            parallel=False,
        )

        indexation = matched.isig[0]
        rhkls = matched.isig[1].data

        indexation_results = VectorMatchingResults(indexation)
        indexation_results.vectors = vectors
        indexation_results.hkls = rhkls
        indexation_results = transfer_navigation_axes(indexation_results,
                                                      vectors.cartesian)

        return indexation_results
Exemple #13
0
    def get_azimuthal_integral(self,
                               origin,
                               detector,
                               detector_distance,
                               wavelength,
                               size_1d,
                               unit='k_A^-1',
                               inplace=False,
                               kwargs_for_map={},
                               kwargs_for_integrator={},
                               kwargs_for_integrate1d={}):
        """
        Returns the azimuthal integral of the diffraction pattern as a
        Diffraction1D signal.

        Parameters
        ----------
        origin : np.array_like
            This parameter should either be a list or numpy.array with two
            coordinates ([x_origin,y_origin]), or an array of the same shape as
            the navigation axes, with an origin (with the shape
            [x_origin,y_origin]) at each navigation location.
        detector : pyFAI.detectors.Detector object
            A pyFAI detector used for the AzimuthalIntegrator.
        detector_distance : float
            Detector distance in meters passed to pyFAI AzimuthalIntegrator.
        wavelength : float
            The electron wavelength in meters. Used by pyFAI AzimuthalIntegrator
        size_1d : int
            The size of the returned 1D signal. (i.e. number of pixels in the
            1D azimuthal integral.)
        unit : str
            The unit for for PyFAI integrate1d. The default "k_A^-1" gives k in
            inverse Angstroms and is not natively in PyFAI. The other options
            are from PyFAI and are can be "q_nm^-1", "q_A^-1", "2th_deg",
            "2th_rad", and "r_mm".
        inplace : bool
            If True (default False), this signal is overwritten. Otherwise,
            returns anew signal.
        kwargs_for_map : dictionary
            Keyword arguments to be passed to self.map().
        kwargs_for_integrator : dictionary
            Keyword arguments to be passed to pyFAI AzimuthalIntegrator().
        kwargs_for_integrate1d : dictionary
            Keyword arguments to be passed to pyFAI ai.integrate1d().


        Returns
        -------
        radial_profile: :obj:`pyxem.signals.ElectronDiffraction1D`
            The radial average profile of each diffraction pattern in the
            ElectronDiffraction2D signal as an ElectronDiffraction1D.

        See also
        --------
        :func:`pyxem.utils.expt_utils.azimuthal_integrate`
        :func:`pyxem.utils.expt_utils.azimuthal_integrate_fast`
        """

        # Scaling factor is used to output the unit in k instead of q.
        # It multiplies the scale that comes out of pyFAI integrate1d
        scaling_factor = 1
        if unit == 'k_A^-1':
            scaling_factor = 1 / 2 / np.pi
            unit = 'q_A^-1'

        if np.array(origin).size == 2:
            # single origin
            # The AzimuthalIntegrator can be defined once and repeatedly used,
            # making for a fast integration
            # this uses azimuthal_integrate_fast

            p1, p2 = origin[0] * detector.pixel1, origin[1] * detector.pixel2
            ai = AzimuthalIntegrator(dist=detector_distance,
                                     poni1=p1,
                                     poni2=p2,
                                     detector=detector,
                                     wavelength=wavelength,
                                     **kwargs_for_integrator)

            azimuthal_integrals = self.map(
                azimuthal_integrate_fast,
                azimuthal_integrator=ai,
                size_1d=size_1d,
                unit=unit,
                inplace=inplace,
                kwargs_for_integrate1d=kwargs_for_integrate1d,
                **kwargs_for_map)

        else:
            # this time each centre is read in origin
            # origin is passed as a flattened array in the navigation dimensions
            azimuthal_integrals = self._map_iterate(
                azimuthal_integrate,
                iterating_kwargs=(('origin', origin.reshape(-1, 2)), ),
                detector_distance=detector_distance,
                detector=detector,
                wavelength=wavelength,
                size_1d=size_1d,
                unit=unit,
                inplace=inplace,
                kwargs_for_integrator=kwargs_for_integrator,
                kwargs_for_integrate1d=kwargs_for_integrate1d,
                **kwargs_for_map)

        if len(azimuthal_integrals.data.shape) == 3:
            ap = Diffraction1D(azimuthal_integrals.data[:, 1, :])
            tth = azimuthal_integrals.data[0, 0, :]  # tth is the signal axis
        else:
            ap = Diffraction1D(azimuthal_integrals.data[:, :, 1, :])
            tth = azimuthal_integrals.data[0, 0,
                                           0, :]  # tth is the signal axis
        scale = (tth[1] - tth[0]) * scaling_factor
        offset = tth[0] * scaling_factor
        ap.axes_manager.signal_axes[0].scale = scale
        ap.axes_manager.signal_axes[0].offset = offset
        ap.axes_manager.signal_axes[0].name = 'scattering'
        ap.axes_manager.signal_axes[0].units = unit

        transfer_navigation_axes(ap, self)
        push_metadata_through(ap, self)

        return ap
Exemple #14
0
    def correlate(
        self,
        n_largest=5,
        method="fast_correlation",
        mask=None,
        print_help=False,
        *args,
        **kwargs,
    ):
        """Correlates the library of simulated diffraction patterns with the
        electron diffraction signal.

        Parameters
        ----------
        n_largest : int
            The n orientations with the highest correlation values are returned.
        method : str
            Name of method used to compute correlation between templates and diffraction patterns. Can be
            'fast_correlation', 'full_frame_correlation' or 'zero_mean_normalized_correlation'.
        mask : Array
            Array with the same size as signal (in navigation) or None
        print_help : bool
            Display information about the method used.
        *args : arguments
            Arguments passed to map().
        **kwargs : arguments
            Keyword arguments passed map().

        Returns
        -------
        matching_results : TemplateMatchingResults
            Navigation axes of the electron diffraction signal containing
            correlation results for each diffraction pattern, in the form
            [Library Number , [z, x, z], Correlation Score]

        """
        signal = self.signal
        library = self.library

        method_dict = {
            "fast_correlation": fast_correlation,
            "zero_mean_normalized_correlation":
            zero_mean_normalized_correlation,
            "full_frame_correlation": full_frame_correlation,
        }

        if mask is None:
            # Index at all real space pixels
            mask = 1

        # tests if selected method is a valid argument, and can print help for selected method.
        chosen_function = select_method_from_method_dict(
            method, method_dict, print_help)
        if method in ["fast_correlation", "zero_mean_normalized_correlation"]:
            # adds a normalisation to library
            for phase in library.keys():
                norm_array = np.ones(
                    library[phase]
                    ["intensities"].shape[0])  # will store the norms

                for i, intensity_array in enumerate(
                        library[phase]["intensities"]):
                    norm_array[i] = np.linalg.norm(intensity_array)
                library[phase][
                    "pattern_norms"] = norm_array  # puts this normalisation into the library

            matches = signal.map(
                correlate_library,
                library=library,
                n_largest=n_largest,
                method=method,
                mask=mask,
                inplace=False,
                **kwargs,
            )

        elif method in ["full_frame_correlation"]:
            shape = signal.data.shape[-2:]
            size = 2 * np.array(shape) - 1
            fsize = [optimal_fft_size(a, real=True) for a in (size)]
            if not (np.asarray(size) + 1 == np.asarray(fsize)).all():
                raise ValueError(
                    "Please select input signal and templates of dimensions 2**n X 2**n"
                )

            library_FT_dict = get_library_FT_dict(library, shape, fsize)

            matches = signal.map(
                correlate_library_from_dict,
                template_dict=library_FT_dict,
                n_largest=n_largest,
                method=method,
                mask=mask,
                inplace=False,
                **kwargs,
            )

        matching_results = TemplateMatchingResults(matches)
        matching_results = transfer_navigation_axes(matching_results, signal)

        return matching_results
    def correlate(self,
                  n_largest=5,
                  method='fast_correlation',
                  mask=None,
                  print_help=False,
                  *args,
                  **kwargs):
        """Correlates the library of simulated diffraction patterns with the
        electron diffraction signal.

        Parameters
        ----------
        n_largest : int
            The n orientations with the highest correlation values are returned.
        method : str
            Name of method used to compute correlation between templates and diffraction patterns. Can be
            'fast_correlation' or 'zero_mean_normalized_correlation'.
        mask : Array
            Array with the same size as signal (in navigation) or None
        print_help : bool
            Display information about the method used.
        *args : arguments
            Arguments passed to map().
        **kwargs : arguments
            Keyword arguments passed map().

        Returns
        -------
        matching_results : TemplateMatchingResults
            Navigation axes of the electron diffraction signal containing
            correlation results for each diffraction pattern, in the form
            [Library Number , [z, x, z], Correlation Score]

        """
        signal = self.signal
        library = self.library

        method_dict = {
            'fast_correlation': fast_correlation,
            'zero_mean_normalized_correlation':
            zero_mean_normalized_correlation
        }

        if mask is None:
            # Index at all real space pixels
            mask = 1

        #tests if selected method is a valid argument, and can print help for selected method.
        chosen_function = select_method_from_method_dict(
            method, method_dict, print_help)

        # adds a normalisation to library
        for phase in library.keys():
            norm_array = np.ones(
                library[phase]['intensities'].shape[0])  # will store the norms

            for i, intensity_array in enumerate(library[phase]['intensities']):
                norm_array[i] = np.linalg.norm(intensity_array)
            library[phase][
                'pattern_norms'] = norm_array  # puts this normalisation into the library

        matches = signal.map(correlate_library,
                             library=library,
                             n_largest=n_largest,
                             method=method,
                             mask=mask,
                             inplace=False,
                             **kwargs)

        matching_results = TemplateMatchingResults(matches)
        matching_results = transfer_navigation_axes(matching_results, signal)

        return matching_results
Exemple #16
0
    def correlate(self,
                  n_largest=5,
                  mask=None,
                  inplane_rotations=np.arange(0, 360, 1),
                  max_peaks=100,
                  *args,
                  **kwargs):
        """Correlates the library of simulated diffraction patterns with the
        electron diffraction signal.

        Parameters
        ----------
        n_largest : int
            The n orientations with the highest correlation values are returned.
        mask : Array
            Array with the same size as signal (in navigation) True False
        inplane_rotations : ndarray
            Array of inplane rotation angles in degrees. Defaults to 0-360 degrees
            at 1 degree resolution.
        max_peaks : int
            Maximum number of peaks to consider when comparing a template to
            the diffraction pattern. The strongest peaks are kept.
        *args : arguments
            Arguments passed to map().
        **kwargs : arguments
            Keyword arguments passed map().

        Returns
        -------
        matching_results : TemplateMatchingResults
            Navigation axes of the electron diffraction signal containing
            correlation results for each diffraction pattern, in the form
            [Library Number , [z, x, z], Correlation Score]

        """
        signal = self.signal
        library = self.library
        inplane_rotations = np.deg2rad(inplane_rotations)
        num_inplane_rotations = inplane_rotations.shape[0]
        sig_shape = signal.axes_manager.signal_shape
        signal_half_width = sig_shape[0] / 2

        if mask is None:
            # Index at all real space pixels
            mask = 1

        # Create a copy of the library, cropping and padding the peaks to match
        # max_peaks. Also create rotated pixel coordinates according to
        # inplane_rotations
        rotation_matrices_2d = np.array([[[np.cos(t), np.sin(t)],
                                          [-np.sin(t), np.cos(t)]]
                                         for t in inplane_rotations])
        cropped_library = {}

        for phase_name, phase_entry in library.items():
            num_orientations = len(phase_entry['orientations'])
            intensities_jagged = phase_entry['intensities']
            intensities = np.zeros((num_orientations, max_peaks))
            pixel_coords_jagged = phase_entry['pixel_coords']
            pixel_coords = np.zeros(
                (num_inplane_rotations, num_orientations, max_peaks, 2))
            for i in range(num_orientations):
                num_peaks = min(pixel_coords_jagged[i].shape[0], max_peaks)
                highest_intensity_indices = np.argpartition(
                    intensities_jagged[i], -num_peaks)[-num_peaks:]
                intensities[i, :num_peaks] = intensities_jagged[i][
                    highest_intensity_indices]
                # Get and compute pixel coordinates for all rotations about the
                # center, clipped to the detector size and rounded to integer positions.
                pixel_coords[:, i, :num_peaks] = np.clip(
                    (signal_half_width + rotation_matrices_2d
                     @ (pixel_coords_jagged[i][highest_intensity_indices].T -
                        signal_half_width)).transpose(0, 2, 1),
                    a_min=0,
                    a_max=np.array(sig_shape) - 1)

            np.rint(pixel_coords, out=pixel_coords)
            cropped_library[phase_name] = {
                'orientations': phase_entry['orientations'],
                'pixel_coords': pixel_coords.astype('int'),
                'intensities': intensities,
                'pattern_norms': np.linalg.norm(intensities, axis=1),
            }

        matches = signal.map(correlate_library,
                             library=cropped_library,
                             n_largest=n_largest,
                             mask=mask,
                             inplace=False,
                             **kwargs)

        matching_results = TemplateMatchingResults(matches)
        matching_results = transfer_navigation_axes(matching_results, signal)

        return matching_results
Exemple #17
0
    def get_azimuthal_integral2d(
        self,
        npt_rad,
        npt_azim=360,
        center=None,
        affine=None,
        mask=None,
        radial_range=None,
        azimuth_range=None,
        wavelength=None,
        unit="pyxem",
        inplace=False,
        method="splitpixel",
        map_kwargs={},
        detector=None,
        detector_dist=None,
        correctSolidAngle=True,
        ai_kwargs={},
        integrate2d_kwargs={},
    ):
        """Creates a polar reprojection using pyFAI's azimuthal integrate 2d.

        This function is designed to be fairly flexible to account for 2 different cases:

        1 - If the unit is "pyxem" then it lets pyXEM take the lead. If wavelength is none in that case
        it doesn't account for the Ewald sphere.

        2 - If unit is any of the options from pyFAI then detector cannot be None and the handling of
        units is passed to pyxem and those units are used.

        Parameters
        ---------------
        npt_rad: int
            The number of radial points to calculate
        npt_azim: int
            The number of azimuthal points to calculate
        center: None or (x,y) or BaseSignal
            The center of the pattern in pixels to preform the integration around
        affine: 3x3 array or BaseSignal
            An affine transformation to apply during the transformation
             (creates a spline map that is used by pyFAI)
        mask:  boolean array or BaseSignal
            A boolean mask to apply to the data to exclude some points.
            If mask is a baseSignal then it is itereated over as well.
        radial_range: None or (float, float)
            The radial range over which to perform the integration. Default is
            the full frame
        azim_range:None or (float, float)
            The azimuthal range over which to perform the integration. Default is
            from -pi to pi
        wavelength: None or float
            The wavelength of for the microscope. Has to be in the same units as the pyxem units if you want
            it to properly work.
        unit: str
            The unit can be "pyxem" to use the pyxem units and “q_nm^-1”, “q_A^-1”, “2th_deg”, “2th_rad”, “r_mm”
            if pyFAI is used for unit handling
        inplace: bool
            If the signal is overwritten or copied to a new signal
        detector: pyFai.detector.Detector
            The detector set up to be used by the integrator
        detector_dist: float
            distance sample - detector plan (orthogonal distance, not along the beam), in meter.
        map_kwargs: dict
            Any other keyword arguments for hyperspys map function
        integrate2d_kwargs:dict
            Any keyword arguements for PyFAI's integrate2d function

        Returns
        ----------
        polar: PolarDiffraction2D
            A polar diffraction signal

        Examples
        ----------
        Basic case using "2th_deg" units (no wavelength needed)

        >>> ds.unit = "2th_deg"
        >>> ds.get_azimuthal_integral2d(npt_rad=100)

        Basic case using a curved Ewald Sphere approximation and pyXEM units
        (wavelength needed)

        >>> ds.unit = "k_nm^-1" # setting units
        >>> ds.get_azimuthal_integral1d(npt_rad=100, wavelength=2.5e-12)

        Using pyFAI to define a detector case using a curved Ewald Sphere approximation and pyXEM units

        >>> from pyFAI.detectors import Detector
        >>> det = Detector(pixel1=1e-4, pixel2=1e-4)
        >>> ds.get_azimuthal_integral1d(npt_rad=100, detector_dist=.2, detector= det, wavelength=2.508e-12)
        """
        pyxem_units = False
        sig_shape = self.axes_manager.signal_shape

        if unit == "pyxem":
            pyxem_units = True
            pixel_scale = [
                self.axes_manager.signal_axes[0].scale,
                self.axes_manager.signal_axes[1].scale,
            ]
            if wavelength is None and self.unit not in ["2th_deg", "2th_rad"]:
                print('if the unit is not "2th_deg", "2th_rad"'
                      "then a wavelength must be given. ")
                return
            setup = _get_setup(wavelength, self.unit, pixel_scale,
                               radial_range)
            detector, detector_dist, radial_range, unit, scale_factor = setup
        use_iterate = any([
            isinstance(mask, BaseSignal),
            isinstance(affine, BaseSignal),
            isinstance(center, BaseSignal),
        ])
        if use_iterate:
            if radial_range is None:  # need consistent range
                if isinstance(center, BaseSignal):
                    ind = (0, ) * len(self.axes_manager.navigation_shape)
                    cen = center.inav[ind].data
                else:
                    cen = center
                ai = get_azimuthal_integrator(
                    detector=detector,
                    detector_distance=detector_dist,
                    shape=sig_shape,
                    center=cen,
                    wavelength=wavelength,
                )  # take 1st center
                radial_range = _get_radial_extent(ai=ai,
                                                  shape=sig_shape,
                                                  unit=unit)
                radial_range[0] = 0
            integration = self.map(azimuthal_integrate2d_slow,
                                   npt_azim=npt_azim,
                                   detector=detector,
                                   center=center,
                                   mask=mask,
                                   affine=affine,
                                   detector_distance=detector_dist,
                                   npt_rad=npt_rad,
                                   wavelength=wavelength,
                                   radial_range=radial_range,
                                   azimuth_range=azimuth_range,
                                   inplace=inplace,
                                   unit=unit,
                                   method=method,
                                   correctSolidAngle=correctSolidAngle,
                                   **integrate2d_kwargs,
                                   **map_kwargs)  # Uses slow methodology

        else:  # much simpler and no changing integrator without using map iterate
            ai = get_azimuthal_integrator(detector=detector,
                                          detector_distance=detector_dist,
                                          shape=sig_shape,
                                          center=center,
                                          affine=affine,
                                          mask=mask,
                                          wavelength=wavelength,
                                          **ai_kwargs)
            if radial_range is None:
                radial_range = _get_radial_extent(ai=ai,
                                                  shape=sig_shape,
                                                  unit=unit)
                radial_range[0] = 0

            integration = self.map(azimuthal_integrate2d_fast,
                                   azimuthal_integrator=ai,
                                   npt_rad=npt_rad,
                                   npt_azim=npt_azim,
                                   azimuth_range=azimuth_range,
                                   radial_range=radial_range,
                                   method=method,
                                   inplace=inplace,
                                   unit=unit,
                                   correctSolidAngle=correctSolidAngle,
                                   **integrate2d_kwargs,
                                   **map_kwargs)

        # Dealing with axis changes
        if inplace:
            t_axis = self.axes_manager.signal_axes[0]
            k_axis = self.axes_manager.signal_axes[1]
            self.set_signal_type("polar_diffraction")
        else:
            transfer_navigation_axes(integration, self)
            integration.set_signal_type("polar_diffraction")
            t_axis = integration.axes_manager.signal_axes[0]
            k_axis = integration.axes_manager.signal_axes[1]
        t_axis.name = "Radians"
        if azimuth_range is None:
            t_axis.scale = np.pi * 2 / npt_azim
            t_axis.offset = -np.pi
        else:
            t_axis.scale = (azimuth_range[1] - azimuth_range[0]) / npt_rad
            t_axis.offset = azimuth_range[0]
        k_axis.name = "Radius"
        if pyxem_units:
            k_axis.scale = (radial_range[1] -
                            radial_range[0]) / npt_rad / scale_factor
            k_axis.offset = radial_range[0] / scale_factor
        else:
            k_axis.scale = (radial_range[1] - radial_range[0]) / npt_rad
            k_axis.units = unit
            k_axis.offset = radial_range[0]

        return integration