diff --git a/py4DSTEM/process/phase/direct_ptychography.py b/py4DSTEM/process/phase/direct_ptychography.py index 882f97575..83858dc93 100644 --- a/py4DSTEM/process/phase/direct_ptychography.py +++ b/py4DSTEM/process/phase/direct_ptychography.py @@ -325,6 +325,7 @@ def preprocess( bf_disk_padding_px: int = 0, dp_mask: np.ndarray = None, in_place_datacube_modification: bool = False, + shifting_interpolation_order: int = 3, fit_function: str = "plane", plot_center_of_mass: str = "default", plot_rotation: bool = True, @@ -354,6 +355,8 @@ def preprocess( in_place_datacube_modification: bool, optional If True, the datacube will be preprocessed in-place. Note this is not possible when either crop_patterns or positions_mask are used. + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. fit_function: str, optional 2D fitting function for CoM fitting. One of 'plane','parabola','bezier_two' plot_center_of_mass: str, optional @@ -527,6 +530,7 @@ def preprocess( None, crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, return_intensities_instead=True, ) @@ -583,7 +587,7 @@ def preprocess( self._vacuum_probe_intensity, -probe_x0, -probe_y0, - bilinear=False, + bilinear=True, device=device, ) diff --git a/py4DSTEM/process/phase/magnetic_ptychographic_tomography.py b/py4DSTEM/process/phase/magnetic_ptychographic_tomography.py index aac28455e..7a0780868 100644 --- a/py4DSTEM/process/phase/magnetic_ptychographic_tomography.py +++ b/py4DSTEM/process/phase/magnetic_ptychographic_tomography.py @@ -227,6 +227,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -264,6 +265,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -496,6 +499,7 @@ def preprocess( self._positions_mask[index], crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) self._mean_diffraction_intensity.append(mean_diffraction_intensity_temp) diff --git a/py4DSTEM/process/phase/magnetic_ptychography.py b/py4DSTEM/process/phase/magnetic_ptychography.py index 4c348971f..0af4d4958 100644 --- a/py4DSTEM/process/phase/magnetic_ptychography.py +++ b/py4DSTEM/process/phase/magnetic_ptychography.py @@ -205,6 +205,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -252,6 +253,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -559,6 +562,7 @@ def preprocess( self._positions_mask[index], crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) self._mean_diffraction_intensity.append(mean_diffraction_intensity_temp) diff --git a/py4DSTEM/process/phase/mixedstate_multislice_ptychography.py b/py4DSTEM/process/phase/mixedstate_multislice_ptychography.py index d5808135b..e992ae3cf 100644 --- a/py4DSTEM/process/phase/mixedstate_multislice_ptychography.py +++ b/py4DSTEM/process/phase/mixedstate_multislice_ptychography.py @@ -264,6 +264,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -311,6 +312,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -498,6 +501,7 @@ def preprocess( self._positions_mask, crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) # explicitly transfer arrays to storage diff --git a/py4DSTEM/process/phase/mixedstate_ptychography.py b/py4DSTEM/process/phase/mixedstate_ptychography.py index 0032313f3..47bced769 100644 --- a/py4DSTEM/process/phase/mixedstate_ptychography.py +++ b/py4DSTEM/process/phase/mixedstate_ptychography.py @@ -210,6 +210,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -257,6 +258,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -444,6 +447,7 @@ def preprocess( self._positions_mask, crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) # explicitly transfer arrays to storage diff --git a/py4DSTEM/process/phase/multislice_ptychography.py b/py4DSTEM/process/phase/multislice_ptychography.py index 3e6fdc2b5..d7daa9d16 100644 --- a/py4DSTEM/process/phase/multislice_ptychography.py +++ b/py4DSTEM/process/phase/multislice_ptychography.py @@ -238,6 +238,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -285,6 +286,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -472,6 +475,7 @@ def preprocess( self._positions_mask, crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) # explicitly transfer arrays to storage diff --git a/py4DSTEM/process/phase/parallax.py b/py4DSTEM/process/phase/parallax.py index f33a9b803..d3d41447c 100644 --- a/py4DSTEM/process/phase/parallax.py +++ b/py4DSTEM/process/phase/parallax.py @@ -32,7 +32,7 @@ from py4DSTEM.process.utils.utils import electron_wavelength_angstrom from py4DSTEM.visualize import return_scaled_histogram_ordering from scipy.linalg import polar -from scipy.ndimage import distance_transform_edt +from scipy.ndimage import distance_transform_edt, shift from scipy.special import comb try: @@ -316,6 +316,7 @@ def preprocess( normalize_images: bool = True, normalize_order=0, descan_correction_fit_function: str = None, + shifting_interpolation_order: int = 3, force_rotation_angle_deg: float = 0, force_transpose: bool = None, aligned_bf_image_guess: np.ndarray = None, @@ -354,6 +355,8 @@ def preprocess( descan_correction_fit_function: str, optional If not None, descan correction will be performed using fit function. One of "constant", "plane", "parabola", or "bezier_two". + shifting_interpolation_order: int, optional + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. plot_average_bf: bool, optional If True, plots the average bright field image, using defocus_guess realspace_mask: np.array, optional @@ -433,15 +436,25 @@ def preprocess( for rx in range(intensities_shifted.shape[0]): for ry in range(intensities_shifted.shape[1]): - intensity_shifted = get_shifted_ar( - intensities_np[rx, ry], - -com_fitted_x[rx, ry] + center_x, - -com_fitted_y[rx, ry] + center_y, - bilinear=False, - device="cpu", - ) - - intensities_shifted[rx, ry] = intensity_shifted + if shifting_interpolation_order == 1: + # faster but may lead to gridding artifacts + intensities_shifted[rx, ry] = get_shifted_ar( + intensities_np[rx, ry].astype(np.float32), + -com_fitted_x[rx, ry] + center_x, + -com_fitted_y[rx, ry] + center_y, + bilinear=True, + device="cpu", + ) + else: + intensities_shifted[rx, ry] = shift( + intensities_np[rx, ry].astype(np.float32), + ( + -com_fitted_x[rx, ry] + center_x, + -com_fitted_y[rx, ry] + center_y, + ), + order=shifting_interpolation_order, + mode="grid-wrap", + ) intensities = xp.asarray(intensities_shifted, xp.float32) diff --git a/py4DSTEM/process/phase/phase_base_class.py b/py4DSTEM/process/phase/phase_base_class.py index 47e9f6a4d..8c408fb0b 100644 --- a/py4DSTEM/process/phase/phase_base_class.py +++ b/py4DSTEM/process/phase/phase_base_class.py @@ -9,7 +9,7 @@ import numpy as np from mpl_toolkits.axes_grid1 import ImageGrid from py4DSTEM.visualize import show_complex -from scipy.ndimage import zoom +from scipy.ndimage import shift, zoom try: import cupy as cp @@ -1352,6 +1352,7 @@ def _normalize_diffraction_intensities( positions_mask, crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=3, return_intensities_instead=False, ): """ @@ -1371,6 +1372,10 @@ def _normalize_diffraction_intensities( If True, patterns are cropped to avoid wrap around of patterns in_place_datacube_modification: bool If True, the diffraction intensities are modified in-place + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. + return_intensities_instead: bool + If True, function returns shifted intensities instead of amplitudes. Used in SSB. Returns ------- @@ -1434,13 +1439,22 @@ def _normalize_diffraction_intensities( if not positions_mask[rx, ry]: continue - intensities = get_shifted_ar( - diff_intensities[rx, ry], - -com_fitted_x[rx, ry], - -com_fitted_y[rx, ry], - bilinear=False, - device="cpu", - ) + if shifting_interpolation_order == 1: + # faster but may lead to gridding artifacts + intensities = get_shifted_ar( + diff_intensities[rx, ry].astype(np.float32), + -com_fitted_x[rx, ry], + -com_fitted_y[rx, ry], + bilinear=True, + device="cpu", + ) + else: + intensities = shift( + diff_intensities[rx, ry].astype(np.float32), + (-com_fitted_x[rx, ry], -com_fitted_y[rx, ry]), + order=shifting_interpolation_order, + mode="grid-wrap", + ) mean_intensity += np.sum(intensities) if return_intensities_instead: diff --git a/py4DSTEM/process/phase/ptychographic_methods.py b/py4DSTEM/process/phase/ptychographic_methods.py index b5b4a694e..bcd518469 100644 --- a/py4DSTEM/process/phase/ptychographic_methods.py +++ b/py4DSTEM/process/phase/ptychographic_methods.py @@ -1077,7 +1077,7 @@ def _initialize_probe( vacuum_probe_intensity, -probe_x0, -probe_y0, - bilinear=False, + bilinear=True, device=device, ) diff --git a/py4DSTEM/process/phase/ptychographic_tomography.py b/py4DSTEM/process/phase/ptychographic_tomography.py index 5dd72986d..3349c03c3 100644 --- a/py4DSTEM/process/phase/ptychographic_tomography.py +++ b/py4DSTEM/process/phase/ptychographic_tomography.py @@ -377,6 +377,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -416,6 +417,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -652,6 +655,7 @@ def preprocess( self._positions_mask[index], crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) self._mean_diffraction_intensity.append(mean_diffraction_intensity_temp) diff --git a/py4DSTEM/process/phase/singleslice_ptychography.py b/py4DSTEM/process/phase/singleslice_ptychography.py index 4eb0549d7..473a5e702 100644 --- a/py4DSTEM/process/phase/singleslice_ptychography.py +++ b/py4DSTEM/process/phase/singleslice_ptychography.py @@ -192,6 +192,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -229,6 +230,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -417,6 +420,7 @@ def preprocess( self._positions_mask, crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) # explicitly transfer arrays to storage diff --git a/py4DSTEM/process/phase/utils.py b/py4DSTEM/process/phase/utils.py index ef4d7f20d..ebfcc0728 100644 --- a/py4DSTEM/process/phase/utils.py +++ b/py4DSTEM/process/phase/utils.py @@ -179,7 +179,7 @@ def evaluate_aperture( xp.asarray(self._vacuum_probe_intensity, dtype=xp.float32), self._origin[0], self._origin[1], - bilinear=False, + bilinear=True, device=self._device, ) else: diff --git a/py4DSTEM/process/phase/xray_magnetic_ptychography.py b/py4DSTEM/process/phase/xray_magnetic_ptychography.py index ce61312e0..01586f1b1 100644 --- a/py4DSTEM/process/phase/xray_magnetic_ptychography.py +++ b/py4DSTEM/process/phase/xray_magnetic_ptychography.py @@ -203,6 +203,7 @@ def preprocess( self, diffraction_intensities_shape: Tuple[int, int] = None, reshaping_method: str = "bilinear", + shifting_interpolation_order: int = 3, padded_diffraction_intensities_shape: Tuple[int, int] = None, region_of_interest_shape: Tuple[int, int] = None, dp_mask: np.ndarray = None, @@ -250,6 +251,8 @@ def preprocess( If None, no resampling of diffraction intenstities is performed reshaping_method: str, optional Method to use for reshaping, either 'bin, 'bilinear', or 'fourier' (default) + shifting_interpolation_order: int + Spline interpolation order used in shifting DPs to origin. Default is bi-cubic. padded_diffraction_intensities_shape: (int,int), optional Padded diffraction intensities shape. If None, no padding is performed @@ -557,6 +560,7 @@ def preprocess( self._positions_mask[index], crop_patterns, in_place_datacube_modification, + shifting_interpolation_order=shifting_interpolation_order, ) self._mean_diffraction_intensity.append(mean_diffraction_intensity_temp)