from __future__ import print_function,division import logging as log log.basicConfig(level=log.INFO) import numpy as np np.seterr(all='ignore') import os import collections import glob import pathlib from . import storage as storage import pyFAI try: import matplotlib.pyplot as plt except ImportError: log.warn("Can't import matplotlib !") def removeExt(fname): """ special remove extension meant to work with compressed files.edf and .edf.gz files """ if fname[-3:] == ".gz": fname = fname[-3:] return os.path.splitext(fname)[0] def getBasename(fname): return removeExt(os.path.basename(fname)) def pyFAIread(fname): """ read data from file using fabio """ import fabio f = fabio.open(fname) data = f.data del f return data def pyFAI_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 pyFAI1d(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 pyFAI2d(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 pyFAI_saveChi(fname,q,i,e=None,ai=None,overwrite=False): if os.path.exists(fname) and not overwrite: log.warn("File %s exists, returning",fname) return if ai is not None: if not isinstance(ai,dict): ai = pyFAI_dict(ai) header = [ "# %s : %s" %(k,v) for (k,v) in zip(ai.keys(),ai.values()) ] header = "\n".join(header)[1:]; # skip first #, will be added by np else: header = "" x = np.stack( (q,i,e) ) if e is not None else np.stack( (q,i) ) np.savetxt(fname,x.T,fmt="%+10.5e",header=header) class pyFAI_storage(dict): """ Storage for pyfai integrated info """ def __init__(self,fileOrDict): if isinstance(fileOrDict,dict): self.filename = None d = fileOrDict else: assert isinstance(fileOrDict,str) self.filename = fileOrDict d = storage.read(fileOrDict) # allow accessing with .data, .delays, etc. for k,v in d.items(): setattr(self,k,v) # allow accessing as proper dict self.update( **dict(d) ) def __setitem__(self, key, value): setattr(self,key,value) super().__setitem__(key, value) def __delitem__(self, key): delattr(self,key) super().__delitem__(key) def save(self,fname=None): if fname is None: fname = self.filename assert fname is not None storage.save(fname,dict(self)) #def asdict(self): return dict(self) def readNpzFile(h5File): if os.path.isdir(h5File): h5File = "%s/pyfai_1d.h5" % h5File return pyFAI_storage(h5File) def _getAI(poni,folder): if isinstance(poni,pyFAI.azimuthalIntegrator.AzimuthalIntegrator): ai = poni elif isinstance(poni,dict): ai = pyFAI.azimuthalIntegrator.AzimuthalIntegrator(**poni) else: if poni == 'auto': 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("~/") ) for path in folders: poni = path + "/" + "pyfai.poni" if os.path.exists(poni): log.info("Found pyfai.poni in %s",path) break else: log.debug("Could not find pyfai.poni in %s",path) ai = pyFAI.load(poni) return ai def doFolder(folder,files='*.edf*',nQ = 1500,force=False,mask=None, saveChi=True,poni='auto',h5File='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 → a dictionary (use to bootstrap an AzimuthalIntegrator using AzimuthalIntegrator(**poni) → the string 'auto' that will look for the file 'pyfai.poni' in: 1 'folder' first 2 in ../folder 3 in ../../folder .... n-1 in pwd n in homefolder """ if h5File == 'auto': h5File = folder + "/" + "pyfai_1d.h5" if os.path.exists(h5File) and not force: print("Loading") saved = readNpzFile(h5File) print("done") else: saved = None # which poni file to use: ai = _getAI(poni,folder) files = glob.glob("%s/%s"%(folder,files)) files.sort() if saved is not None: files = [f for f in files if 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 = pyFAIread(mask).astype(bool) data = np.empty( (len(files),nQ),dtype=np.float32 ) err = np.empty( (len(files),nQ),dtype=np.float32 ) for ifname,fname in enumerate(files): img = pyFAIread(fname) q,i,e = pyFAI1d(ai,img,mask=mask,npt_radial=nQ) data[ifname] = i err[ifname] = e if saveChi: chi_fname = removeExt(fname) + ".chi" pyFAI_saveChi(chi_fname,q,i,e,ai=ai,overwrite=True) files = [ 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=pyFAI_dict(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']] ) if h5File is not None: np.savez(h5File,**ret) else: ret = saved return pyFAI_storage(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 pyFAI_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) ax_img.imshow(img) plt.sca(ax_img); # set figure to use for mouse interaction 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 = pyFAI2d(ai,img) ax_pyfai.pcolormesh(q,az,i) 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 #### Utilities for chi files #### def chiRead(fname,scale=1): q,i = np.loadtxt(fname,unpack=True,usecols=(0,1)) return q,i*scale def chiPlot(fname,useTheta=False,E=12.4): q,i = chiRead(fname) lam = 12.4/E theta = 2*180/3.14*np.arcsin(q*lam/4/3.14) if useTheta: x = theta else: x = q plt.plot(x,i,label=fname) def chiAverage(folder,basename="",scale=1,returnAll=False,plot=False,showTrend=False,norm=None): files = glob.glob("%s/%s*chi"%(folder,basename)) files.sort() print(files) if len(files) == 0: print("No file found (basename %s)" % basename) return None q,_ = chiRead(files[0]) i = np.asarray( [ chiRead(f)[1] for f in files ] ) if norm is not None: idx = ( q>norm[0] ) & (q