def main(): c = Calibration() c.parse() c.read_pixelsSize() c.preprocess() c.gui_peakPicker() six.moves.input("Press enter to quit")
def _collect_img(exposure, dark_sub_bool, sample_md, tag, RE_instance, *, calibrant=None, detector=None): """helper function to collect image and return it""" # grab beamtime object linked to run_engine bto = RE_instance.beamtime plan = ScanPlan(bto, ct, exposure).factory() sample_md.update(bto) if tag == "calib": # instantiate Calibrant class calibrant_obj = Calibration(calibrant=calibrant).calibrant if calibrant_obj is None: raise xpdAcqException("Invalid calibrant") dSpacing = calibrant_obj.dSpacing if not dSpacing: raise xpdAcqException("empty dSpacing from calibrant") sample_md.update({"dSpacing": dSpacing, "detector": detector}) plan = bpp.msg_mutator(plan, _inject_calibration_tag) # collect image uid = RE_instance(sample_md, plan) # last one must be light db = xpd_configuration["db"] light_header = db[uid[-1]] dark_uid = light_header["start"].get("sc_dk_field_uid") dark_header = db[dark_uid] dark_img = dark_header.data(glbl["image_field"]) dark_img = np.asarray(next(dark_img)).squeeze() img = light_header.data(glbl["image_field"]) img = np.asarray(next(img)).squeeze() if dark_sub_bool: img -= dark_img # FIXME: filename template from xpdAn fn_template = "from_calib_func_{}.poni".format(_timestampstr(time.time())) return img, fn_template
def test_load_calibrant(fresh_xrun, bt): xrun = fresh_xrun xrun.beamtime = bt # pyfai factory for k, calibrant_obj in CALIBRANT_FACTORY.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) # clean xrun.unsubscribe(t) # invalid calibrant with pytest.raises(xpdAcqException): run_calibration(calibrant="pyFAI", phase_info="buggy", RE_instance=xrun) # 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 = Calibration(calibrant=dst) def check_eq(name, doc): assert c.calibrant.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) # clean xrun.unsubscribe(t)
def img_calibration(img, wavelength, calibrant="Ni", detector="perkin_elmer", calib_ref_fp=None, **kwargs): """Function to calibrate experimental geometry for an image Parameters ---------- img : ndarray 2D powder diffraction image from calibrant wavelength : float x-ray wavelength in angstrom. calibrant : str, optional calibrant being used, default is 'Ni'. input could be "full file path" to customized d-spacing file with ".D" extension or one of pre-defined calibrant names. List of pre-defined calibrant names: ['NaCl', 'AgBh', 'quartz', 'Si_SRM640', 'Ni', 'Si_SRM640d', 'Si_SRM640a', 'alpha_Al2O3', 'LaB6_SRM660b', 'TiO2', 'CrOx', 'LaB6_SRM660c', 'CeO2', 'Si_SRM640c', 'CuO', 'Si_SRM640e', 'PBBA', 'ZnO', 'Si', 'C14H30O', 'cristobaltite', 'LaB6_SRM660a', 'Au', 'Cr2O3', 'Si_SRM640b', 'LaB6', 'Al', 'mock'] detector : str or pyFAI.detector.Detector instance, optional. detector used to collect data. default value is 'perkin-elmer'. other allowed values are in pyFAI documentation. calib_ref_fp : str, optional full file path to where the native pyFAI calibration information will be saved. Default to current working directory. kwargs: Additional keyword argument for calibration. please refer to pyFAI documentation for all options. Returns ------- ai : pyFAI.AzimuthalIntegrator instance of AzimuthalIntegrator. Can be used to integrate 2D images directly. Examples -------- calib Ni image with pyFAI default ``Ni.D`` d-spacing with wavlength 0.1823 angstrom >>> import tifffile as tif >>> ni_img = tif.imread('<path_to_img_file>') >>> ai = img_calibration(ni_img, 0.1823) calib Ni image with pyFAI customized ``myNi.D`` d-spacing with wavlength 0.1823 angstrom >>> import tifffile as tif >>> ni_img = tif.imread('<path_to_img_file>') >>> ai = img_calibration(ni_img, 0.1823, 'path/to/myNi.D') integrate image right after calibration >>> import matplotlib.pyplot as plt >>> npt = 1482 # just a number for demonstration >>> q, Iq = ai.integrate1d(ni_img, npt, unit="q_nm^-1") >>> plt.plot(q, Iq) References --------- pyFAI documentation: http://pyfai.readthedocs.io/en/latest/ """ from pyFAI.calibration import Calibration, Calibrant wavelength *= 10**-10 if isinstance(calibrant, list): calibrant = Calibrant(dSpacing=calibrant, wavelength=wavelength) # configure calibration instance c = Calibration(calibrant=calibrant, detector=detector, wavelength=wavelength) # pyFAI calibration calib_c, timestr = _calibration(img, c, calib_ref_fp, **kwargs) # TODO: apply polarization correction and recalibrate? return calib_c, calib_c.ai
def run_calibration(exposure=5, dark_sub_bool=True, calibrant=None, phase_info=None, wavelength=None, detector=None, *, RE_instance=None, parallel=True, **kwargs): """function to run entire calibration process. Entire process includes: 1. collect calibration image, 2. trigger pyFAI interactive calibration process, 3. store calibration parameters as a yaml file calibration parameters will be saved under xpdUser/config_base/ and this set of parameters will be injected as metadata to subsequent scans until you perform this process again Parameters ---------- exposure : int, optional total exposure time in sec. default is 5s dark_sub_bool : bool, optional option of turn on/off dark subtraction on this calibration image. default is True. calibrant : str, optional calibrant being used, default is 'Ni'. input could be full file path to customized d-spacing file with ".D" extension or one of pre-defined calibrant names. List of pre-defined calibrant names is: ['NaCl', 'AgBh', 'quartz', 'Si_SRM640', 'Ni', 'Si_SRM640d', 'Si_SRM640a', 'alpha_Al2O3', 'LaB6_SRM660b', 'TiO2', 'CrOx', 'LaB6_SRM660c', 'CeO2', 'Si_SRM640c', 'CuO', 'Si_SRM640e', 'PBBA', 'ZnO', 'Si', 'C14H30O', 'cristobaltite', 'LaB6_SRM660a', 'Au', 'Cr2O3', 'Si_SRM640b', 'LaB6', 'Al', 'mock'] phase_info : str, optional phase infomation of calibrant, which is required to data reduction process. This field will be parsed with the same logic as the one used in parsing spreadsheet information. For detailed information, please visit: http://xpdacq.github.io/usb_Running.html#phase-string If both ``calibrant`` and ``phase_info`` arguments are not provided, this field will be defaulted to ``Ni``. wavelength : float, optional x-ray wavelength in angstrom. default to value stored in existing Beamtime object detector : str or pyFAI.detector.Detector instance, optional. detector used to collect data. default value is 'perkin-elmer'. other allowed values are in pyFAI documentation. RE_instance : bluesky.run_engine.RunEngine instance, optional instance of run engine. Default is xrun. Do not change under normal circumstances. parallel : bool, optional Tag for whether run the calibration step in a separte process. Running in parallel in principle yields better resource allocation. Default is ``True``, only change to ``False`` if error is raised. kwargs: Additional keyword argument for calibration. please refer to pyFAI documentation for all options. Note ---- Details about peak-picking gui from pyFAI documentaion http://pyfai.readthedocs.io/en/latest/usage/cookbook/calibrate.html#start-pyfai-calibration_md """ # default information if detector is None: detector = "perkin_elmer" sample_md = _sample_name_phase_info_configuration(calibrant, phase_info, "calib") if calibrant is None: calibrant = os.path.join(glbl["usrAnalysis_dir"], "Ni24.D") # collect & pull subtracted image if RE_instance is None: xrun_name = _REQUIRED_OBJ_LIST[0] RE_instance = _check_obj(xrun_name) # will raise error if not exists img, fn_template = _collect_img( exposure, dark_sub_bool, sample_md, "calib", RE_instance, detector=detector, calibrant=calibrant, ) print("INFO: Please navigate to the analysis terminal to complete " "the interactive calibration process.\nYou may find the " "the analysis terminal similar to data acquisition terminal" "(current terminal) except there is information about the " "analysis pipeline printed") print("INFO: For a quick guide on the interactive calibration " "process, please visit our web-doc at:\n" "https://xpdacq.github.io/xpdAcq/usb_Running.html#calib-manual\n") if not parallel: # backup when pipeline fails # get wavelength from bt if wavelength is None: bt_fp = os.path.join(glbl["yaml_dir"], "bt_bt.yml") if not os.path.isfile(bt_fp): raise FileNotFoundError("Can't find your Beamtime yaml file.\n" "Did you accidentally delete it? " "Please contact beamline staff " "ASAP") bto = Beamtime.from_yaml(open(bt_fp)) wavelength = float(bto.wavelength) * 10**(-10) # configure calibration instance c = Calibration(calibrant=calibrant, detector=detector, wavelength=wavelength) # pyFAI calibration calib_c, timestr = _calibration(img, c, fn_template, **kwargs) assert calib_c.calibrant.wavelength == wavelength # save param for xpdAcq yaml_name = glbl["calib_config_name"] calib_yml_fp = os.path.join(glbl["config_base"], glbl["calib_config_name"]) _save_calib_param(calib_c, timestr, calib_yml_fp)
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