def add_circle(self, stage_name, index, circle): """ Add a circle to the list of circles. The most outer circle should be at index 0. :param stage_name: supported stage name, 'sample' or 'detector' :param index: index where to put the circle in the list :param circle: valid circle in {'x+', 'x-', 'y+', 'y-', 'z+', 'z-'}. + for a counter-clockwise rotation, - for a clockwise rotation. """ self.valid_name(stage_name) nb_circles = len(self.__getattribute__(self.valid_names[stage_name])) valid.valid_item( index, allowed_types=int, min_included=0, max_included=nb_circles, name="index", ) if circle not in self.valid_circles: raise ValueError(f"{circle} is not in the list of valid circles:" f" {list(self.valid_circles)}") self.__getattribute__(self.valid_names[stage_name]).insert( index, circle)
def _saturation_correction(self, data, mask, nb_frames): """ Mask pixels above a certain threshold. This is detector dependent. If a 2D frames was obtained by summing a series of frames (e.g. series measurement at P10), the threshold is multiplied accordingly. :param data: a 2D numpy array :param mask: a 2D numpy array of the same shape as data :param nb_frames: int, number of frames concatenated to obtain the 2D data array :return: - the masked data - the updated mask """ if self.saturation_threshold is not None: valid.valid_ndarray((data, mask), ndim=2) valid.valid_item(nb_frames, allowed_types=int, min_excluded=0, name="nb_frames") mask[data > self.saturation_threshold * nb_frames] = 1 data[data > self.saturation_threshold * nb_frames] = 0 return data, mask
def scan_number(self, value): valid.valid_item( value, allowed_types=int, min_included=1, allow_none=True, name="scan_number", ) self._scan_number = value
def create_roi(dic: Dict[str, Any]) -> Any: """ Load "roi_detector" from the dictionary of parameters and update it. If the keys "center_roi_x" or "center_roi_y" are defined, it will consider that the current values in roi_detector define a window around the Bragg peak position and the final output will be: [center_roi_y - roi_detector[0], center_roi_y + roi_detector[1], center_roi_x - roi_detector[2], center_roi_x + roi_detector[3]]. If a key is not defined, it will consider that the values of roi_detector are absolute pixels positions, e.g. if only "center_roi_y" is defined, the output will be: [center_roi_y - roi_detector[0], center_roi_y + roi_detector[1], roi_detector[2], roi_detector[3]]. Accordingly, if none of the keys are defined, the output will be: [roi_detector[0], roi_detector[1], roi_detector[2], roi_detector[3]]. :param dic: a dictionary of parameters :return: the calculated region of interest [Vstart, Vstop, Hstart, Hstop] or None """ valid.valid_container(dic, container_types=dict, name="dic") roi = dic.get("roi_detector") valid.valid_container( roi, container_types=(tuple, list, np.ndarray), length=4, item_types=int, allow_none=True, name="roi_detector", ) # update the ROI if roi is not None: center_roi_y = dic.get("center_roi_y") if center_roi_y is not None: valid.valid_item(center_roi_y, allowed_types=int, name="center_roi_y") roi[0] = center_roi_y - roi[0] roi[1] = center_roi_y + roi[1] center_roi_x = dic.get("center_roi_x") if center_roi_x is not None: valid.valid_item(center_roi_x, allowed_types=int, name="center_roi_x") roi[2] = center_roi_x - roi[2] roi[3] = center_roi_x + roi[3] return roi
def remove_circle(self, stage_name, index): """ Remove the circle at index from the list of sample circles. :param stage_name: supported stage name, 'sample' or 'detector' :param index: index of the circle to be removed from the list """ if stage_name not in self.valid_names: raise NotImplementedError( f"'{stage_name}' is not implemented," f" available are {list(self.valid_names.keys())}") nb_circles = len(self.__getattribute__(self.valid_names[stage_name])) if nb_circles > 0: valid.valid_item( index, allowed_types=int, min_included=0, max_included=nb_circles - 1, name="index", ) del self.__getattribute__(self.valid_names[stage_name])[index]
def __init__(self, name, **kwargs): self.custom_pixelsize = kwargs.get("custom_pixelsize") valid.valid_item( self.custom_pixelsize, allowed_types=Real, min_excluded=0, allow_none=True, name="custom_pixelsize", ) self.custom_pixelnumber = kwargs.get("custom_pixelnumber") if isinstance(self.custom_pixelnumber, np.ndarray): self.custom_pixelnumber = list(self.custom_pixelnumber) valid.valid_container( self.custom_pixelnumber, container_types=(list, tuple), length=2, item_types=Integral, min_excluded=0, allow_none=True, name="custom_pixelnumber", ) super().__init__(name=name, **kwargs)
def test_validitem_min_included_valid(self): self.assertTrue( valid.valid_item(value=1, allowed_types=Real, min_included=0))
item_types=Real, min_excluded=0, name=valid_name, ) valid.valid_container( padding_shape, container_types=(tuple, list, np.ndarray), item_types=int, min_excluded=0, length=3, name=valid_name, ) valid.valid_item( peak_value, allowed_types=Real, min_excluded=0, allow_none=True, name=valid_name ) valid.valid_container( ( load_qvalues, flip_phase, save_qyqz, save_qyqx, save_qzqx, save_sum, debug, grey_background, ), container_types=tuple, item_types=bool,
markers = ('.', 'v', '^', '<', '>', 'x', '+', 'o') # for plots mpl.rcParams['axes.linewidth'] = tick_width # set the linewidth globally validation_name = 'bcdi_blurring_function' ######################### # check some parameters # ######################### if not datadir.endswith('/'): datadir += '/' savedir = savedir or datadir if not savedir.endswith('/'): savedir += '/' pathlib.Path(savedir).mkdir(parents=True, exist_ok=True) valid.valid_item(isosurface_threshold, allowed_types=Real, min_included=0, max_excluded=1, name=validation_name) valid.valid_container(phasing_shape, container_types=(tuple, list, np.ndarray), allow_none=True, item_types=int, min_excluded=0, name=validation_name) valid.valid_item(value=upsampling_factor, allowed_types=int, min_included=1, name=validation_name) valid.valid_container(comment, container_types=str, name=validation_name) if len(comment) != 0 and not comment.startswith('_'): comment = '_' + comment
def reload_cdi_data( data, mask, scan_number, setup, normalize_method="skip", debugging=False, **kwargs, ): """ Reload forward CDI data, apply optional threshold, normalization and binning. :param data: the 3D data array :param mask: the 3D mask array :param scan_number: the scan number to load :param setup: an instance of the class Setup :param normalize_method: 'skip' to skip, 'monitor' to normalize by the default monitor, 'sum_roi' to normalize by the integrated intensity in a defined region of interest :param debugging: set to True to see plots :parama kwargs: - 'photon_threshold' = float, photon threshold to apply before binning :return: - the updated 3D data and mask arrays - the monitor values used for the intensity normalization """ valid.valid_ndarray(arrays=(data, mask), ndim=3) # check and load kwargs valid.valid_kwargs( kwargs=kwargs, allowed_kwargs={"photon_threshold"}, name="kwargs", ) photon_threshold = kwargs.get("photon_threshold", 0) valid.valid_item( photon_threshold, allowed_types=Real, min_included=0, name="photon_threshold", ) nbz, nby, nbx = data.shape frames_logical = np.ones(nbz) print((data < 0).sum(), " negative data points masked" ) # can happen when subtracting a background mask[data < 0] = 1 data[data < 0] = 0 # normalize by the incident X-ray beam intensity if normalize_method == "skip": print("Skip intensity normalization") monitor = [] else: if normalize_method == "sum_roi": monitor = data[:, setup.detector.sum_roi[0]:setup.detector.sum_roi[1], setup.detector.sum_roi[2]:setup.detector. sum_roi[3], ].sum(axis=(1, 2)) else: # use the default monitor of the beamline monitor = setup.loader.read_monitor( scan_number=scan_number, setup=setup, ) print("Intensity normalization using " + normalize_method) data, monitor = loader.normalize_dataset( array=data, monitor=monitor, norm_to_min=True, savedir=setup.detector.savedir, debugging=True, ) # pad the data to the shape defined by the ROI if (setup.detector.roi[1] - setup.detector.roi[0] > nby or setup.detector.roi[3] - setup.detector.roi[2] > nbx): start = ( 0, max(0, abs(setup.detector.roi[0])), max(0, abs(setup.detector.roi[2])), ) print("Paddind the data to the shape defined by the ROI") data = util.crop_pad( array=data, pad_start=start, output_shape=( data.shape[0], setup.detector.roi[1] - setup.detector.roi[0], setup.detector.roi[3] - setup.detector.roi[2], ), ) mask = util.crop_pad( array=mask, pad_value=1, pad_start=start, output_shape=( mask.shape[0], setup.detector.roi[1] - setup.detector.roi[0], setup.detector.roi[3] - setup.detector.roi[2], ), ) # apply optional photon threshold before binning if photon_threshold != 0: mask[data < photon_threshold] = 1 data[data < photon_threshold] = 0 print("Applying photon threshold before binning: < ", photon_threshold) # bin data and mask in the detector plane if needed # binning in the stacking dimension is done at the very end of the data processing if (setup.detector.binning[1] != 1) or (setup.detector.binning[2] != 1): print( "Binning the data: detector vertical axis by", setup.detector.binning[1], ", detector horizontal axis by", setup.detector.binning[2], ) data = util.bin_data( data, (1, setup.detector.binning[1], setup.detector.binning[2]), debugging=debugging, ) mask = util.bin_data( mask, (1, setup.detector.binning[1], setup.detector.binning[2]), debugging=debugging, ) mask[np.nonzero(mask)] = 1 return data, mask, frames_logical, monitor
def partial_coherence_rl(measured_intensity, coherent_intensity, iterations=20, debugging=False, **kwargs): """ 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 :param kwargs: - 'scale': scale for the plot, 'linear' or 'log' - 'reciprocal_space': True if the data is in reciprocal space, False otherwise. - 'is_orthogonal': set to True is the frame is orthogonal, False otherwise (detector frame) Used for plot labels. - 'vmin' = lower boundary for the colorbar. Float or tuple of 3 floats - 'vmax' = [higher boundary for the colorbar. Float or tuple of 3 floats - 'guess': ndarray, initial guess for the psf, of the same shape as measured_intensity :return: the retrieved psf (ndarray), the error metric (1D ndarray of len=iterations) """ validation_name = 'algorithms_utils.psf_rl' # check and load kwargs valid.valid_kwargs(kwargs=kwargs, allowed_kwargs={ 'scale', 'reciprocal_space', 'is_orthogonal', 'vmin', 'vmax', 'guess' }, name=validation_name) scale = kwargs.get('scale', 'log') if scale not in {'log', 'linear'}: raise ValueError('"scale" should be either "log" or "linear"') reciprocal_space = kwargs.get('reciprocal_space', True) if not isinstance(reciprocal_space, bool): raise TypeError('"reciprocal_space" should be a boolean') is_orthogonal = kwargs.get('is_orthogonal', True) if not isinstance(is_orthogonal, bool): raise TypeError('"is_orthogonal" should be a boolean') vmin = kwargs.get('vmin', np.nan) valid.valid_item(vmin, allowed_types=Real, name=validation_name) vmax = kwargs.get('vmax', np.nan) valid.valid_item(vmax, allowed_types=Real, name=validation_name) guess = kwargs.get('guess', None) if guess is not None: if not isinstance(guess, np.ndarray): raise TypeError(f"guess should be a ndarray, got {type(guess)}") if guess.shape != measured_intensity.shape: raise ValueError( 'the guess array should have the same shape as measured_intensity' ) # calculate the psf psf, error = richardson_lucy(image=measured_intensity, psf=coherent_intensity, iterations=iterations, clip=False, guess=guess) # optional plot if debugging: gu.multislices_plot(psf, scale=scale, sum_frames=False, title='psf', vmin=vmin, vmax=vmax, reciprocal_space=reciprocal_space, is_orthogonal=is_orthogonal, plot_colorbar=True) _, ax = plt.subplots(figsize=(12, 9)) ax.plot(error, 'r.') ax.set_yscale('log') ax.set_xlabel('iteration number') ax.set_ylabel('difference between consecutive iterates') return psf, error
f"Incorrect setting {strain_range} " 'for the parameter "strain_range"' ) strain_min, strain_max = -np.inf, np.inf else: strain_min, strain_max = -1 * strain_range, strain_range if not isinstance(phase_range, Real): if phase_range != "minmax": raise ValueError( f"Incorrect setting {phase_range} " 'for the parameter "phase_range"' ) phase_min, phase_max = -np.inf, np.inf else: phase_min, phase_max = -1 * phase_range, phase_range valid.valid_item(xmin_histo, allowed_types=Real, min_included=0, name="xmin_histo") valid.valid_container( obj=vline_hist, container_types=(list, tuple, np.ndarray, set), min_excluded=0, allow_none=True, item_types=Real, name="vline_hist", ) if amp_histogram_Yaxis == "linear": valid.valid_container( ylim_histo, container_types=(tuple, list, np.ndarray), item_types=Real, length=2,
ideally the first mode should be as high as possible. Adapted from PyNX. """ datadir = "D:/data/P10_August2020_CDI/data/mag_3_macro1/centrosym/" user_comment = '' # string, should start with "_" nb_mode = 1 # number of modes to return in the mode array alignment_method = 'support' # 'modulus' or 'support' # if 'modulus', use the center of mass of the modulus. If 'support', use the center of mass of a support object defined # by support_threshold support_threshold = 0.2 # threshold on the normalized modulus to define the support if alignement_method is 'support' debug = True # True to see debugging plots ######################### # check some parameters # ######################### valid.valid_container(user_comment, container_types=str, name='modes_decomposition') valid.valid_item(value=nb_mode, allowed_types=int, min_excluded=0, name='modes_decomposition') valid.valid_item(value=support_threshold, allowed_types=Real, min_included=0, name='modes_decomposition') if alignment_method not in {'modulus', 'support'}: raise ValueError(f'wrong value for alignment_method {alignment_method}, allowed are "support" and "modulus"') valid.valid_item(value=debug, allowed_types=bool, name='modes_decomposition') ################ # Load objects # ################ root = tk.Tk() root.withdraw() file_path = filedialog.askopenfilenames(initialdir=datadir, filetypes=[("CXI", "*.cxi"), ("NPZ", "*.npz"), ("NPY", "*.npy"), ("HDF5", "*.h5")]) nbfiles = len(file_path) print('Loading ', nbfiles, 'objects')
################################## ############################# # define default parameters # ############################# colors = ('b', 'g', 'r', 'c', 'm', 'y', 'k') # for plots markers = ('.', 'v', '^', '<', '>', 'x', '+', 'o') # for plots mpl.rcParams['axes.linewidth'] = tick_width # set the linewidth globally validation_name = 'compare_CDI_SEM' ######################### # check some parameters # ######################### valid.valid_item(value=index_sem, allowed_types=int, min_included=0, allow_none=True, name=validation_name) valid.valid_container(plot_sem, container_types=str, name=validation_name) if plot_sem not in {'single', 'fill'}: raise ValueError("allowed values for plot_sem are 'single' and 'all'") valid.valid_container(comment, container_types=str, name=validation_name) if len(comment) != 0 and not comment.startswith('_'): comment = '_' + comment savedir = savedir or datadir pathlib.Path(savedir).mkdir(parents=True, exist_ok=True) ######################## # load the SEM profile # ########################
def test_validitem_max_included_equal(self): self.assertTrue( valid.valid_item(value=0, allowed_types=Real, max_included=0))
def test_validitem_max_excluded_valid(self): self.assertTrue( valid.valid_item(value=1, allowed_types=Real, max_excluded=2))
def angle(self, value): valid.valid_item(value, allowed_types=Real, name="value") if np.isnan(value): raise ValueError("value is a nan") self._angle = value
######################### valid_name = "bcdi_view_psf" savedir = savedir or datadir pathlib.Path(savedir).mkdir(parents=True, exist_ok=True) if width is not None and isinstance(width, Real): width = (width,) * 3 valid.valid_container( width, container_types=(tuple, list, np.ndarray), length=3, item_types=int, min_excluded=0, allow_none=True, name=valid_name, ) valid.valid_item(vmin, allowed_types=Real, name=valid_name) valid.valid_item(vmax, allowed_types=Real, name=valid_name) valid.valid_item(tick_length, allowed_types=int, min_excluded=0, name=valid_name) valid.valid_item(tick_width, allowed_types=int, min_excluded=0, name=valid_name) valid.valid_item(linewidth, allowed_types=int, min_excluded=0, name=valid_name) valid.valid_item(plot_mcf, allowed_types=bool, name=valid_name) valid.valid_item(save_slices, allowed_types=bool, name=valid_name) valid.valid_item(is_orthogonal, allowed_types=bool, name=valid_name) if tick_direction not in {"out", "in", "inout"}: raise ValueError("allowed values for tick_direction: 'out', 'in', 'inout'") if is_orthogonal: title = "log(psf) in laboratory frame" else: title = "log(psf) in detector frame"
def valid_param(key: str, value: Any) -> Tuple[Any, bool]: """ Validate a key value pair corresponding to an input parameter. It will raise an exception if the check fails. :param key: name of the parameter :param value: the value of the parameter :return: a tuple (formatted_value, is_valid). is_valid is True if the key is valid, False otherwise. """ is_valid = True allowed: Any = None # convert 'None' to None if value == "None": value = None # convert 'True' to True if isinstance(value, str) and value.lower() == "true": value = True # convert 'False' to False if isinstance(value, str) and value.lower() == "false": value = False # test the booleans first if key in { "align_axis", "align_q", "apodize", "bin_during_loading", "correct_refraction", "custom_scan", "debug", "flag_interact", "flip_reconstruction", "get_temperature", "grey_background", "invert_phase", "is_series", "keep_size", "mask_zero_event", "reload_orthogonal", "reload_previous", "save", "save_as_int", "save_rawdata", "save_support", "save_to_mat", "save_to_npz", "save_to_vti", "simulation", "use_rawdata", }: valid.valid_item(value, allowed_types=bool, name=key) elif key == "absorption": valid.valid_item(value, allowed_types=Real, min_excluded=0, name=key) elif key == "actuators": valid.valid_container(value, container_types=dict, allow_none=True, name=key) elif key == "apodization_alpha": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=Real, min_included=0, name=key, ) value = np.asarray(value) elif key == "apodization_mu": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=Real, min_included=0, name=key, ) value = np.asarray(value) elif key == "apodization_sigma": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=Real, min_included=0, name=key, ) value = np.asarray(value) elif key == "apodization_window": allowed = {"blackman", "tukey", "normal"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "averaging_space": allowed = {"reciprocal_space", "direct_space"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "axis_to_align": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=Real, name=key, ) value = np.asarray(value) elif key == "backend": allowed = { "Agg", "Qt5Agg", "module://matplotlib_inline.backend_inline" } if value not in allowed: raise ParameterError(key, value, allowed) elif key == "background_file": valid.valid_container(value, container_types=str, min_length=1, allow_none=True, name=key) elif key == "background_plot": valid.valid_container(value, container_types=str, min_length=1, name=key) elif key == "beam_direction": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=Real, name=key, ) value = np.asarray(value) elif key == "beamline": valid.valid_container(value, container_types=str, min_length=1, name=key) elif key == "bragg_peak": valid.valid_container( value, container_types=(tuple, list), item_types=Real, min_included=0, length=3, allow_none=True, name=key, ) elif key == "cch1": valid.valid_item(value, allowed_types=Real, name=key) elif key == "cch2": valid.valid_item(value, allowed_types=Real, name=key) elif key == "center_fft": allowed = { "crop_sym_ZYX", "crop_asym_ZYX", "pad_asym_Z_crop_sym_YX", "pad_sym_Z_crop_asym_YX", "pad_sym_Z", "pad_asym_Z", "pad_sym_ZYX", "pad_asym_ZYX", "skip", } if value not in allowed: raise ParameterError(key, value, allowed) elif key == "centering_method": allowed = {"com", "max", "max_com", "do_nothing"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "center_roi_x": valid.valid_item(value, allowed_types=int, allow_none=True, name=key) elif key == "center_roi_y": valid.valid_item(value, allowed_types=int, allow_none=True, name=key) elif key == "colormap": if value not in ["turbo", "custom"] and value not in cc.cm: raise ValueError(f"unknow colormap '{value}'") elif key == "comment": valid.valid_container(value, container_types=str, name=key) if value and not value.startswith("_"): value += "_" elif key == "config_file": valid.valid_container(value, container_types=str, min_length=1, name=key) if not os.path.isfile(value): raise ValueError(f"The file {value} does not exist") elif key == "correlation_threshold": valid.valid_item(value, allowed_types=Real, min_included=0, max_included=1, name=key) elif key == "custom_images": valid.valid_container( value, container_types=(tuple, list, np.ndarray), item_types=int, min_included=0, allow_none=True, name=key, ) elif key == "custom_monitor": valid.valid_container( value, container_types=(tuple, list, np.ndarray), item_types=Real, min_included=0, allow_none=True, name=key, ) elif key == "custom_motors": valid.valid_container(value, container_types=dict, allow_none=True, name=key) elif key == "custom_pixelsize": valid.valid_item(value, allowed_types=Real, min_excluded=0, allow_none=True, name=key) elif key == "data_dir": if value is not None: valid.valid_container(value, container_types=str, min_length=1, name=key) if not os.path.isdir(value): raise ValueError(f"The directory {value} does not exist") elif key == "data_frame": allowed = {"detector", "crystal", "laboratory"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "dirbeam_detector_angles": valid.valid_container( value, container_types=(list, tuple), item_types=Real, length=2, allow_none=True, name=key, ) elif key == "direct_beam": valid.valid_container( value, container_types=(list, tuple), item_types=Real, length=2, allow_none=True, name=key, ) elif key == "detector": valid.valid_container(value, container_types=str, min_length=1, name=key) elif key == "detrot": valid.valid_item(value, allowed_types=Real, name=key) elif key == "dispersion": valid.valid_item(value, allowed_types=Real, min_excluded=0, name=key) elif key == "energy": if value is None or isinstance(value, Number): valid.valid_item(value, allowed_types=Real, min_excluded=0, allow_none=True, name=key) else: valid.valid_container( value, container_types=(tuple, list, np.ndarray), min_length=1, item_types=Real, min_excluded=0, name=key, ) elif key == "fill_value_mask": allowed = (0, 1) if value not in allowed: raise ParameterError(key, value, allowed) elif key == "fix_size": valid.valid_container( value, container_types=(tuple, list), length=6, item_types=int, allow_none=True, name=key, ) elif key == "fix_voxel": valid.valid_item(value, allowed_types=Real, min_excluded=0, allow_none=True, name=key) elif key == "flatfield_file": valid.valid_container(value, container_types=str, min_length=1, allow_none=True, name=key) elif key == "frames_pattern": if value is not None: value = np.asarray(value) valid.valid_1d_array(value, allow_none=False, allowed_values={0, 1}, name=key) elif key == "half_width_avg_phase": valid.valid_item(value, allowed_types=int, min_included=0, name=key) elif key == "hotpixels_file": valid.valid_container(value, container_types=str, min_length=1, allow_none=True, name=key) elif key == "inplane_angle": valid.valid_item(value, allowed_types=Real, allow_none=True, name=key) elif key == "interpolation_method": allowed = {"xrayutilities", "linearization"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "isosurface_strain": valid.valid_item(value, allowed_types=Real, min_excluded=0, name=key) elif key == "linearity_func": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=5, item_types=Real, allow_none=True, name=key, ) elif key == "median_filter": allowed = {"median", "interp_isolated", "mask_isolated", "skip"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "median_filter_order": valid.valid_item(value, allowed_types=int, min_included=0, name=key) elif key == "normalize_flux": allowed = {"monitor", "skip"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "offset_inplane": valid.valid_item(value, allowed_types=Real, name=key) elif key == "offset_method": allowed = {"com", "mean"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "optical_path_method": allowed = {"threshold", "defect"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "original_size": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=int, min_excluded=0, allow_none=True, name=key, ) elif key == "outofplane_angle": valid.valid_item(value, allowed_types=Real, allow_none=True, name=key) elif key == "output_size": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=int, min_excluded=0, allow_none=True, name=key, ) elif key == "pad_size": valid.valid_container( value, container_types=(tuple, list), length=3, item_types=int, allow_none=True, name=key, ) elif key == "phase_offset": valid.valid_item(value, allowed_types=Real, allow_none=True, name=key) elif key == "phase_offset_origin": valid.valid_item(value, allowed_types=Real, allow_none=True, name=key) elif key == "phase_ramp_removal": allowed = {"gradient", "upsampling"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "phase_range": valid.valid_item(value, allowed_types=Real, min_excluded=0, name=key) elif key == "phasing_binning": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=int, min_excluded=0, name=key, ) elif key == "photon_filter": allowed = {"loading", "postprocessing"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "photon_threshold": valid.valid_item(value, allowed_types=Real, min_included=0, name=key) elif key == "preprocessing_binning": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=int, min_excluded=0, name=key, ) elif key == "reconstruction_file": valid.valid_container(value, container_types=str, min_length=1, allow_none=True, name=key) if value is not None and not os.path.isfile(value): raise ValueError(f"The file {value} does not exist") elif key in {"ref_axis_q", "ref_axis"}: allowed = {"x", "y", "z"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "reference_spacing": valid.valid_item(value, allowed_types=Real, min_included=0, allow_none=True, name=key) elif key == "reference_temperature": valid.valid_item(value, allowed_types=Real, min_included=0, allow_none=True, name=key) elif key == "reflection": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=Real, name=key, ) value = np.asarray(value) elif key == "rocking_angle": allowed = {"outofplane", "inplane", "energy"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "roi_detector": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=4, item_types=int, allow_none=True, name=key, ) elif key == "roll_modes": valid.valid_container( value, container_types=(tuple, list, np.ndarray), length=3, item_types=int, name=key, ) elif key == "root_folder": valid.valid_container(value, container_types=str, min_length=1, name=key) if not os.path.isdir(value): raise ValueError(f"The directory {value} does not exist") elif key == "sample_inplane": valid.valid_container( value, container_types=(tuple, list), length=3, item_types=Real, name=key, ) elif key == "sample_name": valid.valid_container(value, container_types=str, min_length=1, name=key) elif key == "sample_offsets": valid.valid_container(value, container_types=(tuple, list, np.ndarray), allow_none=True, name=key) elif key == "sample_outofplane": valid.valid_container( value, container_types=(tuple, list), length=3, item_types=Real, name=key, ) elif key == "save_dir": valid.valid_container(value, container_types=str, min_length=1, allow_none=True, name=key) if isinstance(value, str) and not value.endswith("/"): value += "/" elif key == "save_frame": allowed = {"laboratory", "crystal", "lab_flat_sample"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "scan": valid.valid_item(value, allowed_types=int, min_included=0, name=key) elif key == "scans": if isinstance(value, Real): value = (value, ) valid.valid_container(value, container_types=(tuple, list, np.ndarray), min_length=1, name=key) elif key == "sdd": valid.valid_item(value, allowed_types=Real, min_excluded=0, allow_none=True, name=key) elif key == "sort_method": allowed = {"mean_amplitude", "variance", "variance/mean", "volume"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "specfile_name": valid.valid_container(value, container_types=str, allow_none=True, name=key) elif key == "strain_method": allowed = {"default", "defect"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "strain_range": valid.valid_item(value, allowed_types=Real, min_excluded=0, name=key) elif key == "template_imagefile": valid.valid_container(value, container_types=str, min_length=0, allow_none=True, name=key) elif key == "threshold_gradient": valid.valid_item(value, allowed_types=Real, min_excluded=0, name=key) elif key == "threshold_unwrap_refraction": valid.valid_item(value, allowed_types=Real, min_included=0, name=key) elif key == "tick_direction": allowed = {"out", "in", "inout"} if value not in allowed: raise ParameterError(key, value, allowed) elif key == "tick_length": valid.valid_item(value, allowed_types=int, min_included=1, name=key) elif key == "tick_spacing": valid.valid_item(value, allowed_types=Real, min_excluded=0, name=key) elif key == "tick_width": valid.valid_item(value, allowed_types=int, min_included=1, name=key) elif key == "tilt_angle": valid.valid_item(value, allowed_types=Real, allow_none=True, name=key) elif key == "tiltazimuth": valid.valid_item(value, allowed_types=Real, name=key) elif key == "tilt_detector": valid.valid_item(value, allowed_types=Real, name=key) else: # this key is not in the known parameters is_valid = False return value, is_valid
def load_cdi_data( scan_number, setup, bin_during_loading=False, flatfield=None, hotpixels=None, background=None, normalize="skip", debugging=False, **kwargs, ): """ Load forward CDI data and preprocess it. It applies beam stop correction and an optional photon threshold, normalization and binning. :param scan_number: the scan number to load :param setup: an instance of the class Setup :param bin_during_loading: True to bin the data during loading (faster) :param flatfield: the 2D flatfield array :param hotpixels: the 2D hotpixels array. 1 for a hotpixel, 0 for normal pixels. :param background: the 2D background array to subtract to the data :param normalize: 'skip' to skip, 'monitor' to normalize by the default monitor, 'sum_roi' to normalize by the integrated intensity in the region of interest defined by detector.sum_roi :param debugging: set to True to see plots :param kwargs: - 'photon_threshold': float, photon threshold to apply before binning - 'frames_pattern': 1D array of int, of length data.shape[0]. If frames_pattern is 0 at index, the frame at data[index] will be skipped, if 1 the frame will added to the stack. :return: - the 3D data and mask arrays - 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. - the monitor values used for the intensity normalization """ valid.valid_item(bin_during_loading, allowed_types=bool, name="bin_during_loading") # check and load kwargs valid.valid_kwargs( kwargs=kwargs, allowed_kwargs={"photon_threshold", "frames_pattern"}, name="kwargs", ) photon_threshold = kwargs.get("photon_threshold", 0) valid.valid_item( photon_threshold, allowed_types=Real, min_included=0, name="photon_threshold", ) frames_pattern = kwargs.get("frames_pattern") valid.valid_1d_array(frames_pattern, allow_none=True, allowed_values={0, 1}, name="frames_pattern") rawdata, rawmask, monitor, frames_logical = setup.loader.load_check_dataset( scan_number=scan_number, setup=setup, frames_pattern=frames_pattern, bin_during_loading=bin_during_loading, flatfield=flatfield, hotpixels=hotpixels, background=background, normalize=normalize, debugging=debugging, ) ################################# # apply the beamstop correction # ################################# rawdata = beamstop_correction(data=rawdata, setup=setup, debugging=debugging) ##################################################### # apply an optional photon threshold before binning # ##################################################### if photon_threshold != 0: rawmask[rawdata < photon_threshold] = 1 rawdata[rawdata < photon_threshold] = 0 print("Applying photon threshold before binning: < ", photon_threshold) #################################################################################### # bin data and mask in the detector plane if not already done during loading # # binning in the stacking dimension is done at the very end of the data processing # #################################################################################### if not bin_during_loading and ((setup.detector.binning[1] != 1) or (setup.detector.binning[2] != 1)): print( "Binning the data: detector vertical axis by", setup.detector.binning[1], ", detector horizontal axis by", setup.detector.binning[2], ) rawdata = util.bin_data( rawdata, (1, setup.detector.binning[1], setup.detector.binning[2]), debugging=False, ) rawmask = util.bin_data( rawmask, (1, setup.detector.binning[1], setup.detector.binning[2]), debugging=False, ) rawmask[np.nonzero(rawmask)] = 1 ################################################ # pad the data to the shape defined by the ROI # ################################################ rawdata, rawmask = util.pad_from_roi( arrays=(rawdata, rawmask), roi=setup.detector.roi, binning=setup.detector.binning[1:], pad_value=(0, 1), ) return rawdata, rawmask, frames_logical, monitor
def test_validitem_allowedtypes_bool(self): self.assertTrue(valid.valid_item(value=True, allowed_types=bool))
min_included=0, name=validation_name, ) valid.valid_container( background_roi, container_types=(list, tuple), allow_none=True, item_types=int, min_included=0, name=validation_name, ) valid.valid_item( value=expected_width, allowed_types=Real, min_excluded=0, allow_none=True, name=validation_name, ) valid.valid_container(comment, container_types=str, name=validation_name) if comment.startswith("_"): comment = comment[1:] 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 background_roi is not None:
def test_validitem_allowedtypes_ndarray(self): self.assertTrue( valid.valid_item(value=np.zeros(4), allowed_types=np.ndarray))
tick_width = 2 # in plots tick_spacing = ( 0.025, 0.025, 0.025, ) # tuple of three numbers, in 1/A. Leave None for default. num_ticks = 5 # number of ticks to use in axes when tick_spacing is not defined ################################## # end of user-defined parameters # ################################## #################### # Check parameters # #################### valid_name = "bcdi_plot_diffpattern_2D" valid.valid_item(save_sum, allowed_types=bool, name=valid_name) if save_sum: comment = comment + "_sum" valid.valid_container( colorbar_range, container_types=(tuple, list, np.ndarray), item_types=Real, length=2, allow_none=True, name=valid_name, ) if isinstance(tick_spacing, Real) or tick_spacing is None: tick_spacing = (tick_spacing,) * 3 valid.valid_container( tick_spacing, container_types=(tuple, list, np.ndarray),
def test_validitem_allownone_true(self): self.assertTrue( valid.valid_item(value=None, allowed_types=Real, allow_none=True))
# end of user-defined parameters # ################################## ############################# # define default parameters # ############################# colors = ("b", "g", "r", "c", "m", "y", "k") # for plots markers = (".", "v", "^", "<", ">") # for plots validation_name = "angular_profile" mpl.rcParams["axes.linewidth"] = tick_width # set the linewidth globally ######################### # check some parameters # ######################### valid.valid_item(value=upsampling_factor, allowed_types=int, min_included=1, name=validation_name) valid.valid_container(comment, container_types=str, name=validation_name) if comment.startswith("_"): comment = comment[1:] ################################################## # create the list of directions for the linecuts # ################################################## angles = np.arange(0, 180, angular_step) nb_dir = len(angles) directions = [] for idx in range(nb_dir): directions.append( (np.sin(angles[idx] * np.pi / 180), np.cos(angles[idx] * np.pi / 180)))
if not strain_range == 'minmax': raise ValueError( f'Incorrect setting {strain_range} for the parameter "strain_range"' ) else: strain_min, strain_max = -strain_range, strain_range if not isinstance(phase_range, Real): if not phase_range == 'minmax': raise ValueError( f'Incorrect setting {phase_range} for the parameter "phase_range"') else: phase_min, phase_max = -phase_range, phase_range valid.valid_item(min_histo, allowed_types=Real, min_included=0, name=valid_name) valid.valid_container(obj=vline_hist, container_types=(list, tuple, np.ndarray, set), min_excluded=0, allow_none=True, item_types=Real, name=valid_name) pathlib.Path(savedir).mkdir(parents=True, exist_ok=True) ############# # load data # ############# plt.ion() root = tk.Tk()