Exemplo n.º 1
0
    def __init__(self, img_model=None):
        """
        :param img_model:
        :type img_model: ImgModel
        """
        super(CalibrationModel, self).__init__()
        self.img_model = img_model
        self.points = []
        self.points_index = []

        self.detector = Detector(pixel1=79e-6, pixel2=79e-6)
        self.detector_mode = DetectorModes.CUSTOM
        self._original_detector = None  # used for saving original state before rotating or flipping
        self.pattern_geometry = GeometryRefinement(
            detector=self.detector, wavelength=0.3344e-10, poni1=0,
            poni2=0)  # default params are necessary, otherwise fails...
        self.pattern_geometry_img_shape = None
        self.cake_geometry = None
        self.cake_geometry_img_shape = None
        self.calibrant = Calibrant()

        self.orig_pixel1 = self.detector.pixel1  # needs to be extra stored for applying supersampling
        self.orig_pixel2 = self.detector.pixel2

        self.start_values = {
            'dist': 200e-3,
            'wavelength': 0.3344e-10,
            'polarization_factor': 0.99
        }
        self.fit_wavelength = False
        self.fixed_values = {
        }  # dictionary for fixed parameters during calibration (keys can be e.g. rot1, poni1 etc.
        # and values are the values to what the respective parameter will be set
        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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    def loadCalibrant(self):
        dialog = self.createCalibrantDialog("Load calibrant file")

        result = dialog.exec_()
        if not result:
            return

        filename = dialog.selectedFiles()[0]
        try:
            calibrant = Calibrant(filename=filename)
        except Exception as e:
            _logger.error(e.args[0])
            _logger.debug("Backtrace", exc_info=True)
            # FIXME Display error dialog
            return
        except KeyboardInterrupt:
            raise

        try:
            settings = self.model().experimentSettingsModel()
            settings.calibrantModel().setCalibrant(calibrant)
        except Exception as e:
            _logger.error(e.args[0])
            _logger.debug("Backtrace", exc_info=True)
            # FIXME Display error dialog
        except KeyboardInterrupt:
            raise
