changeset 7:845270a96464 draft

"planemo upload for repository https://github.com/rolfverberg/galaxytools commit a08ed1264ea7b7c878ba96801e4b27ef621fb6be"
author rv43
date Thu, 07 Apr 2022 21:09:58 +0000
parents baf9f5eef128
children 9cabd6e746d3
files msnc_tools.py tomo.py tomo_find_center.py tomo_find_center.xml tomo_macros.xml tomo_reconstruct.xml tomo_setup.xml
diffstat 7 files changed, 238 insertions(+), 154 deletions(-) [+]
line wrap: on
line diff
--- a/msnc_tools.py	Wed Apr 06 19:48:22 2022 +0000
+++ b/msnc_tools.py	Thu Apr 07 21:09:58 2022 +0000
@@ -619,9 +619,8 @@
         if not os.path.isfile(config_file):
             logging.error(f'Unable to load {config_file}')
             return
-        print(f'\nLoading {config_file}\n')
 
-        # Load config file
+        # Load config file (for now for Galaxy, allow .dat extension)
         self.suffix = os.path.splitext(config_file)[1]
         if self.suffix == '.yml' or self.suffix == '.yaml' or self.suffix == '.dat':
             with open(config_file, 'r') as f:
--- a/tomo.py	Wed Apr 06 19:48:22 2022 +0000
+++ b/tomo.py	Thu Apr 07 21:09:58 2022 +0000
@@ -1166,7 +1166,7 @@
         del edges
 
     def _findCenterOnePlane(self, sinogram, row, thetas_deg, eff_pixel_size, cross_sectional_dim,
-            tol=0.1, num_core=1, recon_pngname=None):
+            tol=0.1, num_core=1, recon_pngname=None, galaxy_param=None):
         """Find center for a single tomography plane.
         """
         # sinogram index order: theta,column
@@ -1177,79 +1177,106 @@
         # try automatic center finding routines for initial value
         tomo_center = tomopy.find_center_vo(sinogram, ncore=num_core)
         center_offset_vo = tomo_center-center
-        if self.test_mode or self.galaxy_flag:
+        if self.test_mode:
+            logging.info(f'Center at row {row} using Nghia Vo’s method = {center_offset_vo:.2f}')
+            del sinogram_T
+            return float(center_offset_vo)
+        elif self.galaxy_flag:
             logging.info(f'Center at row {row} using Nghia Vo’s method = {center_offset_vo:.2f}')
-            if self.test_mode:
+            if recon_pngname:
+                assert(isinstance(recon_pngname, str))
+                recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg,
+                        eff_pixel_size, cross_sectional_dim, False, num_core)
+                title = os.path.basename(recon_pngname)
+                self._plotEdgesOnePlane(recon_plane, title, name=recon_pngname)
+                del recon_plane
+            if not galaxy_param or not galaxy_param['center_type_selector']:
                 del sinogram_T
                 return float(center_offset_vo)
         else:
             print(f'Center at row {row} using Nghia Vo’s method = {center_offset_vo:.2f}')
             if recon_pngname:
                 logging.warning('Ignoring recon_pngname in _findCenterOnePlane (only for Galaxy)')
-        recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg,
-                eff_pixel_size, cross_sectional_dim, False, num_core)
-        if self.galaxy_flag:
-            assert(isinstance(recon_pngname, str))
-            title = os.path.basename(recon_pngname)
-            self._plotEdgesOnePlane(recon_plane, title, name=recon_pngname)
-            del sinogram_T
-            del recon_plane
-            return float(center_offset_vo)
-        else:
-            title = f'edges row{row} center_offset_vo{center_offset_vo:.2f}'
-            self._plotEdgesOnePlane(recon_plane, title)
-        if (pyip.inputYesNo('Try finding center using phase correlation '+
-                '(y/[n])? ', blank=True) == 'yes'):
-            tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1,
-                    rotc_guess=tomo_center)
-            error = 1.
-            while error > tol:
-                prev = tomo_center
-                tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=tol,
-                        rotc_guess=tomo_center)
-                error = np.abs(tomo_center-prev)
-            center_offset = tomo_center-center
-            print(f'Center at row {row} using phase correlation = {center_offset:.2f}')
             recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg,
                     eff_pixel_size, cross_sectional_dim, False, num_core)
