def get_elliptical_distortion( self, mask_radius, scale=100, amplitude=1000, spread=2, direct_beam_amplitude=500, asymmetry=1, rotation=0, center=None, ): """Determine elliptical distortion of the diffraction pattern. Parameters ---------- mask_radius : int The radius in pixels for a mask over the direct beam disc (the direct beam disc within given radius will be excluded from the fit) scale : float An initial guess for the diffraction calibration in 1/Angstrom units amplitude : float An initial guess for the amplitude of the polycrystalline rings in arbitrary units spread : float An initial guess for the spread within each ring (Gaussian width) direct_beam_amplitude : float An initial guess for the background intensity from the direct beam disc in arbitrary units asymmetry : float An initial guess for any elliptical asymmetry in the pattern (for a perfectly circular pattern asymmetry=1) rotation : float An initial guess for the rotation of the (elliptical) pattern in radians. center : None or list The center of the diffraction pattern. Returns ------- fit_params : np.array() Array of fitting parameters. [scale, amplitude, spread, direct_beam_amplitude, asymmetry, rotation]. affine_matrix : np.array() Array defining the affine transformation that corrects for lens distortions in the diffraction pattern. See Also -------- pyxem.utils.calibration_utils.call_ring_pattern """ # Check that necessary calibration data is provided if self.diffraction_pattern is None: raise ValueError( "This method requires a calibration diffraction pattern" " to be provided. Please set self.diffraction_pattern equal" " to some Signal2D." ) standard_dp = self.diffraction_pattern image_size = standard_dp.data.shape[0] if center is None: center = [(image_size -1)/2, (image_size -1)/2] # Set diffraction pattern variable x, y = np.mgrid[0:image_size, 0:image_size] radius_map = calc_radius_with_distortion(x, y, center[0], center[1], 1, 0) mask = radius_map < mask_radius ref = standard_dp.data[~mask] fullx,fully = [x,y] pts = np.array([fully[~mask], fullx[~mask]]).ravel() # Set initial parameters for fitting x0 = [scale, amplitude, spread, direct_beam_amplitude, asymmetry, rotation] # Fit ring pattern to experimental data xf, cov = curve_fit(call_ring_pattern(center[0], center[1]), pts, ref, p0=x0) # Set ring fitting parameters to attribute self.ring_params = xf # Calculate affine transform parameters from fit parameters scaling = np.array([[1, 0], [0, xf[4] ** -0.5]]) rotation = np.array([[cos(xf[5]), -sin(xf[5])], [sin(xf[5]), cos(xf[5])]]) correction = np.linalg.inv(np.dot(rotation.T, np.dot(scaling, rotation))) affine = np.array( [ [correction[0, 0], correction[0, 1], 0.00], [correction[1, 0], correction[1, 1], 0.00], [0.00, 0.00, 1.00], ] ) # Set affine matrix to attribute self.affine_matrix = affine return affine
def get_elliptical_distortion(self, mask_radius, scale=100, amplitude=1000, spread=2, direct_beam_amplitude=500, asymmetry=1, rotation=0): """Determine elliptical distortion of the diffraction pattern. Parameters ---------- mask_radius : int The radius in pixels for a mask over the direct beam disc (the direct beam disc within given radius will be excluded from the fit) scale : float An initial guess for the diffraction calibration in 1/Angstrom units amplitude : float An initial guess for the amplitude of the polycrystalline rings in arbitrary units spread : float An initial guess for the spread within each ring (Gaussian width) direct_beam_amplitude : float An initial guess for the background intensity from the direct beam disc in arbitrary units asymmetry : float An initial guess for any elliptical asymmetry in the pattern (for a perfectly circular pattern asymmetry=1) rotation : float An initial guess for the rotation of the (elliptical) pattern in radians. Returns ------- fit_params : np.array() Array of fitting parameters. [scale, amplitude, spread, direct_beam_amplitude, asymmetry, rotation]. affine_matrix : np.array() Array defining the affine transformation that corrects for lens distortions in the diffraction pattern. See Also -------- pyxem.utils.calibration_utils.call_ring_pattern """ # Check that necessary calibration data is provided if self.calibration_data.au_x_grating_dp is None: raise ValueError( "This method requires an Au X-grating diffraction " "pattern to be provided. Please update the " "CalibrationDataLibrary.") # Set diffraction pattern variable standard_dp = self.calibration_data.au_x_grating_dp # Define grid values and center indices for ring pattern evaluation image_size = standard_dp.data.shape[0] xi = np.linspace(0, image_size - 1, image_size) yi = np.linspace(0, image_size - 1, image_size) x, y = np.meshgrid(xi, yi) xcenter = (image_size - 1) / 2 ycenter = (image_size - 1) / 2 # Calculate eliptical parameters mask = calc_radius_with_distortion(x, y, (image_size - 1) / 2, (image_size - 1) / 2, 1, 0) # Mask direct beam mask[mask > mask_radius] = 0 standard_dp.data[mask > 0] *= 0 # Manipulate measured data for fitting ref = standard_dp.data[standard_dp.data > 0] ref = ref.ravel() # Define points for fitting pts = np.array( [x[standard_dp.data > 0].ravel(), y[standard_dp.data > 0].ravel()]).ravel() # Set initial parameters for fitting x0 = [ scale, amplitude, spread, direct_beam_amplitude, asymmetry, rotation ] # Fit ring pattern to experimental data xf, cov = curve_fit(call_ring_pattern(xcenter, ycenter), pts, ref, p0=x0) # Set ring fitting parameters to attribute self.ring_params = xf # Calculate affine transform parameters from fit parameters scaling = np.array([[1, 0], [0, xf[4]**-0.5]]) rotation = np.array([[cos(xf[5]), -sin(xf[5])], [sin(xf[5]), cos(xf[5])]]) correction = np.linalg.inv( np.dot(rotation.T, np.dot(scaling, rotation))) affine = np.array([[correction[0, 0], correction[0, 1], 0.00], [correction[1, 0], correction[1, 1], 0.00], [0.00, 0.00, 1.00]]) # Set affine matrix to attribute self.affine_matrix = affine return affine