Exemplo n.º 4
0
def test_calib_md(fresh_xrun, exp_hash_uid, glbl, db):
    xrun = fresh_xrun
    # calib run
    sample_md = _sample_name_phase_info_configuration(None, None, "calib")
    calibrant = os.path.join(glbl["usrAnalysis_dir"], "Ni24.D")
    detector = "perkin_elmer"
    _collect_img(
        5,
        True,
        sample_md,
        xrun,
        detector=detector,
        calibrant=calibrant,
    )
    calib_hdr = db[-1]
    assert "Ni_calib" == calib_hdr.start["sample_name"]
    assert detector == calib_hdr.start["detector"]
    calibrant_obj = Calibrant(calibrant)
    start_doc = calib_hdr.start
    assert calibrant_obj.dSpacing == start_doc["dSpacing"]
    assert start_doc["is_calibration"]
    for k, expected in sample_md.items():
        actual = start_doc[k]
        assert expected == actual
    server_uid = start_doc["detector_calibration_server_uid"]
    client_uid = start_doc["detector_calibration_client_uid"]
    assert server_uid == exp_hash_uid
    assert server_uid == client_uid
    # production run
    xrun(0, 0)
    hdr = db[-1]
    client_uid = hdr.start["detector_calibration_client_uid"]
    assert client_uid == exp_hash_uid
    assert "detector_calibration_server_uid" not in hdr.start
    # new uid
    new_hash = update_experiment_hash_uid()
    # production run first
    xrun(0, 0)
    hdr = db[-1]
    client_uid = hdr.start["detector_calibration_client_uid"]
    assert client_uid == new_hash
    assert "detector_calibration_server_uid" not in hdr.start
    # new calib run
    _collect_img(
        5,
        True,
        sample_md,
        xrun,
        detector=detector,
        calibrant=calibrant,
    )
    calib_hdr = db[-1]
    server_uid = calib_hdr.start["detector_calibration_server_uid"]
    client_uid = calib_hdr.start["detector_calibration_client_uid"]
    assert server_uid == new_hash
    assert server_uid == client_uid
    # md link
    calib_server_uid = calib_hdr.start["detector_calibration_server_uid"]
    hdr_client_uid = hdr.start["detector_calibration_client_uid"]
    assert calib_server_uid == hdr_client_uid
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
 def __init__(self, img_data=None):
     self.img_data = img_data
     self.points = []
     self.points_index = []
     self.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.95
     }
     self.fit_wavelength = False
     self.is_calibrated = False
     self.use_mask = False
     self.calibration_name = 'None'
     self.polarization_factor = 0.95
     self._calibrants_working_dir = os.path.dirname(Calibrants.__file__)
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
def test_load_calibrant(fresh_xrun, bt):
    xrun = fresh_xrun
    xrun.beamtime = bt
    # pyfai factory
    for k, calibrant_obj in ALL_CALIBRANTS.items():
        # light weight callback
        def check_eq(name, doc):
            assert calibrant_obj.dSpacing == doc["dSpacing"]
            assert k == doc["sample_name"]

        t = xrun.subscribe(check_eq, "start")
        # execute
        run_calibration(calibrant=k,
                        phase_info=k,
                        RE_instance=xrun,
                        wait_for_cal=False)
        # clean
        xrun.unsubscribe(t)
    # invalid calibrant
    with pytest.raises(xpdAcqException):
        run_calibration(calibrant="pyFAI",
                        phase_info="buggy",
                        RE_instance=xrun,
                        wait_for_cal=False)
    # filepath
    pytest_dir = rs_fn("xpdacq", "tests/")
    src = os.path.join(pytest_dir, "Ni24.D")
    dst_base = os.path.abspath(str(uuid.uuid4()))
    os.makedirs(dst_base)
    fn = str(uuid.uuid4())
    dst = os.path.join(dst_base, fn + ".D")
    shutil.copy(src, dst)
    c = Calibrant(filename=dst)

    def check_eq(name, doc):
        assert c.dSpacing == doc["dSpacing"]
        assert dst == doc["sample_name"]

    t = xrun.subscribe(check_eq, "start")
    # execute
    run_calibration(calibrant=dst,
                    phase_info="buggy",
                    RE_instance=xrun,
                    wait_for_cal=False)
    # clean
    xrun.unsubscribe(t)
Exemplo n.º 9
0
 def __init__(self, img_data=None):
     self.img_data = img_data
     self.points = []
     self.points_index = []
     self.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.95}
     self.fit_wavelength = False
     self.is_calibrated = False
     self.use_mask = False
     self.calibration_name = 'None'
     self.polarization_factor = 0.95
     self._calibrants_working_dir = os.path.dirname(Calibrants.__file__)