-            title = f'edges row{row} center_offset{center_offset:.2f}'
+            title = f'edges row{row} center_offset_vo{center_offset_vo:.2f}'
             self._plotEdgesOnePlane(recon_plane, title)
-        if (pyip.inputYesNo('Accept a center location ([y]) or continue '+
-                'search (n)? ', blank=True) != 'no'):
-            del sinogram_T
-            del recon_plane
-            center_offset = pyip.inputNum(
-                    f'    Enter chosen center offset [{-int(center)}, {int(center)}] '+
-                    f'([{center_offset_vo}])): ', blank=True)
-            if center_offset == '':
-                center_offset = center_offset_vo
-            return float(center_offset)
+        if not self.galaxy_flag:
+            if (pyip.inputYesNo('Try finding center using phase correlation '+
+                    '(y/[n])? ', blank=True) == 'yes'):
+                tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1,
+                        rotc_guess=tomo_center)
+                error = 1.
+                while error > tol:
+                    prev = tomo_center
+                    tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=tol,
+                            rotc_guess=tomo_center)
+                    error = np.abs(tomo_center-prev)
+                center_offset = tomo_center-center
+                print(f'Center at row {row} using phase correlation = {center_offset:.2f}')
+                recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg,
+                        eff_pixel_size, cross_sectional_dim, False, num_core)
+                title = f'edges row{row} center_offset{center_offset:.2f}'
+                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
+                del sinogram_T
+                del recon_plane
+                return float(center_offset)
 
+        # perform center finding search
         while True:
-            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))
-            if center_offset_upp == center_offset_low:
-                center_offset_step = 1
+            if self.galaxy_flag and galaxy_param and galaxy_param['center_type_selector']:
+                set_center = center_offset_vo
+                if galaxy_param['center_type_selector'] == 'user':
+                    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
+                        center_offset_step <= 0):
+                    logging.warning('Illegal center finding search parameter, skip search')
+                    del sinogram_T
+                    return float(center_offset_vo)
+                set_range = center_offset_step*max(1, int(set_range/center_offset_step))
+                center_offset_low = set_center-set_range
+                center_offset_upp = set_center+set_range
             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)
-            for center_offset in range(center_offset_low, center_offset_upp+center_offset_step, 
-                        center_offset_step):
-                logging.info(f'center_offset = {center_offset}')
+                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))
+                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)
+            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:
+                if center_offset == center_offset_vo:
+                    continue
+                logging.info(f'center_offset = {center_offset:.2f}')
                 recon_plane = self._reconstructOnePlane(sinogram_T, center_offset+center,
                         thetas_deg, eff_pixel_size, cross_sectional_dim, False, num_core)
-                title = f'edges row{row} center_offset{center_offset}'
+                title = f'edges row{row} center_offset{center_offset:.2f}'
                 self._plotEdgesOnePlane(recon_plane, title)
-            if pyip.inputInt('\nContinue (0) or end the search (1): ', min=0, max=1):
+            if self.galaxy_flag or pyip.inputInt('\nContinue (0) or end the search (1): ',
+                    min=0, max=1):
                 break
 
         del sinogram_T
         del recon_plane
-        center_offset = pyip.inputNum(f'    Enter chosen center offset '+
+        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))
         return float(center_offset)
 
@@ -1513,8 +1540,7 @@
             stack['ref_height'] = stack['ref_height']+pixel_size
         self.cf.saveFile(self.config_out)
 
