source: trunk/GSASIIimage.py @ 1178

Last change on this file since 1178 was 1178, checked in by vondreele, 10 years ago

penetration correction now for tilted detectors - seems to work (but hard to test for lack of sufficiently extreme example)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 35.5 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII image calculations: ellipse fitting & image integration       
3########### SVN repository information ###################
4# $Date: 2013-12-20 15:57:38 +0000 (Fri, 20 Dec 2013) $
5# $Author: vondreele $
6# $Revision: 1178 $
7# $URL: trunk/GSASIIimage.py $
8# $Id: GSASIIimage.py 1178 2013-12-20 15:57:38Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimage: Image calc module*
12================================
13
14Ellipse fitting & image integration
15
16'''
17
18import math
19import time
20import numpy as np
21import numpy.linalg as nl
22import numpy.ma as ma
23import polymask as pm
24from scipy.optimize import leastsq
25import copy
26import GSASIIpath
27GSASIIpath.SetVersionNumber("$Revision: 1178 $")
28import GSASIIplot as G2plt
29import GSASIIlattice as G2lat
30import GSASIIpwd as G2pwd
31import fellipse as fel
32
33# trig functions in degrees
34sind = lambda x: math.sin(x*math.pi/180.)
35asind = lambda x: 180.*math.asin(x)/math.pi
36tand = lambda x: math.tan(x*math.pi/180.)
37atand = lambda x: 180.*math.atan(x)/math.pi
38atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi
39cosd = lambda x: math.cos(x*math.pi/180.)
40acosd = lambda x: 180.*math.acos(x)/math.pi
41rdsq2d = lambda x,p: round(1.0/math.sqrt(x),p)
42#numpy versions
43npsind = lambda x: np.sin(x*np.pi/180.)
44npasind = lambda x: 180.*np.arcsin(x)/np.pi
45npcosd = lambda x: np.cos(x*np.pi/180.)
46npacosd = lambda x: 180.*np.arccos(x)/np.pi
47nptand = lambda x: np.tan(x*np.pi/180.)
48npatand = lambda x: 180.*np.arctan(x)/np.pi
49npatan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi
50   
51def pointInPolygon(pXY,xy):
52    'Needs a doc string'
53    #pXY - assumed closed 1st & last points are duplicates
54    Inside = False
55    N = len(pXY)
56    p1x,p1y = pXY[0]
57    for i in range(N+1):
58        p2x,p2y = pXY[i%N]
59        if (max(p1y,p2y) >= xy[1] > min(p1y,p2y)) and (xy[0] <= max(p1x,p2x)):
60            if p1y != p2y:
61                xinters = (xy[1]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
62            if p1x == p2x or xy[0] <= xinters:
63                Inside = not Inside
64        p1x,p1y = p2x,p2y
65    return Inside
66   
67def peneCorr(tth,dep,tilt=0.,azm=0.):
68    'Needs a doc string'
69    return dep*(1.-npcosd(abs(tilt*npsind(azm))-tth*npcosd(azm)))
70#    return dep*(1.-npcosd(tth))         #best one
71#    return dep*npsind(tth)             #not as good as 1-cos2Q
72       
73def makeMat(Angle,Axis):
74    '''Make rotation matrix from Angle and Axis
75
76    :param float Angle: in degrees
77    :param int Axis: 0 for rotation about x, 1 for about y, etc.
78    '''
79    cs = cosd(Angle)
80    ss = sind(Angle)
81    M = np.array(([1.,0.,0.],[0.,cs,-ss],[0.,ss,cs]),dtype=np.float32)
82    return np.roll(np.roll(M,Axis,axis=0),Axis,axis=1)
83                   
84def FitEllipse(xy):
85   
86    def ellipse_center(p):
87        ''' gives ellipse center coordinates
88        '''
89        b,c,d,f,g,a = p[1]/2, p[2], p[3]/2, p[4]/2, p[5], p[0]
90        num = b*b-a*c
91        x0=(c*d-b*f)/num
92        y0=(a*f-b*d)/num
93        return np.array([x0,y0])
94   
95    def ellipse_angle_of_rotation( p ):
96        ''' gives rotation of ellipse major axis from x-axis
97        range will be -90 to 90 deg
98        '''
99        b,c,d,f,g,a = p[1]/2, p[2], p[3]/2, p[4]/2, p[5], p[0]
100        return 0.5*npatand(2*b/(a-c))
101   
102    def ellipse_axis_length( p ):
103        ''' gives ellipse radii in [minor,major] order
104        '''
105        b,c,d,f,g,a = p[1]/2, p[2], p[3]/2, p[4]/2, p[5], p[0]
106        up = 2*(a*f*f+c*d*d+g*b*b-2*b*d*f-a*c*g)
107        down1=(b*b-a*c)*( (c-a)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a))
108        down2=(b*b-a*c)*( (a-c)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a))
109        res1=np.sqrt(up/down1)
110        res2=np.sqrt(up/down2)
111        return np.array([ res2,res1])
112   
113    xy = np.array(xy)
114    x = np.asarray(xy.T[0])[:,np.newaxis]
115    y = np.asarray(xy.T[1])[:,np.newaxis]
116    D =  np.hstack((x*x, x*y, y*y, x, y, np.ones_like(x)))
117    S = np.dot(D.T,D)
118    C = np.zeros([6,6])
119    C[0,2] = C[2,0] = 2; C[1,1] = -1
120    E, V =  nl.eig(np.dot(nl.inv(S), C))
121    n = np.argmax(np.abs(E))
122    a = V[:,n]
123    cent = ellipse_center(a)
124    phi = ellipse_angle_of_rotation(a)
125    radii = ellipse_axis_length(a)
126    phi += 90.
127    if radii[0] > radii[1]:
128        radii = [radii[1],radii[0]]
129        phi -= 90.
130    return cent,phi,radii
131
132def FitDetector(rings,varyList,parmDict,Print=True):
133    'Needs a doc string'
134       
135    def CalibPrint(ValSig):
136        print 'Image Parameters:'
137        ptlbls = 'names :'
138        ptstr =  'values:'
139        sigstr = 'esds  :'
140        for name,fmt,value,sig in ValSig:
141            ptlbls += "%s" % (name.rjust(12))
142            if name == 'phi':
143                ptstr += fmt % (value%360.)
144            else:
145                ptstr += fmt % (value)
146            if sig:
147                sigstr += fmt % (sig)
148            else:
149                sigstr += 12*' '
150        print ptlbls
151        print ptstr
152        print sigstr       
153       
154    def ellipseCalcD(B,xyd,varyList,parmDict):
155        x,y,dsp = xyd
156        wave = parmDict['wave']
157        if 'dep' in varyList:
158            dist,x0,y0,tilt,chi,dep = B[:6]
159        else:
160            dist,x0,y0,tilt,chi = B[:5]
161            dep = parmDict['dep']
162        if 'wave' in varyList:
163            wave = B[-1]
164        phi = chi-90.               #get rotation of major axis from tilt axis
165        tth = 2.0*npasind(wave/(2.*dsp))
166        phi0 = npatan2d(y-y0,x-x0)
167        dxy = peneCorr(tth,dep,tilt,phi0)
168        ttth = nptand(tth)
169        stth = npsind(tth)
170        cosb = npcosd(tilt)
171        tanb = nptand(tilt)       
172        tbm = nptand((tth-tilt)/2.)
173        tbp = nptand((tth+tilt)/2.)
174        sinb = npsind(tilt)
175        d = dist+dxy
176        fplus = d*tanb*stth/(cosb+stth)
177        fminus = d*tanb*stth/(cosb-stth)
178        vplus = d*(tanb+(1+tbm)/(1-tbm))*stth/(cosb+stth)
179        vminus = d*(tanb+(1-tbp)/(1+tbp))*stth/(cosb-stth)
180        R0 = np.sqrt((vplus+vminus)**2-(fplus+fminus)**2)/2.      #+minor axis
181        R1 = (vplus+vminus)/2.                                    #major axis
182        zdis = (fplus-fminus)/2.
183        Robs = np.sqrt((x-x0)**2+(y-y0)**2)
184        rsqplus = R0**2+R1**2
185        rsqminus = R0**2-R1**2
186        R = rsqminus*npcosd(2.*phi0-2.*phi)+rsqplus
187        Q = np.sqrt(2.)*R0*R1*np.sqrt(R-2.*zdis**2*npsind(phi0-phi)**2)
188        P = 2.*R0**2*zdis*npcosd(phi0-phi)
189        Rcalc = (P+Q)/R
190        M = Robs-Rcalc
191        return M
192       
193    names = ['dist','det-X','det-Y','tilt','phi','dep','wave']
194    fmt = ['%12.2f','%12.2f','%12.2f','%12.2f','%12.2f','%12.3f','%12.5f']
195    p0 = [parmDict[key] for key in varyList]
196    result = leastsq(ellipseCalcD,p0,args=(rings.T,varyList,parmDict),full_output=True,ftol=1.e-8)
197    chisq = np.sum(result[2]['fvec']**2)
198    parmDict.update(zip(varyList,result[0]))
199    vals = list(result[0])
200    chi = np.sqrt(np.sum(ellipseCalcD(result[0],rings.T,varyList,parmDict)**2))
201    sig = list(chi*np.sqrt(np.diag(result[1])))
202    sigList = np.zeros(7)
203    for i,name in enumerate(names):
204        if name in varyList:
205            sigList[i] = sig[varyList.index(name)]
206    ValSig = zip(names,fmt,vals,sig)
207    if Print:
208        CalibPrint(ValSig)
209    return chi,chisq
210                   
211def ImageLocalMax(image,w,Xpix,Ypix):
212    'Needs a doc string'
213    w2 = w*2
214    sizey,sizex = image.shape
215    xpix = int(Xpix)            #get reference corner of pixel chosen
216    ypix = int(Ypix)
217    if (w < xpix < sizex-w) and (w < ypix < sizey-w) and image[ypix,xpix]:
218        Z = image[ypix-w:ypix+w,xpix-w:xpix+w]
219        Zmax = np.argmax(Z)
220        Zmin = np.argmin(Z)
221        xpix += Zmax%w2-w
222        ypix += Zmax/w2-w
223        return xpix,ypix,np.ravel(Z)[Zmax],max(0.0001,np.ravel(Z)[Zmin])   #avoid neg/zero minimum
224    else:
225        return 0,0,0,0     
226   
227def makeRing(dsp,ellipse,pix,reject,scalex,scaley,image):
228    'Needs a doc string'
229    def ellipseC():
230        'compute estimate of ellipse circumference'
231        if radii[0] < 0:        #hyperbola
232            theta = npacosd(1./np.sqrt(1.+(radii[0]/radii[1])**2))
233            print theta
234            return 0
235        apb = radii[1]+radii[0]
236        amb = radii[1]-radii[0]
237        return np.pi*apb*(1+3*(amb/apb)**2/(10+np.sqrt(4-3*(amb/apb)**2)))
238    cent,phi,radii = ellipse
239    cphi = cosd(phi)
240    sphi = sind(phi)
241    ring = []
242    sumI = 0
243    amin = 0
244    amax = 360
245    C = int(ellipseC())
246    for i in range(0,C,1):
247        a = 360.*i/C
248        x = radii[0]*cosd(a)
249        y = radii[1]*sind(a)
250        X = (cphi*x-sphi*y+cent[0])*scalex      #convert mm to pixels
251        Y = (sphi*x+cphi*y+cent[1])*scaley
252        X,Y,I,J = ImageLocalMax(image,pix,X,Y)
253        if I and J and I/J > reject:
254            sumI += I/J
255            X += .5                             #set to center of pixel
256            Y += .5
257            X /= scalex                         #convert to mm
258            Y /= scaley
259            amin = min(amin,a)
260            amax = max(amax,a)
261            if [X,Y,dsp] not in ring:
262                ring.append([X,Y,dsp])
263    if len(ring) < 10:             #want more than 10 deg
264        ring = []
265    return ring
266   
267def GetEllipse2(tth,dxy,dist,cent,tilt,phi):
268    '''uses Dandelin spheres to find ellipse or hyperbola parameters from detector geometry
269    on output
270    radii[0] (b-minor axis) set < 0. for hyperbola
271   
272    '''
273    radii = [0,0]
274    ttth = tand(tth)
275    stth = sind(tth)
276    ctth = cosd(tth)
277    cosb = cosd(tilt)
278    tanb = tand(tilt)
279    tbm = tand((tth-tilt)/2.)
280    tbp = tand((tth+tilt)/2.)
281    sinb = sind(tilt)
282    d = dist+dxy
283    if tth+abs(tilt) < 90.:      #ellipse
284        fplus = d*tanb*stth/(cosb+stth)
285        fminus = d*tanb*stth/(cosb-stth)
286        vplus = d*(tanb+(1+tbm)/(1-tbm))*stth/(cosb+stth)
287        vminus = d*(tanb+(1-tbp)/(1+tbp))*stth/(cosb-stth)
288        radii[0] = np.sqrt((vplus+vminus)**2-(fplus+fminus)**2)/2.      #+minor axis
289        radii[1] = (vplus+vminus)/2.                                    #major axis
290        zdis = (fplus-fminus)/2.
291    else:   #hyperbola!
292        f = d*abs(tanb)*stth/(cosb+stth)
293        v = d*(abs(tanb)+tand(tth-abs(tilt)))
294        delt = d*stth*(1.+stth*cosb)/(abs(sinb)*cosb*(stth+cosb))
295        eps = (v-f)/(delt-v)
296        radii[0] = -eps*(delt-f)/np.sqrt(eps**2-1.)                     #-minor axis
297        radii[1] = eps*(delt-f)/(eps**2-1.)                             #major axis
298        if tilt > 0:
299            zdis = f+radii[1]*eps
300        else:
301            zdis = -f
302#NB: zdis is || to major axis & phi is rotation of minor axis
303#thus shift from beam to ellipse center is [Z*sin(phi),-Z*cos(phi)]
304    elcent = [cent[0]+zdis*sind(phi),cent[1]-zdis*cosd(phi)]
305    return elcent,phi,radii
306   
307def GetEllipse(dsp,data):
308    '''uses Dandelin spheres to find ellipse or hyperbola parameters from detector geometry
309    as given in image controls dictionary (data) and a d-spacing (dsp)
310    '''
311    cent = data['center']
312    tilt = data['tilt']
313    phi = data['rotation']
314    dep = data['DetDepth']
315    tth = 2.0*asind(data['wavelength']/(2.*dsp))
316    dxy = peneCorr(tth,dep,tilt)
317    dist = data['distance']
318    return GetEllipse2(tth,dxy,dist,cent,tilt,phi)
319       
320def GetDetectorXY(dsp,azm,data):
321    'Needs a doc string'
322   
323    elcent,phi,radii = GetEllipse(dsp,data)
324    phi = data['rotation']-90.          #to give rotation of major axis
325    tilt = data['tilt']
326    dist = data['distance']
327    cent = data['center']
328    tth = 2.0*asind(data['wavelength']/(2.*dsp))
329    ttth = tand(tth)
330    stth = sind(tth)
331    ctth = cosd(tth)
332    cosb = cosd(tilt)
333    if radii[0] > 0.:
334        sinb = sind(tilt)
335        tanb = tand(tilt)
336        fplus = dist*tanb*stth/(cosb+stth)
337        fminus = dist*tanb*stth/(cosb-stth)
338        zdis = (fplus-fminus)/2.
339        rsqplus = radii[0]**2+radii[1]**2
340        rsqminus = radii[0]**2-radii[1]**2
341        R = rsqminus*cosd(2.*azm-2.*phi)+rsqplus
342        Q = np.sqrt(2.)*radii[0]*radii[1]*np.sqrt(R-2.*zdis**2*sind(azm-phi)**2)
343        P = 2.*radii[0]**2*zdis*cosd(azm-phi)
344        radius = (P+Q)/R
345        xy = np.array([radius*cosd(azm),radius*sind(azm)])
346        xy += cent
347    else:   #hyperbola - both branches (one is way off screen!)
348        sinb = abs(sind(tilt))
349        tanb = abs(tand(tilt))
350        f = dist*tanb*stth/(cosb+stth)
351        v = dist*(tanb+tand(tth-abs(tilt)))
352        delt = dist*stth*(1+stth*cosb)/(sinb*cosb*(stth+cosb))
353        ecc = (v-f)/(delt-v)
354        R = radii[1]*(ecc**2-1)/(1-ecc*cosd(azm))
355        if tilt > 0.:
356            offset = 2.*radii[1]*ecc+f      #select other branch
357            xy = [-R*cosd(azm)-offset,R*sind(azm)]
358        else:
359            offset = -f
360            xy = [-R*cosd(azm)-offset,-R*sind(azm)]
361        xy = -np.array([xy[0]*cosd(phi)+xy[1]*sind(phi),xy[0]*sind(phi)-xy[1]*cosd(phi)])
362        xy += cent
363    return xy
364   
365def GetDetXYfromThAzm(Th,Azm,data):
366    'Needs a doc string'
367    dsp = data['wavelength']/(2.0*npsind(Th))   
368    return GetDetectorXY(dsp,azm,data)
369                   
370def GetTthAzmDsp(x,y,data):
371    'Needs a doc string - checked OK for ellipses & hyperbola'
372    wave = data['wavelength']
373    cent = data['center']
374    tilt = data['tilt']
375    dist = data['distance']/cosd(tilt)
376    x0 = data['distance']*tand(tilt)
377    phi = data['rotation']
378    dep = data['DetDepth']
379    LRazim = data['LRazimuth']
380    azmthoff = data['azmthOff']
381    dx = np.array(x-cent[0],dtype=np.float32)
382    dy = np.array(y-cent[1],dtype=np.float32)
383    D = ((dx-x0)**2+dy**2+data['distance']**2)      #sample to pixel distance
384    X = np.array(([dx,dy,np.zeros_like(dx)]),dtype=np.float32).T
385    X = np.dot(X,makeMat(phi,2))
386    Z = np.dot(X,makeMat(tilt,0)).T[2]
387    tth = npatand(np.sqrt(dx**2+dy**2-Z**2)/(dist-Z))
388    dxy = peneCorr(tth,dep,tilt,npatan2d(dy,dx))
389    DX = dist-Z+dxy
390    DY = np.sqrt(dx**2+dy**2-Z**2)
391    tth = npatan2d(DY,DX) 
392    dsp = wave/(2.*npsind(tth/2.))
393    azm = (npatan2d(dy,dx)+azmthoff+720.)%360.
394    G = D/data['distance']**2       #for geometric correction = 1/cos(2theta)^2 if tilt=0.
395    return tth,azm,G,dsp
396   
397def GetTth(x,y,data):
398    'Give 2-theta value for detector x,y position; calibration info in data'
399    return GetTthAzmDsp(x,y,data)[0]
400   
401def GetTthAzm(x,y,data):
402    'Give 2-theta, azimuth values for detector x,y position; calibration info in data'
403    return GetTthAzmDsp(x,y,data)[0:2]
404   
405def GetTthAzmD(x,y,data):
406    '''Give 2-theta, azimuth & geometric correction values for detector x,y position;
407     calibration info in data
408    '''
409    return GetTthAzmDsp(x,y,data)[0:3]
410
411def GetDsp(x,y,data):
412    'Give d-spacing value for detector x,y position; calibration info in data'
413    return GetTthAzmDsp(x,y,data)[3]
414       
415def GetAzm(x,y,data):
416    'Give azimuth value for detector x,y position; calibration info in data'
417    return GetTthAzmDsp(x,y,data)[1]
418       
419def ImageCompress(image,scale):
420    'Needs a doc string'
421    if scale == 1:
422        return image
423    else:
424        return image[::scale,::scale]
425       
426def checkEllipse(Zsum,distSum,xSum,ySum,dist,x,y):
427    'Needs a doc string'
428    avg = np.array([distSum/Zsum,xSum/Zsum,ySum/Zsum])
429    curr = np.array([dist,x,y])
430    return abs(avg-curr)/avg < .02
431
432def EdgeFinder(image,data):
433    '''this makes list of all x,y where I>edgeMin suitable for an ellipse search?
434    Not currently used but might be useful in future?
435    '''
436    import numpy.ma as ma
437    Nx,Ny = data['size']
438    pixelSize = data['pixelSize']
439    edgemin = data['edgemin']
440    scalex = pixelSize[0]/1000.
441    scaley = pixelSize[1]/1000.   
442    tay,tax = np.mgrid[0:Nx,0:Ny]
443    tax = np.asfarray(tax*scalex,dtype=np.float32)
444    tay = np.asfarray(tay*scaley,dtype=np.float32)
445    tam = ma.getmask(ma.masked_less(image.flatten(),edgemin))
446    tax = ma.compressed(ma.array(tax.flatten(),mask=tam))
447    tay = ma.compressed(ma.array(tay.flatten(),mask=tam))
448    return zip(tax,tay)
449   
450def MakeFrameMask(data,frame):
451    pixelSize = data['pixelSize']
452    scalex = pixelSize[0]/1000.
453    scaley = pixelSize[1]/1000.
454    blkSize = 512
455    Nx,Ny = data['size']
456    nXBlks = (Nx-1)/blkSize+1
457    nYBlks = (Ny-1)/blkSize+1
458    tam = ma.make_mask_none(data['size'])
459    for iBlk in range(nXBlks):
460        iBeg = iBlk*blkSize
461        iFin = min(iBeg+blkSize,Nx)
462        for jBlk in range(nYBlks):
463            jBeg = jBlk*blkSize
464            jFin = min(jBeg+blkSize,Ny)               
465            nI = iFin-iBeg
466            nJ = jFin-jBeg
467            tax,tay = np.mgrid[iBeg+0.5:iFin+.5,jBeg+.5:jFin+.5]         #bin centers not corners
468            tax = np.asfarray(tax*scalex,dtype=np.float32)
469            tay = np.asfarray(tay*scaley,dtype=np.float32)
470            tamp = ma.make_mask_none((1024*1024))
471            tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
472                tay.flatten(),len(frame),frame,tamp)[:nI*nJ])-True  #switch to exclude around frame
473            if tamp.shape:
474                tamp = np.reshape(tamp[:nI*nJ],(nI,nJ))
475                tam[iBeg:iFin,jBeg:jFin] = ma.mask_or(tamp[0:nI,0:nJ],tam[iBeg:iFin,jBeg:jFin])
476            else:
477                tam[iBeg:iFin,jBeg:jFin] = True
478    return tam.T
479   
480def ImageRecalibrate(self,data,masks):
481    'Needs a doc string'
482    import ImageCalibrants as calFile
483    print 'Image recalibration:'
484    time0 = time.time()
485    pixelSize = data['pixelSize']
486    scalex = 1000./pixelSize[0]
487    scaley = 1000./pixelSize[1]
488    pixLimit = data['pixLimit']
489    cutoff = data['cutoff']
490    data['rings'] = []
491    data['ellipses'] = []
492    if not data['calibrant']:
493        print 'no calibration material selected'
494        return True   
495    skip = data['calibskip']
496    dmin = data['calibdmin']
497    Bravais,Cells = calFile.Calibrants[data['calibrant']][:2]
498    HKL = []
499    for bravais,cell in zip(Bravais,Cells):
500        A = G2lat.cell2A(cell)
501        hkl = G2lat.GenHBravais(dmin,bravais,A)
502        HKL += hkl
503    HKL = G2lat.sortHKLd(HKL,True,False)
504    varyList = ['dist','det-X','det-Y','tilt','phi']
505    parmDict = {'dist':data['distance'],'det-X':data['center'][0],'det-Y':data['center'][1],
506        'tilt':data['tilt'],'phi':data['rotation'],'wave':data['wavelength'],'dep':data['DetDepth']}
507    Found = False
508    wave = data['wavelength']
509    frame = masks['Frames']
510    tam = ma.make_mask_none(self.ImageZ.shape)
511    if frame:
512        tam = ma.mask_or(tam,MakeFrameMask(data,frame))
513    for iH,H in enumerate(HKL): 
514        dsp = H[3]
515        tth = 2.0*asind(wave/(2.*dsp))
516        if tth+abs(data['tilt']) > 90.:
517            print 'next line is a hyperbola - search stopped'
518            break
519        ellipse = GetEllipse(dsp,data)
520        Ring = makeRing(dsp,ellipse,pixLimit,cutoff,scalex,scaley,ma.array(self.ImageZ,mask=tam))
521        if Ring:
522            if iH >= skip:
523                data['rings'].append(np.array(Ring))
524            data['ellipses'].append(copy.deepcopy(ellipse+('r',)))
525            Found = True
526        elif not Found:         #skipping inner rings, keep looking until ring found
527            continue
528        else:                   #no more rings beyond edge of detector
529            break
530    rings = np.concatenate((data['rings']),axis=0)
531    if data['DetDepthRef']:
532        varyList.append('dep')
533    FitDetector(rings,varyList,parmDict)
534    data['distance'] = parmDict['dist']
535    data['center'] = [parmDict['det-X'],parmDict['det-Y']]
536    data['rotation'] = np.mod(parmDict['phi'],360.0)
537    data['tilt'] = parmDict['tilt']
538    data['DetDepth'] = parmDict['dep']
539    N = len(data['ellipses'])
540    data['ellipses'] = []           #clear away individual ellipse fits
541    for H in HKL[:N]:
542        ellipse = GetEllipse(H[3],data)
543        data['ellipses'].append(copy.deepcopy(ellipse+('b',)))
544   
545    print 'calibration time = ',time.time()-time0
546    G2plt.PlotImage(self,newImage=True)       
547    return True
548           
549def ImageCalibrate(self,data):
550    'Needs a doc string'
551    import copy
552    import ImageCalibrants as calFile
553    print 'Image calibration:'
554    time0 = time.time()
555    ring = data['ring']
556    pixelSize = data['pixelSize']
557    scalex = 1000./pixelSize[0]
558    scaley = 1000./pixelSize[1]
559    pixLimit = data['pixLimit']
560    cutoff = data['cutoff']
561    if len(ring) < 5:
562        print 'not enough inner ring points for ellipse'
563        return False
564       
565    #fit start points on inner ring
566    data['ellipses'] = []
567    data['rings'] = []
568    outE = FitEllipse(ring)
569    fmt  = '%s X: %.3f, Y: %.3f, phi: %.3f, R1: %.3f, R2: %.3f'
570    fmt2 = '%s X: %.3f, Y: %.3f, phi: %.3f, R1: %.3f, R2: %.3f, chi^2: %.3f, N: %d'
571    if outE:
572        print fmt%('start ellipse: ',outE[0][0],outE[0][1],outE[1],outE[2][0],outE[2][1])
573        ellipse = outE
574    else:
575        return False
576       
577    #setup 360 points on that ring for "good" fit
578    data['ellipses'].append(ellipse[:]+('g',))
579    Ring = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ)
580    if Ring:
581        ellipse = FitEllipse(ring)
582        Ring = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ)    #do again
583        ellipse = FitEllipse(ring)
584    else:
585        print '1st ring not sufficiently complete to proceed'
586        return False
587    print fmt2%('inner ring:    ',ellipse[0][0],ellipse[0][1],ellipse[1],ellipse[2][0],ellipse[2][1],0.,len(Ring))     #cent,phi,radii
588    data['ellipses'].append(ellipse[:]+('r',))
589    data['rings'].append(np.array(Ring))
590    G2plt.PlotImage(self,newImage=True)
591   
592#setup for calibration
593    data['rings'] = []
594    if not data['calibrant']:
595        print 'no calibration material selected'
596        return True
597   
598    skip = data['calibskip']
599    dmin = data['calibdmin']
600#generate reflection set
601    Bravais,Cells = calFile.Calibrants[data['calibrant']][:2]
602    HKL = []
603    for bravais,cell in zip(Bravais,Cells):
604        A = G2lat.cell2A(cell)
605        hkl = G2lat.GenHBravais(dmin,bravais,A)[skip:]
606        HKL += hkl
607    HKL = G2lat.sortHKLd(HKL,True,False)
608    wave = data['wavelength']
609#set up 1st ring
610    elcent,phi,radii = ellipse              #from fit of 1st ring
611    dsp = HKL[0][3]
612    tth = 2.0*asind(wave/(2.*dsp))
613    Ring0 = makeRing(dsp,ellipse,3,cutoff,scalex,scaley,self.ImageZ)
614    ttth = nptand(tth)
615    stth = npsind(tth)
616    ctth = npcosd(tth)
617#1st estimate of tilt; assume ellipse - don't know sign though
618    tilt = npasind(np.sqrt(max(0.,1.-(radii[0]/radii[1])**2))*ctth)
619    if not tilt:
620        print 'WARNING - selected ring was fitted as a circle'
621        print ' - if detector was tilted we suggest you skip this ring - WARNING'
622#1st estimate of dist: sample to detector normal to plane
623    data['distance'] = dist = radii[0]**2/(ttth*radii[1])
624#ellipse to cone axis (x-ray beam); 2 choices depending on sign of tilt
625    zdisp = radii[1]*ttth*tand(tilt)
626    zdism = radii[1]*ttth*tand(-tilt)
627#cone axis position; 2 choices. Which is right?     
628#NB: zdisp is || to major axis & phi is rotation of minor axis
629#thus shift from beam to ellipse center is [Z*sin(phi),-Z*cos(phi)]
630    centp = [elcent[0]+zdisp*sind(phi),elcent[1]-zdisp*cosd(phi)]
631    centm = [elcent[0]+zdism*sind(phi),elcent[1]-zdism*cosd(phi)]
632#check get same ellipse parms either way
633#now do next ring; estimate either way & do a FitDetector each way; best fit is correct one
634    dsp = HKL[1][3]
635    tth = 2.0*asind(wave/(2.*dsp))
636    ellipsep = GetEllipse2(tth,0.,dist,centp,tilt,phi)
637    print fmt%('plus ellipse :',ellipsep[0][0],ellipsep[0][1],ellipsep[1],ellipsep[2][0],ellipsep[2][1])
638    Ringp = makeRing(dsp,ellipsep,3,cutoff,scalex,scaley,self.ImageZ)
639    parmDict = {'dist':dist,'det-X':centp[0],'det-Y':centp[1],
640        'tilt':tilt,'phi':phi,'wave':wave,'dep':0.0}
641    varyList = ['dist','det-X','det-Y','tilt','phi']
642    if len(Ringp) > 10:
643        chip,chisqp = FitDetector(np.array(Ring0+Ringp),varyList,parmDict,True)
644        tiltp = parmDict['tilt']
645        phip = parmDict['phi']
646        centp = [parmDict['det-X'],parmDict['det-Y']]
647    else:
648        chip = 1e6
649    ellipsem = GetEllipse2(tth,0.,dist,centm,-tilt,phi)
650    print fmt%('minus ellipse:',ellipsem[0][0],ellipsem[0][1],ellipsem[1],ellipsem[2][0],ellipsem[2][1])
651    Ringm = makeRing(dsp,ellipsem,3,cutoff,scalex,scaley,self.ImageZ)
652    if len(Ringm) > 10:
653        parmDict['tilt'] *= -1
654        chim,chisqm = FitDetector(np.array(Ring0+Ringm),varyList,parmDict,True)
655        tiltm = parmDict['tilt']
656        phim = parmDict['phi']
657        centm = [parmDict['det-X'],parmDict['det-Y']]
658    else:
659        chim = 1e6
660    if chip < chim:
661        data['tilt'] = tiltp
662        data['center'] = centp
663        data['rotation'] = phip
664    else:
665        data['tilt'] = tiltm
666        data['center'] = centm
667        data['rotation'] = phim
668    data['ellipses'].append(ellipsep[:]+('b',))
669    data['rings'].append(np.array(Ringp))
670    data['ellipses'].append(ellipsem[:]+('r',))
671    data['rings'].append(np.array(Ringm))
672    G2plt.PlotImage(self,newImage=True)
673    parmDict = {'dist':data['distance'],'det-X':data['center'][0],'det-Y':data['center'][1],
674        'tilt':data['tilt'],'phi':data['rotation'],'wave':data['wavelength'],'dep':data['DetDepth']}
675    varyList = ['dist','det-X','det-Y','tilt','phi']
676    if data['DetDepthRef']:
677        varyList.append('dep')
678    data['rings'] = []
679    data['ellipses'] = []
680    for i,H in enumerate(HKL):
681        dsp = H[3]
682        tth = 2.0*asind(wave/(2.*dsp))
683        if tth+abs(data['tilt']) > 90.:
684            print 'next line is a hyperbola - search stopped'
685            break
686        print 'HKLD:',H[:4],'2-theta: %.4f'%(tth)
687        elcent,phi,radii = ellipse = GetEllipse(dsp,data)
688        data['ellipses'].append(copy.deepcopy(ellipse+('g',)))
689        print fmt%('predicted ellipse:',elcent[0],elcent[1],phi,radii[0],radii[1])
690        Ring = makeRing(dsp,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ)
691        if Ring:
692            data['rings'].append(np.array(Ring))
693            rings = np.concatenate((data['rings']),axis=0)
694            if i:
695                chi,chisq = FitDetector(rings,varyList,parmDict,False)
696                data['distance'] = parmDict['dist']
697                data['center'] = [parmDict['det-X'],parmDict['det-Y']]
698                data['rotation'] = parmDict['phi']
699                data['tilt'] = parmDict['tilt']
700                data['DetDepth'] = parmDict['dep']
701                elcent,phi,radii = ellipse = GetEllipse(dsp,data)
702                print fmt2%('fitted ellipse:   ',elcent[0],elcent[1],phi,radii[0],radii[1],chisq,len(rings))
703            data['ellipses'].append(copy.deepcopy(ellipse+('r',)))
704#            G2plt.PlotImage(self,newImage=True)
705        else:
706            print 'insufficient number of points in this ellipse to fit'
707            break
708    G2plt.PlotImage(self,newImage=True)
709    fullSize = len(self.ImageZ)/scalex
710    if 2*radii[1] < .9*fullSize:
711        print 'Are all usable rings (>25% visible) used? Try reducing Min ring I/Ib'
712    N = len(data['ellipses'])
713    if N > 2:
714        FitDetector(rings,varyList,parmDict)
715        data['distance'] = parmDict['dist']
716        data['center'] = [parmDict['det-X'],parmDict['det-Y']]
717        data['rotation'] = parmDict['phi']
718        data['tilt'] = parmDict['tilt']
719        data['DetDepth'] = parmDict['dep']
720    for H in HKL[:N]:
721        ellipse = GetEllipse(H[3],data)
722        data['ellipses'].append(copy.deepcopy(ellipse+('b',)))
723    print 'calibration time = ',time.time()-time0
724    G2plt.PlotImage(self,newImage=True)       
725    return True
726   
727def Make2ThetaAzimuthMap(data,masks,iLim,jLim):
728    'Needs a doc string'
729    #transforms 2D image from x,y space to 2-theta,azimuth space based on detector orientation
730    pixelSize = data['pixelSize']
731    scalex = pixelSize[0]/1000.
732    scaley = pixelSize[1]/1000.
733   
734    tay,tax = np.mgrid[iLim[0]+0.5:iLim[1]+.5,jLim[0]+.5:jLim[1]+.5]         #bin centers not corners
735    tax = np.asfarray(tax*scalex,dtype=np.float32)
736    tay = np.asfarray(tay*scaley,dtype=np.float32)
737    nI = iLim[1]-iLim[0]
738    nJ = jLim[1]-jLim[0]
739    #make position masks here
740    frame = masks['Frames']
741    tam = ma.make_mask_none((nI,nJ))
742    if frame:
743        tamp = ma.make_mask_none((1024*1024))
744        tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
745            tay.flatten(),len(frame),frame,tamp)[:nI*nJ])-True  #switch to exclude around frame
746        tam = ma.mask_or(tam.flatten(),tamp)
747    polygons = masks['Polygons']
748    for polygon in polygons:
749        if polygon:
750            tamp = ma.make_mask_none((1024*1024))
751            tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
752                tay.flatten(),len(polygon),polygon,tamp)[:nI*nJ])
753            tam = ma.mask_or(tam.flatten(),tamp)
754    if tam.shape: tam = np.reshape(tam,(nI,nJ))
755    spots = masks['Points']
756    for X,Y,diam in spots:
757        tamp = ma.getmask(ma.masked_less((tax-X)**2+(tay-Y)**2,(diam/2.)**2))
758        tam = ma.mask_or(tam,tamp)
759    TA = np.array(GetTthAzmD(tax,tay,data))     #includes geom. corr. as dist**2/d0**2
760    TA[1] = np.where(TA[1]<0,TA[1]+360,TA[1])
761    return np.array(TA),tam           #2-theta, azimuth & geom. corr. arrays & position mask
762
763def Fill2ThetaAzimuthMap(masks,TA,tam,image):
764    'Needs a doc string'
765    Zlim = masks['Thresholds'][1]
766    rings = masks['Rings']
767    arcs = masks['Arcs']
768    TA = np.dstack((ma.getdata(TA[1]),ma.getdata(TA[0]),ma.getdata(TA[2])))    #azimuth, 2-theta, dist
769    tax,tay,tad = np.dsplit(TA,3)    #azimuth, 2-theta, dist**2/d0**2
770    for tth,thick in rings:
771        tam = ma.mask_or(tam.flatten(),ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.)))
772    for tth,azm,thick in arcs:
773        tamt = ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.))
774        tama = ma.getmask(ma.masked_inside(tax.flatten(),azm[0],azm[1]))
775        tam = ma.mask_or(tam.flatten(),tamt*tama)
776    taz = ma.masked_outside(image.flatten(),int(Zlim[0]),Zlim[1])
777    tabs = np.ones_like(taz)
778    tam = ma.mask_or(tam.flatten(),ma.getmask(taz))
779    tax = ma.compressed(ma.array(tax.flatten(),mask=tam))
780    tay = ma.compressed(ma.array(tay.flatten(),mask=tam))
781    taz = ma.compressed(ma.array(taz.flatten(),mask=tam))
782    tad = ma.compressed(ma.array(tad.flatten(),mask=tam))
783    tabs = ma.compressed(ma.array(tabs.flatten(),mask=tam))
784    return tax,tay,taz,tad,tabs
785   
786def ImageIntegrate(image,data,masks,blkSize=128,dlg=None):
787    'Needs a doc string'
788    import histogram2d as h2d
789    print 'Begin image integration'
790    LUtth = data['IOtth']
791    LRazm = np.array(data['LRazimuth'],dtype=np.float64)
792    numAzms = data['outAzimuths']
793    numChans = data['outChannels']
794    Dtth = (LUtth[1]-LUtth[0])/numChans
795    Dazm = (LRazm[1]-LRazm[0])/numAzms
796    if data['centerAzm']:
797        LRazm += Dazm/2.
798    NST = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
799    H0 = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
800    imageN = len(image)
801    Nx,Ny = data['size']
802    nXBlks = (Nx-1)/blkSize+1
803    nYBlks = (Ny-1)/blkSize+1
804    Nup = nXBlks*nYBlks*3+3
805    t0 = time.time()
806    Nup = 0
807    if dlg:
808        dlg.Update(Nup)
809    for iBlk in range(nYBlks):
810        iBeg = iBlk*blkSize
811        iFin = min(iBeg+blkSize,Ny)
812        for jBlk in range(nXBlks):
813            jBeg = jBlk*blkSize
814            jFin = min(jBeg+blkSize,Nx)               
815            TA,tam = Make2ThetaAzimuthMap(data,masks,(iBeg,iFin),(jBeg,jFin))           #2-theta & azimuth arrays & create position mask
816           
817            Nup += 1
818            if dlg:
819                dlg.Update(Nup)
820            Block = image[iBeg:iFin,jBeg:jFin]
821            tax,tay,taz,tad,tabs = Fill2ThetaAzimuthMap(masks,TA,tam,Block)    #and apply masks
822            Nup += 1
823            if dlg:
824                dlg.Update(Nup)
825            tax = np.where(tax > LRazm[1],tax-360.,tax)                 #put azm inside limits if possible
826            tax = np.where(tax < LRazm[0],tax+360.,tax)
827            if data['SampleAbs'][1]:
828                muR = data['SampleAbs'][0]*(1.+npsind(tax)**2/2.)/(npcosd(tay))
829                tabs = G2pwd.Absorb(data['SampleShape'],muR,tay)
830            if any([tax.shape[0],tay.shape[0],taz.shape[0]]):
831                NST,H0 = h2d.histogram2d(len(tax),tax,tay,taz*tad*tabs,numAzms,numChans,LRazm,LUtth,Dazm,Dtth,NST,H0)
832            Nup += 1
833            if dlg:
834                dlg.Update(Nup)
835    NST = np.array(NST)
836    H0 = np.divide(H0,NST)
837    H0 = np.nan_to_num(H0)
838    del NST
839    if Dtth:
840        H2 = np.array([tth for tth in np.linspace(LUtth[0],LUtth[1],numChans+1)])
841    else:
842        H2 = np.array(LUtth)
843    if Dazm:       
844        H1 = np.array([azm for azm in np.linspace(LRazm[0],LRazm[1],numAzms+1)])
845    else:
846        H1 = LRazm
847    H0 /= npcosd(H2[:-1])**2
848    if data['Oblique'][1]:
849        H0 /= G2pwd.Oblique(data['Oblique'][0],H2[:-1])
850    if 'SASD' in data['type'] and data['PolaVal'][1]:
851        H0 /= np.array([G2pwd.Polarization(data['PolaVal'][0],H2[:-1],Azm=azm)[0] for azm in H1[:-1]+np.diff(H1)/2.])
852    Nup += 1
853    if dlg:
854        dlg.Update(Nup)
855    t1 = time.time()
856    print 'Integration complete'
857    print "Elapsed time:","%8.3f"%(t1-t0), "s"
858    return H0,H1,H2
859   
860def FitStrSta(Image,StrSta,Controls,Masks):
861    'Needs a doc string'
862   
863#    print 'Masks:',Masks
864    StaControls = copy.deepcopy(Controls)
865    phi = StrSta['Sample phi']
866    wave = Controls['wavelength']
867    StaControls['distance'] += StrSta['Sample z']*cosd(phi)
868    pixSize = StaControls['pixelSize']
869    scalex = 1000./pixSize[0]
870    scaley = 1000./pixSize[1]
871    rings = []
872
873    for ring in StrSta['d-zero']:       #get observed x,y,d points for the d-zeros
874        ellipse = GetEllipse(ring['Dset'],StaControls)
875        Ring = makeRing(ring['Dset'],ellipse,ring['pixLimit'],ring['cutoff'],scalex,scaley,Image)
876        Ring = np.array(Ring).T
877        ring['ImxyObs'] = np.array(Ring[:2])      #need to apply masks to this to eliminate bad points
878        Ring[:2] = GetTthAzm(Ring[0],Ring[1],StaControls)       #convert x,y to tth,azm
879        Ring[0] /= 2.                                           #convert to theta
880        if len(rings):
881            rings = np.concatenate((rings,Ring),axis=1)
882        else:
883            rings = np.array(Ring)
884    E = StrSta['strain']
885    p0 = [E[0][0],E[1][1],E[2][2],E[0][1],E[0][2],E[1][2]]
886    E = FitStrain(rings,p0,wave,phi)
887    StrSta['strain'] = E
888
889def calcFij(omg,phi,azm,th):
890    '''Does something...
891
892    Uses parameters as defined by Bob He & Kingsley Smith, Adv. in X-Ray Anal. 41, 501 (1997)
893
894    :param omg: his omega = sample omega rotation; 0 when incident beam || sample surface, 90 when perp. to sample surface
895    :param phi: his phi = sample phi rotation; usually = 0, axis rotates with omg.
896    :param azm: his chi = azimuth around incident beam
897    :param th:  his theta = theta
898    '''
899    a = npsind(th)*npcosd(omg)+npsind(azm)*npcosd(th)*npsind(omg)
900    b = -npcosd(azm)*npcosd(th)
901    c = npsind(th)*npsind(omg)-npsind(azm)*npcosd(th)*npcosd(omg)
902    d = a*npcosd(phi)-b*npsind(phi)
903    e = a*npsind(phi)+b*npcosd(phi)
904    Fij = np.array([
905        [d**2,d*e,c*d],
906        [d*e,e**2,c*e],
907        [c*d,c*e,c**2]])
908    return -Fij*nptand(th)
909
910def FitStrain(rings,p0,wave,phi):
911    'Needs a doc string'
912    def StrainPrint(ValSig):
913        print 'Strain tensor:'
914        ptlbls = 'names :'
915        ptstr =  'values:'
916        sigstr = 'esds  :'
917        for name,fmt,value,sig in ValSig:
918            ptlbls += "%s" % (name.rjust(12))
919            ptstr += fmt % (value)
920            if sig:
921                sigstr += fmt % (sig)
922            else:
923                sigstr += 12*' '
924        print ptlbls
925        print ptstr
926        print sigstr
927       
928    def strainCalc(p,xyd,wave,phi):
929#        E = np.array([[p[0],p[3],p[4]],[p[3],p[1],p[5]],[p[4],p[5],p[2]]])
930        E = np.array([[p[0],0,0],[0,p[1],0],[0,0,0]])
931        th,azm,dsp = xyd
932        th0 = npasind(wave/(2.*dsp))
933        dth = 180.*np.sum(E*calcFij(phi,0.,azm,th).T)/(np.pi*1.e6) #in degrees & microstrain units
934        th0 += dth
935        return (th-th0)**2
936       
937    names = ['e11','e22','e33','e12','e13','e23']
938    fmt = ['%12.2f','%12.2f','%12.2f','%12.2f','%12.2f','%12.5f']
939    p1 = [p0[0],p0[1]]   
940    result = leastsq(strainCalc,p1,args=(rings,wave,phi),full_output=True)
941    vals = list(result[0])
942    chi = np.sqrt(np.sum(strainCalc(result[0],rings,wave,phi)**2))
943    sig = list(chi*np.sqrt(np.diag(result[1])))
944    ValSig = zip(names,fmt,vals,sig)
945    StrainPrint(ValSig)
946#    return np.array([[vals[0],vals[3],vals[4]],[vals[3],vals[1],vals[5]],[vals[4],vals[5],vals[2]]])
947    return np.array([[vals[0],0,0],[0,vals[1],0],[0,0,0]])
948   
949       
Note: See TracBrowser for help on using the repository browser.