1 | #scanCCD data processing |
---|
2 | ''' |
---|
3 | *scanCCD: reduce data from scanning CCD* |
---|
4 | ======================================== |
---|
5 | |
---|
6 | Quickly prototyped routine for reduction of data from detector described in |
---|
7 | B.H. Toby, T.J. Madden, M.R. Suchomel, J.D. Baldwin, and R.B. Von Dreele, |
---|
8 | "A Scanning CCD Detector for Powder Diffraction Measurements". |
---|
9 | Journal of Applied Crystallography. 46(4): p. 1058-63 (2013). |
---|
10 | |
---|
11 | ''' |
---|
12 | import os |
---|
13 | import os.path as ospath |
---|
14 | import sys |
---|
15 | import math |
---|
16 | import time |
---|
17 | import numpy as np |
---|
18 | import numpy.linalg as nl |
---|
19 | import numpy.ma as ma |
---|
20 | import wx |
---|
21 | import matplotlib as mpl |
---|
22 | import GSASIIpath |
---|
23 | import GSASIIIO as G2IO |
---|
24 | import GSASIIimage as G2img |
---|
25 | import GSASIIplot as G2plt |
---|
26 | |
---|
27 | npsind = lambda x: np.sin(x*np.pi/180.) |
---|
28 | npcosd = lambda x: np.cos(x*np.pi/180.) |
---|
29 | npacosd = lambda x: 180.*np.arccos(x)/np.pi |
---|
30 | npasind = lambda x: 180.*np.arcsin(x)/np.pi |
---|
31 | |
---|
32 | def create(parent): |
---|
33 | return scanCCD(parent) |
---|
34 | |
---|
35 | [wxID_FILEEXIT, wxID_FILEOPEN, wxID_INTEGRATE, wxID_OUTPUT, |
---|
36 | ] = [wx.NewId() for _init_coll_File_Items in range(4)] |
---|
37 | |
---|
38 | def FileDlgFixExt(dlg,file): #this is needed to fix a problem in linux wx.FileDialog |
---|
39 | ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*') |
---|
40 | if ext not in file: |
---|
41 | file += ext |
---|
42 | return file |
---|
43 | |
---|
44 | class scanCCD(wx.Frame): |
---|
45 | |
---|
46 | def _init_ctrls(self, parent): |
---|
47 | wx.Frame.__init__(self, name='scanCCD', parent=parent, |
---|
48 | size=wx.Size(460, 250),style=wx.DEFAULT_FRAME_STYLE, title='scanCCD') |
---|
49 | self.scanCCDMenu = wx.MenuBar() |
---|
50 | self.File = wx.Menu(title='') |
---|
51 | self.File.Append(help='Open scanCCD image files (*.tif)', id=wxID_FILEOPEN, |
---|
52 | kind=wx.ITEM_NORMAL,text='Open scanCCD files') |
---|
53 | self.File.Append(help='Integrate scanCCD images',id=wxID_INTEGRATE, |
---|
54 | kind=wx.ITEM_NORMAL,text='Integrate scanCCD images') |
---|
55 | self.File.Append(help='Output fxye file from integration',id=wxID_OUTPUT, |
---|
56 | kind=wx.ITEM_NORMAL,text='Output pattern') |
---|
57 | self.File.Append(help='Exit from scanCCD', id=wxID_FILEEXIT, kind=wx.ITEM_NORMAL, |
---|
58 | text='Exit') |
---|
59 | self.Bind(wx.EVT_MENU, self.OnImageRead, id=wxID_FILEOPEN) |
---|
60 | self.Bind(wx.EVT_MENU,self.OnImageIntegrate,id=wxID_INTEGRATE) |
---|
61 | self.Bind(wx.EVT_MENU,self.OnOutput,id=wxID_OUTPUT) |
---|
62 | self.Bind(wx.EVT_MENU, self.OnFileExit, id=wxID_FILEEXIT) |
---|
63 | self.scanCCDMenu.Append(menu=self.File, title='File') |
---|
64 | self.SetMenuBar(self.scanCCDMenu) |
---|
65 | self.SCCDPanel = wx.Panel(self) |
---|
66 | plotFrame = wx.Frame(None,-1,'scanCCD Plots',size=wx.Size(700,600), \ |
---|
67 | style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX) |
---|
68 | self.plotNB = G2plt.G2PlotNoteBook(plotFrame) |
---|
69 | plotFrame.Show() |
---|
70 | |
---|
71 | def __init__(self, parent): |
---|
72 | self._init_ctrls(parent) |
---|
73 | self.Bind(wx.EVT_CLOSE, self.ExitMain) |
---|
74 | self.dirname = '' |
---|
75 | self.imagefiles = [] |
---|
76 | self.dataFrame = None |
---|
77 | self.itemPicked = None |
---|
78 | self.Image = [] |
---|
79 | self.Hxyw = [] |
---|
80 | self.data = {'color':'Paired','range':[[0,1000],[0,1000]]} |
---|
81 | |
---|
82 | def ExitMain(self, event): |
---|
83 | sys.exit() |
---|
84 | |
---|
85 | def OnFileExit(self,event): |
---|
86 | if self.dataFrame: |
---|
87 | self.dataFrame.Clear() |
---|
88 | self.dataFrame.Destroy() |
---|
89 | self.Close() |
---|
90 | |
---|
91 | def OnImageRead(self,event): |
---|
92 | dlg = wx.FileDialog(self, 'Choose scanCCD image files', '.', '',\ |
---|
93 | 'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|\ |
---|
94 | All files (*.*)|*.*', |
---|
95 | wx.FD_OPEN | wx.FD_MULTIPLE) |
---|
96 | if self.dirname: |
---|
97 | dlg.SetDirectory(self.dirname) |
---|
98 | try: |
---|
99 | self.imagefiles = [] |
---|
100 | if dlg.ShowModal() == wx.ID_OK: |
---|
101 | self.dirname = dlg.GetDirectory() |
---|
102 | self.imagefiles = dlg.GetPaths() |
---|
103 | self.imagefiles.sort() |
---|
104 | self.data['imScale'] = 8 |
---|
105 | self.Image = [] |
---|
106 | for imagefile in self.imagefiles: |
---|
107 | Comments,Data,Npix,image = G2IO.GetImageData(self,imagefile) |
---|
108 | if Comments: |
---|
109 | A = G2img.ImageCompress(image,self.data['imScale']) |
---|
110 | if len(self.Image): |
---|
111 | self.Image = np.concatenate((self.Image,A)) |
---|
112 | else: |
---|
113 | self.Image = A |
---|
114 | self.data['range'][0] = self.data['range'][1] = np.max(self.Image) |
---|
115 | self.data['pixel'] = Data['pixelSize'] |
---|
116 | self.data['size'] = self.Image.shape |
---|
117 | self.data['Zmin'] = np.min(self.Image) |
---|
118 | self.data['Zmax'] = np.max(self.Image) |
---|
119 | self.data['Zeros'] = [-15.3096,16.4] #for bob1-3 files |
---|
120 | self.data['mm/deg'] = 15.56012 #from bob1 fit to 2333 peak positions |
---|
121 | self.data['radius'] = 180.0*self.data['mm/deg']/math.pi |
---|
122 | self.data['TBlimits'] = [0,len(image)] |
---|
123 | self.data['LRlimits'] = [0,len(image)*len(self.imagefiles)] |
---|
124 | self.data['PtScan'] = len(image)/2 |
---|
125 | self.data['2thScan'] = [0.0,90.0,0.001] |
---|
126 | self.data['showBlk'] = False |
---|
127 | self.data['skip'] = 1 #default - skip ramp block |
---|
128 | if len(self.imagefiles) == 1: |
---|
129 | self.data['skip'] = 0 |
---|
130 | self.UpdateControls(event) |
---|
131 | self.PlotImage() |
---|
132 | finally: |
---|
133 | dlg.Destroy() |
---|
134 | |
---|
135 | def OnImageIntegrate(self,event): |
---|
136 | |
---|
137 | def Make2ThetaMap(data,iLim,jLim): |
---|
138 | #transforms scanCCD image from x,y space to 2-theta,y space |
---|
139 | pixelSize = data['pixel'] |
---|
140 | scalex = pixelSize[0]/1000. |
---|
141 | scaley = pixelSize[1]/1000. |
---|
142 | vecA = np.asfarray([1.,0.,0.],dtype=np.float32) |
---|
143 | |
---|
144 | tay,tax = np.mgrid[jLim[0]+.5:jLim[1]+.5,iLim[0]+.5:iLim[1]+.5] #bin centers not corners |
---|
145 | tax = np.asfarray((tax*scalex-data['Zeros'][0])/data['mm/deg'],dtype=np.float32) #scanCCD 2-thetas |
---|
146 | tay = np.asfarray(tay*scaley-data['Zeros'][1],dtype=np.float32) |
---|
147 | vecB = np.array([npcosd(tax)*data['radius'],npsind(tax)*data['radius'],tay]) |
---|
148 | norm = np.sqrt(np.sum((vecB.T*vecB.T),axis=2)) |
---|
149 | vecB /= norm.T |
---|
150 | tax = npacosd(np.dot(vecB.T,vecA))*tax/np.abs(tax) #to get sign of 2-theta |
---|
151 | tay += data['Zeros'][1] |
---|
152 | return tax,tay.T #2-theta arrays & turn y array around!! |
---|
153 | |
---|
154 | |
---|
155 | def Fill2ThetaMap(data,TA,image): |
---|
156 | import numpy.ma as ma |
---|
157 | Zmin = data['Zmin'] |
---|
158 | Zmax = data['Zmax'] |
---|
159 | tax,tay = TA # 2-theta & yaxis |
---|
160 | taz = ma.masked_outside(image.flatten()-Zmin,0,Zmax-Zmin) |
---|
161 | tam = ma.getmask(taz) |
---|
162 | tax = ma.compressed(ma.array(tax.flatten(),mask=tam)) |
---|
163 | tay = ma.compressed(ma.array(tay.flatten(),mask=tam)) |
---|
164 | taz = ma.compressed(ma.array(taz.flatten(),mask=tam)) |
---|
165 | del(tam) |
---|
166 | return tax,tay,taz |
---|
167 | |
---|
168 | import histosigma2d as h2d |
---|
169 | print 'Begin image integration' |
---|
170 | scaley = self.data['pixel'][1]/1000. |
---|
171 | tthStart,tthEnd,tthStep = self.data['2thScan'] |
---|
172 | LUtth = [tthStart,tthEnd] |
---|
173 | TBlim = np.array(self.data['TBlimits'],dtype=np.float32) |
---|
174 | nYpix = TBlim[1]-TBlim[0] |
---|
175 | TBlim *= scaley |
---|
176 | TBdelt = TBlim[1]-TBlim[0] |
---|
177 | nTB = 1 |
---|
178 | numChans = (tthEnd-tthStart)/tthStep |
---|
179 | NST = np.zeros(shape=(numChans,1),order='F',dtype=np.float32) |
---|
180 | H0 = np.zeros(shape=(numChans,1),order='F',dtype=np.float32) |
---|
181 | Amat = np.zeros(shape=(numChans,1),order='F',dtype=np.float32) |
---|
182 | Qmat = np.zeros(shape=(numChans,1),order='F',dtype=np.float32) |
---|
183 | imSize = np.array(self.data['size'])*self.data['imScale'] |
---|
184 | H1 = [tth for tth in np.linspace(LUtth[0],LUtth[1],numChans)] |
---|
185 | blkSize = 2048 |
---|
186 | N = 4096/blkSize |
---|
187 | nBlk = N**2*len(self.imagefiles) #assume 4Kx4K CCD images - done in 1Kx1K blocks |
---|
188 | t0 = time.time() |
---|
189 | dlg = wx.ProgressDialog("Elapsed time","2D image integration",nBlk, |
---|
190 | style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT) |
---|
191 | try: |
---|
192 | nBlk = 0 |
---|
193 | iBegi = 0 |
---|
194 | iFin = 0 |
---|
195 | GoOn = True |
---|
196 | for ifile,imagefile in enumerate(self.imagefiles): |
---|
197 | if ifile >= self.data['skip']: |
---|
198 | image = G2IO.GetImageData(self,imagefile)[3] |
---|
199 | image = np.fliplr(image) |
---|
200 | for iBlk in range(N): |
---|
201 | iBeg = iBegi+iBlk*blkSize |
---|
202 | iFin = iBeg+blkSize |
---|
203 | for jBlk in range(N): |
---|
204 | jBeg = jBlk*blkSize |
---|
205 | jFin = jBeg+blkSize |
---|
206 | print 'Process map block:',nBlk,iBlk,jBlk,' offset:',iBegi,' limits:',iBeg,iFin,jBeg,jFin |
---|
207 | TA = Make2ThetaMap(self.data,(iBeg,iFin),(jBeg,jFin)) #2-theta & Y arrays & create position mask |
---|
208 | Block = image[iBeg-iBegi:iFin-iBegi,jBeg:jFin] |
---|
209 | tax,tay,taz = Fill2ThetaMap(self.data,TA,Block) #and apply masks |
---|
210 | NST,H0,Amat,Qmat = h2d.histosigma2d(len(tax),tax,tay,taz,numChans,nTB,LUtth,TBlim,tthStep,TBdelt,NST,H0,Amat,Qmat) |
---|
211 | H0temp = np.nan_to_num(np.divide(H0,NST)) |
---|
212 | Qtemp = np.nan_to_num(np.divide(Qmat,NST)) |
---|
213 | if self.data['showBlk']: |
---|
214 | self.PlotBlock(Block.T,'%s %d %d'%('Block',iBlk,jBlk)) |
---|
215 | OKdlg = wx.MessageDialog(self,'','Continue',wx.OK) |
---|
216 | try: |
---|
217 | result = OKdlg.ShowModal() |
---|
218 | finally: |
---|
219 | OKdlg.Destroy() |
---|
220 | del tax,tay,taz |
---|
221 | nBlk += 1 |
---|
222 | GoOn = dlg.Update(nBlk)[0] |
---|
223 | if not GoOn: |
---|
224 | break |
---|
225 | else: |
---|
226 | print 'file '+imagefile+' skipped' |
---|
227 | nBlk += N*N |
---|
228 | GoOn = dlg.Update(nBlk)[0] |
---|
229 | iFin += N*blkSize |
---|
230 | if not GoOn: |
---|
231 | break |
---|
232 | iBegi = iFin |
---|
233 | H0 = np.divide(H0,NST) |
---|
234 | H0 = np.nan_to_num(H0) |
---|
235 | Qmat = np.divide(Qmat,NST) |
---|
236 | Qmat = np.nan_to_num(Qmat) |
---|
237 | del NST |
---|
238 | t1 = time.time() |
---|
239 | finally: |
---|
240 | dlg.Destroy() |
---|
241 | Scale = np.sum(Qmat)/np.sum(H0) |
---|
242 | print 'SumI: ',np.sum(H0),' SumV: ',np.sum(Qmat),' Scale:',Scale |
---|
243 | print 'Integration complete' |
---|
244 | print "Elapsed time:","%8.3f"%(t1-t0), "s" |
---|
245 | self.Hxyw = [H1,Scale*H0.T[0],np.sqrt(Qmat.T[0])] |
---|
246 | print |
---|
247 | self.PlotXY(self.Hxyw,True,type='Integration result') |
---|
248 | |
---|
249 | def OnOutput(self,event): |
---|
250 | |
---|
251 | def powderSave(self,powderfile,Fxye=False): |
---|
252 | file = open(powderfile,'w') |
---|
253 | file.write('#%s\n'%('from scanCCD image '+self.imagefiles[0])) |
---|
254 | print 'save powder pattern to file: ',powderfile |
---|
255 | wx.BeginBusyCursor() |
---|
256 | try: |
---|
257 | x,y,e = self.Hxyw |
---|
258 | if Fxye: |
---|
259 | file.write(powderfile+'\n') |
---|
260 | file.write('BANK 1 %d %d CONS %.2f %.2f 0 0 FXYE\n'%(len(x),len(x),\ |
---|
261 | 100.*x[0],100.*(x[1]-x[0]))) |
---|
262 | XYE = zip(x,y,e) |
---|
263 | for X,Y,E in XYE: |
---|
264 | if Fxye: |
---|
265 | file.write("%15.6g %15.6g %15.6g\n" % (100.*X,Y,max(E,1.0))) |
---|
266 | else: |
---|
267 | file.write("%15.6g %15.6g %15.6g\n" % (X,Y,max(E,1.0))) |
---|
268 | file.close() |
---|
269 | finally: |
---|
270 | wx.EndBusyCursor() |
---|
271 | print 'powder pattern file written' |
---|
272 | |
---|
273 | if not self.Hxyw: |
---|
274 | return |
---|
275 | dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', |
---|
276 | 'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye', |
---|
277 | wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) |
---|
278 | if self.dirname: |
---|
279 | dlg.SetDirectory(self.dirname) |
---|
280 | try: |
---|
281 | if dlg.ShowModal() == wx.ID_OK: |
---|
282 | print dlg.GetFilename() |
---|
283 | powderfile = dlg.GetPath() |
---|
284 | if 'fxye' in powderfile: |
---|
285 | powderSave(self,powderfile,Fxye=True) |
---|
286 | else: #just xye |
---|
287 | powderSave(self,powderfile) |
---|
288 | self.dirname = dlg.GetDirectory() |
---|
289 | finally: |
---|
290 | dlg.Destroy() |
---|
291 | |
---|
292 | def UpdateControls(self,event): |
---|
293 | ObjIndx = {} |
---|
294 | self.SCCDPanel.DestroyChildren() |
---|
295 | |
---|
296 | def ColorSizer(data): |
---|
297 | |
---|
298 | def OnNewColorBar(event): |
---|
299 | colSel = event.GetEventObject() |
---|
300 | data['color'] = colSel.GetValue() |
---|
301 | self.PlotImage() |
---|
302 | |
---|
303 | def OnShowBlk(event): |
---|
304 | data['showBlk'] = shoBlk.GetValue() |
---|
305 | |
---|
306 | def OnSkipFiles(event): |
---|
307 | data['skip'] = int(skipFile.GetValue()) |
---|
308 | |
---|
309 | colorList = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")] |
---|
310 | colorSizer = wx.FlexGridSizer(0,5,5,5) |
---|
311 | colorSizer.Add(wx.StaticText(self.SCCDPanel,label=' Color bar '),0,wx.ALIGN_CENTER_VERTICAL) |
---|
312 | colSel = wx.ComboBox(self.SCCDPanel,value=data['color'],choices=colorList, |
---|
313 | style=wx.CB_READONLY|wx.CB_DROPDOWN|wx.CB_SORT) |
---|
314 | colSel.Bind(wx.EVT_COMBOBOX, OnNewColorBar) |
---|
315 | colorSizer.Add(colSel,0,wx.ALIGN_CENTER_VERTICAL) |
---|
316 | colorSizer.Add(wx.StaticText(self.SCCDPanel,label='image files to skip:'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
317 | skipFile = wx.ComboBox(self.SCCDPanel,value=str(data['skip']), |
---|
318 | choices=[str(i) for i in range(len(self.imagefiles))], |
---|
319 | style=wx.CB_READONLY|wx.CB_DROPDOWN) |
---|
320 | skipFile.Bind(wx.EVT_COMBOBOX,OnSkipFiles) |
---|
321 | colorSizer.Add(skipFile,0,wx.ALIGN_CENTER_VERTICAL) |
---|
322 | shoBlk = wx.CheckBox(self.SCCDPanel,-1,label='Show image blocks?') |
---|
323 | shoBlk.SetValue(data['showBlk']) |
---|
324 | shoBlk.Bind(wx.EVT_CHECKBOX, OnShowBlk) |
---|
325 | colorSizer.Add(shoBlk,0,wx.ALIGN_CENTER_VERTICAL) |
---|
326 | return colorSizer |
---|
327 | |
---|
328 | def MaxSizer(data): |
---|
329 | |
---|
330 | def OnMaxSlider(event): |
---|
331 | imax = int(self.maxSel.GetValue())*data['range'][0]/100. |
---|
332 | data['range'][1] = imax |
---|
333 | self.maxVal.SetValue('%d'%(data['range'][1])) |
---|
334 | self.PlotImage() |
---|
335 | |
---|
336 | def OnMaxValue(event): |
---|
337 | try: |
---|
338 | value = int(self.maxVal.GetValue()) |
---|
339 | if value < 0 or value > data['range'][0]: |
---|
340 | raise ValueError |
---|
341 | except ValueError: |
---|
342 | value = data['range'][1] |
---|
343 | data['range'][1] = value |
---|
344 | self.maxSel.SetValue(int(100*value/data['range'][0])) |
---|
345 | self.PlotImage() |
---|
346 | |
---|
347 | maxSizer = wx.FlexGridSizer(0,3,0,5) |
---|
348 | maxSizer.AddGrowableCol(1,1) |
---|
349 | maxSizer.SetFlexibleDirection(wx.HORIZONTAL) |
---|
350 | maxSizer.Add(wx.StaticText(parent=self.SCCDPanel,label=' Max intensity'),0, |
---|
351 | wx.ALIGN_CENTER_VERTICAL) |
---|
352 | self.maxSel = wx.Slider(parent=self.SCCDPanel,style=wx.SL_HORIZONTAL, |
---|
353 | value=int(100*data['range'][1]/data['range'][0])) |
---|
354 | maxSizer.Add(self.maxSel,1,wx.EXPAND) |
---|
355 | self.maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider) |
---|
356 | self.maxVal = wx.TextCtrl(parent=self.SCCDPanel,value='%d'%(data['range'][1])) |
---|
357 | self.maxVal.Bind(wx.EVT_TEXT_ENTER,OnMaxValue) |
---|
358 | self.maxVal.Bind(wx.EVT_KILL_FOCUS,OnMaxValue) |
---|
359 | maxSizer.Add(self.maxVal,0,wx.ALIGN_CENTER_VERTICAL) |
---|
360 | return maxSizer |
---|
361 | |
---|
362 | def ZSizer(data): |
---|
363 | |
---|
364 | def OnZValue(event): |
---|
365 | Obj = event.GetEventObject() |
---|
366 | try: |
---|
367 | value = int(Obj.GetValue()) |
---|
368 | except ValueError: |
---|
369 | value = data[ObjIndx[Obj.GetId()]] |
---|
370 | data[ObjIndx[Obj.GetId()]] = value |
---|
371 | self.PlotImage() |
---|
372 | |
---|
373 | zSizer = wx.FlexGridSizer(0,4,5,5) |
---|
374 | zSizer.Add(wx.StaticText(self.SCCDPanel,label='Upper intensity mask:'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
375 | zMax = wx.TextCtrl(self.SCCDPanel,value='%d'%(data['Zmax'])) |
---|
376 | zMax.Bind(wx.EVT_TEXT_ENTER,OnZValue) |
---|
377 | zMax.Bind(wx.EVT_KILL_FOCUS,OnZValue) |
---|
378 | ObjIndx[zMax.GetId()] = 'Zmax' |
---|
379 | zSizer.Add(zMax) |
---|
380 | zSizer.Add(wx.StaticText(self.SCCDPanel,label='Intensity subtraction:'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
381 | zMin = wx.TextCtrl(self.SCCDPanel,value='%d'%(data['Zmin'])) |
---|
382 | ObjIndx[zMin.GetId()] = 'Zmin' |
---|
383 | zMin.Bind(wx.EVT_TEXT_ENTER,OnZValue) |
---|
384 | zMin.Bind(wx.EVT_KILL_FOCUS,OnZValue) |
---|
385 | zSizer.Add(zMin) |
---|
386 | return zSizer |
---|
387 | |
---|
388 | def ZeroSizer(data): |
---|
389 | |
---|
390 | def OnZeroValue(event): |
---|
391 | Obj = event.GetEventObject() |
---|
392 | item = ObjIndx[Obj.GetId()] |
---|
393 | try: |
---|
394 | value = float(Obj.GetValue()) |
---|
395 | except ValueError: |
---|
396 | value = data[item[0]][item[1]] |
---|
397 | data[item[0]][item[1]] = value |
---|
398 | Obj.SetValue('%.3f'%(value)) |
---|
399 | self.PlotImage() |
---|
400 | |
---|
401 | def OnZpdgValue(event): |
---|
402 | Obj = event.GetEventObject() |
---|
403 | item = ObjIndx[Obj.GetId()] |
---|
404 | try: |
---|
405 | value = float(Obj.GetValue()) |
---|
406 | except ValueError: |
---|
407 | value = self.data[item[0]] |
---|
408 | self.data[item[0]] = value |
---|
409 | self.data['radius'] = 180.0*value/math.pi |
---|
410 | Obj.SetValue('%.3f'%(value)) |
---|
411 | self.PlotImage() |
---|
412 | |
---|
413 | zeroSizer = wx.FlexGridSizer(0,6,5,5) |
---|
414 | zeroSizer.Add(wx.StaticText(self.SCCDPanel,label='X-zero:'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
415 | zMax = wx.TextCtrl(self.SCCDPanel,value='%.3f'%(data['Zeros'][0])) |
---|
416 | zMax.Bind(wx.EVT_TEXT_ENTER,OnZeroValue) |
---|
417 | zMax.Bind(wx.EVT_KILL_FOCUS,OnZeroValue) |
---|
418 | ObjIndx[zMax.GetId()] = ['Zeros',0] |
---|
419 | zeroSizer.Add(zMax) |
---|
420 | zeroSizer.Add(wx.StaticText(self.SCCDPanel,label='Y-zero:'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
421 | zMin = wx.TextCtrl(self.SCCDPanel,value='%.3f'%(data['Zeros'][1])) |
---|
422 | ObjIndx[zMin.GetId()] = ['Zeros',1] |
---|
423 | zMin.Bind(wx.EVT_TEXT_ENTER,OnZeroValue) |
---|
424 | zMin.Bind(wx.EVT_KILL_FOCUS,OnZeroValue) |
---|
425 | zeroSizer.Add(zMin) |
---|
426 | zeroSizer.Add(wx.StaticText(self.SCCDPanel,label='mm per deg:'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
427 | zpdeg = wx.TextCtrl(self.SCCDPanel,value='%.3f'%(data['mm/deg'])) |
---|
428 | ObjIndx[zpdeg.GetId()] = ['mm/deg'] |
---|
429 | zpdeg.Bind(wx.EVT_TEXT_ENTER,OnZpdgValue) |
---|
430 | zpdeg.Bind(wx.EVT_KILL_FOCUS,OnZpdgValue) |
---|
431 | zeroSizer.Add(zpdeg) |
---|
432 | return zeroSizer |
---|
433 | |
---|
434 | def TBLRSizer(data): |
---|
435 | |
---|
436 | def OnTBLRValue(event): |
---|
437 | Obj = event.GetEventObject() |
---|
438 | item = ObjIndx[Obj.GetId()] |
---|
439 | try: |
---|
440 | value = int(Obj.GetValue()) |
---|
441 | except ValueError: |
---|
442 | value = data[item[0]][item[1]] |
---|
443 | data[item[0]][item[1]] = value |
---|
444 | Obj.SetValue('%d'%(value)) |
---|
445 | self.PlotImage() |
---|
446 | |
---|
447 | TBLRsizer = wx.FlexGridSizer(0,4,5,5) |
---|
448 | for i,item in enumerate(['Bottom','Top']): |
---|
449 | TBLRsizer.Add(wx.StaticText(self.SCCDPanel,label=item+' limit, pixels:'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
450 | TBlim = wx.TextCtrl(self.SCCDPanel,value='%d'%(data['TBlimits'][i])) |
---|
451 | TBlim.Bind(wx.EVT_TEXT_ENTER,OnTBLRValue) |
---|
452 | TBlim.Bind(wx.EVT_KILL_FOCUS,OnTBLRValue) |
---|
453 | ObjIndx[TBlim.GetId()] = ['TBlimits',i] |
---|
454 | TBLRsizer.Add(TBlim) |
---|
455 | return TBLRsizer |
---|
456 | |
---|
457 | def ScanSizer(data): |
---|
458 | |
---|
459 | def OnScanValue(event): |
---|
460 | Obj = event.GetEventObject() |
---|
461 | item = ObjIndx[Obj.GetId()][0] |
---|
462 | try: |
---|
463 | value = float(Obj.GetValue()) |
---|
464 | except ValueError: |
---|
465 | value = self.data['2thScan'][item] |
---|
466 | data['2thScan'][item] = value |
---|
467 | Obj.SetValue('%.3f'%(value)) |
---|
468 | if item in [0,1]: |
---|
469 | pixel = data['pixel'] |
---|
470 | zero = data['Zeros'][0] |
---|
471 | tthscale = data['mm/deg'] |
---|
472 | npixel = (value*tthscale+zero)*1000/pixel[0] |
---|
473 | data['LRlimits'][item] = npixel |
---|
474 | self.PlotImage() |
---|
475 | |
---|
476 | scanSizer = wx.FlexGridSizer(0,6,5,5) |
---|
477 | for i,item in enumerate(['Lower 2-th','Upper 2-th','2-th step']): |
---|
478 | scanSizer.Add(wx.StaticText(self.SCCDPanel,label=item+':'),0,wx.ALIGN_CENTER_VERTICAL) |
---|
479 | scanParm = wx.TextCtrl(self.SCCDPanel,value='%.3f'%(data['2thScan'][i])) |
---|
480 | scanParm.Bind(wx.EVT_TEXT_ENTER,OnScanValue) |
---|
481 | scanParm.Bind(wx.EVT_KILL_FOCUS,OnScanValue) |
---|
482 | ObjIndx[scanParm.GetId()] = [i] |
---|
483 | scanSizer.Add(scanParm) |
---|
484 | return scanSizer |
---|
485 | |
---|
486 | mainSizer = wx.BoxSizer(wx.VERTICAL) |
---|
487 | mainSizer.Add(ColorSizer(self.data),0,wx.ALIGN_CENTER_VERTICAL) |
---|
488 | mainSizer.Add(MaxSizer(self.data),0,wx.ALIGN_LEFT|wx.EXPAND) |
---|
489 | mainSizer.Add(ZSizer(self.data),0,wx.ALIGN_CENTER_VERTICAL) |
---|
490 | mainSizer.Add(ZeroSizer(self.data),0,wx.ALIGN_CENTER_VERTICAL) |
---|
491 | mainSizer.Add(ScanSizer(self.data),0,wx.ALIGN_CENTER_VERTICAL) |
---|
492 | mainSizer.Add(TBLRSizer(self.data),0,wx.ALIGN_CENTER_VERTICAL) |
---|
493 | self.SCCDPanel.SetSizer(mainSizer) |
---|
494 | mainSizer.Layout() |
---|
495 | fitSize = mainSizer.Fit(self.SCCDPanel) |
---|
496 | # self.SCCDPanel.GetParent().SetSize(fitSize) |
---|
497 | |
---|
498 | def PlotImage(self): |
---|
499 | pixel = self.data['pixel'] |
---|
500 | scalex = pixel[0]/1000. |
---|
501 | scaley = pixel[1]/1000. |
---|
502 | |
---|
503 | |
---|
504 | def OnMotion(event): |
---|
505 | Page.canvas.SetToolTipString('') |
---|
506 | sizexy = self.data['size'] |
---|
507 | if event.xdata and event.ydata: #avoid out of frame errors |
---|
508 | Page.canvas.SetCursor(wx.CROSS_CURSOR) |
---|
509 | item = self.itemPicked |
---|
510 | if item: |
---|
511 | if 'Line' in str(item): |
---|
512 | Page.canvas.SetToolTipString('%8.3f %8.3fmm'%(event.xdata,event.ydata)) |
---|
513 | else: |
---|
514 | xpos = event.xdata |
---|
515 | ypos = event.ydata |
---|
516 | xpix = xpos/(self.data['imScale']*scalex) |
---|
517 | ypix = sizexy[1]-ypos/(self.data['imScale']*scaley) |
---|
518 | Int = 0 |
---|
519 | if (0 <= xpix <= sizexy[0]) and (0 <= ypix <= sizexy[1]): |
---|
520 | Int = self.Image[xpix][ypix]-self.data['Zmin'] |
---|
521 | tth = (xpos-self.data['Zeros'][0])/self.data['mm/deg'] |
---|
522 | vecA = np.array([1.0,0,0]) |
---|
523 | vecB = np.array([npcosd(tth)*self.data['radius'],npsind(tth)*self.data['radius'],(ypos-self.data['Zeros'][1])]) |
---|
524 | vecB /= nl.norm(vecB) |
---|
525 | tth2 = npacosd(np.dot(vecA,vecB))*tth/abs(tth) |
---|
526 | self.plotNB.status.SetFields(\ |
---|
527 | ['','Detector x,y =%9.3fmm %9.3fmm, 2-th =%9.3f I = %6d'%(xpos,ypos,tth2,Int)]) |
---|
528 | |
---|
529 | def OnPick(event): |
---|
530 | if self.itemPicked is not None: return |
---|
531 | self.itemPicked = event.artist |
---|
532 | self.mousePicked = event.mouseevent |
---|
533 | |
---|
534 | def OnRelease(event): |
---|
535 | if self.itemPicked is None: return |
---|
536 | xpos,ypos = [event.xdata,event.ydata] |
---|
537 | if '_line0' in str(self.itemPicked): #X-zero |
---|
538 | self.data['Zeros'][0] = xpos |
---|
539 | elif '_line1' in str(self.itemPicked): #Y-zero |
---|
540 | self.data['Zeros'][1] = ypos |
---|
541 | elif '_line2' in str(self.itemPicked): #Y-lower limit |
---|
542 | self.data['TBlimits'][0] = int(ypos/scaley) |
---|
543 | elif '_line3' in str(self.itemPicked): #Y-upper limit |
---|
544 | self.data['TBlimits'][1] = int(ypos/scaley) |
---|
545 | elif '_line4' in str(self.itemPicked): #X-lower limit |
---|
546 | self.data['LRlimits'][0] = int(xpos/scalex) |
---|
547 | elif '_line5' in str(self.itemPicked): #X-upper limit |
---|
548 | self.data['LRlimits'][1] = int(xpos/scalex) |
---|
549 | self.itemPicked = None |
---|
550 | self.PlotImage() |
---|
551 | self.UpdateControls(event) |
---|
552 | |
---|
553 | try: |
---|
554 | plotNum = self.plotNB.plotList.index('scanCCD image') |
---|
555 | Page = self.plotNB.nb.GetPage(plotNum) |
---|
556 | Plot = Page.figure.gca() #get previous powder plot & get limits |
---|
557 | xylim = Plot.get_xlim(),Plot.get_ylim() |
---|
558 | Page.figure.clf() |
---|
559 | Plot = Page.figure.gca() |
---|
560 | if not Page.IsShown(): |
---|
561 | Page.Show() |
---|
562 | except ValueError: |
---|
563 | Plot = self.plotNB.addMpl('scanCCD image').gca() |
---|
564 | plotNum = self.plotNB.plotList.index('scanCCD image') |
---|
565 | Page = self.plotNB.nb.GetPage(plotNum) |
---|
566 | Page.canvas.mpl_connect('motion_notify_event', OnMotion) |
---|
567 | Page.canvas.mpl_connect('pick_event', OnPick) |
---|
568 | Page.canvas.mpl_connect('button_release_event', OnRelease) |
---|
569 | xylim = [] |
---|
570 | xlim,ylim = self.data['size'] |
---|
571 | Imin,Imax = [0,self.data['range'][1]] |
---|
572 | Xmax = scalex*xlim*self.data['imScale'] |
---|
573 | Ymax = scaley*ylim*self.data['imScale'] |
---|
574 | TBlimit = np.array(self.data['TBlimits'])*scalex |
---|
575 | LRlimit = np.array(self.data['LRlimits'])*scaley |
---|
576 | |
---|
577 | self.plotNB.status.SetFields(['','']) |
---|
578 | Zmin = self.data['Zmin'] |
---|
579 | Zmax = self.data['Zmax'] |
---|
580 | Zeros = self.data['Zeros'] |
---|
581 | Lines = [] |
---|
582 | |
---|
583 | # MA = ma.masked_greater(ma.masked_less(self.Image.T,Zmin),Zmax) |
---|
584 | # MaskA = ma.getmaskarray(MA) |
---|
585 | # ImgM = Plot.imshow(MaskA,aspect='auto',cmap='Reds', |
---|
586 | # interpolation='nearest',vmin=0,vmax=2,extent=[0,Xmax,0,Ymax]) |
---|
587 | Img = Plot.imshow(self.Image.T-Zmin,interpolation='nearest',vmin=Imin,vmax=Imax, |
---|
588 | aspect='auto',cmap=self.data['color'],extent=[0,Xmax,0,Ymax]) |
---|
589 | Lines.append(Plot.axvline(Zeros[0],color='b',dashes=(5,5),picker=3.)) |
---|
590 | Lines.append(Plot.axhline(Zeros[1],color='b',dashes=(5,5),picker=3.)) |
---|
591 | Lines.append(Plot.axhline(TBlimit[0],color='g',dashes=(5,5),picker=3.)) |
---|
592 | Lines.append(Plot.axhline(TBlimit[1],color='r',dashes=(5,5),picker=3.)) |
---|
593 | Lines.append(Plot.axvline(LRlimit[0],color='g',dashes=(5,5),picker=3.)) |
---|
594 | Lines.append(Plot.axvline(LRlimit[1],color='r',dashes=(5,5),picker=3.)) |
---|
595 | for blk in range(len(self.imagefiles)): |
---|
596 | Lines.append(Plot.axvline(blk*4096*scalex,color='k')) |
---|
597 | Plot.set_title('') |
---|
598 | Plot.set_xlabel('Scan, mm') |
---|
599 | Plot.set_ylabel('Detector Y, mm') |
---|
600 | if xylim: |
---|
601 | Page.toolbar.push_current() |
---|
602 | Plot.set_xlim(xylim[0]) |
---|
603 | Plot.set_ylim(xylim[1]) |
---|
604 | xylim = [] |
---|
605 | Page.toolbar.push_current() |
---|
606 | Page.toolbar.draw() |
---|
607 | else: |
---|
608 | Page.canvas.draw() |
---|
609 | |
---|
610 | def PlotBlock(self,block,title): |
---|
611 | try: |
---|
612 | plotNum = self.plotNB.plotList.index('Block') |
---|
613 | Page = self.plotNB.nb.GetPage(plotNum) |
---|
614 | Plot = Page.figure.gca() #get previous powder plot & get limits |
---|
615 | xylim = Plot.get_xlim(),Plot.get_ylim() |
---|
616 | Page.figure.clf() |
---|
617 | Plot = Page.figure.gca() |
---|
618 | if not Page.IsShown(): |
---|
619 | Page.Show() |
---|
620 | except ValueError: |
---|
621 | Plot = self.plotNB.addMpl('Block').gca() |
---|
622 | plotNum = self.plotNB.plotList.index('Block') |
---|
623 | Page = self.plotNB.nb.GetPage(plotNum) |
---|
624 | xylim = [] |
---|
625 | Img = Plot.imshow(block,interpolation='nearest',aspect='equal',cmap=self.data['color']) |
---|
626 | Plot.invert_yaxis() |
---|
627 | Plot.set_title(title) |
---|
628 | if xylim: |
---|
629 | Page.toolbar.push_current() |
---|
630 | Plot.set_xlim(xylim[0]) |
---|
631 | Plot.set_ylim(xylim[1]) |
---|
632 | xylim = [] |
---|
633 | Page.toolbar.push_current() |
---|
634 | Page.toolbar.draw() |
---|
635 | else: |
---|
636 | Page.canvas.draw() |
---|
637 | |
---|
638 | def PlotXY(self,XY,newPlot=False,type=''): |
---|
639 | '''simple plot of xy data, used for diagnostic purposes |
---|
640 | ''' |
---|
641 | def OnMotion(event): |
---|
642 | xpos = event.xdata |
---|
643 | if xpos: #avoid out of frame mouse position |
---|
644 | ypos = event.ydata |
---|
645 | Page.canvas.SetCursor(wx.CROSS_CURSOR) |
---|
646 | try: |
---|
647 | self.plotNB.status.SetStatusText('X =%9.3f %s =%9.3f'%(xpos,type,ypos),1) |
---|
648 | except TypeError: |
---|
649 | self.plotNB.status.SetStatusText('Select '+type+' pattern first',1) |
---|
650 | |
---|
651 | try: |
---|
652 | plotNum = self.plotNB.plotList.index(type) |
---|
653 | Page = self.plotNB.nb.GetPage(plotNum) |
---|
654 | if not newPlot: |
---|
655 | Plot = Page.figure.gca() |
---|
656 | xylim = Plot.get_xlim(),Plot.get_ylim() |
---|
657 | Page.figure.clf() |
---|
658 | Plot = Page.figure.gca() |
---|
659 | except ValueError: |
---|
660 | newPlot = True |
---|
661 | Plot = self.plotNB.addMpl(type).gca() |
---|
662 | plotNum = self.plotNB.plotList.index(type) |
---|
663 | Page = self.plotNB.nb.GetPage(plotNum) |
---|
664 | Page.canvas.mpl_connect('motion_notify_event', OnMotion) |
---|
665 | |
---|
666 | Page.SetFocus() |
---|
667 | self.plotNB.status.DestroyChildren() |
---|
668 | Plot.set_title(type) |
---|
669 | if type == 'line scan': |
---|
670 | Plot.set_xlabel(r'image x, mm',fontsize=14) |
---|
671 | else: |
---|
672 | Plot.set_xlabel(r'2-theta, deg',fontsize=14) |
---|
673 | Plot.set_ylabel(r''+type,fontsize=14) |
---|
674 | colors=['b','g','r','c','m','k'] |
---|
675 | lenX = 0 |
---|
676 | X,Y,S = XY |
---|
677 | Plot.plot(X,Y,'k',picker=False) |
---|
678 | Plot.plot(X,S,'r',picker=False) |
---|
679 | if not newPlot: |
---|
680 | Page.toolbar.push_current() |
---|
681 | Plot.set_xlim(xylim[0]) |
---|
682 | Plot.set_ylim(xylim[1]) |
---|
683 | xylim = [] |
---|
684 | Page.toolbar.push_current() |
---|
685 | Page.toolbar.draw() |
---|
686 | else: |
---|
687 | Page.canvas.draw() |
---|
688 | |
---|
689 | class scanCCDmain(wx.App): |
---|
690 | def OnInit(self): |
---|
691 | self.main = scanCCD(None) |
---|
692 | self.main.Show() |
---|
693 | self.SetTopWindow(self.main) |
---|
694 | return True |
---|
695 | |
---|
696 | def main(): |
---|
697 | 'starts main application to merge data from scanning CCD' |
---|
698 | application = scanCCDmain(0) |
---|
699 | application.MainLoop() |
---|
700 | |
---|
701 | if __name__ == '__main__': |
---|
702 | main() |
---|