-    def findCenters(self, row_bounds=None, center_rows=None, recon_low_pngname=None,
-            recon_upp_pngname=None, num_core=None):
+    def findCenters(self, galaxy_param=None, num_core=None):
         """Find rotation axis centers for the tomography stacks.
         """
         if num_core is None:
@@ -1524,23 +1550,26 @@
         available_stacks = [stack['index'] for stack in stacks if stack.get('preprocessed', False)]
         logging.debug('Available stacks: {available_stacks}')
         if self.galaxy_flag:
-            assert(isinstance(row_bounds, list) and len(row_bounds) == 2)
-            assert(isinstance(center_rows, list) and len(center_rows) == 2)
-            assert(isinstance(recon_low_pngname, str))
-            assert(isinstance(recon_upp_pngname, str))
+            assert(isinstance(galaxy_param, dict))
+            row_bounds = galaxy_param['row_bounds']
+            center_rows = galaxy_param['center_rows']
+            if center_rows is None:
+                logging.error('Missing center_rows entry in galaxy_param')
+                return
+            center_type_selector = galaxy_param['center_type_selector']
+            if center_type_selector:
+                if center_type_selector == 'vo':
+                    set_center = None
+                elif center_type_selector == 'user':
+                    set_center = galaxy_param['set_center']
+                else:
+                    logging.error('Illegal center_type_selector entry in galaxy_param '+
+                            f'({center_type_selector})')
+                    galaxy_param['center_type_selector'] = None
         else:
-            if row_bounds:
-                logging.warning('Ignoring row_bounds in findCenters (only for Galaxy)')
-                row_bounds = None
-            if center_rows:
-                logging.warning('Ignoring center_rows in findCenters (only for Galaxy)')
-                center_rows = None
-            if recon_low_pngname:
-                logging.warning('Ignoring recon_low_pngname in findCenters (only for Galaxy)')
-                recon_low_pngname = None
-            if recon_upp_pngname:
-                logging.warning('Ignoring recon_upp_pngname in findCenters (only for Galaxy)')
-                recon_upp_pngname = None
+            if galaxy_param:
+                logging.warning('Ignoring galaxy_param in findCenters (only for Galaxy)')
+                galaxy_param = None
 
         # Check for valid available center stack index
         find_center = self.config.get('find_center')
@@ -1610,6 +1639,15 @@
             row_bounds = find_center.get('row_bounds', None)
             center_rows = [find_center.get('lower_row', None),
                     find_center.get('upper_row', None)]
+        if row_bounds is None:
+            row_bounds = [0, center_stack.shape[0]]
+        if row_bounds[0] == -1:
+            row_bounds[0] = 0
+        if row_bounds[1] == -1:
+            row_bounds[1] = center_stack.shape[0]
+        if not msnc.is_index_range(row_bounds, 0, center_stack.shape[0]):
+            msnc.illegal_value('row_bounds', row_bounds)
+            return
 
         # Set thetas (in degrees)
         theta_range = self.config['theta_range']
@@ -1687,9 +1725,10 @@
         use_row = 'no'
         use_center = 'no'
         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):
             if self.test_mode or self.galaxy_flag:
-                assert(row is not None)
                 use_row = 'yes'
             else:
                 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto')
@@ -1714,9 +1753,15 @@
                 if self.save_plots_only:
                     msnc.clearFig(f'theta={theta_start}')
             # center_stack order: row,theta,column
+            if galaxy_param:
+                recon_pngname = galaxy_param['recon_center_low']
+            else:
+                recon_pngname = None
             center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg,
                     eff_pixel_size, cross_sectional_dim, num_core=num_core,
-                    recon_pngname=recon_low_pngname)
+                    recon_pngname=recon_pngname, galaxy_param=galaxy_param)
+        logging.info(f'lower_center_offset = {center_offset:.2f} {type(center_offset)}')
+        print(center_offset)
 
         # Update config and save to file
         find_center['row_bounds'] = [n1, n2]
