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
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
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
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
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
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
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
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
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
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
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
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