Coverage for larch/wxmap/maptomopanel.py: 16%
378 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
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 ..math.tomography import TOMOPY_ALG, TOMOPY_FILT, center_score
44from ..xrmmap import GSEXRM_MapFile, GSEXRM_FileStatus, h5str, ensure_subgroup
47CEN = wx.ALIGN_CENTER
48LEFT = wx.ALIGN_LEFT
49RIGHT = wx.ALIGN_RIGHT
50ALL_CEN = wx.ALL|CEN
51ALL_LEFT = wx.ALL|LEFT
52ALL_RIGHT = wx.ALL|RIGHT
54PLOT_TYPES = ('Single ROI Map', 'Three ROI Map', 'Correlation Plot')
55PLOT_OPERS = ('/', '*', '-', '+')
56CONTRAST_CHOICES = ('None',
57 '0.01', '0.02', '0.05',
58 '0.1', '0.2', '0.5',
59 '1', '2', '5')
61CWID = 150
62WWID = 100 + CWID*4
64class TomographyPanel(GridPanel):
65 '''Panel of Controls for reconstructing a tomographic slice'''
66 label = 'Tomography Tools'
67 def __init__(self, parent, owner=None, **kws):
69 self.owner = owner
70 self.cfile,self.xrmmap = None,None
71 self.npts = None
72 self.resave = False
74 GridPanel.__init__(self, parent, nrows=8, ncols=6, **kws)
76 self.plot_choice = Choice(self, choices=PLOT_TYPES[:-1], size=(CWID, -1))
77 self.plot_choice.Bind(wx.EVT_CHOICE, self.plotSELECT)
79 self.det_choice = [Choice(self, size=(CWID, -1)),
80 Choice(self, size=(CWID, -1)),
81 Choice(self, size=(CWID, -1)),
82 Choice(self, size=(CWID, -1))]
83 self.roi_choice = [Choice(self, size=(CWID, -1)),
84 Choice(self, size=(CWID, -1)),
85 Choice(self, size=(CWID, -1)),
86 Choice(self, size=(CWID, -1))]
88 fopts = dict(minval=0, precision=2, size=(110, -1))
89 self.iminvals = [FloatCtrl(self, value=0, **fopts),
90 FloatCtrl(self, value=0, **fopts),
91 FloatCtrl(self, value=0, **fopts)]
92 self.imaxvals = [FloatCtrl(self, value=0, **fopts),
93 FloatCtrl(self, value=0, **fopts),
94 FloatCtrl(self, value=0, **fopts)]
95 self.icontrast = [Choice(self, choices=CONTRAST_CHOICES, default=4, size=(CWID, -1)),
96 Choice(self, choices=CONTRAST_CHOICES, default=4, size=(CWID, -1)),
97 Choice(self, choices=CONTRAST_CHOICES, default=4, size=(CWID, -1))]
99 for i, d in enumerate(self.icontrast):
100 d.Bind(wx.EVT_CHOICE, partial(self.roiContrast, i))
102 for i,det_chc in enumerate(self.det_choice):
103 det_chc.Bind(wx.EVT_CHOICE, partial(self.detSELECT,i))
105 for i,roi_chc in enumerate(self.roi_choice):
106 roi_chc.Bind(wx.EVT_CHOICE, partial(self.roiSELECT,i))
108 self.det_label = [SimpleText(self,'Intensity'),
109 SimpleText(self,''),
110 SimpleText(self,''),
111 SimpleText(self,'Normalization')]
112 self.roi_label = [SimpleText(self,''),
113 SimpleText(self,''),
114 SimpleText(self,''),
115 SimpleText(self,'')]
117 self.use_dtcorr = Check(self, default=True,
118 label='Correct for Detector Deadtime',
119 action=self.onDTCorrect)
120 self.use_hotcols = Check(self, default=False,
121 label='Remove First and Last columns',
122 action=self.onHotCols)
123 self.i1trans = Check(self, default=True,
124 label='Scalar "i1" is transmission data')
126 self.tomo_show = [Button(self, 'Show New Map', size=(CWID, -1),
127 action=partial(self.onShowTomograph, new=True)),
128 Button(self, 'Replace Last Map', size=(CWID, -1),
129 action=partial(self.onShowTomograph, new=False)),
130 Button(self, 'Show Centering Data', size=(CWID, -1),
131 action=self.onShowCentering)]
133 self.tomo_algo = Choice(self, choices=TOMOPY_ALG, size=(CWID, -1),
134 action=self.onALGchoice)
135 self.tomo_filt = Choice(self, choices=TOMOPY_FILT, size=(CWID, -1))
136 self.tomo_niter = wx.SpinCtrl(self, min=1, max=500, initial=1,
137 size=(CWID, -1),
138 style=wx.SP_VERTICAL|wx.SP_ARROW_KEYS|wx.SP_WRAP)
140 self.center_value = wx.SpinCtrlDouble(self, inc=0.25, size=(100, -1),
141 style=wx.SP_VERTICAL|wx.SP_ARROW_KEYS|wx.SP_WRAP)
142 self.center_value.SetIncrement(0.25)
143 self.center_value.SetDigits(2)
144 self.refine_center = wx.CheckBox(self, label='Refine')
145 self.refine_center.SetValue(False)
147 self.sino_data = Choice(self, size=(200, -1))
148 self.tomo_save = Button(self, 'Save reconstruction', size=(150, -1),
149 action=self.onSaveTomograph)
152 #################################################################################
154 self.Add(SimpleText(self, 'Display Virtual Slices: Plot Type:'), dcol=2,
155 style=LEFT, newrow=True)
157 self.Add(self.plot_choice, dcol=1, style=LEFT)
158 self.Add(self.i1trans, dcol=2, style=LEFT)
159 self.Add(SimpleText(self,'Options:'), dcol=1, style=LEFT, newrow=True)
160 self.Add(self.use_dtcorr, dcol=2, style=LEFT)
161 self.Add(self.use_hotcols, dcol=2, style=LEFT)
163 self.AddMany((SimpleText(self,''), self.det_label[0],
164 self.det_label[1], self.det_label[2], self.det_label[3]),
165 style=LEFT, newrow=True)
167 self.AddMany((SimpleText(self,'Detector:'), self.det_choice[0],
168 self.det_choice[1], self.det_choice[2], self.det_choice[3]),
169 style=LEFT, newrow=True)
171 self.AddMany((SimpleText(self,'ROI:'), self.roi_choice[0],
172 self.roi_choice[1], self.roi_choice[2], self.roi_choice[3]),
173 style=LEFT, newrow=True)
175 self.AddMany((SimpleText(self,''), self.roi_label[0],
176 self.roi_label[1], self.roi_label[2],
177 self.roi_label[3]), style=LEFT, newrow=True)
179 self.AddMany((SimpleText(self,'I Min:'), self.iminvals[0],
180 self.iminvals[1], self.iminvals[2]), style=LEFT,
181 newrow=True)
183 self.AddMany((SimpleText(self,'I Max:'), self.imaxvals[0],
184 self.imaxvals[1], self.imaxvals[2]), style=LEFT,
185 newrow=True)
187 self.AddMany((SimpleText(self,'I contrast %:'), self.icontrast[0],
188 self.icontrast[1], self.icontrast[2]), style=LEFT,
189 newrow=True)
192 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
193 self.Add((5, 5), dcol=1, style=LEFT, newrow=True)
194 self.Add(self.tomo_show[0], dcol=1, style=LEFT)
195 self.Add(self.tomo_show[1], dcol=1, style=LEFT)
196 self.Add(self.tomo_show[2], dcol=1, style=LEFT)
198 self.Add(HLine(self, size=(WWID, 5)), dcol=8, style=LEFT, newrow=True)
200 self.Add(SimpleText(self,'Reconstruction '), dcol=2, style=LEFT, newrow=True)
202 self.Add(SimpleText(self,'Algorithm:'), dcol=1, style=LEFT, newrow=True)
203 self.Add(self.tomo_algo, dcol=1, style=LEFT)
204 self.Add(SimpleText(self,'Filter: '), dcol=1, style=LEFT)
205 self.Add(self.tomo_filt, dcol=1, style=LEFT)
207 self.Add(SimpleText(self,'# Iterations'), dcol=1, style=LEFT, newrow=True)
208 self.Add(self.tomo_niter, dcol=1, style=LEFT)
209 self.Add(SimpleText(self,'Center Pixel:'), dcol=1, style=LEFT)
210 self.Add(self.center_value, dcol=1, style=LEFT)
211 self.Add(self.refine_center, dcol=1, style=LEFT)
213 self.Add(HLine(self, size=(WWID, 5)), dcol=8, style=LEFT, newrow=True)
216 self.Add(SimpleText(self,'Data:'), dcol=1, style=LEFT, newrow=True)
217 self.Add(self.sino_data, dcol=2, style=LEFT)
218 self.Add(self.tomo_save, dcol=2, style=LEFT)
220 #################################################################################
221 self.pack()
223 def onDTCorrect(self, event=None):
224 self.owner.current_file.dtcorrect = self.use_dtcorr.IsChecked()
226 def onHotCols(self, event=None):
227 self.owner.current_file.hotcols = self.use_hotcols.IsChecked()
229 def update_xrmmap(self, xrmfile=None, set_detectors=None):
231 if xrmfile is None:
232 xrmfile = self.owner.current_file
234 self.cfile = xrmfile
235 self.xrmmap = self.cfile.xrmmap
238 if self.cfile.get_rotation_axis() is None:
239 self.center_value.SetValue(0)
240 return
242 self.set_det_choices()
244 try:
245 self.npts = len(self.cfile.get_pos(0, mean=True))
246 except:
247 self.npts = len(self.cfile.get_pos('x', mean=True))
249 center = self.cfile.get_tomography_center()
250 self.center_value.SetRange(-0.5*self.npts,1.5*self.npts)
251 self.center_value.SetValue(center)
253 self.plotSELECT()
256 def onALGchoice(self,event=None):
258 alg = self.tomo_algo.GetStringSelection().lower()
259 enable_filter = False
260 enable_niter = False
262 if alg.startswith('gridrec'):
263 enable_filter = True
264 else:
265 enable_niter = True
267 self.tomo_niter.Enable(enable_niter)
268 self.tomo_filt.Enable(enable_filter)
270 def detSELECT(self, idet, event=None):
271 self.set_roi_choices(idet=idet)
273 def roiContrast(self, iroi, event=None):
274 if iroi > 2:
275 return
276 try:
277 detname = self.det_choice[iroi].GetStringSelection()
278 roiname = self.roi_choice[iroi].GetStringSelection()
279 contrast = self.icontrast[iroi].GetStringSelection()
280 except:
281 return
282 if contrast in ('None', None):
283 contrast = 0.0
284 contrast = float(contrast)
285 try:
286 map = self.cfile.get_roimap(roiname, det=detname)
287 imin, imax = np.percentile(map, (contrast, 100.0-contrast))
288 self.iminvals[iroi].SetValue(imin)
289 self.imaxvals[iroi].SetValue(imax)
290 except:
291 pass
293 def roiSELECT(self, iroi, event=None):
294 detname = self.det_choice[iroi].GetStringSelection()
295 roiname = self.roi_choice[iroi].GetStringSelection()
296 try:
297 contrast = self.icontrast[iroi].GetStringSelection()
298 except:
299 contrast = 0.0
300 if contrast in ('None', None):
301 contrast = 0.0
302 contrast = float(contrast)
305 if version_ge(self.cfile.version, '2.0.0'):
306 try:
307 roi = self.cfile.xrmmap['roimap'][detname][roiname]
308 limits = roi['limits'][:]
309 units = bytes2str(roi['limits'].attrs.get('units',''))
310 if units == '1/A':
311 roistr = '[%0.2f to %0.2f %s]' % (limits[0],limits[1],units)
312 else:
313 roistr = '[%0.1f to %0.1f %s]' % (limits[0],limits[1],units)
314 except:
315 roistr = ''
316 try:
317 map = self.cfile.get_roimap(roiname, det=detname)
318 imin, imax = np.percentile(map, (contrast, 100.0-contrast))
319 self.iminvals[iroi].SetValue(imin)
320 self.imaxvals[iroi].SetValue(imax)
321 except:
322 pass
323 else:
324 try:
325 roi = self.cfile.xrmmap[detname]
326 en = list(roi['energy'][:])
327 index = list(roi['roi_name'][:]).index(roiname)
328 limits = list(roi['roi_limits'][:][index])
329 roistr = '[%0.1f to %0.1f keV]' % (en[limits[0]],en[limits[1]])
330 except:
331 roistr = ''
333 self.roi_label[iroi].SetLabel(roistr)
335 def plotSELECT(self,event=None):
336 if len(self.owner.filemap) > 0:
337 plot_type = self.plot_choice.GetStringSelection().lower()
338 if 'single' in plot_type:
339 for i in (1,2):
340 self.det_choice[i].Disable()
341 self.roi_choice[i].Disable()
342 self.roi_label[i].SetLabel('')
343 for i,label in enumerate(['Intensity', ' ', ' ']):
344 self.det_label[i].SetLabel(label)
345 elif 'three' in plot_type:
346 for i in (1,2):
347 self.det_choice[i].Enable()
348 self.roi_choice[i].Enable()
349 for i,label in enumerate(['Red', 'Green', 'Blue']):
350 self.det_label[i].SetLabel(label)
351 self.set_roi_choices()
353 def onLasso(self, selected=None, mask=None, data=None, xrmfile=None, **kws):
354 if xrmfile is None: xrmfile = self.owner.current_file
355 ny, nx = xrmfile.get_shape()
356 indices = []
357 for idx in selected:
358 iy, ix = divmod(idx, ny)
359 indices.append((ix, iy))
362 def onClose(self):
363 for p in self.plotframes:
364 try:
365 p.Destroy()
366 except:
367 pass
369 def calculateSinogram(self,xrmfile=None):
370 '''
371 returns slice as [slices, x, 2th]
372 '''
373 subtitles = None
374 plt3 = 'three' in self.plot_choice.GetStringSelection().lower()
376 det_name = ['mcasum']*4
377 roi_name = ['']*4
378 plt_name = ['']*4
379 minvals = [0]*4
380 maxvals = [np.inf]*4
381 for i in range(4):
382 det_name[i] = self.det_choice[i].GetStringSelection()
383 roi_name[i] = self.roi_choice[i].GetStringSelection()
384 if det_name[i] == 'scalars':
385 plt_name[i] = '%s' % roi_name[i]
386 else:
387 plt_name[i] = '%s(%s)' % (roi_name[i],det_name[i])
388 if i < 3:
389 minvals[i] = self.iminvals[i].GetValue()
390 maxvals[i] = self.imaxvals[i].GetValue()
392 if plt3:
393 flagxrd = False
394 for det in det_name:
395 if det.startswith('xrd'): flagxrd = True
396 else:
397 flagxrd = True if det_name[0].startswith('xrd') else False
399 if xrmfile is None:
400 xrmfile = self.owner.current_file
402 args={'trim_sino' : flagxrd,
403 'hotcols' : False,
404 'dtcorrect' : self.owner.dtcor}
406 x = xrmfile.get_translation_axis(hotcols=args['hotcols'])
407 omega = xrmfile.get_rotation_axis(hotcols=args['hotcols'])
409 if omega is None:
410 print('\n** Cannot compute tomography: no rotation axis specified in map. **')
411 return
413 # check for common case of a few too many angles -- in which case, always
414 # remove the first and last:
415 domega = abs(np.diff(omega).mean())
416 # if abs(omega[-1] - omega[0]) > 360+2*domega:
417 # if not args['hotcols']:
418 # omega = omega[1:-1]
419 # print("TRIMMED OMEGA ", domega, omega.shape)
420 # args['hotcols'] = True
422 def normalize_map(xmap, normmap, roiname):
423 # print("normalize_map ", xmap.shape, xmap.dtype, normmap.shape, normmap.dtype)
424 xmap = xmap/(1.00*normmap)
425 label = ''
426 if self.i1trans.IsChecked() and roiname.lower().startswith('i1'):
427 xmap = -np.log(xmap)
428 label = '-log'
429 elif isinstance(normmap, np.ndarray):
430 xmap *= normmap.mean()
431 return xmap, label
433 normmap = 1.
434 if roi_name[-1] != '1':
435 normmap, sino_order = xrmfile.get_sinogram(roi_name[-1],
436 det=det_name[-1], **args)
437 normmap[np.where(normmap==0)] = 1.
439 r_map, sino_order = xrmfile.get_sinogram(roi_name[0],
440 det=det_name[0],
441 minval=minvals[0],
442 maxval=maxvals[0], **args)
443 r_map, r_lab = normalize_map(r_map, normmap, roi_name[0])
444 if plt3:
445 g_map, sino_order = xrmfile.get_sinogram(roi_name[1], det=det_name[1],
446 minval=minvals[1],
447 maxval=maxvals[1], **args)
448 b_map, sino_order = xrmfile.get_sinogram(roi_name[2], det=det_name[2],
449 minval=minvals[2],
450 maxval=maxvals[2], **args)
451 g_map, g_lab = normalize_map(g_map, normmap, roi_name[1])
452 b_map, b_lab = normalize_map(b_map, normmap, roi_name[2])
455 pref, fname = os.path.split(xrmfile.filename)
456 if plt3:
457 sino = np.array([r_map, g_map, b_map])
458 sino.resize(tuple(i for i in sino.shape if i!=1))
459 title = fname
460 info = ''
461 if roi_name[-1] == '1':
462 subtitles = {'red': 'Red: %s' % plt_name[0],
463 'green': 'Green: %s' % plt_name[1],
464 'blue': 'Blue: %s' % plt_name[2]}
465 else:
466 subtitles = {'red': 'Red: %s(%s/%s)' % (r_lab, plt_name[0], plt_name[-1]),
467 'green': 'Green: %s(%s/%s)' % (g_lab, plt_name[1], plt_name[-1]),
468 'blue': 'Blue: %s(%s/%s)' % (b_lab, plt_name[2], plt_name[-1])}
470 else:
471 sino = r_map
472 if roi_name[-1] == '1':
473 title = plt_name[0]
474 else:
475 title = '%s(%s/%s)' % (r_lab, plt_name[0] , plt_name[-1])
476 title = '%s: %s' % (fname, title)
477 info = 'Intensity: [%g, %g]' %(sino.min(), sino.max())
478 subtitle = None
480 return title, subtitles, info, x, omega, sino_order, sino
482 def onSaveTomograph(self, event=None):
484 xrmfile = self.owner.current_file
485 detpath = self.sino_data.GetStringSelection()
486 center = self.center_value.GetValue()
488 if not self.owner.dtcor and 'scalars' in detpath:
489 detpath = '%s_raw' % detpath
491 print('\nSaving tomographic reconstruction for %s ...' % detpath)
493 xrmfile.save_tomograph(detpath,
494 algorithm=self.tomo_algo.GetStringSelection(),
495 filter_name=self.tomo_filt.GetStringSelection(),
496 num_iter=self.tomo_niter.GetValue(),
497 center=center, dtcorrect=self.owner.dtcor,
498 hotcols=xrmfile.hotcols)
499 print('Saved.')
501 def onShowCentering(self, event=None):
502 xrmfile = self.owner.current_file
503 det = None
504 title, subtitles, info, x, omega, sino_order, sino = self.calculateSinogram()
505 algorithm = self.tomo_algo.GetStringSelection()
506 filter_name = self.tomo_filt.GetStringSelection()
507 niter = self.tomo_niter.GetValue()
508 center = self.center_value.GetValue()
510 omega = np.radians(omega)
511 img = tomopy.recon(sino, omega, center,
512 sinogram_order=sino_order,
513 algorithm='gridrec', filter_name='shepp')
514 img = tomopy.circ_mask(img, axis=0)
515 ioff = (img.max() - img.min())/25.0
516 imin = img.min() - ioff
517 imax = img.max() + ioff
519 centers = int(center) + np.linspace(-10, 10, 81)
520 scores = np.zeros(len(centers))
521 for i, cen in enumerate(centers):
522 score = center_score(cen, sino, omega, sinogram_order=sino_order,
523 imin=imin, imax=imax)
524 scores[i] = score
525 _plot(centers, scores, xlabel='Center(pixels)', ylabel='Blurriness',
526 new=True, markersize=4, marker='o', title='Image Blurriness Score')
528 def onShowTomograph(self, event=None, new=True):
529 xrmfile = self.owner.current_file
530 det = None
531 title, subtitles, info, x, omega, sino_order, sino = self.calculateSinogram()
533 algorithm = self.tomo_algo.GetStringSelection()
534 filter_name = self.tomo_filt.GetStringSelection()
535 niter = self.tomo_niter.GetValue()
536 center = self.center_value.GetValue()
537 refine_center = self.refine_center.GetValue()
539 tomo = xrmfile.get_tomograph(sino, refine_center=refine_center,
540 algorithm=algorithm,
541 filter_name=filter_name, num_iter=niter,
542 center=center, omega=omega,
543 sinogram_order=sino_order,
544 hotcols=xrmfile.hotcols)
546 # sharpness estimates:
547 if len(tomo.shape) == 3:
548 t = tomo.sum(axis=2)/tomo.max()
549 else:
550 t = tomo/tomo.max()
552 if refine_center:
553 self.set_center(xrmfile.xrmmap['tomo/center'][()])
554 self.refine_center.SetValue(False)
556 omeoff, xoff = 0, 0
557 title = '%s, center=%0.1f' % (title, center)
559 ## for one color plot
560 if sino.shape[0] == 1 and tomo.shape[0] == 1:
561 sino = sino[0]
562 tomo = tomo[0]
563 det = self.det_choice[0].GetStringSelection()
565 if len(self.owner.tomo_displays) == 0 or new:
566 iframe = self.owner.add_tomodisplay(title)
567 self.owner.display_tomo(tomo, title=title, subtitles=subtitles, det=det)
569 def set_center(self,cen):
570 self.center_value.SetValue(cen)
571 self.cfile.set_tomography_center(center=cen)
573 def set_det_choices(self):
574 det_list = self.cfile.get_detector_list()
576 for det_ch in self.det_choice:
577 det_ch.SetChoices(det_list)
578 if 'scalars' in det_list: ## should set 'denominator' to scalars as default
579 self.det_choice[-1].SetStringSelection('scalars')
581 data_list = self.cfile.get_datapath_list(remove='raw')
582 self.sino_data.SetChoices(data_list)
584 self.set_roi_choices()
586 def set_roi_choices(self, idet=None):
588 if idet is None:
589 for idet,det_ch in enumerate(self.det_choice):
590 detname = self.det_choice[idet].GetStringSelection()
591 rois = self.update_roi(detname)
593 self.roi_choice[idet].SetChoices(rois)
594 self.roiSELECT(idet)
595 else:
596 detname = self.det_choice[idet].GetStringSelection()
597 rois = self.update_roi(detname)
599 self.roi_choice[idet].SetChoices(rois)
600 self.roiSELECT(idet)
603 def update_roi(self, detname):
604 return self.cfile.get_roi_list(detname)