@@ -1729,9 +1774,10 @@
         use_row = 'no'
         use_center = 'no'
         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):
             if self.test_mode or self.galaxy_flag:
-                assert(row is not None)
                 use_row = 'yes'
             else:
                 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto')
@@ -1756,10 +1802,14 @@
                 if self.save_plots_only:
                     msnc.clearFig(f'theta={theta_start}')
             # center_stack order: row,theta,column
+            if galaxy_param:
+                recon_pngname = galaxy_param['recon_center_upp']
+            else:
+                recon_pngname = None
             center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg,
                     eff_pixel_size, cross_sectional_dim, num_core=num_core,
-                    recon_pngname=recon_upp_pngname)
-        logging.info(f'upper_center_offset = {center_offset}')
+                    recon_pngname=recon_pngname, galaxy_param=galaxy_param)
+        logging.info(f'upper_center_offset = {center_offset:.2f}')
         del center_stack
 
         # Update config and save to file
--- a/tomo_find_center.py	Wed Apr 06 19:48:22 2022 +0000
+++ b/tomo_find_center.py	Thu Apr 07 21:09:58 2022 +0000
@@ -3,7 +3,6 @@
 import logging
 
 import sys
-import re
 import argparse
 
 from tomo import Tomo
@@ -14,29 +13,31 @@
     parser = argparse.ArgumentParser(
             description='Find the center axis for a tomography reconstruction')
     parser.add_argument('-i', '--input_stacks',
-            help='Preprocessed image file stacks')
+            required=True, help='Preprocessed image file stacks')
     parser.add_argument('-c', '--config',
-            help='Input config')
+            required=True, help='Input config')
     parser.add_argument('--row_bounds',
-            help='Reconstruction row bounds')
+            required=True, nargs=2, type=int, help='Reconstruction row bounds')
     parser.add_argument('--center_rows',
-            help='Center finding rows')
+            required=True, nargs=2, type=int, help='Center finding rows')
+    parser.add_argument('--center_type_selector',
+            help='Reconstruct slices for a set of center positions?')
+    parser.add_argument('--set_center',
+            type=int, help='Set center ')
+    parser.add_argument('--set_range',
+            type=float, help='Set range')
+    parser.add_argument('--set_step',
+            type=float, help='Set step')
     parser.add_argument('--output_config',
-            help='Output config')
+            required=True, help='Output config')
     parser.add_argument('--recon_center_low',
-            help='Lower reconstructed slice center')
+            help='Lower reconstructed slice center image name')
     parser.add_argument('--recon_center_upp',
-            help='Upper reconstructed slice center')
+            help='Upper reconstructed slice center image name')
     parser.add_argument('-l', '--log', 
-            type=argparse.FileType('w'),
-            default=sys.stdout,
-            help='Log file')
+            type=argparse.FileType('w'), default=sys.stdout, help='Log file')
     args = parser.parse_args()
 
-    indexRegex = re.compile(r'\d+')
-    row_bounds = [int(i) for i in indexRegex.findall(args.row_bounds)]
-    center_rows = [int(i) for i in indexRegex.findall(args.center_rows)]
-
     # Set basic log configuration
     logging_format = '%(asctime)s : %(levelname)s - %(module)s : %(funcName)s - %(message)s'
     log_level = 'INFO'
@@ -48,11 +49,15 @@
 
     logging.debug(f'input_stacks = {args.input_stacks}')
     logging.debug(f'config = {args.config}')
-    logging.debug(f'row_bounds = {args.row_bounds} {row_bounds}')
-    logging.debug(f'center_rows = {args.center_rows} {center_rows}')
+    logging.debug(f'row_bounds = {args.row_bounds} {type(args.row_bounds)}')
+    logging.debug(f'center_rows = {args.center_rows} {type(args.center_rows)}')
+    logging.debug(f'center_type_selector = {args.center_type_selector}')
+    logging.debug(f'set_center = {args.set_center}')
+    logging.debug(f'set_range = {args.set_range}')
+    logging.debug(f'set_step = {args.set_step}')
     logging.debug(f'output_config = {args.output_config}')
     logging.debug(f'recon_center_low = {args.recon_center_low}')
