Changeset 2082 for trunk/autoint.py
- Timestamp:
- Dec 4, 2015 10:50:58 PM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/autoint.py
r2065 r2082 1 1 import os 2 import wx2 import sys 3 3 import copy 4 4 import glob 5 5 import re 6 import bisect 7 import numpy as np 8 import wx 9 import wx.lib.mixins.listctrl as listmix 6 10 import GSASIIpath 7 11 import GSASIIIO as G2IO … … 9 13 import GSASIIgrid as G2gd 10 14 import GSASIIimgGUI as G2imG 11 ''' 12 Define a class to be used for Andrey's AutoIntegration process 13 ''' 14 15 import GSASIIpy3 as G2py3 16 17 print 'loading autoint' 18 19 def ReadMask(filename): 20 'Read a mask (.immask) file' 21 File = open(filename,'r') 22 save = {} 23 S = File.readline() 24 while S: 25 if S[0] == '#': 26 S = File.readline() 27 continue 28 [key,val] = S[:-1].split(':') 29 if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']: 30 save[key] = eval(val) 31 S = File.readline() 32 File.close() 33 G2imG.CleanupMasks(save) 34 return save 35 36 def ReadControls(filename): 37 'read an image controls (.imctrl) file' 38 cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type', 39 'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth', 40 'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg', 41 'PolaVal','SampleAbs','dark image','background image'] 42 File = open(filename,'r') 43 save = {} 44 S = File.readline() 45 while S: 46 if S[0] == '#': 47 S = File.readline() 48 continue 49 [key,val] = S[:-1].split(':') 50 if key in ['type','calibrant','binType','SampleShape',]: #strings 51 save[key] = val 52 elif key in ['rotation']: 53 save[key] = float(val) 54 elif key in ['center',]: 55 if ',' in val: 56 save[key] = eval(val) 57 else: 58 vals = val.strip('[] ').split() 59 save[key] = [float(vals[0]),float(vals[1])] 60 elif key in cntlList: 61 save[key] = eval(val) 62 S = File.readline() 63 File.close() 64 return save 65 66 def Read_imctrl(imctrl_file): 67 '''Read an image control file and record control parms into a dict, with some simple 68 type conversions 69 ''' 70 file_opt = options = {} 71 save = {'filename':imctrl_file} 72 immask_file = os.path.splitext(imctrl_file)[0]+'.immask' 73 if os.path.exists(immask_file): 74 save['maskfile'] = immask_file 75 else: 76 save['maskfile'] = '(none)' 77 cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type', 78 'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth', 79 'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg', 80 'PolaVal','SampleAbs','dark image','background image'] 81 File = open(imctrl_file,'r') 82 fullIntegrate = False 83 try: 84 S = File.readline() 85 while S: 86 if S[0] == '#': 87 S = File.readline() 88 continue 89 [key,val] = S[:-1].split(':') 90 if key in ['type','calibrant','binType','SampleShape',]: #strings 91 save[key] = val 92 elif key == 'rotation': 93 save[key] = float(val) 94 elif key == 'fullIntegrate': 95 fullIntegrate = eval(val) 96 elif key == 'LRazimuth': 97 save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2] 98 elif key == 'IOtth': 99 save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2] 100 elif key == 'center': 101 if ',' in val: 102 vals = eval(val) 103 else: 104 vals = val.strip('[] ').split() 105 vals = [float(vals[0]),float(vals[1])] 106 save['center_x'],save['center_y'] = vals[0:2] 107 elif key in cntlList: 108 save[key] = eval(val) 109 S = File.readline() 110 finally: 111 File.close() 112 if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0,0 113 return save 114 15 115 class AutoIntFrame(wx.Frame): 16 116 '''Creates a wx.Frame window for the Image AutoIntegration. … … 48 148 if newImage in imageFileList: continue # already read 49 149 for imgId in G2IO.ReadImages(G2frame,newImage): 50 # update controls from master51 150 controlsDict = G2frame.PatternTree.GetItemPyData( 52 151 G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls')) 53 controlsDict.update(self.ImageControls)54 # update masks from master55 152 ImageMasks = G2frame.PatternTree.GetItemPyData( 56 153 G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks')) 154 if self.params['Mode'] == 'table': 155 dist = controlsDict['distance'] 156 interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values 157 self.ImageControls = ReadControls(imgctrl) 158 self.ImageControls.update(interpDict) 159 self.ImageControls['showLines'] = True 160 self.ImageControls['ring'] = [] 161 self.ImageControls['rings'] = [] 162 self.ImageControls['ellipses'] = [] 163 self.ImageControls['setDefault'] = False 164 for i in 'range','size','GonioAngles': 165 if i in self.ImageControls: 166 del self.ImageControls[i] 167 # load copy of Image Masks 168 if immask: 169 self.ImageMasks = ReadMask(immask) 170 del self.Thresholds['Thresholds'] 171 else: 172 self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]} 173 # update controls from master 174 controlsDict.update(self.ImageControls) 175 # update masks from master w/o Thresholds 57 176 ImageMasks.update(self.ImageMasks) 58 177 # now integrate the images that have not already been processed before … … 117 236 # show current IMG base 118 237 self.ControlBaseLbl.SetLabel(G2frame.PatternTree.GetItemText(G2frame.Image)) 119 if self.params['Mode'] == 'file': 120 'get file info' 121 GSASIIpath.IPyBreak() 122 else: 238 if self.params['Mode'] != 'table': 123 239 # load copy of Image Controls from current image and clean up 124 240 # items that should not be copied … … 179 295 is started. When Pause is pressed, the loop is stopped. 180 296 ''' 181 #print self.params # for debug 182 183 # check inputs before starting 184 err = '' 297 # check inputs for errors before starting 298 #err = '' 185 299 #if not any([self.params[fmt] for fmt in self.fmtlist]): 186 300 # err += '\nPlease select at least one output format\n' 187 if (self.params['Mode'] == 'file' and not 188 os.path.exists(self.params['IMGfile'])): 189 err += '\nThe image controls file could not be found\n' 190 if (self.params['Mode'] == 'file' and 191 not self.params['IgnoreMask'] 192 ) and not os.path.exists(self.params['MaskFile']): 193 err += '\nThe mask file could not be found\n' 194 if err: 195 G2G.G2MessageBox(self,err) 196 return 301 #if err: 302 # G2G.G2MessageBox(self,err) 303 # return 197 304 # change button label 198 305 if btnstart.GetLabel() != 'Pause': … … 244 351 dlg.Destroy() 245 352 return 246 if btn1 == event.GetEventObject():247 ext = '.imctrl'248 title = 'Image control'249 else:250 ext = '.immask'251 title = 'Image masks'252 dlg = wx.FileDialog(253 self, 'Select name for '+title+' file to read',254 '.', '',255 title+'file (*'+ext+')|*'+ext,256 wx.OPEN|wx.CHANGE_DIR)257 dlg.CenterOnParent()258 try:259 if dlg.ShowModal() == wx.ID_OK:260 filename = dlg.GetPath()261 # make sure extension is correct262 #filename = os.path.splitext(filename)[0]+ext263 if btn1 == event.GetEventObject():264 fInp1.SetValue(filename)265 else:266 fInp2.SetValue(filename)267 else:268 filename = None269 finally:270 dlg.Destroy()271 353 272 354 def OnRadioSelect(event): 273 '''Respond to a radiobutton selection and enable or 274 disable widgets accordingly. Also gets called when the 275 "Don't Use" flag for Mask use is called. 355 '''Respond to a radiobutton selection and when in table 356 mode, get parameters from user. 276 357 ''' 277 lbl1.Disable() 278 fInp1.Disable() 279 btn1.Disable() 280 lbl2.Disable() 281 fInp2.Disable() 282 ign2.Disable() 283 btn2.Disable() 358 self.Evaluator = None 284 359 if r2.GetValue(): 285 self.params['Mode'] = ' file'286 fInp1.Enable()287 btn1.Enable()288 lbl1.Enable()289 ign2.Enable()290 if not self.params['IgnoreMask']:291 fInp2.Enable()292 btn2.Enable()293 lbl2.Enable()360 self.params['Mode'] = 'table' 361 try: 362 dlg = IntegParmTable(self.G2frame) # create the dialog 363 if dlg.ShowModal() == wx.ID_OK: 364 self.Evaluator = DefineEvaluator(dlg) 365 else: 366 r1.SetValue(True) 367 finally: 368 dlg.Destroy() 294 369 else: 295 370 self.params['Mode'] = 'active' … … 298 373 ################################################## 299 374 self.G2frame = G2frame 375 self.Evaluator = None 300 376 self.params = {} 301 377 self.Reset = False … … 337 413 lblsizr.Add(r1) 338 414 r1.SetValue(True) 339 r2 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use from file(s)")415 r2 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use from table") 340 416 lblsizr.Add(r2) 341 417 r2.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect) 342 r2.Disable() # deactivate this until implemented343 # Image controls file344 sizer = wx.BoxSizer(wx.HORIZONTAL)345 sizer.Add((20,-1))346 lbl1 = wx.StaticText(mnpnl, wx.ID_ANY,'IMG control file: ')347 sizer.Add(lbl1)348 fInp1 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'IMGfile',349 notBlank=False,size=(300,-1))350 sizer.Add(fInp1)351 btn1 = wx.Button(mnpnl, wx.ID_ANY, "Browse")352 btn1.Bind(wx.EVT_BUTTON, OnBrowse)353 sizer.Add(btn1)354 lblsizr.Add(sizer)355 # Masks input file356 sizer = wx.BoxSizer(wx.HORIZONTAL)357 sizer.Add((20,-1))358 lbl2 = wx.StaticText(mnpnl, wx.ID_ANY,'Mask file: ')359 sizer.Add(lbl2)360 fInp2 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'MaskFile',361 notBlank=False,size=(300,-1))362 sizer.Add(fInp2)363 ign2 = G2G.G2CheckBox(mnpnl,"Don't use",self.params,'IgnoreMask',364 OnChange=OnRadioSelect)365 sizer.Add(ign2)366 btn2 = wx.Button(mnpnl, wx.ID_ANY, "Browse")367 btn2.Bind(wx.EVT_BUTTON, OnBrowse)368 sizer.Add(btn2)369 lblsizr.Add(sizer)370 418 mnsizer.Add(lblsizr) 371 419 … … 420 468 mnsizer.Fit(self) 421 469 self.CenterOnParent() 422 self.Show() 470 self.Show() 471 472 def DefineEvaluator(dlg): 473 '''Creates a function that provides interpolated values for a given distance value 474 ''' 475 def Evaluator(dist): 476 '''Interpolate image parameters for a supplied distance value 477 478 :param float dist: distance to use for interpolation 479 :returns: a list with 3 items: 480 481 * a dict with parameter values, 482 * the closest imctrl and 483 * the closest maskfile (or None) 484 ''' 485 x = np.array([float(i) for i in parms[0]]) 486 closest = abs(x-dist).argmin() 487 closeX = x[closest] 488 D = {'distance':dist} 489 imctfile = IMfileList[closest] 490 if parms[-1][closest].lower() != '(none)': 491 maskfile = parms[-1][closest] 492 else: 493 maskfile = None 494 for c in range(1,cols-1): 495 lbl = ParmList[c] 496 if lbl in nonInterpVars: 497 D[lbl] = float(parms[c][closest]) 498 else: 499 y = np.array([float(i) for i in parms[c]]) 500 D[lbl] = np.interp(dist,x,y) 501 # full integration when angular range is 0 502 D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max']) 503 # conversion for paired values 504 for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'): 505 r = a.split('_')[0] 506 D[r] = [D[a],D[b]] 507 del D[a] 508 del D[b] 509 return D,imctfile,maskfile 510 # save local copies of values needed in Evaluator 511 parms = dlg.ReadImageParmTable() 512 IMfileList = dlg.IMfileList 513 cols = dlg.list.GetColumnCount() 514 ParmList = dlg.ParmList 515 nonInterpVars = dlg.nonInterpVars 516 return Evaluator 517 518 class IntegParmTable(wx.Dialog): 519 '''Creates a dialog window with a table of integration parameters. 520 :meth:`ShowModal` will return wx.ID_OK if the process has been successful. 521 In this case, :func:`DefineEvaluator` should be called to obtain a function that 522 creates a dictionary with interpolated parameter values. 523 ''' 524 ParmList = ('distance','center_x','center_y','wavelength','tilt','rotation','DetDepth', 525 'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels', 526 'maskfile', 527 ) 528 nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max', 529 'outChannels') # values in this list are taken from nearest rather than interpolated 530 HeaderList = ('Det Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth', 531 'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts', 532 'Mask File', 533 ) 534 def __init__(self,G2frame): 535 self.G2frame = G2frame 536 self.parms = [] # list of values by column 537 self.IMfileList = [] # list of .imctrl file names for each entry in table 538 wx.Dialog.__init__(self,G2frame,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE) 539 files = [] 540 try: 541 dlg = wx.FileDialog(self, 'Select image control files or previous table', 542 style=wx.OPEN| wx.MULTIPLE, 543 wildcard='image control files (.imctrl)|*.imctrl|Integration table (*.imtbl)|*.imtbl') 544 if dlg.ShowModal() == wx.ID_OK: 545 files = dlg.GetPaths() 546 self.parms,self.IMfileList = self.ReadFiles(files) 547 finally: 548 dlg.Destroy() 549 if not files: 550 wx.CallAfter(self.EndModal,wx.ID_CANCEL) 551 return 552 mainSizer = wx.BoxSizer(wx.VERTICAL) 553 self.list = ImgIntLstCtrl(self, wx.ID_ANY, 554 style=wx.LC_REPORT 555 | wx.BORDER_SUNKEN 556 #| wx.BORDER_NONE 557 ) 558 mainSizer.Add(self.list,1,wx.EXPAND,1) 559 btnsizer = wx.BoxSizer(wx.HORIZONTAL) 560 btn = wx.Button(self, wx.ID_OK) 561 btnsizer.Add(btn) 562 btn = wx.Button(self, wx.ID_ANY,'Save') 563 btn.Bind(wx.EVT_BUTTON,self._onSave) 564 btnsizer.Add(btn) 565 btn = wx.Button(self, wx.ID_CLOSE,'Quit') 566 btn.Bind(wx.EVT_BUTTON,self._onClose) 567 btnsizer.Add(btn) 568 mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5) 569 self.SetSizer(mainSizer) 570 self.list.FillList(self.parms) 571 mainSizer.Layout() 572 mainSizer.Fit(self) 573 574 def ReadFiles(self,files): 575 '''Reads a list of .imctrl files or a single .imtbl file 576 ''' 577 tmpDict = {} 578 if not files: return 579 # option 1, a dump from a previous save 580 if os.path.splitext(files[0])[1] == '.imtbl': 581 fp = open(files[0],'r') 582 S = fp.readline() 583 while S: 584 if S[0] != '#': 585 [key,val] = S[:-1].split(':') 586 tmpDict[key] = eval(val) 587 S = fp.readline() 588 fp.close() 589 # delete entries 590 m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)] 591 if m1: 592 print('\nimctrl file not found:') 593 for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i]) 594 m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))] 595 if m2: 596 print('\nmask file not found') 597 for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i]) 598 m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0] 599 if m3: 600 print('\nDropping entries due to negative distance: '+str(m3)) 601 m = sorted(set(m1 + m2 + m3)) 602 m.reverse() 603 for c in m: 604 for key in tmpDict: 605 del tmpDict[key][c] 606 fileList = tmpDict.get('filenames','[]') 607 parms = [] 608 for key in self.ParmList: 609 try: 610 float(tmpDict[key][0]) 611 parms.append([str(G2py3.FormatSigFigs(val,sigfigs=5)) for val in tmpDict[key]]) 612 except ValueError: 613 parms.append(tmpDict[key]) 614 return parms,fileList 615 # option 2, read in a list of files 616 for file in files: # read all files; place in dict by distance 617 imgDict = Read_imctrl(file) 618 tmpDict[imgDict.get('distance')] = imgDict 619 parms = [[] for key in self.ParmList] 620 fileList = [] 621 for d in sorted(tmpDict): 622 fileList.append(tmpDict[d].get('filename')) 623 if d is None: continue 624 if d < 0: continue 625 for i,key in enumerate(self.ParmList): 626 val = tmpDict[d].get(key) 627 try: 628 val = str(G2py3.FormatSigFigs(val,sigfigs=5)) 629 except: 630 val = str(val) 631 parms[i].append(val) 632 return parms,fileList 633 634 def ReadImageParmTable(self): 635 '''Reads possibly edited values from the ListCtrl table and returns a list 636 of values for each column. 637 ''' 638 rows = self.list.GetItemCount() 639 cols = self.list.GetColumnCount() 640 parms = [] 641 for c in range(cols): 642 lbl = self.ParmList[c] 643 parms.append([]) 644 for r in range(rows): 645 parms[c].append(self.list.GetItem(r,c).GetText()) 646 return parms 647 648 def _onClose(self,event): 649 'Called when Cancel button is pressed' 650 self.EndModal(wx.ID_CANCEL) 651 652 def _onSave(self,event): 653 'Called when save button is pressed; creates a .imtbl file' 654 fil = '' 655 if self.G2frame.GSASprojectfile: 656 fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl' 657 dir,f = os.path.split(fil) 658 try: 659 dlg = wx.FileDialog(self, 'Save table data as', 660 defaultDir=dir, defaultFile=f, style=wx.SAVE) 661 if dlg.ShowModal() != wx.ID_OK: return 662 fil = dlg.GetPath() 663 fil = os.path.splitext(fil)[0]+'.imtbl' 664 finally: 665 dlg.Destroy() 666 parms = self.ReadImageParmTable() 667 print('Writing image parameter table as '+fil) 668 fp = open(fil,'w') 669 for c in range(len(parms)-1): 670 lbl = self.ParmList[c] 671 fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n') 672 lbl = self.ParmList[c+1] 673 fp.write(lbl+': '+str(parms[c+1])+'\n') 674 lbl = 'filenames' 675 fp.write(lbl+': '+str(self.IMfileList)+'\n') 676 fp.close() 677 678 class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin): 679 '''Creates a custom ListCtrl for editing Image Integration parameters 680 ''' 681 def __init__(self, parent, ID, pos=wx.DefaultPosition, 682 size=(1000,200), style=0): 683 self.parent=parent 684 wx.ListCtrl.__init__(self, parent, ID, pos, size, style) 685 listmix.ListCtrlAutoWidthMixin.__init__(self) 686 listmix.TextEditMixin.__init__(self) 687 self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble) 688 #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick) 689 def FillList(self,parms): 690 'Places the current parms into the table' 691 self.ClearAll() 692 self.rowlen = len(self.parent.ParmList) 693 for i,lbl in enumerate(self.parent.HeaderList): 694 self.InsertColumn(i, lbl) 695 for r,d in enumerate(parms[0]): 696 if float(d) < 0: continue 697 index = self.InsertStringItem(sys.maxint, d) 698 for j in range(1,len(parms)): 699 self.SetStringItem(index, j, parms[j][r]) 700 for i,lbl in enumerate(self.parent.ParmList): 701 self.SetColumnWidth(i, wx.LIST_AUTOSIZE) 702 703 def OnDouble(self,evt): 704 'respond to a double-click' 705 self.CloseEditor() 706 fil = '(none)' 707 try: 708 dlg = wx.FileDialog(G2frame, 'Select mask or control file to add (Press cancel if none)', 709 style=wx.OPEN, 710 wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl') 711 if dlg.ShowModal() == wx.ID_OK: 712 fil = dlg.GetPath() 713 finally: 714 dlg.Destroy() 715 if os.path.splitext(fil)[1] != '.imctrl': 716 self.SetStringItem(self.curRow, self.rowlen-1, fil) 717 self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE) 718 else: 719 # insert or overwrite an instrument parameter set 720 if not os.path.exists(fil): 721 print('Does not exist: '+fil) 722 return 723 imgDict = Read_imctrl(fil) 724 dist = imgDict['distance'] 725 parms = self.parent.ReadImageParmTable() 726 x = np.array([float(i) for i in parms[0]]) 727 closest = abs(x-dist).argmin() 728 closeX = x[closest] 729 # fix IMfileList 730 for c,lbl in enumerate(self.parent.ParmList): 731 try: 732 vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5) 733 except ValueError: 734 vali = imgDict[lbl] 735 if abs(closeX-dist) < 1.: # distance is within 1 mm, replace 736 parms[c][closest] = vali 737 elif dist > closeX: # insert after 738 parms[c].insert(closest+1,vali) 739 else: 740 parms[c].insert(closest,vali) 741 if abs(closeX-dist) < 1.: # distance is within 1 mm, replace 742 self.parent.IMfileList[closest] = fil 743 elif dist > closeX: # insert after 744 self.parent.IMfileList.insert(closest+1,fil) 745 else: 746 self.parent.IMfileList.insert(closest,fil) 747 self.FillList(parms) 423 748 424 749 if __name__ == '__main__':
Note: See TracChangeset
for help on using the changeset viewer.