Mercurial > repos > rv43 > tomo
comparison tomo.py @ 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 |
comparison
equal
deleted
inserted
replaced
| 0:cb1b0d757704 | 1:e4778148df6b |
|---|---|
| 16 import io | 16 import io |
| 17 try: | 17 try: |
| 18 import pyinputplus as pyip | 18 import pyinputplus as pyip |
| 19 except: | 19 except: |
| 20 pass | 20 pass |
| 21 import argparse | |
| 21 import numpy as np | 22 import numpy as np |
| 22 import numexpr as ne | 23 import numexpr as ne |
| 23 import multiprocessing as mp | 24 import multiprocessing as mp |
| 24 import scipy.ndimage as spi | 25 import scipy.ndimage as spi |
| 25 import tomopy | 26 import tomopy |
| 29 | 30 |
| 30 import msnc_tools as msnc | 31 import msnc_tools as msnc |
| 31 | 32 |
| 32 class set_numexpr_threads: | 33 class set_numexpr_threads: |
| 33 | 34 |
| 34 def __init__(self, nthreads): | 35 def __init__(self, num_core): |
| 35 cpu_count = mp.cpu_count() | 36 cpu_count = mp.cpu_count() |
| 36 if nthreads is None or nthreads > cpu_count: | 37 if num_core is None or num_core < 1 or num_core > cpu_count: |
| 37 self.n = cpu_count | 38 self.num_core = cpu_count |
| 38 else: | 39 else: |
| 39 self.n = nthreads | 40 self.num_core = num_core |
| 40 | 41 |
| 41 def __enter__(self): | 42 def __enter__(self): |
| 42 self.oldn = ne.set_num_threads(self.n) | 43 self.num_core_org = ne.set_num_threads(self.num_core) |
| 43 | 44 |
| 44 def __exit__(self, exc_type, exc_value, traceback): | 45 def __exit__(self, exc_type, exc_value, traceback): |
| 45 ne.set_num_threads(self.oldn) | 46 ne.set_num_threads(self.num_core_org) |
| 46 | 47 |
| 47 class ConfigTomo(msnc.Config): | 48 class ConfigTomo(msnc.Config): |
| 48 """Class for processing a config file. | 49 """Class for processing a config file. |
| 49 """ | 50 """ |
| 50 | 51 |
| 208 is_valid = self._validate_yaml() | 209 is_valid = self._validate_yaml() |
| 209 elif self.suffix == '.txt': | 210 elif self.suffix == '.txt': |
| 210 is_valid = self._validate_txt() | 211 is_valid = self._validate_txt() |
| 211 else: | 212 else: |
| 212 logging.error(f'Undefined or illegal config file extension: {self.suffix}') | 213 logging.error(f'Undefined or illegal config file extension: {self.suffix}') |
| 214 is_valid = False | |
| 213 | 215 |
| 214 # Find tomography bright field images file/folder | 216 # Find tomography bright field images file/folder |
| 215 if self.tdf_data_path: | 217 if self.tdf_data_path: |
| 216 if self.data_filetype == 'h5': | 218 if self.data_filetype == 'h5': |
| 217 if isinstance(self.tdf_data_path, str): | 219 if isinstance(self.tdf_data_path, str): |
| 376 class Tomo: | 378 class Tomo: |
| 377 """Processing tomography data with misalignment. | 379 """Processing tomography data with misalignment. |
| 378 """ | 380 """ |
| 379 | 381 |
| 380 def __init__(self, config_file=None, config_dict=None, config_out=None, output_folder='.', | 382 def __init__(self, config_file=None, config_dict=None, config_out=None, output_folder='.', |
| 381 log_level='INFO', log_stream='tomo.log', galaxy_flag=False, test_mode=False): | 383 log_level='INFO', log_stream='tomo.log', galaxy_flag=False, test_mode=False, |
| 384 num_core=-1): | |
| 382 """Initialize with optional config input file or dictionary | 385 """Initialize with optional config input file or dictionary |
| 383 """ | 386 """ |
| 384 self.ncore = mp.cpu_count() | 387 self.num_core = None |
| 385 self.config_out = config_out | 388 self.config_out = config_out |
| 386 self.output_folder = output_folder | 389 self.output_folder = output_folder |
| 387 self.galaxy_flag = galaxy_flag | 390 self.galaxy_flag = galaxy_flag |
| 388 self.test_mode = test_mode | 391 self.test_mode = test_mode |
| 389 self.save_plots = True # Make input argument? | 392 self.save_plots = True # Make input argument? |
| 419 log_stream = f'{output_folder}/{log_stream}' | 422 log_stream = f'{output_folder}/{log_stream}' |
| 420 if not isinstance(galaxy_flag, bool): | 423 if not isinstance(galaxy_flag, bool): |
| 421 raise ValueError(f'Invalid galaxy_flag input {galaxy_flag} {type(galaxy_flag)}') | 424 raise ValueError(f'Invalid galaxy_flag input {galaxy_flag} {type(galaxy_flag)}') |
| 422 if not isinstance(test_mode, bool): | 425 if not isinstance(test_mode, bool): |
| 423 raise ValueError(f'Invalid test_mode input {test_mode} {type(test_mode)}') | 426 raise ValueError(f'Invalid test_mode input {test_mode} {type(test_mode)}') |
| 427 if not isinstance(num_core, int) or num_core < -1 or num_core == 0: | |
| 428 raise ValueError(f'Invalid num_core input {num_core} {type(num_core)}') | |
| 429 if num_core == -1: | |
| 430 self.num_core = mp.cpu_count() | |
| 431 else: | |
| 432 self.num_core = num_core | |
| 424 | 433 |
| 425 # Set log configuration | 434 # Set log configuration |
| 426 logging_format = '%(asctime)s : %(levelname)s - %(module)s : %(funcName)s - %(message)s' | 435 logging_format = '%(asctime)s : %(levelname)s - %(module)s : %(funcName)s - %(message)s' |
| 427 if self.test_mode: | 436 if self.test_mode: |
| 428 self.save_plots_only = True | 437 self.save_plots_only = True |
| 429 if isinstance(log_stream, str): | 438 if isinstance(log_stream, str): |
| 430 logging.basicConfig(filename=f'{log_stream}', filemode='w', | 439 logging.basicConfig(filename=f'{log_stream}', filemode='w', |
| 431 format=logging_format, level=logging.WARNING, force=True) | 440 format=logging_format, level=logging.INFO, force=True) |
| 441 #format=logging_format, level=logging.WARNING, force=True) | |
| 432 elif isinstance(log_stream, io.TextIOWrapper): | 442 elif isinstance(log_stream, io.TextIOWrapper): |
| 433 logging.basicConfig(filemode='w', format=logging_format, level=logging.WARNING, | 443 #logging.basicConfig(filemode='w', format=logging_format, level=logging.WARNING, |
| 444 logging.basicConfig(filemode='w', format=logging_format, level=logging.INFO, | |
| 434 stream=log_stream, force=True) | 445 stream=log_stream, force=True) |
| 435 else: | 446 else: |
| 436 raise ValueError(f'Invalid log_stream: {log_stream}') | 447 raise ValueError(f'Invalid log_stream: {log_stream}') |
| 437 logging.warning('Ignoring log_level argument in test mode') | 448 logging.warning('Ignoring log_level argument in test mode') |
| 438 else: | 449 else: |
| 461 self.config_out = f'{self.output_folder}/config.yaml' | 472 self.config_out = f'{self.output_folder}/config.yaml' |
| 462 elif (self.config_out is os.path.basename(self.config_out) and | 473 elif (self.config_out is os.path.basename(self.config_out) and |
| 463 not os.path.isabs(self.config_out)): | 474 not os.path.isabs(self.config_out)): |
| 464 self.config_out = f'{self.output_folder}/{self.config_out}' | 475 self.config_out = f'{self.output_folder}/{self.config_out}' |
| 465 | 476 |
| 466 logging.info(f'ncore = {self.ncore}') | 477 # Create config object and load config file |
| 478 self.cf = ConfigTomo(config_file, config_dict) | |
| 479 if not self.cf.load_flag: | |
| 480 self.is_valid = False | |
| 481 return | |
| 482 | |
| 483 if self.galaxy_flag: | |
| 484 self.num_core = 1 #RV can I set this? mp.cpu_count() | |
| 485 assert(self.output_folder == '.') | |
| 486 assert(self.test_mode is False) | |
| 487 self.save_plots = True | |
| 488 self.save_plots_only = True | |
| 489 else: | |
| 490 # Input validation is already performed during link_data_to_galaxy | |
| 491 | |
| 492 # Check config file parameters | |
| 493 self.is_valid = self.cf.validate() | |
| 494 | |
| 495 # Load detector info file | |
| 496 df = msnc.Detector(self.cf.config['detector']['id']) | |
| 497 | |
| 498 # Check detector info file parameters | |
| 499 if df.validate(): | |
| 500 pixel_size = df.getPixelSize() | |
| 501 num_rows, num_columns = df.getDimensions() | |
| 502 if not pixel_size or not num_rows or not num_columns: | |
| 503 self.is_valid = False | |
| 504 else: | |
| 505 pixel_size = None | |
| 506 num_rows = None | |
| 507 num_columns = None | |
| 508 self.is_valid = False | |
| 509 | |
| 510 # Update config | |
| 511 self.cf.config['detector']['pixel_size'] = pixel_size | |
| 512 self.cf.config['detector']['rows'] = num_rows | |
| 513 self.cf.config['detector']['columns'] = num_columns | |
| 514 logging.debug(f'pixel_size = self.cf.config["detector"]["pixel_size"]') | |
| 515 logging.debug(f'num_rows: {self.cf.config["detector"]["rows"]}') | |
| 516 logging.debug(f'num_columns: {self.cf.config["detector"]["columns"]}') | |
| 517 | |
| 518 # Safe config to file | |
| 519 if self.is_valid: | |
| 520 self.cf.saveFile(self.config_out) | |
| 521 | |
| 522 # Initialize shortcut to config | |
| 523 self.config = self.cf.config | |
| 524 | |
| 525 # Initialize tomography stack | |
| 526 num_tomo_stacks = self.config['stack_info']['num'] | |
| 527 if num_tomo_stacks: | |
| 528 self.tomo_stacks = [np.array([]) for _ in range(num_tomo_stacks)] | |
| 529 self.tomo_recon_stacks = [np.array([]) for _ in range(num_tomo_stacks)] | |
| 530 | |
| 531 logging.info(f'num_core = {self.num_core}') | |
| 467 logging.debug(f'config_file = {config_file}') | 532 logging.debug(f'config_file = {config_file}') |
| 468 logging.debug(f'config_dict = {config_dict}') | 533 logging.debug(f'config_dict = {config_dict}') |
| 469 logging.debug(f'config_out = {self.config_out}') | 534 logging.debug(f'config_out = {self.config_out}') |
| 470 logging.debug(f'output_folder = {self.output_folder}') | 535 logging.debug(f'output_folder = {self.output_folder}') |
| 471 logging.debug(f'log_stream = {log_stream}') | 536 logging.debug(f'log_stream = {log_stream}') |
| 472 logging.debug(f'log_level = {log_level}') | 537 logging.debug(f'log_level = {log_level}') |
| 473 logging.debug(f'galaxy_flag = {self.galaxy_flag}') | 538 logging.debug(f'galaxy_flag = {self.galaxy_flag}') |
| 474 logging.debug(f'test_mode = {self.test_mode}') | 539 logging.debug(f'test_mode = {self.test_mode}') |
| 475 | |
| 476 # Create config object and load config file | |
| 477 self.cf = ConfigTomo(config_file, config_dict) | |
| 478 if not self.cf.load_flag: | |
| 479 self.is_valid = False | |
| 480 return | |
| 481 | |
| 482 if self.galaxy_flag: | |
| 483 self.ncore = 1 #RV can I set this? mp.cpu_count() | |
| 484 assert(self.output_folder == '.') | |
| 485 assert(self.test_mode is False) | |
| 486 self.save_plots = True | |
| 487 self.save_plots_only = True | |
| 488 else: | |
| 489 # Input validation is already performed during link_data_to_galaxy | |
| 490 | |
| 491 # Check config file parameters | |
| 492 self.is_valid = self.cf.validate() | |
| 493 | |
| 494 # Load detector info file | |
| 495 df = msnc.Detector(self.cf.config['detector']['id']) | |
| 496 | |
| 497 # Check detector info file parameters | |
| 498 if df.validate(): | |
| 499 pixel_size = df.getPixelSize() | |
| 500 num_rows, num_columns = df.getDimensions() | |
| 501 if not pixel_size or not num_rows or not num_columns: | |
| 502 self.is_valid = False | |
| 503 else: | |
| 504 pixel_size = None | |
| 505 num_rows = None | |
| 506 num_columns = None | |
| 507 self.is_valid = False | |
| 508 | |
| 509 # Update config | |
| 510 self.cf.config['detector']['pixel_size'] = pixel_size | |
| 511 self.cf.config['detector']['rows'] = num_rows | |
| 512 self.cf.config['detector']['columns'] = num_columns | |
| 513 logging.debug(f'pixel_size = self.cf.config["detector"]["pixel_size"]') | |
| 514 logging.debug(f'num_rows: {self.cf.config["detector"]["rows"]}') | |
| 515 logging.debug(f'num_columns: {self.cf.config["detector"]["columns"]}') | |
| 516 | |
| 517 # Safe config to file | |
| 518 if self.is_valid: | |
| 519 self.cf.saveFile(self.config_out) | |
| 520 | |
| 521 # Initialize shortcut to config | |
| 522 self.config = self.cf.config | |
| 523 | |
| 524 # Initialize tomography stack | |
| 525 num_tomo_stacks = self.config['stack_info']['num'] | |
| 526 if num_tomo_stacks: | |
| 527 self.tomo_stacks = [np.array([]) for _ in range(num_tomo_stacks)] | |
| 528 self.tomo_recon_stacks = [np.array([]) for _ in range(num_tomo_stacks)] | |
| 529 | |
| 530 logging.debug(f'save_plots = {self.save_plots}') | 540 logging.debug(f'save_plots = {self.save_plots}') |
| 531 logging.debug(f'save_plots_only = {self.save_plots_only}') | 541 logging.debug(f'save_plots_only = {self.save_plots_only}') |
| 532 | 542 |
| 533 def _selectImageRanges(self, available_stacks=None): | 543 def _selectImageRanges(self, available_stacks=None): |
| 534 """Select image files to be included in analysis. | 544 """Select image files to be included in analysis. |
| 580 if not self.test_mode: | 590 if not self.test_mode: |
| 581 img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, | 591 img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, |
| 582 num_imgs, 'bright field') | 592 num_imgs, 'bright field') |
| 583 if img_start < 0 or num_imgs < 1: | 593 if img_start < 0 or num_imgs < 1: |
| 584 logging.error('Unable to find suitable bright field images') | 594 logging.error('Unable to find suitable bright field images') |
| 585 if bright_field['data_path']: | 595 self.is_valid = False |
| 586 self.is_valid = False | |
| 587 bright_field['img_start'] = img_start | 596 bright_field['img_start'] = img_start |
| 588 bright_field['img_offset'] = img_offset | 597 bright_field['img_offset'] = img_offset |
| 589 bright_field['num'] = num_imgs | 598 bright_field['num'] = num_imgs |
| 590 logging.debug(f'Bright field image start index: {bright_field["img_start"]}') | 599 logging.debug(f'Bright field image start index: {bright_field["img_start"]}') |
| 591 logging.debug(f'Bright field image offset: {bright_field["img_offset"]}') | 600 logging.debug(f'Bright field image offset: {bright_field["img_offset"]}') |
| 610 stack['img_offset'] = img_offset | 619 stack['img_offset'] = img_offset |
| 611 stack['num'] = num_imgs | 620 stack['num'] = num_imgs |
| 612 logging.debug(f'Tomography stack {index} image start index: {stack["img_start"]}') | 621 logging.debug(f'Tomography stack {index} image start index: {stack["img_start"]}') |
| 613 logging.debug(f'Tomography stack {index} image offset: {stack["img_offset"]}') | 622 logging.debug(f'Tomography stack {index} image offset: {stack["img_offset"]}') |
| 614 logging.debug(f'Number of tomography images for stack {index}: {stack["num"]}') | 623 logging.debug(f'Number of tomography images for stack {index}: {stack["num"]}') |
| 615 available_stacks[i] = True | |
| 616 | 624 |
| 617 # Safe updated config to file | 625 # Safe updated config to file |
| 618 if self.is_valid: | 626 if self.is_valid: |
| 619 self.cf.saveFile(self.config_out) | 627 self.cf.saveFile(self.config_out) |
| 620 | 628 |
| 687 preprocess = self.config.get('preprocess') | 695 preprocess = self.config.get('preprocess') |
| 688 if preprocess is None: | 696 if preprocess is None: |
| 689 img_x_bounds = [None, None] | 697 img_x_bounds = [None, None] |
| 690 else: | 698 else: |
| 691 img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) | 699 img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) |
| 700 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: | |
| 701 if img_x_bounds[0] < 0: | |
| 702 msnc.illegal_value('img_x_bounds[0]', img_x_bounds[0], 'config file') | |
| 703 img_x_bounds[0] = 0 | |
| 704 if not msnc.is_index_range(img_x_bounds, 0, self.tbf.shape[0]): | |
| 705 msnc.illegal_value('img_x_bounds[1]', img_x_bounds[1], 'config file') | |
| 706 img_x_bounds[1] = self.tbf.shape[0] | |
| 692 if self.test_mode: | 707 if self.test_mode: |
| 693 # Update config and save to file | 708 # Update config and save to file |
| 694 if preprocess is None: | 709 if preprocess is None: |
| 695 self.cf.config['preprocess'] = {'img_x_bounds' : [0, self.tbf.shape[0]]} | 710 self.cf.config['preprocess'] = {'img_x_bounds' : [0, self.tbf.shape[0]]} |
| 696 else: | 711 else: |
| 761 tomo_or_bright = 0 | 776 tomo_or_bright = 0 |
| 762 if tomo_or_bright: | 777 if tomo_or_bright: |
| 763 x_sum = np.sum(tomo_stack[0,:,:], 1) | 778 x_sum = np.sum(tomo_stack[0,:,:], 1) |
| 764 use_bounds = 'no' | 779 use_bounds = 'no' |
| 765 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: | 780 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: |
| 766 if img_x_bounds[0] < 0: | |
| 767 msnc.illegal_value('img_x_bounds[0]', img_x_bounds[0], 'config file') | |
| 768 img_x_bounds[0] = 0 | |
| 769 if not img_x_bounds[0] < img_x_bounds[1] <= x_sum.size: | |
| 770 msnc.illegal_value('img_x_bounds[1]', img_x_bounds[1], 'config file') | |
| 771 img_x_bounds[1] = x_sum.size | |
| 772 tomo_tmp = tomo_stack[0,:,:] | |
| 773 tomo_tmp[img_x_bounds[0],:] = tomo_stack[0,:,:].max() | |
| 774 tomo_tmp[img_x_bounds[1]-1,:] = tomo_stack[0,:,:].max() | |
| 775 title = f'tomography image at theta={self.config["theta_range"]["start"]}' | |
| 776 msnc.quickImshow(tomo_stack[0,:,:], title=title) | |
| 777 msnc.quickPlot((range(x_sum.size), x_sum), | 781 msnc.quickPlot((range(x_sum.size), x_sum), |
| 778 ([img_x_bounds[0], img_x_bounds[0]], [x_sum.min(), x_sum.max()], 'r-'), | 782 ([img_x_bounds[0], img_x_bounds[0]], [x_sum.min(), x_sum.max()], 'r-'), |
| 779 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum.min(), x_sum.max()], 'r-'), | 783 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum.min(), x_sum.max()], 'r-'), |
| 780 title='sum over theta and y') | 784 title='sum over theta and y') |
| 781 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ | 785 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ |
| 785 img_x_bounds = msnc.selectImageBounds(tomo_stack[0,:,:], 0, | 789 img_x_bounds = msnc.selectImageBounds(tomo_stack[0,:,:], 0, |
| 786 img_x_bounds[0], img_x_bounds[1], num_x_min, | 790 img_x_bounds[0], img_x_bounds[1], num_x_min, |
| 787 f'tomography image at theta={self.config["theta_range"]["start"]}') | 791 f'tomography image at theta={self.config["theta_range"]["start"]}') |
| 788 if num_x_min is not None and img_x_bounds[1]-img_x_bounds[0]+1 < num_x_min: | 792 if num_x_min is not None and img_x_bounds[1]-img_x_bounds[0]+1 < num_x_min: |
| 789 logging.warning('Image bounds and pixel size prevent seamless stacking') | 793 logging.warning('Image bounds and pixel size prevent seamless stacking') |
| 790 tomo_tmp = tomo_stack[0,:,:] | |
| 791 tomo_tmp[img_x_bounds[0],:] = tomo_stack[0,:,:].max() | |
| 792 tomo_tmp[img_x_bounds[1]-1,:] = tomo_stack[0,:,:].max() | |
| 793 title = f'tomography image at theta={self.config["theta_range"]["start"]}' | 794 title = f'tomography image at theta={self.config["theta_range"]["start"]}' |
| 794 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, | 795 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, |
| 795 save_fig=self.save_plots, save_only=True) | 796 save_fig=self.save_plots, save_only=True) |
| 796 msnc.quickPlot(range(img_x_bounds[0], img_x_bounds[1]), | 797 msnc.quickPlot(range(img_x_bounds[0], img_x_bounds[1]), |
| 797 x_sum[img_x_bounds[0]:img_x_bounds[1]], | 798 x_sum[img_x_bounds[0]:img_x_bounds[1]], |
| 799 save_fig=self.save_plots, save_only=True) | 800 save_fig=self.save_plots, save_only=True) |
| 800 else: | 801 else: |
| 801 x_sum = np.sum(self.tbf, 1) | 802 x_sum = np.sum(self.tbf, 1) |
| 802 use_bounds = 'no' | 803 use_bounds = 'no' |
| 803 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: | 804 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: |
| 804 if img_x_bounds[0] < 0: | |
| 805 msnc.illegal_value('img_x_bounds[0]', img_x_bounds[0], 'config file') | |
| 806 img_x_bounds[0] = 0 | |
| 807 if not img_x_bounds[0] < img_x_bounds[1] <= x_sum.size: | |
| 808 msnc.illegal_value('img_x_bounds[1]', img_x_bounds[1], 'config file') | |
| 809 img_x_bounds[1] = x_sum.size | |
| 810 msnc.quickPlot((range(x_sum.size), x_sum), | 805 msnc.quickPlot((range(x_sum.size), x_sum), |
| 811 ([img_x_bounds[0], img_x_bounds[0]], [x_sum.min(), x_sum.max()], 'r-'), | 806 ([img_x_bounds[0], img_x_bounds[0]], [x_sum.min(), x_sum.max()], 'r-'), |
| 812 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum.min(), x_sum.max()], 'r-'), | 807 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum.min(), x_sum.max()], 'r-'), |
| 813 title='sum over theta and y') | 808 title='sum over theta and y') |
| 814 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ | 809 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ |
| 971 | 966 |
| 972 preprocess = self.config.get('preprocess') | 967 preprocess = self.config.get('preprocess') |
| 973 if preprocess is None: | 968 if preprocess is None: |
| 974 img_x_bounds = [0, self.tbf.shape[0]] | 969 img_x_bounds = [0, self.tbf.shape[0]] |
| 975 img_y_bounds = [0, self.tbf.shape[1]] | 970 img_y_bounds = [0, self.tbf.shape[1]] |
| 976 zoom_perc = preprocess['zoom_perc'] | 971 zoom_perc = 100 |
| 977 num_theta_skip = preprocess['num_theta_skip'] | 972 num_theta_skip = 0 |
| 978 else: | 973 else: |
| 979 img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) | 974 img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) |
| 980 img_y_bounds = preprocess.get('img_y_bounds', [0, self.tbf.shape[1]]) | 975 img_y_bounds = preprocess.get('img_y_bounds', [0, self.tbf.shape[1]]) |
| 981 zoom_perc = 100 | 976 zoom_perc = preprocess.get('zoom_perc', 100) |
| 982 num_theta_skip = 0 | 977 num_theta_skip = preprocess.get('num_theta_skip', 0) |
| 983 | 978 |
| 984 if self.tdf.size: | 979 if self.tdf.size: |
| 985 tdf = self.tdf[img_x_bounds[0]:img_x_bounds[1],img_y_bounds[0]:img_y_bounds[1]] | 980 tdf = self.tdf[img_x_bounds[0]:img_x_bounds[1],img_y_bounds[0]:img_y_bounds[1]] |
| 986 else: | 981 else: |
| 987 logging.warning('Dark field unavailable') | 982 logging.warning('Dark field unavailable') |
| 993 # Check if stack is already loaded or available | 988 # Check if stack is already loaded or available |
| 994 if self.tomo_stacks[i].size or available_stacks[i]: | 989 if self.tomo_stacks[i].size or available_stacks[i]: |
| 995 continue | 990 continue |
| 996 | 991 |
| 997 # Load a stack of tomography images | 992 # Load a stack of tomography images |
| 993 index = stack['index'] | |
| 998 t0 = time() | 994 t0 = time() |
| 999 tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], | 995 tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], |
| 1000 stack['img_offset'], self.config['theta_range']['num'], num_theta_skip, | 996 stack['img_offset'], self.config['theta_range']['num'], num_theta_skip, |
| 1001 img_x_bounds, img_y_bounds) | 997 img_x_bounds, img_y_bounds) |
| 1002 tomo_stack = tomo_stack.astype('float64') | 998 tomo_stack = tomo_stack.astype('float64') |
| 1003 logging.debug(f'loading took {time()-t0:.2f} seconds!') | 999 logging.debug(f'loading stack {index} took {time()-t0:.2f} seconds!') |
| 1004 | 1000 |
| 1005 # Subtract dark field | 1001 # Subtract dark field |
| 1006 if self.tdf.size: | 1002 if self.tdf.size: |
| 1007 t0 = time() | 1003 t0 = time() |
| 1008 with set_numexpr_threads(self.ncore): | 1004 with set_numexpr_threads(self.num_core): |
| 1009 ne.evaluate('tomo_stack-tdf', out=tomo_stack) | 1005 ne.evaluate('tomo_stack-tdf', out=tomo_stack) |
| 1010 logging.debug(f'subtracting dark field took {time()-t0:.2f} seconds!') | 1006 logging.debug(f'subtracting dark field took {time()-t0:.2f} seconds!') |
| 1011 | 1007 |
| 1012 # Normalize | 1008 # Normalize |
| 1013 t0 = time() | 1009 t0 = time() |
| 1014 with set_numexpr_threads(self.ncore): | 1010 with set_numexpr_threads(self.num_core): |
| 1015 ne.evaluate('tomo_stack/tbf', out=tomo_stack, truediv=True) | 1011 ne.evaluate('tomo_stack/tbf', out=tomo_stack, truediv=True) |
| 1016 logging.debug(f'normalizing took {time()-t0:.2f} seconds!') | 1012 logging.debug(f'normalizing took {time()-t0:.2f} seconds!') |
| 1017 | 1013 |
| 1018 # Remove non-positive values and linearize data | 1014 # Remove non-positive values and linearize data |
| 1019 t0 = time() | 1015 t0 = time() |
| 1020 cutoff = 1.e-6 | 1016 cutoff = 1.e-6 |
| 1021 with set_numexpr_threads(self.ncore): | 1017 with set_numexpr_threads(self.num_core): |
| 1022 ne.evaluate('where(tomo_stack<cutoff, cutoff, tomo_stack)', out=tomo_stack) | 1018 ne.evaluate('where(tomo_stack<cutoff, cutoff, tomo_stack)', out=tomo_stack) |
| 1023 with set_numexpr_threads(self.ncore): | 1019 with set_numexpr_threads(self.num_core): |
| 1024 ne.evaluate('-log(tomo_stack)', out=tomo_stack) | 1020 ne.evaluate('-log(tomo_stack)', out=tomo_stack) |
| 1025 logging.debug('removing non-positive values and linearizing data took '+ | 1021 logging.debug('removing non-positive values and linearizing data took '+ |
| 1026 f'{time()-t0:.2f} seconds!') | 1022 f'{time()-t0:.2f} seconds!') |
| 1027 | 1023 |
| 1028 # Get rid of nans/infs that may be introduced by normalization | 1024 # Get rid of nans/infs that may be introduced by normalization |
| 1031 logging.debug(f'remove nans/infs took {time()-t0:.2f} seconds!') | 1027 logging.debug(f'remove nans/infs took {time()-t0:.2f} seconds!') |
| 1032 | 1028 |
| 1033 # Downsize tomography stack to smaller size | 1029 # Downsize tomography stack to smaller size |
| 1034 tomo_stack = tomo_stack.astype('float32') | 1030 tomo_stack = tomo_stack.astype('float32') |
| 1035 if not self.galaxy_flag: | 1031 if not self.galaxy_flag: |
| 1036 index = stack['index'] | |
| 1037 title = f'red stack fullres {index}' | 1032 title = f'red stack fullres {index}' |
| 1038 if not self.test_mode: | 1033 if not self.test_mode: |
| 1039 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, | 1034 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, |
| 1040 save_fig=self.save_plots, save_only=self.save_plots_only) | 1035 save_fig=self.save_plots, save_only=self.save_plots_only) |
| 1041 if zoom_perc != 100: | 1036 if zoom_perc != 100: |
| 1051 if not self.galaxy_flag: | 1046 if not self.galaxy_flag: |
| 1052 title = f'red stack {zoom_perc}p {index}' | 1047 title = f'red stack {zoom_perc}p {index}' |
| 1053 if not self.test_mode: | 1048 if not self.test_mode: |
| 1054 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, | 1049 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, |
| 1055 save_fig=self.save_plots, save_only=self.save_plots_only) | 1050 save_fig=self.save_plots, save_only=self.save_plots_only) |
| 1056 | 1051 |
| 1057 # Convert tomography stack from theta,row,column to row,theta,column | 1052 # Convert tomography stack from theta,row,column to row,theta,column |
| 1058 tomo_stack = np.swapaxes(tomo_stack, 0, 1) | 1053 tomo_stack = np.swapaxes(tomo_stack, 0, 1) |
| 1059 | 1054 |
| 1060 # Save tomography stack to file | 1055 # Save tomography stack to file |
| 1061 if not self.galaxy_flag: | 1056 if not self.galaxy_flag: |
| 1062 if not self.test_mode: | 1057 if not self.test_mode: |
| 1063 self._saveTomo('red stack', tomo_stack, index) | 1058 self._saveTomo('red stack', tomo_stack, index) |
| 1064 else: | 1059 else: |
| 1065 np.savetxt(self.output_folder+f'red_stack_{index}.txt', | 1060 np.savetxt(f'{self.output_folder}/red_stack_{index}.txt', |
| 1066 tomo_stack[0,:,:], fmt='%.6e') | 1061 tomo_stack[0,:,:], fmt='%.6e') |
| 1067 | 1062 |
| 1068 # Combine stacks | 1063 # Combine stacks |
| 1069 t0 = time() | 1064 t0 = time() |
| 1070 self.tomo_stacks[i] = tomo_stack | 1065 self.tomo_stacks[i] = tomo_stack |
| 1071 logging.debug(f'combining nstack took {time()-t0:.2f} seconds!') | 1066 logging.debug(f'combining nstack took {time()-t0:.2f} seconds!') |
| 1072 | 1067 |
| 1073 # Update config and save to file | 1068 # Update config and save to file |
| 1074 stack['preprocessed'] = True | 1069 stack['preprocessed'] = True |
| 1070 stack.pop('reconstructed', 'reconstructed not found') | |
| 1071 find_center = self.config.get('find_center') | |
| 1072 if find_center: | |
| 1073 if self.test_mode: | |
| 1074 find_center.pop('completed', 'completed not found') | |
| 1075 find_center.pop('lower_center_offset', 'lower_center_offset not found') | |
| 1076 find_center.pop('upper_center_offset', 'upper_center_offset not found') | |
| 1077 else: | |
| 1078 if find_center.get('center_stack_index', -1) == index: | |
| 1079 self.config.pop('find_center') | |
| 1075 self.cf.saveFile(self.config_out) | 1080 self.cf.saveFile(self.config_out) |
| 1076 | 1081 |
| 1077 if self.tdf.size: | 1082 if self.tdf.size: |
| 1078 del tdf | 1083 del tdf |
| 1079 del tbf | 1084 del tbf |
| 1093 logging.debug(f'sinogram range = [{two_offset+dist_from_edge}, {-dist_from_edge}]') | 1098 logging.debug(f'sinogram range = [{two_offset+dist_from_edge}, {-dist_from_edge}]') |
| 1094 sinogram = tomo_plane_T[two_offset+dist_from_edge:-dist_from_edge,:] | 1099 sinogram = tomo_plane_T[two_offset+dist_from_edge:-dist_from_edge,:] |
| 1095 else: | 1100 else: |
| 1096 logging.debug(f'sinogram range = [{dist_from_edge}, {two_offset-dist_from_edge}]') | 1101 logging.debug(f'sinogram range = [{dist_from_edge}, {two_offset-dist_from_edge}]') |
| 1097 sinogram = tomo_plane_T[dist_from_edge:two_offset-dist_from_edge,:] | 1102 sinogram = tomo_plane_T[dist_from_edge:two_offset-dist_from_edge,:] |
| 1098 if plot_sinogram: | 1103 if plot_sinogram and not self.test_mode: |
| 1099 msnc.quickImshow(sinogram.T, f'sinogram center offset{center_offset:.2f}', | 1104 msnc.quickImshow(sinogram.T, f'sinogram center offset{center_offset:.2f}', |
| 1100 path=self.output_folder, save_fig=self.save_plots, | 1105 path=self.output_folder, save_fig=self.save_plots, |
| 1101 save_only=self.save_plots_only, aspect='auto') | 1106 save_only=self.save_plots_only, aspect='auto') |
| 1102 | 1107 |
| 1103 # Inverting sinogram | 1108 # Inverting sinogram |
| 1139 center = sinogram.shape[1]/2 | 1144 center = sinogram.shape[1]/2 |
| 1140 | 1145 |
| 1141 # try automatic center finding routines for initial value | 1146 # try automatic center finding routines for initial value |
| 1142 tomo_center = tomopy.find_center_vo(sinogram) | 1147 tomo_center = tomopy.find_center_vo(sinogram) |
| 1143 center_offset_vo = tomo_center-center | 1148 center_offset_vo = tomo_center-center |
| 1144 print(f'Center at row {row} using Nghia Vo’s method = {center_offset_vo:.2f}') | 1149 if not self.test_mode: |
| 1150 print(f'Center at row {row} using Nghia Vo’s method = {center_offset_vo:.2f}') | |
| 1145 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, | 1151 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, |
| 1146 eff_pixel_size, cross_sectional_dim, False) | 1152 eff_pixel_size, cross_sectional_dim, False) |
| 1147 base_name=f'edges row{row} center_offset_vo{center_offset_vo:.2f}' | 1153 if not self.test_mode: |
| 1148 self._plotEdgesOnePlane(recon_plane, base_name) | 1154 base_name=f'edges row{row} center_offset_vo{center_offset_vo:.2f}' |
| 1149 if pyip.inputYesNo('Try finding center using phase correlation (y/[n])? ', | 1155 self._plotEdgesOnePlane(recon_plane, base_name) |
| 1150 blank=True) == 'yes': | 1156 use_phase_corr = 'no' |
| 1157 if not self.test_mode: | |
| 1158 use_phase_corr = pyip.inputYesNo('Try finding center using phase correlation '+ | |
| 1159 '(y/[n])? ', blank=True) | |
| 1160 if use_phase_corr == 'yes': | |
| 1151 tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1, | 1161 tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1, |
| 1152 rotc_guess=tomo_center) | 1162 rotc_guess=tomo_center) |
| 1153 error = 1. | 1163 error = 1. |
| 1154 while error > tol: | 1164 while error > tol: |
| 1155 prev = tomo_center | 1165 prev = tomo_center |
| 1160 print(f'Center at row {row} using phase correlation = {center_offset:.2f}') | 1170 print(f'Center at row {row} using phase correlation = {center_offset:.2f}') |
| 1161 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, | 1171 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, |
| 1162 eff_pixel_size, cross_sectional_dim, False) | 1172 eff_pixel_size, cross_sectional_dim, False) |
| 1163 base_name=f'edges row{row} center_offset{center_offset:.2f}' | 1173 base_name=f'edges row{row} center_offset{center_offset:.2f}' |
| 1164 self._plotEdgesOnePlane(recon_plane, base_name) | 1174 self._plotEdgesOnePlane(recon_plane, base_name) |
| 1165 if pyip.inputYesNo('Accept a center location ([y]) or continue search (n)? ', | 1175 accept_center = 'yes' |
| 1166 blank=True) != 'no': | 1176 if not self.test_mode: |
| 1177 accept_center = pyip.inputYesNo('Accept a center location ([y]) or continue '+ | |
| 1178 'search (n)? ', blank=True) | |
| 1179 if accept_center != 'no': | |
| 1167 del sinogram_T | 1180 del sinogram_T |
| 1168 del recon_plane | 1181 del recon_plane |
| 1169 center_offset = pyip.inputNum( | 1182 if self.test_mode: |
| 1170 f' Enter chosen center offset [{-int(center)}, {int(center)}] '+ | |
| 1171 f'([{center_offset_vo}])): ', blank=True) | |
| 1172 if center_offset == '': | |
| 1173 center_offset = center_offset_vo | 1183 center_offset = center_offset_vo |
| 1184 else: | |
| 1185 center_offset = pyip.inputNum( | |
| 1186 f' Enter chosen center offset [{-int(center)}, {int(center)}] '+ | |
| 1187 f'([{center_offset_vo}])): ', blank=True) | |
| 1188 if center_offset == '': | |
| 1189 center_offset = center_offset_vo | |
| 1174 return float(center_offset) | 1190 return float(center_offset) |
| 1175 | 1191 |
| 1176 while True: | 1192 while True: |
| 1177 center_offset_low = pyip.inputInt('\nEnter lower bound for center offset '+ | 1193 center_offset_low = pyip.inputInt('\nEnter lower bound for center offset '+ |
| 1178 f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) | 1194 f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) |
| 1200 center_offset = pyip.inputNum(f' Enter chosen center offset '+ | 1216 center_offset = pyip.inputNum(f' Enter chosen center offset '+ |
| 1201 f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) | 1217 f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) |
| 1202 return float(center_offset) | 1218 return float(center_offset) |
| 1203 | 1219 |
| 1204 def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, | 1220 def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, |
| 1205 center_offsets=[], sigma=0.1, rwidth=30, ncore=1, algorithm='gridrec', | 1221 center_offsets=[], sigma=0.1, rwidth=30, num_core=1, algorithm='gridrec', |
| 1206 run_secondary_sirt=False, secondary_iter=100): | 1222 run_secondary_sirt=False, secondary_iter=100): |
| 1207 """reconstruct a single tomography stack. | 1223 """reconstruct a single tomography stack. |
| 1208 """ | 1224 """ |
| 1209 # stack order: row,theta,column | 1225 # stack order: row,theta,column |
| 1210 # thetas must be in radians | 1226 # thetas must be in radians |
| 1215 # https://tomopy.readthedocs.io/en/latest/api/tomopy.misc.corr.html | 1231 # https://tomopy.readthedocs.io/en/latest/api/tomopy.misc.corr.html |
| 1216 # RV: Add an option to do (extra) secondary iterations later or to do some sort of convergence test? | 1232 # RV: Add an option to do (extra) secondary iterations later or to do some sort of convergence test? |
| 1217 if row_bounds is None: | 1233 if row_bounds is None: |
| 1218 row_bounds = [0, tomo_stack.shape[0]] | 1234 row_bounds = [0, tomo_stack.shape[0]] |
| 1219 else: | 1235 else: |
| 1220 if not (0 <= row_bounds[0] <= row_bounds[1] <= tomo_stack.shape[0]): | 1236 if not msnc.is_index_range(row_bounds, 0, tomo_stack.shape[0]): |
| 1221 raise ValueError('Illegal row bounds in reconstructOneTomoStack') | 1237 raise ValueError('Illegal row bounds in reconstructOneTomoStack') |
| 1222 if thetas.size != tomo_stack.shape[1]: | 1238 if thetas.size != tomo_stack.shape[1]: |
| 1223 raise ValueError('theta dimension mismatch in reconstructOneTomoStack') | 1239 raise ValueError('theta dimension mismatch in reconstructOneTomoStack') |
| 1224 if not len(center_offsets): | 1240 if not len(center_offsets): |
| 1225 centers = np.zeros((row_bounds[1]-row_bounds[0])) | 1241 centers = np.zeros((row_bounds[1]-row_bounds[0])) |
| 1230 if center_offsets.size != row_bounds[1]-row_bounds[0]: | 1246 if center_offsets.size != row_bounds[1]-row_bounds[0]: |
| 1231 raise ValueError('center_offsets dimension mismatch in reconstructOneTomoStack') | 1247 raise ValueError('center_offsets dimension mismatch in reconstructOneTomoStack') |
| 1232 centers = center_offsets | 1248 centers = center_offsets |
| 1233 centers += tomo_stack.shape[2]/2 | 1249 centers += tomo_stack.shape[2]/2 |
| 1234 if True: | 1250 if True: |
| 1235 tomo_stack = tomopy.prep.stripe.remove_stripe_fw(tomo_stack[row_bounds[0]:row_bounds[1]], | 1251 tomo_stack = tomopy.prep.stripe.remove_stripe_fw( |
| 1236 sigma=sigma, ncore=ncore) | 1252 tomo_stack[row_bounds[0]:row_bounds[1]], sigma=sigma, ncore=num_core) |
| 1237 else: | 1253 else: |
| 1238 tomo_stack = tomo_stack[row_bounds[0]:row_bounds[1]] | 1254 tomo_stack = tomo_stack[row_bounds[0]:row_bounds[1]] |
| 1239 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, sinogram_order=True, | 1255 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, sinogram_order=True, |
| 1240 algorithm=algorithm, ncore=ncore) | 1256 algorithm=algorithm, ncore=num_core) |
| 1241 if run_secondary_sirt and secondary_iter > 0: | 1257 if run_secondary_sirt and secondary_iter > 0: |
| 1242 #options = {'method':'SIRT_CUDA', 'proj_type':'cuda', 'num_iter':secondary_iter} | 1258 #options = {'method':'SIRT_CUDA', 'proj_type':'cuda', 'num_iter':secondary_iter} |
| 1243 #RV: doesn't work for me: "Error: CUDA error 803: system has unsupported display driver / | 1259 #RV: doesn't work for me: "Error: CUDA error 803: system has unsupported display driver / |
| 1244 # cuda driver combination." | 1260 # cuda driver combination." |
| 1245 #options = {'method':'SIRT', 'proj_type':'linear', 'MinConstraint': 0, 'num_iter':secondary_iter} | 1261 #options = {'method':'SIRT', 'proj_type':'linear', 'MinConstraint': 0, 'num_iter':secondary_iter} |
| 1246 #SIRT did not finish while running overnight | 1262 #SIRT did not finish while running overnight |
| 1247 #options = {'method':'SART', 'proj_type':'linear', 'num_iter':secondary_iter} | 1263 #options = {'method':'SART', 'proj_type':'linear', 'num_iter':secondary_iter} |
| 1248 options = {'method':'SART', 'proj_type':'linear', 'MinConstraint': 0, 'num_iter':secondary_iter} | 1264 options = {'method':'SART', 'proj_type':'linear', 'MinConstraint': 0, |
| 1249 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, init_recon=tomo_recon_stack, | 1265 'num_iter':secondary_iter} |
| 1250 options=options, sinogram_order=True, algorithm=tomopy.astra, ncore=ncore) | 1266 tomo_recon_stack = tomopy.recon(tomo_stack, thetas, centers, |
| 1267 init_recon=tomo_recon_stack, options=options, sinogram_order=True, | |
| 1268 algorithm=tomopy.astra, ncore=num_core) | |
| 1251 if True: | 1269 if True: |
| 1252 tomopy.misc.corr.remove_ring(tomo_recon_stack, rwidth=rwidth, out=tomo_recon_stack) | 1270 tomopy.misc.corr.remove_ring(tomo_recon_stack, rwidth=rwidth, out=tomo_recon_stack) |
| 1253 return tomo_recon_stack | 1271 return tomo_recon_stack |
| 1254 | 1272 |
| 1255 def findImageFiles(self): | 1273 def findImageFiles(self): |
| 1263 dark_field['data_path'], self.config['data_filetype'], 'dark field') | 1281 dark_field['data_path'], self.config['data_filetype'], 'dark field') |
| 1264 if img_start < 0 or num_imgs < 1: | 1282 if img_start < 0 or num_imgs < 1: |
| 1265 logging.error('Unable to find suitable dark field images') | 1283 logging.error('Unable to find suitable dark field images') |
| 1266 if dark_field['data_path']: | 1284 if dark_field['data_path']: |
| 1267 self.is_valid = False | 1285 self.is_valid = False |
| 1268 dark_field['num'] = num_imgs | 1286 img_start_old = dark_field.get('img_start') |
| 1269 dark_field['img_start'] = img_start | 1287 num_imgs_old = dark_field.get('num') |
| 1288 if num_imgs_old is None: | |
| 1289 dark_field['num'] = num_imgs | |
| 1290 else: | |
| 1291 if num_imgs_old > num_imgs: | |
| 1292 logging.error('Inconsistent number of availaible dark field images') | |
| 1293 if dark_field['data_path']: | |
| 1294 self.is_valid = False | |
| 1295 if img_start_old is None: | |
| 1296 dark_field['img_start'] = img_start | |
| 1297 else: | |
| 1298 if img_start_old < img_start: | |
| 1299 logging.error('Inconsistent image start index for dark field images') | |
| 1300 if dark_field['data_path']: | |
| 1301 self.is_valid = False | |
| 1270 logging.info(f'Number of dark field images = {dark_field["num"]}') | 1302 logging.info(f'Number of dark field images = {dark_field["num"]}') |
| 1271 logging.info(f'Dark field image start index = {dark_field["img_start"]}') | 1303 logging.info(f'Dark field image start index = {dark_field["img_start"]}') |
| 1272 | 1304 |
| 1273 # Find bright field images | 1305 # Find bright field images |
| 1274 bright_field = self.config['bright_field'] | 1306 bright_field = self.config['bright_field'] |
| 1275 img_start, num_imgs, bright_files = msnc.findImageFiles( | 1307 img_start, num_imgs, bright_files = msnc.findImageFiles( |
| 1276 bright_field['data_path'], self.config['data_filetype'], 'bright field') | 1308 bright_field['data_path'], self.config['data_filetype'], 'bright field') |
| 1277 if img_start < 0 or num_imgs < 1: | 1309 if img_start < 0 or num_imgs < 1: |
| 1278 logging.error('Unable to find suitable bright field images') | 1310 logging.error('Unable to find suitable bright field images') |
| 1279 self.is_valid = False | 1311 self.is_valid = False |
| 1280 bright_field['num'] = num_imgs | 1312 img_start_old = bright_field.get('img_start') |
| 1281 bright_field['img_start'] = img_start | 1313 num_imgs_old = bright_field.get('num') |
| 1314 if num_imgs_old is None: | |
| 1315 bright_field['num'] = num_imgs | |
| 1316 else: | |
| 1317 if num_imgs_old > num_imgs: | |
| 1318 logging.error('Inconsistent number of availaible bright field images') | |
| 1319 self.is_valid = False | |
| 1320 if img_start_old is None: | |
| 1321 bright_field['img_start'] = img_start | |
| 1322 else: | |
| 1323 if img_start_old < img_start: | |
| 1324 logging.warning('Inconsistent image start index for bright field images') | |
| 1325 self.is_valid = False | |
| 1282 logging.info(f'Number of bright field images = {bright_field["num"]}') | 1326 logging.info(f'Number of bright field images = {bright_field["num"]}') |
| 1283 logging.info(f'Bright field image start index = {bright_field["img_start"]}') | 1327 logging.info(f'Bright field image start index = {bright_field["img_start"]}') |
| 1284 | 1328 |
| 1285 # Find tomography images | 1329 # Find tomography images |
| 1286 tomo_stack_files = [] | 1330 tomo_stack_files = [] |
| 1289 img_start, num_imgs, tomo_files = msnc.findImageFiles( | 1333 img_start, num_imgs, tomo_files = msnc.findImageFiles( |
| 1290 stack['data_path'], self.config['data_filetype'], f'tomography set {index}') | 1334 stack['data_path'], self.config['data_filetype'], f'tomography set {index}') |
| 1291 if img_start < 0 or num_imgs < 1: | 1335 if img_start < 0 or num_imgs < 1: |
| 1292 logging.error('Unable to find suitable tomography images') | 1336 logging.error('Unable to find suitable tomography images') |
| 1293 self.is_valid = False | 1337 self.is_valid = False |
| 1294 stack['num'] = num_imgs | 1338 img_start_old = stack.get('img_start') |
| 1295 stack['img_start'] = img_start | 1339 num_imgs_old = stack.get('num') |
| 1340 if num_imgs_old is None: | |
| 1341 stack['num'] = num_imgs | |
| 1342 else: | |
| 1343 if num_imgs_old > num_imgs: | |
| 1344 logging.error('Inconsistent number of availaible tomography images') | |
| 1345 self.is_valid = False | |
| 1346 if img_start_old is None: | |
| 1347 stack['img_start'] = img_start | |
| 1348 else: | |
| 1349 if img_start_old < img_start: | |
| 1350 logging.warning('Inconsistent image start index for tomography images') | |
| 1351 self.is_valid = False | |
| 1296 logging.info(f'Number of tomography images for set {index} = {stack["num"]}') | 1352 logging.info(f'Number of tomography images for set {index} = {stack["num"]}') |
| 1297 logging.info(f'Tomography set {index} image start index = {stack["img_start"]}') | 1353 logging.info(f'Tomography set {index} image start index = {stack["img_start"]}') |
| 1298 tomo_stack_files.append(tomo_files) | 1354 tomo_stack_files.append(tomo_files) |
| 1299 del tomo_files | 1355 del tomo_files |
| 1300 | 1356 |
| 1408 """Find rotation axis centers for the tomography stacks. | 1464 """Find rotation axis centers for the tomography stacks. |
| 1409 """ | 1465 """ |
| 1410 logging.debug('Find centers for tomography stacks') | 1466 logging.debug('Find centers for tomography stacks') |
| 1411 stacks = self.config['stack_info']['stacks'] | 1467 stacks = self.config['stack_info']['stacks'] |
| 1412 available_stacks = [stack['index'] for stack in stacks if stack.get('preprocessed', False)] | 1468 available_stacks = [stack['index'] for stack in stacks if stack.get('preprocessed', False)] |
| 1413 logging.debug('Avaliable stacks: {available_stacks}') | 1469 logging.debug('Available stacks: {available_stacks}') |
| 1414 | 1470 |
| 1415 # Check for valid available center stack index | 1471 # Check for valid available center stack index |
| 1416 find_center = self.config.get('find_center') | 1472 find_center = self.config.get('find_center') |
| 1473 center_stack_index = None | |
| 1417 if find_center and 'center_stack_index' in find_center: | 1474 if find_center and 'center_stack_index' in find_center: |
| 1418 center_stack_index = find_center['center_stack_index'] | 1475 center_stack_index = find_center['center_stack_index'] |
| 1419 if (not isinstance(center_stack_index, int) or | 1476 if (not isinstance(center_stack_index, int) or |
| 1420 center_stack_index not in available_stacks): | 1477 center_stack_index not in available_stacks): |
| 1421 msnc.illegal_value('center_stack_index', center_stack_index, 'config file') | 1478 msnc.illegal_value('center_stack_index', center_stack_index, 'config file') |
| 1422 else: | 1479 else: |
| 1423 if self.test_mode: | 1480 if self.test_mode: |
| 1424 find_center['completed'] = True | 1481 assert(find_center.get('completed', False) == False) |
| 1425 self.cf.saveFile(self.config_out) | 1482 else: |
| 1426 return | 1483 print('\nFound calibration center offset info for stack '+ |
| 1427 print('\nFound calibration center offset info for stack '+ | 1484 f'{center_stack_index}') |
| 1428 f'{center_stack_index}') | 1485 if (pyip.inputYesNo('Do you want to use this again (y/n)? ') == 'yes' and |
| 1429 if pyip.inputYesNo('Do you want to use this again (y/n)? ') == 'yes': | 1486 find_center.get('completed', False) == True): |
| 1430 find_center['completed'] = True | 1487 return |
| 1431 self.cf.saveFile(self.config_out) | |
| 1432 return | |
| 1433 | 1488 |
| 1434 # Load the required preprocessed stack | 1489 # Load the required preprocessed stack |
| 1435 # preprocessed stack order: row,theta,column | 1490 # preprocessed stack order: row,theta,column |
| 1436 num_tomo_stacks = self.config['stack_info']['num'] | 1491 num_tomo_stacks = self.config['stack_info']['num'] |
| 1437 assert(len(stacks) == num_tomo_stacks) | 1492 assert(len(stacks) == num_tomo_stacks) |
| 1438 assert(len(self.tomo_stacks) == num_tomo_stacks) | 1493 assert(len(self.tomo_stacks) == num_tomo_stacks) |
| 1439 if num_tomo_stacks == 1: | 1494 if num_tomo_stacks == 1: |
| 1440 center_stack_index = stacks[0]['index'] | 1495 if not center_stack_index: |
| 1496 center_stack_index = stacks[0]['index'] | |
| 1497 elif center_stack_index != stacks[0]['index']: | |
| 1498 raise ValueError(f'Inconsistent center_stack_index {center_stack_index}') | |
| 1441 if not self.tomo_stacks[0].size: | 1499 if not self.tomo_stacks[0].size: |
| 1442 self.tomo_stacks[0], available = self._loadTomo('red stack', center_stack_index, | 1500 self.tomo_stacks[0], available = self._loadTomo('red stack', center_stack_index, |
| 1443 required=True) | 1501 required=True) |
| 1444 center_stack = self.tomo_stacks[0] | 1502 center_stack = self.tomo_stacks[0] |
| 1445 if not center_stack.size: | 1503 if not center_stack.size: |
| 1446 logging.error('Unable to load the required preprocessed tomography stack') | 1504 stacks[0]['preprocessed'] = False |
| 1447 return | 1505 raise OSError('Unable to load the required preprocessed tomography stack') |
| 1448 assert(stacks[0].get('preprocessed', False) == True) | 1506 assert(stacks[0].get('preprocessed', False) == True) |
| 1449 else: | 1507 else: |
| 1450 while True: | 1508 while True: |
| 1451 center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' | 1509 if not center_stack_index: |
| 1452 f'rotation axis centers {available_stacks}: ') | 1510 center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' |
| 1511 f'rotation axis centers {available_stacks}: ') | |
| 1453 while center_stack_index not in available_stacks: | 1512 while center_stack_index not in available_stacks: |
| 1454 center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' | 1513 center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' |
| 1455 f'rotation axis centers {available_stacks}: ') | 1514 f'rotation axis centers {available_stacks}: ') |
| 1456 tomo_stack_index = available_stacks.index(center_stack_index) | 1515 tomo_stack_index = available_stacks.index(center_stack_index) |
| 1457 if not self.tomo_stacks[tomo_stack_index].size: | 1516 if not self.tomo_stacks[tomo_stack_index].size: |
| 1458 self.tomo_stacks[tomo_stack_index], available = self._loadTomo( | 1517 self.tomo_stacks[tomo_stack_index], available = self._loadTomo( |
| 1459 'red stack', center_stack_index, required=True) | 1518 'red stack', center_stack_index, required=True) |
| 1460 center_stack = self.tomo_stacks[tomo_stack_index] | 1519 center_stack = self.tomo_stacks[tomo_stack_index] |
| 1461 if not center_stack.size: | 1520 if not center_stack.size: |
| 1521 stacks[tomo_stack_index]['preprocessed'] = False | |
| 1462 logging.error(f'Unable to load the {center_stack_index}th '+ | 1522 logging.error(f'Unable to load the {center_stack_index}th '+ |
| 1463 'preprocessed tomography stack, pick another one') | 1523 'preprocessed tomography stack, pick another one') |
| 1464 else: | 1524 else: |
| 1465 break | 1525 break |
| 1466 assert(stacks[tomo_stack_index].get('preprocessed', False) == True) | 1526 assert(stacks[tomo_stack_index].get('preprocessed', False) == True) |
| 1467 if find_center is None: | 1527 if find_center is None: |
| 1468 self.config['find_center'] = {'center_stack_index' : center_stack_index} | 1528 self.config['find_center'] = {'center_stack_index' : center_stack_index} |
| 1469 find_center = self.config['find_center'] | 1529 find_center = self.config['find_center'] |
| 1530 else: | |
| 1531 find_center['center_stack_index'] = center_stack_index | |
| 1470 | 1532 |
| 1471 # Set thetas (in degrees) | 1533 # Set thetas (in degrees) |
| 1472 theta_range = self.config['theta_range'] | 1534 theta_range = self.config['theta_range'] |
| 1473 theta_start = theta_range['start'] | 1535 theta_start = theta_range['start'] |
| 1474 theta_end = theta_range['end'] | 1536 theta_end = theta_range['end'] |
| 1486 logging.debug(f'eff_pixel_size = {eff_pixel_size}') | 1548 logging.debug(f'eff_pixel_size = {eff_pixel_size}') |
| 1487 tomo_ref_heights = [stack['ref_height'] for stack in stacks] | 1549 tomo_ref_heights = [stack['ref_height'] for stack in stacks] |
| 1488 if num_tomo_stacks == 1: | 1550 if num_tomo_stacks == 1: |
| 1489 n1 = 0 | 1551 n1 = 0 |
| 1490 height = center_stack.shape[0]*eff_pixel_size | 1552 height = center_stack.shape[0]*eff_pixel_size |
| 1491 if pyip.inputYesNo('\nDo you want to reconstruct the full samply height '+ | 1553 if not self.test_mode and pyip.inputYesNo('\nDo you want to reconstruct the full '+ |
| 1492 f'({height:.3f} mm) (y/n)? ') == 'no': | 1554 f'sample height ({height:.3f} mm) (y/n)? ') == 'no': |
| 1493 height = pyip.inputNum('\nEnter the desired reconstructed sample height '+ | 1555 height = pyip.inputNum('\nEnter the desired reconstructed sample height '+ |
| 1494 f'in mm [0, {height:.3f}]: ', min=0, max=height) | 1556 f'in mm [0, {height:.3f}]: ', min=0, max=height) |
| 1495 n1 = int(0.5*(center_stack.shape[0]-height/eff_pixel_size)) | 1557 n1 = int(0.5*(center_stack.shape[0]-height/eff_pixel_size)) |
| 1496 else: | 1558 else: |
| 1497 n1 = int((1.+(tomo_ref_heights[0]+center_stack.shape[0]*eff_pixel_size- | 1559 n1 = int((1.+(tomo_ref_heights[0]+center_stack.shape[0]*eff_pixel_size- |
| 1498 tomo_ref_heights[1])/eff_pixel_size)/2) | 1560 tomo_ref_heights[1])/eff_pixel_size)/2) |
| 1499 n2 = center_stack.shape[0]-n1 | 1561 n2 = center_stack.shape[0]-n1 |
| 1500 logging.info(f'n1 = {n1}, n2 = {n2} (n2-n1) = {(n2-n1)*eff_pixel_size:.3f} mm') | 1562 logging.info(f'n1 = {n1}, n2 = {n2} (n2-n1) = {(n2-n1)*eff_pixel_size:.3f} mm') |
| 1501 if not center_stack.size: | 1563 if not center_stack.size: |
| 1502 RuntimeError('Center stack not loaded') | 1564 RuntimeError('Center stack not loaded') |
| 1503 msnc.quickImshow(center_stack[:,0,:], title=f'center stack theta={theta_start}', | 1565 if not self.test_mode: |
| 1504 path=self.output_folder, save_fig=self.save_plots, save_only=self.save_plots_only) | 1566 msnc.quickImshow(center_stack[:,0,:], title=f'center stack theta={theta_start}', |
| 1567 path=self.output_folder, save_fig=self.save_plots, | |
| 1568 save_only=self.save_plots_only) | |
| 1505 | 1569 |
| 1506 # Get cross sectional diameter in mm | 1570 # Get cross sectional diameter in mm |
| 1507 cross_sectional_dim = center_stack.shape[2]*eff_pixel_size | 1571 cross_sectional_dim = center_stack.shape[2]*eff_pixel_size |
| 1508 logging.debug(f'cross_sectional_dim = {cross_sectional_dim}') | 1572 logging.debug(f'cross_sectional_dim = {cross_sectional_dim}') |
| 1509 | 1573 |
| 1510 # Determine center offset at sample row boundaries | 1574 # Determine center offset at sample row boundaries |
| 1511 logging.info('Determine center offset at sample row boundaries') | 1575 logging.info('Determine center offset at sample row boundaries') |
| 1512 | 1576 |
| 1513 # Lower row center | 1577 # Lower row center |
| 1514 use_row = False | 1578 use_row = 'no' |
| 1515 use_center = False | 1579 use_center = 'no' |
| 1516 row = find_center.get('lower_row') | 1580 row = find_center.get('lower_row') |
| 1517 if msnc.is_int(row, n1, n2-2): | 1581 if msnc.is_int(row, n1, n2-2): |
| 1518 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') | 1582 if self.test_mode: |
| 1519 use_row = pyip.inputYesNo('\nCurrent row index for lower center = ' | 1583 assert(row is not None) |
| 1520 f'{row}, use this value (y/n)? ') | 1584 use_row = 'yes' |
| 1521 if self.save_plots_only: | 1585 else: |
| 1522 msnc.clearFig(f'theta={theta_start}') | |
| 1523 if use_row: | |
| 1524 center_offset = find_center.get('lower_center_offset') | |
| 1525 if msnc.is_num(center_offset): | |
| 1526 use_center = pyip.inputYesNo('Current lower center offset = '+ | |
| 1527 f'{center_offset}, use this value (y/n)? ') | |
| 1528 if not use_center: | |
| 1529 if not use_row: | |
| 1530 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') | 1586 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') |
| 1587 use_row = pyip.inputYesNo('\nCurrent row index for lower center = ' | |
| 1588 f'{row}, use this value ([y]/n)? ', blank=True) | |
| 1589 if self.save_plots_only: | |
| 1590 msnc.clearFig(f'theta={theta_start}') | |
| 1591 if use_row != 'no': | |
| 1592 center_offset = find_center.get('lower_center_offset') | |
| 1593 if msnc.is_num(center_offset): | |
| 1594 use_center = pyip.inputYesNo('Current lower center offset = '+ | |
| 1595 f'{center_offset}, use this value ([y]/n)? ', blank=True) | |
| 1596 if use_center == 'no': | |
| 1597 if use_row == 'no': | |
| 1598 if not self.test_mode: | |
| 1599 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', | |
| 1600 aspect='auto') | |
| 1531 row = pyip.inputInt('\nEnter row index to find lower center '+ | 1601 row = pyip.inputInt('\nEnter row index to find lower center '+ |
| 1532 f'[[{n1}], {n2-2}]: ', min=n1, max=n2-2, blank=True) | 1602 f'[[{n1}], {n2-2}]: ', min=n1, max=n2-2, blank=True) |
| 1533 if row == '': | 1603 if row == '': |
| 1534 row = n1 | 1604 row = n1 |
| 1535 if self.save_plots_only: | 1605 if self.save_plots_only: |
| 1545 find_center['lower_center_offset'] = center_offset | 1615 find_center['lower_center_offset'] = center_offset |
| 1546 self.cf.saveFile(self.config_out) | 1616 self.cf.saveFile(self.config_out) |
| 1547 lower_row = row | 1617 lower_row = row |
| 1548 | 1618 |
| 1549 # Upper row center | 1619 # Upper row center |
| 1550 use_row = False | 1620 use_row = 'no' |
| 1551 use_center = False | 1621 use_center = 'no' |
| 1552 row = find_center.get('upper_row') | 1622 row = find_center.get('upper_row') |
| 1553 if msnc.is_int(row, lower_row+1, n2-1): | 1623 if msnc.is_int(row, lower_row+1, n2-1): |
| 1554 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') | 1624 if self.test_mode: |
| 1555 use_row = pyip.inputYesNo('\nCurrent row index for upper center = ' | 1625 assert(row is not None) |
| 1556 f'{row}, use this value (y/n)? ') | 1626 use_row = 'yes' |
| 1557 if self.save_plots_only: | 1627 else: |
| 1558 msnc.clearFig(f'theta={theta_start}') | |
| 1559 if use_row: | |
| 1560 center_offset = find_center.get('upper_center_offset') | |
| 1561 if msnc.is_num(center_offset): | |
| 1562 use_center = pyip.inputYesNo('Current upper center offset = '+ | |
| 1563 f'{center_offset}, use this value (y/n)? ') | |
| 1564 if not use_center: | |
| 1565 if not use_row: | |
| 1566 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') | 1628 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') |
| 1629 use_row = pyip.inputYesNo('\nCurrent row index for upper center = ' | |
| 1630 f'{row}, use this value ([y]/n)? ', blank=True) | |
| 1631 if self.save_plots_only: | |
| 1632 msnc.clearFig(f'theta={theta_start}') | |
| 1633 if use_row != 'no': | |
| 1634 center_offset = find_center.get('upper_center_offset') | |
| 1635 if msnc.is_num(center_offset): | |
| 1636 use_center = pyip.inputYesNo('Current upper center offset = '+ | |
| 1637 f'{center_offset}, use this value ([y]/n)? ', blank=True) | |
| 1638 if use_center == 'no': | |
| 1639 if use_row == 'no': | |
| 1640 if not self.test_mode: | |
| 1641 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', | |
| 1642 aspect='auto') | |
| 1567 row = pyip.inputInt('\nEnter row index to find upper center '+ | 1643 row = pyip.inputInt('\nEnter row index to find upper center '+ |
| 1568 f'[{lower_row+1}, [{n2-1}]]: ', min=lower_row+1, max=n2-1, blank=True) | 1644 f'[{lower_row+1}, [{n2-1}]]: ', min=lower_row+1, max=n2-1, blank=True) |
| 1569 if row == '': | 1645 if row == '': |
| 1570 row = n2-1 | 1646 row = n2-1 |
| 1571 if self.save_plots_only: | 1647 if self.save_plots_only: |
| 1658 | 1734 |
| 1659 def reconstructTomoStacks(self): | 1735 def reconstructTomoStacks(self): |
| 1660 """Reconstruct tomography stacks. | 1736 """Reconstruct tomography stacks. |
| 1661 """ | 1737 """ |
| 1662 logging.debug('Reconstruct tomography stacks') | 1738 logging.debug('Reconstruct tomography stacks') |
| 1739 stacks = self.config['stack_info']['stacks'] | |
| 1740 assert(len(self.tomo_stacks) == self.config['stack_info']['num']) | |
| 1741 assert(len(self.tomo_stacks) == len(stacks)) | |
| 1742 assert(len(self.tomo_recon_stacks) == len(stacks)) | |
| 1663 | 1743 |
| 1664 # Get rotation axis rows and centers | 1744 # Get rotation axis rows and centers |
| 1665 find_center = self.config['find_center'] | 1745 find_center = self.config['find_center'] |
| 1666 lower_row = find_center.get('lower_row') | 1746 lower_row = find_center.get('lower_row') |
| 1667 if lower_row is None: | 1747 if lower_row is None: |
| 1709 if stack.get('reconstructed', False): | 1789 if stack.get('reconstructed', False): |
| 1710 self.tomo_recon_stacks[i], available = self._loadTomo('recon stack', index) | 1790 self.tomo_recon_stacks[i], available = self._loadTomo('recon stack', index) |
| 1711 if self.tomo_recon_stacks[i].size or available: | 1791 if self.tomo_recon_stacks[i].size or available: |
| 1712 if self.tomo_stacks[i].size: | 1792 if self.tomo_stacks[i].size: |
| 1713 self.tomo_stacks[i] = np.array([]) | 1793 self.tomo_stacks[i] = np.array([]) |
| 1794 assert(stack.get('preprocessed', False) == True) | |
| 1714 assert(stack.get('reconstructed', False) == True) | 1795 assert(stack.get('reconstructed', False) == True) |
| 1715 continue | 1796 continue |
| 1716 else: | 1797 else: |
| 1717 stack['reconstructed'] = False | 1798 stack['reconstructed'] = False |
| 1718 if not self.tomo_stacks[i].size: | 1799 if not self.tomo_stacks[i].size: |
| 1719 self.tomo_stacks[i], available = self._loadTomo('red stack', index, | 1800 self.tomo_stacks[i], available = self._loadTomo('red stack', index, |
| 1720 required=True) | 1801 required=True) |
| 1721 if not self.tomo_stacks[i].size: | 1802 if not self.tomo_stacks[i].size: |
| 1722 logging.error(f'Unable to load tomography stack {index} for reconstruction') | 1803 logging.error(f'Unable to load tomography stack {index} for reconstruction') |
| 1804 stack[i]['preprocessed'] = False | |
| 1723 load_error = True | 1805 load_error = True |
| 1724 continue | 1806 continue |
| 1725 assert(0 <= lower_row < upper_row < self.tomo_stacks[i].shape[0]) | 1807 assert(0 <= lower_row < upper_row < self.tomo_stacks[i].shape[0]) |
| 1726 center_offsets = [lower_center_offset-lower_row*center_slope, | 1808 center_offsets = [lower_center_offset-lower_row*center_slope, |
| 1727 upper_center_offset+(self.tomo_stacks[i].shape[0]-1-upper_row)*center_slope] | 1809 upper_center_offset+(self.tomo_stacks[i].shape[0]-1-upper_row)*center_slope] |
| 1728 t0 = time() | 1810 t0 = time() |
| 1729 self.tomo_recon_stacks[i]= self._reconstructOneTomoStack(self.tomo_stacks[i], | 1811 self.tomo_recon_stacks[i]= self._reconstructOneTomoStack(self.tomo_stacks[i], |
| 1730 thetas, center_offsets=center_offsets, sigma=0.1, ncore=self.ncore, | 1812 thetas, center_offsets=center_offsets, sigma=0.1, num_core=self.num_core, |
| 1731 algorithm='gridrec', run_secondary_sirt=True, secondary_iter=25) | 1813 algorithm='gridrec', run_secondary_sirt=True, secondary_iter=25) |
| 1732 logging.info(f'Reconstruction of stack {index} took {time()-t0:.2f} seconds!') | 1814 logging.info(f'Reconstruction of stack {index} took {time()-t0:.2f} seconds!') |
| 1733 if not self.test_mode: | 1815 if not self.test_mode: |
| 1734 row_slice = int(self.tomo_stacks[i].shape[0]/2) | 1816 row_slice = int(self.tomo_stacks[i].shape[0]/2) |
| 1735 title = f'{basetitle} {index} slice{row_slice}' | 1817 title = f'{basetitle} {index} slice{row_slice}' |
| 1747 # self.tomo_recon_stacks[i][row_slice,:,:], fmt='%.6e') | 1829 # self.tomo_recon_stacks[i][row_slice,:,:], fmt='%.6e') |
| 1748 self.tomo_stacks[i] = np.array([]) | 1830 self.tomo_stacks[i] = np.array([]) |
| 1749 | 1831 |
| 1750 # Update config and save to file | 1832 # Update config and save to file |
| 1751 stack['reconstructed'] = True | 1833 stack['reconstructed'] = True |
| 1834 combine_stacks = self.config.get('combine_stacks') | |
| 1835 if combine_stacks and index in combine_stacks.get('stacks', []): | |
| 1836 combine_stacks['stacks'].remove(index) | |
| 1752 self.cf.saveFile(self.config_out) | 1837 self.cf.saveFile(self.config_out) |
| 1753 | 1838 |
| 1754 def combineTomoStacks(self): | 1839 def combineTomoStacks(self): |
| 1755 """Combine the reconstructed tomography stacks. | 1840 """Combine the reconstructed tomography stacks. |
| 1756 """ | 1841 """ |
| 1757 # stack order: stack,row(z),x,y | 1842 # stack order: stack,row(z),x,y |
| 1758 logging.debug('Combine reconstructed tomography stacks') | 1843 logging.debug('Combine reconstructed tomography stacks') |
| 1759 # Load any unloaded reconstructed stacks | 1844 # Load any unloaded reconstructed stacks |
| 1760 stacks = self.config['stack_info']['stacks'] | 1845 stack_info = self.config['stack_info'] |
| 1846 stacks = stack_info['stacks'] | |
| 1761 for i,stack in enumerate(stacks): | 1847 for i,stack in enumerate(stacks): |
| 1848 available = False | |
| 1762 if not self.tomo_recon_stacks[i].size and stack.get('reconstructed', False): | 1849 if not self.tomo_recon_stacks[i].size and stack.get('reconstructed', False): |
| 1763 self.tomo_recon_stacks[i], available = self._loadTomo('recon stack', | 1850 self.tomo_recon_stacks[i], available = self._loadTomo('recon stack', |
| 1764 stack['index'], required=True) | 1851 stack['index'], required=True) |
| 1765 if not self.tomo_recon_stacks[i].size or not available: | 1852 if not self.tomo_recon_stacks[i].size: |
| 1766 logging.error(f'Unable to load reconstructed stack {stack["index"]}') | 1853 logging.error(f'Unable to load reconstructed stack {stack["index"]}') |
| 1854 stack['reconstructed'] = False | |
| 1767 return | 1855 return |
| 1768 if i: | 1856 if i: |
| 1769 if (self.tomo_recon_stacks[i].shape[1] != self.tomo_recon_stacks[0].shape[1] or | 1857 if (self.tomo_recon_stacks[i].shape[1] != self.tomo_recon_stacks[0].shape[1] or |
| 1770 self.tomo_recon_stacks[i].shape[1] != self.tomo_recon_stacks[0].shape[1]): | 1858 self.tomo_recon_stacks[i].shape[1] != self.tomo_recon_stacks[0].shape[1]): |
| 1771 logging.error('Incompatible reconstructed tomography stack dimensions') | 1859 logging.error('Incompatible reconstructed tomography stack dimensions') |
| 1775 row_bounds = self.config['find_center']['row_bounds'] | 1863 row_bounds = self.config['find_center']['row_bounds'] |
| 1776 if not msnc.is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): | 1864 if not msnc.is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): |
| 1777 msnc.illegal_value('row_bounds', row_bounds, 'config file') | 1865 msnc.illegal_value('row_bounds', row_bounds, 'config file') |
| 1778 return | 1866 return |
| 1779 | 1867 |
| 1780 # Selecting xy bounds | 1868 # Selecting x bounds (in yz-plane) |
| 1781 tomosum = 0 | 1869 tomosum = 0 |
| 1782 #RV FIX := | 1870 #RV FIX := |
| 1783 #[tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,2)) for tomo_recon_stack in | 1871 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,2)) for tomo_recon_stack in |
| 1784 # self.tomo_recon_stacks] | 1872 self.tomo_recon_stacks] |
| 1785 combine_stacks =self.config.get('combine_stacks') | 1873 combine_stacks = self.config.get('combine_stacks') |
| 1786 if combine_stacks and 'x_bounds' in combine_stacks: | 1874 if combine_stacks and 'x_bounds' in combine_stacks: |
| 1787 x_bounds = combine_stacks['x_bounds'] | 1875 x_bounds = combine_stacks['x_bounds'] |
| 1788 if not msnc.is_index_range(x_bounds, 0, self.tomo_recon_stacks[0].shape[1]): | 1876 if not msnc.is_index_range(x_bounds, 0, self.tomo_recon_stacks[0].shape[1]): |
| 1789 msnc.illegal_value('x_bounds', x_bounds, 'config file') | 1877 msnc.illegal_value('x_bounds', x_bounds, 'config file') |
| 1790 elif not self.test_mode: | 1878 elif not self.test_mode: |
| 1803 if pyip.inputYesNo('\nDo you want to change the image x-bounds (y/n)? ') == 'no': | 1891 if pyip.inputYesNo('\nDo you want to change the image x-bounds (y/n)? ') == 'no': |
| 1804 x_bounds = [0, self.tomo_recon_stacks[0].shape[1]] | 1892 x_bounds = [0, self.tomo_recon_stacks[0].shape[1]] |
| 1805 else: | 1893 else: |
| 1806 x_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') | 1894 x_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') |
| 1807 if False and self.test_mode: | 1895 if False and self.test_mode: |
| 1808 np.savetxt(self.output_folder+'recon_stack_sum_yz.txt', | 1896 np.savetxt(f'{self.output_folder}/recon_stack_sum_yz.txt', |
| 1809 tomosum[x_bounds[0]:x_bounds[1]], fmt='%.6e') | 1897 tomosum[x_bounds[0]:x_bounds[1]], fmt='%.6e') |
| 1810 if self.save_plots_only: | 1898 if self.save_plots_only: |
| 1811 msnc.clearFig('recon stack sum yz') | 1899 msnc.clearFig('recon stack sum yz') |
| 1900 | |
| 1901 # Selecting y bounds (in xz-plane) | |
| 1812 tomosum = 0 | 1902 tomosum = 0 |
| 1813 #RV FIX := | 1903 #RV FIX := |
| 1814 #[tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in | 1904 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in |
| 1815 # self.tomo_recon_stacks] | 1905 self.tomo_recon_stacks] |
| 1816 if combine_stacks and 'y_bounds' in combine_stacks: | 1906 if combine_stacks and 'y_bounds' in combine_stacks: |
| 1817 y_bounds = combine_stacks['y_bounds'] | 1907 y_bounds = combine_stacks['y_bounds'] |
| 1818 if not msnc.is_index_range(x_bounds, 0, self.tomo_recon_stacks[0].shape[1]): | 1908 if not msnc.is_index_range(y_bounds, 0, self.tomo_recon_stacks[0].shape[1]): |
| 1819 msnc.illegal_value('y_bounds', y_bounds, 'config file') | 1909 msnc.illegal_value('y_bounds', y_bounds, 'config file') |
| 1820 elif not self.test_mode: | 1910 elif not self.test_mode: |
| 1821 msnc.quickPlot(tomosum, title='recon stack sum xz') | 1911 msnc.quickPlot(tomosum, title='recon stack sum xz') |
| 1822 if pyip.inputYesNo('\nCurrent image y-bounds: '+ | 1912 if pyip.inputYesNo('\nCurrent image y-bounds: '+ |
| 1823 f'[{y_bounds[0]}, {y_bounds[1]}], use these values ([y]/n)? ', | 1913 f'[{y_bounds[0]}, {y_bounds[1]}], use these values ([y]/n)? ', |
| 1833 if pyip.inputYesNo('\nDo you want to change the image y-bounds (y/n)? ') == 'no': | 1923 if pyip.inputYesNo('\nDo you want to change the image y-bounds (y/n)? ') == 'no': |
| 1834 y_bounds = [0, self.tomo_recon_stacks[0].shape[1]] | 1924 y_bounds = [0, self.tomo_recon_stacks[0].shape[1]] |
| 1835 else: | 1925 else: |
| 1836 y_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum xz') | 1926 y_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum xz') |
| 1837 if False and self.test_mode: | 1927 if False and self.test_mode: |
| 1838 np.savetxt(self.output_folder+'recon_stack_sum_xz.txt', | 1928 np.savetxt(f'{self.output_folder}/recon_stack_sum_xz.txt', |
| 1839 tomosum[y_bounds[0]:y_bounds[1]], fmt='%.6e') | 1929 tomosum[y_bounds[0]:y_bounds[1]], fmt='%.6e') |
| 1840 if self.save_plots_only: | 1930 if self.save_plots_only: |
| 1841 msnc.clearFig('recon stack sum xz') | 1931 msnc.clearFig('recon stack sum xz') |
| 1842 | 1932 |
| 1843 # Combine reconstructed tomography stacks | 1933 # Combine reconstructed tomography stacks |
| 1844 logging.info(f'Combining reconstructed stacks ...') | 1934 logging.info(f'Combining reconstructed stacks ...') |
| 1845 t0 = time() | 1935 t0 = time() |
| 1846 num_tomo_stacks = self.config['stack_info']['num'] | 1936 num_tomo_stacks = stack_info['num'] |
| 1847 if num_tomo_stacks == 1: | 1937 if num_tomo_stacks == 1: |
| 1848 low_bound = row_bounds[0] | 1938 low_bound = row_bounds[0] |
| 1849 else: | 1939 else: |
| 1850 low_bound = 0 | 1940 low_bound = 0 |
| 1851 tomo_recon_combined = self.tomo_recon_stacks[0][low_bound:row_bounds[1]:, | 1941 tomo_recon_combined = self.tomo_recon_stacks[0][low_bound:row_bounds[1]:, |
| 1858 if num_tomo_stacks > 1: | 1948 if num_tomo_stacks > 1: |
| 1859 tomo_recon_combined = np.concatenate([tomo_recon_combined]+ | 1949 tomo_recon_combined = np.concatenate([tomo_recon_combined]+ |
| 1860 [self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:, | 1950 [self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:, |
| 1861 x_bounds[0]:x_bounds[1],y_bounds[0]:y_bounds[1]]]) | 1951 x_bounds[0]:x_bounds[1],y_bounds[0]:y_bounds[1]]]) |
| 1862 logging.info(f'... done in {time()-t0:.2f} seconds!') | 1952 logging.info(f'... done in {time()-t0:.2f} seconds!') |
| 1953 combined_stacks = [stack['index'] for stack in stacks] | |
| 1954 | |
| 1955 # Wrap up if in test_mode | |
| 1863 tomosum = np.sum(tomo_recon_combined, axis=(1,2)) | 1956 tomosum = np.sum(tomo_recon_combined, axis=(1,2)) |
| 1864 if self.test_mode: | 1957 if self.test_mode: |
| 1865 zoom_perc = self.config['preprocess'].get('zoom_perc', 100) | 1958 zoom_perc = self.config['preprocess'].get('zoom_perc', 100) |
| 1866 filename = 'recon combined sum xy' | 1959 filename = 'recon combined sum xy' |
| 1867 if zoom_perc is None or zoom_perc == 100: | 1960 if zoom_perc is None or zoom_perc == 100: |
| 1870 filename += f' {zoom_perc}p.dat' | 1963 filename += f' {zoom_perc}p.dat' |
| 1871 msnc.quickPlot(tomosum, title='recon combined sum xy', | 1964 msnc.quickPlot(tomosum, title='recon combined sum xy', |
| 1872 path=self.output_folder, save_fig=self.save_plots, | 1965 path=self.output_folder, save_fig=self.save_plots, |
| 1873 save_only=self.save_plots_only) | 1966 save_only=self.save_plots_only) |
| 1874 if False: | 1967 if False: |
| 1875 np.savetxt(self.output_folder+'recon_combined_sum_xy.txt', | 1968 np.savetxt(f'{self.output_folder}/recon_combined_sum_xy.txt', |
| 1876 tomosum, fmt='%.6e') | 1969 tomosum, fmt='%.6e') |
| 1877 np.savetxt(self.output_folder+'recon_combined.txt', | 1970 np.savetxt(f'{self.output_folder}/recon_combined.txt', |
| 1878 tomo_recon_combined[int(tomo_recon_combined.shape[0]/2),:,:], fmt='%.6e') | 1971 tomo_recon_combined[int(tomo_recon_combined.shape[0]/2),:,:], fmt='%.6e') |
| 1879 combine_stacks =self.config.get('combine_stacks') | |
| 1880 | 1972 |
| 1881 # Update config and save to file | 1973 # Update config and save to file |
| 1882 if combine_stacks: | 1974 if combine_stacks: |
| 1883 combine_stacks['x_bounds'] = x_bounds | 1975 combine_stacks['x_bounds'] = x_bounds |
| 1884 combine_stacks['y_bounds'] = y_bounds | 1976 combine_stacks['y_bounds'] = y_bounds |
| 1885 else: | 1977 combine_stacks['stacks'] = combined_stacks |
| 1886 self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds} | 1978 else: |
| 1979 self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds, | |
| 1980 'stacks' : combined_stacks} | |
| 1887 self.cf.saveFile(self.config_out) | 1981 self.cf.saveFile(self.config_out) |
| 1888 return | 1982 return |
| 1983 | |
| 1984 # Selecting z bounds (in xy-plane) | |
| 1889 msnc.quickPlot(tomosum, title='recon combined sum xy') | 1985 msnc.quickPlot(tomosum, title='recon combined sum xy') |
| 1890 if pyip.inputYesNo( | 1986 if pyip.inputYesNo( |
| 1891 '\nDo you want to change the image z-bounds (y/[n])? ', | 1987 '\nDo you want to change the image z-bounds (y/[n])? ', |
| 1892 blank=True) != 'yes': | 1988 blank=True) != 'yes': |
| 1893 z_bounds = [0, tomo_recon_combined.shape[0]] | 1989 z_bounds = [0, tomo_recon_combined.shape[0]] |
| 1913 path=self.output_folder, save_fig=self.save_plots, | 2009 path=self.output_folder, save_fig=self.save_plots, |
| 1914 save_only=self.save_plots_only) | 2010 save_only=self.save_plots_only) |
| 1915 | 2011 |
| 1916 # Save combined reconstructed tomo stacks | 2012 # Save combined reconstructed tomo stacks |
| 1917 base_name = 'recon combined' | 2013 base_name = 'recon combined' |
| 1918 combined_stacks = [] | |
| 1919 for stack in stacks: | 2014 for stack in stacks: |
| 1920 base_name += f' {stack["index"]}' | 2015 base_name += f' {stack["index"]}' |
| 1921 combined_stacks.append(stack['index']) | |
| 1922 self._saveTomo(base_name, tomo_recon_combined) | 2016 self._saveTomo(base_name, tomo_recon_combined) |
| 1923 | 2017 |
| 1924 # Update config and save to file | 2018 # Update config and save to file |
| 1925 if combine_stacks: | 2019 if combine_stacks: |
| 1926 combine_stacks['x_bounds'] = x_bounds | 2020 combine_stacks['x_bounds'] = x_bounds |
| 1927 combine_stacks['y_bounds'] = y_bounds | 2021 combine_stacks['y_bounds'] = y_bounds |
| 2022 combine_stacks['z_bounds'] = z_bounds | |
| 1928 combine_stacks['stacks'] = combined_stacks | 2023 combine_stacks['stacks'] = combined_stacks |
| 1929 else: | 2024 else: |
| 1930 self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds, | 2025 self.config['combine_stacks'] = {'x_bounds' : x_bounds, 'y_bounds' : y_bounds, |
| 1931 'stacks' : combined_stacks} | 2026 'z_bounds' : z_bounds, 'stacks' : combined_stacks} |
| 1932 self.cf.saveFile(self.config_out) | 2027 self.cf.saveFile(self.config_out) |
| 1933 | 2028 |
| 1934 def runTomo(config_file=None, config_dict=None, output_folder='.', log_level='INFO', | 2029 def runTomo(config_file=None, config_dict=None, output_folder='.', log_level='INFO', |
| 1935 test_mode=False): | 2030 test_mode=False, num_core=-1): |
| 1936 """Run a tomography analysis. | 2031 """Run a tomography analysis. |
| 1937 """ | 2032 """ |
| 1938 # Instantiate Tomo object | 2033 # Instantiate Tomo object |
| 1939 tomo = Tomo(config_file=config_file, output_folder=output_folder, log_level=log_level, | 2034 tomo = Tomo(config_file=config_file, output_folder=output_folder, log_level=log_level, |
| 1940 test_mode=test_mode) | 2035 test_mode=test_mode, num_core=num_core) |
| 1941 if not tomo.is_valid: | 2036 if not tomo.is_valid: |
| 1942 raise ValueError('Invalid config and/or detector file provided.') | 2037 raise ValueError('Invalid config and/or detector file provided.') |
| 1943 | 2038 |
| 1944 # Preprocess the image files | 2039 # Preprocess the image files |
| 2040 assert(tomo.config['stack_info']) | |
| 1945 num_tomo_stacks = tomo.config['stack_info']['num'] | 2041 num_tomo_stacks = tomo.config['stack_info']['num'] |
| 1946 assert(num_tomo_stacks == len(tomo.tomo_stacks)) | 2042 assert(num_tomo_stacks == len(tomo.tomo_stacks)) |
| 1947 preprocess = tomo.config.get('preprocess', None) | |
| 1948 preprocessed_stacks = [] | 2043 preprocessed_stacks = [] |
| 1949 if preprocess: | 2044 if not tomo.test_mode: |
| 1950 preprocessed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] | 2045 preprocess = tomo.config.get('preprocess', None) |
| 1951 if stack.get('preprocessed', False)] | 2046 if preprocess: |
| 1952 if not len(preprocessed_stacks): | 2047 preprocessed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] |
| 2048 if stack.get('preprocessed', False)] | |
| 2049 if len(preprocessed_stacks) != num_tomo_stacks: | |
| 1953 tomo.genTomoStacks() | 2050 tomo.genTomoStacks() |
| 1954 if not tomo.is_valid: | 2051 if not tomo.is_valid: |
| 1955 IOError('Unable to load all required image files.') | 2052 IOError('Unable to load all required image files.') |
| 1956 find_center = tomo.config.get('find_center') | |
| 1957 if find_center and find_center.get('completed', False): | |
| 1958 center_stack_index = find_center['center_stack_index'] | |
| 1959 if not center_stack_index in preprocessed_stacks: | |
| 1960 find_center['completed'] = False | |
| 1961 #RV FIX | |
| 1962 # tomo.config.pop('check_center', 'check_center not found') | |
| 1963 # combined_stacks = tomo.config.get('combined_stacks') | |
| 1964 # if combined_stacks: | |
| 1965 # combined_stacks['completed'] = False | |
| 1966 tomo.cf.saveFile(tomo.config_out) | 2053 tomo.cf.saveFile(tomo.config_out) |
| 1967 | 2054 |
| 1968 # Find centers | 2055 # Find centers |
| 1969 find_center = tomo.config.get('find_center') | 2056 find_center = tomo.config.get('find_center') |
| 1970 if find_center is None or not find_center.get('completed', False): | 2057 if find_center is None or not find_center.get('completed', False): |
| 1973 # Check centers | 2060 # Check centers |
| 1974 #if num_tomo_stacks > 1 and not tomo.config.get('check_centers', False): | 2061 #if num_tomo_stacks > 1 and not tomo.config.get('check_centers', False): |
| 1975 # tomo.checkCenters() | 2062 # tomo.checkCenters() |
| 1976 | 2063 |
| 1977 # Reconstruct tomography stacks | 2064 # Reconstruct tomography stacks |
| 1978 if len(tomo.config.get('reconstructed_stacks', [])) != tomo.config['stack_info']['num']: | 2065 assert(tomo.config['stack_info']['stacks']) |
| 2066 reconstructed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] | |
| 2067 if stack.get('reconstructed', False)] | |
| 2068 if len(reconstructed_stacks) != num_tomo_stacks: | |
| 1979 tomo.reconstructTomoStacks() | 2069 tomo.reconstructTomoStacks() |
| 1980 | 2070 |
| 1981 # Combine reconstructed tomography stacks | 2071 # Combine reconstructed tomography stacks |
| 1982 combined_stacks = tomo.config.get('combined_stacks') | 2072 reconstructed_stacks = [stack['index'] for stack in tomo.config['stack_info']['stacks'] |
| 1983 if combined_stacks is None or not combined_stacks.get('completed', False): | 2073 if stack.get('reconstructed', False)] |
| 2074 combine_stacks = tomo.config.get('combine_stacks') | |
| 2075 if len(reconstructed_stacks) and (combine_stacks is None or | |
| 2076 combine_stacks.get('stacks') != reconstructed_stacks): | |
| 1984 tomo.combineTomoStacks() | 2077 tomo.combineTomoStacks() |
| 1985 | 2078 |
| 1986 #%%============================================================================ | 2079 #%%============================================================================ |
| 1987 if __name__ == '__main__': | 2080 if __name__ == '__main__': |
| 2081 | |
| 1988 # Parse command line arguments | 2082 # Parse command line arguments |
| 1989 arguments = sys.argv[1:] | 2083 parser = argparse.ArgumentParser( |
| 1990 config_file = None | 2084 description='Tomography reconstruction') |
| 1991 output_folder = '.' | 2085 parser.add_argument('-c', '--config', |
| 1992 log_level = 'INFO' | 2086 default=None, |
| 1993 test_mode = False | 2087 help='Input config') |
| 1994 try: | 2088 parser.add_argument('-o', '--output_folder', |
| 1995 opts, args = getopt.getopt(arguments,"hc:o:l:t") | 2089 default='.', |
| 1996 except getopt.GetoptError: | 2090 help='Output folder') |
| 1997 print('usage: tomo.py -c <config_file> -o <output_folder> -l <log_level> -t') | 2091 parser.add_argument('-l', '--log_level', |
| 1998 sys.exit(2) | 2092 default='INFO', |
| 1999 for opt, arg in opts: | 2093 help='Log level') |
| 2000 if opt == '-h': | 2094 parser.add_argument('-t', '--test_mode', |
| 2001 print('usage: tomo.py -c <config_file> -o <output_folder> -l <log_level> -t') | 2095 action='store_true', |
| 2002 sys.exit() | 2096 default=False, |
| 2003 elif opt in ("-c"): | 2097 help='Test mode flag') |
| 2004 config_file = arg | 2098 parser.add_argument('--num_core', |
| 2005 elif opt in ("-o"): | 2099 default=-1, |
| 2006 output_folder = arg | 2100 help='Number of cores') |
| 2007 elif opt in ("-l"): | 2101 args = parser.parse_args() |
| 2008 log_level = arg | 2102 |
| 2009 elif opt in ("-t"): | 2103 if args.config is None: |
| 2010 test_mode = True | |
| 2011 if config_file is None: | |
| 2012 if os.path.isfile('config.yaml'): | 2104 if os.path.isfile('config.yaml'): |
| 2013 config_file = 'config.yaml' | 2105 args.config = 'config.yaml' |
| 2014 else: | 2106 else: |
| 2015 config_file = 'config.txt' | 2107 args.config = 'config.txt' |
| 2016 | 2108 |
| 2017 # Set basic log configuration | 2109 # Set basic log configuration |
| 2018 logging_format = '%(asctime)s : %(levelname)s - %(module)s : %(funcName)s - %(message)s' | 2110 logging_format = '%(asctime)s : %(levelname)s - %(module)s : %(funcName)s - %(message)s' |
| 2019 if not test_mode: | 2111 if not args.test_mode: |
| 2020 level = getattr(logging, log_level.upper(), None) | 2112 level = getattr(logging, args.log_level.upper(), None) |
| 2021 if not isinstance(level, int): | 2113 if not isinstance(level, int): |
| 2022 raise ValueError(f'Invalid log_level: {log_level}') | 2114 raise ValueError(f'Invalid log_level: {args.log_level}') |
| 2023 logging.basicConfig(format=logging_format, level=level, force=True, | 2115 logging.basicConfig(format=logging_format, level=level, force=True, |
| 2024 handlers=[logging.StreamHandler()]) | 2116 handlers=[logging.StreamHandler()]) |
| 2025 | 2117 |
| 2026 logging.debug(f'config_file = {config_file}') | 2118 logging.debug(f'config = {args.config}') |
| 2027 logging.debug(f'output_folder = {output_folder}') | 2119 logging.debug(f'output_folder = {args.output_folder}') |
| 2028 logging.debug(f'log_level = {log_level}') | 2120 logging.debug(f'log_level = {args.log_level}') |
| 2029 logging.debug(f'test_mode = {test_mode}') | 2121 logging.debug(f'test_mode = {args.test_mode}') |
| 2122 logging.debug(f'num_core = {args.num_core}') | |
| 2030 | 2123 |
| 2031 # Run tomography analysis | 2124 # Run tomography analysis |
| 2032 runTomo(config_file=config_file, output_folder=output_folder, log_level=log_level, | 2125 runTomo(config_file=args.config, output_folder=args.output_folder, log_level=args.log_level, |
| 2033 test_mode=test_mode) | 2126 test_mode=args.test_mode, num_core=args.num_core) |
| 2034 | 2127 |
| 2035 #%%============================================================================ | 2128 #%%============================================================================ |
| 2036 input('Press any key to continue') | 2129 # input('Press any key to continue') |
| 2037 #%%============================================================================ | 2130 #%%============================================================================ |
