source: trunk/GSASIIimage.py @ 1184

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

First working version of strain refinement from image data

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 35.9 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII image calculations: ellipse fitting & image integration       
3########### SVN repository information ###################
4# $Date: 2014-01-07 21:45:30 +0000 (Tue, 07 Jan 2014) $
5# $Author: vondreele $
6# $Revision: 1184 $
7# $URL: trunk/GSASIIimage.py $
8# $Id: GSASIIimage.py 1184 2014-01-07 21:45:30Z 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: 1184 $")
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       
239    cent,phi,radii = ellipse
240    cphi = cosd(phi-90.)        #convert to major axis rotation
241    sphi = sind(phi-90.)
242    ring = []
243    C = int(ellipseC())         #ring circumference
244    for i in range(0,C,1):      #step around ring in 1mm increments
245        a = 360.*i/C
246        x = radii[1]*cosd(a)        #major axis
247        y = radii[0]*sind(a)
248        X = (cphi*x-sphi*y+cent[0])*scalex      #convert mm to pixels
249        Y = (sphi*x+cphi*y+cent[1])*scaley
250        X,Y,I,J = ImageLocalMax(image,pix,X,Y)
251        if I and J and I/J > reject:
252            X += .5                             #set to center of pixel
253            Y += .5
254            X /= scalex                         #convert back to mm
255            Y /= scaley
256            if [X,Y,dsp] not in ring:           #no duplicates!
257                ring.append([X,Y,dsp])
258    if len(ring) < 10:
259        ring = []
260    return ring
261   
262def GetEllipse2(tth,dxy,dist,cent,tilt,phi):
263    '''uses Dandelin spheres to find ellipse or hyperbola parameters from detector geometry
264    on output
265    radii[0] (b-minor axis) set < 0. for hyperbola
266   
267    '''
268    radii = [0,0]
269    ttth = tand(tth)
270    stth = sind(tth)
271    ctth = cosd(tth)
272    cosb = cosd(tilt)
273    tanb = tand(tilt)
274    tbm = tand((tth-tilt)/2.)
275    tbp = tand((tth+tilt)/2.)
276    sinb = sind(tilt)
277    d = dist+dxy
278    if tth+abs(tilt) < 90.:      #ellipse
279        fplus = d*tanb*stth/(cosb+stth)
280        fminus = d*tanb*stth/(cosb-stth)
281        vplus = d*(tanb+(1+tbm)/(1-tbm))*stth/(cosb+stth)
282        vminus = d*(tanb+(1-tbp)/(1+tbp))*stth/(cosb-stth)
283        radii[0] = np.sqrt((vplus+vminus)**2-(fplus+fminus)**2)/2.      #+minor axis
284        radii[1] = (vplus+vminus)/2.                                    #major axis
285        zdis = (fplus-fminus)/2.
286    else:   #hyperbola!
287        f = d*abs(tanb)*stth/(cosb+stth)
288        v = d*(abs(tanb)+tand(tth-abs(tilt)))
289        delt = d*stth*(1.+stth*cosb)/(abs(sinb)*cosb*(stth+cosb))
290        eps = (v-f)/(delt-v)
291        radii[0] = -eps*(delt-f)/np.sqrt(eps**2-1.)                     #-minor axis
292        radii[1] = eps*(delt-f)/(eps**2-1.)                             #major axis
293        if tilt > 0:
294            zdis = f+radii[1]*eps
295        else:
296            zdis = -f
297#NB: zdis is || to major axis & phi is rotation of minor axis
298#thus shift from beam to ellipse center is [Z*sin(phi),-Z*cos(phi)]
299    elcent = [cent[0]+zdis*sind(phi),cent[1]-zdis*cosd(phi)]
300    return elcent,phi,radii
301   
302def GetEllipse(dsp,data):
303    '''uses Dandelin spheres to find ellipse or hyperbola parameters from detector geometry
304    as given in image controls dictionary (data) and a d-spacing (dsp)
305    '''
306    cent = data['center']
307    tilt = data['tilt']
308    phi = data['rotation']
309    dep = data['DetDepth']
310    tth = 2.0*asind(data['wavelength']/(2.*dsp))
311    dxy = peneCorr(tth,dep,tilt)
312    dist = data['distance']
313    return GetEllipse2(tth,dxy,dist,cent,tilt,phi)
314       
315def GetDetectorXY(dsp,azm,data):
316    'Needs a doc string'
317   
318    elcent,phi,radii = GetEllipse(dsp,data)
319    phi = data['rotation']-90.          #to give rotation of major axis
320    tilt = data['tilt']
321    dist = data['distance']
322    cent = data['center']
323    tth = 2.0*asind(data['wavelength']/(2.*dsp))
324    ttth = tand(tth)
325    stth = sind(tth)
326    ctth = cosd(tth)
327    cosb = cosd(tilt)
328    if radii[0] > 0.:
329        sinb = sind(tilt)
330        tanb = tand(tilt)
331        fplus = dist*tanb*stth/(cosb+stth)
332        fminus = dist*tanb*stth/(cosb-stth)
333        zdis = (fplus-fminus)/2.
334        rsqplus = radii[0]**2+radii[1]**2
335        rsqminus = radii[0]**2-radii[1]**2
336        R = rsqminus*cosd(2.*azm-2.*phi)+rsqplus
337        Q = np.sqrt(2.)*radii[0]*radii[1]*np.sqrt(R-2.*zdis**2*sind(azm-phi)**2)
338        P = 2.*radii[0]**2*zdis*cosd(azm-phi)
339        radius = (P+Q)/R
340        xy = np.array([radius*cosd(azm),radius*sind(azm)])
341        xy += cent
342    else:   #hyperbola - both branches (one is way off screen!)
343        sinb = abs(sind(tilt))
344        tanb = abs(tand(tilt))
345        f = dist*tanb*stth/(cosb+stth)
346        v = dist*(tanb+tand(tth-abs(tilt)))
347        delt = dist*stth*(1+stth*cosb)/(sinb*cosb*(stth+cosb))
348        ecc = (v-f)/(delt-v)
349        R = radii[1]*(ecc**2-1)/(1-ecc*cosd(azm))
350        if tilt > 0.:
351            offset = 2.*radii[1]*ecc+f      #select other branch
352            xy = [-R*cosd(azm)-offset,R*sind(azm)]
353        else:
354            offset = -f
355            xy = [-R*cosd(azm)-offset,-R*sind(azm)]
356        xy = -np.array([xy[0]*cosd(phi)+xy[1]*sind(phi),xy[0]*sind(phi)-xy[1]*cosd(phi)])
357        xy += cent
358    return xy
359   
360def GetDetXYfromThAzm(Th,Azm,data):
361    'Needs a doc string'
362    dsp = data['wavelength']/(2.0*npsind(Th))   
363    return GetDetectorXY(dsp,azm,data)
364                   
365def GetTthAzmDsp(x,y,data):
366    'Needs a doc string - checked OK for ellipses & hyperbola'
367    wave = data['wavelength']
368    cent = data['center']
369    tilt = data['tilt']
370    dist = data['distance']/cosd(tilt)
371    x0 = data['distance']*tand(tilt)
372    phi = data['rotation']
373    dep = data['DetDepth']
374    LRazim = data['LRazimuth']
375    azmthoff = data['azmthOff']
376    dx = np.array(x-cent[0],dtype=np.float32)
377    dy = np.array(y-cent[1],dtype=np.float32)
378    D = ((dx-x0)**2+dy**2+data['distance']**2)      #sample to pixel distance
379    X = np.array(([dx,dy,np.zeros_like(dx)]),dtype=np.float32).T
380    X = np.dot(X,makeMat(phi,2))
381    Z = np.dot(X,makeMat(tilt,0)).T[2]
382    tth = npatand(np.sqrt(dx**2+dy**2-Z**2)/(dist-Z))
383    dxy = peneCorr(tth,dep,tilt,npatan2d(dy,dx))
384    DX = dist-Z+dxy
385    DY = np.sqrt(dx**2+dy**2-Z**2)
386    tth = npatan2d(DY,DX) 
387    dsp = wave/(2.*npsind(tth/2.))
388    azm = (npatan2d(dy,dx)+azmthoff+720.)%360.
389    G = D/data['distance']**2       #for geometric correction = 1/cos(2theta)^2 if tilt=0.
390    return np.array([tth,azm,G,dsp])
391   
392def GetTth(x,y,data):
393    'Give 2-theta value for detector x,y position; calibration info in data'
394    return GetTthAzmDsp(x,y,data)[0]
395   
396def GetTthAzm(x,y,data):
397    'Give 2-theta, azimuth values for detector x,y position; calibration info in data'
398    return GetTthAzmDsp(x,y,data)[0:2]
399   
400def GetTthAzmD(x,y,data):
401    '''Give 2-theta, azimuth & d-spacing values for detector x,y position;
402     calibration info in data
403    '''
404    return GetTthAzmDsp(x,y,data)[0:3]
405
406def GetDsp(x,y,data):
407    'Give d-spacing value for detector x,y position; calibration info in data'
408    return GetTthAzmDsp(x,y,data)[3]
409       
410def GetAzm(x,y,data):
411    'Give azimuth value for detector x,y position; calibration info in data'
412    return GetTthAzmDsp(x,y,data)[1]
413       
414def ImageCompress(image,scale):
415    'Needs a doc string'
416    if scale == 1:
417        return image
418    else:
419        return image[::scale,::scale]
420       
421def checkEllipse(Zsum,distSum,xSum,ySum,dist,x,y):
422    'Needs a doc string'
423    avg = np.array([distSum/Zsum,xSum/Zsum,ySum/Zsum])
424    curr = np.array([dist,x,y])
425    return abs(avg-curr)/avg < .02
426
427def EdgeFinder(image,data):
428    '''this makes list of all x,y where I>edgeMin suitable for an ellipse search?
429    Not currently used but might be useful in future?
430    '''
431    import numpy.ma as ma
432    Nx,Ny = data['size']
433    pixelSize = data['pixelSize']
434    edgemin = data['edgemin']
435    scalex = pixelSize[0]/1000.
436    scaley = pixelSize[1]/1000.   
437    tay,tax = np.mgrid[0:Nx,0:Ny]
438    tax = np.asfarray(tax*scalex,dtype=np.float32)
439    tay = np.asfarray(tay*scaley,dtype=np.float32)
440    tam = ma.getmask(ma.masked_less(image.flatten(),edgemin))
441    tax = ma.compressed(ma.array(tax.flatten(),mask=tam))
442    tay = ma.compressed(ma.array(tay.flatten(),mask=tam))
443    return zip(tax,tay)
444   
445def MakeFrameMask(data,frame):
446    pixelSize = data['pixelSize']
447    scalex = pixelSize[0]/1000.
448    scaley = pixelSize[1]/1000.
449    blkSize = 512
450    Nx,Ny = data['size']
451    nXBlks = (Nx-1)/blkSize+1
452    nYBlks = (Ny-1)/blkSize+1
453    tam = ma.make_mask_none(data['size'])
454    for iBlk in range(nXBlks):
455        iBeg = iBlk*blkSize
456        iFin = min(iBeg+blkSize,Nx)
457        for jBlk in range(nYBlks):
458            jBeg = jBlk*blkSize
459            jFin = min(jBeg+blkSize,Ny)               
460            nI = iFin-iBeg
461            nJ = jFin-jBeg
462            tax,tay = np.mgrid[iBeg+0.5:iFin+.5,jBeg+.5:jFin+.5]         #bin centers not corners
463            tax = np.asfarray(tax*scalex,dtype=np.float32)
464            tay = np.asfarray(tay*scaley,dtype=np.float32)
465            tamp = ma.make_mask_none((1024*1024))
466            tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
467                tay.flatten(),len(frame),frame,tamp)[:nI*nJ])-True  #switch to exclude around frame
468            if tamp.shape:
469                tamp = np.reshape(tamp[:nI*nJ],(nI,nJ))
470                tam[iBeg:iFin,jBeg:jFin] = ma.mask_or(tamp[0:nI,0:nJ],tam[iBeg:iFin,jBeg:jFin])
471            else:
472                tam[iBeg:iFin,jBeg:jFin] = True
473    return tam.T
474   
475def ImageRecalibrate(self,data,masks):
476    'Needs a doc string'
477    import ImageCalibrants as calFile
478    print 'Image recalibration:'
479    time0 = time.time()
480    pixelSize = data['pixelSize']
481    scalex = 1000./pixelSize[0]
482    scaley = 1000./pixelSize[1]
483    pixLimit = data['pixLimit']
484    cutoff = data['cutoff']
485    data['rings'] = []
486    data['ellipses'] = []
487    if not data['calibrant']:
488        print 'no calibration material selected'
489        return True   
490    skip = data['calibskip']
491    dmin = data['calibdmin']
492    Bravais,Cells = calFile.Calibrants[data['calibrant']][:2]
493    HKL = []
494    for bravais,cell in zip(Bravais,Cells):
495        A = G2lat.cell2A(cell)
496        hkl = G2lat.GenHBravais(dmin,bravais,A)
497        HKL += hkl
498    HKL = G2lat.sortHKLd(HKL,True,False)
499    varyList = ['dist','det-X','det-Y','tilt','phi']
500    parmDict = {'dist':data['distance'],'det-X':data['center'][0],'det-Y':data['center'][1],
501        'tilt':data['tilt'],'phi':data['rotation'],'wave':data['wavelength'],'dep':data['DetDepth']}
502    Found = False
503    wave = data['wavelength']
504    frame = masks['Frames']
505    tam = ma.make_mask_none(self.ImageZ.shape)
506    if frame:
507        tam = ma.mask_or(tam,MakeFrameMask(data,frame))
508    for iH,H in enumerate(HKL): 
509        dsp = H[3]
510        tth = 2.0*asind(wave/(2.*dsp))
511        if tth+abs(data['tilt']) > 90.:
512            print 'next line is a hyperbola - search stopped'
513            break
514        ellipse = GetEllipse(dsp,data)
515        Ring = makeRing(dsp,ellipse,pixLimit,cutoff,scalex,scaley,ma.array(self.ImageZ,mask=tam))
516        if Ring:
517            if iH >= skip:
518                data['rings'].append(np.array(Ring))
519            data['ellipses'].append(copy.deepcopy(ellipse+('r',)))
520            Found = True
521        elif not Found:         #skipping inner rings, keep looking until ring found
522            continue
523        else:                   #no more rings beyond edge of detector
524            break
525    rings = np.concatenate((data['rings']),axis=0)
526    if data['DetDepthRef']:
527        varyList.append('dep')
528    FitDetector(rings,varyList,parmDict)
529    data['distance'] = parmDict['dist']
530    data['center'] = [parmDict['det-X'],parmDict['det-Y']]
531    data['rotation'] = np.mod(parmDict['phi'],360.0)
532    data['tilt'] = parmDict['tilt']
533    data['DetDepth'] = parmDict['dep']
534    N = len(data['ellipses'])
535    data['ellipses'] = []           #clear away individual ellipse fits
536    for H in HKL[:N]:
537        ellipse = GetEllipse(H[3],data)
538        data['ellipses'].append(copy.deepcopy(ellipse+('b',)))
539   
540    print 'calibration time = ',time.time()-time0
541    G2plt.PlotImage(self,newImage=True)       
542    return True
543           
544def ImageCalibrate(self,data):
545    'Needs a doc string'
546    import copy
547    import ImageCalibrants as calFile
548    print 'Image calibration:'
549    time0 = time.time()
550    ring = data['ring']
551    pixelSize = data['pixelSize']
552    scalex = 1000./pixelSize[0]
553    scaley = 1000./pixelSize[1]
554    pixLimit = data['pixLimit']
555    cutoff = data['cutoff']
556    if len(ring) < 5:
557        print 'not enough inner ring points for ellipse'
558        return False
559       
560    #fit start points on inner ring
561    data['ellipses'] = []
562    data['rings'] = []
563    outE = FitEllipse(ring)
564    fmt  = '%s X: %.3f, Y: %.3f, phi: %.3f, R1: %.3f, R2: %.3f'
565    fmt2 = '%s X: %.3f, Y: %.3f, phi: %.3f, R1: %.3f, R2: %.3f, chi^2: %.3f, N: %d'
566    if outE:
567        print fmt%('start ellipse: ',outE[0][0],outE[0][1],outE[1],outE[2][0],outE[2][1])
568        ellipse = outE
569    else:
570        return False
571       
572    #setup 360 points on that ring for "good" fit
573    data['ellipses'].append(ellipse[:]+('g',))
574    Ring = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ)
575    if Ring:
576        ellipse = FitEllipse(Ring)
577        Ring = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ)    #do again
578        ellipse = FitEllipse(Ring)
579    else:
580        print '1st ring not sufficiently complete to proceed'
581        return False
582    print fmt2%('inner ring:    ',ellipse[0][0],ellipse[0][1],ellipse[1],ellipse[2][0],ellipse[2][1],0.,len(Ring))     #cent,phi,radii
583    data['ellipses'].append(ellipse[:]+('r',))
584    data['rings'].append(np.array(Ring))
585    G2plt.PlotImage(self,newImage=True)
586   
587#setup for calibration
588    data['rings'] = []
589    if not data['calibrant']:
590        print 'no calibration material selected'
591        return True
592   
593    skip = data['calibskip']
594    dmin = data['calibdmin']
595#generate reflection set
596    Bravais,Cells = calFile.Calibrants[data['calibrant']][:2]
597    HKL = []
598    for bravais,cell in zip(Bravais,Cells):
599        A = G2lat.cell2A(cell)
600        hkl = G2lat.GenHBravais(dmin,bravais,A)[skip:]
601        HKL += hkl
602    HKL = G2lat.sortHKLd(HKL,True,False)
603    wave = data['wavelength']
604#set up 1st ring
605    elcent,phi,radii = ellipse              #from fit of 1st ring
606    dsp = HKL[0][3]
607    tth = 2.0*asind(wave/(2.*dsp))
608    Ring0 = makeRing(dsp,ellipse,3,cutoff,scalex,scaley,self.ImageZ)
609    ttth = nptand(tth)
610    stth = npsind(tth)
611    ctth = npcosd(tth)
612#1st estimate of tilt; assume ellipse - don't know sign though
613    tilt = npasind(np.sqrt(max(0.,1.-(radii[0]/radii[1])**2))*ctth)
614    if not tilt:
615        print 'WARNING - selected ring was fitted as a circle'
616        print ' - if detector was tilted we suggest you skip this ring - WARNING'
617#1st estimate of dist: sample to detector normal to plane
618    data['distance'] = dist = radii[0]**2/(ttth*radii[1])
619#ellipse to cone axis (x-ray beam); 2 choices depending on sign of tilt
620    zdisp = radii[1]*ttth*tand(tilt)
621    zdism = radii[1]*ttth*tand(-tilt)
622#cone axis position; 2 choices. Which is right?     
623#NB: zdisp is || to major axis & phi is rotation of minor axis
624#thus shift from beam to ellipse center is [Z*sin(phi),-Z*cos(phi)]
625    centp = [elcent[0]+zdisp*sind(phi),elcent[1]-zdisp*cosd(phi)]
626    centm = [elcent[0]+zdism*sind(phi),elcent[1]-zdism*cosd(phi)]
627#check get same ellipse parms either way
628#now do next ring; estimate either way & do a FitDetector each way; best fit is correct one
629    dsp = HKL[1][3]
630    tth = 2.0*asind(wave/(2.*dsp))
631    ellipsep = GetEllipse2(tth,0.,dist,centp,tilt,phi)
632    print fmt%('plus ellipse :',ellipsep[0][0],ellipsep[0][1],ellipsep[1],ellipsep[2][0],ellipsep[2][1])
633    Ringp = makeRing(dsp,ellipsep,3,cutoff,scalex,scaley,self.ImageZ)
634    parmDict = {'dist':dist,'det-X':centp[0],'det-Y':centp[1],
635        'tilt':tilt,'phi':phi,'wave':wave,'dep':0.0}
636    varyList = ['dist','det-X','det-Y','tilt','phi']
637    if len(Ringp) > 10:
638        chip,chisqp = FitDetector(np.array(Ring0+Ringp),varyList,parmDict,True)
639        tiltp = parmDict['tilt']
640        phip = parmDict['phi']
641        centp = [parmDict['det-X'],parmDict['det-Y']]
642    else:
643        chip = 1e6
644    ellipsem = GetEllipse2(tth,0.,dist,centm,-tilt,phi)
645    print fmt%('minus ellipse:',ellipsem[0][0],ellipsem[0][1],ellipsem[1],ellipsem[2][0],ellipsem[2][1])
646    Ringm = makeRing(dsp,ellipsem,3,cutoff,scalex,scaley,self.ImageZ)
647    if len(Ringm) > 10:
648        parmDict['tilt'] *= -1
649        chim,chisqm = FitDetector(np.array(Ring0+Ringm),varyList,parmDict,True)
650        tiltm = parmDict['tilt']
651        phim = parmDict['phi']
652        centm = [parmDict['det-X'],parmDict['det-Y']]
653    else:
654        chim = 1e6
655    if chip < chim:
656        data['tilt'] = tiltp
657        data['center'] = centp
658        data['rotation'] = phip
659    else:
660        data['tilt'] = tiltm
661        data['center'] = centm
662        data['rotation'] = phim
663    data['ellipses'].append(ellipsep[:]+('b',))
664    data['rings'].append(np.array(Ringp))
665    data['ellipses'].append(ellipsem[:]+('r',))
666    data['rings'].append(np.array(Ringm))
667    G2plt.PlotImage(self,newImage=True)
668    parmDict = {'dist':data['distance'],'det-X':data['center'][0],'det-Y':data['center'][1],
669        'tilt':data['tilt'],'phi':data['rotation'],'wave':data['wavelength'],'dep':data['DetDepth']}
670    varyList = ['dist','det-X','det-Y','tilt','phi']
671    if data['DetDepthRef']:
672        varyList.append('dep')
673    data['rings'] = []
674    data['ellipses'] = []
675    for i,H in enumerate(HKL):
676        dsp = H[3]
677        tth = 2.0*asind(wave/(2.*dsp))
678        if tth+abs(data['tilt']) > 90.:
679            print 'next line is a hyperbola - search stopped'
680            break
681        print 'HKLD:',H[:4],'2-theta: %.4f'%(tth)
682        elcent,phi,radii = ellipse = GetEllipse(dsp,data)
683        data['ellipses'].append(copy.deepcopy(ellipse+('g',)))
684        print fmt%('predicted ellipse:',elcent[0],elcent[1],phi,radii[0],radii[1])
685        Ring = makeRing(dsp,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ)
686        if Ring:
687            data['rings'].append(np.array(Ring))
688            rings = np.concatenate((data['rings']),axis=0)
689            if i:
690                chi,chisq = FitDetector(rings,varyList,parmDict,False)
691                data['distance'] = parmDict['dist']
692                data['center'] = [parmDict['det-X'],parmDict['det-Y']]
693                data['rotation'] = parmDict['phi']
694                data['tilt'] = parmDict['tilt']
695                data['DetDepth'] = parmDict['dep']
696                elcent,phi,radii = ellipse = GetEllipse(dsp,data)
697                print fmt2%('fitted ellipse:   ',elcent[0],elcent[1],phi,radii[0],radii[1],chisq,len(rings))
698            data['ellipses'].append(copy.deepcopy(ellipse+('r',)))
699#            G2plt.PlotImage(self,newImage=True)
700        else:
701            print 'insufficient number of points in this ellipse to fit'
702            break
703    G2plt.PlotImage(self,newImage=True)
704    fullSize = len(self.ImageZ)/scalex
705    if 2*radii[1] < .9*fullSize:
706        print 'Are all usable rings (>25% visible) used? Try reducing Min ring I/Ib'
707    N = len(data['ellipses'])
708    if N > 2:
709        FitDetector(rings,varyList,parmDict)
710        data['distance'] = parmDict['dist']
711        data['center'] = [parmDict['det-X'],parmDict['det-Y']]
712        data['rotation'] = parmDict['phi']
713        data['tilt'] = parmDict['tilt']
714        data['DetDepth'] = parmDict['dep']
715    for H in HKL[:N]:
716        ellipse = GetEllipse(H[3],data)
717        data['ellipses'].append(copy.deepcopy(ellipse+('b',)))
718    print 'calibration time = ',time.time()-time0
719    G2plt.PlotImage(self,newImage=True)       
720    return True
721   
722def Make2ThetaAzimuthMap(data,masks,iLim,jLim):
723    'Needs a doc string'
724    #transforms 2D image from x,y space to 2-theta,azimuth space based on detector orientation
725    pixelSize = data['pixelSize']
726    scalex = pixelSize[0]/1000.
727    scaley = pixelSize[1]/1000.
728   
729    tay,tax = np.mgrid[iLim[0]+0.5:iLim[1]+.5,jLim[0]+.5:jLim[1]+.5]         #bin centers not corners
730    tax = np.asfarray(tax*scalex,dtype=np.float32)
731    tay = np.asfarray(tay*scaley,dtype=np.float32)
732    nI = iLim[1]-iLim[0]
733    nJ = jLim[1]-jLim[0]
734    #make position masks here
735    frame = masks['Frames']
736    tam = ma.make_mask_none((nI,nJ))
737    if frame:
738        tamp = ma.make_mask_none((1024*1024))
739        tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
740            tay.flatten(),len(frame),frame,tamp)[:nI*nJ])-True  #switch to exclude around frame
741        tam = ma.mask_or(tam.flatten(),tamp)
742    polygons = masks['Polygons']
743    for polygon in polygons:
744        if polygon:
745            tamp = ma.make_mask_none((1024*1024))
746            tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(),
747                tay.flatten(),len(polygon),polygon,tamp)[:nI*nJ])
748            tam = ma.mask_or(tam.flatten(),tamp)
749    if tam.shape: tam = np.reshape(tam,(nI,nJ))
750    spots = masks['Points']
751    for X,Y,diam in spots:
752        tamp = ma.getmask(ma.masked_less((tax-X)**2+(tay-Y)**2,(diam/2.)**2))
753        tam = ma.mask_or(tam,tamp)
754    TA = np.array(GetTthAzmD(tax,tay,data))     #includes geom. corr. as dist**2/d0**2
755    TA[1] = np.where(TA[1]<0,TA[1]+360,TA[1])
756    return np.array(TA),tam           #2-theta, azimuth & geom. corr. arrays & position mask
757
758def Fill2ThetaAzimuthMap(masks,TA,tam,image):
759    'Needs a doc string'
760    Zlim = masks['Thresholds'][1]
761    rings = masks['Rings']
762    arcs = masks['Arcs']
763    TA = np.dstack((ma.getdata(TA[1]),ma.getdata(TA[0]),ma.getdata(TA[2])))    #azimuth, 2-theta, dist
764    tax,tay,tad = np.dsplit(TA,3)    #azimuth, 2-theta, dist**2/d0**2
765    for tth,thick in rings:
766        tam = ma.mask_or(tam.flatten(),ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.)))
767    for tth,azm,thick in arcs:
768        tamt = ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.))
769        tama = ma.getmask(ma.masked_inside(tax.flatten(),azm[0],azm[1]))
770        tam = ma.mask_or(tam.flatten(),tamt*tama)
771    taz = ma.masked_outside(image.flatten(),int(Zlim[0]),Zlim[1])
772    tabs = np.ones_like(taz)
773    tam = ma.mask_or(tam.flatten(),ma.getmask(taz))
774    tax = ma.compressed(ma.array(tax.flatten(),mask=tam))
775    tay = ma.compressed(ma.array(tay.flatten(),mask=tam))
776    taz = ma.compressed(ma.array(taz.flatten(),mask=tam))
777    tad = ma.compressed(ma.array(tad.flatten(),mask=tam))
778    tabs = ma.compressed(ma.array(tabs.flatten(),mask=tam))
779    return tax,tay,taz,tad,tabs
780   
781def ImageIntegrate(image,data,masks,blkSize=128,dlg=None):
782    'Needs a doc string'
783    import histogram2d as h2d
784    print 'Begin image integration'
785    LUtth = data['IOtth']
786    LRazm = np.array(data['LRazimuth'],dtype=np.float64)
787    numAzms = data['outAzimuths']
788    numChans = data['outChannels']
789    Dtth = (LUtth[1]-LUtth[0])/numChans
790    Dazm = (LRazm[1]-LRazm[0])/numAzms
791    if data['centerAzm']:
792        LRazm += Dazm/2.
793    NST = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
794    H0 = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32)
795    imageN = len(image)
796    Nx,Ny = data['size']
797    nXBlks = (Nx-1)/blkSize+1
798    nYBlks = (Ny-1)/blkSize+1
799    Nup = nXBlks*nYBlks*3+3
800    t0 = time.time()
801    Nup = 0
802    if dlg:
803        dlg.Update(Nup)
804    for iBlk in range(nYBlks):
805        iBeg = iBlk*blkSize
806        iFin = min(iBeg+blkSize,Ny)
807        for jBlk in range(nXBlks):
808            jBeg = jBlk*blkSize
809            jFin = min(jBeg+blkSize,Nx)               
810            TA,tam = Make2ThetaAzimuthMap(data,masks,(iBeg,iFin),(jBeg,jFin))           #2-theta & azimuth arrays & create position mask
811           
812            Nup += 1
813            if dlg:
814                dlg.Update(Nup)
815            Block = image[iBeg:iFin,jBeg:jFin]
816            tax,tay,taz,tad,tabs = Fill2ThetaAzimuthMap(masks,TA,tam,Block)    #and apply masks
817            Nup += 1
818            if dlg:
819                dlg.Update(Nup)
820            tax = np.where(tax > LRazm[1],tax-360.,tax)                 #put azm inside limits if possible
821            tax = np.where(tax < LRazm[0],tax+360.,tax)
822            if data['SampleAbs'][1]:
823                muR = data['SampleAbs'][0]*(1.+npsind(tax)**2/2.)/(npcosd(tay))
824                tabs = G2pwd.Absorb(data['SampleShape'],muR,tay)
825            if any([tax.shape[0],tay.shape[0],taz.shape[0]]):
826                NST,H0 = h2d.histogram2d(len(tax),tax,tay,taz*tad*tabs,numAzms,numChans,LRazm,LUtth,Dazm,Dtth,NST,H0)
827            Nup += 1
828            if dlg:
829                dlg.Update(Nup)
830    NST = np.array(NST)
831    H0 = np.divide(H0,NST)
832    H0 = np.nan_to_num(H0)
833    del NST
834    if Dtth:
835        H2 = np.array([tth for tth in np.linspace(LUtth[0],LUtth[1],numChans+1)])
836    else:
837        H2 = np.array(LUtth)
838    if Dazm:       
839        H1 = np.array([azm for azm in np.linspace(LRazm[0],LRazm[1],numAzms+1)])
840    else:
841        H1 = LRazm
842    H0 /= npcosd(H2[:-1])**2
843    if data['Oblique'][1]:
844        H0 /= G2pwd.Oblique(data['Oblique'][0],H2[:-1])
845    if 'SASD' in data['type'] and data['PolaVal'][1]:
846        H0 /= np.array([G2pwd.Polarization(data['PolaVal'][0],H2[:-1],Azm=azm)[0] for azm in H1[:-1]+np.diff(H1)/2.])
847    Nup += 1
848    if dlg:
849        dlg.Update(Nup)
850    t1 = time.time()
851    print 'Integration complete'
852    print "Elapsed time:","%8.3f"%(t1-t0), "s"
853    return H0,H1,H2
854   
855def MakeStrStaRing(ring,Image,Controls):
856    ellipse = GetEllipse(ring['Dset'],Controls)
857    pixSize = Controls['pixelSize']
858    scalex = 1000./pixSize[0]
859    scaley = 1000./pixSize[1]
860    Ring = np.array(makeRing(ring['Dset'],ellipse,ring['pixLimit'],ring['cutoff'],scalex,scaley,Image)).T   #returns x,y,dsp for each point in ring
861    ring['ImxyObs'] = Ring[:2]
862    TA = GetTthAzm(Ring[0],Ring[1],Controls)       #convert x,y to tth,azm
863    TA[0] = Controls['wavelength']/(2.*npsind(TA[0]/2.))      #convert 2th to d
864    ring['ImtaObs'] = TA
865    ring['ImtaCalc'] = np.zeros_like(ring['ImtaObs'])
866    Ring[0] = TA[0]
867    Ring[1] = TA[1]
868    return Ring,ring
869   
870def FitStrSta(Image,StrSta,Controls):
871    'Needs a doc string'
872   
873    StaControls = copy.deepcopy(Controls)
874    phi = StrSta['Sample phi']
875    wave = Controls['wavelength']
876    StaControls['distance'] += StrSta['Sample z']*cosd(phi)
877    rings = []
878
879    for ring in StrSta['d-zero']:       #get observed x,y,d points for the d-zeros
880        Ring,R = MakeStrStaRing(ring,Image,StaControls)
881        ring.update(R)
882        if len(rings):
883            rings = np.concatenate((rings,Ring),axis=1)
884        else:
885            rings = np.array(Ring)     
886    E = StrSta['strain']
887    p0 = [E[0][0],E[0][1],E[1][1]]
888    E = FitStrain(rings,p0,wave,phi)
889    StrSta['strain'] = E
890    CalcStrSta(StrSta,Controls)
891   
892def CalcStrSta(StrSta,Controls):
893
894    wave = Controls['wavelength']
895    phi = StrSta['Sample phi']
896    E = StrSta['strain']
897    for ring in StrSta['d-zero']:
898        th,azm = ring['ImtaObs']
899        th0 = np.ones_like(azm)*npasind(wave/(2.*ring['Dset']))
900        V = 1.+np.sum(np.sum(E*calcFij(90.,phi,azm,th0).T/1.e6,axis=2),axis=1)
901        ring['ImtaCalc'] = np.array([V*ring['Dset'],azm])
902
903def calcFij(omg,phi,azm,th):
904    '''Does something...
905
906    Uses parameters as defined by Bob He & Kingsley Smith, Adv. in X-Ray Anal. 41, 501 (1997)
907
908    :param omg: his omega = sample omega rotation; 0 when incident beam || sample surface,
909        90 when perp. to sample surface
910    :param phi: his phi = sample phi rotation; usually = 0, axis rotates with omg.
911    :param azm: his chi = azimuth around incident beam
912    :param th:  his theta = theta
913    '''
914    a = npsind(th)*npcosd(omg)+npsind(azm)*npcosd(th)*npsind(omg)
915    b = -npcosd(azm)*npcosd(th)
916    c = npsind(th)*npsind(omg)-npsind(azm)*npcosd(th)*npcosd(omg)
917    d = a*npcosd(phi)-b*npsind(phi)
918    e = a*npsind(phi)+b*npcosd(phi)
919    Fij = np.array([
920        [d**2,d*e,c*d],
921        [d*e,e**2,c*e],
922        [c*d,c*e,c**2]])
923    return -Fij*nptand(th)
924
925def FitStrain(rings,p0,wave,phi):
926    'Needs a doc string'
927    def StrainPrint(ValSig):
928        print 'Strain tensor:'
929        ptlbls = 'names :'
930        ptstr =  'values:'
931        sigstr = 'esds  :'
932        for name,fmt,value,sig in ValSig:
933            ptlbls += "%s" % (name.rjust(12))
934            ptstr += fmt % (value)
935            if sig:
936                sigstr += fmt % (sig)
937            else:
938                sigstr += 12*' '
939        print ptlbls
940        print ptstr
941        print sigstr
942       
943    def strainCalc(p,xyd,wave,phi):
944        E = np.array([[p[0],p[1],0],[p[1],p[2],0],[0,0,0]])
945        dspo,azm,dsp = xyd
946        th = npasind(wave/(2.0*dspo))
947        V = 1.+np.sum(np.sum(E*calcFij(90.,phi,azm,th).T/1.e6,axis=2),axis=1)
948        dspc = dsp*V
949        return dspo-dspc
950       
951    names = ['e11','e12','e22','e33','e13','e23']
952    fmt = ['%12.2f','%12.2f','%12.2f','%12.2f','%12.2f','%12.5f']
953    p1 = [-20000.,0,5000.] 
954    result = leastsq(strainCalc,p1,args=(rings,wave,phi),full_output=True)
955    vals = list(result[0])
956    chi = np.sqrt(np.sum(strainCalc(result[0],rings,wave,phi)**2))
957    sig = list(chi*np.sqrt(np.diag(result[1])))
958    ValSig = zip(names,fmt,vals,sig)
959    StrainPrint(ValSig)
960    return np.array([[vals[0],vals[1],0],[vals[1],vals[2],0],[0,0,0]])
961   
962       
Note: See TracBrowser for help on using the repository browser.