Mercurial > repos > rv43 > tomo
changeset 1:e4778148df6b draft
"planemo upload for repository https://github.com/rolfverberg/galaxytools commit 7bc44bd6a5a7d503fc4d6f6fb67b3a04a631430b"
author | rv43 |
---|---|
date | Thu, 31 Mar 2022 18:24:16 +0000 |
parents | cb1b0d757704 |
children | b8977c98800b |
files | msnc_tools.py tomo.py tomo_setup.xml |
diffstat | 3 files changed, 346 insertions(+), 244 deletions(-) [+] |
line wrap: on
line diff
--- a/msnc_tools.py Tue Mar 29 16:10:16 2022 +0000 +++ b/msnc_tools.py Thu Mar 31 18:24:16 2022 +0000 @@ -36,7 +36,7 @@ """ if not isinstance(v, int): return False - if (v_min != None and v < v_min) or (v_max != None and v > v_max): + if (v_min is not None and v < v_min) or (v_max is not None and v > v_max): return False return True @@ -45,7 +45,7 @@ """ if not isinstance(v, (int,float)): return False - if (v_min != None and v < v_min) or (v_max != None and v > v_max): + if (v_min is not None and v < v_min) or (v_max is not None and v > v_max): return False return True @@ -54,17 +54,17 @@ """ if not isinstance(v, int): return False - if v < v_min or (v_max != None and v >= v_max): + if v < v_min or (v_max is not None and v >= v_max): return False return True def is_index_range(v, v_min=0, v_max=None): - """Value is an array index range in range v_min <= v[0] <= v[1] < v_max. + """Value is an array index range in range v_min <= v[0] <= v[1] <= v_max. """ if not (isinstance(v, list) and len(v) == 2 and isinstance(v[0], int) and isinstance(v[1], int)): return False - if not 0 <= v[0] < v[1] or (v_max != None and v[1] >= v_max): + if not 0 <= v[0] <= v[1] or (v_max is not None and v[1] > v_max): return False return True @@ -83,7 +83,7 @@ def get_trailing_int(string): indexRegex = re.compile(r'\d+$') mo = indexRegex.search(string) - if mo == None: + if mo is None: return None else: return int(mo.group()) @@ -108,7 +108,7 @@ return -1, 0, [] first_index = indexRegex.search(files[0]).group() last_index = indexRegex.search(files[-1]).group() - if first_index == None or last_index == None: + if first_index is None or last_index is None: logging.error('Unable to find correctly indexed'+name+'images') return -1, 0, [] first_index = int(first_index) @@ -151,18 +151,19 @@ use_input = pyip.inputYesNo('\nCurrent'+name+'first index/offset = '+ f'{first_index}/{offset}, use these values ([y]/n)? ', blank=True) - if use_input != 'no': - use_input = pyip.inputYesNo('Current number of'+name+'images = '+ - f'{num_imgs}, use this value ([y]/n)? ', - blank=True) - if use_input == 'yes': + if num_required is None: + if use_input != 'no': + use_input = pyip.inputYesNo('Current number of'+name+'images = '+ + f'{num_imgs}, use this value ([y]/n)? ', + blank=True) + if use_input != 'no': return first_index, offset, num_imgs # Check range against requirements if num_imgs < 1: logging.warning('No available'+name+'images') return -1, -1, 0 - if num_required == None: + if num_required is None: if num_imgs == 1: return first_index, 0, 1 else: @@ -175,7 +176,8 @@ return -1, -1, 0 # Select index range - if num_required == None: + print('\nThe number of available'+name+f'images is {num_imgs}') + if num_required is None: last_index = first_index+num_imgs use_all = f'Use all ([{first_index}, {last_index}])' pick_offset = 'Pick a first index offset and a number of images' @@ -229,7 +231,7 @@ img_y_bounds = [0, img_read.shape[1]] else: if (not isinstance(img_y_bounds, list) or len(img_y_bounds) != 2 or - not (0 <= img_y_bounds[0] < img_y_bounds[1] <= img_read.shape[0])): + not (0 <= img_y_bounds[0] < img_y_bounds[1] <= img_read.shape[1])): logging.error(f'inconsistent column dimension in {f}') return None return img_read[img_x_bounds[0]:img_x_bounds[1],img_y_bounds[0]:img_y_bounds[1]] @@ -296,7 +298,7 @@ def quickImshow(a, title=None, path=None, name=None, save_fig=False, save_only=False, clear=True, **kwargs): - if title != None and not isinstance(title, str): + if title is not None and not isinstance(title, str): illegal_value('title', title, 'quickImshow') return if path is not None and not isinstance(path, str): @@ -343,7 +345,7 @@ def quickPlot(*args, title=None, path=None, name=None, save_fig=False, save_only=False, clear=True, **kwargs): - if title != None and not isinstance(title, str): + if title is not None and not isinstance(title, str): illegal_value('title', title, 'quickPlot') return if path is not None and not isinstance(path, str): @@ -402,13 +404,16 @@ if not isinstance(a, np.ndarray) or a.ndim != 1: logging.error('Illegal array type or dimension in selectArrayBounds') return None - if num_x_min == None: + x_low_save = x_low + x_upp_save = x_upp + num_x_min_save = num_x_min + if num_x_min is None: num_x_min = 1 else: if num_x_min < 2 or num_x_min > a.size: logging.warning('Illegal input for num_x_min in selectArrayBounds, input ignored') num_x_min = 1 - if x_low == None: + if x_low is None: x_min = 0 x_max = a.size x_low_max = a.size-num_x_min @@ -429,7 +434,7 @@ if not is_int(x_low, 0, a.size-num_x_min): illegal_value('x_low', x_low, 'selectArrayBounds') return None - if x_upp == None: + if x_upp is None: x_min = x_low+num_x_min x_max = a.size x_upp_min = x_min @@ -456,7 +461,7 @@ quickPlot((range(a.size), a), ([bounds[0], bounds[0]], [a.min(), a.max()], 'r-'), ([bounds[1], bounds[1]], [a.min(), a.max()], 'r-'), title=title) if pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) == 'no': - bounds = selectArrayBounds(a, title=title) + bounds = selectArrayBounds(a, x_low_save, x_upp_save, num_x_min_save, title=title) return bounds def selectImageBounds(a, axis, low=None, upp=None, num_min=None, @@ -469,13 +474,16 @@ if axis < 0 or axis >= a.ndim: illegal_value('axis', axis, 'selectImageBounds') return None - if num_min == None: + low_save = low + upp_save = upp + num_min_save = num_min + if num_min is None: num_min = 1 else: if num_min < 2 or num_min > a.shape[axis]: logging.warning('Illegal input for num_min in selectImageBounds, input ignored') num_min = 1 - if low == None: + if low is None: min_ = 0 max_ = a.shape[axis] low_max = a.shape[axis]-num_min @@ -501,7 +509,7 @@ if not is_int(low, 0, a.shape[axis]-num_min): illegal_value('low', low, 'selectImageBounds') return None - if upp == None: + if upp is None: min_ = low+num_min max_ = a.shape[axis] upp_min = min_ @@ -538,7 +546,8 @@ print(f'lower bound = {low} (inclusive)\nupper bound = {upp} (exclusive)') quickImshow(a_tmp, title=title) if pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) == 'no': - bounds = selectImageBounds(a, title=title) + bounds = selectImageBounds(a, axis, low=low_save, upp=upp_save, num_min=num_min_save, + title=title) return bounds def fitStep(x=None, y=None, model='step', form='arctan'): @@ -690,14 +699,14 @@ # break # else: # # Insert new key/value pair -# if search_string != None: +# if search_string is not None: # if isinstance(search_string, str): # search_string = [search_string] # elif not isinstance(search_string, (tuple, list)): # illegal_value('search_string', search_string, 'Config.update') # search_string = None # update_flag = False -# if search_string != None: +# if search_string is not None: # indices = [[index for index,line in enumerate(lines) if item in line] # for item in search_string] # for i,index in enumerate(indices):
--- a/tomo.py Tue Mar 29 16:10:16 2022 +0000 +++ b/tomo.py Thu Mar 31 18:24:16 2022 +0000 @@ -18,6 +18,7 @@ import pyinputplus as pyip except: pass +import argparse import numpy as np import numexpr as ne import multiprocessing as mp @@ -31,18 +32,18 @@ class set_numexpr_threads: - def __init__(self, nthreads): + def __init__(self, num_core): cpu_count = mp.cpu_count() - if nthreads is None or nthreads > cpu_count: - self.n = cpu_count + if num_core is None or num_core < 1 or num_core > cpu_count: + self.num_core = cpu_count else: - self.n = nthreads + self.num_core = num_core def __enter__(self): - self.oldn = ne.set_num_threads(self.n) + self.num_core_org = ne.set_num_threads(self.num_core) def __exit__(self, exc_type, exc_value, traceback): - ne.set_num_threads(self.oldn) + ne.set_num_threads(self.num_core_org) class ConfigTomo(msnc.Config): """Class for processing a config file. @@ -210,6 +211,7 @@ is_valid = self._validate_txt() else: logging.error(f'Undefined or illegal config file extension: {self.suffix}') + is_valid = False # Find tomography bright field images file/folder if self.tdf_data_path: @@ -378,10 +380,11 @@ """ def __init__(self, config_file=None, config_dict=None, config_out=None, output_folder='.', - log_level='INFO', log_stream='tomo.log', galaxy_flag=False, test_mode=False): + log_level='INFO', log_stream='tomo.log', galaxy_flag=False, test_mode=False, + num_core=-1): """Initialize with optional config input file or dictionary """ - self.ncore = mp.cpu_count() + self.num_core = None self.config_out = config_out self.output_folder = output_folder self.galaxy_flag = galaxy_flag @@ -421,6 +424,12 @@ raise ValueError(f'Invalid galaxy_flag input {galaxy_flag} {type(galaxy_flag)}') if not isinstance(test_mode, bool): raise ValueError(f'Invalid test_mode input {test_mode} {type(test_mode)}') + if not isinstance(num_core, int) or num_core < -1 or num_core == 0: + raise ValueError(f'Invalid num_core input {num_core} {type(num_core)}') + if num_core == -1: + self.num_core = mp.cpu_count() + else: + self.num_core = num_core # Set log configuration logging_format = '%(asctime)s : %(levelname)s - %(module)s : %(funcName)s - %(message)s' @@ -428,9 +437,11 @@ self.save_plots_only = True if isinstance(log_stream, str): logging.basicConfig(filename=f'{log_stream}', filemode='w', - format=logging_format, level=logging.WARNING, force=True) + format=logging_format, level=logging.INFO, force=True) + #format=logging_format, level=logging.WARNING, force=True) elif isinstance(log_stream, io.TextIOWrapper): - logging.basicConfig(filemode='w', format=logging_format, level=logging.WARNING, + #logging.basicConfig(filemode='w', format=logging_format, level=logging.WARNING, + logging.basicConfig(filemode='w', format=logging_format, level=logging.INFO, stream=log_stream, force=True) else: raise ValueError(f'Invalid log_stream: {log_stream}') @@ -463,16 +474,6 @@ not os.path.isabs(self.config_out)): self.config_out = f'{self.output_folder}/{self.config_out}' - logging.info(f'ncore = {self.ncore}') - logging.debug(f'config_file = {config_file}') - logging.debug(f'config_dict = {config_dict}') - logging.debug(f'config_out = {self.config_out}') - logging.debug(f'output_folder = {self.output_folder}') - logging.debug(f'log_stream = {log_stream}') - logging.debug(f'log_level = {log_level}') - logging.debug(f'galaxy_flag = {self.galaxy_flag}') - logging.debug(f'test_mode = {self.test_mode}') - # Create config object and load config file self.cf = ConfigTomo(config_file, config_dict) if not self.cf.load_flag: @@ -480,7 +481,7 @@ return if self.galaxy_flag: - self.ncore = 1 #RV can I set this? mp.cpu_count() + self.num_core = 1 #RV can I set this? mp.cpu_count() assert(self.output_folder == '.') assert(self.test_mode is False) self.save_plots = True @@ -527,6 +528,15 @@ self.tomo_stacks = [np.array([]) for _ in range(num_tomo_stacks)] self.tomo_recon_stacks = [np.array([]) for _ in range(num_tomo_stacks)] + logging.info(f'num_core = {self.num_core}') + logging.debug(f'config_file = {config_file}') + logging.debug(f'config_dict = {config_dict}') + logging.debug(f'config_out = {self.config_out}') + logging.debug(f'output_folder = {self.output_folder}') + logging.debug(f'log_stream = {log_stream}') + logging.debug(f'log_level = {log_level}') + logging.debug(f'galaxy_flag = {self.galaxy_flag}') + logging.debug(f'test_mode = {self.test_mode}') logging.debug(f'save_plots = {self.save_plots}') logging.debug(f'save_plots_only = {self.save_plots_only}') @@ -582,8 +592,7 @@ num_imgs, 'bright field') if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable bright field images') - if bright_field['data_path']: - self.is_valid = False + self.is_valid = False bright_field['img_start'] = img_start bright_field['img_offset'] = img_offset bright_field['num'] = num_imgs @@ -612,7 +621,6 @@ logging.debug(f'Tomography stack {index} image start index: {stack["img_start"]}') logging.debug(f'Tomography stack {index} image offset: {stack["img_offset"]}') logging.debug(f'Number of tomography images for stack {index}: {stack["num"]}') - available_stacks[i] = True # Safe updated config to file if self.is_valid: @@ -689,6 +697,13 @@ img_x_bounds = [None, None] else: img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) + if img_x_bounds[0] is not None and img_x_bounds[1] is not None: + if img_x_bounds[0] < 0: + msnc.illegal_value('img_x_bounds[0]', img_x_bounds[0], 'config file') + img_x_bounds[0] = 0 + if not msnc.is_index_range(img_x_bounds, 0, self.tbf.shape[0]): + msnc.illegal_value('img_x_bounds[1]', img_x_bounds[1], 'config file') + img_x_bounds[1] = self.tbf.shape[0] if self.test_mode: # Update config and save to file if preprocess is None: @@ -763,17 +778,6 @@ x_sum = np.sum(tomo_stack[0,:,:], 1) use_bounds = 'no' if img_x_bounds[0] is not None and img_x_bounds[1] is not None: - if img_x_bounds[0] < 0: - msnc.illegal_value('img_x_bounds[0]', img_x_bounds[0], 'config file') - img_x_bounds[0] = 0 - if not img_x_bounds[0] < img_x_bounds[1] <= x_sum.size: - msnc.illegal_value('img_x_bounds[1]', img_x_bounds[1], 'config file') - img_x_bounds[1] = x_sum.size - tomo_tmp = tomo_stack[0,:,:] - tomo_tmp[img_x_bounds[0],:] = tomo_stack[0,:,:].max() - tomo_tmp[img_x_bounds[1]-1,:] = tomo_stack[0,:,:].max() - title = f'tomography image at theta={self.config["theta_range"]["start"]}' - msnc.quickImshow(tomo_stack[0,:,:], title=title) msnc.quickPlot((range(x_sum.size), x_sum), ([img_x_bounds[0], img_x_bounds[0]], [x_sum.min(), x_sum.max()], 'r-'), ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum.min(), x_sum.max()], 'r-'), @@ -787,9 +791,6 @@ f'tomography image at theta={self.config["theta_range"]["start"]}') if num_x_min is not None and img_x_bounds[1]-img_x_bounds[0]+1 < num_x_min: logging.warning('Image bounds and pixel size prevent seamless stacking') - tomo_tmp = tomo_stack[0,:,:] - tomo_tmp[img_x_bounds[0],:] = tomo_stack[0,:,:].max() - tomo_tmp[img_x_bounds[1]-1,:] = tomo_stack[0,:,:].max() title = f'tomography image at theta={self.config["theta_range"]["start"]}' msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, save_fig=self.save_plots, save_only=True) @@ -801,12 +802,6 @@ x_sum = np.sum(self.tbf, 1) use_bounds = 'no' if img_x_bounds[0] is not None and img_x_bounds[1] is not None: - if img_x_bounds[0] < 0: - msnc.illegal_value('img_x_bounds[0]', img_x_bounds[0], 'config file') - img_x_bounds[0] = 0 - if not img_x_bounds[0] < img_x_bounds[1] <= x_sum.size: - msnc.illegal_value('img_x_bounds[1]', img_x_bounds[1], 'config file') - img_x_bounds[1] = x_sum.size msnc.quickPlot((range(x_sum.size), x_sum), ([img_x_bounds[0], img_x_bounds[0]], [x_sum.min(), x_sum.max()], 'r-'), ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum.min(), x_sum.max()], 'r-'), @@ -973,13 +968,13 @@ if preprocess is None: img_x_bounds = [0, self.tbf.shape[0]] img_y_bounds = [0, self.tbf.shape[1]] - zoom_perc = preprocess['zoom_perc'] - num_theta_skip = preprocess['num_theta_skip'] + zoom_perc = 100 + num_theta_skip = 0 else: img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) img_y_bounds = preprocess.get('img_y_bounds', [0, self.tbf.shape[1]]) - zoom_perc = 100 - num_theta_skip = 0 + zoom_perc = preprocess.get('zoom_perc', 100) + num_theta_skip = preprocess.get('num_theta_skip', 0) if self.tdf.size: tdf = self.tdf[img_x_bounds[0]:img_x_bounds[1],img_y_bounds[0]:img_y_bounds[1]] @@ -995,32 +990,33 @@ continue # Load a stack of tomography images + index = stack['index'] t0 = time() tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], stack['img_offset'], self.config['theta_range']['num'], num_theta_skip, img_x_bounds, img_y_bounds) tomo_stack = tomo_stack.astype('float64') - logging.debug(f'loading took {time()-t0:.2f} seconds!') + logging.debug(f'loading stack {index} took {time()-t0:.2f} seconds!') # Subtract dark field if self.tdf.size: t0 = time() - with set_numexpr_threads(self.ncore): + with set_numexpr_threads(self.num_core): ne.evaluate('tomo_stack-tdf', out=tomo_stack) logging.debug(f'subtracting dark field took {time()-t0:.2f} seconds!') # Normalize t0 = time() - with set_numexpr_threads(self.ncore): + with set_numexpr_threads(self.num_core): ne.evaluate('tomo_stack/tbf', out=tomo_stack, truediv=True) logging.debug(f'normalizing took {time()-t0:.2f} seconds!') # Remove non-positive values and linearize data t0 = time() cutoff = 1.e-6 - with set_numexpr_threads(self.ncore): + with set_numexpr_threads(self.num_core): ne.evaluate('where(tomo_stack<cutoff, cutoff, tomo_stack)', out=tomo_stack) - with set_numexpr_threads(self.ncore): + with set_numexpr_threads(self.num_core): ne.evaluate('-log(tomo_stack)', out=tomo_stack) logging.debug('removing non-positive values and linearizing data took '+ f'{time()-t0:.2f} seconds!') @@ -1033,7 +1029,6 @@ # Downsize tomography stack to smaller size tomo_stack = tomo_stack.astype('float32') if not self.galaxy_flag: - index = stack['index'] title = f'red stack fullres {index}' if not self.test_mode: msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, @@ -1053,7 +1048,7 @@ if not self.test_mode: msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) - + # Convert tomography stack from theta,row,column to row,theta,column tomo_stack = np.swapaxes(tomo_stack, 0, 1) @@ -1062,7 +1057,7 @@ if not self.test_mode: self._saveTomo('red stack', tomo_stack, index) else: - np.savetxt(self.output_folder+f'red_stack_{index}.txt', + np.savetxt(f'{self.output_folder}/red_stack_{index}.txt', tomo_stack[0,:,:], fmt='%.6e') # Combine stacks @@ -1072,6 +1067,16 @@ # Update config and save to file stack['preprocessed'] = True + stack.pop('reconstructed', 'reconstructed not found') + find_center = self.config.get('find_center') + if find_center: + if self.test_mode: + find_center.pop('completed', 'completed not found') + find_center.pop('lower_center_offset', 'lower_center_offset not found') + find_center.pop('upper_center_offset', 'upper_center_offset not found') + else: + if find_center.get('center_stack_index', -1) == index: + self.config.pop('find_center') self.cf.saveFile(self.config_out) if self.tdf.size: @@ -1095,7 +1100,7 @@ else: logging.debug(f'sinogram range = [{dist_from_edge}, {two_offset-dist_from_edge}]') sinogram = tomo_plane_T[dist_from_edge:two_offset-dist_from_edge,:] - if plot_sinogram: + if plot_sinogram and not self.test_mode: msnc.quickImshow(sinogram.T, f'sinogram center offset{center_offset:.2f}', path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only, aspect='auto') @@ -1141,13 +1146,18 @@ # try automatic center finding routines for initial value tomo_center = tomopy.find_center_vo(sinogram) center_offset_vo = tomo_center-center - print(f'Center at row {row} using Nghia Vo’s method = {center_offset_vo:.2f}') + if not self.test_mode: + print(f'Center at row {row} using Nghia Vo’s method = {center_offset_vo:.2f}') recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, eff_pixel_size, cross_sectional_dim, False) - base_name=f'edges row{row} center_offset_vo{center_offset_vo:.2f}' - self._plotEdgesOnePlane(recon_plane, base_name) - if pyip.inputYesNo('Try finding center using phase correlation (y/[n])? ', - blank=True) == 'yes': + if not self.test_mode: + base_name=f'edges row{row} center_offset_vo{center_offset_vo:.2f}' + self._plotEdgesOnePlane(recon_plane, base_name) + use_phase_corr = 'no' + if not self.test_mode: + use_phase_corr = pyip.inputYesNo('Try finding center using phase correlation '+ + '(y/[n])? ', blank=True) + if use_phase_corr == 'yes': tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1, rotc_guess=tomo_center) error = 1. @@ -1162,15 +1172,21 @@ eff_pixel_size, cross_sectional_dim, False) base_name=f'edges row{row} center_offset{center_offset:.2f}' self._plotEdgesOnePlane(recon_plane, base_name) - if pyip.inputYesNo('Accept a center location ([y]) or continue search (n)? ', - blank=True) != 'no': + accept_center = 'yes' + if not self.test_mode: + accept_center = pyip.inputYesNo('Accept a center location ([y]) or continue '+ + 'search (n)? ', blank=True) + if accept_center != 'no': del sinogram_T del recon_plane - center_offset = pyip.inputNum( - f' Enter chosen center offset [{-int(center)}, {int(center)}] '+ - f'([{center_offset_vo}])): ', blank=True) - if center_offset == '': + if self.test_mode: center_offset = center_offset_vo + else: + center_offset = pyip.inputNum( + f' Enter chosen center offset [{-int(center)}, {int(center)}] '+ + f'([{center_offset_vo}])): ', blank=True) + if center_offset == '': + center_offset = center_offset_vo return float(center_offset) while True: @@ -1202,7 +1218,7 @@ return float(center_offset) def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, - center_offsets=[], sigma=0.1, rwidth=30, ncore=1, algorithm='gridrec', + center_offsets=[], sigma=0.1, rwidth=30, num_core=1, algorithm='gridrec', run_secondary_sirt=False, secondary_iter=100): """reconstruct a single tomography stack. """ @@ -1217,7 +1233,7 @@ if row_bounds is None: row_bounds = [0, tomo_stack.shape[0]] else: - if not (0 <= row_bounds[0] <= row_bounds[1] <= tomo_stack.shape[0]): + if not msnc.is_index_range(row_bounds, 0, tomo_stack.shape[0]): raise ValueError('Illegal row bounds in reconstructOneTomoStack') if thetas.size != tomo_stack.shape[1]: raise ValueError('theta dimension mismatch in reconstructOneTomoStack') @@ -1232,12 +1248,12 @@ centers = center_offsets centers += tomo_stack.shape[2]/2 if True: - tomo_stack = tomopy.prep.stripe.remove_stripe_fw(tomo_stack[row_bounds[0]:row_bounds[1]], - sigma=sigma, ncore=ncore) + tomo_stack = tomopy.prep.stripe.remove_stripe_fw( + tomo_stack[row_bounds[0]:row_bounds[1]], sigma=sigma, ncore=num_core) else: tomo_stack = tomo_stack[row_bounds[0]:row_bounds[1]] tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, sinogram_order=True, - algorithm=algorithm, ncore=ncore) + algorithm=algorithm, ncore=num_core) if run_secondary_sirt and secondary_iter > 0: #options = {'method':'SIRT_CUDA', 'proj_type':'cuda', 'num_iter':secondary_iter} #RV: doesn't work for me: "Error: CUDA error 803: system has unsupported display driver / @@ -1245,9 +1261,11 @@ #options = {'method':'SIRT', 'proj_type':'linear', 'MinConstraint': 0, 'num_iter':secondary_iter} #SIRT did not finish while running overnight #options = {'method':'SART', 'proj_type':'linear', 'num_iter':secondary_iter} - options = {'method':'SART', 'proj_type':'linear', 'MinConstraint': 0, 'num_iter':secondary_iter} - tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, init_recon=tomo_recon_stack, - options=options, sinogram_order=True, algorithm=tomopy.astra, ncore=ncore) + options = {'method':'SART', 'proj_type':'linear', 'MinConstraint': 0, + 'num_iter':secondary_iter} + tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, + init_recon=tomo_recon_stack, options=options, sinogram_order=True, + algorithm=tomopy.astra, ncore=num_core) if True: tomopy.misc.corr.remove_ring(tomo_recon_stack, rwidth=rwidth, out=tomo_recon_stack) return tomo_recon_stack @@ -1265,8 +1283,22 @@ logging.error('Unable to find suitable dark field images') if dark_field['data_path']: self.is_valid = False - dark_field['num'] = num_imgs - dark_field['img_start'] = img_start + img_start_old = dark_field.get('img_start') + num_imgs_old = dark_field.get('num') + if num_imgs_old is None: + dark_field['num'] = num_imgs + else: + if num_imgs_old > num_imgs: + logging.error('Inconsistent number of availaible dark field images') + if dark_field['data_path']: + self.is_valid = False + if img_start_old is None: + dark_field['img_start'] = img_start + else: + if img_start_old < img_start: + logging.error('Inconsistent image start index for dark field images') + if dark_field['data_path']: + self.is_valid = False logging.info(f'Number of dark field images = {dark_field["num"]}') logging.info(f'Dark field image start index = {dark_field["img_start"]}') @@ -1277,8 +1309,20 @@ if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable bright field images') self.is_valid = False - bright_field['num'] = num_imgs - bright_field['img_start'] = img_start + img_start_old = bright_field.get('img_start') + num_imgs_old = bright_field.get('num') + if num_imgs_old is None: + bright_field['num'] = num_imgs + else: + if num_imgs_old > num_imgs: + logging.error('Inconsistent number of availaible bright field images') + self.is_valid = False + if img_start_old is None: + bright_field['img_start'] = img_start + else: + if img_start_old < img_start: + logging.warning('Inconsistent image start index for bright field images') + self.is_valid = False logging.info(f'Number of bright field images = {bright_field["num"]}') logging.info(f'Bright field image start index = {bright_field["img_start"]}') @@ -1291,8 +1335,20 @@ if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable tomography images') self.is_valid = False - stack['num'] = num_imgs - stack['img_start'] = img_start + img_start_old = stack.get('img_start') + num_imgs_old = stack.get('num') + if num_imgs_old is None: + stack['num'] = num_imgs + else: + if num_imgs_old > num_imgs: + logging.error('Inconsistent number of availaible tomography images') + self.is_valid = False + if img_start_old is None: + stack['img_start'] = img_start + else: + if img_start_old < img_start: + logging.warning('Inconsistent image start index for tomography images') + self.is_valid = False logging.info(f'Number of tomography images for set {index} = {stack["num"]}') logging.info(f'Tomography set {index} image start index = {stack["img_start"]}') tomo_stack_files.append(tomo_files) @@ -1410,10 +1466,11 @@ logging.debug('Find centers for tomography stacks') stacks = self.config['stack_info']['stacks'] available_stacks = [stack['index'] for stack in stacks if stack.get('preprocessed', False)] - logging.debug('Avaliable stacks: {available_stacks}') + logging.debug('Available stacks: {available_stacks}') # Check for valid available center stack index find_center = self.config.get('find_center') + center_stack_index = None if find_center and 'center_stack_index' in find_center: center_stack_index = find_center['center_stack_index'] if (not isinstance(center_stack_index, int) or @@ -1421,15 +1478,13 @@ msnc.illegal_value('center_stack_index', center_stack_index, 'config file') else: if self.test_mode: - find_center['completed'] = True - self.cf.saveFile(self.config_out) - return - print('\nFound calibration center offset info for stack '+ - f'{center_stack_index}') - if pyip.inputYesNo('Do you want to use this again (y/n)? ') == 'yes': - find_center['completed'] = True - self.cf.saveFile(self.config_out) - return + assert(find_center.get('completed', False) == False) + else: + print('\nFound calibration center offset info for stack '+ + f'{center_stack_index}') + if (pyip.inputYesNo('Do you want to use this again (y/n)? ') == 'yes' and + find_center.get('completed', False) == True): + return # Load the required preprocessed stack # preprocessed stack order: row,theta,column @@ -1437,19 +1492,23 @@ assert(len(stacks) == num_tomo_stacks) assert(len(self.tomo_stacks) == num_tomo_stacks) if num_tomo_stacks == 1: - center_stack_index = stacks[0]['index'] + if not center_stack_index: + center_stack_index = stacks[0]['index'] + elif center_stack_index != stacks[0]['index']: + raise ValueError(f'Inconsistent center_stack_index {center_stack_index}') if not self.tomo_stacks[0].size: self.tomo_stacks[0], available = self._loadTomo('red stack', center_stack_index, required=True) center_stack = self.tomo_stacks[0] if not center_stack.size: - logging.error('Unable to load the required preprocessed tomography stack') - return + stacks[0]['preprocessed'] = False + raise OSError('Unable to load the required preprocessed tomography stack') assert(stacks[0].get('preprocessed', False) == True) else: while True: - center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' - f'rotation axis centers {available_stacks}: ') + if not center_stack_index: + center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' + f'rotation axis centers {available_stacks}: ') while center_stack_index not in available_stacks: center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' f'rotation axis centers {available_stacks}: ') @@ -1459,14 +1518,17 @@ 'red stack', center_stack_index, required=True) center_stack = self.tomo_stacks[tomo_stack_index] if not center_stack.size: + stacks[tomo_stack_index]['preprocessed'] = False logging.error(f'Unable to load the {center_stack_index}th '+ 'preprocessed tomography stack, pick another one') else: break - assert(stacks[tomo_stack_index].get('preprocessed', False) == True) + assert(stacks[tomo_stack_index].get('preprocessed', False) == True) if find_center is None: self.config['find_center'] = {'center_stack_index' : center_stack_index} - find_center = self.config['find_center'] + find_center = self.config['find_center'] + else: + find_center['center_stack_index'] = center_stack_index # Set thetas (in degrees) theta_range = self.config['theta_range'] @@ -1488,11 +1550,11 @@ if num_tomo_stacks == 1: n1 = 0 height = center_stack.shape[0]*eff_pixel_size - if pyip.inputYesNo('\nDo you want to reconstruct the full samply height '+ - f'({height:.3f} mm) (y/n)? ') == 'no': + if not self.test_mode and pyip.inputYesNo('\nDo you want to reconstruct the full '+ + f'sample height ({height:.3f} mm) (y/n)? ') == 'no': height = pyip.inputNum('\nEnter the desired reconstructed sample height '+ f'in mm [0, {height:.3f}]: ', min=0, max=height) - n1 = int(0.5*(center_stack.shape[0]-height/eff_pixel_size)) + n1 = int(0.5*(center_stack.shape[0]-height/eff_pixel_size)) else: n1 = int((1.+(tomo_ref_heights[0]+center_stack.shape[0]*eff_pixel_size- tomo_ref_heights[1])/eff_pixel_size)/2) @@ -1500,8 +1562,10 @@ logging.info(f'n1 = {n1}, n2 = {n2} (n2-n1) = {(n2-n1)*eff_pixel_size:.3f} mm') if not center_stack.size: RuntimeError('Center stack not loaded') - msnc.quickImshow(center_stack[:,0,:], title=f'center stack theta={theta_start}', - path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) + if not self.test_mode: + msnc.quickImshow(center_stack[:,0,:], title=f'center stack theta={theta_start}', + path=self.output_folder, save_fig=self.save_plots, + save_only=self.save_plots_only) # Get cross sectional diameter in mm cross_sectional_dim = center_stack.shape[2]*eff_pixel_size @@ -1511,23 +1575,29 @@ logging.info('Determine center offset at sample row boundaries') # Lower row center - use_row = False - use_center = False + use_row = 'no' + use_center = 'no' row = find_center.get('lower_row') if msnc.is_int(row, n1, n2-2): - msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') - use_row = pyip.inputYesNo('\nCurrent row index for lower center = ' - f'{row}, use this value (y/n)? ') - if self.save_plots_only: - msnc.clearFig(f'theta={theta_start}') - if use_row: - center_offset = find_center.get('lower_center_offset') - if msnc.is_num(center_offset): - use_center = pyip.inputYesNo('Current lower center offset = '+ - f'{center_offset}, use this value (y/n)? ') - if not use_center: - if not use_row: + if self.test_mode: + assert(row is not None) + use_row = 'yes' + else: msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') + use_row = pyip.inputYesNo('\nCurrent row index for lower center = ' + f'{row}, use this value ([y]/n)? ', blank=True) + if self.save_plots_only: + msnc.clearFig(f'theta={theta_start}') + if use_row != 'no': + center_offset = find_center.get('lower_center_offset') + if msnc.is_num(center_offset): + use_center = pyip.inputYesNo('Current lower center offset = '+ + f'{center_offset}, use this value ([y]/n)? ', blank=True) + if use_center == 'no': + if use_row == 'no': + if not self.test_mode: + msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', + aspect='auto') row = pyip.inputInt('\nEnter row index to find lower center '+ f'[[{n1}], {n2-2}]: ', min=n1, max=n2-2, blank=True) if row == '': @@ -1547,23 +1617,29 @@ lower_row = row # Upper row center - use_row = False - use_center = False + use_row = 'no' + use_center = 'no' row = find_center.get('upper_row') if msnc.is_int(row, lower_row+1, n2-1): - msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') - use_row = pyip.inputYesNo('\nCurrent row index for upper center = ' - f'{row}, use this value (y/n)? ') - if self.save_plots_only: - msnc.clearFig(f'theta={theta_start}') - if use_row: - center_offset = find_center.get('upper_center_offset') - if msnc.is_num(center_offset): - use_center = pyip.inputYesNo('Current upper center offset = '+ - f'{center_offset}, use this value (y/n)? ') - if not use_center: - if not use_row: + if self.test_mode: + assert(row is not None) + use_row = 'yes' + else: msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') + use_row = pyip.inputYesNo('\nCurrent row index for upper center = ' + f'{row}, use this value ([y]/n)? ', blank=True) + if self.save_plots_only: + msnc.clearFig(f'theta={theta_start}') + if use_row != 'no': + center_offset = find_center.get('upper_center_offset') + if msnc.is_num(center_offset): + use_center = pyip.inputYesNo('Current upper center offset = '+ + f'{center_offset}, use this value ([y]/n)? ', blank=True) + if use_center == 'no': + if use_row == 'no': + if not self.test_mode: + msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', + aspect='auto') row = pyip.inputInt('\nEnter row index to find upper center '+ f'[{lower_row+1}, [{n2-1}]]: ', min=lower_row+1, max=n2-1, blank=True) if row == '': @@ -1660,6 +1736,10 @@ """Reconstruct tomography stacks. """ logging.debug('Reconstruct tomography stacks') + stacks = self.config['stack_info']['stacks'] + assert(len(self.tomo_stacks) == self.config['stack_info']['num']) + assert(len(self.tomo_stacks) == len(stacks)) + assert(len(self.tomo_recon_stacks) == len(stacks)) # Get rotation axis rows and centers find_center = self.config['find_center'] @@ -1711,6 +1791,7 @@ if self.tomo_recon_stacks[i].size or available: if self.tomo_stacks[i].size: self.tomo_stacks[i] = np.array([]) + assert(stack.get('preprocessed', False) == True) assert(stack.get('reconstructed', False) == True) continue else: @@ -1720,6 +1801,7 @@ required=True) if not self.tomo_stacks[i].size: logging.error(f'Unable to load tomography stack {index} for reconstruction') + stack[i]['preprocessed'] = False load_error = True continue assert(0 <= lower_row < upper_row < self.tomo_stacks[i].shape[0]) @@ -1727,7 +1809,7 @@ upper_center_offset+(self.tomo_stacks[i].shape[0]-1-upper_row)*center_slope] t0 = time() self.tomo_recon_stacks[i]= self._reconstructOneTomoStack(self.tomo_stacks[i], - thetas, center_offsets=center_offsets, sigma=0.1, ncore=self.ncore, + thetas, center_offsets=center_offsets, sigma=0.1, num_core=self.num_core, algorithm='gridrec', run_secondary_sirt=True, secondary_iter=25) logging.info(f'Reconstruction of stack {index} took {time()-t0:.2f} seconds!') if not self.test_mode: @@ -1749,6 +1831,9 @@ # Update config and save to file stack['reconstructed'] = True + combine_stacks = self.config.get('combine_stacks') + if combine_stacks and index in combine_stacks.get('stacks', []): + combine_stacks['stacks'].remove(index) self.cf.saveFile(self.config_out) def combineTomoStacks(self): @@ -1757,13 +1842,16 @@ # stack order: stack,row(z),x,y logging.debug('Combine reconstructed tomography stacks') # Load any unloaded reconstructed stacks - stacks = self.config['stack_info']['stacks'] + stack_info = self.config['stack_info'] + stacks = stack_info['stacks'] for i,stack in enumerate(stacks): + available = False if not self.tomo_recon_stacks[i].size and stack.get('reconstructed', False): self.tomo_recon_stacks[i], available = self._loadTomo('recon stack', stack['index'], required=True) - if not self.tomo_recon_stacks[i].size or not available: + if not self.tomo_recon_stacks[i].size: logging.error(f'Unable to load reconstructed stack {stack["index"]}') + stack['reconstructed'] = False return if i: if (self.tomo_recon_stacks[i].shape[1] != self.tomo_recon_stacks[0].shape[1] or @@ -1777,12 +1865,12 @@ msnc.illegal_value('row_bounds', row_bounds, 'config file') return - # Selecting xy bounds + # Selecting x bounds (in yz-plane) tomosum = 0 #RV FIX := - #[tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,2)) for tomo_recon_stack in - # self.tomo_recon_stacks] - combine_stacks =self.config.get('combine_stacks') + [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,2)) for tomo_recon_stack in + self.tomo_recon_stacks] + combine_stacks = self.config.get('combine_stacks') if combine_stacks and 'x_bounds' in combine_stacks: x_bounds = combine_stacks['x_bounds'] if not msnc.is_index_range(x_bounds, 0, self.tomo_recon_stacks[0].shape[1]): @@ -1805,17 +1893,19 @@ else: x_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') if False and self.test_mode: - np.savetxt(self.output_folder+'recon_stack_sum_yz.txt', + np.savetxt(f'{self.output_folder}/recon_stack_sum_yz.txt', tomosum[x_bounds[0]:x_bounds[1]], fmt='%.6e') if self.save_plots_only: msnc.clearFig('recon stack sum yz') + + # Selecting y bounds (in xz-plane) tomosum = 0 #RV FIX := - #[tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in - # self.tomo_recon_stacks] + [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in + self.tomo_recon_stacks] if combine_stacks and 'y_bounds' in combine_stacks: y_bounds = combine_stacks['y_bounds'] - if not msnc.is_index_range(x_bounds, 0, self.tomo_recon_stacks[0].shape[1]): + if not msnc.is_index_range(y_bounds, 0, self.tomo_recon_stacks[0].shape[1]): msnc.illegal_value('y_bounds', y_bounds, 'config file') elif not self.test_mode: msnc.quickPlot(tomosum, title='recon stack sum xz') @@ -1835,7 +1925,7 @@ else: y_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum xz') if False and self.test_mode: - np.savetxt(self.output_folder+'recon_stack_sum_xz.txt', + np.savetxt(f'{self.output_folder}/recon_stack_sum_xz.txt', tomosum[y_bounds[0]:y_bounds[1]], fmt='%.6e') if self.save_plots_only: msnc.clearFig('recon stack sum xz') @@ -1843,7 +1933,7 @@ # Combine reconstructed tomography stacks logging.info(f'Combining reconstructed stacks ...') t0 = time() - num_tomo_stacks = self.config['stack_info']['num'] + num_tomo_stacks = stack_info['num'] if num_tomo_stacks == 1: low_bound = row_bounds[0] else: @@ -1860,6 +1950,9 @@ [self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:, x_bounds[0]:x_bounds[1],y_bounds[0]:y_bounds[1]]]) logging.info(f'... done in {time()-t0:.2f} seconds!') + combined_stacks = [stack['index'] for stack in stacks] + + # Wrap up if in test_mode tomosum = np.sum(tomo_recon_combined, axis=(1,2)) if self.test_mode: zoom_perc = self.config['preprocess'].get('zoom_perc', 100) @@ -1872,20 +1965,23 @@ path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) if False: - np.savetxt(self.output_folder+'recon_combined_sum_xy.txt', + np.savetxt(f'{self.output_folder}/recon_combined_sum_xy.txt', tomosum, fmt='%.6e') - np.savetxt(self.output_folder+'recon_combined.txt', + np.savetxt(f'{self.output_folder}/recon_combined.txt', tomo_recon_combined[int(tomo_recon_combined.shape[0]/2),:,:], fmt='%.6e') - combine_stacks =self.config.get('combine_stacks') # Update config and save to file if combine_stacks: combine_stacks['x_bounds'] = x_bounds combine_stacks['y_bounds'] = y_bounds + combine_stacks['stacks'] = combined_stacks else: - self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds} + self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds, + 'stacks' : combined_stacks} self.cf.saveFile(self.config_out) return + + # Selecting z bounds (in xy-plane) msnc.quickPlot(tomosum, title='recon combined sum xy') if pyip.inputYesNo( '\nDo you want to change the image z-bounds (y/[n])? ', @@ -1915,54 +2011,45 @@ # Save combined reconstructed tomo stacks base_name = 'recon combined' - combined_stacks = [] for stack in stacks: base_name += f' {stack["index"]}' - combined_stacks.append(stack['index']) self._saveTomo(base_name, tomo_recon_combined) # Update config and save to file if combine_stacks: combine_stacks['x_bounds'] = x_bounds combine_stacks['y_bounds'] = y_bounds + combine_stacks['z_bounds'] = z_bounds combine_stacks['stacks'] = combined_stacks else: self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds, - 'stacks' : combined_stacks} + 'z_bounds' : z_bounds, 'stacks' : combined_stacks} self.cf.saveFile(self.config_out) def runTomo(config_file=None, config_dict=None, output_folder='.', log_level='INFO', - test_mode=False): + test_mode=False, num_core=-1): """Run a tomography analysis. """ # Instantiate Tomo object tomo = Tomo(config_file=config_file, output_folder=output_folder, log_level=log_level, - test_mode=test_mode) + test_mode=test_mode, num_core=num_core) if not tomo.is_valid: raise ValueError('Invalid config and/or detector file provided.') # Preprocess the image files + assert(tomo.config['stack_info']) num_tomo_stacks = tomo.config['stack_info']['num'] assert(num_tomo_stacks == len(tomo.tomo_stacks)) - preprocess = tomo.config.get('preprocess', None) preprocessed_stacks = [] - if preprocess: - preprocessed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] - if stack.get('preprocessed', False)] - if not len(preprocessed_stacks): + if not tomo.test_mode: + preprocess = tomo.config.get('preprocess', None) + if preprocess: + preprocessed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] + if stack.get('preprocessed', False)] + if len(preprocessed_stacks) != num_tomo_stacks: tomo.genTomoStacks() if not tomo.is_valid: IOError('Unable to load all required image files.') - find_center = tomo.config.get('find_center') - if find_center and find_center.get('completed', False): - center_stack_index = find_center['center_stack_index'] - if not center_stack_index in preprocessed_stacks: - find_center['completed'] = False -#RV FIX -# tomo.config.pop('check_center', 'check_center not found') -# combined_stacks = tomo.config.get('combined_stacks') -# if combined_stacks: -# combined_stacks['completed'] = False tomo.cf.saveFile(tomo.config_out) # Find centers @@ -1975,63 +2062,69 @@ # tomo.checkCenters() # Reconstruct tomography stacks - if len(tomo.config.get('reconstructed_stacks', [])) != tomo.config['stack_info']['num']: + assert(tomo.config['stack_info']['stacks']) + reconstructed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] + if stack.get('reconstructed', False)] + if len(reconstructed_stacks) != num_tomo_stacks: tomo.reconstructTomoStacks() # Combine reconstructed tomography stacks - combined_stacks = tomo.config.get('combined_stacks') - if combined_stacks is None or not combined_stacks.get('completed', False): + reconstructed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] + if stack.get('reconstructed', False)] + combine_stacks = tomo.config.get('combine_stacks') + if len(reconstructed_stacks) and (combine_stacks is None or + combine_stacks.get('stacks') != reconstructed_stacks): tomo.combineTomoStacks() #%%============================================================================ if __name__ == '__main__': + # Parse command line arguments - arguments = sys.argv[1:] - config_file = None - output_folder = '.' - log_level = 'INFO' - test_mode = False - try: - opts, args = getopt.getopt(arguments,"hc:o:l:t") - except getopt.GetoptError: - print('usage: tomo.py -c <config_file> -o <output_folder> -l <log_level> -t') - sys.exit(2) - for opt, arg in opts: - if opt == '-h': - print('usage: tomo.py -c <config_file> -o <output_folder> -l <log_level> -t') - sys.exit() - elif opt in ("-c"): - config_file = arg - elif opt in ("-o"): - output_folder = arg - elif opt in ("-l"): - log_level = arg - elif opt in ("-t"): - test_mode = True - if config_file is None: + parser = argparse.ArgumentParser( + description='Tomography reconstruction') + parser.add_argument('-c', '--config', + default=None, + help='Input config') + parser.add_argument('-o', '--output_folder', + default='.', + help='Output folder') + parser.add_argument('-l', '--log_level', + default='INFO', + help='Log level') + parser.add_argument('-t', '--test_mode', + action='store_true', + default=False, + help='Test mode flag') + parser.add_argument('--num_core', + default=-1, + help='Number of cores') + args = parser.parse_args() + + if args.config is None: if os.path.isfile('config.yaml'): - config_file = 'config.yaml' + args.config = 'config.yaml' else: - config_file = 'config.txt' + args.config = 'config.txt' # Set basic log configuration logging_format = '%(asctime)s : %(levelname)s - %(module)s : %(funcName)s - %(message)s' - if not test_mode: - level = getattr(logging, log_level.upper(), None) + if not args.test_mode: + level = getattr(logging, args.log_level.upper(), None) if not isinstance(level, int): - raise ValueError(f'Invalid log_level: {log_level}') + raise ValueError(f'Invalid log_level: {args.log_level}') logging.basicConfig(format=logging_format, level=level, force=True, handlers=[logging.StreamHandler()]) - logging.debug(f'config_file = {config_file}') - logging.debug(f'output_folder = {output_folder}') - logging.debug(f'log_level = {log_level}') - logging.debug(f'test_mode = {test_mode}') + logging.debug(f'config = {args.config}') + logging.debug(f'output_folder = {args.output_folder}') + logging.debug(f'log_level = {args.log_level}') + logging.debug(f'test_mode = {args.test_mode}') + logging.debug(f'num_core = {args.num_core}') # Run tomography analysis - runTomo(config_file=config_file, output_folder=output_folder, log_level=log_level, - test_mode=test_mode) + runTomo(config_file=args.config, output_folder=args.output_folder, log_level=args.log_level, + test_mode=args.test_mode, num_core=args.num_core) #%%============================================================================ - input('Press any key to continue') +# input('Press any key to continue') #%%============================================================================
--- a/tomo_setup.xml Tue Mar 29 16:10:16 2022 +0000 +++ b/tomo_setup.xml Thu Mar 31 18:24:16 2022 +0000 @@ -10,12 +10,12 @@ -i inputfiles.txt -c '$config' --theta_range '$thetas.theta_start $thetas.theta_end $thetas.num_thetas' - --dark '$dark' - --bright '$bright' - --tomo '$tomo' - --detectorbounds '$detectorbounds' + --dark 'dark_field.png' + --bright 'bright_field.png' + --tomo 'tomo.png' + --detectorbounds 'detectorbounds.png' --output_data '$output_data' - --output_config '$output_config' + --output_config 'output_config.yaml' -l '$log' #for $s in $tomo_sets# ${s.offset} ${s.num} #end for ]]></command> @@ -43,12 +43,12 @@ </inputs> <outputs> <data name="inputfiles" format="txt" label="Input files" from_work_dir="inputfiles.txt" hidden="true"/> - <data name="dark" format="png" label="Dark field"/> - <data name="bright" format="png" label="Bright field"/> - <data name="tomo" format="png" label="First tomography image"/> - <data name="detectorbounds" format="png" label="Detector bounds"/> + <data name="dark_field" format="png" label="Dark field" from_work_dir="dark_field.png"/> + <data name="bright_field" format="png" label="Bright field" from_work_dir="bright_field.png"/> + <data name="tomo" format="png" label="First tomography image" from_work_dir="tomo.png"/> + <data name="detectorbounds" format="png" label="Detector bounds" from_work_dir="detectorbounds.png"/> <data name="output_data" format="npz" label="Preprocessed tomography data"/> - <data name="output_config" format="txt" label="Output config"/> + <data name="output_config" format="yaml" label="Output config" from_work_dir="output_config.yaml"/> <data name="log" format="txt" label="Log"/> </outputs> <help><![CDATA[