From 755991f5bba581b8dfd9c9c3ca24bd99b43d992e Mon Sep 17 00:00:00 2001 From: Tang Cheng <45505657+tctco@users.noreply.github.com> Date: Wed, 20 May 2026 20:54:59 +0800 Subject: [PATCH] Add rigid-only spatial normalization subcommand --- localizer/src/CMakeLists.txt | 2 + .../src/core/common/NormalizationContracts.h | 1 + .../services/SpatialNormalizationService.cpp | 11 ++- .../spatialNormalizations/ModuleCatalog.cpp | 3 +- .../spatialNormalizations/rigid/RigidCLI.cpp | 80 +++++++++++++++++++ .../spatialNormalizations/rigid/RigidCLI.h | 8 ++ localizer/src/tests/test_quick_cli.py | 1 + 7 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 localizer/src/spatialNormalizations/rigid/RigidCLI.cpp create mode 100644 localizer/src/spatialNormalizations/rigid/RigidCLI.h diff --git a/localizer/src/CMakeLists.txt b/localizer/src/CMakeLists.txt index 7c19d8e..dc30760 100644 --- a/localizer/src/CMakeLists.txt +++ b/localizer/src/CMakeLists.txt @@ -180,6 +180,8 @@ target_sources(DCCCcore PRIVATE spatialNormalizations/standard/NormalizeCLI.cpp spatialNormalizations/adni/AdniPetCoreCLI.h spatialNormalizations/adni/AdniPetCoreCLI.cpp + spatialNormalizations/rigid/RigidCLI.h + spatialNormalizations/rigid/RigidCLI.cpp ) # Link libraries diff --git a/localizer/src/core/common/NormalizationContracts.h b/localizer/src/core/common/NormalizationContracts.h index 240043b..63620b3 100644 --- a/localizer/src/core/common/NormalizationContracts.h +++ b/localizer/src/core/common/NormalizationContracts.h @@ -8,6 +8,7 @@ namespace Pipeline { struct SpatialNormalizationOptions { bool useIterativeRigid = false; bool useManualFOV = false; + bool rigidOnly = false; bool enableDebugOutput = false; std::string debugOutputBasePath; bool enableAdniPetCore = false; diff --git a/localizer/src/core/services/SpatialNormalizationService.cpp b/localizer/src/core/services/SpatialNormalizationService.cpp index bee14de..d40b913 100644 --- a/localizer/src/core/services/SpatialNormalizationService.cpp +++ b/localizer/src/core/services/SpatialNormalizationService.cpp @@ -37,15 +37,19 @@ SpatialNormalizationOutput SpatialNormalizationService::normalize(const SpatialN request.options.maxIterations, request.options.convergenceThreshold); output.rigidAlignedImage = normResult.rigidAlignedImage; - output.spatiallyNormalizedImage = normResult.spatiallyNormalizedImage; + output.spatiallyNormalizedImage = request.options.rigidOnly + ? normResult.rigidAlignedImage + : normResult.spatiallyNormalizedImage; } else { auto normResult = normalizer->normalizeWithIntermediateResults(inputImage); output.rigidAlignedImage = normResult.rigidAlignedImage; - output.spatiallyNormalizedImage = normResult.spatiallyNormalizedImage; + output.spatiallyNormalizedImage = request.options.rigidOnly + ? normResult.rigidAlignedImage + : normResult.spatiallyNormalizedImage; } } - if (request.options.enableAdniPetCore) { + if (request.options.enableAdniPetCore && !request.options.rigidOnly) { output.spatiallyNormalizedImage = prepareAdniPetCoreImage( output.rigidAlignedImage, output.spatiallyNormalizedImage); @@ -90,4 +94,3 @@ ImageType::Pointer SpatialNormalizationService::prepareAdniPetCoreImage(ImageTyp } } // namespace Pipeline - diff --git a/localizer/src/spatialNormalizations/ModuleCatalog.cpp b/localizer/src/spatialNormalizations/ModuleCatalog.cpp index 1b3ab39..4bae63a 100644 --- a/localizer/src/spatialNormalizations/ModuleCatalog.cpp +++ b/localizer/src/spatialNormalizations/ModuleCatalog.cpp @@ -1,6 +1,7 @@ #include "ModuleCatalog.h" #include "standard/NormalizeCLI.h" #include "adni/AdniPetCoreCLI.h" +#include "rigid/RigidCLI.h" namespace Pipeline::SpatialNormalization { @@ -8,10 +9,10 @@ std::vector buildCLIModules() { std::vector modules; modules.push_back(Standard::createCLI()); modules.push_back(Adni::createCLI()); + modules.push_back(Rigid::createCLI()); return modules; } } // namespace Pipeline::SpatialNormalization - diff --git a/localizer/src/spatialNormalizations/rigid/RigidCLI.cpp b/localizer/src/spatialNormalizations/rigid/RigidCLI.cpp new file mode 100644 index 0000000..8bfe373 --- /dev/null +++ b/localizer/src/spatialNormalizations/rigid/RigidCLI.cpp @@ -0,0 +1,80 @@ +#include "RigidCLI.h" +#include "../CLIOptions.h" +#include "../../core/common/Filesystem.h" +#include "../../core/di/Bootstrap.h" +#include "../../core/services/IFileService.h" +#include "../../core/services/ISpatialNormalizationService.h" +#include +#include + +namespace Pipeline::SpatialNormalization::Rigid { + +namespace { + +class RigidCLI final : public ISpatialNormalizationCLI { +public: + std::string getSubcommandName() const override { + return "rigid"; + } + + std::string getDescription() const override { + return "Run rigid-only alignment and update affine matrix without resampling"; + } + + void configureArguments(argparse::ArgumentParser& parser) override { + addBaseArguments(parser); + } + + int execute(const argparse::ArgumentParser& parser, const std::string& fullCommand) override { + NormalizeCommandOptions options; + options.inputPath = parser.get("--input"); + options.outputPath = parser.get("--output"); + options.configPath = parser.get("--config"); + options.enableDebugOutput = parser.get("--debug"); + + if (!Common::fs::ensureParentDirectory(options.outputPath)) { + std::cerr << "[rigid] Failed to prepare output directory: " << options.outputPath << std::endl; + return EXIT_FAILURE; + } + + if (options.enableDebugOutput) { + setupDebugOutput(options); + } + + std::cout << "[rigid] Starting processing: " << fullCommand << std::endl; + + BootstrapOptions bootstrapOptions; + bootstrapOptions.configPath = options.configPath; + bootstrapOptions.enableConfigDebug = options.enableDebugOutput; + bootstrapOptions.logTag = "rigid"; + + auto container = Pipeline::buildCoreContainer(bootstrapOptions); + auto spatialService = container->resolve(); + auto fileService = container->resolve(); + + SpatialNormalizationRequest request; + request.inputPath = options.inputPath; + request.skip = false; + request.options.rigidOnly = true; + request.options.enableDebugOutput = options.enableDebugOutput; + request.options.debugOutputBasePath = options.debugOutputBasePath; + + try { + auto output = spatialService->normalize(request); + fileService->saveNormalizedImage({output.spatiallyNormalizedImage, options.outputPath}); + std::cout << "[rigid] Rigid alignment complete. Output saved to " << options.outputPath << std::endl; + return EXIT_SUCCESS; + } catch (const std::exception& ex) { + std::cerr << "[rigid] Processing failed: " << ex.what() << std::endl; + return EXIT_FAILURE; + } + } +}; + +} // namespace + +SpatialNormalizationCLIPtr createCLI() { + return std::make_shared(); +} + +} // namespace Pipeline::SpatialNormalization::Rigid diff --git a/localizer/src/spatialNormalizations/rigid/RigidCLI.h b/localizer/src/spatialNormalizations/rigid/RigidCLI.h new file mode 100644 index 0000000..7ecebc9 --- /dev/null +++ b/localizer/src/spatialNormalizations/rigid/RigidCLI.h @@ -0,0 +1,8 @@ +#pragma once +#include "../../core/interfaces/ISpatialNormalizationCLI.h" + +namespace Pipeline::SpatialNormalization::Rigid { + +SpatialNormalizationCLIPtr createCLI(); + +} // namespace Pipeline::SpatialNormalization::Rigid diff --git a/localizer/src/tests/test_quick_cli.py b/localizer/src/tests/test_quick_cli.py index cdab41e..b9feed7 100644 --- a/localizer/src/tests/test_quick_cli.py +++ b/localizer/src/tests/test_quick_cli.py @@ -19,6 +19,7 @@ {"id": "refactor_adad_help", "args": ["adad", "--help"], "expected_failure": False}, {"id": "normalize_help", "args": ["normalize", "--help"], "expected_failure": False}, {"id": "adni_pet_core_help", "args": ["adni-pet-core", "--help"], "expected_failure": False}, + {"id": "rigid_help", "args": ["rigid", "--help"], "expected_failure": False}, { "id": "refactor_centiloid_basic", "args": ["centiloid", "--input", "{input}", "--output", "{output}"],