1 | #GSASII image calculations: ellipse fitting & image integration |
---|
2 | ########### SVN repository information ################### |
---|
3 | # $Date: 2011-08-19 02:22:29 +0000 (Fri, 19 Aug 2011) $ |
---|
4 | # $Author: vondreele $ |
---|
5 | # $Revision: 347 $ |
---|
6 | # $URL: trunk/GSASIIimage.py $ |
---|
7 | # $Id: GSASIIimage.py 347 2011-08-19 02:22:29Z vondreele $ |
---|
8 | ########### SVN repository information ################### |
---|
9 | import math |
---|
10 | import wx |
---|
11 | import time |
---|
12 | import numpy as np |
---|
13 | import numpy.linalg as nl |
---|
14 | import GSASIIpath |
---|
15 | import GSASIIplot as G2plt |
---|
16 | import GSASIIlattice as G2lat |
---|
17 | import fellipse as fel |
---|
18 | |
---|
19 | # trig functions in degrees |
---|
20 | sind = lambda x: math.sin(x*math.pi/180.) |
---|
21 | asind = lambda x: 180.*math.asin(x)/math.pi |
---|
22 | tand = lambda x: math.tan(x*math.pi/180.) |
---|
23 | atand = lambda x: 180.*math.atan(x)/math.pi |
---|
24 | atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi |
---|
25 | cosd = lambda x: math.cos(x*math.pi/180.) |
---|
26 | acosd = lambda x: 180.*math.acos(x)/math.pi |
---|
27 | rdsq2d = lambda x,p: round(1.0/math.sqrt(x),p) |
---|
28 | #numpy versions |
---|
29 | npsind = lambda x: np.sin(x*np.pi/180.) |
---|
30 | npasind = lambda x: 180.*np.arcsin(x)/np.pi |
---|
31 | npcosd = lambda x: np.cos(x*np.pi/180.) |
---|
32 | nptand = lambda x: np.tan(x*np.pi/180.) |
---|
33 | npatand = lambda x: 180.*np.arctan(x)/np.pi |
---|
34 | npatan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi |
---|
35 | |
---|
36 | def pointInPolygon(pXY,xy): |
---|
37 | #pXY - assumed closed 1st & last points are duplicates |
---|
38 | Inside = False |
---|
39 | N = len(pXY) |
---|
40 | p1x,p1y = pXY[0] |
---|
41 | for i in range(N+1): |
---|
42 | p2x,p2y = pXY[i%N] |
---|
43 | if (max(p1y,p2y) >= xy[1] > min(p1y,p2y)) and (xy[0] <= max(p1x,p2x)): |
---|
44 | if p1y != p2y: |
---|
45 | xinters = (xy[1]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x |
---|
46 | if p1x == p2x or xy[0] <= xinters: |
---|
47 | Inside = not Inside |
---|
48 | p1x,p1y = p2x,p2y |
---|
49 | return Inside |
---|
50 | |
---|
51 | def makeMat(Angle,Axis): |
---|
52 | #Make rotation matrix from Angle in degrees,Axis =0 for rotation about x, =1 for about y, etc. |
---|
53 | cs = cosd(Angle) |
---|
54 | ss = sind(Angle) |
---|
55 | M = np.array(([1.,0.,0.],[0.,cs,-ss],[0.,ss,cs]),dtype=np.float32) |
---|
56 | return np.roll(np.roll(M,Axis,axis=0),Axis,axis=1) |
---|
57 | |
---|
58 | def FitRing(ring,delta): |
---|
59 | parms = [] |
---|
60 | if delta: |
---|
61 | err,parms = FitEllipse(ring) |
---|
62 | errc,parmsc = FitCircle(ring) |
---|
63 | errc = errc[0]/(len(ring)*parmsc[2][0]**2) |
---|
64 | if not parms or errc < .1: |
---|
65 | parms = parmsc |
---|
66 | else: |
---|
67 | err,parms = FitCircle(ring) |
---|
68 | return parms |
---|
69 | |
---|
70 | def FitCircle(ring): |
---|
71 | |
---|
72 | def makeParmsCircle(B): |
---|
73 | cent = [-B[0]/2,-B[1]/2] |
---|
74 | phi = 0. |
---|
75 | sr1 = sr2 = math.sqrt(cent[0]**2+cent[1]**2-B[2]) |
---|
76 | return cent,phi,[sr1,sr2] |
---|
77 | |
---|
78 | ring = np.array(ring) |
---|
79 | x = np.asarray(ring.T[0]) |
---|
80 | y = np.asarray(ring.T[1]) |
---|
81 | |
---|
82 | M = np.array((x,y,np.ones_like(x))) |
---|
83 | B = np.array(-(x**2+y**2)) |
---|
84 | result = nl.lstsq(M.T,B) |
---|
85 | return result[1],makeParmsCircle(result[0]) |
---|
86 | |
---|
87 | def FitEllipse(ring): |
---|
88 | |
---|
89 | def makeParmsEllipse(B): |
---|
90 | det = 4.*(1.-B[0]**2)-B[1]**2 |
---|
91 | if det < 0.: |
---|
92 | print 'hyperbola!' |
---|
93 | return 0 |
---|
94 | elif det == 0.: |
---|
95 | print 'parabola!' |
---|
96 | return 0 |
---|
97 | cent = [(B[1]*B[3]-2.*(1.-B[0])*B[2])/det, \ |
---|
98 | (B[1]*B[2]-2.*(1.+B[0])*B[3])/det] |
---|
99 | phi = 0.5*atand(0.5*B[1]/B[0]) |
---|
100 | |
---|
101 | a = (1.+B[0])/cosd(2*phi) |
---|
102 | b = 2.-a |
---|
103 | f = (1.+B[0])*cent[0]**2+(1.-B[0])*cent[1]**2+B[1]*cent[0]*cent[1]-B[4] |
---|
104 | if f/a < 0 or f/b < 0: |
---|
105 | return 0 |
---|
106 | sr1 = math.sqrt(f/a) |
---|
107 | sr2 = math.sqrt(f/b) |
---|
108 | if sr1 > sr2: |
---|
109 | sr1,sr2 = sr2,sr1 |
---|
110 | phi -= 90. |
---|
111 | if phi < -90.: |
---|
112 | phi += 180. |
---|
113 | return cent,phi,[sr1,sr2] |
---|
114 | |
---|
115 | ring = np.array(ring) |
---|
116 | x = np.asarray(ring.T[0]) |
---|
117 | y = np.asarray(ring.T[1]) |
---|
118 | M = np.array((x**2-y**2,x*y,x,y,np.ones_like(x))) |
---|
119 | B = np.array(-(x**2+y**2)) |
---|
120 | bb,err = fel.fellipse(len(x),x,y,1.E-7) |
---|
121 | # print nl.lstsq(M.T,B)[0] |
---|
122 | # print bb |
---|
123 | return err,makeParmsEllipse(bb) |
---|
124 | |
---|
125 | def FitDetector(rings,p0,wave): |
---|
126 | from scipy.optimize import leastsq |
---|
127 | |
---|
128 | def CalibPrint(ValSig): |
---|
129 | print 'Image Parameters:' |
---|
130 | ptlbls = 'names :' |
---|
131 | ptstr = 'values:' |
---|
132 | sigstr = 'esds :' |
---|
133 | for name,fmt,value,sig in ValSig: |
---|
134 | ptlbls += "%s" % (name.rjust(12)) |
---|
135 | if name == 'rotate': |
---|
136 | ptstr += fmt % (value-90.) #kluge to get rotation from vertical - see GSASIIimgGUI |
---|
137 | else: |
---|
138 | ptstr += fmt % (value) |
---|
139 | if sig: |
---|
140 | sigstr += fmt % (sig) |
---|
141 | else: |
---|
142 | sigstr += 12*' ' |
---|
143 | print ptlbls |
---|
144 | print ptstr |
---|
145 | print sigstr |
---|
146 | |
---|
147 | def ellipseCalcD(B,xyd,wave): |
---|
148 | x = xyd[0] |
---|
149 | y = xyd[1] |
---|
150 | dsp = xyd[2] |
---|
151 | dist,x0,y0,phi,tilt = B |
---|
152 | tth = 2.0*npasind(wave/(2.*dsp)) |
---|
153 | ttth = nptand(tth) |
---|
154 | radius = dist*ttth |
---|
155 | stth = npsind(tth) |
---|
156 | cosb = npcosd(tilt) |
---|
157 | R1 = dist*stth*npcosd(tth)*cosb/(cosb**2-stth**2) |
---|
158 | R0 = np.sqrt(R1*radius*cosb) |
---|
159 | zdis = R1*ttth*nptand(tilt) |
---|
160 | X = x-x0+zdis*npsind(phi) |
---|
161 | Y = y-y0-zdis*npcosd(phi) |
---|
162 | XR = X*npcosd(phi)-Y*npsind(phi) |
---|
163 | YR = X*npsind(phi)+Y*npcosd(phi) |
---|
164 | return (XR/R0)**2+(YR/R1)**2-1 |
---|
165 | |
---|
166 | def ellipseCalcW(C,xyd): |
---|
167 | dist,x0,y0,phi,tilt,wave = C |
---|
168 | B = dist,x0,y0,phi,tilt |
---|
169 | return ellipseCalcD(B,xyd,wave) |
---|
170 | |
---|
171 | names = ['distance','det-X0','det-Y0','rotate','tilt','wavelength'] |
---|
172 | fmt = ['%12.2f','%12.2f','%12.2f','%12.2f','%12.2f','%12.5f'] |
---|
173 | result = leastsq(ellipseCalcD,p0,args=(rings.T,wave),full_output=True) |
---|
174 | result[0][3] = np.mod(result[0][3],360.0) #remove random full circles |
---|
175 | vals = list(result[0]) |
---|
176 | vals.append(wave) |
---|
177 | chi = np.sqrt(np.sum(ellipseCalcD(result[0],rings.T,wave)**2)) |
---|
178 | sig = list(chi*np.sqrt(np.diag(result[1]))) |
---|
179 | sig.append(0.) |
---|
180 | ValSig = zip(names,fmt,vals,sig) |
---|
181 | CalibPrint(ValSig) |
---|
182 | try: |
---|
183 | print 'Trial refinement of wavelength - not used for calibration' |
---|
184 | p0 = result[0] |
---|
185 | p0 = np.append(p0,wave) |
---|
186 | resultW = leastsq(ellipseCalcW,p0,args=(rings.T,),full_output=True) |
---|
187 | resultW[0][3] = np.mod(result[0][3],360.0) #remove random full circles |
---|
188 | sig = np.sqrt(np.sum(ellipseCalcW(resultW[0],rings.T)**2)) |
---|
189 | ValSig = zip(names,fmt,resultW[0],sig*np.sqrt(np.diag(resultW[1]))) |
---|
190 | CalibPrint(ValSig) |
---|
191 | return result[0],resultW[0][-1] |
---|
192 | except ValueError: |
---|
193 | print 'Bad wavelength refinement - no result' |
---|
194 | return result[0],wave |
---|
195 | |
---|
196 | def ImageLocalMax(image,w,Xpix,Ypix): |
---|
197 | w2 = w*2 |
---|
198 | sizey,sizex = image.shape |
---|
199 | xpix = int(Xpix) #get reference corner of pixel chosen |
---|
200 | ypix = int(Ypix) |
---|
201 | if (w < xpix < sizex-w) and (w < ypix < sizey-w) and image[ypix,xpix]: |
---|
202 | Z = image[ypix-w:ypix+w,xpix-w:xpix+w] |
---|
203 | Zmax = np.argmax(Z) |
---|
204 | Zmin = np.argmin(Z) |
---|
205 | xpix += Zmax%w2-w |
---|
206 | ypix += Zmax/w2-w |
---|
207 | return xpix,ypix,np.ravel(Z)[Zmax],np.ravel(Z)[Zmin] |
---|
208 | else: |
---|
209 | return 0,0,0,0 |
---|
210 | |
---|
211 | def makeRing(dsp,ellipse,pix,reject,scalex,scaley,image): |
---|
212 | cent,phi,radii = ellipse |
---|
213 | cphi = cosd(phi) |
---|
214 | sphi = sind(phi) |
---|
215 | ring = [] |
---|
216 | amin = 180 |
---|
217 | amax = -180 |
---|
218 | for a in range(-180,180,1): |
---|
219 | x = radii[0]*cosd(a) |
---|
220 | y = radii[1]*sind(a) |
---|
221 | X = (cphi*x-sphi*y+cent[0])*scalex #convert mm to pixels |
---|
222 | Y = (sphi*x+cphi*y+cent[1])*scaley |
---|
223 | X,Y,I,J = ImageLocalMax(image,pix,X,Y) |
---|
224 | if I and J and I/J > reject: |
---|
225 | X += .5 #set to center of pixel |
---|
226 | Y += .5 |
---|
227 | X /= scalex #convert to mm |
---|
228 | Y /= scaley |
---|
229 | amin = min(amin,a) |
---|
230 | amax = max(amax,a) |
---|
231 | ring.append([X,Y,dsp]) |
---|
232 | delt = amax-amin |
---|
233 | if len(ring) < 20: #want more than 20 deg |
---|
234 | return [],delt > 90 |
---|
235 | return ring,delt > 90 |
---|
236 | |
---|
237 | def makeIdealRing(ellipse,azm=None): |
---|
238 | cent,phi,radii = ellipse |
---|
239 | cphi = cosd(phi) |
---|
240 | sphi = sind(phi) |
---|
241 | if azm: |
---|
242 | aR = azm[0]-90,azm[1]-90,azm[1]-azm[0] |
---|
243 | if azm[1]-azm[0] > 180: |
---|
244 | aR[2] /= 2 |
---|
245 | else: |
---|
246 | aR = 0,362,181 |
---|
247 | |
---|
248 | a = np.linspace(aR[0],aR[1],aR[2]) |
---|
249 | x = radii[0]*npcosd(a-phi) |
---|
250 | y = radii[1]*npsind(a-phi) |
---|
251 | X = (cphi*x-sphi*y+cent[0]) |
---|
252 | Y = (sphi*x+cphi*y+cent[1]) |
---|
253 | return zip(X,Y) |
---|
254 | |
---|
255 | def calcDist(radii,tth): |
---|
256 | stth = sind(tth) |
---|
257 | ctth = cosd(tth) |
---|
258 | ttth = tand(tth) |
---|
259 | return math.sqrt(radii[0]**4/(ttth**2*((radii[0]*ctth)**2+(radii[1]*stth)**2))) |
---|
260 | |
---|
261 | def calcZdisCosB(radius,tth,radii): |
---|
262 | cosB = sinb = radii[0]**2/(radius*radii[1]) |
---|
263 | if cosB > 1.: |
---|
264 | return 0.,1. |
---|
265 | else: |
---|
266 | cosb = math.sqrt(1.-sinb**2) |
---|
267 | ttth = tand(tth) |
---|
268 | zdis = radii[1]*ttth*cosb/sinb |
---|
269 | return zdis,cosB |
---|
270 | |
---|
271 | def GetEllipse(dsp,data): |
---|
272 | dist = data['distance'] |
---|
273 | cent = data['center'] |
---|
274 | tilt = data['tilt'] |
---|
275 | phi = data['rotation'] |
---|
276 | radii = [0,0] |
---|
277 | tth = 2.0*asind(data['wavelength']/(2.*dsp)) |
---|
278 | ttth = tand(tth) |
---|
279 | stth = sind(tth) |
---|
280 | ctth = cosd(tth) |
---|
281 | cosb = cosd(tilt) |
---|
282 | radius = dist*ttth |
---|
283 | radii[1] = dist*stth*ctth*cosb/(cosb**2-stth**2) |
---|
284 | if radii[1] > 0: |
---|
285 | radii[0] = math.sqrt(radii[1]*radius*cosb) |
---|
286 | zdis = radii[1]*ttth*tand(tilt) |
---|
287 | elcent = [cent[0]-zdis*sind(phi),cent[1]+zdis*cosd(phi)] |
---|
288 | return elcent,phi,radii |
---|
289 | else: |
---|
290 | return False |
---|
291 | |
---|
292 | def GetDetectorXY(dsp,azm,data): |
---|
293 | from scipy.optimize import fsolve |
---|
294 | def func(xy,*args): |
---|
295 | azm,phi,R0,R1,A,B = args |
---|
296 | cp = cosd(phi) |
---|
297 | sp = sind(phi) |
---|
298 | x,y = xy |
---|
299 | out = [] |
---|
300 | out.append(y-x*tand(azm)) |
---|
301 | out.append(R0**2*((x+A)*sp-(y+B)*cp)**2+R1**2*((x+A)*cp+(y+B)*sp)**2-(R0*R1)**2) |
---|
302 | return out |
---|
303 | elcent,phi,radii = GetEllipse(dsp,data) |
---|
304 | cent = data['center'] |
---|
305 | tilt = data['tilt'] |
---|
306 | phi = data['rotation'] |
---|
307 | wave = data['wavelength'] |
---|
308 | dist = data['distance'] |
---|
309 | tth = 2.0*asind(wave/(2.*dsp)) |
---|
310 | ttth = tand(tth) |
---|
311 | radius = dist*ttth |
---|
312 | stth = sind(tth) |
---|
313 | cosb = cosd(tilt) |
---|
314 | R1 = dist*stth*cosd(tth)*cosb/(cosb**2-stth**2) |
---|
315 | R0 = math.sqrt(R1*radius*cosb) |
---|
316 | zdis = R1*ttth*tand(tilt) |
---|
317 | A = zdis*sind(phi) |
---|
318 | B = -zdis*cosd(phi) |
---|
319 | xy0 = [radius*cosd(azm),radius*sind(azm)] |
---|
320 | xy = fsolve(func,xy0,args=(azm,phi,R0,R1,A,B))+cent |
---|
321 | return xy |
---|
322 | |
---|
323 | def GetTthAzmDsp(x,y,data): |
---|
324 | wave = data['wavelength'] |
---|
325 | dist = data['distance'] |
---|
326 | cent = data['center'] |
---|
327 | tilt = data['tilt'] |
---|
328 | phi = data['rotation'] |
---|
329 | LRazim = data['LRazimuth'] |
---|
330 | azmthoff = data['azmthOff'] |
---|
331 | dx = np.array(x-cent[0],dtype=np.float32) |
---|
332 | dy = np.array(y-cent[1],dtype=np.float32) |
---|
333 | X = np.array(([dx,dy,np.zeros_like(dx)]),dtype=np.float32).T |
---|
334 | X = np.dot(X,makeMat(phi,2)) |
---|
335 | Z = np.dot(X,makeMat(tilt,0)).T[2] |
---|
336 | tth = npatand(np.sqrt(dx**2+dy**2-Z**2)/(dist-Z)) |
---|
337 | dsp = wave/(2.*npsind(tth/2.)) |
---|
338 | azm = (npatan2d(dx,-dy)+azmthoff+720.)%360. |
---|
339 | azm = np.where(azm>180,azm-360.,azm) |
---|
340 | return tth,azm,dsp |
---|
341 | |
---|
342 | def GetTth(x,y,data): |
---|
343 | return GetTthAzmDsp(x,y,data)[0] |
---|
344 | |
---|
345 | def GetTthAzm(x,y,data): |
---|
346 | return GetTthAzmDsp(x,y,data)[0:2] |
---|
347 | |
---|
348 | def GetDsp(x,y,data): |
---|
349 | return GetTthAzmDsp(x,y,data)[2] |
---|
350 | |
---|
351 | def GetAzm(x,y,data): |
---|
352 | return GetTthAzmDsp(x,y,data)[1] |
---|
353 | |
---|
354 | def ImageCompress(image,scale): |
---|
355 | if scale == 1: |
---|
356 | return image |
---|
357 | else: |
---|
358 | return image[::scale,::scale] |
---|
359 | |
---|
360 | def checkEllipse(Zsum,distSum,xSum,ySum,dist,x,y): |
---|
361 | avg = np.array([distSum/Zsum,xSum/Zsum,ySum/Zsum]) |
---|
362 | curr = np.array([dist,x,y]) |
---|
363 | return abs(avg-curr)/avg < .02 |
---|
364 | |
---|
365 | def EdgeFinder(image,data): #this makes list of all x,y where I>edgeMin suitable for an ellipse search? |
---|
366 | import numpy.ma as ma |
---|
367 | Nx,Ny = data['size'] |
---|
368 | pixelSize = data['pixelSize'] |
---|
369 | edgemin = data['edgemin'] |
---|
370 | scalex = pixelSize[0]/1000. |
---|
371 | scaley = pixelSize[1]/1000. |
---|
372 | tay,tax = np.mgrid[0:Nx,0:Ny] |
---|
373 | tax = np.asfarray(tax*scalex,dtype=np.float32) |
---|
374 | tay = np.asfarray(tay*scaley,dtype=np.float32) |
---|
375 | tam = ma.getmask(ma.masked_less(image.flatten(),edgemin)) |
---|
376 | tax = ma.compressed(ma.array(tax.flatten(),mask=tam)) |
---|
377 | tay = ma.compressed(ma.array(tay.flatten(),mask=tam)) |
---|
378 | return zip(tax,tay) |
---|
379 | |
---|
380 | def ImageCalibrate(self,data): |
---|
381 | import copy |
---|
382 | import ImageCalibrants as calFile |
---|
383 | print 'Image calibration:' |
---|
384 | time0 = time.time() |
---|
385 | ring = data['ring'] |
---|
386 | pixelSize = data['pixelSize'] |
---|
387 | scalex = 1000./pixelSize[0] |
---|
388 | scaley = 1000./pixelSize[1] |
---|
389 | pixLimit = data['pixLimit'] |
---|
390 | cutoff = data['cutoff'] |
---|
391 | if len(ring) < 5: |
---|
392 | print 'not enough inner ring points for ellipse' |
---|
393 | return False |
---|
394 | |
---|
395 | #fit start points on inner ring |
---|
396 | data['ellipses'] = [] |
---|
397 | outE = FitRing(ring,True) |
---|
398 | if outE: |
---|
399 | # print 'start ellipse:',outE |
---|
400 | ellipse = outE |
---|
401 | else: |
---|
402 | return False |
---|
403 | |
---|
404 | #setup 360 points on that ring for "good" fit |
---|
405 | Ring,delt = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ) |
---|
406 | if Ring: |
---|
407 | ellipse = FitRing(Ring,delt) |
---|
408 | Ring,delt = makeRing(1.0,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ) #do again |
---|
409 | ellipse = FitRing(Ring,delt) |
---|
410 | else: |
---|
411 | print '1st ring not sufficiently complete to proceed' |
---|
412 | return False |
---|
413 | # print 'inner ring:',ellipse |
---|
414 | data['center'] = copy.copy(ellipse[0]) #not right!! (but useful for now) |
---|
415 | data['ellipses'].append(ellipse[:]+('r',)) |
---|
416 | G2plt.PlotImage(self,newImage=True) |
---|
417 | |
---|
418 | #setup for calibration |
---|
419 | data['rings'] = [] |
---|
420 | data['ellipses'] = [] |
---|
421 | if not data['calibrant']: |
---|
422 | print 'no calibration material selected' |
---|
423 | return True |
---|
424 | |
---|
425 | skip = data['calibskip'] |
---|
426 | dmin = data['calibdmin'] |
---|
427 | Bravais,Cells = calFile.Calibrants[data['calibrant']][:2] |
---|
428 | HKL = [] |
---|
429 | for bravais,cell in zip(Bravais,Cells): |
---|
430 | A = G2lat.cell2A(cell) |
---|
431 | hkl = G2lat.GenHBravais(dmin,bravais,A)[skip:] |
---|
432 | HKL += hkl |
---|
433 | HKL = G2lat.sortHKLd(HKL,True,False) |
---|
434 | wave = data['wavelength'] |
---|
435 | cent = data['center'] |
---|
436 | elcent,phi,radii = ellipse |
---|
437 | dsp = HKL[0][3] |
---|
438 | tth = 2.0*asind(wave/(2.*dsp)) |
---|
439 | ttth = tand(tth) |
---|
440 | data['distance'] = dist = calcDist(radii,tth) |
---|
441 | radius = dist*tand(tth) |
---|
442 | zdis,cosB = calcZdisCosB(radius,tth,radii) |
---|
443 | cent1 = [] |
---|
444 | cent2 = [] |
---|
445 | xSum = 0 |
---|
446 | ySum = 0 |
---|
447 | zxSum = 0 |
---|
448 | zySum = 0 |
---|
449 | phiSum = 0 |
---|
450 | tiltSum = 0 |
---|
451 | distSum = 0 |
---|
452 | Zsum = 0 |
---|
453 | for i,H in enumerate(HKL): |
---|
454 | dsp = H[3] |
---|
455 | tth = 2.0*asind(0.5*wave/dsp) |
---|
456 | stth = sind(tth) |
---|
457 | ctth = cosd(tth) |
---|
458 | ttth = tand(tth) |
---|
459 | radius = dist*ttth |
---|
460 | elcent,phi,radii = ellipse |
---|
461 | if i: |
---|
462 | radii[1] = dist*stth*ctth*cosB/(cosB**2-stth**2) |
---|
463 | radii[0] = math.sqrt(radii[1]*radius*cosB) |
---|
464 | zdis,cosB = calcZdisCosB(radius,tth,radii) |
---|
465 | zsinp = zdis*sind(phi) |
---|
466 | zcosp = zdis*cosd(phi) |
---|
467 | cent = data['center'] |
---|
468 | elcent = [cent[0]-zsinp,cent[1]+zcosp] |
---|
469 | ellipse = (elcent,phi,radii) |
---|
470 | ratio = radii[1]/radii[0] |
---|
471 | Ring,delt = makeRing(dsp,ellipse,pixLimit,cutoff,scalex,scaley,self.ImageZ) |
---|
472 | if Ring: |
---|
473 | numZ = len(Ring) |
---|
474 | data['rings'].append(np.array(Ring)) |
---|
475 | newellipse = FitRing(Ring,delt) |
---|
476 | elcent,phi,radii = newellipse |
---|
477 | if abs(phi) > 45. and phi < 0.: |
---|
478 | phi += 180. |
---|
479 | dist = calcDist(radii,tth) |
---|
480 | distR = 1.-dist/data['distance'] |
---|
481 | if abs(distR) > 0.1: |
---|
482 | # print dsp,dist,data['distance'],distR,len(Ring),delt |
---|
483 | break |
---|
484 | if distR > 0.001: |
---|
485 | print 'Wavelength too large?' |
---|
486 | elif distR < -0.001: |
---|
487 | print 'Wavelength too small?' |
---|
488 | else: |
---|
489 | ellipse = newellipse |
---|
490 | zdis,cosB = calcZdisCosB(radius,tth,radii) |
---|
491 | Tilt = acosd(cosB) # 0 <= tilt <= 90 |
---|
492 | zsinp = zdis*sind(ellipse[1]) |
---|
493 | zcosp = zdis*cosd(ellipse[1]) |
---|
494 | cent1.append(np.array([elcent[0]+zsinp,elcent[1]-zcosp])) |
---|
495 | cent2.append(np.array([elcent[0]-zsinp,elcent[1]+zcosp])) |
---|
496 | if i: |
---|
497 | d1 = cent1[-1]-cent1[-2] #get shift of 2 possible center solutions |
---|
498 | d2 = cent2[-1]-cent2[-2] |
---|
499 | if np.dot(d2,d2) > np.dot(d1,d1): #right solution is the larger shift |
---|
500 | data['center'] = cent1[-1] |
---|
501 | else: |
---|
502 | data['center'] = cent2[-1] |
---|
503 | Zsum += numZ |
---|
504 | phiSum += numZ*phi |
---|
505 | distSum += numZ*dist |
---|
506 | xSum += numZ*data['center'][0] |
---|
507 | ySum += numZ*data['center'][1] |
---|
508 | tiltSum += numZ*abs(Tilt) |
---|
509 | if not np.all(checkEllipse(Zsum,distSum,xSum,ySum,dist,data['center'][0],data['center'][1])): |
---|
510 | print 'Bad fit for ring # %i. Try reducing Pixel search range'%(i) |
---|
511 | cent = data['center'] |
---|
512 | # print ('for ring # %2i @ d-space %.4f: dist %.3f rotate %6.2f tilt %6.2f Xcent %.3f Ycent %.3f Npts %d' |
---|
513 | # %(i,dsp,dist,phi-90.,Tilt,cent[0],cent[1],numZ)) |
---|
514 | data['ellipses'].append(copy.deepcopy(ellipse+('r',))) |
---|
515 | else: |
---|
516 | break |
---|
517 | G2plt.PlotImage(self,newImage=True) |
---|
518 | fullSize = len(self.ImageZ)/scalex |
---|
519 | if 2*radii[1] < .9*fullSize: |
---|
520 | print 'Are all usable rings (>25% visible) used? Try reducing Min ring I/Ib' |
---|
521 | if not Zsum: |
---|
522 | print 'Only one ring fitted. Check your wavelength.' |
---|
523 | return False |
---|
524 | cent = data['center'] = [xSum/Zsum,ySum/Zsum] |
---|
525 | wave = data['wavelength'] |
---|
526 | dist = data['distance'] = distSum/Zsum |
---|
527 | |
---|
528 | #possible error if no. of rings < 3! Might need trap here |
---|
529 | d1 = cent1[-1]-cent1[1] #compare last ring to 2nd ring |
---|
530 | d2 = cent2[-1]-cent2[1] |
---|
531 | Zsign = 1 |
---|
532 | len1 = math.sqrt(np.dot(d1,d1)) |
---|
533 | len2 = math.sqrt(np.dot(d2,d2)) |
---|
534 | t1 = d1/len1 |
---|
535 | t2 = d2/len2 |
---|
536 | if len2 > len1: |
---|
537 | if -135. < atan2d(t2[1],t2[0]) < 45.: |
---|
538 | Zsign = -1 |
---|
539 | else: |
---|
540 | if -135. < atan2d(t1[1],t1[0]) < 45.: |
---|
541 | Zsign = -1 |
---|
542 | |
---|
543 | tilt = data['tilt'] = Zsign*tiltSum/Zsum |
---|
544 | phi = data['rotation'] = phiSum/Zsum |
---|
545 | rings = np.concatenate((data['rings']),axis=0) |
---|
546 | p0 = [dist,cent[0],cent[1],phi,tilt] |
---|
547 | result,newWave = FitDetector(rings,p0,wave) |
---|
548 | # print 'Suggested new wavelength = ',('%.5f')%(newWave),' (not reliable if distance > 2m)' |
---|
549 | data['distance'] = result[0] |
---|
550 | data['center'] = result[1:3] |
---|
551 | data['rotation'] = np.mod(result[3],360.0) |
---|
552 | data['tilt'] = result[4] |
---|
553 | N = len(data['ellipses']) |
---|
554 | data['ellipses'] = [] #clear away individual ellipse fits |
---|
555 | for H in HKL[:N]: |
---|
556 | ellipse = GetEllipse(H[3],data) |
---|
557 | data['ellipses'].append(copy.deepcopy(ellipse+('b',))) |
---|
558 | print 'calibration time = ',time.time()-time0 |
---|
559 | G2plt.PlotImage(self,newImage=True) |
---|
560 | return True |
---|
561 | |
---|
562 | def Make2ThetaAzimuthMap(data,masks,iLim,jLim): |
---|
563 | import numpy.ma as ma |
---|
564 | import polymask as pm |
---|
565 | #transforms 2D image from x,y space to 2-theta,azimuth space based on detector orientation |
---|
566 | pixelSize = data['pixelSize'] |
---|
567 | scalex = pixelSize[0]/1000. |
---|
568 | scaley = pixelSize[1]/1000. |
---|
569 | |
---|
570 | # tay,tax = np.mgrid[iLim[0]+0.5:iLim[1]+.5,jLim[0]+.5:jLim[1]+.5] #bin centers not corners |
---|
571 | tay,tax = np.mgrid[iLim[0]:iLim[1],jLim[0]:jLim[1]] #bin corners |
---|
572 | tax = np.asfarray(tax*scalex,dtype=np.float32) |
---|
573 | tay = np.asfarray(tay*scaley,dtype=np.float32) |
---|
574 | nI = iLim[1]-iLim[0] |
---|
575 | nJ = jLim[1]-jLim[0] |
---|
576 | #make position masks here |
---|
577 | spots = masks['Points'] |
---|
578 | polygons = masks['Polygons'] |
---|
579 | tam = ma.make_mask_none((nI,nJ)) |
---|
580 | for polygon in polygons: |
---|
581 | if polygon: |
---|
582 | tamp = ma.make_mask_none((nI*nJ)) |
---|
583 | tamp = ma.make_mask(pm.polymask(nI*nJ,tax.flatten(), |
---|
584 | tay.flatten(),len(polygon),polygon,tamp)) |
---|
585 | tam = ma.mask_or(tam.flatten(),tamp) |
---|
586 | if tam.shape: tam = np.reshape(tam,(nI,nJ)) |
---|
587 | for X,Y,diam in spots: |
---|
588 | tamp = ma.getmask(ma.masked_less((tax-X)**2+(tay-Y)**2,(diam/2.)**2)) |
---|
589 | tam = ma.mask_or(tam,tamp) |
---|
590 | TA = np.array(GetTthAzm(tax,tay,data)) |
---|
591 | TA[1] = np.where(TA[1]<0,TA[1]+360,TA[1]) |
---|
592 | return np.array(TA),tam #2-theta & azimuth arrays & position mask |
---|
593 | |
---|
594 | def Fill2ThetaAzimuthMap(masks,TA,tam,image): |
---|
595 | import numpy.ma as ma |
---|
596 | Zlim = masks['Thresholds'][1] |
---|
597 | rings = masks['Rings'] |
---|
598 | arcs = masks['Arcs'] |
---|
599 | TA = np.dstack((ma.getdata(TA[1]),ma.getdata(TA[0]))) #azimuth, 2-theta |
---|
600 | tax,tay = np.dsplit(TA,2) #azimuth, 2-theta |
---|
601 | for tth,thick in rings: |
---|
602 | tam = ma.mask_or(tam.flatten(),ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.))) |
---|
603 | for tth,azm,thick in arcs: |
---|
604 | tamt = ma.getmask(ma.masked_inside(tay.flatten(),max(0.01,tth-thick/2.),tth+thick/2.)) |
---|
605 | tama = ma.getmask(ma.masked_inside(tax.flatten(),azm[0],azm[1])) |
---|
606 | tam = ma.mask_or(tam.flatten(),tamt*tama) |
---|
607 | taz = ma.masked_outside(image.flatten(),int(Zlim[0]),Zlim[1]) |
---|
608 | tam = ma.mask_or(tam.flatten(),ma.getmask(taz)) |
---|
609 | tax = ma.compressed(ma.array(tax.flatten(),mask=tam)) |
---|
610 | tay = ma.compressed(ma.array(tay.flatten(),mask=tam)) |
---|
611 | taz = ma.compressed(ma.array(taz.flatten(),mask=tam)) |
---|
612 | del(tam) |
---|
613 | return tax,tay,taz |
---|
614 | |
---|
615 | def ImageIntegrate(image,data,masks): |
---|
616 | import histogram2d as h2d |
---|
617 | print 'Begin image integration' |
---|
618 | LUtth = data['IOtth'] |
---|
619 | LRazm = data['LRazimuth'] |
---|
620 | numAzms = data['outAzimuths'] |
---|
621 | numChans = data['outChannels'] |
---|
622 | Dtth = (LUtth[1]-LUtth[0])/numChans |
---|
623 | Dazm = (LRazm[1]-LRazm[0])/numAzms |
---|
624 | NST = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32) |
---|
625 | H0 = np.zeros(shape=(numAzms,numChans),order='F',dtype=np.float32) |
---|
626 | imageN = len(image) |
---|
627 | Nx,Ny = data['size'] |
---|
628 | nXBlks = (Nx-1)/1024+1 |
---|
629 | nYBlks = (Ny-1)/1024+1 |
---|
630 | Nup = nXBlks*nYBlks*3+3 |
---|
631 | dlg = wx.ProgressDialog("Elapsed time","2D image integration",Nup, |
---|
632 | style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE) |
---|
633 | try: |
---|
634 | t0 = time.time() |
---|
635 | Nup = 0 |
---|
636 | dlg.Update(Nup) |
---|
637 | for iBlk in range(nYBlks): |
---|
638 | iBeg = iBlk*1024 |
---|
639 | iFin = min(iBeg+1024,Ny) |
---|
640 | for jBlk in range(nXBlks): |
---|
641 | jBeg = jBlk*1024 |
---|
642 | jFin = min(jBeg+1024,Nx) |
---|
643 | print 'Process map block:',iBlk,jBlk,' limits:',iBeg,iFin,jBeg,jFin |
---|
644 | TA,tam = Make2ThetaAzimuthMap(data,masks,(iBeg,iFin),(jBeg,jFin)) #2-theta & azimuth arrays & create position mask |
---|
645 | |
---|
646 | Nup += 1 |
---|
647 | dlg.Update(Nup) |
---|
648 | Block = image[iBeg:iFin,jBeg:jFin] |
---|
649 | tax,tay,taz = Fill2ThetaAzimuthMap(masks,TA,tam,Block) #and apply masks |
---|
650 | del TA,tam |
---|
651 | Nup += 1 |
---|
652 | dlg.Update(Nup) |
---|
653 | tax = np.where(tax > LRazm[1],tax-360.,tax) #put azm inside limits if possible |
---|
654 | tax = np.where(tax < LRazm[0],tax+360.,tax) |
---|
655 | NST,H0 = h2d.histogram2d(len(tax),tax,tay,taz,numAzms,numChans,LRazm,LUtth,Dazm,Dtth,NST,H0) |
---|
656 | print 'block done' |
---|
657 | del tax,tay,taz |
---|
658 | Nup += 1 |
---|
659 | dlg.Update(Nup) |
---|
660 | NST = np.array(NST) |
---|
661 | H0 = np.divide(H0,NST) |
---|
662 | H0 = np.nan_to_num(H0) |
---|
663 | del NST |
---|
664 | if Dtth: |
---|
665 | H2 = [tth for tth in np.linspace(LUtth[0],LUtth[1],numChans+1)] |
---|
666 | else: |
---|
667 | H2 = LUtth |
---|
668 | if Dazm: |
---|
669 | H1 = np.array([azm for azm in np.linspace(LRazm[0],LRazm[1],numAzms+1)]) |
---|
670 | else: |
---|
671 | H1 = LRazm |
---|
672 | Nup += 1 |
---|
673 | dlg.Update(Nup) |
---|
674 | t1 = time.time() |
---|
675 | finally: |
---|
676 | dlg.Destroy() |
---|
677 | print 'Integration complete' |
---|
678 | print "Elapsed time:","%8.3f"%(t1-t0), "s" |
---|
679 | return H0,H1,H2 |
---|