-    logging.debug(f'recon_center_uppm = {args.recon_center_upp}')
+    logging.debug(f'recon_center_upp = {args.recon_center_upp}')
     logging.debug(f'log = {args.log}')
     logging.debug(f'is log stdout? {args.log is sys.stdout}')
 
@@ -67,7 +72,11 @@
     tomo.loadTomoStacks(args.input_stacks)
 
     # Find centers
-    tomo.findCenters(row_bounds, center_rows, args.recon_center_low, args.recon_center_upp)
+    galaxy_param = {'row_bounds' : args.row_bounds, 'center_rows' : args.center_rows,
+            'center_type_selector' : args.center_type_selector, 'set_center' : args.set_center, 
+            'set_range' : args.set_range, 'set_step' : args.set_step, 
+            'recon_center_low' : args.recon_center_low, 'recon_center_upp' : args.recon_center_upp}
+    tomo.findCenters(galaxy_param)
 
 if __name__ == "__main__":
     __main__()
--- a/tomo_find_center.xml	Wed Apr 06 19:48:22 2022 +0000
+++ b/tomo_find_center.xml	Thu Apr 07 21:09:58 2022 +0000
@@ -1,48 +1,66 @@
-<tool id="tomo_find_center" name="Tomo Find Center Axis" version="0.1.0" python_template_version="3.9">
+<tool id="tomo_find_center" name="Tomo Find Center Axis" version="0.1.1" python_template_version="3.9">
     <description>Find the center axis for a tomography reconstruction</description>
-    <requirements>
-        <requirement type="package" version="1.11.0">tomopy</requirement>
-        <requirement type="package" version="3.6.0">h5py</requirement>
-    </requirements>
+    <macros>
+        <import>tomo_macros.xml</import>
+    </macros>
+    <expand macro="requirements" />
     <command detect_errors="exit_code"><![CDATA[
         $__tool_directory__/tomo_find_center.py
         -i '$red_stacks'
         -c '$config'
-        --row_bounds '$row_bounds.row_bound_low $row_bounds.row_bound_upp'
-        --center_rows '$recon_centers.lower_row $recon_centers.upper_row'
+        --row_bounds $row_bounds.row_bound_low $row_bounds.row_bound_upp
+        --center_rows $recon_centers.lower_row $recon_centers.upper_row
+        #if str( $set.set_selector ) == "yes"
+            --center_type_selector '$set.center_type.center_type_selector'
+            #if str( $set.center_type.center_type_selector ) == "user"
+                --set_center '$set.center_type.set_center'
+            #endif
+            --set_range '$set.set_range'
+            --set_step '$set.set_step'
+        #endif
         --output_config 'output_config.yaml'
         --recon_center_low 'recon_center_low.png'
         --recon_center_upp 'recon_center_upp.png'
         -l '$log'
     ]]></command>
     <inputs>
-        <param name="red_stacks" type='data' format='npz' optional='false' label="Reduced stacks"/>
-        <param name="config" type='data' format='yaml' optional='false' label="Input config"/>
+        <expand macro="common_inputs"/>
+        <param name="red_stacks" type="data" format="npz" optional="false" label="Reduced stacks"/>
         <section name="row_bounds" title="Reconstruction row bounds">
             <param name="row_bound_low" type="integer" value="-1" label="Lower bound"/>
             <param name="row_bound_upp" type="integer" value="-1" label="Upper bound"/>
         </section>
         <section name="recon_centers" title="Reconstruction rows to establish center axis">
-            <param name="lower_row" type="integer" value="-1" label="Lower row"/>
-            <param name="upper_row" type="integer" value="-1" label="Upper row"/>
+            <param name="lower_row" type="integer" optional="false" label="Lower row"/>
+            <param name="upper_row" type="integer" optional="false" label="Upper row"/>
         </section>
