source: trunk/GSASIIimage.py @ 1174

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

correct image integration for sample absorption - cylinders only for now

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