Exemplo n.º 10
0
class CalibrationData(object):
    def __init__(self, img_data=None):
        self.img_data = img_data
        self.points = []
        self.points_index = []
        self.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.95}
        self.fit_wavelength = False
        self.is_calibrated = False
        self.use_mask = False
        self.calibration_name = 'None'
        self.polarization_factor = 0.95
        self._calibrants_working_dir = os.path.dirname(Calibrants.__file__)

    def find_peaks_automatic(self, x, y, peak_ind):
        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):
        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 setup_peak_search_algorithm(self, algorithm, mask=None):
        # init the peak search algorithm
        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):
        if not self.is_calibrated:
            return

        #transform delta from degree into radians
        delta_tth = delta_tth / 180.0 * np.pi

        # get appropiate 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.geometry._ttha is None:
            tth_array = self.geometry.twoThetaArray(self.img_data.img_data.shape)
        else:
            tth_array = self.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)

    def set_calibrant(self, filename):
        self.calibrant = Calibrant()
        self.calibrant.load_file(filename)
        self.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.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.refine()
        self.integrate()
        self.is_calibrated = True
        self.calibration_name = 'current'

    def refine(self):
        self.geometry.data = self.create_point_array(self.points, self.points_index)
        self.geometry.refine2()
        if self.fit_wavelength:
            self.geometry.refine2_wavelength(fix=[])

    def integrate(self):
        self.integrate_1d()
        self.integrate_2d()

    def integrate_1d(self, num_points=1400, mask=None, polarization_factor=None, filename=None, unit='2th_deg'):
        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 completelye masked...
            return self.tth, self.int
        if polarization_factor is None:
            polarization_factor = self.polarization_factor
        if unit is 'd_A':
            self.tth, self.int = self.geometry.integrate1d(self.img_data.img_data, num_points, method='lut',
                                                           unit='2th_deg',
                                                           mask=mask, polarization_factor=polarization_factor,
                                                           filename=filename)
            ind = np.where(self.tth > 0)
            self.tth = self.geometry.wavelength / (2 * np.sin(self.tth[ind] / 360 * np.pi)) * 1e10
            self.int = self.int[ind]
        else:
            self.tth, self.int = self.geometry.integrate1d(self.img_data.img_data, num_points, method='lut', unit=unit,
                                                           mask=mask, polarization_factor=polarization_factor,
                                                           filename=filename)
        if self.int.max() > 0:
            ind = np.where(self.int > 0)
            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'):
        if polarization_factor is None:
            polarization_factor = self.polarization_factor
        res = self.geometry.integrate2d(self.img_data.img_data, 2048, 2048, method='lut', mask=mask, unit=unit,
                                        polarization_factor=polarization_factor)
        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.geometry.getPyFAI()
        pyFAI_parameter['polarization_factor'] = self.polarization_factor
        try:
            fit2d_parameter = self.geometry.getFit2D()
            fit2d_parameter['polarization_factor'] = self.polarization_factor
        except TypeError:
            fit2d_parameter = None
        try:
            pyFAI_parameter['wavelength'] = self.geometry.wavelength
            fit2d_parameter['wavelength'] = self.geometry.wavelength
        except RuntimeWarning:
            pyFAI_parameter['wavelength'] = 0

        return pyFAI_parameter, fit2d_parameter

    def load(self, filename):
        self.geometry = GeometryRefinement(np.zeros((2, 3)),
                                           dist=self.start_values['dist'],
                                           wavelength=self.start_values['wavelength'],
                                           pixel1=self.start_values['pixel_width'],
                                           pixel2=self.start_values['pixel_height'])
        self.geometry.load(filename)
        self.calibration_name = get_base_name(filename)
        self.is_calibrated = True

    def save(self, filename):
        self.geometry.save(filename)
        self.calibration_name = get_base_name(filename)
Exemplo n.º 11
0
 def set_calibrant(self, filename):
     self.calibrant = Calibrant()
     self.calibrant.load_file(filename)
     self.spectrum_geometry.calibrant = self.calibrant
Exemplo n.º 12
0
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
Exemplo n.º 13
0
 def set_calibrant(self, filename):
     self.calibrant = Calibrant()
     self.calibrant.load_file(filename)
     self.geometry.calibrant = self.calibrant
