source: trunk/GSASIIimage.py @ 3173

Last change on this file since 3173 was 3173, checked in by vondreele, 5 years ago

ensure limits on multiple powder patterns from integration are independent
Integrate All & Auto integrate now reuse x,y --> 2th,azm maps if image controls unchanged - much faster multiple integrations

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