source: trunk/GSASIIimage.py @ 3154

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

new & imporved spot mask routines:
manual spot mask now fits position & size for selected spot.
changed mouse controls LB drag moves spot, shift-LB changes size & RB deletes spot
autospotmask improved - new algorithm

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 50.6 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII image calculations: ellipse fitting & image integration       
3########### SVN repository information ###################
4# $Date: 2017-11-19 00:14:55 +0000 (Sun, 19 Nov 2017) $
5# $Author: vondreele $
6# $Revision: 3154 $
7# $URL: trunk/GSASIIimage.py $
8# $Id: GSASIIimage.py 3154 2017-11-19 00:14:55Z 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: 3154 $")
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,masks,iLim,jLim,tamp,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    #make position masks here
831    frame = masks['Frames']
832    tam = ma.make_mask_none((nI*nJ))
833    if frame:
834        tam = ma.mask_or(tam,ma.make_mask(pm.polymask(nI*nJ,tax,
835            tay,len(frame),frame,tamp)[:nI*nJ])-True)
836    polygons = masks['Polygons']
837    for polygon in polygons:
838        if polygon:
839            tam = ma.mask_or(tam,ma.make_mask(pm.polymask(nI*nJ,tax,
840                tay,len(polygon),polygon,tamp)[:nI*nJ]))
841    if True:
842#    try:
843#        import spotmask as sm
844#        spots = masks['Points'].T
845#        if len(spots):
846#            tam = ma.mask_or(tam,ma.getmask(sm.spotmask(nI*nJ,tax,
847#                    tay,len(spots),spots,tamp)[:nI*nJ]))
848#    except:
849        for X,Y,rsq in masks['Points'].T:
850            tam = ma.mask_or(tam,ma.getmask(ma.masked_less((tax-X)**2+(tay-Y)**2,rsq)))
851    if tam.shape: tam = np.reshape(tam,(nI,nJ))
852    times[0] += time.time()-t0
853    t0 = time.time()
854    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
855    times[1] += time.time()-t0
856    TA[1] = np.where(TA[1]<0,TA[1]+360,TA[1])
857    return np.array(TA),tam           #2-theta, azimuth & geom. corr. arrays & position mask
858
859def Fill2ThetaAzimuthMap(masks,TA,tam,image):
860    'Needs a doc string'
861    Zlim = masks['Thresholds'][1]
862    rings = masks['Rings']
863    arcs = masks['Arcs']
864    TA = np.dstack((ma.getdata(TA[1]),ma.getdata(TA[0]),ma.getdata(TA[2])))    #azimuth, 2-theta, dist
865    tax,tay,tad = np.dsplit(TA,3)    #azimuth, 2-theta, dist**2/d0**2
866    for tth,thick in rings:
867        tam = ma.mask_or(tam.flatten(),ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.)))
868    for tth,azm,thick in arcs:
869        tamt = ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.))
870        tama = ma.getmask(ma.masked_inside(tax.flatten(),azm[0],azm[1]))
871        tam = ma.mask_or(tam.flatten(),tamt*tama)
872    taz = ma.masked_outside(image.flatten(),int(Zlim[0]),Zlim[1])
873    tabs = np.ones_like(taz)
874    tam = ma.mask_or(tam.flatten(),ma.getmask(taz))
875    tax = ma.compressed(ma.array(tax.flatten(),mask=tam))   #azimuth
876    tay = ma.compressed(ma.array(tay.flatten(),mask=tam))   #2-theta
877    taz = ma.compressed(ma.array(taz.flatten(),mask=tam))   #intensity
878    tad = ma.compressed(ma.array(tad.flatten(),mask=tam))   #dist**2/d0**2
879    tabs = ma.compressed(ma.array(tabs.flatten(),mask=tam)) #ones - later used for absorption corr.
880    return tax,tay,taz,tad,tabs
881
882def ImageIntegrate(image,data,masks,blkSize=128,returnN=False):
883    'Integrate an image; called from OnIntegrateAll and OnIntegrate in G2imgGUI'    #for q, log(q) bins need data['binType']
884    import histogram2d as h2d
885    print ('Begin image integration')
886    CancelPressed = False
887    LUtth = np.array(data['IOtth'])
888    LRazm = np.array(data['LRazimuth'],dtype=np.float64)
889    numAzms = data['outAzimuths']
890    numChans = data['outChannels']
891    Dazm = (LRazm[1]-LRazm[0])/numAzms
892    if '2-theta' in data.get('binType','2-theta'):
893        lutth = LUtth               
894    elif 'log(q)' in data['binType']:
895        lutth = np.log(4.*np.pi*npsind(LUtth/2.)/data['wavelength'])
896    elif 'q' == data['binType'].lower():
897        lutth = 4.*np.pi*npsind(LUtth/2.)/data['wavelength']
898    dtth = (lutth[1]-lutth[0])/numChans
899    muT = data.get('SampleAbs',[0.0,''])[0]
900    if data['DetDepth'] > 0.5:          #patch - redefine DetDepth
901        data['DetDepth'] /= data['distance']
902    if 'SASD' in data['type']:
903        muT = -np.log(muT)/2.       #Transmission to 1/2 thickness muT
904    Masks = copy.deepcopy(masks)
905    Masks['Points'] = np.array(Masks['Points']).T           #get spots as X,Y,R arrays
906    if np.any(masks['Points']):
907        Masks['Points'][2] = np.square(Masks['Points'][2]/2.)
908    NST = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
909    H0 = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
910    Nx,Ny = data['size']
911    nXBlks = (Nx-1)//blkSize+1
912    nYBlks = (Ny-1)//blkSize+1
913    tbeg = time.time()
914    times = [0,0,0,0,0]
915    tamp = ma.make_mask_none((1024*1024))       #NB: this array size used in the fortran histogram2d
916    for iBlk in range(nYBlks):
917        iBeg = iBlk*blkSize
918        iFin = min(iBeg+blkSize,Ny)
919        for jBlk in range(nXBlks):
920            jBeg = jBlk*blkSize
921            jFin = min(jBeg+blkSize,Nx)
922            # next is most expensive step!
923            TA,tam = Make2ThetaAzimuthMap(data,Masks,(iBeg,iFin),(jBeg,jFin),tamp,times)           #2-theta & azimuth arrays & create position mask
924            Block = image[iBeg:iFin,jBeg:jFin]
925            t0 = time.time()
926            tax,tay,taz,tad,tabs = Fill2ThetaAzimuthMap(Masks,TA,tam,Block)    #and apply masks
927            del TA; del tam
928            times[2] += time.time()-t0
929            tax = np.where(tax > LRazm[1],tax-360.,tax)                 #put azm inside limits if possible
930            tax = np.where(tax < LRazm[0],tax+360.,tax)
931            if data.get('SampleAbs',[0.0,''])[1]:
932                if 'Cylind' in data['SampleShape']:
933                    muR = muT*(1.+npsind(tax)**2/2.)/(npcosd(tay))      #adjust for additional thickness off sample normal
934                    tabs = G2pwd.Absorb(data['SampleShape'],muR,tay)
935                elif 'Fixed' in data['SampleShape']:    #assumes flat plate sample normal to beam
936                    tabs = G2pwd.Absorb('Fixed',muT,tay)
937            if 'log(q)' in data.get('binType',''):
938                tay = np.log(4.*np.pi*npsind(tay/2.)/data['wavelength'])
939            elif 'q' == data.get('binType','').lower():
940                tay = 4.*np.pi*npsind(tay/2.)/data['wavelength']
941            t0 = time.time()
942            taz = np.array((taz*tad/tabs),dtype='float32')
943            if any([tax.shape[0],tay.shape[0],taz.shape[0]]):
944                NST,H0 = h2d.histogram2d(len(tax),tax,tay,taz,
945                    numAzms,numChans,LRazm,lutth,Dazm,dtth,NST,H0)
946            times[3] += time.time()-t0
947            del tax; del tay; del taz; del tad; del tabs
948    t0 = time.time()
949    H2 = np.array([tth for tth in np.linspace(lutth[0],lutth[1],numChans+1)])
950    NST = np.array(NST,dtype=np.float)
951    #prepare masked arrays of bins with pixels for interpolation setup
952    H2msk = [ma.array(H2[:-1],mask=np.logical_not(nst)) for nst in NST]
953    H0msk = [ma.array(np.divide(h0,nst),mask=np.logical_not(nst)) for nst,h0 in zip(NST,H0)]
954    #make linear interpolators; outside limits give NaN
955    H0int = [scint.interp1d(h2msk.compressed(),h0msk.compressed(),bounds_error=False) for h0msk,h2msk in zip(H0msk,H2msk)]
956    #do interpolation on all points - fills in the empty bins; leaves others the same
957    H0 = np.array([h0int(H2[:-1]) for h0int in H0int])
958    H0 = np.nan_to_num(H0)
959    if 'log(q)' in data.get('binType',''):
960        H2 = 2.*npasind(np.exp(H2)*data['wavelength']/(4.*np.pi))
961    elif 'q' == data.get('binType','').lower():
962        H2 = 2.*npasind(H2*data['wavelength']/(4.*np.pi))
963    if Dazm:       
964        H1 = np.array([azm for azm in np.linspace(LRazm[0],LRazm[1],numAzms+1)])
965    else:
966        H1 = LRazm
967    H0 /= npcosd(H2[:-1])           #**2? I don't think so, **1 is right for powders
968    if 'SASD' in data['type']:
969        H0 /= npcosd(H2[:-1])           #one more for small angle scattering data?
970    if data['Oblique'][1]:
971        H0 /= G2pwd.Oblique(data['Oblique'][0],H2[:-1])
972    if 'SASD' in data['type'] and data['PolaVal'][1]:
973        #NB: in G2pwd.Polarization azm is defined from plane of polarization, not image x axis!
974        H0 /= np.array([G2pwd.Polarization(data['PolaVal'][0],H2[:-1],Azm=azm-90.)[0] for azm in (H1[:-1]+np.diff(H1)/2.)])
975    times[4] += time.time()-t0
976    print ('Step times: \n apply masks  %8.3fs xy->th,azm   %8.3fs fill map     %8.3fs \
977        \n binning      %8.3fs cleanup      %8.3fs'%(times[0],times[1],times[2],times[3],times[4]))
978    print ("Elapsed time:","%8.3fs"%(time.time()-tbeg))
979    print ('Integration complete')
980    if returnN:     #As requested by Steven Weigand
981        return H0,H1,H2,NST,CancelPressed
982    else:
983        return H0,H1,H2,CancelPressed
984   
985def MakeStrStaRing(ring,Image,Controls):
986    ellipse = GetEllipse(ring['Dset'],Controls)
987    pixSize = Controls['pixelSize']
988    scalex = 1000./pixSize[0]
989    scaley = 1000./pixSize[1]
990    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
991    if len(Ring):
992        ring['ImxyObs'] = copy.copy(Ring[:2])
993        TA = GetTthAzm(Ring[0],Ring[1],Controls)       #convert x,y to tth,azm
994        TA[0] = Controls['wavelength']/(2.*npsind(TA[0]/2.))      #convert 2th to d
995        ring['ImtaObs'] = TA
996        ring['ImtaCalc'] = np.zeros_like(ring['ImtaObs'])
997        Ring[0] = TA[0]
998        Ring[1] = TA[1]
999        return Ring,ring
1000    else:
1001        ring['ImxyObs'] = [[],[]]
1002        ring['ImtaObs'] = [[],[]]
1003        ring['ImtaCalc'] = [[],[]]
1004        return [],[]    #bad ring; no points found
1005   
1006def FitStrSta(Image,StrSta,Controls):
1007    'Needs a doc string'
1008   
1009    StaControls = copy.deepcopy(Controls)
1010    phi = StrSta['Sample phi']
1011    wave = Controls['wavelength']
1012    pixelSize = Controls['pixelSize']
1013    scalex = 1000./pixelSize[0]
1014    scaley = 1000./pixelSize[1]
1015    StaType = StrSta['Type']
1016    StaControls['distance'] += StrSta['Sample z']*cosd(phi)
1017
1018    for ring in StrSta['d-zero']:       #get observed x,y,d points for the d-zeros
1019        dset = ring['Dset']
1020        Ring,R = MakeStrStaRing(ring,Image,StaControls)
1021        if len(Ring):
1022            ring.update(R)
1023            p0 = ring['Emat']
1024            val,esd,covMat = FitStrain(Ring,p0,dset,wave,phi,StaType)
1025            ring['Emat'] = val
1026            ring['Esig'] = esd
1027            ellipse = FitEllipse(R['ImxyObs'].T)
1028            ringxy,ringazm = makeRing(ring['Dcalc'],ellipse,0,0.,scalex,scaley,Image)
1029            ring['ImxyCalc'] = np.array(ringxy).T[:2]
1030            ringint = np.array([float(Image[int(x*scalex),int(y*scaley)]) for y,x in np.array(ringxy)[:,:2]])
1031            ringint /= np.mean(ringint)
1032            ring['Ivar'] = np.var(ringint)
1033            ring['covMat'] = covMat
1034            print ('Variance in normalized ring intensity: %.3f'%(ring['Ivar']))
1035    CalcStrSta(StrSta,Controls)
1036   
1037def IntStrSta(Image,StrSta,Controls):
1038    StaControls = copy.deepcopy(Controls)
1039    pixelSize = Controls['pixelSize']
1040    scalex = 1000./pixelSize[0]
1041    scaley = 1000./pixelSize[1]
1042    phi = StrSta['Sample phi']
1043    StaControls['distance'] += StrSta['Sample z']*cosd(phi)
1044    RingsAI = []
1045    for ring in StrSta['d-zero']:       #get observed x,y,d points for the d-zeros
1046        Ring,R = MakeStrStaRing(ring,Image,StaControls)
1047        if len(Ring):
1048            ellipse = FitEllipse(R['ImxyObs'].T)
1049            ringxy,ringazm = makeRing(ring['Dcalc'],ellipse,0,0.,scalex,scaley,Image)
1050            ring['ImxyCalc'] = np.array(ringxy).T[:2]
1051            ringint = np.array([float(Image[int(x*scalex),int(y*scaley)]) for y,x in np.array(ringxy)[:,:2]])
1052            ringint /= np.mean(ringint)
1053            print (' %s %.3f %s %.3f'%('d-spacing',ring['Dcalc'],'sig(MRD):',np.sqrt(np.var(ringint))))
1054            RingsAI.append(np.array(zip(ringazm,ringint)).T)
1055    return RingsAI
1056   
1057def CalcStrSta(StrSta,Controls):
1058
1059    wave = Controls['wavelength']
1060    phi = StrSta['Sample phi']
1061    StaType = StrSta['Type']
1062    for ring in StrSta['d-zero']:
1063        Eij = ring['Emat']
1064        E = [[Eij[0],Eij[1],0],[Eij[1],Eij[2],0],[0,0,0]]
1065        th,azm = ring['ImtaObs']
1066        th0 = np.ones_like(azm)*npasind(wave/(2.*ring['Dset']))
1067        V = -np.sum(np.sum(E*calcFij(90.,phi,azm,th0).T/1.e6,axis=2),axis=1)
1068        if StaType == 'True':
1069            ring['ImtaCalc'] = np.array([np.exp(V)*ring['Dset'],azm])
1070        else:
1071            ring['ImtaCalc'] = np.array([(V+1.)*ring['Dset'],azm])
1072        dmin = np.min(ring['ImtaCalc'][0])
1073        dmax = np.max(ring['ImtaCalc'][0])
1074        if ring.get('fixDset',True):
1075            if abs(Eij[0]) < abs(Eij[2]):         #tension
1076                ring['Dcalc'] = dmin+(dmax-dmin)/4.
1077            else:                       #compression
1078                ring['Dcalc'] = dmin+3.*(dmax-dmin)/4.
1079        else:
1080            ring['Dcalc'] = np.mean(ring['ImtaCalc'][0])
1081
1082def calcFij(omg,phi,azm,th):
1083    '''    Uses parameters as defined by Bob He & Kingsley Smith, Adv. in X-Ray Anal. 41, 501 (1997)
1084
1085    :param omg: his omega = sample omega rotation; 0 when incident beam || sample surface,
1086        90 when perp. to sample surface
1087    :param phi: his phi = sample phi rotation; usually = 0, axis rotates with omg.
1088    :param azm: his chi = azimuth around incident beam
1089    :param th:  his theta = theta
1090    '''
1091    a = npsind(th)*npcosd(omg)+npsind(azm)*npcosd(th)*npsind(omg)
1092    b = -npcosd(azm)*npcosd(th)
1093    c = npsind(th)*npsind(omg)-npsind(azm)*npcosd(th)*npcosd(omg)
1094    d = a*npsind(phi)+b*npcosd(phi)
1095    e = a*npcosd(phi)-b*npsind(phi)
1096    Fij = np.array([
1097        [d**2,d*e,c*d],
1098        [d*e,e**2,c*e],
1099        [c*d,c*e,c**2]])
1100    return -Fij
1101
1102def FitStrain(rings,p0,dset,wave,phi,StaType):
1103    'Needs a doc string'
1104    def StrainPrint(ValSig,dset):
1105        print ('Strain tensor for Dset: %.6f'%(dset))
1106        ptlbls = 'names :'
1107        ptstr =  'values:'
1108        sigstr = 'esds  :'
1109        for name,fmt,value,sig in ValSig:
1110            ptlbls += "%s" % (name.rjust(12))
1111            ptstr += fmt % (value)
1112            if sig:
1113                sigstr += fmt % (sig)
1114            else:
1115                sigstr += 12*' '
1116        print (ptlbls)
1117        print (ptstr)
1118        print (sigstr)
1119       
1120    def strainCalc(p,xyd,dset,wave,phi,StaType):
1121        E = np.array([[p[0],p[1],0],[p[1],p[2],0],[0,0,0]])
1122        dspo,azm,dsp = xyd
1123        th = npasind(wave/(2.0*dspo))
1124        V = -np.sum(np.sum(E*calcFij(90.,phi,azm,th).T/1.e6,axis=2),axis=1)
1125        if StaType == 'True':
1126            dspc = dset*np.exp(V)
1127        else:
1128            dspc = dset*(V+1.)
1129        return dspo-dspc
1130       
1131    names = ['e11','e12','e22']
1132    fmt = ['%12.2f','%12.2f','%12.2f']
1133    result = leastsq(strainCalc,p0,args=(rings,dset,wave,phi,StaType),full_output=True)
1134    vals = list(result[0])
1135    chisq = np.sum(result[2]['fvec']**2)/(rings.shape[1]-3)     #reduced chi^2 = M/(Nobs-Nvar)
1136    covM = result[1]
1137    covMatrix = covM*chisq
1138    sig = list(np.sqrt(chisq*np.diag(result[1])))
1139    ValSig = zip(names,fmt,vals,sig)
1140    StrainPrint(ValSig,dset)
1141    return vals,sig,covMatrix
1142
1143def FitImageSpots(Image,ImMax,ind,pixSize,nxy):
1144   
1145    def calcMean(nxy,pixSize,img):
1146        gdx,gdy = np.mgrid[0:nxy,0:nxy]
1147        gdx = ma.array((gdx-nxy//2)*pixSize[0]/1000.,mask=~ma.getmaskarray(ImBox))
1148        gdy = ma.array((gdy-nxy//2)*pixSize[1]/1000.,mask=~ma.getmaskarray(ImBox))
1149        posx = ma.sum(gdx)/ma.count(gdx)
1150        posy = ma.sum(gdy)/ma.count(gdy)
1151        return posx,posy
1152   
1153    def calcPeak(values,nxy,pixSize,img):
1154        back,mag,px,py,sig = values
1155        peak = np.zeros([nxy,nxy])+back
1156        nor = 1./(2.*np.pi*sig**2)
1157        gdx,gdy = np.mgrid[0:nxy,0:nxy]
1158        gdx = (gdx-nxy//2)*pixSize[0]/1000.
1159        gdy = (gdy-nxy//2)*pixSize[1]/1000.
1160        arg = (gdx-px)**2+(gdy-py)**2       
1161        peak += mag*nor*np.exp(-arg/(2.*sig**2))
1162        return ma.compressed(img-peak)/np.sqrt(ma.compressed(img))
1163   
1164    def calc2Peak(values,nxy,pixSize,img):
1165        back,mag,px,py,sigx,sigy,rho = values
1166        peak = np.zeros([nxy,nxy])+back
1167        nor = 1./(2.*np.pi*sigx*sigy*np.sqrt(1.-rho**2))
1168        gdx,gdy = np.mgrid[0:nxy,0:nxy]
1169        gdx = (gdx-nxy//2)*pixSize[0]/1000.
1170        gdy = (gdy-nxy//2)*pixSize[1]/1000.
1171        argnor = -1./(2.*(1.-rho**2))
1172        arg = (gdx-px)**2/sigx**2+(gdy-py)**2/sigy**2-2.*rho*(gdx-px)*(gdy-py)/(sigx*sigy)       
1173        peak += mag*nor*np.exp(argnor*arg)
1174        return ma.compressed(img-peak)/np.sqrt(ma.compressed(img))       
1175   
1176    nxy2 = nxy//2
1177    ImBox = Image[ind[1]-nxy2:ind[1]+nxy2+1,ind[0]-nxy2:ind[0]+nxy2+1]
1178    back = np.min(ImBox)
1179    mag = np.sum(ImBox-back)
1180    vals = [back,mag,0.,0.,0.2,0.2,0.]
1181    ImBox = ma.array(ImBox,dtype=float,mask=ImBox>0.75*ImMax)
1182    px = (ind[0]+.5)*pixSize[0]/1000.
1183    py = (ind[1]+.5)*pixSize[1]/1000.
1184    if ma.any(ma.getmaskarray(ImBox)):
1185        vals = calcMean(nxy,pixSize,ImBox)
1186        posx,posy = [px+vals[0],py+vals[1]]
1187        return [posx,posy,6.]
1188    else:
1189        result = leastsq(calc2Peak,vals,args=(nxy,pixSize,ImBox),full_output=True)
1190        vals = result[0]
1191        ratio = vals[4]/vals[5]
1192        if 0.5 < ratio < 2.0 and vals[2] < 2. and vals[3] < 2.:
1193            posx,posy = [px+vals[2],py+vals[3]]
1194            return [posx,posy,min(6.*vals[4],6.)]
1195        else:
1196            return None
1197   
1198def AutoSpotMasks(Image,Masks,Controls):
1199   
1200    print ('auto spot search')
1201    nxy = 15
1202    rollImage = lambda rho,roll: np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1)
1203    pixelSize = Controls['pixelSize']
1204    spotMask = ma.array(Image,mask=(Image<np.mean(Image)))
1205    indices = (-1,0,1)
1206    rolls = np.array([[ix,iy] for ix in indices for iy in indices])
1207    time0 = time.time()
1208    for roll in rolls:
1209        if np.any(roll):        #avoid [0,0]
1210            spotMask = ma.array(spotMask,mask=(spotMask-rollImage(Image,roll)<0.),dtype=float)
1211    mags = spotMask[spotMask.nonzero()]
1212    indx = np.transpose(spotMask.nonzero())
1213    size1 = mags.shape[0]
1214    magind = [[indx[0][0],indx[0][1],mags[0]],]
1215    for ind,mag in list(zip(indx,mags))[1:]:        #remove duplicates
1216#            ind[0],ind[1],I,J = ImageLocalMax(Image,nxy,ind[0],ind[1])
1217        if (magind[-1][0]-ind[0])**2+(magind[-1][1]-ind[1])**2 > 16:
1218            magind.append([ind[0],ind[1],Image[ind[0],ind[1]]]) 
1219    magind = np.array(magind).T
1220    indx = np.array(magind[0:2],dtype=np.int32)
1221    mags = magind[2]
1222    size2 = mags.shape[0]
1223    print ('Initial search done: %d -->%d %.2fs'%(size1,size2,time.time()-time0))
1224    nx,ny = Image.shape
1225    ImMax = np.max(Image)
1226    peaks = []
1227    nxy2 = nxy//2
1228    mult = 0.001
1229    num = 1e6
1230    while num>500:
1231        mult += .0001           
1232        minM = mult*np.max(mags)
1233        num = ma.count(ma.array(mags,mask=mags<=minM))
1234        print('try',mult,minM,num)
1235    minM = mult*np.max(mags)
1236    print ('Find biggest spots:',mult,num,minM)
1237    for i,mag in enumerate(mags):
1238        if mag > minM:
1239            if (nxy2 < indx[0][i] < nx-nxy2-1) and (nxy2 < indx[1][i] < ny-nxy2-1):
1240#                    print ('try:%d %d %d %.2f'%(i,indx[0][i],indx[1][i],mags[i]))
1241                peak = FitImageSpots(Image,ImMax,[indx[1][i],indx[0][i]],pixelSize,nxy)
1242                if peak and not any(np.isnan(np.array(peak))):
1243                    peaks.append(peak)
1244#                    print (' Spot found: %s'%str(peak))
1245    peaks = G2mth.sortArray(G2mth.sortArray(peaks,1),0)
1246    Peaks = [peaks[0],]
1247    for peak in peaks[1:]:
1248        if GetDsp(peak[0],peak[1],Controls) >= 1.:      #toss non-diamond low angle spots
1249            continue
1250        if (peak[0]-Peaks[-1][0])**2+(peak[1]-Peaks[-1][1])**2 > peak[2]*Peaks[-1][2] :
1251            Peaks.append(peak)
1252#            print (' Spot found: %s'%str(peak))
1253    print ('Spots found: %d time %.2fs'%(len(Peaks),time.time()-time0))
1254    Masks['Points'] = Peaks
1255    return None
Note: See TracBrowser for help on using the repository browser.