Exemple #1
0
def psf_rl(measured_intensity,
           coherent_intensity,
           iterations=20,
           debugging=False):
    """
    Partial coherence deconvolution using Richardson-Lucy algorithm. See J.N. Clark et al., Nat. Comm. 3, 993 (2012).

    :param measured_intensity: measured object with partial coherent illumination
    :param coherent_intensity: estimate of the object measured by a fully coherent illumination
    :param iterations: number of iterations for the Richardson-Lucy algorithm
    :param debugging: True to see plots
    :return:
    """
    psf = np.abs(
        richardson_lucy(image=measured_intensity,
                        psf=coherent_intensity,
                        iterations=iterations,
                        clip=False))
    psf = (psf / psf.sum()).astype(np.float32)
    if debugging:
        gu.multislices_plot(fftshift(psf),
                            scale='log',
                            sum_frames=False,
                            title='log(psf) in detector frame',
                            reciprocal_space=True,
                            vmin=-5,
                            is_orthogonal=False,
                            plot_colorbar=True)
    return psf
Exemple #2
0
def deconvolution_rl(image,
                     psf=None,
                     psf_shape=(10, 10, 10),
                     iterations=20,
                     debugging=False):
    """
    Image deconvolution using Richardson-Lucy algorithm. The algorithm is based on a PSF (Point Spread Function),
    where PSF is described as the impulse response of the optical system.

    :param image: image to be deconvoluted
    :param psf: psf to be used as a first guess
    :param psf_shape: shape of the kernel used for deconvolution
    :param iterations: number of iterations for the Richardson-Lucy algorithm
    :param debugging: True to see plots
    :return:
    """
    image = image.astype(np.float)
    ndim = image.ndim
    if psf is None:
        print('Initializing the psf using a', ndim,
              'D multivariate normal window\n')
        print('sigma =', 0.3, ' mu =', 0.0)
        psf = pu.gaussian_window(window_shape=psf_shape,
                                 sigma=0.3,
                                 mu=0.0,
                                 debugging=False)
    psf = psf.astype(float)
    if debugging:
        gu.multislices_plot(array=psf,
                            sum_frames=False,
                            plot_colorbar=True,
                            scale='linear',
                            title='Gaussian window',
                            reciprocal_space=False,
                            is_orthogonal=True)

    im_deconv = np.abs(
        richardson_lucy(image=image,
                        psf=psf,
                        iterations=iterations,
                        clip=False))

    if debugging:
        image = abs(image) / abs(image).max()
        im_deconv = abs(im_deconv) / abs(im_deconv).max()
        gu.combined_plots(tuple_array=(image, im_deconv),
                          tuple_sum_frames=False,
                          tuple_colorbar=True,
                          tuple_scale='linear',
                          tuple_width_v=None,
                          tuple_width_h=None,
                          tuple_vmin=0,
                          tuple_vmax=1,
                          tuple_title=('Before RL',
                                       'After ' + str(iterations) +
                                       ' iterations of RL (normalized)'))

    return im_deconv
