from __future__ import print_function,division import logging log = logging.getLogger(__name__) import numpy as np np.seterr(all='ignore') import os import collections import glob import pathlib from . import storage from . import utils import fabio import pyFAI try: import matplotlib.pyplot as plt except ImportError: log.warn("Can't import matplotlib !") def _read(fname): """ read data from file using fabio """ f = fabio.open(fname) data = f.data del f; # close file return data def read(fnames): """ read data from file(s) using fabio """ if isinstance(fnames,str): data = _read(fnames) else: # read one image to know img size temp = _read(fnames[0]) shape = [len(fnames),]+list(temp.shape) data = np.empty(shape) data[0] = temp for i in range(1,len(fnames)): data[i] = _read(fnames[i]) return data def ai_as_dict(ai): """ ai is a pyFAI azimuthal intagrator""" methods = dir(ai) methods = [m for m in methods if m.find("get_") == 0] names = [m[4:] for m in methods] values = [getattr(ai,m)() for m in methods] ret = dict( zip(names,values) ) ret["detector"] = ai.detector.get_name() return ret def ai_as_str(ai): """ ai is a pyFAI azimuthal intagrator""" return "#" + str(ai).replace("\n","\n#") def do1d(ai, imgs, mask = None, npt_radial = 600, method = 'csr',safe=True,dark=10., polCorr = 1): """ ai is a pyFAI azimuthal intagrator it can be defined with pyFAI.load(ponifile) mask: True are points to be masked out """ # force float to be sure of type casting for img if isinstance(dark,int): dark = float(dark); if imgs.ndim == 2: imgs = (imgs,) out_i = np.empty( ( len(imgs), npt_radial) ) out_s = np.empty( ( len(imgs), npt_radial) ) for _i,img in enumerate(imgs): q,i, sig = ai.integrate1d(img-dark, npt_radial, mask= mask, safe = safe,\ unit="q_A^-1", method = method, error_model = "poisson", polarization_factor = polCorr) out_i[_i] = i out_s[_i] = sig return q,np.squeeze(out_i),np.squeeze(out_s) def do2d(ai, imgs, mask = None, npt_radial = 600, npt_azim=360,method = 'csr',safe=True,dark=10., polCorr = 1): """ ai is a pyFAI azimuthal intagrator it can be defined with pyFAI.load(ponifile) mask: True are points to be masked out """ # force float to be sure of type casting for img if isinstance(dark,int): dark = float(dark); if imgs.ndim == 2: imgs = (imgs,) out = np.empty( ( len(imgs), npt_azim,npt_radial) ) for _i,img in enumerate(imgs): i2d,q,azTheta = ai.integrate2d(img-dark, npt_radial, npt_azim=npt_azim, mask= mask, safe = safe,unit="q_A^-1", method = method, polarization_factor = polCorr ) out[_i] = i2d return q,azTheta,np.squeeze(out) def getAI(poni,folder=None): """ get AzimuthalIntegrator instance: → if poni is a dictionary use it to define one → if poni is a string look, it is used as filename to read. in this case if folder is given it is used (together with all its subfolder) as search path (along with ./ and home folder) """ if isinstance(poni,pyFAI.azimuthalIntegrator.AzimuthalIntegrator): ai = poni elif isinstance(poni,dict): ai = pyFAI.azimuthalIntegrator.AzimuthalIntegrator(**poni) elif isinstance(poni,str): # look is file exists in cwd if os.path.exists(poni): ai = pyFAI.load(poni) # if file does not exist look for one with that name around else: # build search paths folders = [] if folder is not None: temp = os.path.abspath(folder) path = pathlib.Path(temp) folders = [ str(path), ] for p in path.parents: folders.append(str(p)) folders.append( "./" ) folders.append( os.path.expanduser("~/") ) # look for file for path in folders: fname = path + "/" + poni if os.path.exists(fname): log.info("Found poni file %s",fname) break else: log.debug("Could not poni file %s",fname) ai = pyFAI.load(fname) return ai def doFolder(folder,files='*.edf*',nQ = 1500,force=False,mask=None, saveChi=True,poni='pyfai.poni',storageFile='auto',diagnostic=None): """ calc 1D curves from files in folder, returning a dictionary of stuff nQ : number of Q-points (equispaced) force : if True, redo from beginning even if previous data are found if False, do only new files mask : can be a filename or an array of booleans; pixels that are True are dis-regarded saveChi: self-explanatory poni : could be: → an AzimuthalIntegrator instance → a filename that will be look for in 1 'folder' first 2 in ../folder 3 in ../../folder .... n-1 in pwd n in homefolder → a dictionary (use to bootstrap an AzimuthalIntegrator using AzimuthalIntegrator(**poni) """ if storageFile == 'auto': storageFile = folder + "/" + "pyfai_1d.h5" if os.path.exists(storageFile) and not force: saved = storage.DataStorage(storageFile) else: saved = None # which poni file to use: ai = getAI(poni,folder) files = utils.getFiles(folder,files) if saved is not None: files = [f for f in files if utils.getBasename(f) not in saved["files"]] if len(files) > 0: # work out mask to use if isinstance(mask,np.ndarray): mask = mask.astype(bool) elif mask is not None: mask = read(mask).astype(bool) data = np.empty( (len(files),nQ) ) err = np.empty( (len(files),nQ) ) for ifname,fname in enumerate(files): img = read(fname) q,i,e = do1d(ai,img,mask=mask,npt_radial=nQ) data[ifname] = i err[ifname] = e if saveChi: chi_fname = utils.removeExt(fname) + ".chi" utils.saveTxt(chi_fname,q,i,e,info=ai_as_str(ai),overwrite=True) files = [ utils.getBasename(f) for f in files ] files = np.asarray(files) if saved is not None: files = np.concatenate( (saved["files"] ,files ) ) data = np.concatenate( (saved["data"] ,data ) ) err = np.concatenate( (saved["err"] ,err ) ) ret = dict(q=q,folder=folder,files=files,data=data,err=err, pyfai=ai_as_dict(ai),pyfai_info=ai_as_str(ai),mask=mask) # add info from diagnostic if provided if diagnostic is not None: for k in diagnostic: ret[k] = np.asarray( [diagnostic[k][f] for f in ret['files']] ) ret = storage.DataStorage(ret) if storageFile is not None: ret.save(storageFile) else: ret = saved return ret def _calc_R(x,y, xc, yc): """ calculate the distance of each 2D points from the center (xc, yc) """ return np.sqrt((x-xc)**2 + (y-yc)**2) def _chi2(c, x, y): """ calculate the algebraic distance between the data points and the mean circle centered at c=(xc, yc) """ Ri = _calc_R(x, y, *c) return Ri - Ri.mean() def leastsq_circle(x,y): from scipy import optimize # coordinates of the barycenter center_estimate = np.nanmean(x), np.nanmean(y) center, ier = optimize.leastsq(_chi2, center_estimate, args=(x,y)) xc, yc = center Ri = _calc_R(x, y, *center) R = Ri.mean() residu = np.sum((Ri - R)**2) return xc, yc, R def find_center(img,psize=100e-6,dist=0.1,wavelength=0.8e-10,**kwargs): plt.ion() kw = dict( pixel1 = psize, pixel2 = psize, dist = dist,wavelength=wavelength ) kw.update(kwargs) ai = pyFAI.azimuthalIntegrator.AzimuthalIntegrator(**kw) fig_img,ax_img = plt.subplots(1,1) fig_pyfai,ax_pyfai = plt.subplots(1,1) fig_pyfai = plt.figure(2) temp= ax_img.imshow(img) plt.sca(ax_img); # set figure to use for mouse interaction temp.set_clim( *np.percentile(img,(2,95) ) ) ans = "" print("Enter 'end' when done") while ans != "end": if ans == "": print("Click on beam center:") plt.sca(ax_img); # set figure to use for mouse interaction xc,yc = plt.ginput()[0] else: xc,yc = map(float,ans.split(",")) print("Selected center:",xc,yc) ai.set_poni1(xc*psize) ai.set_poni2(yc*psize) q,az,i = do2d(ai,img) mesh = ax_pyfai.pcolormesh(q,az,i) mesh.set_clim( *np.percentile(i,(2,95) ) ) ax_pyfai.set_title(str( (xc,yc) )) plt.pause(0.01) plt.draw() ans=input("Enter to continue with clinking or enter xc,yc values ") print("Final values: (in pixels) %.3f %.3f"%(xc,yc)) return ai def average(fileOrFolder,delays=slice(None),scale=1,norm=None,returnAll=False,plot=False, showTrend=False): data = storage.DataStorage(fileOrFolder) if isinstance(delays,slice): idx = np.arange(data.delays.shape[0])[delays] elif isinstance(delays,(int,float)): idx = data.delays == float(delays) else: idx = data.delays < 0 if idx.sum() == 0: print("No data with the current filter") return None i = data.data[idx] q = data.q if isinstance(norm,(tuple,list)): idx = ( q>norm[0] ) & (qnorm[0] ) & (q