+        <conditional name="set">
+            <param name="set_selector" type="select" label="Reconstruct slices for a set of center positions?">
+                <option value="no" selected="true">No</option>
+                <option value="yes">Yes</option>
+            </param>
+            <when value="yes">
+                <conditional name="center_type">
+                    <param name="center_type_selector" argument="--center_type_selector" type="select" label="Choose the center (C) of the set">
+                        <option value="vo" selected="true">Use the center obtained by Nghia Vo’s method</option>
+                        <option value="user">Enter the center of the set</option>
+                    </param>
+                    <when value="user">
+                        <param name="set_center" argument="--set_center" type="integer" value="0" label="Center (C) of the set (0: middle of the detector row)"/>
+                    </when>
+                </conditional>
+                <param name="set_range" argument="--set_range" type="float" value="20" label="Half-width (H) of the set ranch (in detector pixels)"/>
+                <param name="set_step" argument="--set_step" type="float" value="5" label="Step size (S) (in detector pixels, reconstruct slices for center at [C-H, C-H+S, ..., C+H])"/>
+            </when>
+        </conditional>
     </inputs>
     <outputs>
-        <data name="output_config" format="yaml" label="Output config" from_work_dir="output_config.yaml"/>
-        <data name="recon_center_low" format="png" label="Recontructed slice lower center" from_work_dir="recon_center_low.png"/>
-        <data name="recon_center_upp" format="png" label="Recontructed slice upper center" from_work_dir="recon_center_upp.png"/>
-        <data name="log" format="txt" label="Log"/>
+        <expand macro="common_outputs"/>
+        <data name="recon_center_low" format="png" label="Recontructed slice lower center using Nghia Vo’s method" from_work_dir="recon_center_low.png"/>
+        <data name="recon_center_upp" format="png" label="Recontructed slice upper center using Nghia Vo’s method" from_work_dir="recon_center_upp.png"/>
     </outputs>
     <help><![CDATA[
         Preprocess tomography images.
     ]]></help>
-    <citations>
-        <citation type="bibtex">
-@misc{githubsum_files,
-  author = {Verberg, Rolf},
-  year = {2022},
-  title = {Tomo Find Center Axis},
-}</citation>
-    </citations>
-
+    <expand macro="citations"/>
 </tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tomo_macros.xml	Thu Apr 07 21:09:58 2022 +0000
@@ -0,0 +1,26 @@
+<macros>
+    <xml name="requirements">
+        <requirements>
+            <requirement type="package" version="1.11.0">tomopy</requirement>
+            <requirement type="package" version="3.6.0">h5py</requirement>
+        </requirements>
+    </xml>
+    <xml name="citations">
+        <citations>
+            <citation type="bibtex">
+@misc{githubsum_files,
+  author = {Verberg, Rolf},
+  year = {2022},
+  title = {Tomo Reconstruction},
+}</citation>
+        </citations>
+    </xml>
+    <xml name="common_inputs">
+        <param name="config" type='data' format='yaml' optional='false' label="Input config"/>
+    </xml>
+    <xml name="common_outputs">
+        <data name="output_config" format="yaml" label="Output config" from_work_dir="output_config.yaml"/>
+        <data name="log" format="txt" label="Log"/>
+    </xml>
+</macros>
+ 
--- a/tomo_reconstruct.xml	Wed Apr 06 19:48:22 2022 +0000
+++ b/tomo_reconstruct.xml	Thu Apr 07 21:09:58 2022 +0000
@@ -1,9 +1,9 @@
 <tool id="tomo_reconstruct" name="Tomo Reconstruction" version="0.1.0" python_template_version="3.9">
     <description>Perform a tomography reconstruction</description>