Exemple #3
0
def upsample(array, upsampling_factor, voxelsizes, debugging=False):
    """
    Upsample array using a factor of upsampling.

    :param array: the real array to be upsampled
    :param upsampling_factor: int, the upsampling factor
    :param voxelsizes: list, the voxel sizes of array
    :param debugging: True to see plots
    :return: the upsampled array
    """
    from scipy.interpolate import RegularGridInterpolator

    if array.ndim != 3:
        raise ValueError('Expecting a 3D array as input')

    if not isinstance(upsampling_factor, int):
        raise ValueError('upsampling_factor should be an integer')
    if debugging:
        gu.multislices_plot(array,
                            sum_frames=False,
                            invert_yaxis=True,
                            title='Array before upsampling')

    nbz, nby, nbx = array.shape
    numz, numy, numx = nbz * upsampling_factor, nby * upsampling_factor, nbx * upsampling_factor
    newvoxelsizes = [voxsize / upsampling_factor for voxsize in voxelsizes]

    newz, newy, newx = np.meshgrid(
        np.arange(-numz // 2, numz // 2, 1) * newvoxelsizes[0],
        np.arange(-numy // 2, numy // 2, 1) * newvoxelsizes[1],
        np.arange(-numx // 2, numx // 2, 1) * newvoxelsizes[2],
        indexing='ij')

    rgi = RegularGridInterpolator(
        (np.arange(-nbz // 2, nbz // 2) * voxelsizes[0],
         np.arange(-nby // 2, nby // 2) * voxelsizes[1],
         np.arange(-nbx // 2, nbx // 2) * voxelsizes[2]),
        array,
        method='linear',
        bounds_error=False,
        fill_value=0)

    obj = rgi(
        np.concatenate((newz.reshape(
            (1, newz.size)), newy.reshape(
                (1, newz.size)), newx.reshape((1, newz.size)))).transpose())

    obj = obj.reshape((numz, numy, numx)).astype(array.dtype)

    if debugging:
        gu.multislices_plot(obj,
                            sum_frames=False,
                            invert_yaxis=True,
                            title='Array after upsampling')

    return obj, newvoxelsizes
Exemple #4
0
    data = pu.flip_reconstruction(data, debugging=True)

data = abs(data)  # take the real part

###################################
# clean interactively the support #
###################################
if flag_interact:
    data = data / data.max(initial=None)  # normalize
    data[data < support_threshold] = 0

    fig, _, _ = gu.multislices_plot(data,
                                    sum_frames=False,
                                    scale='linear',
                                    plot_colorbar=True,
                                    vmin=0,
                                    vmax=1,
                                    title='Support before masking',
                                    is_orthogonal=True,
                                    reciprocal_space=False)
    fig.canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id)
    cid = plt.connect('close_event', close_event)
    fig.waitforbuttonpress()
    plt.disconnect(cid)
    plt.close(fig)

    #############################################
    # mask the projected data in each dimension #
    #############################################
    plt.ioff()
    width = 0
Exemple #5
0
                                     pivot=pivot)
###############
# plot result #
###############
qx, qz, qy = q_values
# mark the direct beam position
struct_array[pivot[0] - 2:pivot[0] + 3, pivot[1] - 2:pivot[1] + 3,
             pivot[2] - 2:pivot[2] + 3] = maxpeak

if debug:
    fig, _, _ = gu.multislices_plot(
        struct_array,
        sum_frames=False,
        title='Simulated diffraction pattern',
        vmin=0,
        vmax=maxpeak,
        slice_position=[pivot[0], pivot[1], pivot[2]],
        plot_colorbar=True,
        cmap=my_cmap,
        is_orthogonal=True,
        reciprocal_space=True)
    fig.text(0.60,
             0.30,
             "Origin of reciprocal space (Qx,Qz,Qy) = " + str(pivot[0]) + "," +
             str(pivot[1]) + "," + str(pivot[2]),
             size=12)
    fig.text(0.60, 0.25, "Energy = " + str(energy / 1000) + " keV", size=12)
    fig.text(0.60, 0.20, "SDD = " + str(sdd) + " m", size=12)
    fig.text(0.60,
             0.15,
             unitcell + " unit cell of parameter = " + str(unitcell_param) +
Exemple #6
0
    comment = f'_direction{direction[0]}_{direction[1]}_{comment}'

#########################
# normalize the modulus #
#########################
obj = abs(obj) / abs(obj).max()  # normalize the modulus to 1
obj[np.isnan(obj)] = 0  # remove nans
if ndim == 2:
    gu.imshow_plot(array=obj,
                   plot_colorbar=True,
                   reciprocal_space=False,
                   is_orthogonal=True)
else:
    gu.multislices_plot(array=obj,
                        sum_frames=False,
                        plot_colorbar=True,
                        reciprocal_space=False,
                        is_orthogonal=True,
                        slice_position=(25, 37, 25))

#####################################
# create the linecut for each point #
#####################################
result = dict()
for point in points:
    # get the distances and the modulus values along the linecut
    distance, cut = util.linecut(array=obj,
                                 point=point,
                                 direction=direction,
                                 voxel_size=voxel_size)
    # store the result in a dictionary (cuts can have different lengths depending on the direction)
    result[f'voxel {point}'] = {'distance': distance, 'cut': cut}
Exemple #7
0
    def detector_frame(self,
                       obj,
                       voxelsize,
                       width_z=None,
                       width_y=None,
                       width_x=None,
                       debugging=False,
                       **kwargs):
        """
        Interpolate the orthogonal object back into the non-orthogonal detector frame

        :param obj: real space object, in the orthogonal laboratory frame
        :param voxelsize: voxel size of the original object
        :param width_z: size of the area to plot in z (axis 0), centered on the middle of the initial array
        :param width_y: size of the area to plot in y (axis 1), centered on the middle of the initial array
        :param width_x: size of the area to plot in x (axis 2), centered on the middle of the initial array
        :param debugging: True to show plots before and after interpolation
        :param kwargs:
         - 'title': title for the debugging plots
        :return: object interpolated on an orthogonal grid
        """
        for k in kwargs.keys():
            if k in ['title']:
                title = kwargs['title']
            else:
                raise Exception(
                    "unknown keyword argument given: allowed is 'title'")
        try:
            title
        except NameError:  # title not declared
            title = 'Object'

        nbz, nby, nbx = obj.shape

        if debugging:
            gu.multislices_plot(abs(obj),
                                sum_frames=True,
                                width_z=width_z,
                                width_y=width_y,
                                width_x=width_x,
                                title=title + ' before interpolation\n')

        ortho_matrix = self.update_coords(array_shape=(nbz, nby, nbx),
                                          tilt_angle=self.tilt_angle,
                                          pixel_x=self.pixel_x,
                                          pixel_y=self.pixel_y)

        ################################################
        # interpolate the data into the detector frame #
        ################################################
        myz, myy, myx = np.meshgrid(np.arange(-nbz // 2, nbz // 2, 1),
                                    np.arange(-nby // 2, nby // 2, 1),
                                    np.arange(-nbx // 2, nbx // 2, 1),
                                    indexing='ij')

        new_x = ortho_matrix[0, 0] * myx + ortho_matrix[
            0, 1] * myy + ortho_matrix[0, 2] * myz
        new_y = ortho_matrix[1, 0] * myx + ortho_matrix[
            1, 1] * myy + ortho_matrix[1, 2] * myz
        new_z = ortho_matrix[2, 0] * myx + ortho_matrix[
            2, 1] * myy + ortho_matrix[2, 2] * myz
        del myx, myy, myz
        # la partie rgi est sure: c'est la taille de l'objet orthogonal de depart
        rgi = RegularGridInterpolator(
            (np.arange(-nbz // 2, nbz // 2) * voxelsize,
             np.arange(-nby // 2, nby // 2) * voxelsize,
             np.arange(-nbx // 2, nbx // 2) * voxelsize),
            obj,
            method='linear',
            bounds_error=False,
            fill_value=0)
        detector_obj = rgi(
            np.concatenate((new_z.reshape(
                (1, new_z.size)), new_y.reshape(
                    (1, new_z.size)), new_x.reshape(
                        (1, new_z.size)))).transpose())
        detector_obj = detector_obj.reshape((nbz, nby, nbx)).astype(obj.dtype)

        if debugging:
            gu.multislices_plot(abs(detector_obj),
                                sum_frames=True,
                                width_z=width_z,
                                width_y=width_y,
                                width_x=width_x,
                                title=title +
                                ' interpolated in detector frame\n')

        return detector_obj
Exemple #8
0
    title="Select the reconstruction file",
    filetypes=[("NPZ", "*.npz")],
)
npzfile = np.load(file_path)
phase = npzfile["displacement"]
if flip_phase:
    phase = -1 * phase
amp = npzfile["amp"]
if amp.ndim != 3:
    raise ValueError("3D arrays are expected")

gu.multislices_plot(
    array=amp,
    sum_frames=False,
    scale="linear",
    plot_colorbar=True,
    reciprocal_space=False,
    is_orthogonal=True,
    title="Modulus",
)

################################
# optionally load the q values #
################################
if load_qvalues:
    file_path = filedialog.askopenfilename(
        initialdir=datadir, title="Select the q values", filetypes=[("NPZ", "*.npz")]
    )
    q_values = np.load(file_path)
    qx = q_values["qx"]
    qz = q_values["qz"]
        ycom,
        nby - ycom,
        xcom,
        nbx - xcom,
    )  # asymmetric half ranges

for idx, val in enumerate(half_range):
    plot_range.append(min(val or max_range[2 * idx], max_range[2 * idx]))
    plot_range.append(min(val or max_range[2 * idx + 1], max_range[2 * idx + 1]))
print("\nPlotting symmetrical ranges:", plot_symmetrical)
print("Plotting range from the center of mass:", plot_range)

gu.multislices_plot(
    array=data,
    sum_frames=True,
    scale="log",
    cmap=my_cmap,
    reciprocal_space=True,
    is_orthogonal=is_orthogonal,
)

################################
# optionally load the q values #
################################
if load_qvalues:
    file_path = filedialog.askopenfilename(
        initialdir=datadir, title="Select the q values", filetypes=[("NPZ", "*.npz")]
    )
    q_values = np.load(file_path)
    qx = q_values["qx"]
    qz = q_values["qz"]
    qy = q_values["qy"]
Exemple #10
0
#############
# load mask #
#############
file_path = filedialog.askopenfilename(initialdir=datadir,
                                       title="Select the mask",
                                       filetypes=[("NPZ", "*.npz")])
npzfile = np.load(file_path)
mask = npzfile[list(npzfile.files)[0]]
if debug:
    gu.multislices_plot(
        diff_pattern,
        sum_frames=False,
        plot_colorbar=True,
        cmap=my_cmap,
        title="measured amplitude",
        scale="log",
        vmin=np.nan,
        vmax=np.nan,
        reciprocal_space=True,
        is_orthogonal=True,
    )

    gu.multislices_plot(
        mask,
        sum_frames=False,
        plot_colorbar=True,
        cmap=my_cmap,
        title="mask",
        scale="linear",
        vmin=0,
        vmax=1,
Exemple #11
0
    print(
        'The experimental data and calculated q values have different shape, check "roi_detector" parameter!'
    )
    sys.exit()

print('Origin of the reciprocal space at pixel', pivot)

##########################
# plot experimental data #
##########################
if debug:
    gu.multislices_plot(data,
                        sum_frames=True,
                        title='data',
                        vmin=0,
                        vmax=np.log10(data).max(),
                        scale='log',
                        plot_colorbar=True,
                        cmap=my_cmap,
                        is_orthogonal=True,
                        reciprocal_space=True)

    if qvalues_flag:
        gu.contour_slices(data,
                          q_coordinates=(exp_qvalues['qx'], exp_qvalues['qz'],
                                         exp_qvalues['qy']),
                          sum_frames=True,
                          title='Experimental data',
                          levels=np.linspace(0,
                                             np.log10(data.max()) + 1,
                                             20,
                                             endpoint=False),
if background_roi is not None:
    background = obj[background_roi[0]:background_roi[1] + 1,
                     background_roi[2:background_roi[3] + 1], ].mean()
    print(f"removing background = {background:.2f} from the data")
    obj = obj - background

if ndim == 2:
    gu.imshow_plot(array=obj,
                   plot_colorbar=True,
                   reciprocal_space=False,
                   is_orthogonal=True)
else:
    gu.multislices_plot(
        array=obj,
        sum_frames=False,
        plot_colorbar=True,
        reciprocal_space=False,
        is_orthogonal=True,
    )

#####################################
# create the linecut for each point #
#####################################
result = {}
for point in points:
    # get the distances and the modulus values along the linecut
    distance, cut = util.linecut(array=obj,
                                 point=point,
                                 direction=direction,
                                 voxel_size=voxel_size)
    # store the result in a dictionary
Exemple #13
0
support = np.zeros(amp.shape)
support[abs(amp) > support_threshold * abs(amp).max()] = 1
coordination_matrix = pu.calc_coordination(support, kernel=np.ones((3, 3, 3)), debugging=False)
surface = np.copy(support)
surface[coordination_matrix > 22] = 0  # remove the bulk 22
bulk = support - surface
del coordination_matrix
gc.collect()

########################################################
# define edges using the coordination number of voxels #
########################################################
edges = pu.calc_coordination(support, kernel=np.ones((9, 9, 9)), debugging=False)
edges[support == 0] = 0
if debug:
    gu.multislices_plot(edges, invert_yaxis=True, vmin=0, title='Coordination matrix')
edges[edges > edges_coord] = 0  # remove facets and bulk
edges[np.nonzero(edges)] = 1  # edge support
gu.scatter_plot(array=np.asarray(np.nonzero(edges)).T, markersize=2, markercolor='b', labels=('x', 'y', 'z'),
                title='edges')

########################################################
# define corners using the coordination number of voxels #
########################################################
corners = pu.calc_coordination(support, kernel=np.ones((9, 9, 9)), debugging=False)
corners[support == 0] = 0
if debug:
    gu.multislices_plot(corners, invert_yaxis=True, vmin=0, title='Coordination matrix')
corners[corners > corners_coord] = 0  # remove edges, facets and bulk
corners[np.nonzero(corners)] = 1  # corner support
gu.scatter_plot(array=np.asarray(np.nonzero(corners)).T, markersize=2, markercolor='b', labels=('x', 'y', 'z'),
Exemple #14
0
def grid_cdi(
    data,
    mask,
    setup,
    frames_logical,
    correct_curvature=False,
    debugging=False,
    **kwargs,
):
    """
    Interpolate reciprocal space forward CDI data.

    The interpolation is done from the measurement cylindrical frame to the
    laboratory frame (cartesian coordinates). Note that it is based on PetraIII P10
    beamline ( counterclockwise rotation, detector seen from the front).

    :param data: the 3D data, already binned in the detector frame
    :param mask: the corresponding 3D mask
    :param setup: an instance of the class Setup
    :param frames_logical: array of initial length the number of measured frames.
     In case of padding the length changes. A frame whose index is set to 1 means
     that it is used, 0 means not used, -1 means padded (added) frame.
    :param correct_curvature: if True, will correct for the curvature of
     the Ewald sphere
    :param debugging: set to True to see plots
    :param kwargs:
     - 'fill_value': tuple of two real numbers, fill values to use for pixels outside
       of the interpolation range. The first value is for the data, the second for the
       mask. Default is (0, 0)

    :return: the data and mask interpolated in the laboratory frame, q values
     (downstream, vertical up, outboard)
    """
    fill_value = kwargs.get("fill_value", (0, 0))
    valid.valid_ndarray(arrays=(data, mask), ndim=3)
    if setup.name == "P10_SAXS":
        if setup.rocking_angle == "inplane":
            if setup.custom_scan:
                cdi_angle = setup.custom_motors["hprz"]
            else:
                cdi_angle, _, _ = setup.loader.motor_positions(setup=setup)
                # second return value is the X-ray energy, third the detector distance
        else:
            raise ValueError(
                "out-of-plane rotation not yet implemented for forward CDI data"
            )
    else:
        raise NotImplementedError(
            "Not yet implemented for beamlines other than P10")

    data, mask, cdi_angle, frames_logical = check_cdi_angle(
        data=data,
        mask=mask,
        cdi_angle=cdi_angle,
        frames_logical=frames_logical,
        debugging=debugging,
    )
    if debugging:
        print("\ncdi_angle", cdi_angle)
    nbz, nby, nbx = data.shape
    print("\nData shape after check_cdi_angle and before regridding:", nbz,
          nby, nbx)
    print("\nAngle range:", cdi_angle.min(), cdi_angle.max())

    (interp_data, interp_mask), q_values, corrected_dirbeam = setup.ortho_cdi(
        arrays=(data, mask),
        cdi_angle=cdi_angle,
        fill_value=fill_value,
        correct_curvature=correct_curvature,
        debugging=debugging,
    )
    qx, qz, qy = q_values

    # check for Nan
    interp_mask[np.isnan(interp_data)] = 1
    interp_data[np.isnan(interp_data)] = 0
    interp_mask[np.isnan(interp_mask)] = 1
    # set the mask as an array of integers, 0 or 1
    interp_mask[np.nonzero(interp_mask)] = 1
    interp_mask = interp_mask.astype(int)

    # apply the mask to the data
    interp_data[np.nonzero(interp_mask)] = 0

    # calculate the position in pixels of the origin of the reciprocal space
    pivot_z = int((setup.direct_beam[1] - setup.detector.roi[2]) /
                  setup.detector.binning[2])
    # 90 degrees conter-clockwise rotation of detector X around qz, downstream
    _, numy, numx = interp_data.shape
    pivot_y = int(numy - corrected_dirbeam[0])
    # detector Y vertical down, opposite to qz vertical up
    pivot_x = int(numx - corrected_dirbeam[1])
    # detector X inboard at P10, opposite to qy outboard
    print("\nOrigin of the reciprocal space (Qx,Qz,Qy): "
          f"({pivot_z}, {pivot_y}, {pivot_x})\n")

    # plot the gridded data
    final_binning = (
        setup.detector.preprocessing_binning[2] * setup.detector.binning[2],
        setup.detector.preprocessing_binning[1] * setup.detector.binning[1],
        setup.detector.preprocessing_binning[2] * setup.detector.binning[2],
    )
    plot_comment = (
        f"_{numx}_{numy}_{numx}"
        f"_{final_binning[0]}_{final_binning[1]}_{final_binning[2]}.png")
    # sample rotation around the vertical direction at P10: the effective
    # binning in axis 0 is binning[2]

    max_z = interp_data.sum(axis=0).max()
    fig, _, _ = gu.contour_slices(
        interp_data,
        (qx, qz, qy),
        sum_frames=True,
        title="Regridded data",
        levels=np.linspace(0, np.ceil(np.log10(max_z)), 150, endpoint=True),
        plot_colorbar=True,
        scale="log",
        is_orthogonal=True,
        reciprocal_space=True,
    )
    fig.text(
        0.55,
        0.30,
        "Origin of the reciprocal space (Qx,Qz,Qy):\n\n" +
        "     ({:d}, {:d}, {:d})".format(pivot_z, pivot_y, pivot_x),
        size=14,
    )
    fig.savefig(setup.detector.savedir + "reciprocal_space_sum" + plot_comment)
    plt.close(fig)

    fig, _, _ = gu.contour_slices(
        interp_data,
        (qx, qz, qy),
        sum_frames=False,
        title="Regridded data",
        levels=np.linspace(0,
                           np.ceil(np.log10(interp_data.max(initial=None))),
                           150,
                           endpoint=True),
        slice_position=(pivot_z, pivot_y, pivot_x),
        plot_colorbar=True,
        scale="log",
        is_orthogonal=True,
        reciprocal_space=True,
    )
    fig.text(
        0.55,
        0.30,
        "Origin of the reciprocal space (Qx,Qz,Qy):\n\n" +
        "     ({:d}, {:d}, {:d})".format(pivot_z, pivot_y, pivot_x),
        size=14,
    )
    fig.savefig(setup.detector.savedir + "reciprocal_space_central" +
                plot_comment)
    plt.close(fig)

    fig, _, _ = gu.multislices_plot(
        interp_data,
        sum_frames=False,
        scale="log",
        plot_colorbar=True,
        vmin=0,
        slice_position=(pivot_z, pivot_y, pivot_x),
        title="Regridded data",
        is_orthogonal=True,
        reciprocal_space=True,
    )
    fig.text(
        0.55,
        0.30,
        "Origin of the reciprocal space (Qx,Qz,Qy):\n\n" +
        "     ({:d}, {:d}, {:d})".format(pivot_z, pivot_y, pivot_x),
        size=14,
    )
    fig.savefig(setup.detector.savedir + "reciprocal_space_central_pix" +
                plot_comment)
    plt.close(fig)
    if debugging:
        gu.multislices_plot(
            interp_mask,
            sum_frames=False,
            scale="linear",
            plot_colorbar=True,
            vmin=0,
            title="Regridded mask",
            is_orthogonal=True,
            reciprocal_space=True,
        )

    return interp_data, interp_mask, [qx, qz, qy], frames_logical
Exemple #15
0
def run(prm):
    """
    Run the postprocessing.

    :param prm: the parsed parameters
    """
    pretty = pprint.PrettyPrinter(indent=4)

    ################################
    # assign often used parameters #
    ################################
    bragg_peak = prm.get("bragg_peak")
    debug = prm.get("debug", False)
    comment = prm.get("comment", "")
    centering_method = prm.get("centering_method", "max_com")
    original_size = prm.get("original_size")
    phasing_binning = prm.get("phasing_binning", [1, 1, 1])
    preprocessing_binning = prm.get("preprocessing_binning", [1, 1, 1])
    ref_axis_q = prm.get("ref_axis_q", "y")
    fix_voxel = prm.get("fix_voxel")
    save = prm.get("save", True)
    tick_spacing = prm.get("tick_spacing", 50)
    tick_direction = prm.get("tick_direction", "inout")
    tick_length = prm.get("tick_length", 10)
    tick_width = prm.get("tick_width", 2)
    invert_phase = prm.get("invert_phase", True)
    correct_refraction = prm.get("correct_refraction", False)
    threshold_unwrap_refraction = prm.get("threshold_unwrap_refraction", 0.05)
    threshold_gradient = prm.get("threshold_gradient", 1.0)
    offset_method = prm.get("offset_method", "mean")
    phase_offset = prm.get("phase_offset", 0)
    offset_origin = prm.get("phase_offset_origin")
    sort_method = prm.get("sort_method", "variance/mean")
    correlation_threshold = prm.get("correlation_threshold", 0.90)
    roi_detector = create_roi(dic=prm)

    # parameters below must be provided
    try:
        detector_name = prm["detector"]
        beamline_name = prm["beamline"]
        rocking_angle = prm["rocking_angle"]
        isosurface_strain = prm["isosurface_strain"]
        output_size = prm["output_size"]
        save_frame = prm["save_frame"]
        data_frame = prm["data_frame"]
        scan = prm["scan"]
        sample_name = prm["sample_name"]
        root_folder = prm["root_folder"]
    except KeyError as ex:
        print("Required parameter not defined")
        raise ex

    prm["sample"] = (f"{sample_name}+{scan}",)
    #########################
    # Check some parameters #
    #########################
    if not prm.get("backend"):
        prm["backend"] = "Qt5Agg"
    matplotlib.use(prm["backend"])
    if prm["simulation"]:
        invert_phase = False
        correct_refraction = 0
    if invert_phase:
        phase_fieldname = "disp"
    else:
        phase_fieldname = "phase"

    if data_frame == "detector":
        is_orthogonal = False
    else:
        is_orthogonal = True

    if data_frame == "crystal" and save_frame != "crystal":
        print(
            "data already in the crystal frame before phase retrieval,"
            " it is impossible to come back to the laboratory "
            "frame, parameter 'save_frame' defaulted to 'crystal'"
        )
        save_frame = "crystal"

    axis_to_array_xyz = {
        "x": np.array([1, 0, 0]),
        "y": np.array([0, 1, 0]),
        "z": np.array([0, 0, 1]),
    }  # in xyz order

    ###############
    # Set backend #
    ###############
    if prm.get("backend") is not None:
        try:
            plt.switch_backend(prm["backend"])
        except ModuleNotFoundError:
            print(f"{prm['backend']} backend is not supported.")

    ###################
    # define colormap #
    ###################
    if prm.get("grey_background"):
        bad_color = "0.7"
    else:
        bad_color = "1.0"  # white background
    colormap = gu.Colormap(bad_color=bad_color)
    my_cmap = colormap.cmap

    #######################
    # Initialize detector #
    #######################
    detector = create_detector(
        name=detector_name,
        template_imagefile=prm.get("template_imagefile"),
        roi=roi_detector,
        binning=phasing_binning,
        preprocessing_binning=preprocessing_binning,
        pixel_size=prm.get("pixel_size"),
    )

    ####################################
    # define the experimental geometry #
    ####################################
    setup = Setup(
        beamline=beamline_name,
        detector=detector,
        energy=prm.get("energy"),
        outofplane_angle=prm.get("outofplane_angle"),
        inplane_angle=prm.get("inplane_angle"),
        tilt_angle=prm.get("tilt_angle"),
        rocking_angle=rocking_angle,
        distance=prm.get("sdd"),
        sample_offsets=prm.get("sample_offsets"),
        actuators=prm.get("actuators"),
        custom_scan=prm.get("custom_scan", False),
        custom_motors=prm.get("custom_motors"),
        dirbeam_detector_angles=prm.get("dirbeam_detector_angles"),
        direct_beam=prm.get("direct_beam"),
        is_series=prm.get("is_series", False),
    )

    ########################################
    # Initialize the paths and the logfile #
    ########################################
    setup.init_paths(
        sample_name=sample_name,
        scan_number=scan,
        root_folder=root_folder,
        data_dir=prm.get("data_dir"),
        save_dir=prm.get("save_dir"),
        specfile_name=prm.get("specfile_name"),
        template_imagefile=prm.get("template_imagefile"),
    )

    setup.create_logfile(
        scan_number=scan, root_folder=root_folder, filename=detector.specfile
    )

    # load the goniometer positions needed in the calculation
    # of the transformation matrix
    setup.read_logfile(scan_number=scan)

    ###################
    # print instances #
    ###################
    print(f'{"#"*(5+len(str(scan)))}\nScan {scan}\n{"#"*(5+len(str(scan)))}')
    print("\n##############\nSetup instance\n##############")
    pretty.pprint(setup.params)
    print("\n#################\nDetector instance\n#################")
    pretty.pprint(detector.params)

    ################
    # preload data #
    ################
    if prm.get("reconstruction_file") is not None:
        file_path = (prm["reconstruction_file"],)
    else:
        root = tk.Tk()
        root.withdraw()
        file_path = filedialog.askopenfilenames(
            initialdir=detector.scandir
            if prm.get("data_dir") is None
            else detector.datadir,
            filetypes=[
                ("NPZ", "*.npz"),
                ("NPY", "*.npy"),
                ("CXI", "*.cxi"),
                ("HDF5", "*.h5"),
            ],
        )

    nbfiles = len(file_path)
    plt.ion()

    obj, extension = util.load_file(file_path[0])
    if extension == ".h5":
        comment = comment + "_mode"

    print("\n###############\nProcessing data\n###############")
    nz, ny, nx = obj.shape
    print("Initial data size: (", nz, ",", ny, ",", nx, ")")
    if not original_size:
        original_size = obj.shape
    print("FFT size before accounting for phasing_binning", original_size)
    original_size = tuple(
        [
            original_size[index] // phasing_binning[index]
            for index in range(len(phasing_binning))
        ]
    )
    print("Binning used during phasing:", detector.binning)
    print("Padding back to original FFT size", original_size)
    obj = util.crop_pad(array=obj, output_shape=original_size)

    ###########################################################################
    # define range for orthogonalization and plotting - speed up calculations #
    ###########################################################################
    zrange, yrange, xrange = pu.find_datarange(
        array=obj, amplitude_threshold=0.05, keep_size=prm.get("keep_size", False)
    )

    numz = zrange * 2
    numy = yrange * 2
    numx = xrange * 2
    print(
        f"Data shape used for orthogonalization and plotting: ({numz}, {numy}, {numx})"
    )

    ####################################################################################
    # find the best reconstruction from the list, based on mean amplitude and variance #
    ####################################################################################
    if nbfiles > 1:
        print("\nTrying to find the best reconstruction\nSorting by ", sort_method)
        sorted_obj = pu.sort_reconstruction(
            file_path=file_path,
            amplitude_threshold=isosurface_strain,
            data_range=(zrange, yrange, xrange),
            sort_method=sort_method,
        )
    else:
        sorted_obj = [0]

    #######################################
    # load reconstructions and average it #
    #######################################
    avg_obj = np.zeros((numz, numy, numx))
    ref_obj = np.zeros((numz, numy, numx))
    avg_counter = 1
    print("\nAveraging using", nbfiles, "candidate reconstructions")
    for counter, value in enumerate(sorted_obj):
        obj, extension = util.load_file(file_path[value])
        print("\nOpening ", file_path[value])
        prm[f"from_file_{counter}"] = file_path[value]

        if prm.get("flip_reconstruction", False):
            obj = pu.flip_reconstruction(obj, debugging=True)

        if extension == ".h5":
            centering_method = "do_nothing"  # do not center, data is already cropped
            # just on support for mode decomposition
            # correct a roll after the decomposition into modes in PyNX
            obj = np.roll(obj, prm.get("roll_modes", [0, 0, 0]), axis=(0, 1, 2))
            fig, _, _ = gu.multislices_plot(
                abs(obj),
                sum_frames=True,
                plot_colorbar=True,
                title="1st mode after centering",
            )

        # use the range of interest defined above
        obj = util.crop_pad(obj, [2 * zrange, 2 * yrange, 2 * xrange], debugging=False)

        # align with average reconstruction
        if counter == 0:  # the fist array loaded will serve as reference object
            print("This reconstruction will be used as reference.")
            ref_obj = obj

        avg_obj, flag_avg = reg.average_arrays(
            avg_obj=avg_obj,
            ref_obj=ref_obj,
            obj=obj,
            support_threshold=0.25,
            correlation_threshold=correlation_threshold,
            aligning_option="dft",
            space=prm.get("averaging_space", "reciprocal_space"),
            reciprocal_space=False,
            is_orthogonal=is_orthogonal,
            debugging=debug,
        )
        avg_counter = avg_counter + flag_avg

    avg_obj = avg_obj / avg_counter
    if avg_counter > 1:
        print("\nAverage performed over ", avg_counter, "reconstructions\n")
    del obj, ref_obj
    gc.collect()

    ################
    # unwrap phase #
    ################
    phase, extent_phase = pu.unwrap(
        avg_obj,
        support_threshold=threshold_unwrap_refraction,
        debugging=debug,
        reciprocal_space=False,
        is_orthogonal=is_orthogonal,
    )

    print(
        "Extent of the phase over an extended support (ceil(phase range)) ~ ",
        int(extent_phase),
        "(rad)",
    )
    phase = util.wrap(phase, start_angle=-extent_phase / 2, range_angle=extent_phase)
    if debug:
        gu.multislices_plot(
            phase,
            width_z=2 * zrange,
            width_y=2 * yrange,
            width_x=2 * xrange,
            plot_colorbar=True,
            title="Phase after unwrap + wrap",
            reciprocal_space=False,
            is_orthogonal=is_orthogonal,
        )

    #############################################
    # phase ramp removal before phase filtering #
    #############################################
    amp, phase, rampz, rampy, rampx = pu.remove_ramp(
        amp=abs(avg_obj),
        phase=phase,
        initial_shape=original_size,
        method="gradient",
        amplitude_threshold=isosurface_strain,
        threshold_gradient=threshold_gradient,
    )
    del avg_obj
    gc.collect()

    if debug:
        gu.multislices_plot(
            phase,
            width_z=2 * zrange,
            width_y=2 * yrange,
            width_x=2 * xrange,
            plot_colorbar=True,
            title="Phase after ramp removal",
            reciprocal_space=False,
            is_orthogonal=is_orthogonal,
        )

    ########################
    # phase offset removal #
    ########################
    support = np.zeros(amp.shape)
    support[amp > isosurface_strain * amp.max()] = 1
    phase = pu.remove_offset(
        array=phase,
        support=support,
        offset_method=offset_method,
        phase_offset=phase_offset,
        offset_origin=offset_origin,
        title="Phase",
        debugging=debug,
    )
    del support
    gc.collect()

    phase = util.wrap(
        obj=phase, start_angle=-extent_phase / 2, range_angle=extent_phase
    )

    ##############################################################################
    # average the phase over a window or apodize to reduce noise in strain plots #
    ##############################################################################
    half_width_avg_phase = prm.get("half_width_avg_phase", 0)
    if half_width_avg_phase != 0:
        bulk = pu.find_bulk(
            amp=amp, support_threshold=isosurface_strain, method="threshold"
        )
        # the phase should be averaged only in the support defined by the isosurface
        phase = pu.mean_filter(
            array=phase, support=bulk, half_width=half_width_avg_phase
        )
        del bulk
        gc.collect()

    if half_width_avg_phase != 0:
        comment = comment + "_avg" + str(2 * half_width_avg_phase + 1)

    gridz, gridy, gridx = np.meshgrid(
        np.arange(0, numz, 1),
        np.arange(0, numy, 1),
        np.arange(0, numx, 1),
        indexing="ij",
    )

    phase = (
        phase + gridz * rampz + gridy * rampy + gridx * rampx
    )  # put back the phase ramp otherwise the diffraction
    # pattern will be shifted and the prtf messed up

    if prm.get("apodize", False):
        amp, phase = pu.apodize(
            amp=amp,
            phase=phase,
            initial_shape=original_size,
            window_type=prm.get("apodization_window", "blackman"),
            sigma=prm.get("apodization_sigma", [0.30, 0.30, 0.30]),
            mu=prm.get("apodization_mu", [0.0, 0.0, 0.0]),
            alpha=prm.get("apodization_alpha", [1.0, 1.0, 1.0]),
            is_orthogonal=is_orthogonal,
            debugging=True,
        )
        comment = comment + "_apodize_" + prm.get("apodization_window", "blackman")

    ################################################################
    # save the phase with the ramp for PRTF calculations,          #
    # otherwise the object will be misaligned with the measurement #
    ################################################################
    np.savez_compressed(
        detector.savedir + "S" + str(scan) + "_avg_obj_prtf" + comment,
        obj=amp * np.exp(1j * phase),
    )

    ####################################################
    # remove again phase ramp before orthogonalization #
    ####################################################
    phase = phase - gridz * rampz - gridy * rampy - gridx * rampx

    avg_obj = amp * np.exp(1j * phase)  # here the phase is again wrapped in [-pi pi[

    del amp, phase, gridz, gridy, gridx, rampz, rampy, rampx
    gc.collect()

    ######################
    # centering of array #
    ######################
    if centering_method == "max":
        avg_obj = pu.center_max(avg_obj)
        # shift based on max value,
        # required if it spans across the edge of the array before COM
    elif centering_method == "com":
        avg_obj = pu.center_com(avg_obj)
    elif centering_method == "max_com":
        avg_obj = pu.center_max(avg_obj)
        avg_obj = pu.center_com(avg_obj)

    #######################
    #  save support & vti #
    #######################
    if prm.get("save_support", False):
        # to be used as starting support in phasing, hence still in the detector frame
        support = np.zeros((numz, numy, numx))
        support[abs(avg_obj) / abs(avg_obj).max() > 0.01] = 1
        # low threshold because support will be cropped by shrinkwrap during phasing
        np.savez_compressed(
            detector.savedir + "S" + str(scan) + "_support" + comment, obj=support
        )
        del support
        gc.collect()

    if prm.get("save_rawdata", False):
        np.savez_compressed(
            detector.savedir + "S" + str(scan) + "_raw_amp-phase" + comment,
            amp=abs(avg_obj),
            phase=np.angle(avg_obj),
        )

        # voxel sizes in the detector frame
        voxel_z, voxel_y, voxel_x = setup.voxel_sizes_detector(
            array_shape=original_size,
            tilt_angle=(
                prm.get("tilt_angle")
                * detector.preprocessing_binning[0]
                * detector.binning[0]
            ),
            pixel_x=detector.pixelsize_x,
            pixel_y=detector.pixelsize_y,
            verbose=True,
        )
        # save raw amp & phase to VTK
        # in VTK, x is downstream, y vertical, z inboard,
        # thus need to flip the last axis
        gu.save_to_vti(
            filename=os.path.join(
                detector.savedir, "S" + str(scan) + "_raw_amp-phase" + comment + ".vti"
            ),
            voxel_size=(voxel_z, voxel_y, voxel_x),
            tuple_array=(abs(avg_obj), np.angle(avg_obj)),
            tuple_fieldnames=("amp", "phase"),
            amplitude_threshold=0.01,
        )

    #########################################################
    # calculate q of the Bragg peak in the laboratory frame #
    #########################################################
    q_lab = (
        setup.q_laboratory
    )  # (1/A), in the laboratory frame z downstream, y vertical, x outboard
    qnorm = np.linalg.norm(q_lab)
    q_lab = q_lab / qnorm

    angle = simu.angle_vectors(
        ref_vector=[q_lab[2], q_lab[1], q_lab[0]],
        test_vector=axis_to_array_xyz[ref_axis_q],
    )
    print(
        f"\nNormalized diffusion vector in the laboratory frame (z*, y*, x*): "
        f"({q_lab[0]:.4f} 1/A, {q_lab[1]:.4f} 1/A, {q_lab[2]:.4f} 1/A)"
    )

    planar_dist = 2 * np.pi / qnorm  # qnorm should be in angstroms
    print(f"Wavevector transfer: {qnorm:.4f} 1/A")
    print(f"Atomic planar distance: {planar_dist:.4f} A")
    print(f"\nAngle between q_lab and {ref_axis_q} = {angle:.2f} deg")
    if debug:
        print(
            "Angle with y in zy plane = "
            f"{np.arctan(q_lab[0]/q_lab[1])*180/np.pi:.2f} deg"
        )
        print(
            "Angle with y in xy plane = "
            f"{np.arctan(-q_lab[2]/q_lab[1])*180/np.pi:.2f} deg"
        )
        print(
            "Angle with z in xz plane = "
            f"{180+np.arctan(q_lab[2]/q_lab[0])*180/np.pi:.2f} deg\n"
        )

    planar_dist = planar_dist / 10  # switch to nm

    #######################
    #  orthogonalize data #
    #######################
    print("\nShape before orthogonalization", avg_obj.shape, "\n")
    if data_frame == "detector":
        if debug:
            phase, _ = pu.unwrap(
                avg_obj,
                support_threshold=threshold_unwrap_refraction,
                debugging=True,
                reciprocal_space=False,
                is_orthogonal=False,
            )
            gu.multislices_plot(
                phase,
                width_z=2 * zrange,
                width_y=2 * yrange,
                width_x=2 * xrange,
                sum_frames=False,
                plot_colorbar=True,
                reciprocal_space=False,
                is_orthogonal=False,
                title="unwrapped phase before orthogonalization",
            )
            del phase
            gc.collect()

        if not prm.get("outofplane_angle") and not prm.get("inplane_angle"):
            print("Trying to correct detector angles using the direct beam")
            # corrected detector angles not provided
            if bragg_peak is None and detector.template_imagefile is not None:
                # Bragg peak position not provided, find it from the data
                data, _, _, _ = setup.diffractometer.load_check_dataset(
                    scan_number=scan,
                    detector=detector,
                    setup=setup,
                    frames_pattern=prm.get("frames_pattern"),
                    bin_during_loading=False,
                    flatfield=prm.get("flatfield"),
                    hotpixels=prm.get("hotpix_array"),
                    background=prm.get("background"),
                    normalize=prm.get("normalize_flux", "skip"),
                )
                bragg_peak = bu.find_bragg(
                    data=data,
                    peak_method="maxcom",
                    roi=detector.roi,
                    binning=None,
                )
                roi_center = (
                    bragg_peak[0],
                    bragg_peak[1] - detector.roi[0],  # no binning as in bu.find_bragg
                    bragg_peak[2] - detector.roi[2],  # no binning as in bu.find_bragg
                )
                bu.show_rocking_curve(
                    data,
                    roi_center=roi_center,
                    tilt_values=setup.incident_angles,
                    savedir=detector.savedir,
                )
            setup.correct_detector_angles(bragg_peak_position=bragg_peak)
            prm["outofplane_angle"] = setup.outofplane_angle
            prm["inplane_angle"] = setup.inplane_angle

        obj_ortho, voxel_size, transfer_matrix = setup.ortho_directspace(
            arrays=avg_obj,
            q_com=np.array([q_lab[2], q_lab[1], q_lab[0]]),
            initial_shape=original_size,
            voxel_size=fix_voxel,
            reference_axis=axis_to_array_xyz[ref_axis_q],
            fill_value=0,
            debugging=True,
            title="amplitude",
        )
        prm["transformation_matrix"] = transfer_matrix
    else:  # data already orthogonalized using xrayutilities
        # or the linearized transformation matrix
        obj_ortho = avg_obj
        try:
            print("Select the file containing QxQzQy")
            file_path = filedialog.askopenfilename(
                title="Select the file containing QxQzQy",
                initialdir=detector.savedir,
                filetypes=[("NPZ", "*.npz")],
            )
            npzfile = np.load(file_path)
            qx = npzfile["qx"]
            qy = npzfile["qy"]
            qz = npzfile["qz"]
        except FileNotFoundError:
            raise FileNotFoundError(
                "q values not provided, the voxel size cannot be calculated"
            )
        dy_real = (
            2 * np.pi / abs(qz.max() - qz.min()) / 10
        )  # in nm qz=y in nexus convention
        dx_real = (
            2 * np.pi / abs(qy.max() - qy.min()) / 10
        )  # in nm qy=x in nexus convention
        dz_real = (
            2 * np.pi / abs(qx.max() - qx.min()) / 10
        )  # in nm qx=z in nexus convention
        print(
            f"direct space voxel size from q values: ({dz_real:.2f} nm,"
            f" {dy_real:.2f} nm, {dx_real:.2f} nm)"
        )
        if fix_voxel:
            voxel_size = fix_voxel
            print(f"Direct space pixel size for the interpolation: {voxel_size} (nm)")
            print("Interpolating...\n")
            obj_ortho = pu.regrid(
                array=obj_ortho,
                old_voxelsize=(dz_real, dy_real, dx_real),
                new_voxelsize=voxel_size,
            )
        else:
            # no need to interpolate
            voxel_size = dz_real, dy_real, dx_real  # in nm

        if (
            data_frame == "laboratory"
        ):  # the object must be rotated into the crystal frame
            # before the strain calculation
            print("Rotating the object in the crystal frame for the strain calculation")

            amp, phase = util.rotate_crystal(
                arrays=(abs(obj_ortho), np.angle(obj_ortho)),
                is_orthogonal=True,
                reciprocal_space=False,
                voxel_size=voxel_size,
                debugging=(True, False),
                axis_to_align=q_lab[::-1],
                reference_axis=axis_to_array_xyz[ref_axis_q],
                title=("amp", "phase"),
            )

            obj_ortho = amp * np.exp(
                1j * phase
            )  # here the phase is again wrapped in [-pi pi[
            del amp, phase

    del avg_obj
    gc.collect()

    ######################################################
    # center the object (centering based on the modulus) #
    ######################################################
    print("\nCentering the crystal")
    obj_ortho = pu.center_com(obj_ortho)

    ####################
    # Phase unwrapping #
    ####################
    print("\nPhase unwrapping")
    phase, extent_phase = pu.unwrap(
        obj_ortho,
        support_threshold=threshold_unwrap_refraction,
        debugging=True,
        reciprocal_space=False,
        is_orthogonal=True,
    )
    amp = abs(obj_ortho)
    del obj_ortho
    gc.collect()

    #############################################
    # invert phase: -1*phase = displacement * q #
    #############################################
    if invert_phase:
        phase = -1 * phase

    ########################################
    # refraction and absorption correction #
    ########################################
    if correct_refraction:  # or correct_absorption:
        bulk = pu.find_bulk(
            amp=amp,
            support_threshold=threshold_unwrap_refraction,
            method=prm.get("optical_path_method", "threshold"),
            debugging=debug,
        )

        kin = setup.incident_wavevector
        kout = setup.exit_wavevector
        # kin and kout were calculated in the laboratory frame,
        # but after the geometric transformation of the crystal, this
        # latter is always in the crystal frame (for simpler strain calculation).
        # We need to transform kin and kout back
        # into the crystal frame (also, xrayutilities output is in crystal frame)
        kin = util.rotate_vector(
            vectors=[kin[2], kin[1], kin[0]],
            axis_to_align=axis_to_array_xyz[ref_axis_q],
            reference_axis=[q_lab[2], q_lab[1], q_lab[0]],
        )
        kout = util.rotate_vector(
            vectors=[kout[2], kout[1], kout[0]],
            axis_to_align=axis_to_array_xyz[ref_axis_q],
            reference_axis=[q_lab[2], q_lab[1], q_lab[0]],
        )

        # calculate the optical path of the incoming wavevector
        path_in = pu.get_opticalpath(
            support=bulk, direction="in", k=kin, debugging=debug
        )  # path_in already in nm

        # calculate the optical path of the outgoing wavevector
        path_out = pu.get_opticalpath(
            support=bulk, direction="out", k=kout, debugging=debug
        )  # path_our already in nm

        optical_path = path_in + path_out
        del path_in, path_out
        gc.collect()

        if correct_refraction:
            phase_correction = (
                2 * np.pi / (1e9 * setup.wavelength) * prm["dispersion"] * optical_path
            )
            phase = phase + phase_correction

            gu.multislices_plot(
                np.multiply(phase_correction, bulk),
                width_z=2 * zrange,
                width_y=2 * yrange,
                width_x=2 * xrange,
                sum_frames=False,
                plot_colorbar=True,
                vmin=0,
                vmax=np.nan,
                title="Refraction correction on the support",
                is_orthogonal=True,
                reciprocal_space=False,
            )
        correct_absorption = False
        if correct_absorption:
            amp_correction = np.exp(
                2 * np.pi / (1e9 * setup.wavelength) * prm["absorption"] * optical_path
            )
            amp = amp * amp_correction

            gu.multislices_plot(
                np.multiply(amp_correction, bulk),
                width_z=2 * zrange,
                width_y=2 * yrange,
                width_x=2 * xrange,
                sum_frames=False,
                plot_colorbar=True,
                vmin=1,
                vmax=1.1,
                title="Absorption correction on the support",
                is_orthogonal=True,
                reciprocal_space=False,
            )

        del bulk, optical_path
        gc.collect()

    ##############################################
    # phase ramp and offset removal (mean value) #
    ##############################################
    print("\nPhase ramp removal")
    amp, phase, _, _, _ = pu.remove_ramp(
        amp=amp,
        phase=phase,
        initial_shape=original_size,
        method=prm.get("phase_ramp_removal", "gradient"),
        amplitude_threshold=isosurface_strain,
        threshold_gradient=threshold_gradient,
        debugging=debug,
    )

    ########################
    # phase offset removal #
    ########################
    print("\nPhase offset removal")
    support = np.zeros(amp.shape)
    support[amp > isosurface_strain * amp.max()] = 1
    phase = pu.remove_offset(
        array=phase,
        support=support,
        offset_method=offset_method,
        phase_offset=phase_offset,
        offset_origin=offset_origin,
        title="Orthogonal phase",
        debugging=debug,
        reciprocal_space=False,
        is_orthogonal=True,
    )
    del support
    gc.collect()
    # Wrap the phase around 0 (no more offset)
    phase = util.wrap(
        obj=phase, start_angle=-extent_phase / 2, range_angle=extent_phase
    )

    ################################################################
    # calculate the strain depending on which axis q is aligned on #
    ################################################################
    print(f"\nCalculation of the strain along {ref_axis_q}")
    strain = pu.get_strain(
        phase=phase,
        planar_distance=planar_dist,
        voxel_size=voxel_size,
        reference_axis=ref_axis_q,
        extent_phase=extent_phase,
        method=prm.get("strain_method", "default"),
        debugging=debug,
    )

    ################################################
    # optionally rotates back the crystal into the #
    # laboratory frame (for debugging purpose)     #
    ################################################
    q_final = None
    if save_frame in {"laboratory", "lab_flat_sample"}:
        comment = comment + "_labframe"
        print("\nRotating back the crystal in laboratory frame")
        amp, phase, strain = util.rotate_crystal(
            arrays=(amp, phase, strain),
            axis_to_align=axis_to_array_xyz[ref_axis_q],
            voxel_size=voxel_size,
            is_orthogonal=True,
            reciprocal_space=False,
            reference_axis=[q_lab[2], q_lab[1], q_lab[0]],
            debugging=(True, False, False),
            title=("amp", "phase", "strain"),
        )
        # q_lab is already in the laboratory frame
        q_final = q_lab

    if save_frame == "lab_flat_sample":
        comment = comment + "_flat"
        print("\nSending sample stage circles to 0")
        (amp, phase, strain), q_final = setup.diffractometer.flatten_sample(
            arrays=(amp, phase, strain),
            voxel_size=voxel_size,
            q_com=q_lab[::-1],  # q_com needs to be in xyz order
            is_orthogonal=True,
            reciprocal_space=False,
            rocking_angle=setup.rocking_angle,
            debugging=(True, False, False),
            title=("amp", "phase", "strain"),
        )
    if save_frame == "crystal":
        # rotate also q_lab to have it along ref_axis_q,
        # as a cross-checkm, vectors needs to be in xyz order
        comment = comment + "_crystalframe"
        q_final = util.rotate_vector(
            vectors=q_lab[::-1],
            axis_to_align=axis_to_array_xyz[ref_axis_q],
            reference_axis=q_lab[::-1],
        )

    ###############################################
    # rotates the crystal e.g. for easier slicing #
    # of the result along a particular direction  #
    ###############################################
    # typically this is an inplane rotation, q should stay aligned with the axis
    # along which the strain was calculated
    if prm.get("align_axis", False):
        print("\nRotating arrays for visualization")
        amp, phase, strain = util.rotate_crystal(
            arrays=(amp, phase, strain),
            reference_axis=axis_to_array_xyz[prm["ref_axis"]],
            axis_to_align=prm["axis_to_align"],
            voxel_size=voxel_size,
            debugging=(True, False, False),
            is_orthogonal=True,
            reciprocal_space=False,
            title=("amp", "phase", "strain"),
        )
        # rotate q accordingly, vectors needs to be in xyz order
        q_final = util.rotate_vector(
            vectors=q_final[::-1],
            axis_to_align=axis_to_array_xyz[prm["ref_axis"]],
            reference_axis=prm["axis_to_align"],
        )

    q_final = q_final * qnorm
    print(
        f"\nq_final = ({q_final[0]:.4f} 1/A,"
        f" {q_final[1]:.4f} 1/A, {q_final[2]:.4f} 1/A)"
    )

    ##############################################
    # pad array to fit the output_size parameter #
    ##############################################
    if output_size is not None:
        amp = util.crop_pad(array=amp, output_shape=output_size)
        phase = util.crop_pad(array=phase, output_shape=output_size)
        strain = util.crop_pad(array=strain, output_shape=output_size)
    print(f"\nFinal data shape: {amp.shape}")

    ######################
    # save result to vtk #
    ######################
    print(
        f"\nVoxel size: ({voxel_size[0]:.2f} nm, {voxel_size[1]:.2f} nm,"
        f" {voxel_size[2]:.2f} nm)"
    )
    bulk = pu.find_bulk(
        amp=amp, support_threshold=isosurface_strain, method="threshold"
    )
    if save:
        prm["comment"] = comment
        np.savez_compressed(
            f"{detector.savedir}S{scan}_amp{phase_fieldname}strain{comment}",
            amp=amp,
            phase=phase,
            bulk=bulk,
            strain=strain,
            q_com=q_final,
            voxel_sizes=voxel_size,
            detector=detector.params,
            setup=setup.params,
            params=prm,
        )

        # save results in hdf5 file
        with h5py.File(
            f"{detector.savedir}S{scan}_amp{phase_fieldname}strain{comment}.h5", "w"
        ) as hf:
            out = hf.create_group("output")
            par = hf.create_group("params")
            out.create_dataset("amp", data=amp)
            out.create_dataset("bulk", data=bulk)
            out.create_dataset("phase", data=phase)
            out.create_dataset("strain", data=strain)
            out.create_dataset("q_com", data=q_final)
            out.create_dataset("voxel_sizes", data=voxel_size)
            par.create_dataset("detector", data=str(detector.params))
            par.create_dataset("setup", data=str(setup.params))
            par.create_dataset("parameters", data=str(prm))

        # save amp & phase to VTK
        # in VTK, x is downstream, y vertical, z inboard,
        # thus need to flip the last axis
        gu.save_to_vti(
            filename=os.path.join(
                detector.savedir,
                "S"
                + str(scan)
                + "_amp-"
                + phase_fieldname
                + "-strain"
                + comment
                + ".vti",
            ),
            voxel_size=voxel_size,
            tuple_array=(amp, bulk, phase, strain),
            tuple_fieldnames=("amp", "bulk", phase_fieldname, "strain"),
            amplitude_threshold=0.01,
        )

    ######################################
    # estimate the volume of the crystal #
    ######################################
    amp = amp / amp.max()
    temp_amp = np.copy(amp)
    temp_amp[amp < isosurface_strain] = 0
    temp_amp[np.nonzero(temp_amp)] = 1
    volume = temp_amp.sum() * reduce(lambda x, y: x * y, voxel_size)  # in nm3
    del temp_amp
    gc.collect()

    ##############################
    # plot slices of the results #
    ##############################
    pixel_spacing = [tick_spacing / vox for vox in voxel_size]
    print(
        "\nPhase extent without / with thresholding the modulus "
        f"(threshold={isosurface_strain}): {phase.max()-phase.min():.2f} rad, "
        f"{phase[np.nonzero(bulk)].max()-phase[np.nonzero(bulk)].min():.2f} rad"
    )
    piz, piy, pix = np.unravel_index(phase.argmax(), phase.shape)
    print(
        f"phase.max() = {phase[np.nonzero(bulk)].max():.2f} "
        f"at voxel ({piz}, {piy}, {pix})"
    )
    strain[bulk == 0] = np.nan
    phase[bulk == 0] = np.nan

    # plot the slice at the maximum phase
    gu.combined_plots(
        (phase[piz, :, :], phase[:, piy, :], phase[:, :, pix]),
        tuple_sum_frames=False,
        tuple_sum_axis=0,
        tuple_width_v=None,
        tuple_width_h=None,
        tuple_colorbar=True,
        tuple_vmin=np.nan,
        tuple_vmax=np.nan,
        tuple_title=("phase at max in xy", "phase at max in xz", "phase at max in yz"),
        tuple_scale="linear",
        cmap=my_cmap,
        is_orthogonal=True,
        reciprocal_space=False,
    )

    # bulk support
    fig, _, _ = gu.multislices_plot(
        bulk,
        sum_frames=False,
        title="Orthogonal bulk",
        vmin=0,
        vmax=1,
        is_orthogonal=True,
        reciprocal_space=False,
    )
    fig.text(0.60, 0.45, "Scan " + str(scan), size=20)
    fig.text(
        0.60,
        0.40,
        "Bulk - isosurface=" + str("{:.2f}".format(isosurface_strain)),
        size=20,
    )
    plt.pause(0.1)
    if save:
        plt.savefig(detector.savedir + "S" + str(scan) + "_bulk" + comment + ".png")

    # amplitude
    fig, _, _ = gu.multislices_plot(
        amp,
        sum_frames=False,
        title="Normalized orthogonal amp",
        vmin=0,
        vmax=1,
        tick_direction=tick_direction,
        tick_width=tick_width,
        tick_length=tick_length,
        pixel_spacing=pixel_spacing,
        plot_colorbar=True,
        is_orthogonal=True,
        reciprocal_space=False,
    )
    fig.text(0.60, 0.45, f"Scan {scan}", size=20)
    fig.text(
        0.60,
        0.40,
        f"Voxel size=({voxel_size[0]:.1f}, {voxel_size[1]:.1f}, "
        f"{voxel_size[2]:.1f}) (nm)",
        size=20,
    )
    fig.text(0.60, 0.35, f"Ticks spacing={tick_spacing} nm", size=20)
    fig.text(0.60, 0.30, f"Volume={int(volume)} nm3", size=20)
    fig.text(0.60, 0.25, "Sorted by " + sort_method, size=20)
    fig.text(0.60, 0.20, f"correlation threshold={correlation_threshold}", size=20)
    fig.text(0.60, 0.15, f"average over {avg_counter} reconstruction(s)", size=20)
    fig.text(0.60, 0.10, f"Planar distance={planar_dist:.5f} nm", size=20)
    if prm.get("get_temperature", False):
        temperature = pu.bragg_temperature(
            spacing=planar_dist * 10,
            reflection=prm["reflection"],
            spacing_ref=prm.get("reference_spacing"),
            temperature_ref=prm.get("reference_temperature"),
            use_q=False,
            material="Pt",
        )
        fig.text(0.60, 0.05, f"Estimated T={temperature} C", size=20)
    if save:
        plt.savefig(detector.savedir + f"S{scan}_amp" + comment + ".png")

    # amplitude histogram
    fig, ax = plt.subplots(1, 1)
    ax.hist(amp[amp > 0.05 * amp.max()].flatten(), bins=250)
    ax.set_ylim(bottom=1)
    ax.tick_params(
        labelbottom=True,
        labelleft=True,
        direction="out",
        length=tick_length,
        width=tick_width,
    )
    ax.spines["right"].set_linewidth(1.5)
    ax.spines["left"].set_linewidth(1.5)
    ax.spines["top"].set_linewidth(1.5)
    ax.spines["bottom"].set_linewidth(1.5)
    fig.savefig(detector.savedir + f"S{scan}_histo_amp" + comment + ".png")

    # phase
    fig, _, _ = gu.multislices_plot(
        phase,
        sum_frames=False,
        title="Orthogonal displacement",
        vmin=-prm.get("phase_range", np.pi / 2),
        vmax=prm.get("phase_range", np.pi / 2),
        tick_direction=tick_direction,
        cmap=my_cmap,
        tick_width=tick_width,
        tick_length=tick_length,
        pixel_spacing=pixel_spacing,
        plot_colorbar=True,
        is_orthogonal=True,
        reciprocal_space=False,
    )
    fig.text(0.60, 0.30, f"Scan {scan}", size=20)
    fig.text(
        0.60,
        0.25,
        f"Voxel size=({voxel_size[0]:.1f}, {voxel_size[1]:.1f}, "
        f"{voxel_size[2]:.1f}) (nm)",
        size=20,
    )
    fig.text(0.60, 0.20, f"Ticks spacing={tick_spacing} nm", size=20)
    fig.text(0.60, 0.15, f"average over {avg_counter} reconstruction(s)", size=20)
    if half_width_avg_phase > 0:
        fig.text(
            0.60, 0.10, f"Averaging over {2*half_width_avg_phase+1} pixels", size=20
        )
    else:
        fig.text(0.60, 0.10, "No phase averaging", size=20)
    if save:
        plt.savefig(detector.savedir + f"S{scan}_displacement" + comment + ".png")

    # strain
    fig, _, _ = gu.multislices_plot(
        strain,
        sum_frames=False,
        title="Orthogonal strain",
        vmin=-prm.get("strain_range", 0.002),
        vmax=prm.get("strain_range", 0.002),
        tick_direction=tick_direction,
        tick_width=tick_width,
        tick_length=tick_length,
        plot_colorbar=True,
        cmap=my_cmap,
        pixel_spacing=pixel_spacing,
        is_orthogonal=True,
        reciprocal_space=False,
    )
    fig.text(0.60, 0.30, f"Scan {scan}", size=20)
    fig.text(
        0.60,
        0.25,
        f"Voxel size=({voxel_size[0]:.1f}, "
        f"{voxel_size[1]:.1f}, {voxel_size[2]:.1f}) (nm)",
        size=20,
    )
    fig.text(0.60, 0.20, f"Ticks spacing={tick_spacing} nm", size=20)
    fig.text(0.60, 0.15, f"average over {avg_counter} reconstruction(s)", size=20)
    if half_width_avg_phase > 0:
        fig.text(
            0.60, 0.10, f"Averaging over {2*half_width_avg_phase+1} pixels", size=20
        )
    else:
        fig.text(0.60, 0.10, "No phase averaging", size=20)
    if save:
        plt.savefig(detector.savedir + f"S{scan}_strain" + comment + ".png")
Exemple #16
0
        max_z, max_y, max_x = np.unravel_index(data.argmax(), shape=data.shape)
        com_z, com_y, com_x = center_of_mass(
            data[:,
                 int(max_y) - debug_pix:int(max_y) + debug_pix,
                 int(max_x) - debug_pix:int(max_x) + debug_pix, ])
        # correct the pixel offset due to the ROI defined by debug_pix around the max
        piz = com_z  # the data was not cropped along the first axis
        piy = com_y + max_y - debug_pix
        pix = com_x + max_x - debug_pix

    if debug:
        fig, _, _ = gu.multislices_plot(
            data,
            sum_frames=True,
            plot_colorbar=True,
            cmap=my_cmap,
            title="scan" + str(scan_nb),
            scale="log",
            is_orthogonal=False,
            reciprocal_space=True,
        )
        fig.text(0.60,
                 0.30,
                 f"(piz, piy, pix) = ({piz:.1f}, {piy:.1f}, {pix:.1f})",
                 size=12)
        plt.draw()

        if peak_method == "max_com":
            fig, _, _ = gu.multislices_plot(
                data[:,
                     int(max_y) - debug_pix:int(max_y) + debug_pix,
                     int(max_x) - debug_pix:int(max_x) + debug_pix, ],
Exemple #17
0
final_binning = np.multiply(pre_binning, phasing_binning)
detector.binning = final_binning
print('Pixel sizes after phasing_binning (vertical, horizontal): ', detector.pixelsize_y, detector.pixelsize_x, '(m)')
diff_pattern = pu.bin_data(array=diff_pattern, binning=phasing_binning, debugging=False)
mask = pu.bin_data(array=mask, binning=phasing_binning, debugging=False)

numz, numy, numx = diff_pattern.shape  # this shape will be used for the calculation of q values
print('\nMeasured data shape =', numz, numy, numx, ' Max(measured amplitude)=', np.sqrt(diff_pattern).max())
diff_pattern[np.nonzero(mask)] = 0

z0, y0, x0 = center_of_mass(diff_pattern)
z0, y0, x0 = [int(z0), int(y0), int(x0)]
print("COM of measured pattern after masking: ", z0, y0, x0, ' Number of unmasked photons =', diff_pattern.sum())

fig, _, _ = gu.multislices_plot(np.sqrt(diff_pattern), sum_frames=False, title='3D diffraction amplitude', vmin=0,
                                vmax=3.5, is_orthogonal=False, reciprocal_space=True, slice_position=[z0, y0, x0],
                                scale='log', plot_colorbar=True)

plt.figure()
plt.imshow(np.log10(np.sqrt(diff_pattern).sum(axis=0)), cmap=my_cmap, vmin=0, vmax=3.5)
plt.title('abs(diffraction amplitude).sum(axis=0)')
plt.colorbar()
plt.pause(0.1)

################################################
# calculate the q matrix respective to the COM #
################################################
hxrd.Ang2Q.init_area('z-', 'y+', cch1=int(y0), cch2=int(x0), Nch1=numy, Nch2=numx,
                     pwidth1=detector.pixelsize_y, pwidth2=detector.pixelsize_x, distance=setup.distance)
# first two arguments in init_area are the direction of the detector
if simulation:
Exemple #18
0
                                       int(y0) - 20:int(y0) + 21,
                                       int(x0) - 20:int(x0) + 21, ])
z0, y0, x0 = [
    int(np.rint(z0 - 20 + fine_com[0])),
    int(np.rint(y0 - 20 + fine_com[1])),
    int(np.rint(x0 - 20 + fine_com[2])),
]
print(f"refined COM: {z0}, {y0}, {x0}, "
      f"Number of unmasked photons = {diff_pattern.sum():.0f}\n")

fig, _, _ = gu.multislices_plot(
    np.sqrt(diff_pattern),
    sum_frames=False,
    title="3D diffraction amplitude",
    vmin=0,
    vmax=3.5,
    is_orthogonal=False,
    reciprocal_space=True,
    slice_position=[z0, y0, x0],
    scale="log",
    plot_colorbar=True,
)

plt.figure()
plt.imshow(np.log10(np.sqrt(diff_pattern).sum(axis=0)),
           cmap=my_cmap,
           vmin=0,
           vmax=3.5)
plt.title("abs(diffraction amplitude).sum(axis=0)")
plt.colorbar()
plt.pause(0.1)
Exemple #19
0
if use_bulk:
    try:
        bulk = npzfile["bulk"]
    except KeyError:
        print("Bulk is not of key of the npz file")
        print("Using the modulus and support_threshold to define the bulk")
        amp = npzfile["amp"]
        bulk = pu.find_bulk(amp=amp,
                            support_threshold=support_threshold,
                            method="threshold")
        if debug:
            gu.multislices_plot(
                amp,
                sum_frames=False,
                title="Amplitude",
                plot_colorbar=True,
                cmap=my_cmap,
                is_orthogonal=True,
                reciprocal_space=False,
            )
        del amp
    nz, ny, nx = bulk.shape
    support = bulk
else:  # use amplitude
    print("Using the modulus and support_threshold to define the support")
    amp = npzfile["amp"]
    nz, ny, nx = amp.shape
    support = np.ones((nz, ny, nx))
    support[abs(amp) < support_threshold * abs(amp).max()] = 0
    if debug:
        gu.multislices_plot(
Exemple #20
0
######################
# define the support #
######################
obj = abs(obj)
min_obj = obj[np.nonzero(obj)].min()
obj = obj / min_obj  # normalize to the non-zero min to avoid dividing by small numbers
obj[obj == 0] = min_offset  # avoid dividing by 0
support = np.zeros(obj.shape)
support[obj >= isosurface_threshold * obj.max()] = 1
support[support == 0] = min_offset

if debug:
    gu.multislices_plot(obj,
                        sum_frames=False,
                        reciprocal_space=False,
                        is_orthogonal=True,
                        plot_colorbar=True,
                        title='normalized modulus')
    gu.multislices_plot(support,
                        sum_frames=False,
                        reciprocal_space=False,
                        is_orthogonal=True,
                        vmin=0,
                        vmax=1,
                        plot_colorbar=True,
                        title=f'support at threshold {isosurface_threshold}')

###################################
# calculate the blurring function #
###################################
psf_guess = pu.gaussian_window(window_shape=obj.shape,
Exemple #21
0
root.withdraw()
file_path = filedialog.askopenfilename(initialdir=root_folder,
                                       title="Select the diffraction pattern",
                                       filetypes=[("NPZ", "*.npz")])
npzfile = np.load(file_path)
diff_pattern = pu.bin_data(npzfile[list(npzfile.files)[0]].astype(int),
                           (bin_factor, bin_factor, bin_factor),
                           debugging=False)
diff_pattern[diff_pattern < threshold] = 0
nz, ny, nx = diff_pattern.shape
print('Data shape after binning:', nz, ny, nx)
gu.multislices_plot(diff_pattern,
                    sum_frames=True,
                    plot_colorbar=True,
                    cmap=my_cmap,
                    title='diffraction pattern',
                    scale='log',
                    vmin=np.nan,
                    vmax=np.nan,
                    reciprocal_space=True,
                    is_orthogonal=True)
#############
# load mask #
#############
if load_mask:
    file_path = filedialog.askopenfilename(initialdir=root_folder,
                                           title="Select the mask",
                                           filetypes=[("NPZ", "*.npz")])
    npzfile = np.load(file_path)
    mask = pu.bin_data(npzfile[list(npzfile.files)[0]],
                       (bin_factor, bin_factor, bin_factor),
                       debugging=False)
    new_shape = [
        max(numz, 2 * pixel_FOV),
        max(numy, 2 * pixel_FOV),
        max(numx, 2 * pixel_FOV)
    ]
    amp = pu.crop_pad(array=amp, output_shape=new_shape, debugging=False)
    phase = pu.crop_pad(array=phase, output_shape=new_shape, debugging=False)
    numz, numy, numx = amp.shape

    if debug:
        gu.multislices_plot(amp,
                            sum_frames=False,
                            title='Input modulus',
                            vmin=0,
                            tick_direction=tick_direction,
                            tick_width=tick_width,
                            tick_length=tick_length,
                            pixel_spacing=pixel_spacing,
                            plot_colorbar=True,
                            is_orthogonal=True,
                            reciprocal_space=False)
        gu.multislices_plot(phase,
                            sum_frames=False,
                            title='Input phase',
                            vmin=-phase_range,
                            vmax=phase_range,
                            tick_direction=tick_direction,
                            cmap=my_cmap,
                            tick_width=tick_width,
                            tick_length=tick_length,
                            pixel_spacing=pixel_spacing,
#########################
# load a reconstruction #
#########################
plt.ion()
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename(initialdir=datadir,
                                       filetypes=[("NPZ", "*.npz")])
npzfile = np.load(file_path)
amp = npzfile["amp"]
gu.multislices_plot(
    amp,
    sum_frames=False,
    plot_colorbar=False,
    vmin=0,
    vmax=1,
    cmap=my_cmap,
    title="Input amplitude",
)

#################################
# pad data to the original size #
#################################
print("Initial data size:", amp.shape)
if len(original_size) == 0:
    original_size = amp.shape
print("FFT size before accounting for binning", original_size)
original_size = tuple(
    [original_size[index] // binning[index] for index in range(len(binning))])
print("Binning used during phasing:", binning)
Exemple #24
0
avg_obj = np.zeros((numz, numy, numx))
ref_obj = np.zeros((numz, numy, numx))
avg_counter = 1
print('\nAveraging using', nbfiles, 'candidate reconstructions')
for counter, value in enumerate(sorted_obj):
    obj, extension = util.load_file(file_path[value])
    print('\nOpening ', file_path[value])

    if flip_reconstruction:
        obj = pu.flip_reconstruction(obj, debugging=debug)

    if extension == '.h5':
        centering_method = 'do_nothing'  # do not center, data is already cropped just on support for mode decomposition
        # correct a roll after the decomposition into modes in PyNX
        obj = np.roll(obj, roll_modes, axis=(0, 1, 2))
        fig, _, _ = gu.multislices_plot(abs(obj), sum_frames=True, plot_colorbar=True, title='1st mode after centering')
        # fig.waitforbuttonpress()
        plt.close(fig)
    # use the range of interest defined above
    obj = pu.crop_pad(obj, [2 * zrange, 2 * yrange, 2 * xrange], debugging=debug)

    # align with average reconstruction
    if counter == 0:  # the fist array loaded will serve as reference object
        print('This reconstruction will serve as reference object.')
        ref_obj = obj

    avg_obj, flag_avg = pu.average_obj(avg_obj=avg_obj, ref_obj=ref_obj, obj=obj, support_threshold=0.25,
                                       correlation_threshold=avg_threshold, aligning_option='dft',
                                       method=avg_method, debugging=debug)
    avg_counter = avg_counter + flag_avg
Exemple #25
0
    def orthogonalize(self,
                      obj,
                      initial_shape=(),
                      voxel_size=np.nan,
                      width_z=None,
                      width_y=None,
                      width_x=None,
                      debugging=False,
                      **kwargs):
        """
        Interpolate obj on the orthogonal reference frame defined by the setup.

        :param obj: real space object, in a non-orthogonal frame (output of phasing program)
        :param initial_shape: shape of the FFT used for phasing
        :param voxel_size: user-defined voxel size, in nm
        :param width_z: size of the area to plot in z (axis 0), centered on the middle of the initial array
        :param width_y: size of the area to plot in y (axis 1), centered on the middle of the initial array
        :param width_x: size of the area to plot in x (axis 2), centered on the middle of the initial array
        :param debugging: True to show plots before and after interpolation
        :param kwargs:
         - 'title': title for the debugging plots
        :return: object interpolated on an orthogonal grid
        """
        for k in kwargs.keys():
            if k in ['title']:
                title = kwargs['title']
            else:
                raise Exception(
                    "unknown keyword argument given: allowed is 'title'")
        try:
            title
        except NameError:  # title not declared
            title = 'Object'

        if len(initial_shape) == 0:
            initial_shape = obj.shape

        if debugging:
            gu.multislices_plot(abs(obj),
                                sum_frames=True,
                                width_z=width_z,
                                width_y=width_y,
                                width_x=width_x,
                                title=title + ' in detector frame')

        tilt_sign = np.sign(self.tilt_angle)
        wavelength = 12.398 * 1e-7 / self.energy  # in m

        dz_realspace = wavelength / (initial_shape[0] * abs(self.tilt_angle) *
                                     np.pi / 180) * 1e9  # in nm
        dy_realspace = wavelength * self.distance / (
            initial_shape[1] * self.pixel_y) * 1e9  # in nm
        dx_realspace = wavelength * self.distance / (
            initial_shape[2] * self.pixel_x) * 1e9  # in nm
        print('Real space pixel size (z, y, x) based on initial FFT shape: (',
              str('{:.2f}'.format(dz_realspace)), 'nm,',
              str('{:.2f}'.format(dy_realspace)), 'nm,',
              str('{:.2f}'.format(dx_realspace)), 'nm )')

        nbz, nby, nbx = obj.shape  # could be smaller if the object was cropped around the support
        if nbz != initial_shape[0] or nby != initial_shape[
                1] or nbx != initial_shape[2]:
            tilt = tilt_sign * wavelength / (nbz * dz_realspace * np.pi /
                                             180) * 1e9  # in m
            pixel_y = wavelength * self.distance / (nby *
                                                    dy_realspace) * 1e9  # in m
            pixel_x = wavelength * self.distance / (nbx *
                                                    dx_realspace) * 1e9  # in m
            print('Tilt, pixel_y, pixel_x based on actual array shape: (',
                  str('{:.4f}'.format(tilt)), 'deg,',
                  str('{:.2f}'.format(pixel_y * 1e6)), 'um,',
                  str('{:.2f}'.format(pixel_x * 1e6)), 'um)')
            dz_realspace = wavelength / (nbz * abs(tilt) * np.pi /
                                         180) * 1e9  # in nm
            dy_realspace = wavelength * self.distance / (
                nby * pixel_y) * 1e9  # in nm
            dx_realspace = wavelength * self.distance / (
                nbx * pixel_x) * 1e9  # in nm
            print(
                'New real space pixel size (z, y, x) based on actual array shape: (',
                str('{:.2f}'.format(dz_realspace)), ' nm,',
                str('{:.2f}'.format(dy_realspace)), 'nm,',
                str('{:.2f}'.format(dx_realspace)), 'nm )')
        else:
            tilt = self.tilt_angle
            pixel_y = self.pixel_y
            pixel_x = self.pixel_x

        if np.isnan(voxel_size):
            voxel = np.mean([dz_realspace, dy_realspace,
                             dx_realspace])  # in nm
        else:
            voxel = voxel_size

        ortho_matrix = self.update_coords(array_shape=(nbz, nby, nbx),
                                          tilt_angle=tilt,
                                          pixel_x=pixel_x,
                                          pixel_y=pixel_y)

        ###############################################################
        # Vincent Favre-Nicolin's method using inverse transformation #
        ###############################################################
        myz, myy, myx = np.meshgrid(np.arange(-nbz // 2, nbz // 2, 1) * voxel,
                                    np.arange(-nby // 2, nby // 2, 1) * voxel,
                                    np.arange(-nbx // 2, nbx // 2, 1) * voxel,
                                    indexing='ij')
        ortho_imatrix = np.linalg.inv(ortho_matrix)
        new_x = ortho_imatrix[0, 0] * myx + ortho_imatrix[
            0, 1] * myy + ortho_imatrix[0, 2] * myz
        new_y = ortho_imatrix[1, 0] * myx + ortho_imatrix[
            1, 1] * myy + ortho_imatrix[1, 2] * myz
        new_z = ortho_imatrix[2, 0] * myx + ortho_imatrix[
            2, 1] * myy + ortho_imatrix[2, 2] * myz
        del myx, myy, myz
        gc.collect()

        rgi = RegularGridInterpolator(
            (np.arange(-nbz // 2, nbz // 2), np.arange(
                -nby // 2, nby // 2), np.arange(-nbx // 2, nbx // 2)),
            obj,
            method='linear',
            bounds_error=False,
            fill_value=0)
        ortho_obj = rgi(
            np.concatenate((new_z.reshape(
                (1, new_z.size)), new_y.reshape(
                    (1, new_z.size)), new_x.reshape(
                        (1, new_z.size)))).transpose())
        ortho_obj = ortho_obj.reshape((nbz, nby, nbx)).astype(obj.dtype)

        if debugging:
            gu.multislices_plot(abs(ortho_obj),
                                sum_frames=True,
                                width_z=width_z,
                                width_y=width_y,
                                width_x=width_x,
                                title=title +
                                ' in the orthogonal laboratory frame')
        return ortho_obj, voxel
Exemple #26
0
    if reload_mask:
        print('Reload previous mask')
        file_path = filedialog.askopenfilename(initialdir=homedir,
                                               title="Select mask file",
                                               filetypes=[("NPZ", "*.npz")])
        mask = np.load(file_path)
        npz_key = mask.files
        mask = mask[npz_key[0]]

    ##############################
    # save the raw data and mask #
    ##############################
    fig, _, _ = gu.multislices_plot(data,
                                    sum_frames=True,
                                    scale='log',
                                    plot_colorbar=True,
                                    vmin=0,
                                    title='Data before aliens removal\n',
                                    invert_yaxis=False,
                                    reciprocal_space=True)
    plt.savefig(savedir + 'rawdata_S' + str(scans[scan_nb]) + '.png')

    if flag_interact:
        fig.waitforbuttonpress()
    plt.close(fig)

    fig, _, _ = gu.multislices_plot(mask,
                                    sum_frames=True,
                                    scale='linear',
                                    plot_colorbar=True,
                                    vmin=0,
                                    vmax=(nz, ny, nx),
Exemple #27
0
obj, _ = util.load_file(file_path)

if obj.ndim != 3:
    print('a 3D reconstruction array is expected')
    sys.exit()

nbz, nby, nbx = obj.shape
print("Initial data size:", nbz, nby, nbx)

amp = abs(obj)
amp = amp / amp.max()

gu.multislices_plot(amp,
                    sum_frames=False,
                    title='Normalized modulus',
                    vmin=0,
                    vmax=1,
                    plot_colorbar=True,
                    is_orthogonal=True,
                    reciprocal_space=False)

mean_amp = amp[amp > cutoff_amp].mean()
std_amp = amp[amp > cutoff_amp].std()
print("Mean amp=", mean_amp)
print("Std amp=", std_amp)
hist, bin_edges = np.histogram(amp[amp > cutoff_amp].flatten(), bins=50)
bin_step = (bin_edges[1] - bin_edges[0]) / 2
bin_axis = bin_edges + bin_step
bin_axis = bin_axis[0:len(hist)]

# interpolate the histogram
newbin_axis = np.linspace(bin_axis.min(), bin_axis.max(), 500)
Exemple #28
0
           and origin[2] + crop_shape[2] // 2 <= nbx, 'origin incompatible with crop_shape'

    data = pu.crop_pad(array=data, output_shape=crop_shape, crop_center=origin)
    gc.collect()
    nbz, nby, nbx = data.shape
    print('data shape after cropping:', data.shape)
    # calculate the new position of origin
    new_origin = (crop_shape[0] // 2, crop_shape[1] // 2, crop_shape[2] // 2)
else:
    new_origin = origin

if plots:
    gu.multislices_plot(data,
                        sum_frames=True,
                        scale='log',
                        plot_colorbar=True,
                        title='S' + str(scan) + '\n Data before rotation',
                        vmin=0,
                        reciprocal_space=True,
                        is_orthogonal=True)

###################
# rotate the data #
###################
# define the rotation matrix in the order (x, y, z)
rotation_matrix = np.array(
    [[
        np.cos(tilt) + (1 - np.cos(tilt)) * rotation_axis[0]**2,
        rotation_axis[0] * rotation_axis[1] * (1 - np.cos(tilt)) -
        rotation_axis[2] * np.sin(tilt), rotation_axis[0] * rotation_axis[2] *
        (1 - np.cos(tilt)) + rotation_axis[1] * np.sin(tilt)
    ],
Exemple #29
0
    print('Data size after cropping / padding:', nz, ny, nx)

    if mask_zero_event:
        # mask points when there is no intensity along the whole rocking curve - probably dead pixels
        for idx in range(nz):
            temp_mask = mask[idx, :, :]
            temp_mask[np.sum(data, axis=0) == 0] = 1  # enough, numpy array is mutable hence mask will be modified
        del temp_mask

    plt.ioff()

    ##############################
    # save the raw data and mask #
    ##############################
    fig, _, _ = gu.multislices_plot(data, sum_frames=True, scale='log', plot_colorbar=True, vmin=0,
                                    title='Data before aliens removal\n',
                                    is_orthogonal=not use_rawdata, reciprocal_space=True)
    plt.savefig(savedir + 'data_before_masking_sum_S' + str(scans[scan_nb]) + '_' + str(nz) + '_' + str(ny) + '_' +
                str(nx) + '_' + str(binning[0]) + '_' + str(binning[1]) + '_' + str(binning[2]) + '.png')
    if flag_interact:
        cid = plt.connect('close_event', close_event)
        fig.waitforbuttonpress()
        plt.disconnect(cid)
    plt.close(fig)

    piz, piy, pix = np.unravel_index(data.argmax(), data.shape)
    fig = gu.combined_plots((data[piz, :, :], data[:, piy, :], data[:, :, pix]), tuple_sum_frames=False,
                            tuple_sum_axis=0, tuple_width_v=None, tuple_width_h=None, tuple_colorbar=True,
                            tuple_vmin=0, tuple_vmax=np.nan, tuple_scale='log',
                            tuple_title=('data at max in xy', 'data at max in xz', 'data at max in yz'),
                            is_orthogonal=not use_rawdata, reciprocal_space=False)
Exemple #30
0
        else:  # the data will be gridded, binning[0] is already set to 1
            # sample rotation around the vertical direction at P10:
            # the effective binning in axis 0 is preprocessing_binning[2]*binning[2]
            binning_comment = (
                f"_{detector.preprocessing_binning[2]*detector.binning[2]}"
                f"_{detector.preprocessing_binning[1]*detector.binning[1]}"
                f"_{detector.preprocessing_binning[2]*detector.binning[2]}")

            tmp_data = np.copy(
                data)  # do not modify the raw data before the interpolation
            tmp_data[mask == 1] = 0
            fig, _, _ = gu.multislices_plot(
                tmp_data,
                sum_frames=True,
                scale="log",
                plot_colorbar=True,
                vmin=0,
                title="Data before gridding\n",
                is_orthogonal=False,
                reciprocal_space=True,
            )
            plt.savefig(detector.savedir +
                        f"data_before_gridding_S{scan_nb}_{nz}_{ny}_{nx}" +
                        binning_comment + ".png")
            plt.close(fig)
            del tmp_data
            gc.collect()

            print("\nGridding the data in the orthonormal laboratory frame")
            data, mask, q_values, frames_logical = cdi.grid_cdi(
                data=data,
                mask=mask,