Exemplo n.º 14
0
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
Exemplo n.º 15
0
def calibration(img, calibrant_file=None, wavelength=None,
                calib_collection_uid=None, save_file_name=None,
                detector=None, gaussian=None):
    """ run calibration process on a image with geometry correction
    software

    current backend is ``pyFAI``.

    Parameters
    ----------
    img : ndarray
        image to be calibrated
    calibrant_file : str, optional
        calibrant file being used, default is 'Ni.D' under 
        xpdUser/userAnalysis/
    wavelength : flot, optional
        current of x-ray wavelength, in angstrom. Default value is 
        read out from existing xpdacq.Beamtime object
    calibration_collection_uid : str, optional
        uid of calibration collection. default is generated from run
        calibration
    save_file_name : str, optional
        file name for yaml that carries resultant calibration parameters
    detector : pyfai.detector.Detector, optional.
        instance of detector which defines pixel size in x- and
        y-direction. Default is set to Perkin Elmer detector
    gaussian : int, optional
        gaussian width between rings, Default is 100.
    """
    # default params
    interactive = True
    dist = 0.1

    _check_obj(_REQUIRED_OBJ_LIST)
    ips = get_ipython()
    bto = ips.ns_table['user_global']['bt']
    xrun = ips.ns_table['user_global']['xrun']

    calibrant = Calibrant()
    # d-spacing
    if calibrant_file is not None:
        calibrant.load_file(calibrant_file)
        calibrant_name = os.path.split(calibrant_file)[1]
        calibrant_name = os.path.splitext(calibrant_name)[0]
    else:
        calibrant.load_file(os.path.join(glbl.usrAnalysis_dir, 'Ni.D'))
        calibrant_name = 'Ni'
    # wavelength
    if wavelength is None:
        _wavelength = bto['bt_wavelength']
    else:
        _wavelength = wavelength
    calibrant.wavelength = _wavelength * 10 ** (-10)
    # detector
    if detector is None:
        detector = Perkin()
    # calibration
    timestr = _timestampstr(time.time())
    basename = '_'.join(['pyFAI_calib', calibrant_name, timestr])
    w_name = os.path.join(glbl.config_base, basename)  # poni name
    c = Calibration(wavelength=calibrant.wavelength,
                    detector=detector,
                    calibrant=calibrant,
                    gaussianWidth=gaussian)
    c.gui = interactive
    c.basename = w_name
    c.pointfile = w_name + ".npt"
    c.ai = AzimuthalIntegrator(dist=dist, detector=detector,
                               wavelength=calibrant.wavelength)
    c.peakPicker = PeakPicker(img, reconst=True, mask=detector.mask,
                              pointfile=c.pointfile, calibrant=calibrant,
                              wavelength=calibrant.wavelength)
    # method=method)
    if gaussian is not None:
        c.peakPicker.massif.setValleySize(gaussian)
    else:
        c.peakPicker.massif.initValleySize()

    if interactive:
        c.peakPicker.gui(log=True, maximize=True, pick=True)
        update_fig(c.peakPicker.fig)
    c.gui_peakPicker()
    c.ai.setPyFAI(**c.geoRef.getPyFAI())
    c.ai.wavelength = c.geoRef.wavelength

    return c.ai
Exemplo n.º 16
0
 def set_calibrant(self, filename):
     self.calibrant = Calibrant()
     self.calibrant.load_file(filename)
     self.pattern_geometry.calibrant = self.calibrant
Exemplo n.º 17
0
class test_peak_picking(unittest.TestCase):
    """basic test"""
    calibFile = "1788/moke.tif"
#    gr = GeometryRefinement()


    ctrlPt = {0:(300, 230),
              1:(300, 212),
              2:(300, 195),
              3:(300, 177),
              4:(300, 159),
              5:(300, 140),
              6:(300, 123),
              7:(300, 105),
              8:(300, 87)}
    tth = numpy.radians(numpy.arange(4, 13))
    wavelength = 1e-10
    ds = wavelength * 5e9 / numpy.sin(tth / 2)
    calibrant = Calibrant(dSpacing=ds)
    maxiter = 100
    tmp_dir = os.environ.get("PYFAI_TEMPDIR", os.path.join(os.path.dirname(os.path.abspath(__file__)), "tmp"))
    logfile = os.path.join(tmp_dir, "testpeakPicking.log")
    nptfile = os.path.join(tmp_dir, "testpeakPicking.npt")
    def setUp(self):
        """Download files"""
        self.img = UtilsTest.getimage(self.__class__.calibFile)
        self.pp = PeakPicker(self.img, calibrant=self.calibrant, wavelength=self.wavelength)
        if not os.path.isdir(self.tmp_dir):
            os.makedirs(self.tmp_dir)
        if os.path.isfile(self.logfile):
            os.unlink(self.logfile)
        if os.path.isfile(self.nptfile):
            os.unlink(self.nptfile)
    def tearDown(self):
        """Remove temporary files"""
        unittest.TestCase.tearDown(self)
        if os.path.isfile(self.logfile):
            os.unlink(self.logfile)
        if os.path.isfile(self.nptfile):
            os.unlink(self.nptfile)

    def test_peakPicking(self):
        """first test peak-picking then checks the geometry found is OK"""
        for i in self.ctrlPt:
            pts = self.pp.massif.find_peaks(self.ctrlPt[i], stdout=open(self.logfile, "a"))
            logger.info("point %s at ring #%i (tth=%.1f deg) generated %i points", self.ctrlPt[i], i, self.tth[i], len(pts))
            if len(pts) > 0:
                self.pp.points.append(pts, angle=self.tth[i], ring=i)
            else:
                logger.error("point %s caused error (%s) ", i, self.ctrlPt[i])

        self.pp.points.save(self.nptfile)
        lstPeak = self.pp.points.getListRing()
