Coverage for /Users/Newville/Codes/xraylarch/larch/wxmap/maptomopanel.py: 17%
380 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1#!/usr/bin/env python
2"""
3GUI for displaying maps from HDF5 files
5"""
7VERSION = '10 (14-March-2018)'
9import os
10import platform
11import sys
12import time
13import json
14import socket
15import datetime
16from functools import partial
17from threading import Thread
19import wx
20import wx.lib.scrolledpanel as scrolled
21import wx.lib.mixins.inspection
22try:
23 from wx._core import PyDeadObjectError
24except:
25 PyDeadObjectError = Exception
27HAS_tomopy = False
28try:
29 import tomopy
30 HAS_tomopy = True
31except ImportError:
32 pass
34import numpy as np
35import scipy.stats as stats
36from wxmplot import PlotFrame
37from ..wxlib import (EditableListBox, SimpleText,
38 FloatCtrl, Font, pack, Popup, Button, MenuItem,
39 Choice, Check, GridPanel, FileSave, HLine)
40from ..wxlib.plotter import _plot
41from ..utils.strutils import bytes2str, version_ge
42from ..io import nativepath
43from ..math.tomography import TOMOPY_ALG, TOMOPY_FILT, center_score
45from ..xrmmap import GSEXRM_MapFile, GSEXRM_FileStatus, h5str, ensure_subgroup
48CEN = wx.ALIGN_CENTER
49LEFT = wx.ALIGN_LEFT
50RIGHT = wx.ALIGN_RIGHT
51ALL_CEN = wx.ALL|CEN
52ALL_LEFT = wx.ALL|LEFT
53ALL_RIGHT = wx.ALL|RIGHT
55PLOT_TYPES = ('Single ROI Map', 'Three ROI Map', 'Correlation Plot')
56PLOT_OPERS = ('/', '*', '-', '+')
57CONTRAST_CHOICES = ('None',
58 '0.01', '0.02', '0.05',
59 '0.1', '0.2', '0.5',
60 '1', '2', '5')
62CWID = 150
63WWID = 100 + CWID*4
65class TomographyPanel(GridPanel):
66 '''Panel of Controls for reconstructing a tomographic slice'''
67 label = 'Tomography Tools'
68 def __init__(self, parent, owner=None, **kws):
70 self.owner = owner
71 self.cfile,self.xrmmap = None,None
72 self.npts = None
73 self.resave = False
75 GridPanel.__init__(self, parent, nrows=8, ncols=6, **kws)
77 self.plot_choice = Choice(self, choices=PLOT_TYPES[:-1], size=(CWID, -1))
78 self.plot_choice.Bind(wx.EVT_CHOICE, self.plotSELECT)
80 self.det_choice = [Choice(self, size=(CWID, -1)),
81 Choice(self, size=(CWID, -1)),
82 Choice(self, size=(CWID, -1)),
83 Choice(self, size=(CWID, -1))]
84 self.roi_choice = [Choice(self, size=(CWID, -1)),
85 Choice(self, size=(CWID, -1)),
86 Choice(self, size=(CWID, -1)),
87 Choice(self, size=(CWID, -1))]
89 fopts = dict(minval=0, precision=2, size=(110, -1))
90 self.iminvals = [FloatCtrl(self, value=0, **fopts),
91 FloatCtrl(self, value=0, **fopts),
92 FloatCtrl(self, value=0, **fopts)]
93 self.imaxvals = [FloatCtrl(self, value=0, **fopts),
94 FloatCtrl(self, value=0, **fopts),
95 FloatCtrl(self, value=0, **fopts)]
96 self.icontrast = [Choice(self, choices=CONTRAST_CHOICES, default=4, size=(CWID, -1)),
97 Choice(self, choices=CONTRAST_CHOICES, default=4, size=(CWID, -1)),
98 Choice(self, choices=CONTRAST_CHOICES, default=4, size=(CWID, -1))]
100 for i, d in enumerate(self.icontrast):
101 d.Bind(wx.EVT_CHOICE, partial(self.roiContrast, i))
103 for i,det_chc in enumerate(self.det_choice):
104 det_chc.Bind(wx.EVT_CHOICE, partial(self.detSELECT,i))
106 for i,roi_chc in enumerate(self.roi_choice):
107 roi_chc.Bind(wx.EVT_CHOICE, partial(self.roiSELECT,i))
109 self.det_label = [SimpleText(self,'Intensity'),
110 SimpleText(self,''),
111 SimpleText(self,''),
112 SimpleText(self,'Normalization')]
113 self.roi_label = [SimpleText(self,''),
114 SimpleText(self,''),
115 SimpleText(self,''),
116 SimpleText(self,'')]
118 self.use_dtcorr = Check(self, default=True,
119 label='Correct for Detector Deadtime',
120 action=self.onDTCorrect)
121 self.use_hotcols = Check(self, default=False,
122 label='Remove First and Last columns',
123 action=self.onHotCols)
124 self.i1trans = Check(self, default=True,
125 label='Scalar "i1" is transmission data')
127 self.tomo_show = [Button(self, 'Show New Map', size=(CWID, -1),
128 action=partial(self.onShowTomograph, new=True)),
129 Button(self, 'Replace Last Map', size=(CWID, -1),
130 action=partial(self.onShowTomograph, new=False)),
131 Button(self, 'Show Centering Data', size=(CWID, -1),
132 action=self.onShowCentering)]
134 self.tomo_algo = Choice(self, choices=TOMOPY_ALG, size=(CWID, -1),
135 action=self.onALGchoice)
136 self.tomo_filt = Choice(self, choices=TOMOPY_FILT, size=(CWID, -1))
137 self.tomo_niter = wx.SpinCtrl(self, min=1, max=500, initial=1,
138 size=(CWID, -1),
139 style=wx.SP_VERTICAL|wx.SP_ARROW_KEYS|wx.SP_WRAP)
141 self.center_value = wx.SpinCtrlDouble(self, inc=0.25, size=(100, -1),
142 style=wx.SP_VERTICAL|wx.SP_ARROW_KEYS|wx.SP_WRAP)
143 self.center_value.SetIncrement(0.25)
144 self.center_value.SetDigits(2)
145 self.refine_center = wx.CheckBox(self, label='Refine')
146 self.refine_center.SetValue(False)
148 self.sino_data = Choice(self, size=(200, -1))
149 self.tomo_save = Button(self, 'Save reconstruction', size=(150, -1),
150 action=self.onSaveTomograph)
153 #################################################################################
155 self.Add(SimpleText(self, 'Display Virtual Slices: Plot Type:'), dcol=2,
156 style=LEFT, newrow=True)
158 self.Add(self.plot_choice, dcol=1, style=LEFT)
159 self.Add(self.i1trans, dcol=2, style=LEFT)
160 self.Add(SimpleText(self,'Options:'), dcol=1, style=LEFT, newrow=True)
161 self.Add(self.use_dtcorr, dcol=2, style=LEFT)
162 self.Add(self.use_hotcols, dcol=2, style=LEFT)
164 self.AddMany((SimpleText(self,''), self.det_label[0],
165 self.det_label[1], self.det_label[2], self.det_label[3]),
166 style=LEFT, newrow=True)
168 self.AddMany((SimpleText(self,'Detector:'), self.det_choice[0],
169 self.det_choice[1], self.det_choice[2], self.det_choice[3]),
170 style=LEFT, newrow=True)
172 self.AddMany((SimpleText(self,'ROI:'), self.roi_choice[0],
173 self.roi_choice[1], self.roi_choice[2], self.roi_choice[3]),
174 style=LEFT, newrow=True)
176 self.AddMany((SimpleText(self,''), self.roi_label[0],
177 self.roi_label[1], self.roi_label[2],
178 self.roi_label[3]), style=LEFT, newrow=True)
180 self.AddMany((SimpleText(self,'I Min:'), self.iminvals[0],
181 self.iminvals[1], self.iminvals[2]), style=LEFT,
182 newrow=True)
184 self.AddMany((SimpleText(self,'I Max:'), self.imaxvals[0],
185 self.imaxvals[1], self.imaxvals[2]), style=LEFT,
186 newrow=True)
188 self.AddMany((SimpleText(self,'I contrast %:'), self.icontrast[0],
189 self.icontrast[1], self.icontrast[2]), style=LEFT,
190 newrow=True)
193 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
194 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
195 self.Add(self.tomo_show[0], dcol=1, style=LEFT)
196 self.Add(self.tomo_show[1], dcol=1, style=LEFT)
197 self.Add(self.tomo_show[2], dcol=1, style=LEFT)
199 self.Add(HLine(self, size=(WWID, 5)), dcol=8, style=LEFT, newrow=True)
201 self.Add(SimpleText(self,'Reconstruction '), dcol=2, style=LEFT, newrow=True)
203 self.Add(SimpleText(self,'Algorithm:'), dcol=1, style=LEFT, newrow=True)
204 self.Add(self.tomo_algo, dcol=1, style=LEFT)
205 self.Add(SimpleText(self,'Filter: '), dcol=1, style=LEFT)
206 self.Add(self.tomo_filt, dcol=1, style=LEFT)
208 self.Add(SimpleText(self,'# Iterations'), dcol=1, style=LEFT, newrow=True)
209 self.Add(self.tomo_niter, dcol=1, style=LEFT)
210 self.Add(SimpleText(self,'Center Pixel:'), dcol=1, style=LEFT)
211 self.Add(self.center_value, dcol=1, style=LEFT)
212 self.Add(self.refine_center, dcol=1, style=LEFT)
214 self.Add(HLine(self, size=(WWID, 5)), dcol=8, style=LEFT, newrow=True)
217 self.Add(SimpleText(self,'Data:'), dcol=1, style=LEFT, newrow=True)
218 self.Add(self.sino_data, dcol=2, style=LEFT)
219 self.Add(self.tomo_save, dcol=2, style=LEFT)
221 #################################################################################
222 self.pack()
224 def onDTCorrect(self, event=None):
225 self.owner.current_file.dtcorrect = self.use_dtcorr.IsChecked()
227 def onHotCols(self, event=None):
228 self.owner.current_file.hotcols = self.use_hotcols.IsChecked()
230 def update_xrmmap(self, xrmfile=None, set_detectors=None):
232 if xrmfile is None:
233 xrmfile = self.owner.current_file
235 self.cfile = xrmfile
236 self.xrmmap = self.cfile.xrmmap
239 if self.cfile.get_rotation_axis() is None:
240 self.center_value.SetValue(0)
241 return
243 self.set_det_choices()
245 try:
246 self.npts = len(self.cfile.get_pos(0, mean=True))
247 except:
248 self.npts = len(self.cfile.get_pos('x', mean=True))
250 center = self.cfile.get_tomography_center()
251 self.center_value.SetRange(-0.5*self.npts,1.5*self.npts)
252 self.center_value.SetValue(center)
254 self.plotSELECT()
257 def onALGchoice(self,event=None):
259 alg = self.tomo_algo.GetStringSelection().lower()
260 enable_filter = False
261 enable_niter = False
263 if alg.startswith('gridrec'):
264 enable_filter = True
265 else:
266 enable_niter = True
268 self.tomo_niter.Enable(enable_niter)
269 self.tomo_filt.Enable(enable_filter)
271 def detSELECT(self, idet, event=None):
272 self.set_roi_choices(idet=idet)
274 def roiContrast(self, iroi, event=None):
275 if iroi > 2:
276 return
277 try:
278 detname = self.det_choice[iroi].GetStringSelection()
279 roiname = self.roi_choice[iroi].GetStringSelection()
280 contrast = self.icontrast[iroi].GetStringSelection()
281 except:
282 return
283 if contrast in ('None', None):
284 contrast = 0.0
285 contrast = float(contrast)
286 try:
287 map = self.cfile.get_roimap(roiname, det=detname)
288 imin, imax = np.percentile(map, (contrast, 100.0-contrast))
289 self.iminvals[iroi].SetValue(imin)
290 self.imaxvals[iroi].SetValue(imax)
291 except:
292 pass
294 def roiSELECT(self, iroi, event=None):
295 detname = self.det_choice[iroi].GetStringSelection()
296 roiname = self.roi_choice[iroi].GetStringSelection()
297 try:
298 contrast = self.icontrast[iroi].GetStringSelection()
299 except:
300 contrast = 0.0
301 if contrast in ('None', None):
302 contrast = 0.0
303 contrast = float(contrast)
306 if version_ge(self.cfile.version, '2.0.0'):
307 try:
308 roi = self.cfile.xrmmap['roimap'][detname][roiname]
309 limits = roi['limits'][:]
310 units = bytes2str(roi['limits'].attrs.get('units',''))
311 if units == '1/A':
312 roistr = '[%0.2f to %0.2f %s]' % (limits[0],limits[1],units)
313 else:
314 roistr = '[%0.1f to %0.1f %s]' % (limits[0],limits[1],units)
315 except:
316 roistr = ''
317 try:
318 map = self.cfile.get_roimap(roiname, det=detname)
319 imin, imax = np.percentile(map, (contrast, 100.0-contrast))
320 self.iminvals[iroi].SetValue(imin)
321 self.imaxvals[iroi].SetValue(imax)
322 except:
323 pass
324 else:
325 try:
326 roi = self.cfile.xrmmap[detname]
327 en = list(roi['energy'][:])
328 index = list(roi['roi_name'][:]).index(roiname)
329 limits = list(roi['roi_limits'][:][index])
330 roistr = '[%0.1f to %0.1f keV]' % (en[limits[0]],en[limits[1]])
331 except:
332 roistr = ''
334 self.roi_label[iroi].SetLabel(roistr)
336 def plotSELECT(self,event=None):
337 if len(self.owner.filemap) > 0:
338 plot_type = self.plot_choice.GetStringSelection().lower()
339 if 'single' in plot_type:
340 for i in (1,2):
341 self.det_choice[i].Disable()
342 self.roi_choice[i].Disable()
343 self.roi_label[i].SetLabel('')
344 for i,label in enumerate(['Intensity', ' ', ' ']):
345 self.det_label[i].SetLabel(label)
346 elif 'three' in plot_type:
347 for i in (1,2):
348 self.det_choice[i].Enable()
349 self.roi_choice[i].Enable()
350 for i,label in enumerate(['Red', 'Green', 'Blue']):
351 self.det_label[i].SetLabel(label)
352 self.set_roi_choices()
354 def onLasso(self, selected=None, mask=None, data=None, xrmfile=None, **kws):
355 if xrmfile is None: xrmfile = self.owner.current_file
356 ny, nx = xrmfile.get_shape()
357 indices = []
358 for idx in selected:
359 iy, ix = divmod(idx, ny)
360 indices.append((ix, iy))
363 def onClose(self):
364 for p in self.plotframes:
365 try:
366 p.Destroy()
367 except:
368 pass
370 def calculateSinogram(self,xrmfile=None):
371 '''
372 returns slice as [slices, x, 2th]
373 '''
374 subtitles = None
375 plt3 = 'three' in self.plot_choice.GetStringSelection().lower()
377 det_name = ['mcasum']*4
378 roi_name = ['']*4
379 plt_name = ['']*4
380 minvals = [0]*4
381 maxvals = [np.inf]*4
382 for i in range(4):
383 det_name[i] = self.det_choice[i].GetStringSelection()
384 roi_name[i] = self.roi_choice[i].GetStringSelection()
385 if det_name[i] == 'scalars':
386 plt_name[i] = '%s' % roi_name[i]
387 else:
388 plt_name[i] = '%s(%s)' % (roi_name[i],det_name[i])
389 if i < 3:
390 minvals[i] = self.iminvals[i].GetValue()
391 maxvals[i] = self.imaxvals[i].GetValue()
393 if plt3:
394 flagxrd = False
395 for det in det_name:
396 if det.startswith('xrd'): flagxrd = True
397 else:
398 flagxrd = True if det_name[0].startswith('xrd') else False
400 if xrmfile is None:
401 xrmfile = self.owner.current_file
403 args={'trim_sino' : flagxrd,
404 'hotcols' : False,
405 'dtcorrect' : self.owner.dtcor}
407 x = xrmfile.get_translation_axis(hotcols=args['hotcols'])
408 omega = xrmfile.get_rotation_axis(hotcols=args['hotcols'])
410 if omega is None:
411 print('\n** Cannot compute tomography: no rotation axis specified in map. **')
412 return
414 # check for common case of a few too many angles -- in which case, always
415 # remove the first and last:
416 domega = abs(np.diff(omega).mean())
417 # if abs(omega[-1] - omega[0]) > 360+2*domega:
418 # if not args['hotcols']:
419 # omega = omega[1:-1]
420 # print("TRIMMED OMEGA ", domega, omega.shape)
421 # args['hotcols'] = True
423 def normalize_map(xmap, normmap, roiname):
424 # print("normalize_map ", xmap.shape, xmap.dtype, normmap.shape, normmap.dtype)
425 xmap = xmap/(1.00*normmap)
426 label = ''
427 if self.i1trans.IsChecked() and roiname.lower().startswith('i1'):
428 xmap = -np.log(xmap)
429 label = '-log'
430 elif isinstance(normmap, np.ndarray):
431 xmap *= normmap.mean()
432 return xmap, label
434 normmap = 1.
435 if roi_name[-1] != '1':
436 normmap, sino_order = xrmfile.get_sinogram(roi_name[-1],
437 det=det_name[-1], **args)
438 normmap[np.where(normmap==0)] = 1.
440 r_map, sino_order = xrmfile.get_sinogram(roi_name[0],
441 det=det_name[0],
442 minval=minvals[0],
443 maxval=maxvals[0], **args)
444 r_map, r_lab = normalize_map(r_map, normmap, roi_name[0])
445 if plt3:
446 g_map, sino_order = xrmfile.get_sinogram(roi_name[1], det=det_name[1],
447 minval=minvals[1],
448 maxval=maxvals[1], **args)
449 b_map, sino_order = xrmfile.get_sinogram(roi_name[2], det=det_name[2],
450 minval=minvals[2],
451 maxval=maxvals[2], **args)
452 g_map, g_lab = normalize_map(g_map, normmap, roi_name[1])
453 b_map, b_lab = normalize_map(b_map, normmap, roi_name[2])
456 pref, fname = os.path.split(xrmfile.filename)
457 if plt3:
458 sino = np.array([r_map, g_map, b_map])
459 sino.resize(tuple(i for i in sino.shape if i!=1))
460 title = fname
461 info = ''
462 if roi_name[-1] == '1':
463 subtitles = {'red': 'Red: %s' % plt_name[0],
464 'green': 'Green: %s' % plt_name[1],
465 'blue': 'Blue: %s' % plt_name[2]}
466 else:
467 subtitles = {'red': 'Red: %s(%s/%s)' % (r_lab, plt_name[0], plt_name[-1]),
468 'green': 'Green: %s(%s/%s)' % (g_lab, plt_name[1], plt_name[-1]),
469 'blue': 'Blue: %s(%s/%s)' % (b_lab, plt_name[2], plt_name[-1])}
471 else:
472 sino = r_map
473 if roi_name[-1] == '1':
474 title = plt_name[0]
475 else:
476 title = '%s(%s/%s)' % (r_lab, plt_name[0] , plt_name[-1])
477 title = '%s: %s' % (fname, title)
478 info = 'Intensity: [%g, %g]' %(sino.min(), sino.max())
479 subtitle = None
481 return title, subtitles, info, x, omega, sino_order, sino
483 def onSaveTomograph(self, event=None):
485 xrmfile = self.owner.current_file
486 detpath = self.sino_data.GetStringSelection()
487 center = self.center_value.GetValue()
489 if not self.owner.dtcor and 'scalars' in detpath:
490 detpath = '%s_raw' % detpath
492 print('\nSaving tomographic reconstruction for %s ...' % detpath)
494 xrmfile.save_tomograph(detpath,
495 algorithm=self.tomo_algo.GetStringSelection(),
496 filter_name=self.tomo_filt.GetStringSelection(),
497 num_iter=self.tomo_niter.GetValue(),
498 center=center, dtcorrect=self.owner.dtcor,
499 hotcols=xrmfile.hotcols)
500 print('Saved.')
502 def onShowCentering(self, event=None):
503 xrmfile = self.owner.current_file
504 det = None
505 title, subtitles, info, x, omega, sino_order, sino = self.calculateSinogram()
506 algorithm = self.tomo_algo.GetStringSelection()
507 filter_name = self.tomo_filt.GetStringSelection()
508 niter = self.tomo_niter.GetValue()
509 center = self.center_value.GetValue()
511 omega = np.radians(omega)
512 img = tomopy.recon(sino, omega, center,
513 sinogram_order=sino_order,
514 algorithm='gridrec', filter_name='shepp')
515 img = tomopy.circ_mask(img, axis=0)
516 ioff = (img.max() - img.min())/25.0
517 imin = img.min() - ioff
518 imax = img.max() + ioff
520 centers = int(center) + np.linspace(-10, 10, 81)
521 scores = np.zeros(len(centers))
522 for i, cen in enumerate(centers):
523 score = center_score(cen, sino, omega, sinogram_order=sino_order,
524 imin=imin, imax=imax)
525 scores[i] = score
526 _plot(centers, scores, xlabel='Center(pixels)', ylabel='Blurriness',
527 new=True, markersize=4, marker='o', title='Image Blurriness Score')
529 def onShowTomograph(self, event=None, new=True):
530 xrmfile = self.owner.current_file
531 det = None
532 title, subtitles, info, x, omega, sino_order, sino = self.calculateSinogram()
534 algorithm = self.tomo_algo.GetStringSelection()
535 filter_name = self.tomo_filt.GetStringSelection()
536 niter = self.tomo_niter.GetValue()
537 center = self.center_value.GetValue()
538 refine_center = self.refine_center.GetValue()
540 tomo = xrmfile.get_tomograph(sino, refine_center=refine_center,
541 algorithm=algorithm,
542 filter_name=filter_name, num_iter=niter,
543 center=center, omega=omega,
544 sinogram_order=sino_order,
545 hotcols=xrmfile.hotcols)
547 # sharpness estimates:
548 if len(tomo.shape) == 3:
549 t = tomo.sum(axis=2)/tomo.max()
550 else:
551 t = tomo/tomo.max()
553 if refine_center:
554 self.set_center(xrmfile.xrmmap['tomo/center'][()])
555 self.refine_center.SetValue(False)
557 omeoff, xoff = 0, 0
558 title = '%s, center=%0.1f' % (title, center)
560 ## for one color plot
561 if sino.shape[0] == 1 and tomo.shape[0] == 1:
562 sino = sino[0]
563 tomo = tomo[0]
564 det = self.det_choice[0].GetStringSelection()
566 if len(self.owner.tomo_displays) == 0 or new:
567 iframe = self.owner.add_tomodisplay(title)
568 self.owner.display_tomo(tomo, title=title, subtitles=subtitles, det=det)
570 def set_center(self,cen):
571 self.center_value.SetValue(cen)
572 self.cfile.set_tomography_center(center=cen)
574 def set_det_choices(self):
575 det_list = self.cfile.get_detector_list()
577 for det_ch in self.det_choice:
578 det_ch.SetChoices(det_list)
579 if 'scalars' in det_list: ## should set 'denominator' to scalars as default
580 self.det_choice[-1].SetStringSelection('scalars')
582 data_list = self.cfile.get_datapath_list(remove='raw')
583 self.sino_data.SetChoices(data_list)
585 self.set_roi_choices()
587 def set_roi_choices(self, idet=None):
589 if idet is None:
590 for idet,det_ch in enumerate(self.det_choice):
591 detname = self.det_choice[idet].GetStringSelection()
592 rois = self.update_roi(detname)
594 self.roi_choice[idet].SetChoices(rois)
595 self.roiSELECT(idet)
596 else:
597 detname = self.det_choice[idet].GetStringSelection()
598 rois = self.update_roi(detname)
600 self.roi_choice[idet].SetChoices(rois)
601 self.roiSELECT(idet)
604 def update_roi(self, detname):
605 return self.cfile.get_roi_list(detname)