comparison tomo.py @ 61:75dd6e15f628 draft

"planemo upload for repository https://github.com/rolfverberg/galaxytools commit 47b7bc203de6eac4d3e081b1fae8acb352f54b55"
author rv43
date Wed, 17 Aug 2022 12:11:20 +0000
parents 26f99fdd8d61
children 98a83f03d91b
comparison
equal deleted inserted replaced
60:52db7707ff48 61:75dd6e15f628
43 from fit import Fit 43 from fit import Fit
44 #from msnctools.general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \ 44 #from msnctools.general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \
45 from general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \ 45 from general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \
46 input_int, input_num, input_yesno, input_menu, findImageFiles, loadImageStack, clearPlot, \ 46 input_int, input_num, input_yesno, input_menu, findImageFiles, loadImageStack, clearPlot, \
47 draw_mask_1d, quickPlot, clearImshow, quickImshow, combine_tiffs_in_h5, Config 47 draw_mask_1d, quickPlot, clearImshow, quickImshow, combine_tiffs_in_h5, Config
48 from general import selectArrayBounds,selectImageRange, selectImageBounds 48 from general import selectImageRange, selectImageBounds
49 49
50 # the following tomopy routines don't run with more than 24 cores on Galaxy-Dev 50 # the following tomopy routines don't run with more than 24 cores on Galaxy-Dev
51 # - tomopy.find_center_vo 51 # - tomopy.find_center_vo
52 # - tomopy.prep.stripe.remove_stripe_fw 52 # - tomopy.prep.stripe.remove_stripe_fw
53 num_core_tomopy_limit = 24 53 num_core_tomopy_limit = 24
427 # Validate input parameters 427 # Validate input parameters
428 if config_file is not None and not os.path.isfile(config_file): 428 if config_file is not None and not os.path.isfile(config_file):
429 raise OSError(f'Invalid config_file input {config_file} {type(config_file)}') 429 raise OSError(f'Invalid config_file input {config_file} {type(config_file)}')
430 if config_dict is not None and not isinstance(config_dict, dict): 430 if config_dict is not None and not isinstance(config_dict, dict):
431 raise ValueError(f'Invalid config_dict input {config_dict} {type(config_dict)}') 431 raise ValueError(f'Invalid config_dict input {config_dict} {type(config_dict)}')
432 if config_out is not None: 432 if self.config_out is not None and not isinstance(self.config_out, str):
433 if isinstance(config_out, str): 433 raise OSError(f'Invalid config_out input {self.config_out} {type(self.config_out)}')
434 if isinstance(log_stream, str):
435 path = os.path.split(log_stream)[0]
436 if path and not os.path.isdir(path):
437 raise OSError(f'Invalid log_stream path')
438 else:
439 raise OSError(f'Invalid config_out input {config_out} {type(config_out)}')
440 if not os.path.isdir(output_folder): 434 if not os.path.isdir(output_folder):
441 os.mkdir(os.path.abspath(output_folder)) 435 os.mkdir(os.path.abspath(output_folder))
442 if isinstance(log_stream, str): 436 if isinstance(log_stream, str):
443 path = os.path.split(log_stream)[0] 437 path = os.path.split(log_stream)[0]
444 if path and not os.path.isdir(path): 438 if path and not os.path.isdir(path):
445 raise OSError(f'Invalid log_stream path') 439 raise OSError(f'Invalid log_stream path')
446 if not os.path.isabs(path): 440 if not os.path.isabs(path):
447 log_stream = f'{output_folder}/{log_stream}' 441 log_stream = f'{output_folder}/{log_stream}'
448 if not isinstance(galaxy_flag, bool): 442 if not isinstance(galaxy_flag, bool):
449 raise ValueError(f'Invalid galaxy_flag input {galaxy_flag} {type(galaxy_flag)}') 443 raise ValueError(f'Invalid galaxy_flag input {galaxy_flag} {type(galaxy_flag)}')
450 if not isinstance(test_mode, bool): 444 if not isinstance(self.test_mode, bool):
451 raise ValueError(f'Invalid test_mode input {test_mode} {type(test_mode)}') 445 raise ValueError(f'Invalid test_mode input {self.test_mode} {type(self.test_mode)}')
452 if not isinstance(num_core, int) or num_core < -1 or num_core == 0: 446 if not isinstance(num_core, int) or num_core < -1 or num_core == 0:
453 raise ValueError(f'Invalid num_core input {num_core} {type(num_core)}') 447 raise ValueError(f'Invalid num_core input {num_core} {type(num_core)}')
454 if num_core == -1: 448 if num_core == -1:
455 self.num_core = mp.cpu_count() 449 self.num_core = mp.cpu_count()
456 else: 450 else:
663 657
664 # Take median 658 # Take median
665 self.tdf = np.median(tdf_stack, axis=0) 659 self.tdf = np.median(tdf_stack, axis=0)
666 del tdf_stack 660 del tdf_stack
667 661
668 # RV make input of some kind (not always needed) 662 # Remove dark field intensities above the cutoff
669 tdf_cutoff = 21 663 tdf_cutoff = dark_field.get('cutoff')
670 self.tdf[self.tdf > tdf_cutoff] = np.nan 664 if tdf_cutoff is not None:
665 if not is_num(tdf_cutoff, 0):
666 logging.warning(f'Ignoring illegal value of tdf_cutoff {tdf_cutoff}')
667 else:
668 self.tdf[self.tdf > tdf_cutoff] = np.nan
669 logging.debug(f'tdf_cutoff = {tdf_cutoff}')
670
671 tdf_mean = np.nanmean(self.tdf) 671 tdf_mean = np.nanmean(self.tdf)
672 logging.debug(f'tdf_cutoff = {tdf_cutoff}')
673 logging.debug(f'tdf_mean = {tdf_mean}') 672 logging.debug(f'tdf_mean = {tdf_mean}')
674 np.nan_to_num(self.tdf, copy=False, nan=tdf_mean, posinf=tdf_mean, neginf=0.) 673 np.nan_to_num(self.tdf, copy=False, nan=tdf_mean, posinf=tdf_mean, neginf=0.)
675 if self.galaxy_flag: 674 if self.galaxy_flag:
676 quickImshow(self.tdf, title='dark field', path='setup_pngs', 675 quickImshow(self.tdf, title='dark field', path='setup_pngs',
677 save_fig=True, save_only=True) 676 save_fig=True, save_only=True)
1240 t0 = time() 1239 t0 = time()
1241 recon_sinogram = iradon(sinogram, theta=thetas_deg, circle=True) 1240 recon_sinogram = iradon(sinogram, theta=thetas_deg, circle=True)
1242 logging.debug(f'inverting sinogram took {time()-t0:.2f} seconds!') 1241 logging.debug(f'inverting sinogram took {time()-t0:.2f} seconds!')
1243 del sinogram 1242 del sinogram
1244 1243
1245 # Removing ring artifacts 1244 # Performing Gaussian filtering and removing ring artifacts
1246 # RV parameters for the denoise, gaussian, and ring removal will be different for different feature sizes 1245 recon_parameters = self.config.get('recon_parameters')
1246 if recon_parameters is None:
1247 sigma = 1.0
1248 ring_width = 15
1249 else:
1250 sigma = recon_parameters.get('gaussian_sigma', 1.0)
1251 if not is_num(sigma, 0.0):
1252 logging.warning(f'Illegal gaussian_sigma ({sigma}) in _reconstructOnePlane, '+
1253 'set to a default value of 1.0')
1254 sigma = 1.0
1255 ring_width = recon_parameters.get('ring_width', 15)
1256 if not is_int(ring_width, 0):
1257 logging.warning(f'Illegal ring_width ({ring_width}) in _reconstructOnePlane, '+
1258 'set to a default value of 15')
1259 ring_width = 15
1247 t0 = time() 1260 t0 = time()
1248 # recon_sinogram = filters.gaussian(recon_sinogram, 3.0) 1261 recon_sinogram = spi.gaussian_filter(recon_sinogram, sigma, mode='nearest')
1249 recon_sinogram = spi.gaussian_filter(recon_sinogram, 0.5)
1250 recon_clean = np.expand_dims(recon_sinogram, axis=0) 1262 recon_clean = np.expand_dims(recon_sinogram, axis=0)
1251 del recon_sinogram 1263 del recon_sinogram
1252 t1 = time() 1264 t1 = time()
1253 logging.debug(f'running remove_ring on {num_core} cores ...') 1265 logging.debug(f'running remove_ring on {num_core} cores ...')
1254 recon_clean = tomopy.misc.corr.remove_ring(recon_clean, rwidth=17, ncore=num_core) 1266 recon_clean = tomopy.misc.corr.remove_ring(recon_clean, rwidth=ring_width, ncore=num_core)
1255 logging.debug(f'... remove_ring took {time()-t1:.2f} seconds!') 1267 logging.debug(f'... remove_ring took {time()-t1:.2f} seconds!')
1256 logging.debug(f'filtering and removing ring artifact took {time()-t0:.2f} seconds!') 1268 logging.debug(f'filtering and removing ring artifact took {time()-t0:.2f} seconds!')
1257 return recon_clean 1269 return recon_clean
1258 1270
1259 def _plotEdgesOnePlane(self, recon_plane, title, path=None, weight=0.001): 1271 def _plotEdgesOnePlane(self, recon_plane, title, path=None):
1260 # RV parameters for the denoise, gaussian, and ring removal will be different for different feature sizes 1272 vis_parameters = self.config.get('vis_parameters')
1261 edges = denoise_tv_chambolle(recon_plane, weight = weight) 1273 if vis_parameters is None:
1274 weight = 0.1
1275 else:
1276 weight = vis_parameters.get('denoise_weight', 0.1)
1277 if not is_num(weight, 0.0):
1278 logging.warning(f'Illegal weight ({weight}) in _plotEdgesOnePlane, '+
1279 'set to a default value of 0.1')
1280 weight = 0.1
1281 edges = denoise_tv_chambolle(recon_plane, weight=weight)
1262 vmax = np.max(edges[0,:,:]) 1282 vmax = np.max(edges[0,:,:])
1263 vmin = -vmax 1283 vmin = -vmax
1264 if self.galaxy_flag: 1284 if self.galaxy_flag:
1265 quickImshow(edges[0,:,:], title, path=path, save_fig=True, save_only=True, 1285 quickImshow(edges[0,:,:], title, path=path, save_fig=True, save_only=True,
1266 cmap='gray', vmin=vmin, vmax=vmax) 1286 cmap='gray', vmin=vmin, vmax=vmax)
1393 center_offset = center_offset_vo 1413 center_offset = center_offset_vo
1394 else: 1414 else:
1395 center_offset = input_num(' Enter chosen center offset', -center, center) 1415 center_offset = input_num(' Enter chosen center offset', -center, center)
1396 return float(center_offset) 1416 return float(center_offset)
1397 1417
1398 def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, 1418 def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, center_offsets=[],
1399 center_offsets=[], sigma=0.1, rwidth=30, num_core=1, algorithm='gridrec', 1419 num_core=1, algorithm='gridrec'):
1400 run_secondary_sirt=False, secondary_iter=100):
1401 """reconstruct a single tomography stack. 1420 """reconstruct a single tomography stack.
1402 """ 1421 """
1403 # stack order: row,theta,column 1422 # stack order: row,theta,column
1404 # thetas must be in radians 1423 # thetas must be in radians
1405 # centers_offset: tomography axis shift in pixels relative to column center 1424 # centers_offset: tomography axis shift in pixels relative to column center
1423 else: 1442 else:
1424 if center_offsets.size != row_bounds[1]-row_bounds[0]: 1443 if center_offsets.size != row_bounds[1]-row_bounds[0]:
1425 raise ValueError('center_offsets dimension mismatch in reconstructOneTomoStack') 1444 raise ValueError('center_offsets dimension mismatch in reconstructOneTomoStack')
1426 centers = center_offsets 1445 centers = center_offsets
1427 centers += tomo_stack.shape[2]/2 1446 centers += tomo_stack.shape[2]/2
1447
1448 # Removing horizontal stripe and ring artifacts and perform secondary iterations
1449 recon_parameters = self.config.get('recon_parameters')
1450 if recon_parameters is None:
1451 sigma = 2.0
1452 secondary_iters = 0
1453 ring_width = 15
1454 else:
1455 sigma = recon_parameters.get('stripe_fw_sigma', 2.0)
1456 if not is_num(sigma, 0):
1457 logging.warning(f'Illegal stripe_fw_sigma ({sigma}) in '+
1458 '_reconstructOneTomoStack, set to a default value of 2.0')
1459 ring_width = 15
1460 secondary_iters = recon_parameters.get('secondary_iters', 0)
1461 if not is_int(secondary_iters, 0):
1462 logging.warning(f'Illegal secondary_iters ({secondary_iters}) in '+
1463 '_reconstructOneTomoStack, set to a default value of 0 (skip them)')
1464 ring_width = 0
1465 ring_width = recon_parameters.get('ring_width', 15)
1466 if not is_int(ring_width, 0):
1467 logging.warning(f'Illegal ring_width ({ring_width}) in _reconstructOnePlane, '+
1468 'set to a default value of 15')
1469 ring_width = 15
1428 if True: 1470 if True:
1429 t0 = time() 1471 t0 = time()
1430 if num_core > num_core_tomopy_limit: 1472 if num_core > num_core_tomopy_limit:
1431 logging.debug('running remove_stripe_fw on {num_core_tomopy_limit} cores ...') 1473 logging.debug('running remove_stripe_fw on {num_core_tomopy_limit} cores ...')
1432 tomo_stack = tomopy.prep.stripe.remove_stripe_fw( 1474 tomo_stack = tomopy.prep.stripe.remove_stripe_fw(
1443 t0 = time() 1485 t0 = time()
1444 logging.debug(f'running recon on {num_core} cores ...') 1486 logging.debug(f'running recon on {num_core} cores ...')
1445 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, sinogram_order=True, 1487 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, sinogram_order=True,
1446 algorithm=algorithm, ncore=num_core) 1488 algorithm=algorithm, ncore=num_core)
1447 logging.debug(f'... recon took {time()-t0:.2f} seconds!') 1489 logging.debug(f'... recon took {time()-t0:.2f} seconds!')
1448 if run_secondary_sirt and secondary_iter > 0: 1490 if secondary_iters > 0:
1449 logging.debug(f'running {secondary_iter} secondary iterations') 1491 logging.debug(f'running {secondary_iters} secondary iterations')
1450 #options = {'method':'SIRT_CUDA', 'proj_type':'cuda', 'num_iter':secondary_iter} 1492 #options = {'method':'SIRT_CUDA', 'proj_type':'cuda', 'num_iter':secondary_iters}
1451 #RV: doesn't work for me: "Error: CUDA error 803: system has unsupported display driver / 1493 #RV: doesn't work for me: "Error: CUDA error 803: system has unsupported display driver /
1452 # cuda driver combination." 1494 # cuda driver combination."
1453 #options = {'method':'SIRT', 'proj_type':'linear', 'MinConstraint': 0, 'num_iter':secondary_iter} 1495 #options = {'method':'SIRT', 'proj_type':'linear', 'MinConstraint': 0, 'num_iter':secondary_iters}
1454 #SIRT did not finish while running overnight 1496 #SIRT did not finish while running overnight
1455 #options = {'method':'SART', 'proj_type':'linear', 'num_iter':secondary_iter} 1497 #options = {'method':'SART', 'proj_type':'linear', 'num_iter':secondary_iters}
1456 options = {'method':'SART', 'proj_type':'linear', 'MinConstraint': 0, 1498 options = {'method':'SART', 'proj_type':'linear', 'MinConstraint': 0,
1457 'num_iter':secondary_iter} 1499 'num_iter':secondary_iters}
1458 t0 = time() 1500 t0 = time()
1459 logging.debug(f'running recon on {num_core} cores ...') 1501 logging.debug(f'running recon on {num_core} cores ...')
1460 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, 1502 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers,
1461 init_recon=tomo_recon_stack, options=options, sinogram_order=True, 1503 init_recon=tomo_recon_stack, options=options, sinogram_order=True,
1462 algorithm=tomopy.astra, ncore=num_core) 1504 algorithm=tomopy.astra, ncore=num_core)
1463 logging.debug(f'... recon took {time()-t0:.2f} seconds!') 1505 logging.debug(f'... recon took {time()-t0:.2f} seconds!')
1464 if True: 1506 if True:
1465 t0 = time() 1507 t0 = time()
1466 logging.debug(f'running remove_ring on {num_core} cores ...') 1508 logging.debug(f'running remove_ring on {num_core} cores ...')
1467 tomopy.misc.corr.remove_ring(tomo_recon_stack, rwidth=rwidth, out=tomo_recon_stack, 1509 tomopy.misc.corr.remove_ring(tomo_recon_stack, rwidth=ring_width, out=tomo_recon_stack,
1468 ncore=num_core) 1510 ncore=num_core)
1469 logging.debug(f'... remove_ring took {time()-t0:.2f} seconds!') 1511 logging.debug(f'... remove_ring took {time()-t0:.2f} seconds!')
1470 return tomo_recon_stack 1512 return tomo_recon_stack
1471 1513
1472 def findImageFiles(self, tiff_to_h5_flag = False): 1514 def findImageFiles(self, tiff_to_h5_flag = False):
1621 logging.warning('Ignoring galaxy_param in genTomoStacks (only for Galaxy)') 1663 logging.warning('Ignoring galaxy_param in genTomoStacks (only for Galaxy)')
1622 galaxy_param = None 1664 galaxy_param = None
1623 tdf_files, tbf_files, tomo_stack_files = self.findImageFiles() 1665 tdf_files, tbf_files, tomo_stack_files = self.findImageFiles()
1624 if not self.is_valid: 1666 if not self.is_valid:
1625 return 1667 return
1668 if self.test_mode:
1669 required = True
1670 else:
1671 required = False
1626 for i,stack in enumerate(stacks): 1672 for i,stack in enumerate(stacks):
1627 if not self.tomo_stacks[i].size and stack.get('preprocessed', False): 1673 if not self.tomo_stacks[i].size and stack.get('preprocessed', False):
1628 self.tomo_stacks[i], available_stacks[i] = \ 1674 self.tomo_stacks[i], available_stacks[i] = \
1629 self._loadTomo('red stack', stack['index']) 1675 self._loadTomo('red stack', stack['index'], required=required)
1630 1676
1631 # Preprocess any unloaded stacks 1677 # Preprocess any unloaded stacks
1632 if False in available_stacks: 1678 if False in available_stacks:
1633 logging.debug('Preprocessing tomography images') 1679 logging.debug('Preprocessing tomography images')
1634 1680
1926 if self.save_plots_only: 1972 if self.save_plots_only:
1927 clearImshow(f'theta={theta_start}') 1973 clearImshow(f'theta={theta_start}')
1928 if use_row: 1974 if use_row:
1929 center_offset = find_center.get('upper_center_offset') 1975 center_offset = find_center.get('upper_center_offset')
1930 if is_num(center_offset): 1976 if is_num(center_offset):
1931 use_center = input_yesrnNo('Current upper center offset = '+ 1977 use_center = input_yesno('Current upper center offset = '+
1932 f'{center_offset}, use this value (y/n)?', 'y') 1978 f'{center_offset}, use this value (y/n)?', 'y')
1933 if not use_center: 1979 if not use_center:
1934 if not use_row: 1980 if not use_row:
1935 if not self.test_mode: 1981 if not self.test_mode:
1936 quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', 1982 quickImshow(center_stack[:,0,:], title=f'theta={theta_start}',
2126 center_offsets = [lower_center_offset-lower_row*center_slope, 2172 center_offsets = [lower_center_offset-lower_row*center_slope,
2127 upper_center_offset+(self.tomo_stacks[i].shape[0]-1-upper_row)*center_slope] 2173 upper_center_offset+(self.tomo_stacks[i].shape[0]-1-upper_row)*center_slope]
2128 t0 = time() 2174 t0 = time()
2129 logging.debug(f'running _reconstructOneTomoStack on {num_core} cores ...') 2175 logging.debug(f'running _reconstructOneTomoStack on {num_core} cores ...')
2130 self.tomo_recon_stacks[i]= self._reconstructOneTomoStack(self.tomo_stacks[i], 2176 self.tomo_recon_stacks[i]= self._reconstructOneTomoStack(self.tomo_stacks[i],
2131 thetas, center_offsets=center_offsets, sigma=0.1, num_core=num_core, 2177 thetas, center_offsets=center_offsets, num_core=num_core,
2132 algorithm='gridrec', run_secondary_sirt=True, secondary_iter=25) 2178 algorithm='gridrec')
2133 logging.debug(f'... _reconstructOneTomoStack took {time()-t0:.2f} seconds!') 2179 logging.debug(f'... _reconstructOneTomoStack took {time()-t0:.2f} seconds!')
2134 logging.info(f'Reconstruction of stack {index} took {time()-t0:.2f} seconds!') 2180 logging.info(f'Reconstruction of stack {index} took {time()-t0:.2f} seconds!')
2135 if self.galaxy_flag: 2181 if self.galaxy_flag:
2136 x_slice = int(self.tomo_stacks[i].shape[0]/2) 2182 x_slice = int(self.tomo_recon_stacks[i].shape[0]/2)
2137 title = f'{basetitle} {index} xslice{x_slice}' 2183 title = f'{basetitle} {index} xslice{x_slice}'
2138 quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, 2184 quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title,
2139 path='center_slice_pngs', save_fig=True, save_only=True) 2185 path='center_slice_pngs', save_fig=True, save_only=True)
2140 y_slice = int(self.tomo_stacks[i].shape[0]/2) 2186 y_slice = int(self.tomo_recon_stacks[i].shape[1]/2)
2141 title = f'{basetitle} {index} yslice{y_slice}' 2187 title = f'{basetitle} {index} yslice{y_slice}'
2142 quickImshow(self.tomo_recon_stacks[i][:,y_slice,:], title=title, 2188 quickImshow(self.tomo_recon_stacks[i][:,y_slice,:], title=title,
2143 path='center_slice_pngs', save_fig=True, save_only=True) 2189 path='center_slice_pngs', save_fig=True, save_only=True)
2144 z_slice = int(self.tomo_stacks[i].shape[0]/2) 2190 z_slice = int(self.tomo_recon_stacks[i].shape[2]/2)
2145 title = f'{basetitle} {index} zslice{z_slice}' 2191 title = f'{basetitle} {index} zslice{z_slice}'
2146 quickImshow(self.tomo_recon_stacks[i][:,:,z_slice], title=title, 2192 quickImshow(self.tomo_recon_stacks[i][:,:,z_slice], title=title,
2147 path='center_slice_pngs', save_fig=True, save_only=True) 2193 path='center_slice_pngs', save_fig=True, save_only=True)
2148 elif not self.test_mode: 2194 else:
2149 x_slice = int(self.tomo_stacks[i].shape[0]/2) 2195 x_slice = int(self.tomo_recon_stacks[i].shape[0]/2)
2150 title = f'{basetitle} {index} xslice{x_slice}' 2196 title = f'{basetitle} {index} xslice{x_slice}'
2151 quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, 2197 quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title,
2152 path=self.output_folder, save_fig=self.save_plots, 2198 path=self.output_folder, save_fig=self.save_plots,
2153 save_only=self.save_plots_only) 2199 save_only=self.save_plots_only)
2154 quickPlot(self.tomo_recon_stacks[i] 2200 y_slice = int(self.tomo_recon_stacks[i].shape[1]/2)
2155 [x_slice,int(self.tomo_recon_stacks[i].shape[2]/2),:], 2201 title = f'{basetitle} {index} yslice{y_slice}'
2156 title=f'{title} cut{int(self.tomo_recon_stacks[i].shape[2]/2)}', 2202 quickImshow(self.tomo_recon_stacks[i][:,y_slice,:], title=title,
2157 path=self.output_folder, save_fig=self.save_plots, 2203 path=self.output_folder, save_fig=self.save_plots,
2158 save_only=self.save_plots_only) 2204 save_only=self.save_plots_only)
2159 self._saveTomo('recon stack', self.tomo_recon_stacks[i], index) 2205 z_slice = int(self.tomo_recon_stacks[i].shape[2]/2)
2206 title = f'{basetitle} {index} zslice{z_slice}'
2207 quickImshow(self.tomo_recon_stacks[i][:,:,z_slice], title=title,
2208 path=self.output_folder, save_fig=self.save_plots,
2209 save_only=self.save_plots_only)
2210 # quickPlot(self.tomo_recon_stacks[i]
2211 # [x_slice,int(self.tomo_recon_stacks[i].shape[1]/2),:],
2212 # title=f'{title} cut{int(self.tomo_recon_stacks[i].shape[1]/2)}',
2213 # path=self.output_folder, save_fig=self.save_plots,
2214 # save_only=self.save_plots_only)
2215 if not self.test_mode:
2216 self._saveTomo('recon stack', self.tomo_recon_stacks[i], index)
2160 self.tomo_stacks[i] = np.array([]) 2217 self.tomo_stacks[i] = np.array([])
2161 2218
2162 # Update config and save to file 2219 # Update config and save to file
2163 stack['reconstructed'] = True 2220 stack['reconstructed'] = True
2164 combine_stacks = self.config.get('combine_stacks') 2221 combine_stacks = self.config.get('combine_stacks')
2279 quickPlot((range(tomosum.size), tomosum), 2336 quickPlot((range(tomosum.size), tomosum),
2280 ([x_bounds[0], x_bounds[0]], [tomosum_min, tomosum_max], 'r-'), 2337 ([x_bounds[0], x_bounds[0]], [tomosum_min, tomosum_max], 'r-'),
2281 ([x_bounds[1]-1, x_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), 2338 ([x_bounds[1]-1, x_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'),
2282 title=f'recon stack sum yz', path='combine_pngs', save_fig=True, save_only=True) 2339 title=f'recon stack sum yz', path='combine_pngs', save_fig=True, save_only=True)
2283 else: 2340 else:
2341 x_bounds = None
2342 change_x_bounds = 'y'
2284 if combine_stacks and 'x_bounds' in combine_stacks: 2343 if combine_stacks and 'x_bounds' in combine_stacks:
2285 x_bounds = combine_stacks['x_bounds'] 2344 x_bounds = combine_stacks['x_bounds']
2286 if not is_index_range(x_bounds, 0, tomosum.size): 2345 if is_index_range(x_bounds, 0, tomosum.size):
2346 if not self.test_mode:
2347 quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz')
2348 print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+
2349 'exclusive)')
2350 change_x_bounds = 'n'
2351 else:
2287 illegal_value(x_bounds, 'combine_stacks:x_bounds', 'config file') 2352 illegal_value(x_bounds, 'combine_stacks:x_bounds', 'config file')
2288 elif not self.test_mode: 2353 x_bounds = None
2354 if self.test_mode:
2355 if x_bounds is None:
2356 x_bounds = [0, tomosum.size]
2357 else:
2358 if not input_yesno('\nDo you want to change the image x-bounds (y/n)?',
2359 change_x_bounds):
2360 if x_bounds is None:
2361 x_bounds = [0, tomosum.size]
2362 else:
2289 accept = False 2363 accept = False
2364 if x_bounds is None:
2365 index_ranges = None
2366 else:
2367 index_ranges = [x_bounds]
2290 while not accept: 2368 while not accept:
2291 mask, x_bounds = draw_mask_1d(tomosum, current_index_ranges=x_bounds, 2369 mask, x_bounds = draw_mask_1d(tomosum, current_index_ranges=index_ranges,
2292 title='select x data range', legend='recon stack sum yz') 2370 title='select x data range', legend='recon stack sum yz')
2293 while len(x_bounds) != 1: 2371 while len(x_bounds) != 1:
2294 print('Please select exactly one continuous range') 2372 print('Please select exactly one continuous range')
2295 mask, x_bounds = draw_mask_1d(tomosum, title='select x data range', 2373 mask, x_bounds = draw_mask_1d(tomosum, title='select x data range',
2296 legend='recon stack sum yz') 2374 legend='recon stack sum yz')
2297 x_bounds = list(x_bounds[0]) 2375 x_bounds = list(x_bounds[0])
2298 quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz') 2376 quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz')
2299 print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+ 2377 print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+
2300 'exclusive)') 2378 'exclusive)')
2301 accept = input_yesno('Accept these bounds (y/n)?', 'y') 2379 accept = input_yesno('Accept these bounds (y/n)?', 'y')
2302 if not accept:
2303 x_bounds = None
2304 else:
2305 if not input_yesno('\nDo you want to change the image x-bounds (y/n)?', 'n'):
2306 x_bounds = [0, tomosum.size]
2307 else:
2308 accept = False
2309 while not accept:
2310 mask, x_bounds = draw_mask_1d(tomosum, title='select x data range',
2311 legend='recon stack sum yz')
2312 while len(x_bounds) != 1:
2313 print('Please select exactly one continuous range')
2314 mask, x_bounds = draw_mask_1d(tomosum, title='select x data range',
2315 legend='recon stack sum yz')
2316 x_bounds = list(x_bounds[0])
2317 quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz')
2318 print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+
2319 'exclusive)')
2320 accept = input_yesno('Accept these bounds (y/n)?', 'y')
2321 if False and self.test_mode:
2322 np.savetxt(f'{self.output_folder}/recon_stack_sum_yz.txt',
2323 tomosum[x_bounds[0]:x_bounds[1]], fmt='%.6e')
2324 if self.save_plots_only: 2380 if self.save_plots_only:
2325 clearPlot('recon stack sum yz') 2381 clearPlot('recon stack sum yz')
2382 logging.info(f'x_bounds = {x_bounds}')
2326 2383
2327 # Selecting y bounds (in xz-plane) 2384 # Selecting y bounds (in xz-plane)
2328 tomosum = 0 2385 tomosum = 0
2329 #RV FIX := 2386 #RV FIX :=
2330 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in 2387 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in
2341 quickPlot((range(tomosum.size), tomosum), 2398 quickPlot((range(tomosum.size), tomosum),
2342 ([y_bounds[0], y_bounds[0]], [tomosum_min, tomosum_max], 'r-'), 2399 ([y_bounds[0], y_bounds[0]], [tomosum_min, tomosum_max], 'r-'),
2343 ([y_bounds[1]-1, y_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), 2400 ([y_bounds[1]-1, y_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'),
2344 title=f'recon stack sum xz', path='combine_pngs', save_fig=True, save_only=True) 2401 title=f'recon stack sum xz', path='combine_pngs', save_fig=True, save_only=True)
2345 else: 2402 else:
2403 y_bounds = None
2404 change_y_bounds = 'y'
2346 if combine_stacks and 'y_bounds' in combine_stacks: 2405 if combine_stacks and 'y_bounds' in combine_stacks:
2347 y_bounds = combine_stacks['y_bounds'] 2406 y_bounds = combine_stacks['y_bounds']
2348 if not is_index_range(y_bounds, 0, tomosum.size): 2407 if is_index_range(y_bounds, 0, tomosum.size):
2408 if not self.test_mode:
2409 quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz')
2410 print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+
2411 'exclusive)')
2412 change_y_bounds = 'n'
2413 else:
2349 illegal_value(y_bounds, 'combine_stacks:y_bounds', 'config file') 2414 illegal_value(y_bounds, 'combine_stacks:y_bounds', 'config file')
2350 elif not self.test_mode: 2415 y_bounds = None
2416 if self.test_mode:
2417 if y_bounds is None:
2418 y_bounds = [0, tomosum.size]
2419 else:
2420 if not input_yesno('\nDo you want to change the image y-bounds (y/n)?',
2421 change_y_bounds):
2422 if y_bounds is None:
2423 y_bounds = [0, tomosum.size]
2424 else:
2351 accept = False 2425 accept = False
2426 if y_bounds is None:
2427 index_ranges = None
2428 else:
2429 index_ranges = [y_bounds]
2352 while not accept: 2430 while not accept:
2353 mask, y_bounds = draw_mask_1d(tomosum, current_index_ranges=y_bounds, 2431 mask, y_bounds = draw_mask_1d(tomosum, current_index_ranges=index_ranges,
2354 title='select y data range', legend='recon stack sum xz') 2432 title='select x data range', legend='recon stack sum xz')
2355 while len(y_bounds) != 1: 2433 while len(y_bounds) != 1:
2356 print('Please select exactly one continuous range') 2434 print('Please select exactly one continuous range')
2357 mask, y_bounds = draw_mask_1d(tomosum, title='select y data range', 2435 mask, y_bounds = draw_mask_1d(tomosum, title='select x data range',
2358 legend='recon stack sum xz') 2436 legend='recon stack sum xz')
2359 y_bounds = list(y_bounds[0]) 2437 y_bounds = list(y_bounds[0])
2360 quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz') 2438 quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz')
2361 print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+ 2439 print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+
2362 'exclusive)') 2440 'exclusive)')
2363 accept = input_yesno('Accept these bounds (y/n)?', 'y') 2441 accept = input_yesno('Accept these bounds (y/n)?', 'y')
2364 if not accept:
2365 y_bounds = None
2366 else:
2367 if not input_yesno('\nDo you want to change the image y-bounds (y/n)?', 'n'):
2368 y_bounds = [0, tomosum.size]
2369 else:
2370 accept = False
2371 while not accept:
2372 mask, y_bounds = draw_mask_1d(tomosum, title='select y data range',
2373 legend='recon stack sum xz')
2374 while len(y_bounds) != 1:
2375 print('Please select exactly one continuous range')
2376 mask, y_bounds = draw_mask_1d(tomosum, title='select y data range',
2377 legend='recon stack sum xz')
2378 y_bounds = list(y_bounds[0])
2379 quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz')
2380 print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+
2381 'exclusive)')
2382 accept = input_yesno('Accept these bounds (y/n)?', 'y')
2383 if False and self.test_mode:
2384 np.savetxt(f'{self.output_folder}/recon_stack_sum_xz.txt',
2385 tomosum[y_bounds[0]:y_bounds[1]], fmt='%.6e')
2386 if self.save_plots_only: 2442 if self.save_plots_only:
2387 clearPlot('recon stack sum xz') 2443 clearPlot('recon stack sum xz')
2444 logging.info(f'y_bounds = {y_bounds}')
2388 2445
2389 # Combine reconstructed tomography stacks 2446 # Combine reconstructed tomography stacks
2390 logging.info(f'Combining reconstructed stacks ...') 2447 logging.info(f'Combining reconstructed stacks ...')
2391 t0 = time() 2448 t0 = time()
2392 num_tomo_stacks = len(stacks) 2449 num_tomo_stacks = len(stacks)
2406 [self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:, 2463 [self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:,
2407 x_bounds[0]:x_bounds[1],y_bounds[0]:y_bounds[1]]]) 2464 x_bounds[0]:x_bounds[1],y_bounds[0]:y_bounds[1]]])
2408 logging.info(f'... done in {time()-t0:.2f} seconds!') 2465 logging.info(f'... done in {time()-t0:.2f} seconds!')
2409 combined_stacks = [stack['index'] for stack in stacks] 2466 combined_stacks = [stack['index'] for stack in stacks]
2410 2467
2411 # Wrap up if in test_mode 2468 # Selecting z bounds (in xy-plane)
2412 tomosum = np.sum(tomo_recon_combined, axis=(1,2)) 2469 tomosum = np.sum(tomo_recon_combined, axis=(1,2))
2413 if self.test_mode:
2414 zoom_perc = self.config['preprocess'].get('zoom_perc', 100)
2415 filename = 'recon combined sum xy'
2416 if zoom_perc is None or zoom_perc == 100:
2417 filename += ' fullres.dat'
2418 else:
2419 filename += f' {zoom_perc}p.dat'
2420 quickPlot(tomosum, title='recon combined sum xy',
2421 path=self.output_folder, save_fig=self.save_plots,
2422 save_only=self.save_plots_only)
2423 if False:
2424 np.savetxt(f'{self.output_folder}/recon_combined_sum_xy.txt',
2425 tomosum, fmt='%.6e')
2426 np.savetxt(f'{self.output_folder}/recon_combined.txt',
2427 tomo_recon_combined[int(tomosum.size/2),:,:], fmt='%.6e')
2428
2429 # Update config and save to file
2430 if combine_stacks:
2431 combine_stacks['x_bounds'] = x_bounds
2432 combine_stacks['y_bounds'] = y_bounds
2433 combine_stacks['stacks'] = combined_stacks
2434 else:
2435 self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds,
2436 'stacks' : combined_stacks}
2437 self.cf.saveFile(self.config_out)
2438 return
2439
2440 # Selecting z bounds (in xy-plane)
2441 if self.galaxy_flag: 2470 if self.galaxy_flag:
2442 if z_bounds[0] == -1: 2471 if z_bounds[0] == -1:
2443 z_bounds[0] = 0 2472 z_bounds[0] = 0
2444 if z_bounds[1] == -1: 2473 if z_bounds[1] == -1:
2445 z_bounds[1] = tomosum.size 2474 z_bounds[1] = tomosum.size
2450 quickPlot((range(tomosum.size), tomosum), 2479 quickPlot((range(tomosum.size), tomosum),
2451 ([z_bounds[0], z_bounds[0]], [tomosum_min, tomosum_max], 'r-'), 2480 ([z_bounds[0], z_bounds[0]], [tomosum_min, tomosum_max], 'r-'),
2452 ([z_bounds[1]-1, z_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), 2481 ([z_bounds[1]-1, z_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'),
2453 title=f'recon stack sum xy', path='combine_pngs', save_fig=True, save_only=True) 2482 title=f'recon stack sum xy', path='combine_pngs', save_fig=True, save_only=True)
2454 else: 2483 else:
2455 if not input_yesno('\nDo you want to change the image z-bounds (y/n)?', 'n'): 2484 z_bounds = None
2456 z_bounds = [0, tomosum.size] 2485 if combine_stacks and 'z_bounds' in combine_stacks:
2457 else: 2486 z_bounds = combine_stacks['z_bounds']
2458 accept = False 2487 if is_index_range(z_bounds, 0, tomosum.size):
2459 while not accept: 2488 if not self.test_mode:
2460 mask, z_bounds = draw_mask_1d(tomosum, title='select z data range', 2489 quickPlot(tomosum, vlines=z_bounds, title='recon stack sum xy')
2461 legend='recon stack sum xy') 2490 print(f'z_bounds = {z_bounds} (lower bound inclusive, upper bound '+
2462 while len(z_bounds) != 1: 2491 'exclusive)')
2463 print('Please select exactly one continuous range') 2492 else:
2464 mask, z_bounds = draw_mask_1d(tomosum, title='select z data range', 2493 illegal_value(z_bounds, 'combine_stacks:z_bounds', 'config file')
2465 legend='recon stack sum xy') 2494 z_bounds = None
2466 z_bounds = list(z_bounds[0]) 2495 if self.test_mode:
2467 quickPlot(tomosum, vlines=z_bounds, title='recon stack sum xy') 2496 if z_bounds is None:
2468 print(f'z_bounds = {z_bounds} (lower bound inclusive, upper bound exclusive)') 2497 z_bounds = [0, tomosum.size]
2469 accept = input_yesno('Accept these bounds (y/n)?', 'y') 2498 else:
2499 if not input_yesno('\nDo you want to change the image z-bounds (y/n)?', 'n'):
2500 if z_bounds is None:
2501 z_bounds = [0, tomosum.size]
2502 else:
2503 accept = False
2504 if z_bounds is None:
2505 index_ranges = None
2506 else:
2507 index_ranges = [z_bounds]
2508 while not accept:
2509 mask, z_bounds = draw_mask_1d(tomosum, current_index_ranges=index_ranges,
2510 title='select x data range', legend='recon stack sum xy')
2511 while len(z_bounds) != 1:
2512 print('Please select exactly one continuous range')
2513 mask, z_bounds = draw_mask_1d(tomosum, title='select x data range',
2514 legend='recon stack sum xy')
2515 z_bounds = list(z_bounds[0])
2516 quickPlot(tomosum, vlines=z_bounds, title='recon stack sum xy')
2517 print(f'z_bounds = {z_bounds} (lower bound inclusive, upper bound '+
2518 'exclusive)')
2519 accept = input_yesno('Accept these bounds (y/n)?', 'y')
2470 if self.save_plots_only: 2520 if self.save_plots_only:
2471 clearPlot('recon combined sum xy') 2521 clearPlot('recon stack sum xy')
2472 if z_bounds[0] != 0 or z_bounds[1] != tomosum.size: 2522 logging.info(f'z_bounds = {z_bounds}')
2473 tomo_recon_combined = tomo_recon_combined[z_bounds[0]:z_bounds[1],:,:]
2474 2523
2475 # Plot center slices 2524 # Plot center slices
2476 if self.galaxy_flag: 2525 if self.galaxy_flag:
2477 path = 'combine_pngs' 2526 path = 'combine_pngs'
2478 save_fig = True 2527 save_fig = True
2489 path=path, save_fig=save_fig, save_only=save_only) 2538 path=path, save_fig=save_fig, save_only=save_only)
2490 quickImshow(tomo_recon_combined[:,:,int(tomo_recon_combined.shape[2]/2)], 2539 quickImshow(tomo_recon_combined[:,:,int(tomo_recon_combined.shape[2]/2)],
2491 title=f'recon combined zslice{int(tomo_recon_combined.shape[2]/2)}', 2540 title=f'recon combined zslice{int(tomo_recon_combined.shape[2]/2)}',
2492 path=path, save_fig=save_fig, save_only=save_only) 2541 path=path, save_fig=save_fig, save_only=save_only)
2493 2542
2494 # Save combined reconstructed tomography stack to file 2543 # Save combined reconstructed tomography stack or test mode data to file
2495 if self.galaxy_flag: 2544 if self.galaxy_flag:
2496 t0 = time() 2545 t0 = time()
2497 output_name = galaxy_param['output_name'] 2546 output_name = galaxy_param['output_name']
2498 logging.info(f'Saving combined reconstructed tomography stack to {output_name} ...') 2547 logging.info(f'Saving combined reconstructed tomography stack to {output_name} ...')
2499 np.save(output_name, tomo_recon_combined) 2548 np.save(output_name, tomo_recon_combined)
2500 logging.info(f'... done in {time()-t0:.2f} seconds!') 2549 logging.info(f'... done in {time()-t0:.2f} seconds!')
2550 elif self.test_mode:
2551 zoom_perc = self.config['preprocess'].get('zoom_perc', 100)
2552 filename = 'recon combined sum xy'
2553 if zoom_perc is None or zoom_perc == 100:
2554 filename += ' fullres.dat'
2555 else:
2556 filename += f' {zoom_perc}p.dat'
2557 quickPlot(tomosum, title='recon combined sum xy',
2558 path=self.output_folder, save_fig=self.save_plots,
2559 save_only=self.save_plots_only)
2560 np.savetxt(f'{self.output_folder}/recon_combined.txt',
2561 tomo_recon_combined[int(tomosum.size/2),:,:], fmt='%.6e')
2501 else: 2562 else:
2502 base_name = 'recon combined' 2563 base_name = 'recon combined'
2503 for stack in stacks: 2564 for stack in stacks:
2504 base_name += f' {stack["index"]}' 2565 base_name += f' {stack["index"]}'
2505 self._saveTomo(base_name, tomo_recon_combined) 2566 self._saveTomo(base_name, tomo_recon_combined)