#        print self.pp.points
#        print lstPeak
        logger.info("After peak-picking, we have %s points generated from %s groups ", len(lstPeak), len(self.ctrlPt))
        gr = GeometryRefinement(lstPeak, dist=0.01, pixel1=1e-4, pixel2=1e-4, wavelength=self.wavelength, calibrant=self.calibrant)
        gr.guess_poni()
        logger.info(gr.__repr__())
        last = sys.maxint
        for i in range(self.maxiter):
            delta2 = gr.refine2()
            logger.info(gr.__repr__())
            if delta2 == last:
                logger.info("refinement finished after %s iteration" % i)
                break
            last = delta2
        self.assertEquals(last < 1e-4, True, "residual error is less than 1e-4, got %s" % last)
        self.assertAlmostEquals(gr.dist, 0.1, 2, "distance is OK, got %s, expected 0.1" % gr.dist)
        self.assertAlmostEquals(gr.poni1, 3e-2, 2, "PONI1 is OK, got %s, expected 3e-2" % gr.poni1)
        self.assertAlmostEquals(gr.poni2, 3e-2, 2, "PONI2 is OK, got %s, expected 3e-2" % gr.poni2)
        self.assertAlmostEquals(gr.rot1, 0, 2, "rot1 is OK, got %s, expected 0" % gr.rot1)
        self.assertAlmostEquals(gr.rot2, 0, 2, "rot2 is OK, got %s, expected 0" % gr.rot2)
        self.assertAlmostEquals(gr.rot3, 0, 2, "rot3 is OK, got %s, expected 0" % gr.rot3)
