Mercurial > repos > rv43 > tomo
diff tomo.py @ 49:26f99fdd8d61 draft
"planemo upload for repository https://github.com/rolfverberg/galaxytools commit 4f7738d02f4a3fd91373f43937ed311b6fe11a12"
author | rv43 |
---|---|
date | Thu, 28 Jul 2022 16:05:24 +0000 |
parents | 059819ea1f0e |
children | 75dd6e15f628 |
line wrap: on
line diff
--- a/tomo.py Wed Apr 27 17:28:26 2022 +0000 +++ b/tomo.py Thu Jul 28 16:05:24 2022 +0000 @@ -14,10 +14,6 @@ import getopt import re import io -try: - import pyinputplus as pyip -except: - pass import argparse import numpy as np try: @@ -43,7 +39,13 @@ except: pass -import msnc_tools as msnc +from detector import TomoDetectorConfig +from fit import Fit +#from msnctools.general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \ +from general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \ + input_int, input_num, input_yesno, input_menu, findImageFiles, loadImageStack, clearPlot, \ + draw_mask_1d, quickPlot, clearImshow, quickImshow, combine_tiffs_in_h5, Config +from general import selectArrayBounds,selectImageRange, selectImageBounds # the following tomopy routines don't run with more than 24 cores on Galaxy-Dev # - tomopy.find_center_vo @@ -68,7 +70,7 @@ def __exit__(self, exc_type, exc_value, traceback): ne.set_num_threads(self.num_core_org) -class ConfigTomo(msnc.Config): +class ConfigTomo(Config): """Class for processing a config file. """ @@ -96,9 +98,9 @@ # Check number of tomography image stacks self.num_tomo_stacks = self.config.get('num_tomo_stacks', 1) - if not msnc.is_int(self.num_tomo_stacks, 1): + if not is_int(self.num_tomo_stacks, 1): self.num_tomo_stacks = None - msnc.illegal_value('num_tomo_stacks', self.num_tomo_stacks, 'config file') + illegal_value(self.num_tomo_stacks, 'num_tomo_stacks', 'config file') return False logging.info(f'num_tomo_stacks = {self.num_tomo_stacks}') @@ -109,8 +111,8 @@ logging.error(f'Incorrect number of tomography data path names in config file') is_valid = False self.tomo_data_paths = [tomo_data_paths_indices[i][1] for i in range(self.num_tomo_stacks)] - self.tomo_data_indices = [msnc.get_trailing_int(tomo_data_paths_indices[i][0]) - if msnc.get_trailing_int(tomo_data_paths_indices[i][0]) else None + self.tomo_data_indices = [get_trailing_int(tomo_data_paths_indices[i][0]) + if get_trailing_int(tomo_data_paths_indices[i][0]) else None for i in range(self.num_tomo_stacks)] tomo_ref_height_indices = sorted({key:value for key,value in self.config.items() if 'z_pos' in key}.items()) @@ -125,18 +127,18 @@ # Check tomo angle (theta) range self.start_theta = self.config.get('start_theta', 0.) - if not msnc.is_num(self.start_theta, 0.): - msnc.illegal_value('start_theta', self.start_theta, 'config file') + if not is_num(self.start_theta, 0.): + illegal_value(self.start_theta, 'start_theta', 'config file') is_valid = False logging.debug(f'start_theta = {self.start_theta}') self.end_theta = self.config.get('end_theta', 180.) - if not msnc.is_num(self.end_theta, self.start_theta): - msnc.illegal_value('end_theta', self.end_theta, 'config file') + if not is_num(self.end_theta, self.start_theta): + illegal_value(self.end_theta, 'end_theta', 'config file') is_valid = False logging.debug(f'end_theta = {self.end_theta}') self.num_thetas = self.config.get('num_thetas') - if not (self.num_thetas is None or msnc.is_int(self.num_thetas, 1)): - msnc.illegal_value('num_thetas', self.num_thetas, 'config file') + if not (self.num_thetas is None or is_int(self.num_thetas, 1)): + illegal_value(self.num_thetas, 'num_thetas', 'config file') self.num_thetas = None is_valid = False logging.debug(f'num_thetas = {self.num_thetas}') @@ -165,16 +167,16 @@ # Check number of tomography image stacks stack_info = self.config['stack_info'] self.num_tomo_stacks = stack_info.get('num', 1) - if not msnc.is_int(self.num_tomo_stacks, 1): + if not is_int(self.num_tomo_stacks, 1): self.num_tomo_stacks = None - msnc.illegal_value('stack_info:num', self.num_tomo_stacks, 'config file') + illegal_value(self.num_tomo_stacks, 'num_tomo_stacks', 'config file') return False logging.info(f'num_tomo_stacks = {self.num_tomo_stacks}') # Find tomography images file/folders and stack parameters stacks = stack_info.get('stacks') if stacks is None or len(stacks) is not self.num_tomo_stacks: - msnc.illegal_value('stack_info:stacks', stacks, 'config file') + illegal_value(stacks, 'stacks', 'config file') return False self.tomo_data_paths = [] self.tomo_data_indices = [] @@ -192,18 +194,18 @@ self.num_thetas = None else: self.start_theta = theta_range.get('start', 0.) - if not msnc.is_num(self.start_theta, 0.): - msnc.illegal_value('theta_range:start', self.start_theta, 'config file') + if not is_num(self.start_theta, 0.): + illegal_value(self.start_theta, 'theta_range:start', 'config file') is_valid = False logging.debug(f'start_theta = {self.start_theta}') self.end_theta = theta_range.get('end', 180.) - if not msnc.is_num(self.end_theta, self.start_theta): - msnc.illegal_value('theta_range:end', self.end_theta, 'config file') + if not is_num(self.end_theta, self.start_theta): + illegal_value(self.end_theta, 'theta_range:end', 'config file') is_valid = False logging.debug(f'end_theta = {self.end_theta}') self.num_thetas = theta_range.get('num') - if self.num_thetas and not msnc.is_int(self.num_thetas, 1): - msnc.illegal_value('theta_range:num', self.num_thetas, 'config file') + if self.num_thetas and not is_int(self.num_thetas, 1): + illegal_value(self.num_thetas, 'theta_range:num', 'config file') self.num_thetas = None is_valid = False logging.debug(f'num_thetas = {self.num_thetas}') @@ -218,7 +220,7 @@ # Check work_folder (shared by both file formats) work_folder = os.path.abspath(self.config.get('work_folder', '')) if not os.path.isdir(work_folder): - msnc.illegal_value('work_folder', work_folder, 'config file') + illegal_value(work_folder, 'work_folder', 'config file') is_valid = False logging.info(f'work_folder: {work_folder}') @@ -226,7 +228,7 @@ self.data_filetype = self.config.get('data_filetype', 'tif') if not isinstance(self.data_filetype, str) or (self.data_filetype != 'tif' and self.data_filetype != 'h5'): - msnc.illegal_value('data_filetype', self.data_filetype, 'config file') + illegal_value(self.data_filetype, 'data_filetype', 'config file') if self.suffix == '.yml' or self.suffix == '.yaml': is_valid = self._validate_yaml() @@ -244,7 +246,7 @@ self.tdf_data_path = os.path.abspath( f'{work_folder}/{self.tdf_data_path}') else: - msnc.illegal_value('tdf_data_path', tdf_data_fil, 'config file') + illegal_value(tdf_data_fil, 'tdf_data_path', 'config file') is_valid = False else: if isinstance(self.tdf_data_path, int): @@ -255,7 +257,7 @@ self.tdf_data_path = os.path.abspath( f'{work_folder}/{self.tdf_data_path}') else: - msnc.illegal_value('tdf_data_path', self.tdf_data_path, 'config file') + illegal_value(self.tdf_data_path, 'tdf_data_path', 'config file') is_valid = False logging.info(f'dark field images path = {self.tdf_data_path}') @@ -267,7 +269,7 @@ self.tbf_data_path = os.path.abspath( f'{work_folder}/{self.tbf_data_path}') else: - msnc.illegal_value('tbf_data_path', tbf_data_fil, 'config file') + illegal_value(tbf_data_fil, 'tbf_data_path', 'config file') is_valid = False else: if isinstance(self.tbf_data_path, int): @@ -278,7 +280,7 @@ self.tbf_data_path = os.path.abspath( f'{work_folder}/{self.tbf_data_path}') else: - msnc.illegal_value('tbf_data_path', self.tbf_data_path, 'config file') + illegal_value(self.tbf_data_path, 'tbf_data_path', 'config file') is_valid = False logging.info(f'bright field images path = {self.tbf_data_path}') @@ -293,7 +295,7 @@ if not os.path.isabs(data_path): data_path = os.path.abspath(f'{work_folder}/{data_path}') else: - msnc.illegal_value(f'stack_info:stacks:data_path', data_path, 'config file') + illegal_value(data_path, 'stack_info:stacks:data_path', 'config file') is_valid = False data_path = None else: @@ -303,7 +305,7 @@ if not os.path.isabs(data_path): data_path = os.path.abspath(f'{work_folder}/{data_path}') else: - msnc.illegal_value(f'stack_info:stacks:data_path', data_path, 'config file') + illegal_value(data_path, 'stack_info:stacks:data_path', 'config file') is_valid = False data_path = None tomo_data_paths.append(data_path) @@ -315,7 +317,7 @@ else: index = 1 elif not isinstance(index, int): - msnc.illegal_value(f'stack_info:stacks:index', index, 'config file') + illegal_value(index, 'stack_info:stacks:index', 'config file') is_valid = False index = None tomo_data_indices.append(index) @@ -326,13 +328,13 @@ ref_height = None else: ref_height = 0. - elif not msnc.is_num(ref_height): - msnc.illegal_value(f'stack_info:stacks:ref_height', ref_height, 'config file') + elif not is_num(ref_height): + illegal_value(ref_height, 'stack_info:stacks:ref_height', 'config file') is_valid = False ref_height = None # Set reference heights relative to first stack - if (len(tomo_ref_heights) and msnc.is_num(ref_height) and - msnc.is_num(tomo_ref_heights[0])): + if (len(tomo_ref_heights) and is_num(ref_height) and + is_num(tomo_ref_heights[0])): ref_height = (round(ref_height-tomo_ref_heights[0], 3)) tomo_ref_heights.append(ref_height) tomo_ref_heights[0] = 0.0 @@ -436,7 +438,7 @@ else: raise OSError(f'Invalid config_out input {config_out} {type(config_out)}') if not os.path.isdir(output_folder): - raise OSError(f'Invalid output_folder input {output_folder} {type(output_folder)}') + os.mkdir(os.path.abspath(output_folder)) if isinstance(log_stream, str): path = os.path.split(log_stream)[0] if path and not os.path.isdir(path): @@ -515,12 +517,12 @@ self.is_valid = self.cf.validate() # Load detector info file - df = msnc.Detector(self.cf.config['detector']['id']) + df = TomoDetectorConfig(self.cf.config['detector']['id']) # Check detector info file parameters - if df.validate(): - pixel_size = df.getPixelSize() - num_rows, num_columns = df.getDimensions() + if df.valid: + pixel_size = df.pixel_size + num_rows, num_columns = df.dimensions if not pixel_size or not num_rows or not num_columns: self.is_valid = False else: @@ -577,9 +579,9 @@ # Check number of tomography angles/thetas num_thetas = self.config['theta_range'].get('num') if num_thetas is None: - num_thetas = pyip.inputInt('\nEnter the number of thetas (>0): ', greaterThan=0) - elif not msnc.is_int(num_thetas, 0): - msnc.illegal_value('num_thetas', num_thetas, 'config file') + num_thetas = input_int('\nEnter the number of thetas', 1) + elif not is_int(num_thetas, 0): + illegal_value(num_thetas, 'num_thetas', 'config file') self.is_valid = False return self.config['theta_range']['num'] = num_thetas @@ -591,7 +593,7 @@ img_offset = dark_field.get('img_offset', -1) num_imgs = dark_field.get('num', 0) if not self.test_mode: - img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, + img_start, img_offset, num_imgs = selectImageRange(img_start, img_offset, num_imgs, 'dark field') if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable dark field images') @@ -610,7 +612,7 @@ img_offset = bright_field.get('img_offset', -1) num_imgs = bright_field.get('num', 0) if not self.test_mode: - img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, + img_start, img_offset, num_imgs = selectImageRange(img_start, img_offset, num_imgs, 'bright field') if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable bright field images') @@ -632,7 +634,7 @@ img_offset = stack.get('img_offset', -1) num_imgs = stack.get('num', 0) if not self.test_mode: - img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, + img_start, img_offset, num_imgs = selectImageRange(img_start, img_offset, num_imgs, f'tomography stack {index}', num_thetas) if img_start < 0 or num_imgs != num_thetas: logging.error('Unable to find suitable tomography images') @@ -656,7 +658,7 @@ # Load the dark field images logging.debug('Loading dark field...') dark_field = self.config['dark_field'] - tdf_stack = msnc.loadImageStack(tdf_files, self.config['data_filetype'], + tdf_stack = loadImageStack(tdf_files, self.config['data_filetype'], dark_field['img_offset'], dark_field['num']) # Take median @@ -671,10 +673,10 @@ logging.debug(f'tdf_mean = {tdf_mean}') np.nan_to_num(self.tdf, copy=False, nan=tdf_mean, posinf=tdf_mean, neginf=0.) if self.galaxy_flag: - msnc.quickImshow(self.tdf, title='dark field', path='setup_pngs', + quickImshow(self.tdf, title='dark field', path='setup_pngs', save_fig=True, save_only=True) elif not self.test_mode: - msnc.quickImshow(self.tdf, title='dark field', path=self.output_folder, + quickImshow(self.tdf, title='dark field', path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) def _genBright(self, tbf_files): @@ -683,7 +685,7 @@ # Load the bright field images logging.debug('Loading bright field...') bright_field = self.config['bright_field'] - tbf_stack = msnc.loadImageStack(tbf_files, self.config['data_filetype'], + tbf_stack = loadImageStack(tbf_files, self.config['data_filetype'], bright_field['img_offset'], bright_field['num']) # Take median @@ -705,10 +707,10 @@ else: logging.warning('Dark field unavailable') if self.galaxy_flag: - msnc.quickImshow(self.tbf, title='bright field', path='setup_pngs', + quickImshow(self.tbf, title='bright field', path='setup_pngs', save_fig=True, save_only=True) elif not self.test_mode: - msnc.quickImshow(self.tbf, title='bright field', path=self.output_folder, + quickImshow(self.tbf, title='bright field', path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) def _setDetectorBounds(self, tomo_stack_files): @@ -721,10 +723,10 @@ 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') + illegal_value(img_x_bounds[0], 'preprocess: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') + if not is_index_range(img_x_bounds, 0, self.tbf.shape[0]): + illegal_value(img_x_bounds[1], 'preprocess:img_x_bounds[1]', 'config file') img_x_bounds[1] = self.tbf.shape[0] if self.test_mode: # Update config and save to file @@ -763,11 +765,13 @@ x_low = 0 x_upp = x_sum.size if num_x_min is not None: - fit = msnc.fitStep(y=x_sum, model='rectangle', form='atan') - x_low = fit.get('center1', None) - x_upp = fit.get('center2', None) - sig_low = fit.get('sigma1', None) - sig_upp = fit.get('sigma2', None) + fit = Fit.fit_data(np.array(range(len(x_sum))), x_sum, models='rectangle', + form='atan', guess=True) + parameters = fit.best_values + x_low = parameters.get('center1', None) + x_upp = parameters.get('center2', None) + sig_low = parameters.get('sigma1', None) + sig_upp = parameters.get('sigma2', None) if (x_low is not None and x_upp is not None and sig_low is not None and sig_upp is not None and 0 <= x_low < x_upp <= x_sum.size and (sig_low+sig_upp)/(x_upp-x_low) < 0.1): @@ -784,14 +788,14 @@ else: x_low = 0 x_upp = x_sum.size - msnc.quickPlot((range(x_sum.size), x_sum), + quickPlot((range(x_sum.size), x_sum), ([x_low, x_low], [x_sum_min, x_sum_max], 'r-'), ([x_upp-1, x_upp-1], [x_sum_min, x_sum_max], 'r-'), title=f'sum bright field over theta/y (row bounds: [{x_low}, {x_upp}])', path='setup_pngs', name='detectorbounds.png', save_fig=True, save_only=True, show_grid=True) for i,stack in enumerate(stacks): - tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], + tomo_stack = loadImageStack(tomo_stack_files[i], self.config['data_filetype'], stack['img_offset'], 1) tomo_stack = tomo_stack[0,:,:] if num_x_min is not None: @@ -799,7 +803,7 @@ tomo_stack[x_low,:] = tomo_stack_max tomo_stack[x_upp-1,:] = tomo_stack_max title = f'tomography image at theta={self.config["theta_range"]["start"]}' - msnc.quickImshow(tomo_stack, title=title, path='setup_pngs', + quickImshow(tomo_stack, title=title, path='setup_pngs', name=f'tomo_{stack["index"]}.png', save_fig=True, save_only=True, show_grid=True) del tomo_stack @@ -817,47 +821,47 @@ # For one tomography stack only: load the first image title = None - msnc.quickImshow(self.tbf, title='bright field') + quickImshow(self.tbf, title='bright field') if num_tomo_stacks == 1: - tomo_stack = msnc.loadImageStack(tomo_stack_files[0], self.config['data_filetype'], + tomo_stack = loadImageStack(tomo_stack_files[0], self.config['data_filetype'], stacks[0]['img_offset'], 1) title = f'tomography image at theta={self.config["theta_range"]["start"]}' - msnc.quickImshow(tomo_stack[0,:,:], title=title) - tomo_or_bright = pyip.inputNum('\nSelect image bounds from bright field (0) or '+ - 'from first tomography image (1): ', min=0, max=1) + quickImshow(tomo_stack[0,:,:], title=title) + tomo_or_bright = input_menu(['bright field', 'first tomography image'], + header='\nSelect image bounds from') else: print('\nSelect image bounds from bright field') tomo_or_bright = 0 if tomo_or_bright: x_sum = np.sum(tomo_stack[0,:,:], 1) - use_bounds = 'no' + use_bounds = False if img_x_bounds[0] is not None and img_x_bounds[1] is not None: tmp = np.copy(tomo_stack[0,:,:]) tmp_max = tmp.max() tmp[img_x_bounds[0],:] = tmp_max tmp[img_x_bounds[1]-1,:] = tmp_max title = f'tomography image at theta={self.config["theta_range"]["start"]}' - msnc.quickImshow(tmp, title=title) + quickImshow(tmp, title=title) del tmp x_sum_min = x_sum.min() x_sum_max = x_sum.max() - msnc.quickPlot((range(x_sum.size), x_sum), + 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-'), title='sum over theta and y') print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ f'upper bound = {img_x_bounds[1]} (exclusive)]') - use_bounds = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) - if use_bounds == 'no': - img_x_bounds = msnc.selectImageBounds(tomo_stack[0,:,:], 0, + use_bounds = input_yesno('Accept these bounds (y/n)?', 'y') + if not use_bounds: + img_x_bounds = list(selectImageBounds(tomo_stack[0,:,:], 0, img_x_bounds[0], img_x_bounds[1], num_x_min, - f'tomography image at theta={self.config["theta_range"]["start"]}') + 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') title = f'tomography image at theta={self.config["theta_range"]["start"]}' - msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, + quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, save_fig=self.save_plots, save_only=True) - msnc.quickPlot(range(img_x_bounds[0], img_x_bounds[1]), + quickPlot(range(img_x_bounds[0], img_x_bounds[1]), x_sum[img_x_bounds[0]:img_x_bounds[1]], title='sum over theta and y', path=self.output_folder, save_fig=self.save_plots, save_only=True) @@ -865,29 +869,31 @@ x_sum = np.sum(self.tbf, 1) x_sum_min = x_sum.min() x_sum_max = x_sum.max() - use_bounds = 'no' + use_bounds = False if img_x_bounds[0] is not None and img_x_bounds[1] is not None: tmp = np.copy(self.tbf) tmp_max = tmp.max() tmp[img_x_bounds[0],:] = tmp_max tmp[img_x_bounds[1]-1,:] = tmp_max title = 'bright field' - msnc.quickImshow(tmp, title=title) + quickImshow(tmp, title=title) del tmp - msnc.quickPlot((range(x_sum.size), x_sum), + 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-'), title='sum over theta and y') print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ f'upper bound = {img_x_bounds[1]} (exclusive)]') - use_bounds = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) - if use_bounds == 'no': - use_fit = 'no' - fit = msnc.fitStep(y=x_sum, model='rectangle', form='atan') - x_low = fit.get('center1', None) - x_upp = fit.get('center2', None) - sig_low = fit.get('sigma1', None) - sig_upp = fit.get('sigma2', None) + use_bounds = input_yesno('Accept these bounds (y/n)?', 'y') + if not use_bounds: + use_fit = False + fit = Fit.fit_data(np.array(range(len(x_sum))), x_sum, models='rectangle', + form='atan', guess=True) + parameters = fit.best_values + x_low = parameters.get('center1', None) + x_upp = parameters.get('center2', None) + sig_low = parameters.get('sigma1', None) + sig_upp = parameters.get('sigma2', None) if (x_low is not None and x_upp is not None and sig_low is not None and sig_upp is not None and 0 <= x_low < x_upp <= x_sum.size and (sig_low+sig_upp)/(x_upp-x_low) < 0.1): @@ -906,34 +912,50 @@ tmp[x_low,:] = tmp_max tmp[x_upp-1,:] = tmp_max title = 'bright field' - msnc.quickImshow(tmp, title=title) + quickImshow(tmp, title=title) del tmp - msnc.quickPlot((range(x_sum.size), x_sum), + quickPlot((range(x_sum.size), x_sum), ([x_low, x_low], [x_sum_min, x_sum_max], 'r-'), ([x_upp, x_upp], [x_sum_min, x_sum_max], 'r-'), title='sum over theta and y') print(f'lower bound = {x_low} (inclusive)') print(f'upper bound = {x_upp} (exclusive)]') - use_fit = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) - if use_fit == 'no': - img_x_bounds = msnc.selectArrayBounds(x_sum, img_x_bounds[0], img_x_bounds[1], - num_x_min, 'sum over theta and y') + use_fit = input_yesno('Accept these bounds (y/n)?', 'y') + if use_fit: + img_x_bounds = [x_low, x_upp] else: - img_x_bounds = [x_low, x_upp] + accept = False + while not accept: + mask, img_x_bounds = draw_mask_1d(x_sum, title='select x data range', + legend='sum over theta and y') + print(f'img_x_bounds = {img_x_bounds}') + while (len(img_x_bounds) != 1 or (len(x_sum) >= num_x_min and + img_x_bounds[0][1]-img_x_bounds[0][0]+1 < num_x_min)): + exit('Should not be here') + print('Please select exactly one continuous range') + mask, img_x_bounds = draw_mask_1d(x_sum, title='select x data range', + legend='sum over theta and y') + img_x_bounds = list(img_x_bounds[0]) + quickPlot(x_sum, vlines=img_x_bounds, title='sum over theta and y') + print(f'img_x_bounds = {img_x_bounds} (lower bound inclusive, upper bound '+ + 'exclusive)') + accept = input_yesno('Accept these bounds (y/n)?', 'y') + if not accept: + img_x_bounds = None 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') - #msnc.quickPlot(range(img_x_bounds[0], img_x_bounds[1]), + #quickPlot(range(img_x_bounds[0], img_x_bounds[1]), # x_sum[img_x_bounds[0]:img_x_bounds[1]], # title='sum over theta and y', path=self.output_folder, # save_fig=self.save_plots, save_only=True) - msnc.quickPlot((range(x_sum.size), x_sum), + 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], img_x_bounds[1]], [x_sum_min, x_sum_max], 'r-'), title='sum over theta and y', path=self.output_folder, save_fig=self.save_plots, save_only=True) del x_sum for i,stack in enumerate(stacks): - tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], + tomo_stack = loadImageStack(tomo_stack_files[i], self.config['data_filetype'], stack['img_offset'], 1) tomo_stack = tomo_stack[0,:,:] if num_x_min is not None: @@ -942,21 +964,21 @@ tomo_stack[img_x_bounds[1]-1,:] = tomo_stack_max title = f'tomography image at theta={self.config["theta_range"]["start"]}' if self.galaxy_flag: - msnc.quickImshow(tomo_stack, title=title, path='setup_pngs', + quickImshow(tomo_stack, title=title, path='setup_pngs', name=f'tomo_{stack["index"]}.png', save_fig=True, save_only=True, show_grid=True) else: - msnc.quickImshow(tomo_stack, title=title, path=self.output_folder, + quickImshow(tomo_stack, title=title, path=self.output_folder, name=f'tomo_{stack["index"]}.png', save_fig=self.save_plots, save_only=True, show_grid=True) del tomo_stack logging.debug(f'img_x_bounds: {img_x_bounds}') if self.save_plots_only: - msnc.clearFig('bright field') - msnc.clearFig('sum over theta and y') + clearImshow('bright field') + clearPlot('sum over theta and y') if title: - msnc.clearFig(title) + clearPlot(title) # Update config and save to file if preprocess is None: @@ -972,30 +994,26 @@ zoom_perc = 100 if not self.galaxy_flag: if preprocess is None or 'zoom_perc' not in preprocess: - if pyip.inputYesNo( - '\nDo you want to zoom in to reduce memory requirement (y/[n])? ', - blank=True) == 'yes': - zoom_perc = pyip.inputInt(' Enter zoom percentage [1, 100]: ', - min=1, max=100) + if input_yesno('\nDo you want to zoom in to reduce memory requirement (y/n)?', 'n'): + zoom_perc = input_int(' Enter zoom percentage', 1, 100) else: zoom_perc = preprocess['zoom_perc'] - if msnc.is_num(zoom_perc, 1., 100.): + if is_num(zoom_perc, 1., 100.): zoom_perc = int(zoom_perc) else: - msnc.illegal_value('zoom_perc', zoom_perc, 'config file') + illegal_value(zoom_perc, 'preprocess:zoom_perc', 'config file') zoom_perc = 100 num_theta_skip = 0 if not self.galaxy_flag: if preprocess is None or 'num_theta_skip' not in preprocess: - if pyip.inputYesNo( - 'Do you want to skip thetas to reduce memory requirement (y/[n])? ', - blank=True) == 'yes': - num_theta_skip = pyip.inputInt(' Enter the number skip theta interval'+ - f' [0, {self.num_thetas-1}]: ', min=0, max=self.num_thetas-1) + if input_yesno('Do you want to skip thetas to reduce memory requirement (y/n)?', + 'n'): + num_theta_skip = input_int(' Enter the number skip theta interval', 0, + self.num_thetas-1) else: num_theta_skip = preprocess['num_theta_skip'] - if not msnc.is_int(num_theta_skip, 0): - msnc.illegal_value('num_theta_skip', num_theta_skip, 'config file') + if not is_int(num_theta_skip, 0): + illegal_value(num_theta_skip, 'preprocess:num_theta_skip', 'config file') num_theta_skip = 0 logging.debug(f'zoom_perc = {zoom_perc}') logging.debug(f'num_theta_skip = {num_theta_skip}') @@ -1023,16 +1041,16 @@ title = f'{base_name} {zoom_perc}p' title += f'_{index}' tomo_file = re.sub(r"\s+", '_', f'{self.output_folder}/{title}.npy') - load_flag = 'no' + load_flag = False available = False if os.path.isfile(tomo_file): available = True if required: - load_flag = 'yes' + load_flag = True else: - load_flag = pyip.inputYesNo(f'\nDo you want to load {tomo_file} (y/n)? ') + load_flag = input_yesno(f'\nDo you want to load {tomo_file} (y/n)?') stack = np.array([]) - if load_flag == 'yes': + if load_flag: t0 = time() logging.info(f'Loading {tomo_file} ...') try: @@ -1042,7 +1060,7 @@ logging.error(f'Error loading {tomo_file}') logging.info(f'... done in {time()-t0:.2f} seconds!') if stack.size: - msnc.quickImshow(stack[:,0,:], title=title, path=self.output_folder, + quickImshow(stack[:,0,:], title=title, path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) return stack, available @@ -1106,7 +1124,7 @@ # Load a stack of tomography images index = stack['index'] t0 = time() - tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], + tomo_stack = 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') @@ -1145,7 +1163,7 @@ if not self.galaxy_flag: title = f'red stack fullres {index}' if not self.test_mode: - msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, + quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) if zoom_perc != 100: t0 = time() @@ -1160,7 +1178,7 @@ if not self.galaxy_flag: title = f'red stack {zoom_perc}p {index}' if not self.test_mode: - msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, + 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 @@ -1214,7 +1232,7 @@ 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 and not self.test_mode: - msnc.quickImshow(sinogram.T, f'sinogram center offset{center_offset:.2f}', + 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') @@ -1244,14 +1262,14 @@ vmax = np.max(edges[0,:,:]) vmin = -vmax if self.galaxy_flag: - msnc.quickImshow(edges[0,:,:], title, path=path, save_fig=True, save_only=True, + quickImshow(edges[0,:,:], title, path=path, save_fig=True, save_only=True, cmap='gray', vmin=vmin, vmax=vmax) else: if path is None: path=self.output_folder - msnc.quickImshow(edges[0,:,:], f'{title} coolwarm', path=path, + quickImshow(edges[0,:,:], f'{title} coolwarm', path=path, save_fig=self.save_plots, save_only=self.save_plots_only, cmap='coolwarm') - msnc.quickImshow(edges[0,:,:], f'{title} gray', path=path, + quickImshow(edges[0,:,:], f'{title} gray', path=path, save_fig=self.save_plots, save_only=self.save_plots_only, cmap='gray', vmin=vmin, vmax=vmax) del edges @@ -1303,8 +1321,7 @@ title = f'edges row{row} center offset{center_offset_vo:.2f} Vo' self._plotEdgesOnePlane(recon_plane, title) if not self.galaxy_flag: - if (pyip.inputYesNo('Try finding center using phase correlation '+ - '(y/[n])? ', blank=True) == 'yes'): + if input_yesno('Try finding center using phase correlation (y/n)?', 'n'): tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1, rotc_guess=tomo_center) error = 1. @@ -1319,13 +1336,9 @@ eff_pixel_size, cross_sectional_dim, False, num_core) title = f'edges row{row} center_offset{center_offset:.2f} PC' self._plotEdgesOnePlane(recon_plane, title) - if (pyip.inputYesNo('Accept a center location ([y]) or continue '+ - 'search (n)? ', blank=True) != 'no'): - center_offset = pyip.inputNum( - f' Enter chosen center offset [{-int(center)}, {int(center)}] '+ - f'([{center_offset_vo:.2f}])): ', blank=True) - if center_offset == '': - center_offset = center_offset_vo + if input_yesno('Accept a center location (y) or continue search (n)?', 'y'): + center_offset = input_num(' Enter chosen center offset', -center, center, + center_offset_vo) del sinogram_T del recon_plane return float(center_offset) @@ -1338,7 +1351,7 @@ set_center = galaxy_param['set_center'] set_range = galaxy_param['set_range'] center_offset_step = galaxy_param['set_step'] - if (not msnc.is_num(set_range, 0) or not msnc.is_num(center_offset_step) or + if (not is_num(set_range, 0) or not is_num(center_offset_step) or center_offset_step <= 0): logging.warning('Illegal center finding search parameter, skip search') del sinogram_T @@ -1347,17 +1360,15 @@ center_offset_low = set_center-set_range center_offset_upp = set_center+set_range else: - center_offset_low = pyip.inputInt('\nEnter lower bound for center offset '+ - f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) - center_offset_upp = pyip.inputInt('Enter upper bound for center offset '+ - f'[{center_offset_low}, {int(center)}]: ', - min=center_offset_low, max=int(center)) + center_offset_low = input_int('\nEnter lower bound for center offset', + -center, center) + center_offset_upp = input_int('Enter upper bound for center offset', + center_offset_low, center) if center_offset_upp == center_offset_low: center_offset_step = 1 else: - center_offset_step = pyip.inputInt('Enter step size for center offset search '+ - f'[1, {center_offset_upp-center_offset_low}]: ', - min=1, max=center_offset_upp-center_offset_low) + center_offset_step = input_int('Enter step size for center offset search', + 1, center_offset_upp-center_offset_low) num_center_offset = 1+int((center_offset_upp-center_offset_low)/center_offset_step) center_offsets = np.linspace(center_offset_low, center_offset_upp, num_center_offset) for center_offset in center_offsets: @@ -1373,8 +1384,7 @@ self._plotEdgesOnePlane(recon_plane, title, path='find_center_pngs') else: self._plotEdgesOnePlane(recon_plane, title) - if self.galaxy_flag or pyip.inputInt('\nContinue (0) or end the search (1): ', - min=0, max=1): + if self.galaxy_flag or input_int('\nContinue (0) or end the search (1)', 0, 1): break del sinogram_T @@ -1382,8 +1392,7 @@ if self.galaxy_flag: center_offset = center_offset_vo else: - center_offset = pyip.inputNum(f' Enter chosen center offset '+ - f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) + center_offset = input_num(' Enter chosen center offset', -center, center) return float(center_offset) def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, @@ -1402,7 +1411,7 @@ if row_bounds is None: row_bounds = [0, tomo_stack.shape[0]] else: - if not msnc.is_index_range(row_bounds, 0, tomo_stack.shape[0]): + if not 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') @@ -1467,7 +1476,7 @@ # Find dark field images dark_field = self.config['dark_field'] - img_start, num_imgs, dark_files = msnc.findImageFiles( + img_start, num_imgs, dark_files = findImageFiles( dark_field['data_path'], self.config['data_filetype'], 'dark field') if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable dark field images') @@ -1492,13 +1501,13 @@ logging.info(f'Number of dark field images = {dark_field["num"]}') logging.info(f'Dark field image start index = {dark_field["img_start"]}') if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': - dark_files = msnc.combine_tiffs_in_h5(dark_files, num_imgs, + dark_files = combine_tiffs_in_h5(dark_files, num_imgs, f'{self.config["work_folder"]}/dark_field.h5') dark_field['data_path'] = dark_files[0] # Find bright field images bright_field = self.config['bright_field'] - img_start, num_imgs, bright_files = msnc.findImageFiles( + img_start, num_imgs, bright_files = findImageFiles( bright_field['data_path'], self.config['data_filetype'], 'bright field') if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable bright field images') @@ -1520,7 +1529,7 @@ logging.info(f'Number of bright field images = {bright_field["num"]}') logging.info(f'Bright field image start index = {bright_field["img_start"]}') if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': - bright_files = msnc.combine_tiffs_in_h5(bright_files, num_imgs, + bright_files = combine_tiffs_in_h5(bright_files, num_imgs, f'{self.config["work_folder"]}/bright_field.h5') bright_field['data_path'] = bright_files[0] @@ -1528,7 +1537,7 @@ tomo_stack_files = [] for stack in self.config['stack_info']['stacks']: index = stack['index'] - img_start, num_imgs, tomo_files = msnc.findImageFiles( + img_start, num_imgs, tomo_files = findImageFiles( stack['data_path'], self.config['data_filetype'], f'tomography set {index}') if img_start < 0 or num_imgs < 1: logging.error('Unable to find suitable tomography images') @@ -1550,7 +1559,7 @@ logging.info(f'Number of tomography images for set {index} = {stack["num"]}') logging.info(f'Tomography set {index} image start index = {stack["img_start"]}') if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': - tomo_files = msnc.combine_tiffs_in_h5(tomo_files, num_imgs, + tomo_files = combine_tiffs_in_h5(tomo_files, num_imgs, f'{self.config["work_folder"]}/tomo_field_{index}.h5') stack['data_path'] = tomo_files[0] tomo_stack_files.append(tomo_files) @@ -1709,15 +1718,15 @@ center_stack_index = find_center['center_stack_index'] if (not isinstance(center_stack_index, int) or center_stack_index not in available_stacks): - msnc.illegal_value('center_stack_index', center_stack_index, 'config file') + illegal_value(center_stack_index, 'find_center:center_stack_index', 'config file') else: if self.test_mode: 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)? ', - blank=True) != 'no' and find_center.get('completed', False) == True): + if (input_yesno('Do you want to use this again (y/n)?', 'y') + and find_center.get('completed', False) == True): return # Load the required preprocessed stack @@ -1749,11 +1758,11 @@ else: while True: if not center_stack_index: - center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' - f'rotation axis centers {available_stacks}: ') + center_stack_index = input_int('\nEnter tomography stack index to get ' + f'rotation axis centers ({available_stacks})', inset=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}: ') + center_stack_index = input_int('\nEnter tomography stack index to get ' + f'rotation axis centers ({available_stacks})', inset=available_stacks) tomo_stack_index = available_stacks.index(center_stack_index) if not self.tomo_stacks[tomo_stack_index].size: self.tomo_stacks[tomo_stack_index], available = self._loadTomo( @@ -1785,8 +1794,8 @@ center_rows[0] = 0 if center_rows[1] == -1: center_rows[1] = center_stack.shape[0]-1 - if not msnc.is_index_range(row_bounds, 0, center_stack.shape[0]): - msnc.illegal_value('row_bounds', row_bounds) + if not is_index_range(row_bounds, 0, center_stack.shape[0]): + illegal_value(row_bounds, 'row_bounds', 'Tomo:findCenters') return # Set thetas (in degrees) @@ -1806,27 +1815,27 @@ eff_pixel_size = 100.*pixel_size/zoom_perc logging.debug(f'eff_pixel_size = {eff_pixel_size}') if num_tomo_stacks == 1: - accept = 'yes' + accept = True if not self.test_mode and not self.galaxy_flag: - accept = 'no' + accept = False print('\nSelect bounds for image reconstruction') - if msnc.is_index_range(row_bounds, 0, center_stack.shape[0]): + if is_index_range(row_bounds, 0, center_stack.shape[0]): a_tmp = np.copy(center_stack[:,0,:]) a_tmp_max = a_tmp.max() a_tmp[row_bounds[0],:] = a_tmp_max a_tmp[row_bounds[1]-1,:] = a_tmp_max print(f'lower bound = {row_bounds[0]} (inclusive)') print(f'upper bound = {row_bounds[1]} (exclusive)') - msnc.quickImshow(a_tmp, title=f'center stack theta={theta_start}', + quickImshow(a_tmp, title=f'center stack theta={theta_start}', aspect='auto') del a_tmp - accept = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) - if accept == 'no': - (n1, n2) = msnc.selectImageBounds(center_stack[:,0,:], 0, - title=f'center stack theta={theta_start}') - else: + accept = input_yesno('Accept these bounds (y/n)?', 'y') + if accept: n1 = row_bounds[0] n2 = row_bounds[1] + else: + n1, n2 = selectImageBounds(center_stack[:,0,:], 0, + title=f'center stack theta={theta_start}') else: tomo_ref_heights = [stack['ref_height'] for stack in stacks] n1 = int((1.+(tomo_ref_heights[0]+center_stack.shape[0]*eff_pixel_size- @@ -1838,16 +1847,16 @@ tmp_max = tmp.max() tmp[n1,:] = tmp_max tmp[n2-1,:] = tmp_max - if msnc.is_index_range(center_rows, 0, tmp.shape[0]): + if is_index_range(center_rows, 0, tmp.shape[0]): tmp[center_rows[0],:] = tmp_max tmp[center_rows[1]-1,:] = tmp_max extent = [0, tmp.shape[1], tmp.shape[0], 0] - msnc.quickImshow(tmp, title=f'center stack theta={theta_start}', + quickImshow(tmp, title=f'center stack theta={theta_start}', path=self.output_folder, extent=extent, save_fig=self.save_plots, save_only=self.save_plots_only, aspect='auto') del tmp #extent = [0, center_stack.shape[2], n2, n1] - #msnc.quickImshow(center_stack[n1:n2,0,:], title=f'center stack theta={theta_start}', + #quickImshow(center_stack[n1:n2,0,:], title=f'center stack theta={theta_start}', # path=self.output_folder, extent=extent, save_fig=self.save_plots, # save_only=self.save_plots_only, show_grid=True, aspect='auto') @@ -1859,36 +1868,35 @@ logging.info('Determine center offset at sample row boundaries') # Lower row center - use_row = 'no' - use_center = 'no' + use_row = False + use_center = False row = center_rows[0] if self.test_mode or self.galaxy_flag: - assert(msnc.is_int(row, n1, n2-2)) - if msnc.is_int(row, n1, n2-2): + assert(is_int(row, n1, n2-2)) + if is_int(row, n1, n2-2): if self.test_mode or self.galaxy_flag: - use_row = 'yes' + use_row = True 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) + quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') + use_row = input_yesno('\nCurrent row index for lower center = ' + f'{row}, use this value (y/n)?', 'y') if self.save_plots_only: - msnc.clearFig(f'theta={theta_start}') - if use_row != 'no': + clearImshow(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)? ', blank=True) - if use_center == 'no': - if use_row == 'no': + if is_num(center_offset): + use_center = input_yesno('Current lower center offset = '+ + f'{center_offset}, use this value (y/n)?', 'y') + if not use_center: + if not use_row: if not self.test_mode: - msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', + 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) + row = input_int('\nEnter row index to find lower center', n1, n2-2, n1) if row == '': row = n1 if self.save_plots_only: - msnc.clearFig(f'theta={theta_start}') + clearImshow(f'theta={theta_start}') # center_stack order: row,theta,column center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg, eff_pixel_size, cross_sectional_dim, num_core=num_core, @@ -1903,36 +1911,35 @@ lower_row = row # Upper row center - use_row = 'no' - use_center = 'no' + use_row = False + use_center = False row = center_rows[1] if self.test_mode or self.galaxy_flag: - assert(msnc.is_int(row, lower_row+1, n2-1)) - if msnc.is_int(row, lower_row+1, n2-1): + assert(is_int(row, lower_row+1, n2-1)) + if is_int(row, lower_row+1, n2-1): if self.test_mode or self.galaxy_flag: - use_row = 'yes' + use_row = True 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) + quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') + use_row = input_yesno('\nCurrent row index for upper center = ' + f'{row}, use this value (y/n)?', 'y') if self.save_plots_only: - msnc.clearFig(f'theta={theta_start}') - if use_row != 'no': + clearImshow(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)? ', blank=True) - if use_center == 'no': - if use_row == 'no': + if is_num(center_offset): + use_center = input_yesrnNo('Current upper center offset = '+ + f'{center_offset}, use this value (y/n)?', 'y') + if not use_center: + if not use_row: if not self.test_mode: - msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', + 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) + row = input_int('\nEnter row index to find upper center', lower_row+1, n2-1, n2-1) if row == '': row = n2-1 if self.save_plots_only: - msnc.clearFig(f'theta={theta_start}') + clearImshow(f'theta={theta_start}') # center_stack order: row,theta,column center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg, eff_pixel_size, cross_sectional_dim, num_core=num_core, @@ -1992,7 +1999,7 @@ for i in range(-2, 3): shift_i = shift+2*i plane1_shifted = spi.shift(plane2, [0, shift_i]) - msnc.quickPlot((plane1[0,:],), (plane1_shifted[0,:],), + quickPlot((plane1[0,:],), (plane1_shifted[0,:],), title=f'stacks {stack1} {stack2} shifted {2*i} theta={self.start_theta}', path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) @@ -2011,14 +2018,14 @@ for i in range(-2, 3): shift_i = -shift+2*i plane1_shifted = spi.shift(plane2, [0, shift_i]) - msnc.quickPlot((plane1[0,:],), (plane1_shifted[0,:],), + quickPlot((plane1[0,:],), (plane1_shifted[0,:],), title=f'stacks {stack1} {stack2} shifted {2*i} theta={start_theta}', path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) del plane1, plane2, plane1_shifted # Update config file - self.config = msnc.update('config.txt', 'check_centers', True, 'find_centers') + self.config = update('config.txt', 'check_centers', True, 'find_centers') def reconstructTomoStacks(self, galaxy_param=None, num_core=None): """Reconstruct tomography stacks. @@ -2041,9 +2048,9 @@ center_offsets = galaxy_param['center_offsets'] assert(isinstance(center_offsets, list) and len(center_offsets) == 2) lower_center_offset = center_offsets[0] - assert(msnc.is_num(lower_center_offset)) + assert(is_num(lower_center_offset)) upper_center_offset = center_offsets[1] - assert(msnc.is_num(upper_center_offset)) + assert(is_num(upper_center_offset)) else: if galaxy_param: logging.warning('Ignoring galaxy_param in reconstructTomoStacks (only for Galaxy)') @@ -2128,23 +2135,23 @@ if self.galaxy_flag: x_slice = int(self.tomo_stacks[i].shape[0]/2) title = f'{basetitle} {index} xslice{x_slice}' - msnc.quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, + quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, path='center_slice_pngs', save_fig=True, save_only=True) y_slice = int(self.tomo_stacks[i].shape[0]/2) title = f'{basetitle} {index} yslice{y_slice}' - msnc.quickImshow(self.tomo_recon_stacks[i][:,y_slice,:], title=title, + quickImshow(self.tomo_recon_stacks[i][:,y_slice,:], title=title, path='center_slice_pngs', save_fig=True, save_only=True) z_slice = int(self.tomo_stacks[i].shape[0]/2) title = f'{basetitle} {index} zslice{z_slice}' - msnc.quickImshow(self.tomo_recon_stacks[i][:,:,z_slice], title=title, + quickImshow(self.tomo_recon_stacks[i][:,:,z_slice], title=title, path='center_slice_pngs', save_fig=True, save_only=True) elif not self.test_mode: x_slice = int(self.tomo_stacks[i].shape[0]/2) title = f'{basetitle} {index} xslice{x_slice}' - msnc.quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, + quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) - msnc.quickPlot(self.tomo_recon_stacks[i] + quickPlot(self.tomo_recon_stacks[i] [x_slice,int(self.tomo_recon_stacks[i].shape[2]/2),:], title=f'{title} cut{int(self.tomo_recon_stacks[i].shape[2]/2)}', path=self.output_folder, save_fig=self.save_plots, @@ -2173,21 +2180,21 @@ tomosum = 0 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,2)) for tomo_recon_stack in self.tomo_recon_stacks] - msnc.quickPlot(tomosum, title='recon stack sum yz', path='center_slice_pngs', + quickPlot(tomosum, title='recon stack sum yz', path='center_slice_pngs', save_fig=True, save_only=True) # Create cross section profile in xz-plane tomosum = 0 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in self.tomo_recon_stacks] - msnc.quickPlot(tomosum, title='recon stack sum xz', path='center_slice_pngs', + quickPlot(tomosum, title='recon stack sum xz', path='center_slice_pngs', save_fig=True, save_only=True) # Create cross section profile in xy-plane num_tomo_stacks = len(stacks) row_bounds = self.config['find_center']['row_bounds'] - if not msnc.is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): - msnc.illegal_value('row_bounds', row_bounds, 'config file') + if not is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): + illegal_value(row_bounds, 'find_center:row_bounds', 'config file') return if num_tomo_stacks == 1: low_bound = row_bounds[0] @@ -2202,7 +2209,7 @@ tomosum = np.concatenate([tomosum, np.sum(self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:,:,:], axis=(1,2))]) - msnc.quickPlot(tomosum, title='recon stack sum xy', path='center_slice_pngs', + quickPlot(tomosum, title='recon stack sum xy', path='center_slice_pngs', save_fig=True, save_only=True) def combineTomoStacks(self, galaxy_param=None): @@ -2250,8 +2257,8 @@ # Get center stack boundaries row_bounds = self.config['find_center']['row_bounds'] - if not msnc.is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): - msnc.illegal_value('row_bounds', row_bounds, 'config file') + if not is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): + illegal_value(row_bounds, 'find_center:row_bounds', 'config file') return # Selecting x bounds (in yz-plane) @@ -2265,41 +2272,57 @@ x_bounds[0] = 0 if x_bounds[1] == -1: x_bounds[1] = tomosum.size - if not msnc.is_index_range(x_bounds, 0, tomosum.size): - msnc.illegal_value('x_bounds', x_bounds, 'galaxy input') + if not is_index_range(x_bounds, 0, tomosum.size): + illegal_value(x_bounds, 'x_bounds', 'galaxy input') tomosum_min = tomosum.min() tomosum_max = tomosum.max() - msnc.quickPlot((range(tomosum.size), tomosum), + quickPlot((range(tomosum.size), tomosum), ([x_bounds[0], x_bounds[0]], [tomosum_min, tomosum_max], 'r-'), ([x_bounds[1]-1, x_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), title=f'recon stack sum yz', path='combine_pngs', save_fig=True, save_only=True) else: if combine_stacks and 'x_bounds' in combine_stacks: x_bounds = combine_stacks['x_bounds'] - if not msnc.is_index_range(x_bounds, 0, tomosum.size): - msnc.illegal_value('x_bounds', x_bounds, 'config file') + if not is_index_range(x_bounds, 0, tomosum.size): + illegal_value(x_bounds, 'combine_stacks:x_bounds', 'config file') elif not self.test_mode: - msnc.quickPlot(tomosum, title='recon stack sum yz') - if pyip.inputYesNo('\nCurrent image x-bounds: '+ - f'[{x_bounds[0]}, {x_bounds[1]}], use these values ([y]/n)? ', - blank=True) == 'no': - if pyip.inputYesNo( - 'Do you want to change the image x-bounds ([y]/n)? ', - blank=True) == 'no': - x_bounds = [0, tomosum.size] - else: - x_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') + accept = False + while not accept: + mask, x_bounds = draw_mask_1d(tomosum, current_index_ranges=x_bounds, + title='select x data range', legend='recon stack sum yz') + while len(x_bounds) != 1: + print('Please select exactly one continuous range') + mask, x_bounds = draw_mask_1d(tomosum, title='select x data range', + legend='recon stack sum yz') + x_bounds = list(x_bounds[0]) + quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz') + print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+ + 'exclusive)') + accept = input_yesno('Accept these bounds (y/n)?', 'y') + if not accept: + x_bounds = None else: - msnc.quickPlot(tomosum, title='recon stack sum yz') - if pyip.inputYesNo('\nDo you want to change the image x-bounds (y/n)? ') == 'no': + if not input_yesno('\nDo you want to change the image x-bounds (y/n)?', 'n'): x_bounds = [0, tomosum.size] else: - x_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') + accept = False + while not accept: + mask, x_bounds = draw_mask_1d(tomosum, title='select x data range', + legend='recon stack sum yz') + while len(x_bounds) != 1: + print('Please select exactly one continuous range') + mask, x_bounds = draw_mask_1d(tomosum, title='select x data range', + legend='recon stack sum yz') + x_bounds = list(x_bounds[0]) + quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz') + print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+ + 'exclusive)') + accept = input_yesno('Accept these bounds (y/n)?', 'y') if False and self.test_mode: 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') + clearPlot('recon stack sum yz') # Selecting y bounds (in xz-plane) tomosum = 0 @@ -2311,41 +2334,57 @@ y_bounds[0] = 0 if y_bounds[1] == -1: y_bounds[1] = tomosum.size - if not msnc.is_index_range(y_bounds, 0, tomosum.size): - msnc.illegal_value('y_bounds', y_bounds, 'galaxy input') + if not is_index_range(y_bounds, 0, tomosum.size): + illegal_value(y_bounds, 'y_bounds', 'galaxy input') tomosum_min = tomosum.min() tomosum_max = tomosum.max() - msnc.quickPlot((range(tomosum.size), tomosum), + quickPlot((range(tomosum.size), tomosum), ([y_bounds[0], y_bounds[0]], [tomosum_min, tomosum_max], 'r-'), ([y_bounds[1]-1, y_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), title=f'recon stack sum xz', path='combine_pngs', save_fig=True, save_only=True) else: if combine_stacks and 'y_bounds' in combine_stacks: y_bounds = combine_stacks['y_bounds'] - if not msnc.is_index_range(y_bounds, 0, tomosum.size): - msnc.illegal_value('y_bounds', y_bounds, 'config file') + if not is_index_range(y_bounds, 0, tomosum.size): + illegal_value(y_bounds, 'combine_stacks:y_bounds', 'config file') elif not self.test_mode: - msnc.quickPlot(tomosum, title='recon stack sum xz') - if pyip.inputYesNo('\nCurrent image y-bounds: '+ - f'[{y_bounds[0]}, {y_bounds[1]}], use these values ([y]/n)? ', - blank=True) == 'no': - if pyip.inputYesNo( - 'Do you want to change the image y-bounds ([y]/n)? ', - blank=True) == 'no': - y_bounds = [0, tomosum.size] - else: - y_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') + accept = False + while not accept: + mask, y_bounds = draw_mask_1d(tomosum, current_index_ranges=y_bounds, + title='select y data range', legend='recon stack sum xz') + while len(y_bounds) != 1: + print('Please select exactly one continuous range') + mask, y_bounds = draw_mask_1d(tomosum, title='select y data range', + legend='recon stack sum xz') + y_bounds = list(y_bounds[0]) + quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz') + print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+ + 'exclusive)') + accept = input_yesno('Accept these bounds (y/n)?', 'y') + if not accept: + y_bounds = None else: - msnc.quickPlot(tomosum, title='recon stack sum xz') - if pyip.inputYesNo('\nDo you want to change the image y-bounds (y/n)? ') == 'no': + if not input_yesno('\nDo you want to change the image y-bounds (y/n)?', 'n'): y_bounds = [0, tomosum.size] else: - y_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum xz') + accept = False + while not accept: + mask, y_bounds = draw_mask_1d(tomosum, title='select y data range', + legend='recon stack sum xz') + while len(y_bounds) != 1: + print('Please select exactly one continuous range') + mask, y_bounds = draw_mask_1d(tomosum, title='select y data range', + legend='recon stack sum xz') + y_bounds = list(y_bounds[0]) + quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz') + print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+ + 'exclusive)') + accept = input_yesno('Accept these bounds (y/n)?', 'y') if False and self.test_mode: 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') + clearPlot('recon stack sum xz') # Combine reconstructed tomography stacks logging.info(f'Combining reconstructed stacks ...') @@ -2378,7 +2417,7 @@ filename += ' fullres.dat' else: filename += f' {zoom_perc}p.dat' - msnc.quickPlot(tomosum, title='recon combined sum xy', + quickPlot(tomosum, title='recon combined sum xy', path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) if False: @@ -2404,24 +2443,32 @@ z_bounds[0] = 0 if z_bounds[1] == -1: z_bounds[1] = tomosum.size - if not msnc.is_index_range(z_bounds, 0, tomosum.size): - msnc.illegal_value('z_bounds', z_bounds, 'galaxy input') + if not is_index_range(z_bounds, 0, tomosum.size): + illegal_value(z_bounds, 'z_bounds', 'galaxy input') tomosum_min = tomosum.min() tomosum_max = tomosum.max() - msnc.quickPlot((range(tomosum.size), tomosum), + quickPlot((range(tomosum.size), tomosum), ([z_bounds[0], z_bounds[0]], [tomosum_min, tomosum_max], 'r-'), ([z_bounds[1]-1, z_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), title=f'recon stack sum xy', path='combine_pngs', save_fig=True, save_only=True) else: - msnc.quickPlot(tomosum, title='recon combined sum xy') - if pyip.inputYesNo( - '\nDo you want to change the image z-bounds (y/[n])? ', - blank=True) != 'yes': + if not input_yesno('\nDo you want to change the image z-bounds (y/n)?', 'n'): z_bounds = [0, tomosum.size] else: - z_bounds = msnc.selectArrayBounds(tomosum, title='recon combined sum xy') + accept = False + while not accept: + mask, z_bounds = draw_mask_1d(tomosum, title='select z data range', + legend='recon stack sum xy') + while len(z_bounds) != 1: + print('Please select exactly one continuous range') + mask, z_bounds = draw_mask_1d(tomosum, title='select z data range', + legend='recon stack sum xy') + z_bounds = list(z_bounds[0]) + quickPlot(tomosum, vlines=z_bounds, title='recon stack sum xy') + print(f'z_bounds = {z_bounds} (lower bound inclusive, upper bound exclusive)') + accept = input_yesno('Accept these bounds (y/n)?', 'y') if self.save_plots_only: - msnc.clearFig('recon combined sum xy') + clearPlot('recon combined sum xy') if z_bounds[0] != 0 or z_bounds[1] != tomosum.size: tomo_recon_combined = tomo_recon_combined[z_bounds[0]:z_bounds[1],:,:] @@ -2434,13 +2481,13 @@ path = self.output_folder save_fig = self.save_plots save_only = self.save_plots_only - msnc.quickImshow(tomo_recon_combined[int(tomo_recon_combined.shape[0]/2),:,:], + quickImshow(tomo_recon_combined[int(tomo_recon_combined.shape[0]/2),:,:], title=f'recon combined xslice{int(tomo_recon_combined.shape[0]/2)}', path=path, save_fig=save_fig, save_only=save_only) - msnc.quickImshow(tomo_recon_combined[:,int(tomo_recon_combined.shape[1]/2),:], + quickImshow(tomo_recon_combined[:,int(tomo_recon_combined.shape[1]/2),:], title=f'recon combined yslice{int(tomo_recon_combined.shape[1]/2)}', path=path, save_fig=save_fig, save_only=save_only) - msnc.quickImshow(tomo_recon_combined[:,:,int(tomo_recon_combined.shape[2]/2)], + quickImshow(tomo_recon_combined[:,:,int(tomo_recon_combined.shape[2]/2)], title=f'recon combined zslice{int(tomo_recon_combined.shape[2]/2)}', path=path, save_fig=save_fig, save_only=save_only)