source: trunk/GSASIIimage.py @ 2824

Last change on this file since 2824 was 2824, checked in by vondreele, 6 years ago

fix 2D strain d-zero calc
set Refine dlg=None default for easier scripting use
fix to put (?) space in BANK n

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 48.4 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII image calculations: ellipse fitting & image integration       
3########### SVN repository information ###################
4# $Date: 2017-05-10 14:14:10 +0000 (Wed, 10 May 2017) $
5# $Author: vondreele $
6# $Revision: 2824 $
7# $URL: trunk/GSASIIimage.py $
8# $Id: GSASIIimage.py 2824 2017-05-10 14:14:10Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimage: Image calc module*
12================================
13
14Ellipse fitting & image integration
15
16'''
17
18import math
19import time
20import numpy as np
21import numpy.linalg as nl
22import numpy.ma as ma
23import polymask as pm
24from scipy.optimize import leastsq
25import scipy.signal as scsg
26import scipy.cluster.vq as scvq
27import copy
28import GSASIIpath
29GSASIIpath.SetVersionNumber("$Revision: 2824 $")
30import GSASIIplot as G2plt
31import GSASIIlattice as G2lat
32import GSASIIpwd as G2pwd
33import GSASIIspc as G2spc
34
35# trig functions in degrees
36sind = lambda x: math.sin(x*math.pi/180.)
37asind = lambda x: 180.*math.asin(x)/math.pi
38tand = lambda x: math.tan(x*math.pi/180.)
39atand = lambda x: 180.*math.atan(x)/math.pi
40atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi
41cosd = lambda x: math.cos(x*math.pi/180.)
42acosd = lambda x: 180.*math.acos(x)/math.pi
43rdsq2d = lambda x,p: round(1.0/math.sqrt(x),p)
44#numpy versions
45npsind = lambda x: np.sin(x*np.pi/180.)
46npasind = lambda x: 180.*np.arcsin(x)/np.pi
47npcosd = lambda x: np.cos(x*np.pi/180.)
48npacosd = lambda x: 180.*np.arccos(x)/np.pi
49nptand = lambda x: np.tan(x*np.pi/180.)
50npatand = lambda x: 180.*np.arctan(x)/np.pi
51npatan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi
52debug = False
53   
54def pointInPolygon(pXY,xy):
55    'Needs a doc string'
56    #pXY - assumed closed 1st & last points are duplicates
57    Inside = False
58    N = len(pXY)
59    p1x,p1y = pXY[0]
60    for i in range(N+1):
61        p2x,p2y = pXY[i%N]
62        if (max(p1y,p2y) >= xy[1] > min(p1y,p2y)) and (xy[0] <= max(p1x,p2x)):
63            if p1y != p2y:
64                xinters = (xy[1]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
65            if p1x == p2x or xy[0] <= xinters:
66                Inside = not Inside
67        p1x,p1y = p2x,p2y
68    return Inside
69   
70def peneCorr(tth,dep,dist,tilt=0.,azm=0.):
71    'Needs a doc string'
72#    return dep*(1.-npcosd(abs(tilt*npsind(azm))-tth*npcosd(azm)))  #something wrong here
73    return dep*(1.-npcosd(tth))*dist**2/1000.         #best one
74#    return dep*npsind(tth)             #not as good as 1-cos2Q
75       
76def makeMat(Angle,Axis):
77    '''Make rotation matrix from Angle and Axis
78
79    :param float Angle: in degrees
80    :param int Axis: 0 for rotation about x, 1 for about y, etc.
81    '''
82    cs = npcosd(Angle)
83    ss = npsind(Angle)
84    M = np.array(([1.,0.,0.],[0.,cs,-ss],[0.,ss,cs]),dtype=np.float32)
85    return np.roll(np.roll(M,Axis,axis=0),Axis,axis=1)
86                   
87def FitEllipse(xy):
88   
89    def ellipse_center(p):
90        ''' gives ellipse center coordinates
91        '''
92        b,c,d,f,a = p[1]/2, p[2], p[3]/2, p[4]/2, p[0]
93        num = b*b-a*c
94        x0=(c*d-b*f)/num
95        y0=(a*f-b*d)/num
96        return np.array([x0,y0])
97   
98    def ellipse_angle_of_rotation( p ):
99        ''' gives rotation of ellipse major axis from x-axis
100        range will be -90 to 90 deg
101        '''
102        b,c,a = p[1]/2, p[2], p[0]
103        return 0.5*npatand(2*b/(a-c))
104   
105    def ellipse_axis_length( p ):
106        ''' gives ellipse radii in [minor,major] order
107        '''
108        b,c,d,f,g,a = p[1]/2, p[2], p[3]/2, p[4]/2, p[5], p[0]
109        up = 2*(a*f*f+c*d*d+g*b*b-2*b*d*f-a*c*g)
110        down1=(b*b-a*c)*( (c-a)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a))
111        down2=(b*b-a*c)*( (a-c)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a))
112        res1=np.sqrt(up/down1)
113        res2=np.sqrt(up/down2)
114        return np.array([ res2,res1])
115   
116    xy = np.array(xy)
117    x = np.asarray(xy.T[0])[:,np.newaxis]
118    y = np.asarray(xy.T[1])[:,np.newaxis]
119    D =  np.hstack((x*x, x*y, y*y, x, y, np.ones_like(x)))
120    S = np.dot(D.T,D)
121    C = np.zeros([6,6])
122    C[0,2] = C[2,0] = 2; C[1,1] = -1
123    E, V =  nl.eig(np.dot(nl.inv(S), C))
124    n = np.argmax(np.abs(E))
125    a = V[:,n]
126    cent = ellipse_center(a)
127    phi = ellipse_angle_of_rotation(a)
128    radii = ellipse_axis_length(a)
129    phi += 90.
130    if radii[0] > radii[1]:
131        radii = [radii[1],radii[0]]
132        phi -= 90.
133    return cent,phi,radii
134
135def FitDetector(rings,varyList,parmDict,Print=True):
136    'Needs a doc string'
137       
138    def CalibPrint(ValSig,chisq,Npts):
139        print 'Image Parameters: chi**2: %12.3g, Np: %d'%(chisq,Npts)
140        ptlbls = 'names :'
141        ptstr =  'values:'
142        sigstr = 'esds  :'
143        for name,value,sig in ValSig:
144            ptlbls += "%s" % (name.rjust(12))
145            if name == 'phi':
146                ptstr += Fmt[name] % (value%360.)
147            else:
148                ptstr += Fmt[name] % (value)
149            if sig:
150                sigstr += Fmt[name] % (sig)
151            else:
152                sigstr += 12*' '
153        print ptlbls
154        print ptstr
155        print sigstr       
156       
157    def ellipseCalcD(B,xyd,varyList,parmDict):
158       
159        x,y,dsp = xyd
160        varyDict = dict(zip(varyList,B))
161        parms = {}
162        for parm in parmDict:
163            if parm in varyList:
164                parms[parm] = varyDict[parm]
165            else:
166                parms[parm] = parmDict[parm]
167        phi = parms['phi']-90.               #get rotation of major axis from tilt axis
168        tth = 2.0*npasind(parms['wave']/(2.*dsp))
169        phi0 = npatan2d(y-parms['det-Y'],x-parms['det-X'])
170        dxy = peneCorr(tth,parms['dep'],parms['dist'],parms['tilt'],phi0)
171        stth = npsind(tth)
172        cosb = npcosd(parms['tilt'])
173        tanb = nptand(parms['tilt'])       
174        tbm = nptand((tth-parms['tilt'])/2.)
175        tbp = nptand((tth+parms['tilt'])/2.)
176        d = parms['dist']+dxy
177        fplus = d*tanb*stth/(cosb+stth)
178        fminus = d*tanb*stth/(cosb-stth)
179        vplus = d*(tanb+(1+tbm)/(1-tbm))*stth/(cosb+stth)
180        vminus = d*(tanb+(1-tbp)/(1+tbp))*stth/(cosb-stth)
181        R0 = np.sqrt((vplus+vminus)**2-(fplus+fminus)**2)/2.      #+minor axis
182        R1 = (vplus+vminus)/2.                                    #major axis
183        zdis = (fplus-fminus)/2.
184        Robs = np.sqrt((x-parms['det-X'])**2+(y-parms['det-Y'])**2)
185        rsqplus = R0**2+R1**2
186        rsqminus = R0**2-R1**2
187        R = rsqminus*npcosd(2.*phi0-2.*phi)+rsqplus
188        Q = np.sqrt(2.)*R0*R1*np.sqrt(R-2.*zdis**2*npsind(phi0-phi)**2)
189        P = 2.*R0**2*zdis*npcosd(phi0-phi)
190        Rcalc = (P+Q)/R
191        M = (Robs-Rcalc)*10.        #why 10? does make "chi**2" more reasonable
192        return M
193       
194    names = ['dist','det-X','det-Y','tilt','phi','dep','wave']
195    fmt = ['%12.3f','%12.3f','%12.3f','%12.3f','%12.3f','%12.4f','%12.6f']
196    Fmt = dict(zip(names,fmt))
197    p0 = [parmDict[key] for key in varyList]
198    result = leastsq(ellipseCalcD,p0,args=(rings.T,varyList,parmDict),full_output=True,ftol=1.e-8)
199    chisq = np.sum(result[2]['fvec']**2)/(rings.shape[0]-len(p0))   #reduced chi^2 = M/(Nobs-Nvar)
200    parmDict.update(zip(varyList,result[0]))
201    vals = list(result[0])
202    sig = list(np.sqrt(chisq*np.diag(result[1])))
203    sigList = np.zeros(7)
204    for i,name in enumerate(varyList):
205        sigList[i] = sig[varyList.index(name)]
206    ValSig = zip(varyList,vals,sig)
207    if Print:
208        CalibPrint(ValSig,chisq,rings.shape[0])
209    return [chisq,vals,sigList]
210
211def ImageLocalMax(image,w,Xpix,Ypix):
212    'Needs a doc string'
213    w2 = w*2
214    sizey,sizex = image.shape
215    xpix = int(Xpix)            #get reference corner of pixel chosen
216    ypix = int(Ypix)
217    if not w:
218        ZMax = np.sum(image[ypix-2:ypix+2,xpix-2:xpix+2])
219        return xpix,ypix,ZMax,0.0001
220    if (w2 < xpix < sizex-w2) and (w2 < ypix < sizey-w2) and image[ypix,xpix]:
221        ZMax = image[ypix-w:ypix+w,xpix-w:xpix+w]
222        Zmax = np.argmax(ZMax)
223        ZMin = image[ypix-w2:ypix+w2,xpix-w2:xpix+w2]
224        Zmin = np.argmin(ZMin)
225        xpix += Zmax%w2-w
226        ypix += Zmax/w2-w
227        return xpix,ypix,np.ravel(ZMax)[Zmax],max(0.0001,np.ravel(ZMin)[Zmin])   #avoid neg/zero minimum
228    else:
229        return 0,0,0,0     
230   
231def makeRing(dsp,ellipse,pix,reject,scalex,scaley,image):
232    'Needs a doc string'
233    def ellipseC():
234        'compute estimate of ellipse circumference'
235        if radii[0] < 0:        #hyperbola
236            theta = npacosd(1./np.sqrt(1.+(radii[0]/radii[1])**2))
237            print theta
238            return 0
239        apb = radii[1]+radii[0]
240        amb = radii[1]-radii[0]
241        return np.pi*apb*(1+3*(amb/apb)**2/(10+np.sqrt(4-3*(amb/apb)**2)))
242       
243    cent,phi,radii = ellipse
244    cphi = cosd(phi-90.)        #convert to major axis rotation
245    sphi = sind(phi-90.)
246    ring = []
247    C = int(ellipseC())         #ring circumference
248    azm = []
249    for i in range(0,C,1):      #step around ring in 1mm increments
250        a = 360.*i/C
251        x = radii[1]*cosd(a-phi+90.)        #major axis
252        y = radii[0]*sind(a-phi+90.)
253        X = (cphi*x-sphi*y+cent[0])*scalex      #convert mm to pixels
254        Y = (sphi*x+cphi*y+cent[1])*scaley
255        X,Y,I,J = ImageLocalMax(image,pix,X,Y)
256        if I and J and float(I)/J > reject:
257            X += .5                             #set to center of pixel
258            Y += .5
259            X /= scalex                         #convert back to mm
260            Y /= scaley
261            if [X,Y,dsp] not in ring:           #no duplicates!
262                ring.append([X,Y,dsp])
263                azm.append(a)
264    if len(ring) < 10:
265        ring = []
266        azm = []
267    return ring,azm
268   
269def GetEllipse2(tth,dxy,dist,cent,tilt,phi):
270    '''uses Dandelin spheres to find ellipse or hyperbola parameters from detector geometry
271    on output
272    radii[0] (b-minor axis) set < 0. for hyperbola
273   
274    '''
275    radii = [0,0]
276    stth = sind(tth)
277    cosb = cosd(tilt)
278    tanb = tand(tilt)
279    tbm = tand((tth-tilt)/2.)
280    tbp = tand((tth+tilt)/2.)
281    sinb = sind(tilt)
282    d = dist+dxy
283    if tth+abs(tilt) < 90.:      #ellipse
284        fplus = d*tanb*stth/(cosb+stth)
285        fminus = d*tanb*stth/(cosb-stth)
286        vplus = d*(tanb+(1+tbm)/(1-tbm))*stth/(cosb+stth)
287        vminus = d*(tanb+(1-tbp)/(1+tbp))*stth/(cosb-stth)
288        radii[0] = np.sqrt((vplus+vminus)**2-(fplus+fminus)**2)/2.      #+minor axis
289        radii[1] = (vplus+vminus)/2.                                    #major axis
290        zdis = (fplus-fminus)/2.
291    else:   #hyperbola!
292        f = d*abs(tanb)*stth/(cosb+stth)
293        v = d*(abs(tanb)+tand(tth-abs(tilt)))
294        delt = d*stth*(1.+stth*cosb)/(abs(sinb)*cosb*(stth+cosb))
295        eps = (v-f)/(delt-v)
296        radii[0] = -eps*(delt-f)/np.sqrt(eps**2-1.)                     #-minor axis
297        radii[1] = eps*(delt-f)/(eps**2-1.)                             #major axis
298        if tilt > 0:
299            zdis = f+radii[1]*eps
300        else:
301            zdis = -f
302#NB: zdis is || to major axis & phi is rotation of minor axis
303#thus shift from beam to ellipse center is [Z*sin(phi),-Z*cos(phi)]
304    elcent = [cent[0]+zdis*sind(phi),cent[1]-zdis*cosd(phi)]
305    return elcent,phi,radii
306   
307def GetEllipse(dsp,data):
308    '''uses Dandelin spheres to find ellipse or hyperbola parameters from detector geometry
309    as given in image controls dictionary (data) and a d-spacing (dsp)
310    '''
311    cent = data['center']
312    tilt = data['tilt']
313    phi = data['rotation']
314    dep = data.get('DetDepth',0.0)
315    tth = 2.0*asind(data['wavelength']/(2.*dsp))
316    dist = data['distance']
317    dxy = peneCorr(tth,dep,dist,tilt)
318    return GetEllipse2(tth,dxy,dist,cent,tilt,phi)
319       
320def GetDetectorXY(dsp,azm,data):
321    'Needs a doc string'
322   
323    elcent,phi,radii = GetEllipse(dsp,data)
324    phi = data['rotation']-90.          #to give rotation of major axis
325    tilt = data['tilt']
326    dist = data['distance']
327    cent = data['center']
328    tth = 2.0*asind(data['wavelength']/(2.*dsp))
329    stth = sind(tth)
330    cosb = cosd(tilt)
331    if radii[0] > 0.:
332        sinb = sind(tilt)
333        tanb = tand(tilt)
334        fplus = dist*tanb*stth/(cosb+stth)
335        fminus = dist*tanb*stth/(cosb-stth)
336        zdis = (fplus-fminus)/2.
337        rsqplus = radii[0]**2+radii[1]**2
338        rsqminus = radii[0]**2-radii[1]**2
339        R = rsqminus*cosd(2.*azm-2.*phi)+rsqplus
340        Q = np.sqrt(2.)*radii[0]*radii[1]*np.sqrt(R-2.*zdis**2*sind(azm-phi)**2)
341        P = 2.*radii[0]**2*zdis*cosd(azm-phi)
342        radius = (P+Q)/R
343        xy = np.array([radius*cosd(azm),radius*sind(azm)])
344        xy += cent
345    else:   #hyperbola - both branches (one is way off screen!)
346        sinb = abs(sind(tilt))
347        tanb = abs(tand(tilt))
348        f = dist*tanb*stth/(cosb+stth)
349        v = dist*(tanb+tand(tth-abs(tilt)))
350        delt = dist*stth*(1+stth*cosb)/(sinb*cosb*(stth+cosb))
351        ecc = (v-f)/(delt-v)
352        R = radii[1]*(ecc**2-1)/(1-ecc*cosd(azm))
353        if tilt > 0.:
354            offset = 2.*radii[1]*ecc+f      #select other branch
355            xy = [-R*cosd(azm)-offset,R*sind(azm)]
356        else:
357            offset = -f
358            xy = [-R*cosd(azm)-offset,-R*sind(azm)]
359        xy = -np.array([xy[0]*cosd(phi)+xy[1]*sind(phi),xy[0]*sind(phi)-xy[1]*cosd(phi)])
360        xy += cent
361    return xy
362   
363def GetDetXYfromThAzm(Th,Azm,data):
364    '''Computes a detector position from a 2theta angle and an azimultal
365    angle (both in degrees)
366    '''
367    dsp = data['wavelength']/(2.0*npsind(Th))   
368    return GetDetectorXY(dsp,Azm,data)
369                   
370def GetTthAzmDsp(x,y,data): #expensive
371    '''Computes a 2theta, etc. from a detector position and calibration constants - checked
372    OK for ellipses & hyperbola.
373
374    :returns: np.array(tth,azm,G,dsp) where tth is 2theta, azm is the azimutal angle,
375       G is ? and dsp is the d-space
376    '''
377    wave = data['wavelength']
378    cent = data['center']
379    tilt = data['tilt']
380    dist = data['distance']/cosd(tilt)
381    x0 = dist*tand(tilt)
382    phi = data['rotation']
383    dep = data.get('DetDepth',0.)
384    azmthoff = data['azmthOff']
385    dx = np.array(x-cent[0],dtype=np.float32)
386    dy = np.array(y-cent[1],dtype=np.float32)
387    D = ((dx-x0)**2+dy**2+dist**2)      #sample to pixel distance
388    X = np.array(([dx,dy,np.zeros_like(dx)]),dtype=np.float32).T
389    X = np.dot(X,makeMat(phi,2))
390    Z = np.dot(X,makeMat(tilt,0)).T[2]
391    tth = npatand(np.sqrt(dx**2+dy**2-Z**2)/(dist-Z))
392    dxy = peneCorr(tth,dep,dist,tilt,npatan2d(dy,dx))
393    DX = dist-Z+dxy
394    DY = np.sqrt(dx**2+dy**2-Z**2)
395    tth = npatan2d(DY,DX) 
396    dsp = wave/(2.*npsind(tth/2.))
397    azm = (npatan2d(dy,dx)+azmthoff+720.)%360.
398    G = D/dist**2       #for geometric correction = 1/cos(2theta)^2 if tilt=0.
399    return np.array([tth,azm,G,dsp])
400   
401def GetTth(x,y,data):
402    'Give 2-theta value for detector x,y position; calibration info in data'
403    return GetTthAzmDsp(x,y,data)[0]
404   
405def GetTthAzm(x,y,data):
406    'Give 2-theta, azimuth values for detector x,y position; calibration info in data'
407    return GetTthAzmDsp(x,y,data)[0:2]
408   
409def GetTthAzmG(x,y,data):
410    '''Give 2-theta, azimuth & geometric corr. values for detector x,y position;
411     calibration info in data - only used in integration
412    '''
413    'Needs a doc string - checked OK for ellipses & hyperbola'
414    tilt = data['tilt']
415    dist = data['distance']/npcosd(tilt)
416    x0 = data['distance']*nptand(tilt)
417    MN = -np.inner(makeMat(data['rotation'],2),makeMat(tilt,0))
418    distsq = data['distance']**2
419    dx = x-data['center'][0]
420    dy = y-data['center'][1]
421    G = ((dx-x0)**2+dy**2+distsq)/distsq       #for geometric correction = 1/cos(2theta)^2 if tilt=0.
422    X = np.dstack([dx.T,dy.T,np.zeros_like(dx.T)])
423    Z = np.dot(X,MN).T[2]
424    xyZ = dx**2+dy**2-Z**2   
425    tth = npatand(np.sqrt(xyZ)/(dist-Z))
426    dxy = peneCorr(tth,data['DetDepth'],dist,tilt,npatan2d(dy,dx))
427    tth = npatan2d(np.sqrt(xyZ),dist-Z+dxy) 
428    azm = (npatan2d(dy,dx)+data['azmthOff']+720.)%360.
429    return tth,azm,G
430
431def GetDsp(x,y,data):
432    'Give d-spacing value for detector x,y position; calibration info in data'
433    return GetTthAzmDsp(x,y,data)[3]
434       
435def GetAzm(x,y,data):
436    'Give azimuth value for detector x,y position; calibration info in data'
437    return GetTthAzmDsp(x,y,data)[1]
438   
439def meanAzm(a,b):
440    AZM = lambda a,b: npacosd(0.5*(npsind(2.*b)-npsind(2.*a))/(np.pi*(b-a)/180.))/2.
441    azm = AZM(a,b)
442#    quad = int((a+b)/180.)
443#    if quad == 1:
444#        azm = 180.-azm
445#    elif quad == 2:
446#        azm += 180.
447#    elif quad == 3:
448#        azm = 360-azm
449    return azm     
450       
451def ImageCompress(image,scale):
452    ''' Reduces size of image by selecting every n'th point
453    param: image array: original image
454    param: scale int: intervsl between selected points
455    returns: array: reduced size image
456    '''
457    if scale == 1:
458        return image
459    else:
460        return image[::scale,::scale]
461       
462def checkEllipse(Zsum,distSum,xSum,ySum,dist,x,y):
463    'Needs a doc string'
464    avg = np.array([distSum/Zsum,xSum/Zsum,ySum/Zsum])
465    curr = np.array([dist,x,y])
466    return abs(avg-curr)/avg < .02
467
468def EdgeFinder(image,data):
469    '''this makes list of all x,y where I>edgeMin suitable for an ellipse search?
470    Not currently used but might be useful in future?
471    '''
472    import numpy.ma as ma
473    Nx,Ny = data['size']
474    pixelSize = data['pixelSize']
475    edgemin = data['edgemin']
476    scalex = pixelSize[0]/1000.
477    scaley = pixelSize[1]/1000.   
478    tay,tax = np.mgrid[0:Nx,0:Ny]
479    tax = np.asfarray(tax*scalex,dtype=np.float32)
480    tay = np.asfarray(tay*scaley,dtype=np.float32)
481    tam = ma.getmask(ma.masked_less(image.flatten(),edgemin))
482    tax = ma.compressed(ma.array(tax.flatten(),mask=tam))
483    tay = ma.compressed(ma.array(tay.flatten(),mask=tam))
484    return zip(tax,tay)
485   
486def MakeFrameMask(data,frame):
487    pixelSize = data['pixelSize']
488    scalex = pixelSize[0]/1000.
489    scaley = pixelSize[1]/1000.
490    blkSize = 512
491    Nx,Ny = data['size']
492    nXBlks = (Nx-1)/blkSize+1
493    nYBlks = (Ny-1)/blkSize+1
494    tam = ma.make_mask_none(data['size'])
495    for iBlk in range(nXBlks):
496        iBeg = iBlk*blkSize
497        iFin = min(iBeg+blkSize,Nx)
498        for jBlk in range(nYBlks):
499            jBeg = jBlk*blkSize
500            jFin = min(jBeg+blkSize,Ny)               
501            nI = iFin-iBeg
502            nJ = jFin-jBeg
503            tax,tay = np.mgrid[iBeg+0.5:iFin+.5,jBeg+.5:jFin+.5]         #bin centers not corners
504            tax = np.asfarray(tax*scalex,dtype=np.float32)
505            tay = np.asfarray(tay*scaley,dtype=np.float32)
506            tamp = ma.make_mask_none((1024*1024))
507            tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
508                tay.flatten(),len(frame),frame,tamp)[:nI*nJ])-True  #switch to exclude around frame
509            if tamp.shape:
510                tamp = np.reshape(tamp[:nI*nJ],(nI,nJ))
511                tam[iBeg:iFin,jBeg:jFin] = ma.mask_or(tamp[0:nI,0:nJ],tam[iBeg:iFin,jBeg:jFin])
512            else:
513                tam[iBeg:iFin,jBeg:jFin] = True
514    return tam.T
515   
516def ImageRecalibrate(G2frame,data,masks):
517    '''Called to repeat the calibration on an image, usually called after
518    calibration is done initially to improve the fit.
519    '''
520    import ImageCalibrants as calFile
521    print 'Image recalibration:'
522    time0 = time.time()
523    pixelSize = data['pixelSize']
524    scalex = 1000./pixelSize[0]
525    scaley = 1000./pixelSize[1]
526    pixLimit = data['pixLimit']
527    cutoff = data['cutoff']
528    data['rings'] = []
529    data['ellipses'] = []
530    if data['DetDepth'] > 0.5:          #patch - redefine DetDepth
531        data['DetDepth'] /= data['distance']
532    if not data['calibrant']:
533        print 'no calibration material selected'
534        return []   
535    skip = data['calibskip']
536    dmin = data['calibdmin']
537    Bravais,SGs,Cells = calFile.Calibrants[data['calibrant']][:3]
538    HKL = []
539    for bravais,sg,cell in zip(Bravais,SGs,Cells):
540        A = G2lat.cell2A(cell)
541        if sg:
542            SGData = G2spc.SpcGroup(sg)[1]
543            hkl = G2pwd.getHKLpeak(dmin,SGData,A)
544            HKL += hkl
545        else:
546            hkl = G2lat.GenHBravais(dmin,bravais,A)
547            HKL += hkl
548    HKL = G2lat.sortHKLd(HKL,True,False)
549    varyList = [item for item in data['varyList'] if data['varyList'][item]]
550    parmDict = {'dist':data['distance'],'det-X':data['center'][0],'det-Y':data['center'][1],
551        'setdist':data.get('setdist',data['distance']),
552        'tilt':data['tilt'],'phi':data['rotation'],'wave':data['wavelength'],'dep':data['DetDepth']}
553    Found = False
554    wave = data['wavelength']
555    frame = masks['Frames']
556    tam = ma.make_mask_none(G2frame.ImageZ.shape)
557    if frame:
558        tam = ma.mask_or(tam,MakeFrameMask(data,frame))
559    for iH,H in enumerate(HKL):
560        if debug:   print H
561        dsp = H[3]
562        tth = 2.0*asind(wave/(2.*dsp))
563        if tth+abs(data['tilt']) > 90.:
564            print 'next line is a hyperbola - search stopped'
565            break
566        ellipse = GetEllipse(dsp,data)
567        Ring = makeRing(dsp,ellipse,pixLimit,cutoff,scalex,scaley,ma.array(G2frame.ImageZ,mask=tam))[0]
568        if Ring:
569            if iH >= skip:
570                data['rings'].append(np.array(Ring))
571            data['ellipses'].append(copy.deepcopy(ellipse+('r',)))
572            Found = True
573        elif not Found:         #skipping inner rings, keep looking until ring found
574            continue
575        else:                   #no more rings beyond edge of detector
576            data['ellipses'].append([])
577            continue
578    if not data['rings']:
579        print 'no rings found; try lower Min ring I/Ib'
580        return []   
581       
582    rings = np.concatenate((data['rings']),axis=0)
583    [chisq,vals,sigList] = FitDetector(rings,varyList,parmDict)
584    data['wavelength'] = parmDict['wave']
585    data['distance'] = parmDict['dist']
586    data['center'] = [parmDict['det-X'],parmDict['det-Y']]
587    data['rotation'] = np.mod(parmDict['phi'],360.0)
588    data['tilt'] = parmDict['tilt']
589    data['DetDepth'] = parmDict['dep']
590    data['chisq'] = chisq
591    N = len(data['ellipses'])
592    data['ellipses'] = []           #clear away individual ellipse fits
593    for H in HKL[:N]:
594        ellipse = GetEllipse(H[3],data)
595        data['ellipses'].append(copy.deepcopy(ellipse+('b',)))   
596    print 'calibration time = %.3f'%(time.time()-time0)
597    G2plt.PlotImage(G2frame,newImage=True)       
598    return [vals,varyList,sigList,parmDict]
599           
600def ImageCalibrate(G2frame,data):
601    '''Called to perform an initial image calibration after points have been
602    selected for the inner ring.
603    '''
604    import copy
605    import ImageCalibrants as calFile
606    print 'Image calibration:'
607    time0 = time.time()
608    ring = data['ring']
609    pixelSize = data['pixelSize']
610    scalex = 1000./pixelSize[0]
611    scaley = 1000./pixelSize[1]
612    pixLimit = data['pixLimit']
613    cutoff = data['cutoff']
614    varyDict = data['varyList']
615    if varyDict['dist'] and varyDict['wave']:
616        print 'ERROR - you can not simultaneously calibrate distance and wavelength'
617        return False
618    if len(ring) < 5:
619        print 'ERROR - not enough inner ring points for ellipse'
620        return False
621       
622    #fit start points on inner ring
623    data['ellipses'] = []
624    data['rings'] = []
625    outE = FitEllipse(ring)
626    fmt  = '%s X: %.3f, Y: %.3f, phi: %.3f, R1: %.3f, R2: %.3f'
627    fmt2 = '%s X: %.3f, Y: %.3f, phi: %.3f, R1: %.3f, R2: %.3f, chi**2: %.3f, Np: %d'
628    if outE:
629        print fmt%('start ellipse: ',outE[0][0],outE[0][1],outE[1],outE[2][0],outE[2][1])
630        ellipse = outE
631    else:
632        return False
633       
634    #setup 360 points on that ring for "good" fit
635    data['ellipses'].append(ellipse[:]+('g',))
636    Ring = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,G2frame.ImageZ)[0]
637    if Ring:
638        ellipse = FitEllipse(Ring)
639        Ring = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,G2frame.ImageZ)[0]    #do again
640        ellipse = FitEllipse(Ring)
641    else:
642        print '1st ring not sufficiently complete to proceed'
643        return False
644    if debug:
645        print fmt2%('inner ring:    ',ellipse[0][0],ellipse[0][1],ellipse[1],
646            ellipse[2][0],ellipse[2][1],0.,len(Ring))     #cent,phi,radii
647    data['ellipses'].append(ellipse[:]+('r',))
648    data['rings'].append(np.array(Ring))
649    G2plt.PlotImage(G2frame,newImage=True)
650   
651#setup for calibration
652    data['rings'] = []
653    if not data['calibrant']:
654        print 'no calibration material selected'
655        return True
656   
657    skip = data['calibskip']
658    dmin = data['calibdmin']
659#generate reflection set
660    Bravais,SGs,Cells = calFile.Calibrants[data['calibrant']][:3]
661    HKL = []
662    for bravais,sg,cell in zip(Bravais,SGs,Cells):
663        A = G2lat.cell2A(cell)
664        if sg:
665            SGData = G2spc.SpcGroup(sg)[1]
666            hkl = G2pwd.getHKLpeak(dmin,SGData,A)
667            HKL += hkl
668        else:
669            hkl = G2lat.GenHBravais(dmin,bravais,A)
670            HKL += hkl
671    HKL = G2lat.sortHKLd(HKL,True,False)[skip:]
672#set up 1st ring
673    elcent,phi,radii = ellipse              #from fit of 1st ring
674    dsp = HKL[0][3]
675    print '1st ring: try %.4f'%(dsp)
676    if varyDict['dist']:
677        wave = data['wavelength']
678        tth = 2.0*asind(wave/(2.*dsp))
679    else:   #varyDict['wave']!
680        dist = data['distance']
681        tth = npatan2d(radii[0],dist)
682        data['wavelength'] = wave =  2.0*dsp*sind(tth/2.0)
683    Ring0 = makeRing(dsp,ellipse,3,cutoff,scalex,scaley,G2frame.ImageZ)[0]
684    ttth = nptand(tth)
685    ctth = npcosd(tth)
686#1st estimate of tilt; assume ellipse - don't know sign though
687    if varyDict['tilt']:
688        tilt = npasind(np.sqrt(max(0.,1.-(radii[0]/radii[1])**2))*ctth)
689        if not tilt:
690            print 'WARNING - selected ring was fitted as a circle'
691            print ' - if detector was tilted we suggest you skip this ring - WARNING'
692    else:
693        tilt = data['tilt']
694#1st estimate of dist: sample to detector normal to plane
695    if varyDict['dist']:
696        data['distance'] = dist = radii[0]**2/(ttth*radii[1])
697    else:
698        dist = data['distance']
699    if varyDict['tilt']:
700#ellipse to cone axis (x-ray beam); 2 choices depending on sign of tilt
701        zdisp = radii[1]*ttth*tand(tilt)
702        zdism = radii[1]*ttth*tand(-tilt)
703#cone axis position; 2 choices. Which is right?     
704#NB: zdisp is || to major axis & phi is rotation of minor axis
705#thus shift from beam to ellipse center is [Z*sin(phi),-Z*cos(phi)]
706        centp = [elcent[0]+zdisp*sind(phi),elcent[1]-zdisp*cosd(phi)]
707        centm = [elcent[0]+zdism*sind(phi),elcent[1]-zdism*cosd(phi)]
708#check get same ellipse parms either way
709#now do next ring; estimate either way & do a FitDetector each way; best fit is correct one
710        fail = True
711        i2 = 1
712        while fail:
713            dsp = HKL[i2][3]
714            print '2nd ring: try %.4f'%(dsp)
715            tth = 2.0*asind(wave/(2.*dsp))
716            ellipsep = GetEllipse2(tth,0.,dist,centp,tilt,phi)
717            print fmt%('plus ellipse :',ellipsep[0][0],ellipsep[0][1],ellipsep[1],ellipsep[2][0],ellipsep[2][1])
718            Ringp = makeRing(dsp,ellipsep,3,cutoff,scalex,scaley,G2frame.ImageZ)[0]
719            parmDict = {'dist':dist,'det-X':centp[0],'det-Y':centp[1],
720                'tilt':tilt,'phi':phi,'wave':wave,'dep':0.0}       
721            varyList = [item for item in varyDict if varyDict[item]]
722            if len(Ringp) > 10:
723                chip = FitDetector(np.array(Ring0+Ringp),varyList,parmDict,True)[0]
724                tiltp = parmDict['tilt']
725                phip = parmDict['phi']
726                centp = [parmDict['det-X'],parmDict['det-Y']]
727                fail = False
728            else:
729                chip = 1e6
730            ellipsem = GetEllipse2(tth,0.,dist,centm,-tilt,phi)
731            print fmt%('minus ellipse:',ellipsem[0][0],ellipsem[0][1],ellipsem[1],ellipsem[2][0],ellipsem[2][1])
732            Ringm = makeRing(dsp,ellipsem,3,cutoff,scalex,scaley,G2frame.ImageZ)[0]
733            if len(Ringm) > 10:
734                parmDict['tilt'] *= -1
735                chim = FitDetector(np.array(Ring0+Ringm),varyList,parmDict,True)[0]
736                tiltm = parmDict['tilt']
737                phim = parmDict['phi']
738                centm = [parmDict['det-X'],parmDict['det-Y']]
739                fail = False
740            else:
741                chim = 1e6
742            if fail:
743                i2 += 1
744        if chip < chim:
745            data['tilt'] = tiltp
746            data['center'] = centp
747            data['rotation'] = phip
748        else:
749            data['tilt'] = tiltm
750            data['center'] = centm
751            data['rotation'] = phim
752        data['ellipses'].append(ellipsep[:]+('b',))
753        data['rings'].append(np.array(Ringp))
754        data['ellipses'].append(ellipsem[:]+('r',))
755        data['rings'].append(np.array(Ringm))
756        G2plt.PlotImage(G2frame,newImage=True)
757    if data['DetDepth'] > 0.5:          #patch - redefine DetDepth
758        data['DetDepth'] /= data['distance']
759    parmDict = {'dist':data['distance'],'det-X':data['center'][0],'det-Y':data['center'][1],
760        'tilt':data['tilt'],'phi':data['rotation'],'wave':data['wavelength'],'dep':data['DetDepth']}
761    varyList = [item for item in varyDict if varyDict[item]]
762    data['rings'] = []
763    data['ellipses'] = []
764    for i,H in enumerate(HKL):
765        dsp = H[3]
766        tth = 2.0*asind(wave/(2.*dsp))
767        if tth+abs(data['tilt']) > 90.:
768            print 'next line is a hyperbola - search stopped'
769            break
770        if debug:   print 'HKLD:',H[:4],'2-theta: %.4f'%(tth)
771        elcent,phi,radii = ellipse = GetEllipse(dsp,data)
772        data['ellipses'].append(copy.deepcopy(ellipse+('g',)))
773        if debug:   print fmt%('predicted ellipse:',elcent[0],elcent[1],phi,radii[0],radii[1])
774        Ring = makeRing(dsp,ellipse,pixLimit,cutoff,scalex,scaley,G2frame.ImageZ)[0]
775        if Ring:
776            data['rings'].append(np.array(Ring))
777            rings = np.concatenate((data['rings']),axis=0)
778            if i:
779                chisq = FitDetector(rings,varyList,parmDict,False)[0]
780                data['distance'] = parmDict['dist']
781                data['center'] = [parmDict['det-X'],parmDict['det-Y']]
782                data['rotation'] = parmDict['phi']
783                data['tilt'] = parmDict['tilt']
784                data['DetDepth'] = parmDict['dep']
785                data['chisq'] = chisq
786                elcent,phi,radii = ellipse = GetEllipse(dsp,data)
787                if debug:   print fmt2%('fitted ellipse:   ',elcent[0],elcent[1],phi,radii[0],radii[1],chisq,len(rings))
788            data['ellipses'].append(copy.deepcopy(ellipse+('r',)))
789#            G2plt.PlotImage(G2frame,newImage=True)
790        else:
791            if debug:   print 'insufficient number of points in this ellipse to fit'
792#            break
793    G2plt.PlotImage(G2frame,newImage=True)
794    fullSize = len(G2frame.ImageZ)/scalex
795    if 2*radii[1] < .9*fullSize:
796        print 'Are all usable rings (>25% visible) used? Try reducing Min ring I/Ib'
797    N = len(data['ellipses'])
798    if N > 2:
799        FitDetector(rings,varyList,parmDict)[0]
800        data['wavelength'] = parmDict['wave']
801        data['distance'] = parmDict['dist']
802        data['center'] = [parmDict['det-X'],parmDict['det-Y']]
803        data['rotation'] = parmDict['phi']
804        data['tilt'] = parmDict['tilt']
805        data['DetDepth'] = parmDict['dep']
806    for H in HKL[:N]:
807        ellipse = GetEllipse(H[3],data)
808        data['ellipses'].append(copy.deepcopy(ellipse+('b',)))
809    print 'calibration time = %.3f'%(time.time()-time0)
810    G2plt.PlotImage(G2frame,newImage=True)       
811    return True
812   
813def Make2ThetaAzimuthMap(data,masks,iLim,jLim,times): #most expensive part of integration!
814    'Needs a doc string'
815    #transforms 2D image from x,y space to 2-theta,azimuth space based on detector orientation
816    pixelSize = data['pixelSize']
817    scalex = pixelSize[0]/1000.
818    scaley = pixelSize[1]/1000.
819   
820    tay,tax = np.mgrid[iLim[0]+0.5:iLim[1]+.5,jLim[0]+.5:jLim[1]+.5]         #bin centers not corners
821    tax = np.asfarray(tax*scalex,dtype=np.float32)
822    tay = np.asfarray(tay*scaley,dtype=np.float32)
823    nI = iLim[1]-iLim[0]
824    nJ = jLim[1]-jLim[0]
825    t0 = time.time()
826    #make position masks here
827    frame = masks['Frames']
828    tam = ma.make_mask_none((nI,nJ))
829    if frame:
830        tamp = ma.make_mask_none((1024*1024))
831        tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
832            tay.flatten(),len(frame),frame,tamp)[:nI*nJ])-True  #switch to exclude around frame
833        tam = ma.mask_or(tam.flatten(),tamp)
834    polygons = masks['Polygons']
835    for polygon in polygons:
836        if polygon:
837            tamp = ma.make_mask_none((1024*1024))
838            tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
839                tay.flatten(),len(polygon),polygon,tamp)[:nI*nJ])
840            tam = ma.mask_or(tam.flatten(),tamp)
841    if tam.shape: tam = np.reshape(tam,(nI,nJ))
842    spots = masks['Points']
843    for X,Y,diam in spots:
844        tamp = ma.getmask(ma.masked_less((tax-X)**2+(tay-Y)**2,(diam/2.)**2))
845        tam = ma.mask_or(tam,tamp)
846    times[0] += time.time()-t0
847    t0 = time.time()
848    TA = np.array(GetTthAzmG(tax,tay,data))     #includes geom. corr. as dist**2/d0**2 - most expensive step
849    times[1] += time.time()-t0
850    TA[1] = np.where(TA[1]<0,TA[1]+360,TA[1])
851    return np.array(TA),tam           #2-theta, azimuth & geom. corr. arrays & position mask
852
853def Fill2ThetaAzimuthMap(masks,TA,tam,image):
854    'Needs a doc string'
855    Zlim = masks['Thresholds'][1]
856    rings = masks['Rings']
857    arcs = masks['Arcs']
858    TA = np.dstack((ma.getdata(TA[1]),ma.getdata(TA[0]),ma.getdata(TA[2])))    #azimuth, 2-theta, dist
859    tax,tay,tad = np.dsplit(TA,3)    #azimuth, 2-theta, dist**2/d0**2
860    for tth,thick in rings:
861        tam = ma.mask_or(tam.flatten(),ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.)))
862    for tth,azm,thick in arcs:
863        tamt = ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.))
864        tama = ma.getmask(ma.masked_inside(tax.flatten(),azm[0],azm[1]))
865        tam = ma.mask_or(tam.flatten(),tamt*tama)
866    taz = ma.masked_outside(image.flatten(),int(Zlim[0]),Zlim[1])
867    tabs = np.ones_like(taz)
868    tam = ma.mask_or(tam.flatten(),ma.getmask(taz))
869    tax = ma.compressed(ma.array(tax.flatten(),mask=tam))   #azimuth
870    tay = ma.compressed(ma.array(tay.flatten(),mask=tam))   #2-theta
871    taz = ma.compressed(ma.array(taz.flatten(),mask=tam))   #intensity
872    tad = ma.compressed(ma.array(tad.flatten(),mask=tam))   #dist**2/d0**2
873    tabs = ma.compressed(ma.array(tabs.flatten(),mask=tam)) #ones - later used for absorption corr.
874    return tax,tay,taz,tad,tabs
875   
876def ImageIntegrate(image,data,masks,blkSize=128,returnN=False):
877    'Integrate an image; called from OnIntegrateAll and OnIntegrate in G2imgGUI'    #for q, log(q) bins need data['binType']
878    import histogram2d as h2d
879    print 'Begin image integration'
880    CancelPressed = False
881    LUtth = np.array(data['IOtth'])
882    LRazm = np.array(data['LRazimuth'],dtype=np.float64)
883    numAzms = data['outAzimuths']
884    numChans = data['outChannels']
885    Dazm = (LRazm[1]-LRazm[0])/numAzms
886    if '2-theta' in data.get('binType','2-theta'):
887        lutth = LUtth               
888    elif 'log(q)' in data['binType']:
889        lutth = np.log(4.*np.pi*npsind(LUtth/2.)/data['wavelength'])
890    elif 'q' == data['binType'].lower():
891        lutth = 4.*np.pi*npsind(LUtth/2.)/data['wavelength']
892    dtth = (lutth[1]-lutth[0])/numChans
893    muT = data.get('SampleAbs',[0.0,''])[0]
894    if data['DetDepth'] > 0.5:          #patch - redefine DetDepth
895        data['DetDepth'] /= data['distance']
896    if 'SASD' in data['type']:
897        muT = -np.log(muT)/2.       #Transmission to 1/2 thickness muT
898    NST = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
899    H0 = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
900    Nx,Ny = data['size']
901    nXBlks = (Nx-1)/blkSize+1
902    nYBlks = (Ny-1)/blkSize+1
903    tbeg = time.time()
904    times = [0,0,0,0,0]
905    for iBlk in range(nYBlks):
906        iBeg = iBlk*blkSize
907        iFin = min(iBeg+blkSize,Ny)
908        for jBlk in range(nXBlks):
909            jBeg = jBlk*blkSize
910            jFin = min(jBeg+blkSize,Nx)
911            # next is most expensive step!
912            TA,tam = Make2ThetaAzimuthMap(data,masks,(iBeg,iFin),(jBeg,jFin),times)           #2-theta & azimuth arrays & create position mask
913            Block = image[iBeg:iFin,jBeg:jFin]
914            t0 = time.time()
915            tax,tay,taz,tad,tabs = Fill2ThetaAzimuthMap(masks,TA,tam,Block)    #and apply masks
916            del TA; del tam
917            times[2] += time.time()-t0
918            tax = np.where(tax > LRazm[1],tax-360.,tax)                 #put azm inside limits if possible
919            tax = np.where(tax < LRazm[0],tax+360.,tax)
920            if data.get('SampleAbs',[0.0,''])[1]:
921                if 'Cylind' in data['SampleShape']:
922                    muR = muT*(1.+npsind(tax)**2/2.)/(npcosd(tay))      #adjust for additional thickness off sample normal
923                    tabs = G2pwd.Absorb(data['SampleShape'],muR,tay)
924                elif 'Fixed' in data['SampleShape']:    #assumes flat plate sample normal to beam
925                    tabs = G2pwd.Absorb('Fixed',muT,tay)
926            if 'log(q)' in data.get('binType',''):
927                tay = np.log(4.*np.pi*npsind(tay/2.)/data['wavelength'])
928            elif 'q' == data.get('binType','').lower():
929                tay = 4.*np.pi*npsind(tay/2.)/data['wavelength']
930            t0 = time.time()
931            taz = np.array((taz*tad/tabs),dtype='float32')
932            if any([tax.shape[0],tay.shape[0],taz.shape[0]]):
933                NST,H0 = h2d.histogram2d(len(tax),tax,tay,taz,
934                    numAzms,numChans,LRazm,lutth,Dazm,dtth,NST,H0)
935            times[3] += time.time()-t0
936            del tax; del tay; del taz; del tad; del tabs
937    t0 = time.time()
938    NST = np.array(NST,dtype=np.float)
939    H0 = np.divide(H0,NST)
940    H0 = np.nan_to_num(H0)
941    H2 = np.array([tth for tth in np.linspace(lutth[0],lutth[1],numChans+1)])
942    if 'log(q)' in data.get('binType',''):
943        H2 = 2.*npasind(np.exp(H2)*data['wavelength']/(4.*np.pi))
944    elif 'q' == data.get('binType','').lower():
945        H2 = 2.*npasind(H2*data['wavelength']/(4.*np.pi))
946    if Dazm:       
947        H1 = np.array([azm for azm in np.linspace(LRazm[0],LRazm[1],numAzms+1)])
948    else:
949        H1 = LRazm
950    H0 /= npcosd(H2[:-1])           #**2? I don't think so, **1 is right for powders
951    if 'SASD' in data['type']:
952        H0 /= npcosd(H2[:-1])           #one more for small angle scattering data?
953    if data['Oblique'][1]:
954        H0 /= G2pwd.Oblique(data['Oblique'][0],H2[:-1])
955    if 'SASD' in data['type'] and data['PolaVal'][1]:
956        #NB: in G2pwd.Polarization azm is defined from plane of polarization, not image x axis!
957        H0 /= np.array([G2pwd.Polarization(data['PolaVal'][0],H2[:-1],Azm=azm-90.)[0] for azm in (H1[:-1]+np.diff(H1)/2.)])
958    times[4] += time.time()-t0
959    print 'Step times: \n apply masks  %8.3fs xy->th,azm   %8.3fs fill map     %8.3fs \
960        \n binning      %8.3fs cleanup      %8.3fs'%(times[0],times[1],times[2],times[3],times[4])
961    print "Elapsed time:","%8.3fs"%(time.time()-tbeg)
962    print 'Integration complete'
963    if returnN:     #As requested by Steven Weigand
964        return H0,H1,H2,NST,CancelPressed
965    else:
966        return H0,H1,H2,CancelPressed
967   
968def MakeStrStaRing(ring,Image,Controls):
969    ellipse = GetEllipse(ring['Dset'],Controls)
970    pixSize = Controls['pixelSize']
971    scalex = 1000./pixSize[0]
972    scaley = 1000./pixSize[1]
973    Ring = np.array(makeRing(ring['Dset'],ellipse,ring['pixLimit'],ring['cutoff'],scalex,scaley,Image)[0]).T   #returns x,y,dsp for each point in ring
974    if len(Ring):
975        ring['ImxyObs'] = copy.copy(Ring[:2])
976        TA = GetTthAzm(Ring[0],Ring[1],Controls)       #convert x,y to tth,azm
977        TA[0] = Controls['wavelength']/(2.*npsind(TA[0]/2.))      #convert 2th to d
978        ring['ImtaObs'] = TA
979        ring['ImtaCalc'] = np.zeros_like(ring['ImtaObs'])
980        Ring[0] = TA[0]
981        Ring[1] = TA[1]
982        return Ring,ring
983    else:
984        ring['ImxyObs'] = [[],[]]
985        ring['ImtaObs'] = [[],[]]
986        ring['ImtaCalc'] = [[],[]]
987        return [],[]    #bad ring; no points found
988   
989def FitStrSta(Image,StrSta,Controls):
990    'Needs a doc string'
991   
992    StaControls = copy.deepcopy(Controls)
993    phi = StrSta['Sample phi']
994    wave = Controls['wavelength']
995    pixelSize = Controls['pixelSize']
996    scalex = 1000./pixelSize[0]
997    scaley = 1000./pixelSize[1]
998    StaType = StrSta['Type']
999    StaControls['distance'] += StrSta['Sample z']*cosd(phi)
1000
1001    for ring in StrSta['d-zero']:       #get observed x,y,d points for the d-zeros
1002        dset = ring['Dset']
1003        Ring,R = MakeStrStaRing(ring,Image,StaControls)
1004        if len(Ring):
1005            ring.update(R)
1006            p0 = ring['Emat']
1007            val,esd = FitStrain(Ring,p0,dset,wave,phi,StaType)
1008            ring['Emat'] = val
1009            ring['Esig'] = esd
1010            ellipse = FitEllipse(R['ImxyObs'].T)
1011            ringxy,ringazm = makeRing(ring['Dcalc'],ellipse,0,0.,scalex,scaley,Image)
1012            ring['ImxyCalc'] = np.array(ringxy).T[:2]
1013            ringint = np.array([float(Image[int(x*scalex),int(y*scaley)]) for y,x in np.array(ringxy)[:,:2]])
1014            ringint /= np.mean(ringint)
1015            ring['Ivar'] = np.var(ringint)
1016            print 'Variance in normalized ring intensity: %.3f'%(ring['Ivar'])
1017    CalcStrSta(StrSta,Controls)
1018   
1019def IntStrSta(Image,StrSta,Controls):
1020    StaControls = copy.deepcopy(Controls)
1021    pixelSize = Controls['pixelSize']
1022    scalex = 1000./pixelSize[0]
1023    scaley = 1000./pixelSize[1]
1024    phi = StrSta['Sample phi']
1025    StaControls['distance'] += StrSta['Sample z']*cosd(phi)
1026    RingsAI = []
1027    for ring in StrSta['d-zero']:       #get observed x,y,d points for the d-zeros
1028        Ring,R = MakeStrStaRing(ring,Image,StaControls)
1029        if len(Ring):
1030            ellipse = FitEllipse(R['ImxyObs'].T)
1031            ringxy,ringazm = makeRing(ring['Dcalc'],ellipse,0,0.,scalex,scaley,Image)
1032            ring['ImxyCalc'] = np.array(ringxy).T[:2]
1033            ringint = np.array([float(Image[int(x*scalex),int(y*scaley)]) for y,x in np.array(ringxy)[:,:2]])
1034            ringint /= np.mean(ringint)
1035            print ' %s %.3f %s %.3f'%('d-spacing',ring['Dcalc'],'sig(MRD):',np.sqrt(np.var(ringint)))
1036            RingsAI.append(np.array(zip(ringazm,ringint)).T)
1037    return RingsAI
1038   
1039def CalcStrSta(StrSta,Controls):
1040
1041    wave = Controls['wavelength']
1042    phi = StrSta['Sample phi']
1043    StaType = StrSta['Type']
1044    for ring in StrSta['d-zero']:
1045        Eij = ring['Emat']
1046        E = [[Eij[0],Eij[1],0],[Eij[1],Eij[2],0],[0,0,0]]
1047        th,azm = ring['ImtaObs']
1048        th0 = np.ones_like(azm)*npasind(wave/(2.*ring['Dset']))
1049        V = -np.sum(np.sum(E*calcFij(90.,phi,azm,th0).T/1.e6,axis=2),axis=1)
1050        if StaType == 'True':
1051            ring['ImtaCalc'] = np.array([np.exp(V)*ring['Dset'],azm])
1052        else:
1053            ring['ImtaCalc'] = np.array([(V+1.)*ring['Dset'],azm])
1054        dmin = np.min(ring['ImtaCalc'][0])
1055        dmax = np.max(ring['ImtaCalc'][0])
1056        if ring.get('fixDset',True):
1057            if abs(Eij[0]) < abs(Eij[2]):         #tension
1058                ring['Dcalc'] = dmin+(dmax-dmin)/4.
1059            else:                       #compression
1060                ring['Dcalc'] = dmin+3.*(dmax-dmin)/4.
1061        else:
1062            ring['Dcalc'] = np.mean(ring['ImtaCalc'][0])
1063
1064def calcFij(omg,phi,azm,th):
1065    '''    Uses parameters as defined by Bob He & Kingsley Smith, Adv. in X-Ray Anal. 41, 501 (1997)
1066
1067    :param omg: his omega = sample omega rotation; 0 when incident beam || sample surface,
1068        90 when perp. to sample surface
1069    :param phi: his phi = sample phi rotation; usually = 0, axis rotates with omg.
1070    :param azm: his chi = azimuth around incident beam
1071    :param th:  his theta = theta
1072    '''
1073    a = npsind(th)*npcosd(omg)+npsind(azm)*npcosd(th)*npsind(omg)
1074    b = -npcosd(azm)*npcosd(th)
1075    c = npsind(th)*npsind(omg)-npsind(azm)*npcosd(th)*npcosd(omg)
1076    d = a*npsind(phi)+b*npcosd(phi)
1077    e = a*npcosd(phi)-b*npsind(phi)
1078    Fij = np.array([
1079        [d**2,d*e,c*d],
1080        [d*e,e**2,c*e],
1081        [c*d,c*e,c**2]])
1082    return -Fij
1083
1084def FitStrain(rings,p0,dset,wave,phi,StaType):
1085    'Needs a doc string'
1086    def StrainPrint(ValSig,dset):
1087        print 'Strain tensor for Dset: %.6f'%(dset)
1088        ptlbls = 'names :'
1089        ptstr =  'values:'
1090        sigstr = 'esds  :'
1091        for name,fmt,value,sig in ValSig:
1092            ptlbls += "%s" % (name.rjust(12))
1093            ptstr += fmt % (value)
1094            if sig:
1095                sigstr += fmt % (sig)
1096            else:
1097                sigstr += 12*' '
1098        print ptlbls
1099        print ptstr
1100        print sigstr
1101       
1102    def strainCalc(p,xyd,dset,wave,phi,StaType):
1103        E = np.array([[p[0],p[1],0],[p[1],p[2],0],[0,0,0]])
1104        dspo,azm,dsp = xyd
1105        th = npasind(wave/(2.0*dspo))
1106        V = -np.sum(np.sum(E*calcFij(90.,phi,azm,th).T/1.e6,axis=2),axis=1)
1107        if StaType == 'True':
1108            dspc = dset*np.exp(V)
1109        else:
1110            dspc = dset*(V+1.)
1111        return dspo-dspc
1112       
1113    names = ['e11','e12','e22']
1114    fmt = ['%12.2f','%12.2f','%12.2f']
1115    result = leastsq(strainCalc,p0,args=(rings,dset,wave,phi,StaType),full_output=True)
1116    vals = list(result[0])
1117    chisq = np.sum(result[2]['fvec']**2)/(rings.shape[1]-3)     #reduced chi^2 = M/(Nobs-Nvar)
1118    sig = list(np.sqrt(chisq*np.diag(result[1])))
1119    ValSig = zip(names,fmt,vals,sig)
1120    StrainPrint(ValSig,dset)
1121    return vals,sig
1122   
1123def AutoSpotMasks(Image,Masks,Controls):
1124    print 'auto spot search'
1125    maxPks = 500
1126    dmax = 1.5         #just beyond diamond 111 @ 2.0596
1127    wave = Controls['wavelength']
1128    tthMax = 2.0*asind(wave/(2.*dmax))
1129    pixelSize = Controls['pixelSize']
1130    lap = np.array([[0,1,0],[1,-4,1],[0,1,0]],np.float32)
1131    ck = scsg.cspline2d(Image.T,24.0)
1132    spotMask1=-scsg.convolve2d(ck,lap,mode='same',boundary='symm')
1133    for mul in range(10):
1134        spotMask = ma.array(Image,mask=(spotMask1<(mul+1)*1.e4))
1135        indx = np.transpose(spotMask.nonzero())
1136        if not len(indx):       #smooth image - no sharp points
1137            break
1138        pts,distort = scvq.kmeans(np.array(indx,np.float32),maxPks,thresh=1.0)
1139        peaks = pts*pixelSize/1000.
1140        tths = GetTth(peaks.T[0],peaks.T[1],Controls)
1141        masktths = ma.getmask(ma.array(tths,mask=tths>tthMax))
1142        pts = pts[masktths,:]
1143        A,B = np.mgrid[0:len(pts),0:len(pts)]   
1144        M = np.sqrt(np.sum((pts[A]-pts[B])**2,axis=-1))
1145        MX = ma.array(M,mask=(M<10.))
1146        Indx = ma.nonzero(ma.getmask(MX))
1147        print 'Spots found: ',mul,len(pts),distort,len(Indx[0])
1148        if distort < .3:
1149            break
1150    if not len(Indx[0]):
1151        print 'no auto search spots found'
1152        return None
1153    if distort > 2.:
1154        print 'no big spots found'
1155        return None
1156    #use clustered points to get position & spread
1157   
1158    Indx = np.sort(Indx,axis=0).T
1159    Nmax = np.max(Indx)
1160    nums = [list(set([ind[1] for ind in Indx if ind[1]>=jnd and ind[0] == jnd])) for jnd in range(Nmax+1)]
1161    delList = []
1162    for Num in nums:
1163        delList += list(np.sort(Num))[1:]
1164    delList = list(set(delList))
1165    Nums = [num for inum,num in enumerate(nums) if inum not in delList]
1166    points = []
1167    esds = []
1168    for num in Nums:
1169        points.append(np.mean(pts[num],axis=0))
1170        esds.append(np.mean(np.var(pts[num],axis=0)))
1171    peaks = (np.array(points)+np.array([.5,.5]))*pixelSize/1000.
1172    Points = np.ones((peaks.shape[0],3))*2.
1173    Points[:,2] += np.array(esds)*pixelSize[0]/1000.
1174    Points[:,:2] = peaks
1175    Masks['Points'] = list(Points)
1176    return None
1177
1178#    rollImage = lambda rho,roll: np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1)
1179#    print 'auto spot search'
1180#    pixelSize = Controls['pixelSize']
1181#    spotMask = ma.array(Image,mask=(Image<np.mean(Image)))
1182#    indices = (-1,0,1)
1183#    rolls = np.array([[ix,iy] for ix in indices for iy in indices])
1184#    for roll in rolls:
1185#        if np.any(roll):        #avoid [0,0]
1186#            spotMask = ma.array(spotMask,mask=(spotMask-rollImage(Image,roll)<0.))
1187#    mags = spotMask[spotMask.nonzero()]
1188#    indx = np.transpose(spotMask.nonzero())
1189#    nx,ny = Image.shape
1190#    jndx = []
1191#    for [ind,mag] in zip(indx,mags):
1192#        if (1 < ind[0] < nx-2) and (1 < ind[1] < ny-2):
1193#            cent = np.zeros((3,3))
1194#            cent[1,1] = mag
1195#            msk = np.array(Image[ind[0]-2:ind[0]+3,ind[1]-2:ind[1]+3])
1196#            msk = ma.array(msk,mask=(msk==mag))
1197#            if mag > 1.5*ma.mean(msk):
1198#                jndx.append([ind[1]+.5,ind[0]+.5])
1199#    print 'Spots found: ',len(jndx)
1200#    jndx = np.array(jndx)
1201#    peaks = jndx*pixelSize/1000.
1202#    tth = GetTth(peaks.T[0],peaks.T[1],Controls)
1203#    histth,bins = np.histogram(tth,2500)
1204#    for shft in [-1,1]:
1205#        histmsk = ma.array(histth,mask=-(histth-np.roll(histth,shft)<2.))
1206#    histmsk = ma.array(histmsk,mask=(histmsk>5))
1207#    binmsk = np.array(ma.nonzero(histmsk))
1208#    digts = np.digitize(tth,bins)-1
1209#    PeaksList = []
1210#    for digt,peak in zip(digts,peaks):
1211#        if digt in binmsk:
1212#            PeaksList.append(peak)
1213#    #should be able to filter out spotty Bragg rings here
1214#    PeaksList = np.array(PeaksList)
1215#    Points = np.ones((PeaksList.shape[0],3))
1216#    Points[:,:2] = PeaksList
1217#    Masks['Points'] = list(Points)
1218#    return None
1219       
1220                   
1221   
1222       
Note: See TracBrowser for help on using the repository browser.