Exemplo n.º 18
0
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
Exemplo n.º 19
0
def run_calibration(exposure=60, calibrant_file=None, wavelength=None,
                    detector=None, gaussian=None):
    """ function to run entire calibration process.

    Entire process includes: collect calibration image, trigger pyFAI 
    calibration process, store calibration parameters as a yaml file 
    under xpdUser/config_base/ and inject uid of calibration image to
    following scans, until this function is run again.

    Parameters
    ----------
    exposure : int, optional
        total exposure time in sec. Default is 60s
    calibrant_name : str, optional
        name of calibrant used, different calibrants correspond to 
        different d-spacing profiles. Default is 'Ni'. User can assign 
        different calibrant, given d-spacing file path presents
    wavelength : flot, optional
        current of x-ray wavelength, in angstrom. Default value is 
        read out from existing xpdacq.Beamtime object
    detector : pyfai.detector.Detector, optional.
        instance of detector which defines pxiel size in x- and
        y-direction. Default is set to Perkin Elmer detector
    gaussian : int, optional
        gaussian width between rings, Default is 100.
    """
    # default params
    interactive = True
    dist = 0.1

    _check_obj(_REQUIRED_OBJ_LIST)
    ips = get_ipython()
    bto = ips.ns_table['user_global']['bt']
    prun = ips.ns_table['user_global']['prun']
    # print('*** current beamtime info = {} ***'.format(bto.md))
    calibrant = Calibrant()
    # d-spacing
    if calibrant_file is not None:
        calibrant.load_file(calibrant_file)
        calibrant_name = os.path.split(calibrant_file)[1]
        calibrant_name = os.path.splitext(calibrant_name)[0]
    else:
        calibrant.load_file(os.path.join(glbl.usrAnalysis_dir, 'Ni24.D'))
        calibrant_name = 'Ni'
    # wavelength
    if wavelength is None:
        _wavelength = bto['bt_wavelength']
    else:
        _wavelength = wavelength
    calibrant.wavelength = _wavelength * 10 ** (-10)
    # detector
    if detector is None:
        detector = Perkin()
    # scan
    # simplified version of Sample object
    calib_collection_uid = str(uuid.uuid4())
    calibration_dict = {'sample_name':calibrant_name,
                        'sample_composition':{calibrant_name :1},
                        'is_calibration': True,
                        'calibration_collection_uid': calib_collection_uid}
    prun_uid = prun(calibration_dict, ScanPlan(bto, ct, exposure))
    light_header = glbl.db[prun_uid[-1]]  # last one is always light
    dark_uid = light_header.start['sc_dk_field_uid']
    dark_header = glbl.db[dark_uid]
    # unknown signature of get_images
    dark_img = np.asarray(
        get_images(dark_header, glbl.det_image_field)).squeeze()
    # dark_img = np.asarray(glbl.get_images(dark_header, glbl.det_image_field)).squeeze()
    for ev in glbl.get_events(light_header, fill=True):
        img = ev['data'][glbl.det_image_field]
        img -= dark_img
    # calibration
    timestr = _timestampstr(time.time())
    basename = '_'.join(['pyFAI_calib', calibrant_name, timestr])
    w_name = os.path.join(glbl.config_base, basename)  # poni name
    c = Calibration(wavelength=calibrant.wavelength,
                    detector=detector,
                    calibrant=calibrant,
                    gaussianWidth=gaussian)
    c.gui = interactive
    c.basename = w_name
    c.pointfile = w_name + ".npt"
    c.ai = AzimuthalIntegrator(dist=dist, detector=detector,
                               wavelength=calibrant.wavelength)
    c.peakPicker = PeakPicker(img, reconst=True, mask=detector.mask,
                              pointfile=c.pointfile, calibrant=calibrant,
                              wavelength=calibrant.wavelength)
    # method=method)
    if gaussian is not None:
        c.peakPicker.massif.setValleySize(gaussian)
    else:
        c.peakPicker.massif.initValleySize()

    if interactive:
        c.peakPicker.gui(log=True, maximize=True, pick=True)
        update_fig(c.peakPicker.fig)
    c.gui_peakPicker()
    c.ai.setPyFAI(**c.geoRef.getPyFAI())
    c.ai.wavelength = c.geoRef.wavelength
    # update until next time
    glbl.calib_config_dict = c.ai.getPyFAI()
    Fit2D_dict = c.ai.getFit2D()
    glbl.calib_config_dict.update(Fit2D_dict)
    glbl.calib_config_dict.update({'file_name':basename})
    glbl.calib_config_dict.update({'time':timestr})
    # FIXME: need a solution for selecting desired calibration image
    # based on calibration_collection_uid later
    glbl.calib_config_dict.update({'calibration_collection_uid':
                                   calib_collection_uid})
    # write yaml
    yaml_name = glbl.calib_config_name
    with open(os.path.join(glbl.config_base, yaml_name), 'w') as f:
        yaml.dump(glbl.calib_config_dict, f)

    return c.ai