-    <requirements>
-        <requirement type="package" version="1.11.0">tomopy</requirement>
-        <requirement type="package" version="3.6.0">h5py</requirement>
-    </requirements>
+    <macros>
+        <import>tomo_macros.xml</import>
+    </macros>
+    <expand macro="requirements" />
     <command detect_errors="exit_code"><![CDATA[
         $__tool_directory__/tomo_reconstruct.py
         -i '$red_stacks'
@@ -13,24 +13,15 @@
         -l '$log'
     ]]></command>
     <inputs>
+        <expand macro="common_inputs"/>
         <param name="red_stacks" type='data' format='npz' optional='false' label="Reduced stacks"/>
-        <param name="config" type='data' format='yaml' optional='false' label="Input config"/>
     </inputs>
     <outputs>
+        <expand macro="common_outputs"/>
         <data name="output_data" format="npz" label="Reconstructed tomography stacks" from_work_dir="output_data.npz"/>
-        <data name="output_config" format="yaml" label="Output config" from_work_dir="output_config.yaml"/>
-        <data name="log" format="txt" label="Log"/>
     </outputs>
     <help><![CDATA[
         Reconstruct tomography images.
     ]]></help>
-    <citations>
-        <citation type="bibtex">
-@misc{githubsum_files,
-  author = {Verberg, Rolf},
-  year = {2022},
-  title = {Tomo Reconstruction},
-}</citation>
-    </citations>
-
+    <expand macro="citations"/>
 </tool>
--- a/tomo_setup.xml	Wed Apr 06 19:48:22 2022 +0000
+++ b/tomo_setup.xml	Thu Apr 07 21:09:58 2022 +0000
@@ -1,9 +1,9 @@
 <tool id="tomo_setup" name="Tomo Setup" version="0.1.1" python_template_version="3.9">
     <description>Preprocess tomography images</description>
-    <requirements>
-        <requirement type="package" version="1.11.0">tomopy</requirement>
-        <requirement type="package" version="3.6.0">h5py</requirement>
-    </requirements>
+    <macros>
+        <import>tomo_macros.xml</import>
+    </macros>
+    <expand macro="requirements" />
     <command detect_errors="exit_code"><![CDATA[
         cp '$inputfiles' inputfiles.txt &&
         $__tool_directory__/tomo_setup.py
@@ -29,7 +29,7 @@
 ]]></configfile>
     </configfiles>
     <inputs>
-        <param name="config" type='data' format='yaml' optional='false' label="Input config"/>
+        <expand macro="common_inputs"/>
         <section name="thetas" title="Tomography angles">
             <param name="theta_start" type="float" min="0.0" max="360.0" value="0.0" label="Lower bound"/>
             <param name="theta_end" type="float" min="0.0" max="360.0" value="0.0" label="Upper bound"/>
@@ -42,25 +42,16 @@
         </repeat>
     </inputs>
     <outputs>
+        <expand macro="common_outputs"/>
         <data name="inputfiles" format="txt" label="Input files" from_work_dir="inputfiles.txt" hidden="true"/>
         <data name="dark_field" format="png" label="Dark field" from_work_dir="dark_field.png"/>
         <data name="bright_field" format="png" label="Bright field" from_work_dir="bright_field.png"/>
         <data name="tomo" format="png" label="First tomography image" from_work_dir="tomo.png"/>
         <data name="detectorbounds" format="png" label="Detector bounds" from_work_dir="detectorbounds.png"/>
         <data name="output_data" format="npz" label="Preprocessed tomography data" from_work_dir="output_data.npz"/>
-        <data name="output_config" format="yaml" label="Output config" from_work_dir="output_config.yaml"/>
-        <data name="log" format="txt" label="Log"/>
     </outputs>
     <help><![CDATA[
         Preprocess tomography images.
     ]]></help>
-    <citations>
-        <citation type="bibtex">
-@misc{githubsum_files,
-  author = {Verberg, Rolf},
-  year = {2022},
-  title = {Tomo Setup},
-}</citation>
-    </citations>
-
+    <expand macro="citations"/>
 </tool>