def cake_integration(azimuthal_integrator: AzimuthalIntegrator, data: np.ndarray, npt_rad: int = 1000, npt_azim: int = 1000, polz_factor: float = 0, unit: Union[str, units.Unit] = "q_A^-1", radial_range: Tuple[float] = None, azimuth_range: Tuple[float] = None, mask: np.ndarray = None, dark: np.ndarray = None, flat: np.ndarray = None, method: str = 'splitbbox', normalization_factor: float = 1) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: cake, q, chi = azimuthal_integrator.integrate2d(data=data, npt_rad=npt_rad, npt_azim=npt_azim, radial_range=radial_range, azimuth_range=azimuth_range, mask=mask, polarization_factor=polz_factor, dark=dark, flat=flat, method=method, unit=unit, normalization_factor=normalization_factor) return cake, q, chi
def run(self): ai = AzimuthalIntegrator(dist=self.__distance, poni1=self.__poni1, poni2=self.__poni2, rot1=self.__rotation1, rot2=self.__rotation2, rot3=self.__rotation3, detector=self.__detector, wavelength=self.__wavelength) # FIXME error model, method self.__result1d = ai.integrate1d( data=self.__image, npt=self.__nPointsRadial, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) self.__result2d = ai.integrate2d( data=self.__image, npt_rad=self.__nPointsRadial, npt_azim=self.__nPointsAzimuthal, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) try: self.__directDist = ai.getFit2D()["directDist"] except Exception: # The geometry could not fit this param _logger.debug("Backtrace", exc_info=True) self.__directDist = None if self.__calibrant: rings = self.__calibrant.get_2th() try: rings = utils.from2ThRad(rings, self.__radialUnit, self.__wavelength, self.__directDist) except ValueError: message = "Convertion to unit %s not supported. Ring marks ignored" _logger.warning(message, self.__radialUnit) rings = [] # Filter the rings which are not part of the result rings = filter( lambda x: self.__result1d.radial[0] <= x <= self.__result1d. radial[-1], rings) rings = list(rings) else: rings = [] self.__ringAngles = rings self.__ai = ai
def run(self): ai = AzimuthalIntegrator(dist=self.__distance, poni1=self.__poni1, poni2=self.__poni2, rot1=self.__rotation1, rot2=self.__rotation2, rot3=self.__rotation3, detector=self.__detector, wavelength=self.__wavelength) numberPoint1D = 1024 numberPointRadial = 400 numberPointAzimuthal = 360 # FIXME error model, method self.__result1d = ai.integrate1d( data=self.__image, npt=numberPoint1D, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) self.__result2d = ai.integrate2d( data=self.__image, npt_rad=numberPointRadial, npt_azim=numberPointAzimuthal, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) if self.__calibrant: rings = self.__calibrant.get_2th() rings = filter(lambda x: x <= self.__result1d.radial[-1], rings) rings = list(rings) try: rings = utils.from2ThRad(rings, self.__radialUnit, self.__wavelength, ai) except ValueError: message = "Convertion to unit %s not supported. Ring marks ignored" _logger.warning(message, self.__radialUnit) rings = [] else: rings = [] self.__ringAngles = rings
def run(self): ai = AzimuthalIntegrator( dist=self.__distance, poni1=self.__poni1, poni2=self.__poni2, rot1=self.__rotation1, rot2=self.__rotation2, rot3=self.__rotation3, detector=self.__detector, wavelength=self.__wavelength) numberPoint1D = 1024 numberPointRadial = 400 numberPointAzimuthal = 360 # FIXME error model, method self.__result1d = ai.integrate1d( data=self.__image, npt=numberPoint1D, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) self.__result2d = ai.integrate2d( data=self.__image, npt_rad=numberPointRadial, npt_azim=numberPointAzimuthal, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) if self.__calibrant: rings = self.__calibrant.get_2th() rings = filter(lambda x: x <= self.__result1d.radial[-1], rings) rings = list(rings) try: rings = utils.from2ThRad(rings, self.__radialUnit, self.__wavelength, ai) except ValueError: message = "Convertion to unit %s not supported. Ring marks ignored" _logger.warning(message, self.__radialUnit) rings = [] else: rings = [] self.__ringAngles = rings
def run(self): ai = AzimuthalIntegrator(dist=self.__distance, poni1=self.__poni1, poni2=self.__poni2, rot1=self.__rotation1, rot2=self.__rotation2, rot3=self.__rotation3, detector=self.__detector, wavelength=self.__wavelength) # FIXME Add error model method = method_registry.Method(0, self.__method.split, self.__method.algo, self.__method.impl, None) method1d = method.fixed(dim=1) methods = method_registry.IntegrationMethod.select_method( method=method1d) if len(methods) == 0: method1d = method_registry.Method(1, method1d.split, "*", "*", None) _logger.warning("Downgrade 1D integration method to %s", method1d) else: method1d = methods[0].method method2d = method.fixed(dim=2) methods = method_registry.IntegrationMethod.select_method( method=method2d) if len(methods) == 0: method2d = method_registry.Method(2, method2d.split, "*", "*", None) _logger.warning("Downgrade 2D integration method to %s", method2d) else: method2d = methods[0].method try: self.__result1d = ai.integrate1d_ng( method=method1d, data=self.__image, npt=self.__nPointsRadial, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) self.__result2d = ai.integrate2d( method=method2d, data=self.__image, npt_rad=self.__nPointsRadial, npt_azim=self.__nPointsAzimuthal, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) # Create an image masked where data exists self.__resultMask2d = None if self.__mask is not None: if self.__mask.shape == self.__image.shape: maskData = numpy.ones(shape=self.__image.shape, dtype=numpy.float32) maskData[self.__mask == 0] = float("NaN") if self.__displayMask: self.__resultMask2d = ai.integrate2d( method=method2d, data=maskData, npt_rad=self.__nPointsRadial, npt_azim=self.__nPointsAzimuthal, unit=self.__radialUnit, polarization_factor=self.__polarizationFactor) else: _logger.warning( "Inconsistency between image and mask sizes. %s != %s", self.__image.shape, self.__mask.shape) except Exception as e: _logger.debug("Error while integrating", exc_info=True) self.__errorMessage = e # TODO: Could be nice to compute anyway other content (directDist...) return try: self.__directDist = ai.getFit2D()["directDist"] except Exception: # The geometry could not fit this param _logger.debug("Backtrace", exc_info=True) self.__directDist = None if self.__calibrant: rings = self.__calibrant.get_2th() try: rings = unitutils.from2ThRad(rings, self.__radialUnit, self.__wavelength, self.__directDist) except ValueError: message = "Convertion to unit %s not supported. Ring locations ignored." _logger.warning(message, self.__radialUnit) self.__errorMessage = message % self.__radialUnit rings = [] # Filter the rings which are not part of the result minAngle, maxAngle = self.__result1d.radial[ 0], self.__result1d.radial[-1] rings = [(i, angle) for i, angle in enumerate(rings) if minAngle <= angle <= maxAngle] else: rings = [] self.__rings = rings self.__ai = ai
class CalibrationModel(QtCore.QObject): def __init__(self, img_model=None): super(CalibrationModel, self).__init__() """ :param img_model: :type img_model: ImgModel """ self.img_model = img_model self.points = [] self.points_index = [] self.pattern_geometry = AzimuthalIntegrator() self.pattern_geometry_img_shape = None self.cake_geometry = None self.cake_geometry_img_shape = None self.calibrant = Calibrant() self.pattern_geometry.wavelength = 0.3344e-10 self.start_values = {'dist': 200e-3, 'wavelength': 0.3344e-10, 'pixel_width': 79e-6, 'pixel_height': 79e-6, 'polarization_factor': 0.99} self.orig_pixel1 = 79e-6 self.orig_pixel2 = 79e-6 self.fit_wavelength = False self.fit_distance = True self.fit_poni1 = True self.fit_poni2 = True self.fit_rot1 = True self.fit_rot2 = True self.fit_rot3 = True self.is_calibrated = False self.use_mask = False self.filename = '' self.calibration_name = 'None' self.polarization_factor = 0.99 self.supersampling_factor = 1 self.correct_solid_angle = True self._calibrants_working_dir = calibrants_path self.distortion_spline_filename = None self.tth = np.linspace(0, 25) self.int = np.sin(self.tth) self.num_points = len(self.int) self.cake_img = np.zeros((2048, 2048)) self.cake_tth = None self.cake_azi = None self.peak_search_algorithm = None def find_peaks_automatic(self, x, y, peak_ind): """ Searches peaks by using the Massif algorithm :param float x: x-coordinate in pixel - should be from original image (not supersampled x-coordinate) :param float y: y-coordinate in pixel - should be from original image (not supersampled y-coordinate) :param peak_ind: peak/ring index to which the found points will be added :return: array of points found """ massif = Massif(self.img_model._img_data) cur_peak_points = massif.find_peaks((int(np.round(x)), int(np.round(y))), stdout=DummyStdOut()) if len(cur_peak_points): self.points.append(np.array(cur_peak_points)) self.points_index.append(peak_ind) return np.array(cur_peak_points) def find_peak(self, x, y, search_size, peak_ind): """ Searches a peak around the x,y position. It just searches for the maximum value in a specific search size. :param int x: x-coordinate in pixel - should be from original image (not supersampled x-coordinate) :param int y: y-coordinate in pixel - should be form original image (not supersampled y-coordinate) :param search_size: the length of the search rectangle in pixels in all direction in which the algorithm searches for the maximum peak :param peak_ind: peak/ring index to which the found points will be added :return: point found (as array) """ left_ind = int(np.round(x - search_size * 0.5)) if left_ind < 0: left_ind = 0 top_ind = int(np.round(y - search_size * 0.5)) if top_ind < 0: top_ind = 0 search_array = self.img_model.img_data[left_ind:(left_ind + search_size), top_ind:(top_ind + search_size)] x_ind, y_ind = np.where(search_array == search_array.max()) x_ind = x_ind[0] + left_ind y_ind = y_ind[0] + top_ind self.points.append(np.array([x_ind, y_ind])) self.points_index.append(peak_ind) return np.array([np.array((x_ind, y_ind))]) def clear_peaks(self): self.points = [] self.points_index = [] def remove_last_peak(self): if self.points: num_points = int(self.points[-1].size/2) # each peak is x, y so length is twice as number of peaks self.points.pop(-1) self.points_index.pop(-1) return num_points def create_cake_geometry(self): self.cake_geometry = AzimuthalIntegrator(splineFile=self.distortion_spline_filename) pyFAI_parameter = self.pattern_geometry.getPyFAI() pyFAI_parameter['polarization_factor'] = self.polarization_factor pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.cake_geometry.wavelength = pyFAI_parameter['wavelength'] def setup_peak_search_algorithm(self, algorithm, mask=None): """ Initializes the peak search algorithm on the current image :param algorithm: peak search algorithm used. Possible algorithms are 'Massif' and 'Blob' :param mask: if a mask is used during the process this is provided here as a 2d array for the image. """ if algorithm == 'Massif': self.peak_search_algorithm = Massif(self.img_model.raw_img_data) elif algorithm == 'Blob': if mask is not None: self.peak_search_algorithm = BlobDetection(self.img_model.raw_img_data * mask) else: self.peak_search_algorithm = BlobDetection(self.img_model.raw_img_data) self.peak_search_algorithm.process() else: return def search_peaks_on_ring(self, ring_index, delta_tth=0.1, min_mean_factor=1, upper_limit=55000, mask=None): """ This function is searching for peaks on an expected ring. It needs an initial calibration before. Then it will search for the ring within some delta_tth and other parameters to get peaks from the calibrant. :param ring_index: the index of the ring for the search :param delta_tth: search space around the expected position in two theta :param min_mean_factor: a factor determining the minimum peak intensity to be picked up. it is based on the mean value of the search area defined by delta_tth. Pick a large value for larger minimum value and lower for lower minimum value. Therefore, a smaller number is more prone to picking up noise. typical values like between 1 and 3. :param upper_limit: maximum intensity for the peaks to be picked :param mask: in case the image has to be masked from certain areas, it need to be given here. Default is None. The mask should be given as an 2d array with the same dimensions as the image, where 1 denotes a masked pixel and all others should be 0. """ self.reset_supersampling() if not self.is_calibrated: return # transform delta from degree into radians delta_tth = delta_tth / 180.0 * np.pi # get appropriate two theta value for the ring number tth_calibrant_list = self.calibrant.get_2th() if ring_index >= len(tth_calibrant_list): raise NotEnoughSpacingsInCalibrant() tth_calibrant = np.float(tth_calibrant_list[ring_index]) # get the calculated two theta values for the whole image tth_array = self.pattern_geometry.twoThetaArray(self.img_model._img_data.shape) # create mask based on two_theta position ring_mask = abs(tth_array - tth_calibrant) <= delta_tth if mask is not None: mask = np.logical_and(ring_mask, np.logical_not(mask)) else: mask = ring_mask # calculate the mean and standard deviation of this area sub_data = np.array(self.img_model._img_data.ravel()[np.where(mask.ravel())], dtype=np.float64) sub_data[np.where(sub_data > upper_limit)] = np.NaN mean = np.nanmean(sub_data) std = np.nanstd(sub_data) # set the threshold into the mask (don't detect very low intensity peaks) threshold = min_mean_factor * mean + std mask2 = np.logical_and(self.img_model._img_data > threshold, mask) mask2[np.where(self.img_model._img_data > upper_limit)] = False size2 = mask2.sum(dtype=int) keep = int(np.ceil(np.sqrt(size2))) try: sys.stdout = DummyStdOut res = self.peak_search_algorithm.peaks_from_area(mask2, Imin=mean - std, keep=keep) sys.stdout = sys.__stdout__ except IndexError: res = [] # Store the result if len(res): self.points.append(np.array(res)) self.points_index.append(ring_index) self.set_supersampling() self.pattern_geometry.reset() def set_calibrant(self, filename): self.calibrant = Calibrant() self.calibrant.load_file(filename) self.pattern_geometry.calibrant = self.calibrant def set_start_values(self, start_values): self.start_values = start_values self.polarization_factor = start_values['polarization_factor'] def calibrate(self): self.pattern_geometry = GeometryRefinement(self.create_point_array(self.points, self.points_index), dist=self.start_values['dist'], wavelength=self.start_values['wavelength'], pixel1=self.start_values['pixel_width'], pixel2=self.start_values['pixel_height'], calibrant=self.calibrant, splineFile=self.distortion_spline_filename) self.orig_pixel1 = self.start_values['pixel_width'] self.orig_pixel2 = self.start_values['pixel_height'] self.refine() self.create_cake_geometry() self.is_calibrated = True self.calibration_name = 'current' self.set_supersampling() # reset the integrator (not the geometric parameters) self.pattern_geometry.reset() def refine(self): self.reset_supersampling() self.pattern_geometry.data = self.create_point_array(self.points, self.points_index) fix = ['wavelength'] if self.fit_wavelength: fix = [] if not self.fit_distance: fix.append('dist') if not self.fit_poni1: fix.append('poni1') if not self.fit_poni2: fix.append('poni2') if not self.fit_rot1: fix.append('rot1') if not self.fit_rot2: fix.append('rot2') if not self.fit_rot3: fix.append('rot3') if self.fit_wavelength: self.pattern_geometry.refine2() self.pattern_geometry.refine2_wavelength(fix=fix) self.create_cake_geometry() self.set_supersampling() # reset the integrator (not the geometric parameters) self.pattern_geometry.reset() def integrate_1d(self, num_points=None, mask=None, polarization_factor=None, filename=None, unit='2th_deg', method='csr'): if np.sum(mask) == self.img_model.img_data.shape[0] * self.img_model.img_data.shape[1]: # do not perform integration if the image is completely masked... return self.tth, self.int if self.pattern_geometry_img_shape != self.img_model.img_data.shape: # if cake geometry was used on differently shaped image before the azimuthal integrator needs to be reset self.pattern_geometry.reset() self.pattern_geometry_img_shape = self.img_model.img_data.shape if polarization_factor is None: polarization_factor = self.polarization_factor if num_points is None: num_points = self.calculate_number_of_pattern_points(2) self.num_points = num_points t1 = time.time() if unit is 'd_A': try: self.tth, self.int = self.pattern_geometry.integrate1d(self.img_model.img_data, num_points, method=method, unit='2th_deg', mask=mask, polarization_factor=polarization_factor, correctSolidAngle=self.correct_solid_angle, filename=filename) except NameError: self.tth, self.int = self.pattern_geometry.integrate1d(self.img_model.img_data, num_points, method='csr', unit='2th_deg', mask=mask, polarization_factor=polarization_factor, correctSolidAngle=self.correct_solid_angle, filename=filename) self.tth = self.pattern_geometry.wavelength / (2 * np.sin(self.tth / 360 * np.pi)) * 1e10 self.int = self.int else: try: self.tth, self.int = self.pattern_geometry.integrate1d(self.img_model.img_data, num_points, method=method, unit=unit, mask=mask, polarization_factor=polarization_factor, correctSolidAngle=self.correct_solid_angle, filename=filename) except NameError: self.tth, self.int = self.pattern_geometry.integrate1d(self.img_model.img_data, num_points, method='csr', unit=unit, mask=mask, polarization_factor=polarization_factor, correctSolidAngle=self.correct_solid_angle, filename=filename) logger.info('1d integration of {0}: {1}s.'.format(os.path.basename(self.img_model.filename), time.time() - t1)) ind = np.where((self.int > 0) & (~np.isnan(self.int))) self.tth = self.tth[ind] self.int = self.int[ind] return self.tth, self.int def integrate_2d(self, mask=None, polarization_factor=None, unit='2th_deg', method='csr', rad_points=None, azimuth_points=360, azimuth_range=None): if polarization_factor is None: polarization_factor = self.polarization_factor if self.cake_geometry_img_shape != self.img_model.img_data.shape: # if cake geometry was used on differently shaped image before the azimuthal integrator needs to be reset self.cake_geometry.reset() self.cake_geometry_img_shape = self.img_model.img_data.shape if rad_points is None: rad_points = self.calculate_number_of_pattern_points(2) self.num_points = rad_points t1 = time.time() res = self.cake_geometry.integrate2d(self.img_model.img_data, rad_points, azimuth_points, azimuth_range=azimuth_range, method=method, mask=mask, unit=unit, polarization_factor=polarization_factor, correctSolidAngle=self.correct_solid_angle) logger.info('2d integration of {0}: {1}s.'.format(os.path.basename(self.img_model.filename), time.time() - t1)) self.cake_img = res[0] self.cake_tth = res[1] self.cake_azi = res[2] return self.cake_img def create_point_array(self, points, points_ind): res = [] for i, point_list in enumerate(points): if point_list.shape == (2,): res.append([point_list[0], point_list[1], points_ind[i]]) else: for point in point_list: res.append([point[0], point[1], points_ind[i]]) return np.array(res) def get_point_array(self): return self.create_point_array(self.points, self.points_index) def get_calibration_parameter(self): pyFAI_parameter = self.pattern_geometry.getPyFAI() pyFAI_parameter['polarization_factor'] = self.polarization_factor try: fit2d_parameter = self.pattern_geometry.getFit2D() fit2d_parameter['polarization_factor'] = self.polarization_factor except TypeError: fit2d_parameter = None pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength if fit2d_parameter: fit2d_parameter['wavelength'] = self.pattern_geometry.wavelength return pyFAI_parameter, fit2d_parameter def calculate_number_of_pattern_points(self, max_dist_factor=1.5): # calculates the number of points for an integrated pattern, based on the distance of the beam center to the the # image corners. Maximum value is determined by the shape of the image. fit2d_parameter = self.pattern_geometry.getFit2D() center_x = fit2d_parameter['centerX'] center_y = fit2d_parameter['centerY'] width, height = self.img_model.img_data.shape if width > center_x > 0: side1 = np.max([abs(width - center_x), center_x]) else: side1 = width if center_y < height and center_y > 0: side2 = np.max([abs(height - center_y), center_y]) else: side2 = height max_dist = np.sqrt(side1 ** 2 + side2 ** 2) return int(max_dist * max_dist_factor) def load(self, filename): """ Loads a calibration file and and sets all the calibration parameter. :param filename: filename for a *.poni calibration file """ self.pattern_geometry = AzimuthalIntegrator() self.pattern_geometry.load(filename) self.orig_pixel1 = self.pattern_geometry.pixel1 self.orig_pixel2 = self.pattern_geometry.pixel2 self.calibration_name = get_base_name(filename) self.filename = filename self.is_calibrated = True self.create_cake_geometry() self.set_supersampling() def save(self, filename): """ Saves the current calibration parameters into a a text file. Default extension is *.poni """ self.cake_geometry.save(filename) self.calibration_name = get_base_name(filename) self.filename = filename def create_file_header(self): try: # pyFAI version 0.12.0 return self.pattern_geometry.makeHeaders(polarization_factor=self.polarization_factor) except AttributeError: # pyFAI after version 0.12.0 from pyFAI.io import DefaultAiWriter return DefaultAiWriter(None, self.pattern_geometry).make_headers() def set_fit2d(self, fit2d_parameter): """ Reads in a dictionary with fit2d parameters where the fields of the dictionary are: 'directDist', 'centerX', 'centerY', 'tilt', 'tiltPlanRotation', 'pixelX', pixelY', 'polarization_factor', 'wavelength' """ self.pattern_geometry.setFit2D(directDist=fit2d_parameter['directDist'], centerX=fit2d_parameter['centerX'], centerY=fit2d_parameter['centerY'], tilt=fit2d_parameter['tilt'], tiltPlanRotation=fit2d_parameter['tiltPlanRotation'], pixelX=fit2d_parameter['pixelX'], pixelY=fit2d_parameter['pixelY']) self.pattern_geometry.wavelength = fit2d_parameter['wavelength'] self.create_cake_geometry() self.polarization_factor = fit2d_parameter['polarization_factor'] self.orig_pixel1 = fit2d_parameter['pixelX'] * 1e-6 self.orig_pixel2 = fit2d_parameter['pixelY'] * 1e-6 self.is_calibrated = True self.set_supersampling() def set_pyFAI(self, pyFAI_parameter): """ Reads in a dictionary with pyFAI parameters where the fields of dictionary are: 'dist', 'poni1', 'poni2', 'rot1', 'rot2', 'rot3', 'pixel1', 'pixel2', 'wavelength', 'polarization_factor' """ self.pattern_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.pattern_geometry.wavelength = pyFAI_parameter['wavelength'] self.create_cake_geometry() self.polarization_factor = pyFAI_parameter['polarization_factor'] self.orig_pixel1 = pyFAI_parameter['pixel1'] self.orig_pixel2 = pyFAI_parameter['pixel2'] self.is_calibrated = True self.set_supersampling() def load_distortion(self, spline_filename): self.distortion_spline_filename = spline_filename self.pattern_geometry.set_splineFile(spline_filename) if self.cake_geometry: self.cake_geometry.set_splineFile(spline_filename) def reset_distortion_correction(self): self.distortion_spline_filename = None self.pattern_geometry.set_splineFile(None) if self.cake_geometry: self.cake_geometry.set_splineFile(None) def set_supersampling(self, factor=None): """ Sets the supersampling to a specific factor. Whereby the factor determines in how many artificial pixel the original pixel is split. (factor^2) factor n_pixel 1 1 2 4 3 9 4 16 """ if factor is None: factor = self.supersampling_factor self.pattern_geometry.pixel1 = self.orig_pixel1 / float(factor) self.pattern_geometry.pixel2 = self.orig_pixel2 / float(factor) if factor != self.supersampling_factor: self.pattern_geometry.reset() self.supersampling_factor = factor def reset_supersampling(self): self.pattern_geometry.pixel1 = self.orig_pixel1 self.pattern_geometry.pixel2 = self.orig_pixel2 def get_two_theta_img(self, x, y): """ Gives the two_theta value for the x,y coordinates on the image. Be aware that this function will be incorrect for pixel indices, since it does not correct for center of the pixel. :param x: x-coordinate in pixel on the image :type x: ndarray :param y: y-coordinate in pixel on the image :type y: ndarray :return : two theta in radians """ x *= self.supersampling_factor y *= self.supersampling_factor return self.pattern_geometry.tth(x - 0.5, y - 0.5)[0] # deletes 0.5 because tth function uses pixel indices def get_azi_img(self, x, y): """ Gives chi for position on image. :param x: x-coordinate in pixel on the image :type x: ndarray :param y: y-coordinate in pixel on the image :type y: ndarray :return : azimuth in radians """ x *= self.supersampling_factor y *= self.supersampling_factor return self.pattern_geometry.chi(x - 0.5, y - 0.5)[0] def get_two_theta_array(self): return self.pattern_geometry.twoThetaArray(self.img_model.img_data.shape)[::self.supersampling_factor, ::self.supersampling_factor] def get_pixel_ind(self, tth, azi): """ Calculates pixel index for a specfic two theta and azimutal value. :param tth: two theta in radians :param azi: azimuth in radians :return: tuple of index 1 and 2 """ tth_ind = find_contours(self.pattern_geometry.ttha, tth) if len(tth_ind) == 0: return [] tth_ind = np.vstack(tth_ind) azi_values = self.pattern_geometry.chi(tth_ind[:, 0], tth_ind[:, 1]) min_index = np.argmin(np.abs(azi_values - azi)) return tth_ind[min_index, 0], tth_ind[min_index, 1] @property def wavelength(self): return self.pattern_geometry.wavelength
class TestMultiGeometry(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) self.data = fabio.open(UtilsTest.getimage("1788/moke.tif")).data self.lst_data = [self.data[:250, :300], self.data[250:, :300], self.data[:250, 300:], self.data[250:, 300:]] self.det = Detector(1e-4, 1e-4) self.det.max_shape = (500, 600) self.sub_det = Detector(1e-4, 1e-4) self.sub_det.max_shape = (250, 300) self.ai = AzimuthalIntegrator(0.1, 0.03, 0.03, detector=self.det) self.range = (0, 23) self.ais = [AzimuthalIntegrator(0.1, 0.030, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.030, 0.00, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.00, detector=self.sub_det), ] self.mg = MultiGeometry(self.ais, radial_range=self.range, unit="2th_deg") self.N = 390 def tearDown(self): unittest.TestCase.tearDown(self) self.data = None self.lst_data = None self.det = None self.sub_det = None self.ai = None self.ais = None self.mg = None def test_integrate1d(self): tth_ref, I_ref = self.ai.integrate1d(self.data, radial_range=self.range, npt=self.N, unit="2th_deg", method="splitpixel") obt = self.mg.integrate1d(self.lst_data, self.N) tth_obt, I_obt = obt self.assertEqual(abs(tth_ref - tth_obt).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = (abs(I_obt * 1e6 - I_ref).max()) self.assertLessEqual(delta, 5e-5, "Intensity is the same delta=%s" % delta) def test_integrate2d(self): ref = self.ai.integrate2d(self.data, self.N, 360, radial_range=self.range, azimuth_range=(-180, 180), unit="2th_deg", method="splitpixel", all=True) obt = self.mg.integrate2d(self.lst_data, self.N, 360, all=True) self.assertEqual(abs(ref["radial"] - obt["radial"]).max(), 0, "Bin position is the same") self.assertEqual(abs(ref["azimuthal"] - obt["azimuthal"]).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = abs(obt["I"] * 1e6 - ref["I"])[obt["count"] >= 1] # restict on valid pixel delta_cnt = abs(obt["count"] - ref["count"]) delta_sum = abs(obt["sum"] * 1e6 - ref["sum"]) if delta.max() > 1: logger.warning("TestMultiGeometry.test_integrate2d gave intensity difference of %s" % delta.max()) if logger.level <= logging.DEBUG: from matplotlib import pyplot as plt f = plt.figure() a1 = f.add_subplot(2, 2, 1) a1.imshow(ref["sum"]) a2 = f.add_subplot(2, 2, 2) a2.imshow(obt["sum"]) a3 = f.add_subplot(2, 2, 3) a3.imshow(delta_sum) a4 = f.add_subplot(2, 2, 4) a4.plot(delta_sum.sum(axis=0)) f.show() raw_input() self.assertLess(delta_cnt.max(), 0.001, "pixel count is the same delta=%s" % delta_cnt.max()) self.assertLess(delta_sum.max(), 0.03, "pixel sum is the same delta=%s" % delta_sum.max()) self.assertLess(delta.max(), 0.004, "pixel intensity is the same (for populated pixels) delta=%s" % delta.max())
class CalibrationModel(object): def __init__(self, img_model=None): """ :param img_model: :type img_model: ImgModel """ self.img_model = img_model self.points = [] self.points_index = [] self.spectrum_geometry = AzimuthalIntegrator() self.cake_geometry = None self.calibrant = Calibrant() self.start_values = {'dist': 200e-3, 'wavelength': 0.3344e-10, 'pixel_width': 79e-6, 'pixel_height': 79e-6, 'polarization_factor': 0.99} self.orig_pixel1 = 79e-6 self.orig_pixel2 = 79e-6 self.fit_wavelength = False self.fit_distance = True self.is_calibrated = False self.use_mask = False self.filename = '' self.calibration_name = 'None' self.polarization_factor = 0.99 self.supersampling_factor = 1 self._calibrants_working_dir = os.path.dirname(calibrants.__file__) self.cake_img = np.zeros((2048, 2048)) self.tth = np.linspace(0, 25) self.int = np.sin(self.tth) self.num_points = len(self.int) self.peak_search_algorithm = None def find_peaks_automatic(self, x, y, peak_ind): """ Searches peaks by using the Massif algorithm :param x: x-coordinate in pixel - should be from original image (not supersampled x-coordinate) :param y: y-coordinate in pixel - should be from original image (not supersampled y-coordinate) :param peak_ind: peak/ring index to which the found points will be added :return: array of points found """ massif = Massif(self.img_model._img_data) cur_peak_points = massif.find_peaks([x, y], stdout=DummyStdOut()) if len(cur_peak_points): self.points.append(np.array(cur_peak_points)) self.points_index.append(peak_ind) return np.array(cur_peak_points) def find_peak(self, x, y, search_size, peak_ind): """ Searches a peak around the x,y position. It just searches for the maximum value in a specific search size. :param x: x-coordinate in pixel - should be from original image (not supersampled x-coordinate) :param y: y-coordinate in pixel - should be form original image (not supersampled y-coordinate) :param search_size: the length of the search rectangle in pixels in all direction in which the algorithm searches for the maximum peak :param peak_ind: peak/ring index to which the found points will be added :return: point found (as array) """ left_ind = np.round(x - search_size * 0.5) if left_ind < 0: left_ind = 0 top_ind = np.round(y - search_size * 0.5) if top_ind < 0: top_ind = 0 search_array = self.img_model.img_data[left_ind:(left_ind + search_size), top_ind:(top_ind + search_size)] x_ind, y_ind = np.where(search_array == search_array.max()) x_ind = x_ind[0] + left_ind y_ind = y_ind[0] + top_ind self.points.append(np.array([x_ind, y_ind])) self.points_index.append(peak_ind) return np.array([np.array((x_ind, y_ind))]) def clear_peaks(self): self.points = [] self.points_index = [] def create_cake_geometry(self): self.cake_geometry = AzimuthalIntegrator() pyFAI_parameter = self.spectrum_geometry.getPyFAI() pyFAI_parameter['polarization_factor'] = self.polarization_factor pyFAI_parameter['wavelength'] = self.spectrum_geometry.wavelength self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.cake_geometry.wavelength = pyFAI_parameter['wavelength'] def setup_peak_search_algorithm(self, algorithm, mask=None): """ Initializes the peak search algorithm on the current image :param algorithm: peak search algorithm used. Possible algorithms are 'Massif' and 'Blob' :param mask: if a mask is used during the process this is provided here as a 2d array for the image. """ if algorithm == 'Massif': self.peak_search_algorithm = Massif(self.img_model.get_raw_img_data()) elif algorithm == 'Blob': if mask is not None: self.peak_search_algorithm = BlobDetection(self.img_model.get_raw_img_data() * mask) else: self.peak_search_algorithm = BlobDetection(self.img_model.get_raw_img_data()) self.peak_search_algorithm.process() else: return def search_peaks_on_ring(self, ring_index, delta_tth=0.1, min_mean_factor=1, upper_limit=55000, mask=None): """ This function is searching for peaks on an expected ring. It needs an initial calibration before. Then it will search for the ring within some delta_tth and other parameters to get peaks from the calibrant. :param ring_index: the index of the ring for the search :param delta_tth: search space around the expected position in two theta :param min_mean_factor: a factor determining the minimum peak intensity to be picked up. it is based on the mean value of the search area defined by delta_tth. Pick a large value for larger minimum value and lower for lower minimum value. Therefore, a smaller number is more prone to picking up noise. typical values like between 1 and 3. :param upper_limit: maximum intensity for the peaks to be picked :param mask: in case the image has to be masked from certain areas, it need to be given here. Default is None. The mask should be given as an 2d array with the same dimensions as the image, where 1 denotes a masked pixel and all others should be 0. """ self.reset_supersampling() if not self.is_calibrated: return # transform delta from degree into radians delta_tth = delta_tth / 180.0 * np.pi # get appropriate two theta value for the ring number tth_calibrant_list = self.calibrant.get_2th() tth_calibrant = np.float(tth_calibrant_list[ring_index]) # get the calculated two theta values for the whole image if self.spectrum_geometry._ttha is None: tth_array = self.spectrum_geometry.twoThetaArray(self.img_model._img_data.shape) else: tth_array = self.spectrum_geometry._ttha # create mask based on two_theta position ring_mask = abs(tth_array - tth_calibrant) <= delta_tth if mask is not None: mask = np.logical_and(ring_mask, np.logical_not(mask)) else: mask = ring_mask # calculate the mean and standard deviation of this area sub_data = np.array(self.img_model._img_data.ravel()[np.where(mask.ravel())], dtype=np.float64) sub_data[np.where(sub_data > upper_limit)] = np.NaN mean = np.nanmean(sub_data) std = np.nanstd(sub_data) # set the threshold into the mask (don't detect very low intensity peaks) threshold = min_mean_factor * mean + std mask2 = np.logical_and(self.img_model._img_data > threshold, mask) mask2[np.where(self.img_model._img_data > upper_limit)] = False size2 = mask2.sum(dtype=int) keep = int(np.ceil(np.sqrt(size2))) try: sys.stdout = DummyStdOut res = self.peak_search_algorithm.peaks_from_area(mask2, Imin=mean - std, keep=keep) sys.stdout = sys.__stdout__ except IndexError: res = [] # Store the result if len(res): self.points.append(np.array(res)) self.points_index.append(ring_index) self.set_supersampling() self.spectrum_geometry.reset() def set_calibrant(self, filename): self.calibrant = Calibrant() self.calibrant.load_file(filename) self.spectrum_geometry.calibrant = self.calibrant def set_start_values(self, start_values): self.start_values = start_values self.polarization_factor = start_values['polarization_factor'] def calibrate(self): self.spectrum_geometry = GeometryRefinement(self.create_point_array(self.points, self.points_index), dist=self.start_values['dist'], wavelength=self.start_values['wavelength'], pixel1=self.start_values['pixel_width'], pixel2=self.start_values['pixel_height'], calibrant=self.calibrant) self.orig_pixel1 = self.start_values['pixel_width'] self.orig_pixel2 = self.start_values['pixel_height'] self.refine() self.create_cake_geometry() self.is_calibrated = True self.calibration_name = 'current' self.set_supersampling() # reset the integrator (not the geometric parameters) self.spectrum_geometry.reset() def refine(self): self.reset_supersampling() self.spectrum_geometry.data = self.create_point_array(self.points, self.points_index) fix = ['wavelength'] if self.fit_wavelength: fix = [] if not self.fit_distance: fix.append('dist') if self.fit_wavelength: self.spectrum_geometry.refine2() self.spectrum_geometry.refine2_wavelength(fix=fix) self.create_cake_geometry() self.set_supersampling() # reset the integrator (not the geometric parameters) self.spectrum_geometry.reset() def integrate_1d(self, num_points=None, mask=None, polarization_factor=None, filename=None, unit='2th_deg', method='csr'): if np.sum(mask) == self.img_model.img_data.shape[0] * self.img_model.img_data.shape[1]: # do not perform integration if the image is completely masked... return self.tth, self.int if self.spectrum_geometry._polarization is not None: if self.img_model.img_data.shape != self.spectrum_geometry._polarization.shape: # resetting the integrator if the polarization correction matrix has not the correct shape self.spectrum_geometry.reset() if polarization_factor is None: polarization_factor = self.polarization_factor if num_points is None: num_points = self.calculate_number_of_spectrum_points(2) self.num_points = num_points t1 = time.time() if unit is 'd_A': try: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_model.img_data, num_points, method=method, unit='2th_deg', mask=mask, polarization_factor=polarization_factor, filename=filename) except NameError: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_model.img_data, num_points, method='csr', unit='2th_deg', mask=mask, polarization_factor=polarization_factor, filename=filename) self.tth = self.spectrum_geometry.wavelength / (2 * np.sin(self.tth / 360 * np.pi)) * 1e10 self.int = self.int else: try: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_model.img_data, num_points, method=method, unit=unit, mask=mask, polarization_factor=polarization_factor, filename=filename) except NameError: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_model.img_data, num_points, method='csr', unit=unit, mask=mask, polarization_factor=polarization_factor, filename=filename) logger.info('1d integration of {0}: {1}s.'.format(os.path.basename(self.img_model.filename), time.time() - t1)) ind = np.where((self.int > 0) & (~np.isnan(self.int))) self.tth = self.tth[ind] self.int = self.int[ind] return self.tth, self.int def integrate_2d(self, mask=None, polarization_factor=None, unit='2th_deg', method='csr', dimensions=(2048, 2048)): if polarization_factor is None: polarization_factor = self.polarization_factor if self.cake_geometry._polarization is not None: if self.img_model.img_data.shape != self.cake_geometry._polarization.shape: # resetting the integrator if the polarization correction matrix has not the same shape as the image self.cake_geometry.reset() t1 = time.time() res = self.cake_geometry.integrate2d(self.img_model._img_data, dimensions[0], dimensions[1], method=method, mask=mask, unit=unit, polarization_factor=polarization_factor) logger.info('2d integration of {0}: {1}s.'.format(os.path.basename(self.img_model.filename), time.time() - t1)) self.cake_img = res[0] self.cake_tth = res[1] self.cake_azi = res[2] return self.cake_img def create_point_array(self, points, points_ind): res = [] for i, point_list in enumerate(points): if point_list.shape == (2,): res.append([point_list[0], point_list[1], points_ind[i]]) else: for point in point_list: res.append([point[0], point[1], points_ind[i]]) return np.array(res) def get_point_array(self): return self.create_point_array(self.points, self.points_index) def get_calibration_parameter(self): pyFAI_parameter = self.cake_geometry.getPyFAI() pyFAI_parameter['polarization_factor'] = self.polarization_factor try: fit2d_parameter = self.cake_geometry.getFit2D() fit2d_parameter['polarization_factor'] = self.polarization_factor except TypeError: fit2d_parameter = None try: pyFAI_parameter['wavelength'] = self.spectrum_geometry.wavelength fit2d_parameter['wavelength'] = self.spectrum_geometry.wavelength except RuntimeWarning: pyFAI_parameter['wavelength'] = 0 return pyFAI_parameter, fit2d_parameter def calculate_number_of_spectrum_points(self, max_dist_factor=1.5): # calculates the number of points for an integrated spectrum, based on the distance of the beam center to the the # image corners. Maximum value is determined by the shape of the image. fit2d_parameter = self.spectrum_geometry.getFit2D() center_x = fit2d_parameter['centerX'] center_y = fit2d_parameter['centerY'] width, height = self.img_model.img_data.shape if center_x < width and center_x > 0: side1 = np.max([abs(width - center_x), center_x]) else: side1 = width if center_y < height and center_y > 0: side2 = np.max([abs(height - center_y), center_y]) else: side2 = height max_dist = np.sqrt(side1 ** 2 + side2 ** 2) return int(max_dist * max_dist_factor) def load(self, filename): """ Loads a calibration file and and sets all the calibration parameter. :param filename: filename for a *.poni calibration file """ self.spectrum_geometry = AzimuthalIntegrator() self.spectrum_geometry.load(filename) self.orig_pixel1 = self.spectrum_geometry.pixel1 self.orig_pixel2 = self.spectrum_geometry.pixel2 self.calibration_name = get_base_name(filename) self.filename = filename self.is_calibrated = True self.create_cake_geometry() self.set_supersampling() def save(self, filename): """ Saves the current calibration parameters into a a text file. Default extension is *.poni """ self.cake_geometry.save(filename) self.calibration_name = get_base_name(filename) self.filename = filename def create_file_header(self): return self.cake_geometry.makeHeaders(polarization_factor=self.polarization_factor) def set_fit2d(self, fit2d_parameter): """ Reads in a dictionary with fit2d parameters where the fields of the dictionary are: 'directDist', 'centerX', 'centerY', 'tilt', 'tiltPlanRotation', 'pixelX', pixelY', 'polarization_factor', 'wavelength' """ self.spectrum_geometry.setFit2D(directDist=fit2d_parameter['directDist'], centerX=fit2d_parameter['centerX'], centerY=fit2d_parameter['centerY'], tilt=fit2d_parameter['tilt'], tiltPlanRotation=fit2d_parameter['tiltPlanRotation'], pixelX=fit2d_parameter['pixelX'], pixelY=fit2d_parameter['pixelY']) self.spectrum_geometry.wavelength = fit2d_parameter['wavelength'] self.create_cake_geometry() self.polarization_factor = fit2d_parameter['polarization_factor'] self.orig_pixel1 = fit2d_parameter['pixelX'] * 1e-6 self.orig_pixel2 = fit2d_parameter['pixelY'] * 1e-6 self.is_calibrated = True self.set_supersampling() def set_pyFAI(self, pyFAI_parameter): """ Reads in a dictionary with pyFAI parameters where the fields of dictionary are: 'dist', 'poni1', 'poni2', 'rot1', 'rot2', 'rot3', 'pixel1', 'pixel2', 'wavelength', 'polarization_factor' """ self.spectrum_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.spectrum_geometry.wavelength = pyFAI_parameter['wavelength'] self.create_cake_geometry() self.polarization_factor = pyFAI_parameter['polarization_factor'] self.orig_pixel1 = pyFAI_parameter['pixel1'] self.orig_pixel2 = pyFAI_parameter['pixel2'] self.is_calibrated = True self.set_supersampling() def set_supersampling(self, factor=None): """ Sets the supersampling to a specific factor. Whereby the factor determines in how many artificial pixel the original pixel is split. (factor^2) factor n_pixel 1 1 2 4 3 9 4 16 """ if factor is None: factor = self.supersampling_factor self.spectrum_geometry.pixel1 = self.orig_pixel1 / float(factor) self.spectrum_geometry.pixel2 = self.orig_pixel2 / float(factor) if factor != self.supersampling_factor: self.spectrum_geometry.reset() self.supersampling_factor = factor def reset_supersampling(self): self.spectrum_geometry.pixel1 = self.orig_pixel1 self.spectrum_geometry.pixel2 = self.orig_pixel2 def get_two_theta_img(self, x, y): """ Gives the two_theta value for the x,y coordinates on the image. Be aware that this function will be incorrect for pixel indices, since it does not correct for center of the pixel. :return: two theta in radians """ x = np.array([x]) * self.supersampling_factor y = np.array([y]) * self.supersampling_factor return self.spectrum_geometry.tth(x - 0.5, y - 0.5)[0] # deletes 0.5 because tth function uses pixel indices def get_azi_img(self, x, y): """ Gives chi for position on image. :param x: x-coordinate in pixel :param y: y-coordinate in pixel :return: azimuth in radians """ x *= self.supersampling_factor y *= self.supersampling_factor return self.spectrum_geometry.chi(x, y)[0] def get_two_theta_cake(self, x): """ Gives the two_theta value for the x coordinate in the cake :param x: x-coordinate on image :return: two theta in degree """ x -= 0.5 cake_step = self.cake_tth[1] - self.cake_tth[0] tth = self.cake_tth[int(np.floor(x))] + (x - np.floor(x)) * cake_step return tth def get_azi_cake(self, x): """ Gives the azimuth value for a cake. :param x: x-coordinate in pixel :return: azimuth in degree """ x -= 0.5 azi_step = self.cake_azi[1] - self.cake_azi[0] azi = self.cake_azi[int(np.floor(x))] + (x - np.floor(x)) * azi_step return azi def get_two_theta_array(self): return self.spectrum_geometry._ttha[::self.supersampling_factor, ::self.supersampling_factor] def get_pixel_ind(self, tth, azi): """ Calculates pixel index for a specfic two theta and azimutal value. :param tth: two theta in radians :param azi: azimuth in radians :return: tuple of index 1 and 2 """ tth_ind = find_contours(self.spectrum_geometry.ttha, tth) tth_ind = np.vstack(tth_ind) azi_values = self.spectrum_geometry.chi(tth_ind[:, 0], tth_ind[:, 1]) min_index = np.argmin(np.abs(azi_values - azi)) return tth_ind[min_index, 0], tth_ind[min_index, 1] @property def wavelength(self): return self.spectrum_geometry.wavelength
class XAnoS_Reducer(QWidget): """ This widget is developed to reduce on the fly 2D SAXS data to azimuthally averaged 1D SAXS data """ def __init__(self,poniFile=None,dataFile=None, darkFile=None, maskFile=None,extractedFolder='/tmp', npt=1000, azimuthalRange=(-180.0,180.0), parent=None): """ poniFile is the calibration file obtained after Q-calibration """ QWidget.__init__(self,parent) self.setup_dict=json.load(open('./SetupData/reducer_setup.txt','r')) if poniFile is not None: self.poniFile=poniFile else: self.poniFile=self.setup_dict['poniFile'] if maskFile is not None: self.maskFile=maskFile else: self.maskFile=self.setup_dict['maskFile'] self.dataFile=dataFile if darkFile is None: self.dark_corrected=False self.darkFile='' else: self.darkFile=darkFile self.dark_corrected=True self.curDir=os.getcwd() self.extractedBaseFolder=extractedFolder self.npt=npt self.set_externally=False #ai=AIWidget() #self.layout.addWidget(ai) self.azimuthalRange=azimuthalRange self.create_UI() if os.path.exists(self.poniFile): self.openPoniFile(file=self.poniFile) if os.path.exists(self.maskFile): self.openMaskFile(file=self.maskFile) self.clientRunning=False def create_UI(self): """ Creates the widget user interface """ loadUi('UI_Forms/Data_Reduction_Client.ui',self) self.poniFileLineEdit.setText(str(self.poniFile)) self.maskFileLineEdit.setText(str(self.maskFile)) self.darkFileLineEdit.setText(str(self.darkFile)) self.extractedBaseFolderLineEdit.setText(self.extractedBaseFolder) self.radialPointsLineEdit.setText(str(self.npt)) self.openDataPushButton.clicked.connect(self.openDataFiles) self.reducePushButton.clicked.connect(self.reduce_multiple) self.openDarkPushButton.clicked.connect(self.openDarkFile) self.openPoniPushButton.clicked.connect(lambda x: self.openPoniFile(file=None)) self.calibratePushButton.clicked.connect(self.calibrate) self.maskFileLineEdit.returnPressed.connect(self.maskFileChanged) self.openMaskPushButton.clicked.connect(lambda x: self.openMaskFile(file=None)) self.createMaskPushButton.clicked.connect(self.createMask) self.extractedFolderPushButton.clicked.connect(self.openFolder) self.extractedFolderLineEdit.textChanged.connect(self.extractedFolderChanged) self.polCorrComboBox.currentIndexChanged.connect(self.polarizationChanged) self.polarizationChanged() self.radialPointsLineEdit.returnPressed.connect(self.nptChanged) self.azimuthalRangeLineEdit.returnPressed.connect(self.azimuthalRangeChanged) self.azimuthalRangeChanged() #self.statusLabel.setStyleSheet("color:rgba(0,1,0,0)") self.imageWidget=Image_Widget(zeros((100,100))) self.cakedImageWidget=Image_Widget(zeros((100,100))) imgNumberLabel=QLabel('Image number') self.imgNumberSpinBox=QSpinBox() self.imgNumberSpinBox.setSingleStep(1) self.imageWidget.imageLayout.addWidget(imgNumberLabel,row=2,col=1) self.imageWidget.imageLayout.addWidget(self.imgNumberSpinBox,row=2,col=2) self.imageView=self.imageWidget.imageView.getView() self.plotWidget=PlotWidget() self.plotWidget.setXLabel('Q, Å<sup>-1</sup>',fontsize=5) self.plotWidget.setYLabel('Intensity',fontsize=5) self.tabWidget.addTab(self.plotWidget,'Reduced 1D-data') self.tabWidget.addTab(self.imageWidget,'Masked 2D-data') self.tabWidget.addTab(self.cakedImageWidget,'Reduced Caked Data') self.serverAddress=self.serverAddressLineEdit.text() self.startClientPushButton.clicked.connect(self.startClient) self.stopClientPushButton.clicked.connect(self.stopClient) self.serverAddressLineEdit.returnPressed.connect(self.serverAddressChanged) self.startServerPushButton.clicked.connect(self.startServer) self.stopServerPushButton.clicked.connect(self.stopServer) def startServer(self): serverAddr=self.serverAddressLineEdit.text() dataDir=QFileDialog.getExistingDirectory(self,'Select data folder',options=QFileDialog.ShowDirsOnly) self.serverStatusLabel.setText('<font color="Red">Transmitting</font>') QApplication.processEvents() self.serverThread=QThread() self.zeromq_server=ZeroMQ_Server(serverAddr,dataDir) self.zeromq_server.moveToThread(self.serverThread) self.serverThread.started.connect(self.zeromq_server.loop) self.zeromq_server.messageEmitted.connect(self.updateServerMessage) self.zeromq_server.folderFinished.connect(self.serverDone) QTimer.singleShot(0,self.serverThread.start) def updateServerMessage(self,mesg): #self.serverStatusLabel.setText('<font color="Red">Transmitting</font>') self.serverMessageLabel.setText('Server sends: %s'%mesg) QApplication.processEvents() def serverDone(self): self.serverStatusLabel.setText('<font color="Green">Idle</font>') self.zeromq_server.socket.unbind(self.zeromq_server.socket.last_endpoint) self.serverThread.quit() self.serverThread.wait() self.serverThread.deleteLater() self.zeromq_server.deleteLater() def stopServer(self): try: self.zeromq_server.running=False self.serverStatusLabel.setText('<font color="Green">Idle</font>') self.zeromq_server.socket.unbind(self.zeromq_server.socket.last_endpoint) self.serverThread.quit() self.serverThread.wait() self.serverThread.deleteLater() self.zeromq_server.deleteLater() except: QMessageBox.warning(self,'Server Error','Start the server before stopping it') def enableClient(self,enable=True): self.startClientPushButton.setEnabled(enable) self.stopClientPushButton.setEnabled(enable) def enableServer(self,enable=True): self.startServerPushButton.setEnabled(enable) self.stopServerPushButton.setEnabled(enable) def startClient(self): if self.clientRunning: self.stopClient() else: self.clientFree=True self.clientRunning=True self.files=[] self.listenerThread = QThread() addr=self.clientAddressLineEdit.text() self.zeromq_listener = ZeroMQ_Listener(addr) self.zeromq_listener.moveToThread(self.listenerThread) self.listenerThread.started.connect(self.zeromq_listener.loop) self.zeromq_listener.messageReceived.connect(self.signal_received) QTimer.singleShot(0, self.listenerThread.start) QTimer.singleShot(0,self.clientReduce) self.clientStatusLabel.setText('<font color="red">Connected</font>') def stopClient(self): try: self.clientRunning=False self.clientFree=False self.zeromq_listener.messageReceived.disconnect() self.zeromq_listener.running=False self.listenerThread.quit() self.listenerThread.wait() self.listenerThread.deleteLater() self.zeromq_listener.deleteLater() self.clientStatusLabel.setText('<font color="green">Idle</font>') except: QMessageBox.warning(self,'Client Error', 'Please start the client first before closing.',QMessageBox.Ok) def serverAddressChanged(self): if self.clientRunning: self.startClient() def signal_received(self, message): self.clientMessageLabel.setText('Client receives: %s'%message) if 'dark.edf' not in message: self.files.append(message) def clientReduce(self): while self.clientFree: QApplication.processEvents() if len(self.files)>0: message=self.files[0] self.dataFiles=[message] self.dataFileLineEdit.setText(str(self.dataFiles)) self.extractedBaseFolder=os.path.dirname(message) self.extractedFolder=os.path.join(self.extractedBaseFolder,self.extractedFolderLineEdit.text()) if not os.path.exists(self.extractedFolder): os.makedirs(self.extractedFolder) self.extractedBaseFolderLineEdit.setText(self.extractedBaseFolder) self.set_externally=True self.reduce_multiple() self.set_externally=False self.files.pop(0) def closeEvent(self, event): if self.clientRunning: self.stopClient() event.accept() def polarizationChanged(self): if self.polCorrComboBox.currentText()=='Horizontal': self.polarization_factor=1 elif self.polCorrComboBox.currentText()=='Vertical': self.polarization_factor=-1 elif self.polCorrComboBox.currentText()=='Circular': self.polarization_factor=0 else: self.polarization_factor=None def createMask(self): """ Opens a mask-widget to create mask file """ fname=str(QFileDialog.getOpenFileName(self,'Select an image file', directory=self.curDir,filter='Image file (*.edf *.tif)')[0]) if fname is not None or fname!='': img=fb.open(fname).data self.maskWidget=MaskWidget(img) self.maskWidget.saveMaskPushButton.clicked.disconnect() self.maskWidget.saveMaskPushButton.clicked.connect(self.save_mask) self.maskWidget.show() else: QMessageBox.warning(self,'File error','Please import a data file first for creating the mask',QMessageBox.Ok) def maskFileChanged(self): """ Changes the mask file """ maskFile=str(self.maskFileLineEdit.text()) if str(maskFile)=='': self.maskFile=None elif os.path.exists(maskFile): self.maskFile=maskFile else: self.maskFile=None def save_mask(self): """ Saves the entire mask combining all the shape ROIs """ fname=str(QFileDialog.getSaveFileName(filter='Mask Files (*.msk)')[0]) name,extn=os.path.splitext(fname) if extn=='': fname=name+'.msk' elif extn!='.msk': QMessageBox.warning(self,'File extension error','Please donot provide file extension other than ".msk". Thank you!') return else: tmpfile=fb.edfimage.EdfImage(data=self.maskWidget.full_mask_data.T,header=None) tmpfile.save(fname) self.maskFile=fname self.maskFileLineEdit.setText(self.maskFile) def calibrate(self): """ Opens a calibartion widget to create calibration file """ fname=str(QFileDialog.getOpenFileName(self,'Select calibration image',directory=self.curDir, filter='Calibration image (*.edf *.tif)')[0]) if fname is not None: img=fb.open(fname).data if self.maskFile is not None: try: mask=fb.open(self.maskFile).data except: QMessageBox.warning(self,'Mask File Error','Cannot open %s.\n No masking will be done.'%self.maskFile) mask=None else: mask=None pixel1=79.0 pixel2=79.0 self.calWidget=CalibrationWidget(img,pixel1,pixel2,mask=mask) self.calWidget.saveCalibrationPushButton.clicked.disconnect() self.calWidget.saveCalibrationPushButton.clicked.connect(self.save_calibration) self.calWidget.show() else: QMessageBox.warning(self,'File error','Please import a data file first for creating the calibration file',QMessageBox.Ok) def save_calibration(self): fname=str(QFileDialog.getSaveFileName(self,'Calibration file',directory=self.curDir,filter='Clibration files (*.poni)')[0]) tfname=os.path.splitext(fname)[0]+'.poni' self.calWidget.applyPyFAI() self.calWidget.geo.save(tfname) self.poniFile=tfname self.poniFileLineEdit.setText(self.poniFile) self.openPoniFile(file=self.poniFile) def openPoniFile(self,file=None): """ Select and imports the calibration file """ if file is None: self.poniFile=QFileDialog.getOpenFileName(self,'Select calibration file',directory=self.curDir,filter='Calibration file (*.poni)')[0] self.poniFileLineEdit.setText(self.poniFile) else: self.poniFile=file if os.path.exists(self.poniFile): self.setup_dict['poniFile']=self.poniFile json.dump(self.setup_dict,open('./SetupData/reducer_setup.txt','w')) fh=open(self.poniFile,'r') lines=fh.readlines() self.calib_data={} for line in lines: if line[0]!='#': key,val=line.split(': ') self.calib_data[key]=float(val) self.dist=self.calib_data['Distance'] self.pixel1=self.calib_data['PixelSize1'] self.pixel2=self.calib_data['PixelSize2'] self.poni1=self.calib_data['Poni1'] self.poni2=self.calib_data['Poni2'] self.rot1=self.calib_data['Rot1'] self.rot2=self.calib_data['Rot2'] self.rot3=self.calib_data['Rot3'] self.wavelength=self.calib_data['Wavelength'] self.ai=AzimuthalIntegrator(dist=self.dist,poni1=self.poni1,poni2=self.poni2,pixel1=self.pixel1,pixel2=self.pixel2,rot1=self.rot1,rot2=self.rot2,rot3=self.rot3,wavelength=self.wavelength) #pos=[self.poni2/self.pixel2,self.poni1/self.pixel1] #self.roi=cake(pos,movable=False) #self.roi.sigRegionChangeStarted.connect(self.endAngleChanged) #self.imageView.addItem(self.roi) else: QMessageBox.warning(self,'File error','The calibration file '+self.poniFile+' doesnot exists.',QMessageBox.Ok) def endAngleChanged(self,evt): print(evt.pos()) def nptChanged(self): """ Changes the number of radial points """ try: self.npt=int(self.radialPointsLineEdit.text()) except: QMessageBox.warning(self,'Value error', 'Please input positive integers only.',QMessageBox.Ok) def azimuthalRangeChanged(self): """ Changes the azimuth angular range """ try: self.azimuthalRange=tuple(map(float, self.azimuthalRangeLineEdit.text().split(':'))) except: QMessageBox.warning(self,'Value error','Please input min:max angles in floating point numbers',QMessageBox.Ok) def openDataFile(self): """ Select and imports one data file """ dataFile=QFileDialog.getOpenFileName(self,'Select data file',directory=self.curDir,filter='Data file (*.edf *.tif)')[0] if dataFile!='': self.dataFile=dataFile self.curDir=os.path.dirname(self.dataFile) self.dataFileLineEdit.setText(self.dataFile) self.data2d=fb.open(self.dataFile).data if self.darkFile is not None: self.applyDark() if self.maskFile is not None: self.applyMask() self.imageWidget.setImage(self.data2d,transpose=True) self.tabWidget.setCurrentWidget(self.imageWidget) if not self.set_externally: self.extractedFolder=os.path.join(self.curDir,self.extractedFolderLineEdit.text()) if not os.path.exists(self.extractedFolder): os.makedirs(self.extractedFolder) def openDataFiles(self): """ Selects and imports multiple data files """ self.dataFiles=QFileDialog.getOpenFileNames(self,'Select data files', directory=self.curDir,filter='Data files (*.edf *.tif)')[0] if len(self.dataFiles)!=0: self.imgNumberSpinBox.valueChanged.connect(self.imageChanged) self.imgNumberSpinBox.setMinimum(0) self.imgNumberSpinBox.setMaximum(len(self.dataFiles)-1) self.dataFileLineEdit.setText(str(self.dataFiles)) self.curDir=os.path.dirname(self.dataFiles[0]) self.extractedBaseFolder=self.curDir self.extractedFolder=os.path.abspath(os.path.join(self.extractedBaseFolder,self.extractedFolderLineEdit.text())) if not os.path.exists(self.extractedFolder): os.makedirs(self.extractedFolder) self.extractedBaseFolderLineEdit.setText(self.extractedBaseFolder) self.imgNumberSpinBox.setValue(0) self.imageChanged() def imageChanged(self): self.data2d=fb.open(self.dataFiles[self.imgNumberSpinBox.value()]).data if self.darkFile is not None: self.applyDark() if self.maskFile is not None: self.applyMask() self.imageWidget.setImage(self.data2d,transpose=True) def applyDark(self): if not self.dark_corrected and self.darkFile!='': self.dark2d=fb.open(self.darkFile).data self.data2d=self.data2d-self.dark2d self.dark_corrected=True def applyMask(self): self.mask2d=fb.open(self.maskFile).data self.data2d=self.data2d*(1+self.mask2d)/2.0 self.mask_applied=True def openDarkFile(self): """ Select and imports the dark file """ self.darkFile=QFileDialog.getOpenFileName(self,'Select dark file',directory=self.curDir,filter='Dark file (*.edf)')[0] if self.darkFile!='': self.dark_corrected=False self.darkFileLineEdit.setText(self.darkFile) if self.dataFile is not None: self.data2d=fb.open(self.dataFile).data self.applyDark() def openMaskFile(self,file=None): """ Select and imports the Mask file """ if file is None: self.maskFile=QFileDialog.getOpenFileName(self,'Select mask file',directory=self.curDir,filter='Mask file (*.msk)')[0] else: self.maskFile=file if self.maskFile!='': self.mask_applied=False if os.path.exists(self.maskFile): self.curDir=os.path.dirname(self.maskFile) self.maskFileLineEdit.setText(self.maskFile) self.setup_dict['maskFile']=self.maskFile self.setup_dict['poniFile']=self.poniFile json.dump(self.setup_dict,open('./SetupData/reducer_setup.txt','w')) else: self.openMaskFile(file=None) if self.dataFile is not None: self.applyMask() else: self.maskFile=None self.maskFileLineEdit.clear() def openFolder(self): """ Select the folder to save the reduce data """ oldfolder=self.extractedBaseFolder.text() folder=QFileDialog.getExistingDirectory(self,'Select extracted directory',directory=self.curDir) if folder!='': self.extractedBaseFolder=folder self.extractedBaseFolderLineEdit.setText(folder) self.extractedFolder=os.path.join(folder,self.extractedFolderLineEdit.text()) self.set_externally=True else: self.extractedBaseFolder=oldfolder self.extractedBaseFolderLineEdit.setText(oldfolder) self.extractedFolder = os.path.join(oldfolder, self.extractedFolderLineEdit.text()) self.set_externally = True def extractedFolderChanged(self,txt): self.extractedFolder=os.path.join(self.extractedBaseFolder,txt) self.set_externally=True def reduceData(self): """ Reduces the 2d data to 1d data """ if (self.dataFile is not None) and (os.path.exists(self.dataFile)): if (self.poniFile is not None) and (os.path.exists(self.poniFile)): # self.statusLabel.setText('Busy') # self.progressBar.setRange(0, 0) imageData=fb.open(self.dataFile) #self.data2d=imageData.data #if self.maskFile is not None: # self.applyMask() #self.imageWidget.setImage(self.data2d,transpose=True) #self.tabWidget.setCurrentWidget(self.imageWidget) self.header=imageData.header try: self.ai.set_wavelength(float(self.header['Wavelength'])*1e-10) except: self.ai.set_wavelength(self.wavelength) #print(self.darkFile) if os.path.exists(self.dataFile.split('.')[0]+'_dark.edf') and self.darkCheckBox.isChecked(): self.darkFile=self.dataFile.split('.')[0]+'_dark.edf' dark=fb.open(self.darkFile) self.darkFileLineEdit.setText(self.darkFile) imageDark=dark.data self.header['BSDiode_corr']=max([1.0,(float(imageData.header['BSDiode'])-float(dark.header['BSDiode']))]) self.header['Monitor_corr']=max([1.0,(float(imageData.header['Monitor'])-float(dark.header['Monitor']))]) print("Dark File read from existing dark files") elif self.darkFile is not None and self.darkFile!='' and self.darkCheckBox.isChecked(): dark=fb.open(self.darkFile) imageDark=dark.data self.header['BSDiode_corr']=max([1.0,(float(imageData.header['BSDiode'])-float(dark.header['BSDiode']))]) self.header['Monitor_corr']=max([1.0,(float(imageData.header['Monitor'])-float(dark.header['Monitor']))]) print("Dark File from memory subtracted") else: imageDark=None try: self.header['BSDiode_corr']=float(imageData.header['BSDiode']) self.header['Monitor_corr']=float(imageData.header['Monitor']) self.header['Transmission'] = float(imageData.header['Transmission']) except: self.normComboBox.setCurrentText('None') print("No dark correction done") if str(self.normComboBox.currentText())=='BSDiode': norm_factor=self.header['BSDiode_corr']#/self.header['Monitor_corr']#float(self.header[ # 'count_time']) elif str(self.normComboBox.currentText())=='TransDiode': norm_factor=self.header['Transmission']*self.header['Monitor_corr'] elif str(self.normComboBox.currentText())=='Monitor': norm_factor=self.header['Monitor_corr'] elif str(self.normComboBox.currentText())=='Image Sum': norm_factor=sum(imageData.data) else: norm_factor=1.0 if self.maskFile is not None: imageMask=fb.open(self.maskFile).data else: imageMask=None # QApplication.processEvents() #print(self.azimuthalRange) self.q,self.I,self.Ierr=self.ai.integrate1d(imageData.data,self.npt,error_model='poisson',mask=imageMask,dark=imageDark,unit='q_A^-1',normalization_factor=norm_factor,azimuth_range=self.azimuthalRange,polarization_factor=self.polarization_factor) self.plotWidget.add_data(self.q,self.I,yerr=self.Ierr,name='Reduced data') if not self.set_externally: cakedI,qr,phir=self.ai.integrate2d(imageData.data,self.npt,mask=imageMask,dark=imageDark,unit='q_A^-1',normalization_factor=norm_factor,polarization_factor=self.polarization_factor) self.cakedImageWidget.setImage(cakedI,xmin=qr[0],xmax=qr[-1],ymin=phir[0],ymax=phir[-1],transpose=True,xlabel='Q ', ylabel='phi ',unit=['Å<sup>-1</sup>','degree']) self.cakedImageWidget.imageView.view.setAspectLocked(False) try: self.azimuthalRegion.setRegion(self.azimuthalRange) except: self.azimuthalRegion=pg.LinearRegionItem(values=self.azimuthalRange,orientation=pg.LinearRegionItem.Horizontal,movable=True,bounds=[-180,180]) self.cakedImageWidget.imageView.getView().addItem(self.azimuthalRegion) self.azimuthalRegion.sigRegionChanged.connect(self.azimuthalRegionChanged) self.plotWidget.setTitle(self.dataFile,fontsize=3) # self.progressBar.setRange(0,100) # self.progressBar.setValue(100) # self.statusLabel.setText('Idle') # QApplication.processEvents() self.saveData() #self.tabWidget.setCurrentWidget(self.plotWidget) else: QMessageBox.warning(self,'Calibration File Error','Data reduction failed because either no calibration file provided or the provided file or path do not exists',QMessageBox.Ok) else: QMessageBox.warning(self,'Data File Error','No data file provided', QMessageBox.Ok) def azimuthalRegionChanged(self): minp,maxp=self.azimuthalRegion.getRegion() self.azimuthalRangeLineEdit.setText('%.1f:%.1f'%(minp,maxp)) self.azimuthalRange=[minp,maxp] self.set_externally=True def reduce_multiple(self): """ Reduce multiple files """ try: i=0 self.progressBar.setRange(0,len(self.dataFiles)) self.progressBar.setValue(i) self.statusLabel.setText('<font color="red">Busy</font>') for file in self.dataFiles: self.dataFile=file QApplication.processEvents() self.reduceData() i=i+1 self.progressBar.setValue(i) QApplication.processEvents() self.statusLabel.setText('<font color="green">Idle</font>') self.progressBar.setValue(0) except: QMessageBox.warning(self,'File error','No data files to reduce',QMessageBox.Ok) def saveData(self): """ saves the extracted data into a file """ if not os.path.exists(self.extractedFolder): os.makedirs(self.extractedFolder) filename=os.path.join(self.extractedFolder,os.path.splitext(os.path.basename(self.dataFile))[0]+'.txt') headers='File extracted on '+time.asctime()+'\n' headers='Files used for extraction are:\n' headers+='Data file: '+self.dataFile+'\n' if self.darkFile is not None: headers+='Dark file: '+self.darkFile+'\n' else: headers+='Dark file: None\n' headers+='Poni file: '+self.poniFile+'\n' if self.maskFile is not None: headers+='mask file: '+self.maskFile+'\n' else: headers+='mask file: None\n' for key in self.header.keys(): headers+=key+'='+str(self.header[key])+'\n' headers+="col_names=['Q (inv Angs)','Int','Int_err']\n" headers+='Q (inv Angs)\tInt\tInt_err' data=vstack((self.q,self.I,self.Ierr)).T savetxt(filename,data,header=headers,comments='#')
class CalibrateDialog(BaseDialog): def __init__(self, parent=None): super(CalibrateDialog, self).__init__(parent) self.plotview = None self.data = None self.counts = None self.points = [] self.pattern_geometry = None self.cake_geometry = None self.is_calibrated = False cstr = str(ALL_CALIBRANTS) calibrants = sorted(cstr[cstr.index(':') + 2:].split(', ')) self.parameters = GridParameters() self.parameters.add('calibrant', calibrants, 'Calibrant') self.parameters['calibrant'].value = 'CeO2' self.parameters.add('wavelength', 0.5, 'Wavelength (Ang)', False) self.parameters.add('distance', 100.0, 'Detector Distance (mm)', True) self.parameters.add('xc', 512, 'Beam Center - x', True) self.parameters.add('yc', 512, 'Beam Center - y', True) self.parameters.add('yaw', 0.0, 'Yaw (degrees)', True) self.parameters.add('pitch', 0.0, 'Pitch (degrees)', True) self.parameters.add('roll', 0.0, 'Roll (degrees)', True) self.parameters.add('search_size', 10, 'Search Size (pixels)') rings = ['Ring1', 'Ring2', 'Ring3', 'Ring4', 'Ring5'] self.rings_box = self.select_box(rings) self.set_layout( self.select_entry(self.choose_entry), self.action_buttons(('Plot Calibration', self.plot_data)), self.parameters.grid(header=False), self.make_layout( self.action_buttons(('Select Points', self.select)), self.rings_box), self.action_buttons(('Calibrate', self.calibrate), ('Plot Cake', self.plot_cake), ('Restore', self.restore_parameters), ('Save', self.save_parameters)), self.close_buttons(close=True)) self.set_title('Calibrating Powder') def choose_entry(self): if 'calibration' not in self.entry['instrument']: raise NeXusError('Please load calibration data to this entry') self.update_parameters() self.plot_data() def update_parameters(self): self.parameters['wavelength'].value = self.entry[ 'instrument/monochromator/wavelength'] detector = self.entry['instrument/detector'] self.parameters['distance'].value = detector['distance'] self.parameters['yaw'].value = detector['yaw'] self.parameters['pitch'].value = detector['pitch'] self.parameters['roll'].value = detector['roll'] if 'beam_center_x' in detector: self.parameters['xc'].value = detector['beam_center_x'] if 'beam_center_y' in detector: self.parameters['yc'].value = detector['beam_center_y'] self.data = self.entry['instrument/calibration'] self.counts = self.data.nxsignal.nxvalue @property def search_size(self): return int(self.parameters['search_size'].value) @property def ring(self): return int(self.rings_box.currentText()[-1]) - 1 @property def ring_color(self): colors = ['r', 'b', 'g', 'c', 'm'] return colors[self.ring] def plot_data(self): if self.plotview is None: if 'Powder Calibration' in plotviews: self.plotview = plotviews['Powder Calibration'] else: self.plotview = NXPlotView('Powder Calibration') self.plotview.plot(self.data, log=True) self.plotview.aspect = 'equal' self.plotview.ytab.flipped = True self.clear_peaks() def on_button_press(self, event): self.plotview.make_active() if event.inaxes: self.xp, self.yp = event.x, event.y else: self.xp, self.yp = 0, 0 def on_button_release(self, event): if event.inaxes: if abs(event.x - self.xp) > 5 or abs(event.y - self.yp) > 5: return x, y = self.plotview.inverse_transform(event.xdata, event.ydata) for i, point in enumerate(self.points): circle = point[0] if circle.contains_point( self.plotview.ax.transData.transform((x, y))): circle.remove() for circle in point[2]: circle.remove() del self.points[i] return idx, idy = self.find_peak(x, y) points = [(idy, idx)] circles = [] massif = Massif(self.counts) extra_points = massif.find_peaks((idy, idx)) for point in extra_points: points.append(point) circles.append(self.circle(point[1], point[0], alpha=0.3)) self.points.append( [self.circle(idx, idy), points, circles, self.ring]) def circle(self, idx, idy, alpha=1.0): return self.plotview.circle(idx, idy, self.search_size, facecolor=self.ring_color, edgecolor='k', alpha=alpha) def select(self): self.plotview.cidpress = self.plotview.mpl_connect( 'button_press_event', self.on_button_press) self.plotview.cidrelease = self.plotview.mpl_connect( 'button_release_event', self.on_button_release) def find_peak(self, x, y): s = self.search_size left = int(np.round(x - s * 0.5)) if left < 0: left = 0 top = int(np.round(y - s * 0.5)) if top < 0: top = 0 region = self.counts[top:(top + s), left:(left + s)] idy, idx = np.where(region == region.max()) idx = left + idx[0] idy = top + idy[0] return idx, idy def clear_peaks(self): self.points = [] @property def calibrant(self): return ALL_CALIBRANTS[self.parameters['calibrant'].value] @property def point_array(self): points = [] for point in self.points: for p in point[1]: points.append((p[0], p[1], point[3])) return np.array(points) def prepare_parameters(self): self.parameters.set_parameters() self.wavelength = self.parameters['wavelength'].value * 1e-10 self.distance = self.parameters['distance'].value * 1e-3 self.yaw = np.radians(self.parameters['yaw'].value) self.pitch = np.radians(self.parameters['pitch'].value) self.roll = np.radians(self.parameters['roll'].value) self.pixel_size = self.entry[ 'instrument/detector/pixel_size'].nxvalue * 1e-3 self.xc = self.parameters['xc'].value self.yc = self.parameters['yc'].value def calibrate(self): self.prepare_parameters() self.orig_pixel1 = self.pixel_size self.orig_pixel2 = self.pixel_size self.pattern_geometry = GeometryRefinement(self.point_array, dist=self.distance, wavelength=self.wavelength, pixel1=self.pixel_size, pixel2=self.pixel_size, calibrant=self.calibrant) self.refine() self.create_cake_geometry() self.pattern_geometry.reset() def refine(self): self.pattern_geometry.data = self.point_array if self.parameters['wavelength'].vary: self.pattern_geometry.refine2() fix = [] else: fix = ['wavelength'] if not self.parameters['distance'].vary: fix.append('dist') self.pattern_geometry.refine2_wavelength(fix=fix) self.read_parameters() self.is_calibrated = True self.create_cake_geometry() self.pattern_geometry.reset() def create_cake_geometry(self): self.cake_geometry = AzimuthalIntegrator() pyFAI_parameter = self.pattern_geometry.getPyFAI() pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.cake_geometry.wavelength = pyFAI_parameter['wavelength'] def plot_cake(self): if 'Cake Plot' in plotviews: plotview = plotviews['Cake Plot'] else: plotview = NXPlotView('Cake Plot') if not self.is_calibrated: raise NeXusError('No refinement performed') res = self.cake_geometry.integrate2d(self.counts, 1024, 1024, method='csr', unit='2th_deg', correctSolidAngle=True) self.cake_data = NXdata(res[0], (NXfield(res[2], name='azimumthal_angle'), NXfield(res[1], name='polar_angle'))) self.cake_data['title'] = self.entry['instrument/calibration/title'] plotview.plot(self.cake_data, log=True) wavelength = self.parameters['wavelength'].value polar_angles = [ 2 * np.degrees(np.arcsin(wavelength / (2 * d))) for d in self.calibrant.dSpacing ] plotview.vlines([ polar_angle for polar_angle in polar_angles if polar_angle < plotview.xaxis.max ], linestyle=':', color='r') def read_parameters(self): pyFAI = self.pattern_geometry.getPyFAI() fit2d = self.pattern_geometry.getFit2D() self.parameters[ 'wavelength'].value = self.pattern_geometry.wavelength * 1e10 self.parameters['distance'].value = pyFAI['dist'] * 1e3 self.parameters['yaw'].value = np.degrees(pyFAI['rot1']) self.parameters['pitch'].value = np.degrees(pyFAI['rot2']) self.parameters['roll'].value = np.degrees(pyFAI['rot3']) self.parameters['xc'].value = fit2d['centerX'] self.parameters['yc'].value = fit2d['centerY'] def restore_parameters(self): self.parameters.restore_parameters() def save_parameters(self): if not self.is_calibrated: raise NeXusError('No refinement performed') elif 'refinement' in self.entry['instrument/calibration']: if confirm_action('Overwrite previous refinement?'): del self.entry['instrument/calibration/refinement'] else: return self.entry['instrument/calibration/calibrant'] = self.parameters[ 'calibrant'].value process = NXprocess() process.program = 'pyFAI' process.version = pyFAI.version process.parameters = NXcollection() process.parameters['Detector'] = self.entry[ 'instrument/detector/description'] pyFAI_parameter = self.pattern_geometry.getPyFAI() process.parameters['PixelSize1'] = pyFAI_parameter['pixel1'] process.parameters['PixelSize2'] = pyFAI_parameter['pixel2'] process.parameters['Distance'] = pyFAI_parameter['dist'] process.parameters['Poni1'] = pyFAI_parameter['poni1'] process.parameters['Poni2'] = pyFAI_parameter['poni2'] process.parameters['Rot1'] = pyFAI_parameter['rot1'] process.parameters['Rot2'] = pyFAI_parameter['rot2'] process.parameters['Rot3'] = pyFAI_parameter['rot3'] process.parameters['Wavelength'] = pyFAI_parameter['wavelength'] self.entry['instrument/calibration/refinement'] = process self.entry['instrument/monochromator/wavelength'] = self.parameters[ 'wavelength'].value self.entry[ 'instrument/monochromator/energy'] = 12.398419739640717 / self.parameters[ 'wavelength'].value detector = self.entry['instrument/detector'] detector['distance'] = self.parameters['distance'].value detector['yaw'] = self.parameters['yaw'].value detector['pitch'] = self.parameters['pitch'].value detector['roll'] = self.parameters['roll'].value detector['beam_center_x'] = self.parameters['xc'].value detector['beam_center_y'] = self.parameters['yc'].value def reject(self): super(CalibrateDialog, self).reject() if 'Powder Calibration' in plotviews: plotviews['Powder Calibration'].close_view() if 'Cake Plot' in plotviews: plotviews['Cake Plot'].close_view()
class CalibrationData(object): def __init__(self, img_data=None): self.img_data = img_data self.points = [] self.points_index = [] self.spectrum_geometry = AzimuthalIntegrator() self.calibrant = Calibrant() self.start_values = {'dist': 200e-3, 'wavelength': 0.3344e-10, 'pixel_width': 79e-6, 'pixel_height': 79e-6, 'polarization_factor': 0.99} self.orig_pixel1 = 79e-6 self.orig_pixel2 = 79e-6 self.fit_wavelength = False self.fit_distance = True self.is_calibrated = False self.use_mask = False self.filename = '' self.calibration_name = 'None' self.polarization_factor = 0.99 self.supersampling_factor = 1 self._calibrants_working_dir = os.path.dirname(Calibrants.__file__) self.cake_img = np.zeros((2048, 2048)) self.tth = np.linspace(0, 25) self.int = np.sin(self.tth) def find_peaks_automatic(self, x, y, peak_ind): """ Searches peaks by using the Massif algorithm :param x: x-coordinate in pixel - should be from original image (not supersampled x-coordinate) :param y: y-coordinate in pixel - should be from original image (not supersampled y-coordinate) :param peak_ind: peak/ring index to which the found points will be added :return: array of points found """ massif = Massif(self.img_data._img_data) cur_peak_points = massif.find_peaks([x, y]) if len(cur_peak_points): self.points.append(np.array(cur_peak_points)) self.points_index.append(peak_ind) return np.array(cur_peak_points) def find_peak(self, x, y, search_size, peak_ind): """ Searches a peak around the x,y position. It just searches for the maximum value in a specific search size. :param x: x-coordinate in pixel - should be from original image (not supersampled x-coordinate) :param y: y-coordinate in pixel - should be form original image (not supersampled y-coordinate) :param search_size: the amount of pixels in all direction in which the algorithm searches for the maximum peak :param peak_ind: peak/ring index to which the found points will be added :return: point found (as array) """ left_ind = np.round(x - search_size * 0.5) top_ind = np.round(y - search_size * 0.5) x_ind, y_ind = np.where(self.img_data._img_data[left_ind:(left_ind + search_size), top_ind:(top_ind + search_size)] == \ self.img_data._img_data[left_ind:(left_ind + search_size), top_ind:(top_ind + search_size)].max()) x_ind = x_ind[0] + left_ind y_ind = y_ind[0] + top_ind self.points.append(np.array([x_ind, y_ind])) self.points_index.append(peak_ind) return np.array([np.array((x_ind, y_ind))]) def clear_peaks(self): self.points = [] self.points_index = [] def create_cake_geometry(self): self.cake_geometry = AzimuthalIntegrator() pyFAI_parameter = self.spectrum_geometry.getPyFAI() pyFAI_parameter['polarization_factor'] = self.polarization_factor pyFAI_parameter['wavelength'] = self.spectrum_geometry.wavelength self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.cake_geometry.wavelength = pyFAI_parameter['wavelength'] def setup_peak_search_algorithm(self, algorithm, mask=None): """ Initializes the peak search algorithm on the current image :param algorithm: peak search algorithm used. Possible algorithms are 'Massif' and 'Blob' :param mask: if a mask is used during the process this is provided here as a 2d array for the image. """ if algorithm == 'Massif': self.peak_search_algorithm = Massif(self.img_data._img_data) elif algorithm == 'Blob': if mask is not None: self.peak_search_algorithm = BlobDetection(self.img_data._img_data * mask) else: self.peak_search_algorithm = BlobDetection(self.img_data._img_data) self.peak_search_algorithm.process() else: return def search_peaks_on_ring(self, peak_index, delta_tth=0.1, min_mean_factor=1, upper_limit=55000, mask=None): self.reset_supersampling() if not self.is_calibrated: return # transform delta from degree into radians delta_tth = delta_tth / 180.0 * np.pi # get appropriate two theta value for the ring number tth_calibrant_list = self.calibrant.get_2th() tth_calibrant = np.float(tth_calibrant_list[peak_index]) # get the calculated two theta values for the whole image if self.spectrum_geometry._ttha is None: tth_array = self.spectrum_geometry.twoThetaArray(self.img_data._img_data.shape) else: tth_array = self.spectrum_geometry._ttha # create mask based on two_theta position ring_mask = abs(tth_array - tth_calibrant) <= delta_tth if mask is not None: mask = np.logical_and(ring_mask, np.logical_not(mask)) else: mask = ring_mask # calculate the mean and standard deviation of this area sub_data = np.array(self.img_data._img_data.ravel()[np.where(mask.ravel())], dtype=np.float64) sub_data[np.where(sub_data > upper_limit)] = np.NaN mean = np.nanmean(sub_data) std = np.nanstd(sub_data) # set the threshold into the mask (don't detect very low intensity peaks) threshold = min_mean_factor * mean + std mask2 = np.logical_and(self.img_data._img_data > threshold, mask) mask2[np.where(self.img_data._img_data > upper_limit)] = False size2 = mask2.sum(dtype=int) keep = int(np.ceil(np.sqrt(size2))) try: res = self.peak_search_algorithm.peaks_from_area(mask2, Imin=mean - std, keep=keep) except IndexError: res = [] # Store the result if len(res): self.points.append(np.array(res)) self.points_index.append(peak_index) self.set_supersampling() self.spectrum_geometry.reset() def set_calibrant(self, filename): self.calibrant = Calibrant() self.calibrant.load_file(filename) self.spectrum_geometry.calibrant = self.calibrant def set_start_values(self, start_values): self.start_values = start_values self.polarization_factor = start_values['polarization_factor'] def calibrate(self): self.spectrum_geometry = GeometryRefinement(self.create_point_array(self.points, self.points_index), dist=self.start_values['dist'], wavelength=self.start_values['wavelength'], pixel1=self.start_values['pixel_width'], pixel2=self.start_values['pixel_height'], calibrant=self.calibrant) self.orig_pixel1 = self.start_values['pixel_width'] self.orig_pixel2 = self.start_values['pixel_height'] self.refine() self.create_cake_geometry() self.is_calibrated = True self.calibration_name = 'current' self.set_supersampling() # reset the integrator (not the geometric parameters) self.spectrum_geometry.reset() def refine(self): self.reset_supersampling() self.spectrum_geometry.data = self.create_point_array(self.points, self.points_index) fix = ['wavelength'] if self.fit_wavelength: fix = [] if not self.fit_distance: fix.append('dist') if self.fit_wavelength: self.spectrum_geometry.refine2() self.spectrum_geometry.refine2_wavelength(fix=fix) self.create_cake_geometry() self.set_supersampling() # reset the integrator (not the geometric parameters) self.spectrum_geometry.reset() def integrate_1d(self, num_points=None, mask=None, polarization_factor=None, filename=None, unit='2th_deg', method='csr'): if np.sum(mask) == self.img_data.img_data.shape[0] * self.img_data.img_data.shape[1]: # do not perform integration if the image is completely masked... return self.tth, self.int if self.spectrum_geometry._polarization is not None: if self.img_data.img_data.shape != self.spectrum_geometry._polarization.shape: # resetting the integrator if the polarization correction matrix has not the correct shape self.spectrum_geometry.reset() if polarization_factor is None: polarization_factor = self.polarization_factor if num_points is None: num_points = self.calculate_number_of_spectrum_points(2) self.num_points = num_points t1 = time.time() if unit is 'd_A': try: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_data.img_data, num_points, method=method, unit='2th_deg', mask=mask, polarization_factor=polarization_factor, filename=filename) except NameError: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_data.img_data, num_points, method=method, unit='2th_deg', mask=mask, polarization_factor=polarization_factor, filename=filename) self.tth = self.spectrum_geometry.wavelength / (2 * np.sin(self.tth / 360 * np.pi)) * 1e10 self.int = self.int else: try: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_data.img_data, num_points, method=method, unit=unit, mask=mask, polarization_factor=polarization_factor, filename=filename) except NameError: self.tth, self.int = self.spectrum_geometry.integrate1d(self.img_data.img_data, num_points, method='lut', unit=unit, mask=mask, polarization_factor=polarization_factor, filename=filename) logger.info('1d integration of {}: {}s.'.format(os.path.basename(self.img_data.filename), time.time() - t1)) ind = np.where((self.int > 0) & (~np.isnan(self.int))) self.tth = self.tth[ind] self.int = self.int[ind] return self.tth, self.int def integrate_2d(self, mask=None, polarization_factor=None, unit='2th_deg', method='csr', dimensions=(2048, 2048)): if polarization_factor is None: polarization_factor = self.polarization_factor if self.cake_geometry._polarization is not None: if self.img_data.img_data.shape != self.cake_geometry._polarization.shape: # resetting the integrator if the polarization correction matrix has not the same shape as the image self.cake_geometry.reset() t1 = time.time() res = self.cake_geometry.integrate2d(self.img_data._img_data, dimensions[0], dimensions[1], method=method, mask=mask, unit=unit, polarization_factor=polarization_factor) logger.info('2d integration of {}: {}s.'.format(os.path.basename(self.img_data.filename), time.time() - t1)) self.cake_img = res[0] self.cake_tth = res[1] self.cake_azi = res[2] return self.cake_img def create_point_array(self, points, points_ind): res = [] for i, point_list in enumerate(points): if point_list.shape == (2,): res.append([point_list[0], point_list[1], points_ind[i]]) else: for point in point_list: res.append([point[0], point[1], points_ind[i]]) return np.array(res) def get_point_array(self): return self.create_point_array(self.points, self.points_index) def get_calibration_parameter(self): pyFAI_parameter = self.cake_geometry.getPyFAI() pyFAI_parameter['polarization_factor'] = self.polarization_factor try: fit2d_parameter = self.cake_geometry.getFit2D() fit2d_parameter['polarization_factor'] = self.polarization_factor except TypeError: fit2d_parameter = None try: pyFAI_parameter['wavelength'] = self.spectrum_geometry.wavelength fit2d_parameter['wavelength'] = self.spectrum_geometry.wavelength except RuntimeWarning: pyFAI_parameter['wavelength'] = 0 return pyFAI_parameter, fit2d_parameter def calculate_number_of_spectrum_points(self, max_dist_factor=1.5): # calculates the number of points for an integrated spectrum, based on the distance of the beam center to the the #image corners. Maximum value is determined by the shape of the image. fit2d_parameter = self.spectrum_geometry.getFit2D() center_x = fit2d_parameter['centerX'] center_y = fit2d_parameter['centerY'] width, height = self.img_data.img_data.shape if center_x < width and center_x > 0: side1 = np.max([abs(width - center_x), center_x]) else: side1 = width if center_y < height and center_y > 0: side2 = np.max([abs(height - center_y), center_y]) else: side2 = height max_dist = np.sqrt(side1 ** 2 + side2 ** 2) return int(max_dist * max_dist_factor) def load(self, filename): self.spectrum_geometry = AzimuthalIntegrator() self.spectrum_geometry.load(filename) self.orig_pixel1 = self.spectrum_geometry.pixel1 self.orig_pixel2 = self.spectrum_geometry.pixel2 self.calibration_name = get_base_name(filename) self.filename = filename self.is_calibrated = True self.create_cake_geometry() self.set_supersampling() def save(self, filename): self.cake_geometry.save(filename) self.calibration_name = get_base_name(filename) self.filename = filename def create_file_header(self): return self.cake_geometry.makeHeaders(polarization_factor=self.polarization_factor) def set_fit2d(self, fit2d_parameter): print fit2d_parameter self.spectrum_geometry.setFit2D(directDist=fit2d_parameter['directDist'], centerX=fit2d_parameter['centerX'], centerY=fit2d_parameter['centerY'], tilt=fit2d_parameter['tilt'], tiltPlanRotation=fit2d_parameter['tiltPlanRotation'], pixelX=fit2d_parameter['pixelX'], pixelY=fit2d_parameter['pixelY']) self.spectrum_geometry.wavelength = fit2d_parameter['wavelength'] self.create_cake_geometry() self.polarization_factor = fit2d_parameter['polarization_factor'] self.orig_pixel1 = fit2d_parameter['pixelX'] * 1e-6 self.orig_pixel2 = fit2d_parameter['pixelY'] * 1e-6 self.is_calibrated = True self.set_supersampling() def set_pyFAI(self, pyFAI_parameter): self.spectrum_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.spectrum_geometry.wavelength = pyFAI_parameter['wavelength'] self.create_cake_geometry() self.polarization_factor = pyFAI_parameter['polarization_factor'] self.orig_pixel1 = pyFAI_parameter['pixel1'] self.orig_pixel2 = pyFAI_parameter['pixel2'] self.is_calibrated = True self.set_supersampling() def set_supersampling(self, factor=None): if factor is None: factor = self.supersampling_factor self.spectrum_geometry.pixel1 = self.orig_pixel1 / float(factor) self.spectrum_geometry.pixel2 = self.orig_pixel2 / float(factor) if factor != self.supersampling_factor: self.spectrum_geometry.reset() self.supersampling_factor = factor def reset_supersampling(self): self.spectrum_geometry.pixel1 = self.orig_pixel1 self.spectrum_geometry.pixel2 = self.orig_pixel2 def get_two_theta_img(self, x, y): """ Gives the two_theta value for the x,y coordinates on the image :return: two theta in radians """ x = np.array([x]) * self.supersampling_factor y = np.array([y]) * self.supersampling_factor return self.spectrum_geometry.tth(x, y)[0] def get_azi_img(self, x, y): """ Gives chi for position on image. :param x: x-coordinate in pixel :param y: y-coordinate in pixel :return: azimuth in radians """ x *= self.supersampling_factor y *= self.supersampling_factor return self.spectrum_geometry.chi(x, y)[0] def get_two_theta_cake(self, y): """ Gives the two_theta value for the x coordinate in the cake :param x: y-coordinate on image :return: two theta in degree """ return self.cake_tth[np.round(y[0])] def get_azi_cake(self, x): """ Gives the azimuth value for a cake. :param x: x-coordinate in pixel :return: azimuth in degree """ return self.cake_azi[np.round(x[0])] def get_two_theta_array(self): return self.spectrum_geometry._ttha[::self.supersampling_factor, ::self.supersampling_factor] @property def wavelength(self): return self.spectrum_geometry.wavelength
class TestMultiGeometry(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) self.data = fabio.open(UtilsTest.getimage("1788/moke.tif")).data self.lst_data = [ self.data[:250, :300], self.data[250:, :300], self.data[:250, 300:], self.data[250:, 300:] ] self.det = Detector(1e-4, 1e-4) self.det.max_shape = (500, 600) self.sub_det = Detector(1e-4, 1e-4) self.sub_det.max_shape = (250, 300) self.ai = AzimuthalIntegrator(0.1, 0.03, 0.03, detector=self.det) self.range = (0, 23) self.ais = [ AzimuthalIntegrator(0.1, 0.030, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.03, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.030, 0.00, detector=self.sub_det), AzimuthalIntegrator(0.1, 0.005, 0.00, detector=self.sub_det), ] self.mg = MultiGeometry(self.ais, radial_range=self.range, unit="2th_deg") self.N = 390 def tearDown(self): unittest.TestCase.tearDown(self) self.data = None self.lst_data = None self.det = None self.sub_det = None self.ai = None self.ais = None self.mg = None def test_integrate1d(self): tth_ref, I_ref = self.ai.integrate1d(self.data, radial_range=self.range, npt=self.N, unit="2th_deg", method="splitpixel") obt = self.mg.integrate1d(self.lst_data, self.N) tth_obt, I_obt = obt self.assertEqual( abs(tth_ref - tth_obt).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = (abs(I_obt * 1e6 - I_ref).max()) self.assert_(delta < 5e-5, "Intensity is the same delta=%s" % delta) def test_integrate2d(self): ref = self.ai.integrate2d(self.data, self.N, 360, radial_range=self.range, azimuth_range=(-180, 180), unit="2th_deg", method="splitpixel", all=True) obt = self.mg.integrate2d(self.lst_data, self.N, 360, all=True) self.assertEqual( abs(ref["radial"] - obt["radial"]).max(), 0, "Bin position is the same") self.assertEqual( abs(ref["azimuthal"] - obt["azimuthal"]).max(), 0, "Bin position is the same") # intensity need to be scaled by solid angle 1e-4*1e-4/0.1**2 = 1e-6 delta = abs(obt["I"] * 1e6 - ref["I"])[obt["count"] >= 1e-6] # restrict on valid pixel delta_cnt = abs(obt["count"] - ref["count"]) delta_sum = abs(obt["sum"] * 1e6 - ref["sum"]) if delta.max() > 0: logger.warning( "TestMultiGeometry.test_integrate2d gave intensity difference of %s" % delta.max()) if logger.level <= logging.DEBUG: from matplotlib import pyplot as plt f = plt.figure() a1 = f.add_subplot(2, 2, 1) a1.imshow(ref["sum"]) a2 = f.add_subplot(2, 2, 2) a2.imshow(obt["sum"]) a3 = f.add_subplot(2, 2, 3) a3.imshow(delta_sum) a4 = f.add_subplot(2, 2, 4) a4.plot(delta_sum.sum(axis=0)) f.show() raw_input() self.assert_(delta_cnt.max() < 0.001, "pixel count is the same delta=%s" % delta_cnt.max()) self.assert_(delta_sum.max() < 0.03, "pixel sum is the same delta=%s" % delta_sum.max()) self.assert_( delta.max() < 0.004, "pixel intensity is the same (for populated pixels) delta=%s" % delta.max())
class CalibrateDialog(NXDialog): def __init__(self, parent=None): super().__init__(parent) self.plotview = None self.data = None self.counts = None self.points = [] self.pattern_geometry = None self.cake_geometry = None self.polarization = None self.is_calibrated = False self.phi_max = -np.pi cstr = str(ALL_CALIBRANTS) calibrants = sorted(cstr[cstr.index(':')+2:].split(', ')) self.parameters = GridParameters() self.parameters.add('calibrant', calibrants, 'Calibrant') self.parameters['calibrant'].value = 'CeO2' self.parameters.add('wavelength', 0.5, 'Wavelength (Ang)', False) self.parameters.add('distance', 100.0, 'Detector Distance (mm)', True) self.parameters.add('xc', 512, 'Beam Center - x', True) self.parameters.add('yc', 512, 'Beam Center - y', True) self.parameters.add('yaw', 0.0, 'Yaw (degrees)', True) self.parameters.add('pitch', 0.0, 'Pitch (degrees)', True) self.parameters.add('roll', 0.0, 'Roll (degrees)', True) self.parameters.add('search_size', 10, 'Search Size (pixels)') self.rings_box = self.select_box([f'Ring{i}' for i in range(1, 21)]) self.set_layout(self.select_entry(self.choose_entry), self.progress_layout(close=True)) self.set_title('Calibrating Powder') def choose_file(self): super().choose_file() powder_file = self.get_filename() if powder_file: self.data = load_image(powder_file) self.counts = self.data.nxsignal.nxvalue self.plot_data() def choose_entry(self): if self.layout.count() == 2: self.insert_layout( 1, self.filebox('Choose Powder Calibration File')) self.insert_layout(2, self.parameters.grid(header=False)) self.insert_layout( 3, self.action_buttons(('Select Points', self.select), ('Autogenerate Rings', self.auto), ('Clear Points', self.clear_points))) self.insert_layout(4, self.make_layout(self.rings_box)) self.insert_layout( 5, self.action_buttons(('Calibrate', self.calibrate), ('Plot Cake', self.plot_cake), ('Restore', self.restore_parameters), ('Save', self.save_parameters))) self.parameters['wavelength'].value = ( self.entry['instrument/monochromator/wavelength']) detector = self.entry['instrument/detector'] self.parameters['distance'].value = detector['distance'] self.parameters['yaw'].value = detector['yaw'] self.parameters['pitch'].value = detector['pitch'] self.parameters['roll'].value = detector['roll'] if 'beam_center_x' in detector: self.parameters['xc'].value = detector['beam_center_x'] if 'beam_center_y' in detector: self.parameters['yc'].value = detector['beam_center_y'] self.pixel_size = ( self.entry['instrument/detector/pixel_size'].nxvalue * 1e-3) self.pixel_mask = self.entry['instrument/detector/pixel_mask'].nxvalue self.ring = self.selected_ring if 'calibration' in self.entry['instrument']: self.data = self.entry['instrument/calibration'] self.counts = self.data.nxsignal.nxvalue self.plot_data() else: self.close_plots() @property def search_size(self): return int(self.parameters['search_size'].value) @property def selected_ring(self): return int(self.rings_box.currentText()[4:]) - 1 @property def ring_color(self): colors = ['r', 'b', 'g', 'c', 'm'] * 4 return colors[self.ring] def plot_data(self): if self.plotview is None: if 'Powder Calibration' in plotviews: self.plotview = plotviews['Powder Calibration'] else: self.plotview = NXPlotView('Powder Calibration') self.plotview.plot(self.data, log=True) self.plotview.aspect = 'equal' self.plotview.ytab.flipped = True self.clear_points() def on_button_press(self, event): self.plotview.make_active() if event.inaxes: self.xp, self.yp = event.x, event.y else: self.xp, self.yp = 0, 0 def on_button_release(self, event): self.ring = self.selected_ring if event.inaxes: if abs(event.x - self.xp) > 5 or abs(event.y - self.yp) > 5: return x, y = self.plotview.inverse_transform(event.xdata, event.ydata) for i, point in enumerate(self.points): circle = point[0] if circle.shape.contains_point( self.plotview.ax.transData.transform((x, y))): circle.remove() for circle in point[2]: circle.remove() del self.points[i] return self.add_points(x, y) def circle(self, idx, idy, alpha=1.0): return self.plotview.circle(idx, idy, self.search_size, facecolor=self.ring_color, edgecolor='k', alpha=alpha) def select(self): self.plotview.cidpress = self.plotview.mpl_connect( 'button_press_event', self.on_button_press) self.plotview.cidrelease = self.plotview.mpl_connect( 'button_release_event', self.on_button_release) def auto(self): xc, yc = self.parameters['xc'].value, self.parameters['yc'].value wavelength = self.parameters['wavelength'].value distance = self.parameters['distance'].value * 1e-3 self.start_progress((0, self.selected_ring+1)) for ring in range(self.selected_ring+1): self.update_progress(ring) if len([p for p in self.points if p[3] == ring]) > 0: continue self.ring = ring theta = 2 * np.arcsin(wavelength / (2*self.calibrant.dSpacing[ring])) r = distance * np.tan(theta) / self.pixel_size phi = self.phi_max = -np.pi while phi < np.pi: x, y = np.int(xc + r*np.cos(phi)), np.int(yc + r*np.sin(phi)) if ((x > 0 and x < self.data.x.max()) and (y > 0 and y < self.data.y.max()) and not self.pixel_mask[y, x]): self.add_points(x, y, phi) phi = self.phi_max + 0.2 else: phi = phi + 0.2 self.stop_progress() def add_points(self, x, y, phi=0.0): xc, yc = self.parameters['xc'].value, self.parameters['yc'].value idx, idy = self.find_peak(x, y) points = [(idy, idx)] circles = [] massif = Massif(self.counts) extra_points = massif.find_peaks((idy, idx)) for point in extra_points: points.append(point) circles.append(self.circle(point[1], point[0], alpha=0.3)) phis = np.array([np.arctan2(p[0]-yc, p[1]-xc) for p in points]) if phi < -0.5*np.pi: phis[np.where(phis > 0.0)] -= 2 * np.pi self.phi_max = max(*phis, self.phi_max) self.points.append([self.circle(idx, idy), points, circles, self.ring]) def find_peak(self, x, y): s = self.search_size left = int(np.round(x - s * 0.5)) if left < 0: left = 0 top = int(np.round(y - s * 0.5)) if top < 0: top = 0 region = self.counts[top:(top+s), left:(left+s)] idy, idx = np.where(region == region.max()) idx = left + idx[0] idy = top + idy[0] return idx, idy def clear_points(self): for i, point in enumerate(self.points): circle = point[0] circle.remove() for circle in point[2]: circle.remove() self.points = [] @property def calibrant(self): return ALL_CALIBRANTS[self.parameters['calibrant'].value] @property def point_array(self): points = [] for point in self.points: for p in point[1]: points.append((p[0], p[1], point[3])) return np.array(points) def prepare_parameters(self): self.parameters.set_parameters() self.wavelength = self.parameters['wavelength'].value * 1e-10 self.distance = self.parameters['distance'].value * 1e-3 self.yaw = np.radians(self.parameters['yaw'].value) self.pitch = np.radians(self.parameters['pitch'].value) self.roll = np.radians(self.parameters['roll'].value) self.xc = self.parameters['xc'].value self.yc = self.parameters['yc'].value def calibrate(self): self.prepare_parameters() self.orig_pixel1 = self.pixel_size self.orig_pixel2 = self.pixel_size self.pattern_geometry = GeometryRefinement(self.point_array, dist=self.distance, wavelength=self.wavelength, pixel1=self.pixel_size, pixel2=self.pixel_size, calibrant=self.calibrant) self.refine() self.create_cake_geometry() self.pattern_geometry.reset() def refine(self): self.pattern_geometry.data = self.point_array if self.parameters['wavelength'].vary: self.pattern_geometry.refine2() fix = [] else: fix = ['wavelength'] if not self.parameters['distance'].vary: fix.append('dist') self.pattern_geometry.refine2_wavelength(fix=fix) self.read_parameters() self.is_calibrated = True self.create_cake_geometry() self.pattern_geometry.reset() def create_cake_geometry(self): self.cake_geometry = AzimuthalIntegrator() pyFAI_parameter = self.pattern_geometry.getPyFAI() pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.cake_geometry.wavelength = pyFAI_parameter['wavelength'] def plot_cake(self): if 'Cake Plot' in plotviews: plotview = plotviews['Cake Plot'] else: plotview = NXPlotView('Cake Plot') if not self.is_calibrated: raise NeXusError('No refinement performed') res = self.cake_geometry.integrate2d(self.counts, 1024, 1024, method='csr', unit='2th_deg', correctSolidAngle=True) self.cake_data = NXdata(res[0], (NXfield(res[2], name='azimumthal_angle'), NXfield(res[1], name='polar_angle'))) self.cake_data['title'] = 'Cake Plot' plotview.plot(self.cake_data, log=True) wavelength = self.parameters['wavelength'].value polar_angles = [2 * np.degrees(np.arcsin(wavelength/(2*d))) for d in self.calibrant.dSpacing] plotview.vlines([polar_angle for polar_angle in polar_angles if polar_angle < plotview.xaxis.max], linestyle=':', color='r') def read_parameters(self): pyFAI = self.pattern_geometry.getPyFAI() fit2d = self.pattern_geometry.getFit2D() self.parameters['wavelength'].value = ( self.pattern_geometry.wavelength * 1e10) self.parameters['distance'].value = pyFAI['dist'] * 1e3 self.parameters['yaw'].value = np.degrees(pyFAI['rot1']) self.parameters['pitch'].value = np.degrees(pyFAI['rot2']) self.parameters['roll'].value = np.degrees(pyFAI['rot3']) self.parameters['xc'].value = fit2d['centerX'] self.parameters['yc'].value = fit2d['centerY'] def restore_parameters(self): self.parameters.restore_parameters() def save_parameters(self): if not self.is_calibrated: raise NeXusError('No refinement performed') elif 'calibration' in self.entry['instrument']: if confirm_action( "Do you want to overwrite existing calibration data?"): del self.entry['instrument/calibration'] else: return self.entry['instrument/calibration'] = self.data if 'refinement' in self.entry['instrument/calibration']: if confirm_action('Overwrite previous refinement?'): del self.entry['instrument/calibration/refinement'] else: return self.entry['instrument/calibration/calibrant'] = ( self.parameters['calibrant'].value) process = NXprocess() process.program = 'pyFAI' process.version = pyFAI.version process.parameters = NXcollection() process.parameters['Detector'] = ( self.entry['instrument/detector/description']) pyFAI_parameter = self.pattern_geometry.getPyFAI() process.parameters['PixelSize1'] = pyFAI_parameter['pixel1'] process.parameters['PixelSize2'] = pyFAI_parameter['pixel2'] process.parameters['Distance'] = pyFAI_parameter['dist'] process.parameters['Poni1'] = pyFAI_parameter['poni1'] process.parameters['Poni2'] = pyFAI_parameter['poni2'] process.parameters['Rot1'] = pyFAI_parameter['rot1'] process.parameters['Rot2'] = pyFAI_parameter['rot2'] process.parameters['Rot3'] = pyFAI_parameter['rot3'] process.parameters['Wavelength'] = pyFAI_parameter['wavelength'] self.entry['instrument/calibration/refinement'] = process self.entry['instrument/monochromator/wavelength'] = ( self.parameters['wavelength'].value) self.entry['instrument/monochromator/energy'] = ( 12.398419739640717 / self.parameters['wavelength'].value) detector = self.entry['instrument/detector'] detector['distance'] = self.parameters['distance'].value detector['yaw'] = self.parameters['yaw'].value detector['pitch'] = self.parameters['pitch'].value detector['roll'] = self.parameters['roll'].value detector['beam_center_x'] = self.parameters['xc'].value detector['beam_center_y'] = self.parameters['yc'].value try: detector['polarization'] = self.pattern_geometry.polarization( factor=0.99, shape=detector['mask'].shape) except Exception: pass def close_plots(self): if 'Powder Calibration' in plotviews: plotviews['Powder Calibration'].close() if 'Cake Plot' in plotviews: plotviews['Cake Plot'].close() def closeEvent(self, event): self.close_plots() event.accept() def accept(self): super().accept() self.close_plots() def reject(self): super().reject() self.close_plots()
def run(self): ai = AzimuthalIntegrator( dist=self.__distance, poni1=self.__poni1, poni2=self.__poni2, rot1=self.__rotation1, rot2=self.__rotation2, rot3=self.__rotation3, detector=self.__detector, wavelength=self.__wavelength) # FIXME Add error model method1d = method_registry.Method(1, self.__method.split, self.__method.algo, self.__method.impl, None) methods = method_registry.IntegrationMethod.select_method(method=method1d) if len(methods) == 0: method1d = method_registry.Method(1, method1d.split, "*", "*", None) _logger.warning("Downgrade 1D integration method to %s", method1d) else: method1d = methods[0].method method2d = method_registry.Method(2, self.__method.split, self.__method.algo, self.__method.impl, None) methods = method_registry.IntegrationMethod.select_method(method=method2d) if len(methods) == 0: method2d = method_registry.Method(2, method2d.split, "*", "*", None) _logger.warning("Downgrade 2D integration method to %s", method2d) else: method2d = methods[0].method self.__result1d = ai.integrate1d( method=method1d, data=self.__image, npt=self.__nPointsRadial, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) self.__result2d = ai.integrate2d( method=method2d, data=self.__image, npt_rad=self.__nPointsRadial, npt_azim=self.__nPointsAzimuthal, unit=self.__radialUnit, mask=self.__mask, polarization_factor=self.__polarizationFactor) # Create an image masked where data exists self.__resultMask2d = None if self.__mask is not None: if self.__mask.shape == self.__image.shape: maskData = numpy.ones(shape=self.__image.shape, dtype=numpy.float32) maskData[self.__mask == 0] = float("NaN") if self.__displayMask: self.__resultMask2d = ai.integrate2d( method=method2d, data=maskData, npt_rad=self.__nPointsRadial, npt_azim=self.__nPointsAzimuthal, unit=self.__radialUnit, polarization_factor=self.__polarizationFactor) else: _logger.warning("Inconsistency between image and mask sizes. %s != %s", self.__image.shape, self.__mask.shape) try: self.__directDist = ai.getFit2D()["directDist"] except Exception: # The geometry could not fit this param _logger.debug("Backtrace", exc_info=True) self.__directDist = None if self.__calibrant: rings = self.__calibrant.get_2th() try: rings = unitutils.from2ThRad(rings, self.__radialUnit, self.__wavelength, self.__directDist) except ValueError: message = "Convertion to unit %s not supported. Ring marks ignored" _logger.warning(message, self.__radialUnit) rings = [] # Filter the rings which are not part of the result rings = filter(lambda x: self.__result1d.radial[0] <= x <= self.__result1d.radial[-1], rings) rings = list(rings) else: rings = [] self.__ringAngles = rings self.__ai = ai
class CalibrateDialog(BaseDialog): def __init__(self, parent=None): super(CalibrateDialog, self).__init__(parent) self.plotview = None self.data = None self.counts = None self.points = [] self.pattern_geometry = None self.cake_geometry = None self.is_calibrated = False cstr = str(ALL_CALIBRANTS) calibrants = sorted(cstr[cstr.index(':')+2:].split(', ')) self.parameters = GridParameters() self.parameters.add('calibrant', calibrants, 'Calibrant') self.parameters['calibrant'].value = 'CeO2' self.parameters.add('wavelength', 0.5, 'Wavelength (Ang)', False) self.parameters.add('distance', 100.0, 'Detector Distance (mm)', True) self.parameters.add('xc', 512, 'Beam Center - x', True) self.parameters.add('yc', 512, 'Beam Center - y', True) self.parameters.add('yaw', 0.0, 'Yaw (degrees)', True) self.parameters.add('pitch', 0.0, 'Pitch (degrees)', True) self.parameters.add('roll', 0.0, 'Roll (degrees)', True) self.parameters.add('search_size', 10, 'Search Size (pixels)') rings = ['Ring%s' % i for i in range(1,21)] self.rings_box = self.select_box(rings) self.set_layout(self.select_entry(self.choose_entry), self.action_buttons(('Plot Calibration', self.plot_data)), self.parameters.grid(header=False), self.make_layout( self.action_buttons(('Select Points', self.select)), self.rings_box), self.action_buttons(('Calibrate', self.calibrate), ('Plot Cake', self.plot_cake), ('Restore', self.restore_parameters), ('Save', self.save_parameters)), self.close_buttons(close=True)) self.set_title('Calibrating Powder') def choose_entry(self): if 'calibration' not in self.entry['instrument']: raise NeXusError('Please load calibration data to this entry') self.update_parameters() self.plot_data() def update_parameters(self): self.parameters['wavelength'].value = self.entry['instrument/monochromator/wavelength'] detector = self.entry['instrument/detector'] self.parameters['distance'].value = detector['distance'] self.parameters['yaw'].value = detector['yaw'] self.parameters['pitch'].value = detector['pitch'] self.parameters['roll'].value = detector['roll'] if 'beam_center_x' in detector: self.parameters['xc'].value = detector['beam_center_x'] if 'beam_center_y' in detector: self.parameters['yc'].value = detector['beam_center_y'] self.data = self.entry['instrument/calibration'] self.counts = self.data.nxsignal.nxvalue @property def search_size(self): return int(self.parameters['search_size'].value) @property def ring(self): return int(self.rings_box.currentText()[4:]) - 1 @property def ring_color(self): colors = ['r', 'b', 'g', 'c', 'm'] * 4 return colors[self.ring] def plot_data(self): if self.plotview is None: if 'Powder Calibration' in plotviews: self.plotview = plotviews['Powder Calibration'] else: self.plotview = NXPlotView('Powder Calibration') self.plotview.plot(self.data, log=True) self.plotview.aspect='equal' self.plotview.ytab.flipped = True self.clear_peaks() def on_button_press(self, event): self.plotview.make_active() if event.inaxes: self.xp, self.yp = event.x, event.y else: self.xp, self.yp = 0, 0 def on_button_release(self, event): if event.inaxes: if abs(event.x - self.xp) > 5 or abs(event.y - self.yp) > 5: return x, y = self.plotview.inverse_transform(event.xdata, event.ydata) for i, point in enumerate(self.points): circle = point[0] if circle.contains_point(self.plotview.ax.transData.transform((x,y))): circle.remove() for circle in point[2]: circle.remove() del self.points[i] return idx, idy = self.find_peak(x, y) points = [(idy, idx)] circles = [] massif = Massif(self.counts) extra_points = massif.find_peaks((idy, idx)) for point in extra_points: points.append(point) circles.append(self.circle(point[1], point[0], alpha=0.3)) self.points.append([self.circle(idx, idy), points, circles, self.ring]) def circle(self, idx, idy, alpha=1.0): return self.plotview.circle(idx, idy, self.search_size, facecolor=self.ring_color, edgecolor='k', alpha=alpha) def select(self): self.plotview.cidpress = self.plotview.mpl_connect( 'button_press_event', self.on_button_press) self.plotview.cidrelease = self.plotview.mpl_connect( 'button_release_event', self.on_button_release) def find_peak(self, x, y): s = self.search_size left = int(np.round(x - s * 0.5)) if left < 0: left = 0 top = int(np.round(y - s * 0.5)) if top < 0: top = 0 region = self.counts[top:(top+s),left:(left+s)] idy, idx = np.where(region == region.max()) idx = left + idx[0] idy = top + idy[0] return idx, idy def clear_peaks(self): self.points = [] @property def calibrant(self): return ALL_CALIBRANTS[self.parameters['calibrant'].value] @property def point_array(self): points = [] for point in self.points: for p in point[1]: points.append((p[0], p[1], point[3])) return np.array(points) def prepare_parameters(self): self.parameters.set_parameters() self.wavelength = self.parameters['wavelength'].value * 1e-10 self.distance = self.parameters['distance'].value * 1e-3 self.yaw = np.radians(self.parameters['yaw'].value) self.pitch = np.radians(self.parameters['pitch'].value) self.roll = np.radians(self.parameters['roll'].value) self.pixel_size = self.entry['instrument/detector/pixel_size'].nxvalue * 1e-3 self.xc = self.parameters['xc'].value self.yc = self.parameters['yc'].value def calibrate(self): self.prepare_parameters() self.orig_pixel1 = self.pixel_size self.orig_pixel2 = self.pixel_size self.pattern_geometry = GeometryRefinement(self.point_array, dist=self.distance, wavelength=self.wavelength, pixel1=self.pixel_size, pixel2=self.pixel_size, calibrant=self.calibrant) self.refine() self.create_cake_geometry() self.pattern_geometry.reset() def refine(self): self.pattern_geometry.data = self.point_array if self.parameters['wavelength'].vary: self.pattern_geometry.refine2() fix = [] else: fix = ['wavelength'] if not self.parameters['distance'].vary: fix.append('dist') self.pattern_geometry.refine2_wavelength(fix=fix) self.read_parameters() self.is_calibrated = True self.create_cake_geometry() self.pattern_geometry.reset() def create_cake_geometry(self): self.cake_geometry = AzimuthalIntegrator() pyFAI_parameter = self.pattern_geometry.getPyFAI() pyFAI_parameter['wavelength'] = self.pattern_geometry.wavelength self.cake_geometry.setPyFAI(dist=pyFAI_parameter['dist'], poni1=pyFAI_parameter['poni1'], poni2=pyFAI_parameter['poni2'], rot1=pyFAI_parameter['rot1'], rot2=pyFAI_parameter['rot2'], rot3=pyFAI_parameter['rot3'], pixel1=pyFAI_parameter['pixel1'], pixel2=pyFAI_parameter['pixel2']) self.cake_geometry.wavelength = pyFAI_parameter['wavelength'] def plot_cake(self): if 'Cake Plot' in plotviews: plotview = plotviews['Cake Plot'] else: plotview = NXPlotView('Cake Plot') if not self.is_calibrated: raise NeXusError('No refinement performed') res = self.cake_geometry.integrate2d(self.counts, 1024, 1024, method='csr', unit='2th_deg', correctSolidAngle=True) self.cake_data = NXdata(res[0], (NXfield(res[2], name='azimumthal_angle'), NXfield(res[1], name='polar_angle'))) self.cake_data['title'] = self.entry['instrument/calibration/title'] plotview.plot(self.cake_data, log=True) wavelength = self.parameters['wavelength'].value polar_angles = [2 * np.degrees(np.arcsin(wavelength/(2*d))) for d in self.calibrant.dSpacing] plotview.vlines([polar_angle for polar_angle in polar_angles if polar_angle < plotview.xaxis.max], linestyle=':', color='r') def read_parameters(self): pyFAI = self.pattern_geometry.getPyFAI() fit2d = self.pattern_geometry.getFit2D() self.parameters['wavelength'].value = self.pattern_geometry.wavelength * 1e10 self.parameters['distance'].value = pyFAI['dist'] * 1e3 self.parameters['yaw'].value = np.degrees(pyFAI['rot1']) self.parameters['pitch'].value = np.degrees(pyFAI['rot2']) self.parameters['roll'].value = np.degrees(pyFAI['rot3']) self.parameters['xc'].value = fit2d['centerX'] self.parameters['yc'].value = fit2d['centerY'] def restore_parameters(self): self.parameters.restore_parameters() def save_parameters(self): if not self.is_calibrated: raise NeXusError('No refinement performed') elif 'refinement' in self.entry['instrument/calibration']: if confirm_action('Overwrite previous refinement?'): del self.entry['instrument/calibration/refinement'] else: return self.entry['instrument/calibration/calibrant'] = self.parameters['calibrant'].value process = NXprocess() process.program = 'pyFAI' process.version = pyFAI.version process.parameters = NXcollection() process.parameters['Detector'] = self.entry['instrument/detector/description'] pyFAI_parameter = self.pattern_geometry.getPyFAI() process.parameters['PixelSize1'] = pyFAI_parameter['pixel1'] process.parameters['PixelSize2'] = pyFAI_parameter['pixel2'] process.parameters['Distance'] = pyFAI_parameter['dist'] process.parameters['Poni1'] = pyFAI_parameter['poni1'] process.parameters['Poni2'] = pyFAI_parameter['poni2'] process.parameters['Rot1'] = pyFAI_parameter['rot1'] process.parameters['Rot2'] = pyFAI_parameter['rot2'] process.parameters['Rot3'] = pyFAI_parameter['rot3'] process.parameters['Wavelength'] = pyFAI_parameter['wavelength'] self.entry['instrument/calibration/refinement'] = process self.entry['instrument/monochromator/wavelength'] = self.parameters['wavelength'].value self.entry['instrument/monochromator/energy'] = 12.398419739640717 / self.parameters['wavelength'].value detector = self.entry['instrument/detector'] detector['distance'] = self.parameters['distance'].value detector['yaw'] = self.parameters['yaw'].value detector['pitch'] = self.parameters['pitch'].value detector['roll'] = self.parameters['roll'].value detector['beam_center_x'] = self.parameters['xc'].value detector['beam_center_y'] = self.parameters['yc'].value def reject(self): super(CalibrateDialog, self).reject() if 'Powder Calibration' in plotviews: plotviews['Powder Calibration'].close_view() if 'Cake Plot' in plotviews: plotviews['Cake Plot'].close_view()