From eaf5d28a94028f149311a23f6da505f129eb7d00 Mon Sep 17 00:00:00 2001 From: Axel Garcia Date: Thu, 9 Apr 2026 16:27:56 +0200 Subject: [PATCH 1/2] ENH: Add pctinputprojection_group module --- applications/pctfdk/pctfdk.py | 32 ++-------- applications/pctinputprojection_group.py | 77 ++++++++++++++++++++++++ wrapping/__init_pct__.py | 1 + 3 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 applications/pctinputprojection_group.py diff --git a/applications/pctfdk/pctfdk.py b/applications/pctfdk/pctfdk.py index 23a3075..53306ae 100644 --- a/applications/pctfdk/pctfdk.py +++ b/applications/pctfdk/pctfdk.py @@ -16,16 +16,6 @@ def build_parser(): parser.add_argument( "--geometry", "-g", help="XML geometry file name", type=str, required=True ) - parser.add_argument( - "--path", "-p", help="Path containing projections", type=str, required=True - ) - parser.add_argument( - "--regexp", - "-r", - help="Regular expression to select projection files in path", - type=str, - required=True, - ) parser.add_argument( "--output", "-o", help="Output file name", type=str, required=True ) @@ -35,12 +25,6 @@ def build_parser(): help="Load only one projection per thread in memory", action="store_true", ) - parser.add_argument( - "--wpc", - help="Water precorrection coefficients (default is no correction)", - type=float, - nargs="+", - ) # Ramp filter parser.add_argument( @@ -81,29 +65,21 @@ def build_parser(): help="Copy info from image (origin, size, spacing, direction)", type=str, ) + + pct.add_pctinputprojections_group(parser) + return parser def process(args_info: argparse.Namespace): from itk import RTK as rtk - # Generate file names - names = itk.RegularExpressionSeriesFileNames.New() - names.SetDirectory(args_info.path) - names.SetNumericSort(False) - names.SetRegularExpression(args_info.regexp) - names.SetSubMatch(0) - if args_info.verbose: - print(f"Regular expression matches {len(names.GetFileNames())} file(s)...") - # Projections reader OutputPixelType = itk.F ProjectionImageType = itk.Image[OutputPixelType, 4] ReaderType = rtk.ProjectionsReader[ProjectionImageType] reader = ReaderType.New() - reader.SetFileNames(names.GetFileNames()) - if args_info.wpc: - reader.SetWaterPrecorrectionCoefficients([float(c) for c in args_info.wpc]) + pct.SetProjectionsReaderFromArgParse(reader, args_info) # Geometry if args_info.verbose: diff --git a/applications/pctinputprojection_group.py b/applications/pctinputprojection_group.py new file mode 100644 index 0000000..080ab80 --- /dev/null +++ b/applications/pctinputprojection_group.py @@ -0,0 +1,77 @@ +import itk +from itk import PCT as pct + +__all__ = [ + "add_pctinputprojections_group", + "GetProjectionsFileNamesFromArgParse", +] + + +# Mimicks pctinputprojections_section.ggo +def add_pctinputprojections_group(parser): + pctinputprojections_group = parser.add_argument_group( + "Input projections and their pre-processing" + ) + pctinputprojections_group.add_argument( + "--path", "-p", help="Path containing projections", required=True + ) + pctinputprojections_group.add_argument( + "--regexp", + "-r", + help="Regular expression to select projection files in path", + required=True, + ) + pctinputprojections_group.add_argument( + "--nsort", + help="Numeric sort for regular expression matches", + action="store_true", + ) + pctinputprojections_group.add_argument( + "--submatch", + help="Index of the submatch that will be used to sort matches", + type=int, + default=0, + ) + pctinputprojections_group.add_argument( + "--wpc", + help="Water precorrection coefficients (default is no correction)", + type=float, + nargs="+", + ) + + +# Mimicks GetProjectionsFileNamesFromGgo +def GetProjectionsFileNamesFromArgParse(args_info): + # Generate file names + names = itk.RegularExpressionSeriesFileNames.New() + names.SetDirectory(args_info.path) + names.SetNumericSort(args_info.nsort) + names.SetRegularExpression(args_info.regexp) + names.SetSubMatch(args_info.submatch) + + if args_info.verbose: + print(f"Regular expression matches {len(names.GetFileNames())} file(s)...") + + fileNames = [] + for fn in names.GetFileNames(): + imageio = itk.ImageIOFactory.CreateImageIO( + fn, itk.CommonEnums.IOFileMode_ReadMode + ) + if imageio is None: + print(f"Ignoring file: {fn}") + continue + fileNames.append(fn) + + return fileNames + + +def SetProjectionsReaderFromArgParse(reader, args_info): + fileNames = GetProjectionsFileNamesFromArgParse(args_info) + + # Water precorrection + if args_info.wpc is not None: + reader.SetWaterPrecorrectionCoefficients([float(c) for c in args_info.wpc]) + + # Pass list to projections reader and update information + reader.SetFileNames(fileNames) + reader.UpdateOutputInformation() diff --git a/wrapping/__init_pct__.py b/wrapping/__init_pct__.py index f695de2..1a599b7 100644 --- a/wrapping/__init_pct__.py +++ b/wrapping/__init_pct__.py @@ -9,6 +9,7 @@ "itk.pctversion", "itk.pctargumentparser", "itk.pctExtras", + "itk.pctinputprojection_group", ] for mod_name in pct_submodules: mod = importlib.import_module(mod_name) From 7ce992b3d301746a65c4f0699f309d989a3194ab Mon Sep 17 00:00:00 2001 From: Axel Garcia Date: Fri, 10 Apr 2026 10:24:29 +0200 Subject: [PATCH 2/2] ENH: Automates python application modules discovery --- wrapping/CMakeLists.txt | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/wrapping/CMakeLists.txt b/wrapping/CMakeLists.txt index 4e8115f..e6347d6 100644 --- a/wrapping/CMakeLists.txt +++ b/wrapping/CMakeLists.txt @@ -9,24 +9,21 @@ configure_file( ) file( - GLOB PCT_PYTHON_APP + GLOB PCT_GROUP_SCRIPTS + CONFIGURE_DEPENDS + "${PCT_SOURCE_DIR}/applications/pct*_group.py" +) +file( + GLOB PCT_APP_SCRIPTS CONFIGURE_DEPENDS "${PCT_SOURCE_DIR}/applications/pct*/pct*.py" ) wrap_itk_python_bindings_install(/itk "PCT" __init_pct__.py - ${PCT_PYTHON_APP} + ${PCT_GROUP_SCRIPTS} + ${PCT_APP_SCRIPTS} ${PCT_VERSION_SCRIPT} ${PCT_SOURCE_DIR}/wrapping/pctExtras.py ${PCT_SOURCE_DIR}/applications/pctargumentparser.py ) - -# Copy python applications to the ITK wrapping directory to ensure they can be imported in tests. -# This directory is added to the PYTHONPATH by itk_python_add_test. -if(ITK_DIR) - set(itk_wrap_python_binary_dir "${ITK_DIR}/Wrapping/Generators/Python") -else() - set(itk_wrap_python_binary_dir "${ITK_BINARY_DIR}/Wrapping/Generators/Python") -endif() -file(COPY ${PCT_PYTHON_APP} DESTINATION "${itk_wrap_python_binary_dir}")