Exemplo n.º 20
0
class CalibrationData(object):
    def __init__(self, img_data=None):
        self.img_data = img_data
        self.points = []
        self.points_index = []
        self.geometry = AzimuthalIntegrator()
        self.geometry.set_wavelength(0.3344e-10)
        self.calibrant = Calibrant()
        self.start_values = {
            'dist': 200e-3,
            'wavelength': 0.3344e-10,
            'pixel_width': 79e-6,
            'pixel_height': 79e-6,
            'polarization_factor': 0.95
        }
        self.fit_wavelength = False
        self.is_calibrated = False
        self.use_mask = False
        self.calibration_name = 'None'
        self.polarization_factor = 0.95
        self._calibrants_working_dir = 'ExampleData/Calibrants'

    def find_peaks_automatic(self, x, y, peak_ind):
        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):
        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 search_peaks_on_ring(self,
                             peak_index,
                             delta_tth=0.1,
                             algorithm='Massif',
                             min_mean_factor=1,
                             upper_limit=55000):
        if not self.is_calibrated:
            return

        #transform delta from degree into radians
        delta_tth = delta_tth / 180.0 * np.pi

        # get appropiate two theta value for the ring number
        tth_calibrant_list = self.calibrant.get_2th()
        tth_calibrant = np.float(tth_calibrant_list[peak_index])
        print tth_calibrant

        # get the calculated two theta values for the whole image
        if self.geometry._ttha is None:
            tth_array = self.geometry.twoThetaArray(
                self.img_data.img_data.shape)
        else:
            tth_array = self.geometry._ttha

        # create mask based on two_theta position
        mask = abs(tth_array - tth_calibrant) <= delta_tth

        # init the peak search algorithm
        if algorithm == 'Massif':
            peak_search_algorithm = Massif(self.img_data.img_data)
        elif algorithm == 'Blob':
            peak_search_algorithm = BlobDetection(self.img_data.img_data *
                                                  mask)
            peak_search_algorithm.process()
        else:
            return

        # 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 = 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)

    def set_calibrant(self, filename):
        self.calibrant = Calibrant()
        self.calibrant.load_file(filename)
        self.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.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.refine()
        self.integrate()
        self.is_calibrated = True
        self.calibration_name = 'current'

    def refine(self):
        self.geometry.data = self.create_point_array(self.points,
                                                     self.points_index)
        self.geometry.refine2()
        if self.fit_wavelength:
            self.geometry.refine2_wavelength(fix=[])

    def integrate(self):
        self.integrate_1d()
        self.integrate_2d()

    def integrate_1d(self,
                     num_points=1400,
                     mask=None,
                     polarization_factor=None,
                     filename=None,
                     unit='2th_deg'):
        if polarization_factor is None:
            polarization_factor = self.polarization_factor
        if unit is 'd_A':
            self.tth, self.int = self.geometry.integrate1d(
                self.img_data.img_data,
                num_points,
                method='lut',
                unit='2th_deg',
                mask=mask,
                polarization_factor=polarization_factor,
                filename=filename)
            ind = np.where(self.tth > 0)
            self.tth = self.geometry.wavelength / (
                2 * np.sin(self.tth[ind] / 360 * np.pi)) * 1e10
            self.int = self.int[ind]
        else:
            self.tth, self.int = self.geometry.integrate1d(
                self.img_data.img_data,
                num_points,
                method='lut',
                unit=unit,
                mask=mask,
                polarization_factor=polarization_factor,
                filename=filename)
        return self.tth, self.int

    def integrate_2d(self,
                     mask=None,
                     polarization_factor=None,
                     unit='2th_deg'):
        if polarization_factor is None:
            polarization_factor = self.polarization_factor
        res = self.geometry.integrate2d(
            self.img_data.img_data,
            2024,
            2024,
            method='lut',
            mask=mask,
            unit=unit,
            polarization_factor=polarization_factor)
        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.geometry.getPyFAI()
        pyFAI_parameter['polarization_factor'] = self.polarization_factor
        try:
            fit2d_parameter = self.geometry.getFit2D()
            fit2d_parameter['polarization_factor'] = self.polarization_factor
        except TypeError:
            fit2d_parameter = None
        try:
            pyFAI_parameter['wavelength'] = self.geometry.wavelength
            fit2d_parameter['wavelength'] = self.geometry.wavelength
        except RuntimeWarning:
            pyFAI_parameter['wavelength'] = 0

        return pyFAI_parameter, fit2d_parameter

    def load(self, filename):
        self.geometry = GeometryRefinement(
            np.zeros((2, 3)),
            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.geometry.load(filename)
        self.calibration_name = get_base_name(filename)
        self.is_calibrated = True

    def save(self, filename):
        self.geometry.save(filename)
        self.calibration_name = get_base_name(filename)