# This file was auto generated by Styx.
# Do not edit this file directly.

import typing
import pathlib
from styxdefs import *
import dataclasses

REGISTRATION_METADATA = Metadata(
    id="5f520d7384383baaf15d7e4094d989410172644f.boutiques",
    name="registration",
    package="ants",
    container_image_tag="antsx/ants:v2.5.3",
)


@dataclasses.dataclass
class RegistrationInitialMovingTransform:
    initial_moving_transform: InputPathType
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append(execution.input_file(self.initial_moving_transform))
        return cargs


@dataclasses.dataclass
class RegistrationInitialMovingTransformUseInverse:
    initial_moving_transform: InputPathType
    use_inverse: typing.Literal[0, 1] | None = None
    """Use the inverse of the initial moving transform."""
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        if self.use_inverse is not None:
            cargs.append("[" + execution.input_file(self.initial_moving_transform) + "," + str(self.use_inverse) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationInitialMovingTransformInitializationFeature:
    fixed_image: InputPathType
    moving_image: InputPathType
    initialization_feature: typing.Literal[0, 1, 2]
    """Initialization feature. 0: Geometric center of images, 1: Image
    intensities, 2: Origin of images"""
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("[" + execution.input_file(self.fixed_image) + "," + execution.input_file(self.moving_image) + "," + str(self.initialization_feature) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformRigid:
    gradient_step: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("Rigid[" + str(self.gradient_step) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformAffine:
    gradient_step: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("Affine[" + str(self.gradient_step) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformCompositeAffine:
    gradient_step: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("CompositeAffine[" + str(self.gradient_step) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformSimilarity:
    gradient_step: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("Similarity[" + str(self.gradient_step) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformTranslation:
    gradient_step: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("Translation[" + str(self.gradient_step) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformBspline:
    gradient_step: float
    mesh_size_at_base_level: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("BSpline[" + str(self.gradient_step) + "," + str(self.mesh_size_at_base_level) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformGaussianDisplacementField:
    gradient_step: float
    update_field_variance_in_voxel_space: float
    total_field_variance_in_voxel_space: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("GaussianDisplacementField[" + str(self.gradient_step) + "," + str(self.update_field_variance_in_voxel_space) + "," + str(self.total_field_variance_in_voxel_space) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformBsplineDisplacementField:
    gradient_step: float
    update_field_mesh_size_at_base_level: float
    total_field_mesh_size_at_base_level: float | None = None
    spline_order: float | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        if self.total_field_mesh_size_at_base_level is not None and self.spline_order is not None:
            cargs.append("BSplineDisplacementField[" + str(self.gradient_step) + "," + str(self.update_field_mesh_size_at_base_level) + "," + str(self.total_field_mesh_size_at_base_level) + str(self.spline_order) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformTimeVaryingVelocityField:
    gradient_step: float
    number_of_time_indices: float
    update_field_variance_in_voxel_space: float
    update_field_time_variance: float
    total_field_variance_in_voxel_space: float
    total_field_time_variance: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("TimeVaryingVelocityField[" + str(self.gradient_step) + "," + str(self.number_of_time_indices) + "," + str(self.update_field_variance_in_voxel_space) + "," + str(self.update_field_time_variance) + "," + str(self.total_field_variance_in_voxel_space) + "," + str(self.total_field_time_variance) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformTimeVaryingBsplineVelocityField:
    gradient_step: float
    velocity_field_mesh_size: float
    number_of_time_point_samples: float | None = None
    spline_order: float | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        if self.number_of_time_point_samples is not None and self.spline_order is not None:
            cargs.append("TimeVaryingBSplineVelocityField[" + str(self.gradient_step) + "," + str(self.velocity_field_mesh_size) + "," + str(self.number_of_time_point_samples) + "," + str(self.spline_order) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformSyn:
    gradient_step: float
    update_field_variance_in_voxel_space: float
    total_field_variance_in_voxel_space: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("SyN[" + str(self.gradient_step) + "," + str(self.update_field_variance_in_voxel_space) + "," + str(self.total_field_variance_in_voxel_space) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformBsplineSyn:
    gradient_step: float
    update_field_mesh_size_at_base_level: float
    total_field_mesh_size_at_base_level: float | None = None
    spline_order: float | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        if self.total_field_mesh_size_at_base_level is not None and self.spline_order is not None:
            cargs.append("BSplineSyN[" + str(self.gradient_step) + "," + str(self.update_field_mesh_size_at_base_level) + "," + str(self.total_field_mesh_size_at_base_level) + "," + str(self.spline_order) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformExponential:
    gradient_step: float
    update_field_variance_in_voxel_space: float
    velocity_field_variance_in_voxel_space: float
    number_of_integration_steps: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("Exponential[" + str(self.gradient_step) + "," + str(self.update_field_variance_in_voxel_space) + "," + str(self.velocity_field_variance_in_voxel_space) + "," + str(self.number_of_integration_steps) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationTransformBsplineExponential:
    gradient_step: float
    update_field_mesh_size_at_base_level: float
    velocity_field_mesh_size_at_base_level: float | None = None
    number_of_integration_steps: float | None = None
    spline_order: float | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        if self.velocity_field_mesh_size_at_base_level is not None and self.number_of_integration_steps is not None and self.spline_order is not None:
            cargs.append("BSplineExponential[" + str(self.gradient_step) + "," + str(self.update_field_mesh_size_at_base_level) + "," + str(self.velocity_field_mesh_size_at_base_level) + "," + str(self.number_of_integration_steps) + "," + str(self.spline_order) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricAntsNeighbourhoodCrossCorrelation:
    fixed_image: str
    moving_image: str
    metric_weight: float
    radius: float | None = None
    sampling_strategy: typing.Literal["None", "Regular", "Random"] | None = None
    sampling_percentage: float | None = None
    use_gradient_filter: typing.Literal["true", "false"] | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.radius is not None and self.sampling_strategy is not None and self.sampling_percentage is not None and self.use_gradient_filter is not None:
            cargs.append("CC[" + self.fixed_image + "," + self.moving_image + "," + str(self.metric_weight) + "," + str(self.radius) + "," + self.sampling_strategy + "," + str(self.sampling_percentage) + "," + self.use_gradient_filter + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricMutualInformation:
    fixed_image: str
    moving_image: str
    metric_weight: float
    number_of_bins: float | None = None
    sampling_strategy: typing.Literal["None", "Regular", "Random"] | None = None
    sampling_percentage: float | None = None
    use_gradient_filter: typing.Literal["true", "false"] | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.number_of_bins is not None and self.sampling_strategy is not None and self.sampling_percentage is not None and self.use_gradient_filter is not None:
            cargs.append("MI[" + self.fixed_image + "," + self.moving_image + "," + str(self.metric_weight) + "," + str(self.number_of_bins) + "," + self.sampling_strategy + "," + str(self.sampling_percentage) + "," + self.use_gradient_filter + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricMattes:
    fixed_image: str
    moving_image: str
    metric_weight: float
    number_of_bins: float | None = None
    sampling_strategy: typing.Literal["None", "Regular", "Random"] | None = None
    sampling_percentage: float | None = None
    use_gradient_filter: typing.Literal["true", "false"] | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.number_of_bins is not None and self.sampling_strategy is not None and self.sampling_percentage is not None and self.use_gradient_filter is not None:
            cargs.append("Mattes[" + self.fixed_image + "," + self.moving_image + "," + str(self.metric_weight) + "," + str(self.number_of_bins) + "," + self.sampling_strategy + "," + str(self.sampling_percentage) + "," + self.use_gradient_filter + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricMeanSquares:
    fixed_image: str
    moving_image: str
    metric_weight: float
    radius: float | None = None
    sampling_strategy: typing.Literal["None", "Regular", "Random"] | None = None
    sampling_percentage: float | None = None
    use_gradient_filter: typing.Literal["true", "false"] | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.radius is not None and self.sampling_strategy is not None and self.sampling_percentage is not None and self.use_gradient_filter is not None:
            cargs.append("MeanSquares[" + self.fixed_image + "," + self.moving_image + "," + str(self.metric_weight) + "," + str(self.radius) + "," + self.sampling_strategy + "," + str(self.sampling_percentage) + "," + self.use_gradient_filter + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricDemons:
    fixed_image: str
    moving_image: str
    metric_weight: float
    number_of_bins: float | None = None
    sampling_strategy: typing.Literal["None", "Regular", "Random"] | None = None
    sampling_percentage: float | None = None
    use_gradient_filter: typing.Literal["true", "false"] | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.number_of_bins is not None and self.sampling_strategy is not None and self.sampling_percentage is not None and self.use_gradient_filter is not None:
            cargs.append("Demons[" + self.fixed_image + "," + self.moving_image + "," + str(self.metric_weight) + "," + str(self.number_of_bins) + "," + self.sampling_strategy + "," + str(self.sampling_percentage) + "," + self.use_gradient_filter + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricGlobalCorrelation:
    fixed_image: str
    moving_image: str
    metric_weight: float
    radius: float | None = None
    sampling_strategy: typing.Literal["None", "Regular", "Random"] | None = None
    sampling_percentage: float | None = None
    use_gradient_filter: typing.Literal["true", "false"] | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.radius is not None and self.sampling_strategy is not None and self.sampling_percentage is not None and self.use_gradient_filter is not None:
            cargs.append("GC[" + self.fixed_image + "," + self.moving_image + "," + str(self.metric_weight) + "," + str(self.radius) + "," + self.sampling_strategy + "," + str(self.sampling_percentage) + "," + self.use_gradient_filter + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricEuclideanIcp:
    fixed_point_set: str
    moving_point_set: str
    metric_weight: float
    sampling_percentage: float | None = None
    boundary_points_only: typing.Literal["0"] | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.sampling_percentage is not None and self.boundary_points_only is not None:
            cargs.append("ICP[" + self.fixed_point_set + "," + self.moving_point_set + "," + str(self.metric_weight) + "," + str(self.sampling_percentage) + "," + self.boundary_points_only + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricPointSetExpectation:
    metric_weight: float
    point_set_sigma: float | None = None
    sampling_percentage: float | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.point_set_sigma is not None and self.sampling_percentage is not None:
            cargs.append("PSE[[FIXED_IMAGE],[MOVING_IMAGE]," + str(self.metric_weight) + "," + "," + str(self.point_set_sigma) + "[SAMPLING_STRATEGY]" + "," + str(self.sampling_percentage) + "[USE_GRADIENT_FILTER]]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricJensenHavrdaCharvetTsallis:
    fixed_point_set: str
    moving_point_set: str
    metric_weight: float
    sampling_percentage: float | None = None
    boundary_points_only: typing.Literal["0"] | None = None
    point_set_sigma: float | None = None
    k_neighborhood: float | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        if sampling_percentage is not None and not (0 <= sampling_percentage <= 1): 
            raise ValueError(f"'sampling_percentage' must be between 0 <= x <= 1 but was {sampling_percentage}")
        cargs = []
        if self.sampling_percentage is not None and self.boundary_points_only is not None and self.point_set_sigma is not None and self.k_neighborhood is not None:
            cargs.append("JHCT[" + self.fixed_point_set + "," + self.moving_point_set + "," + str(self.metric_weight) + "," + str(self.sampling_percentage) + "," + self.boundary_points_only + "," + str(self.point_set_sigma) + "," + str(self.k_neighborhood) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMetricIgdm:
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("IGDM[")
        return cargs


@dataclasses.dataclass
class RegistrationConvergence:
    """
    Convergence is determined from the number of iterations per level and is
    determined by fitting a line to the normalized energy profile of the last N
    iterations (where N is specified by the window size) and determining the
    slope which is then compared with the convergence threshold. .
    """
    convergence: str
    convergence_threshold: float
    convergence_window_size: int
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("[" + self.convergence + "," + str(self.convergence_threshold) + "," + str(self.convergence_window_size) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationStage:
    """
    Stages of the registration process.
    """
    transform: typing.Union[RegistrationTransformRigid, RegistrationTransformAffine, RegistrationTransformCompositeAffine, RegistrationTransformSimilarity, RegistrationTransformTranslation, RegistrationTransformBspline, RegistrationTransformGaussianDisplacementField, RegistrationTransformBsplineDisplacementField, RegistrationTransformTimeVaryingVelocityField, RegistrationTransformTimeVaryingBsplineVelocityField, RegistrationTransformSyn, RegistrationTransformBsplineSyn, RegistrationTransformExponential, RegistrationTransformBsplineExponential]
    """Several transform options are available. The gradientStep or learningRate
    characterizes the gradient descent optimization and is scaled appropriately
    for each transform using the shift scales estimator. Subsequent parameters
    are transform-specific and can be determined from the usage. For the
    B-spline transforms one can also specify the smoothing in terms of spline
    distance (i.e. knot spacing)."""
    metric: typing.Union[RegistrationMetricAntsNeighbourhoodCrossCorrelation, RegistrationMetricMutualInformation, RegistrationMetricMattes, RegistrationMetricMeanSquares, RegistrationMetricDemons, RegistrationMetricGlobalCorrelation, RegistrationMetricEuclideanIcp, RegistrationMetricPointSetExpectation, RegistrationMetricJensenHavrdaCharvetTsallis, RegistrationMetricIgdm]
    """These image metrics are available--- CC: ANTS neighborhood cross
    correlation, MI: Mutual information, Demons: (Thirion), MeanSquares, and GC:
    Global Correlation. The "metricWeight" variable is used to modulate the per
    stage weighting of the metrics. The metrics can also employ a sampling
    strategy defined by a sampling percentage. The sampling strategy defaults to
    'None' (aka a dense sampling of one sample per voxel), otherwise it defines
    a point set over which to optimize the metric. The point set can be on a
    regular lattice or a random lattice of points slightly perturbed to minimize
    aliasing artifacts. samplingPercentage defines the fraction of points to
    select from the domain. useGradientFilter specifies whether a
    smoothingfilter is applied when estimating the metric gradient.In addition,
    three point set metrics are available: Euclidean (ICP), Point-set
    expectation (PSE), and Jensen-Havrda-Charvet-Tsallis (JHCT)."""
    convergence: RegistrationConvergence
    """Convergence is determined from the number of iterations per level and is
    determined by fitting a line to the normalized energy profile of the last N
    iterations (where N is specified by the window size) and determining the
    slope which is then compared with the convergence threshold. """
    smoothing_sigmas: str
    """Specify the sigma of gaussian smoothing at each level. Units are given in
    terms of voxels ('vox') or physical spacing ('mm'). Example usage is
    '4x2x1mm' and '4x2x1vox' where no units implies voxel spacing."""
    shrink_factors: str
    """Specify the shrink factor for the virtual domain (typically the fixed
    image) at each level."""
    use_histogram_matching: typing.Literal[0, 1] | None = None
    """Use histogram matching."""
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.extend([
            "--transform",
            *self.transform.run(execution)
        ])
        cargs.extend([
            "--metric",
            *self.metric.run(execution)
        ])
        cargs.extend([
            "--convergence",
            *self.convergence.run(execution)
        ])
        cargs.extend([
            "--smoothing-sigmas",
            self.smoothing_sigmas
        ])
        cargs.extend([
            "--shrink-factors",
            self.shrink_factors
        ])
        if self.use_histogram_matching is not None:
            cargs.extend([
                "--use-histogram-matching",
                str(self.use_histogram_matching)
            ])
        return cargs


@dataclasses.dataclass
class RegistrationWinsorizeImageIntensities:
    """
    Winsorize data based on specified quantiles.
    """
    lower_quantile: float
    upper_quantile: float
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        cargs.append("[" + str(self.lower_quantile) + str(self.upper_quantile) + "]")
        return cargs


@dataclasses.dataclass
class RegistrationMasks:
    """
    Image masks to limit voxels considered by the metric. Two options are
    allowed for mask specification: 1) Either the user specifies a single mask
    to be used for all stages or 2) the user specifies a mask for each stage.
    With the latter one can select to which stages masks are applied by
    supplying valid file names. If the file does not exist, a mask will not be
    used for that stage. Note that we handle the fixed and moving masks
    separately to enforce this constraint.
    """
    fixed_mask: str | None = None
    moving_mask: str | None = None
    
    def run(
        self,
        execution: Execution,
    ) -> list[str]:
        """
        Build command line arguments. This method is called by the main command.
        
        Args:
            execution: The execution object.
        Returns:
            Command line arguments
        """
        cargs = []
        if self.fixed_mask is not None and self.moving_mask is not None:
            cargs.append("[" + self.fixed_mask + self.moving_mask + "]")
        return cargs


class RegistrationOutputs(typing.NamedTuple):
    """
    Output object returned when calling `registration(...)`.
    """
    root: OutputPathType
    """Output root folder. This is the root folder for all outputs."""
    generic_affine: OutputPathType | None
    """The output is the generic affine transformation matrix."""
    inverse_warped: OutputPathType | None
    """The output is the warped fixed image."""
    inverse_warp: OutputPathType | None
    """The output is the inverse warp field."""
    warped: OutputPathType | None
    """The output is the warped moving image."""
    warp: OutputPathType | None
    """The output is the warp field."""


def registration(
    stages: list[RegistrationStage],
    dimensionality: typing.Literal[2, 3, 4] | None = None,
    output: str | None = None,
    save_state: str | None = None,
    restore_state: str | None = None,
    write_composite_transform: typing.Literal[0, 1] | None = None,
    print_similarity_measure_interval: int | None = None,
    write_interval_volumes: int | None = None,
    collapse_output_transforms: typing.Literal[1, 0] | None = None,
    initialize_transforms_per_stage: typing.Literal[1, 0] | None = None,
    interpolation: typing.Literal["Linear", "NearestNeighbor", "MultiLabel", "Gaussian", "BSpline", "CosineWindowedSinc", "WelchWindowedSinc", "HammingWindowedSinc", "LanczosWindowedSinc", "GenericLabel"] | None = None,
    restrict_deformation: list[typing.Literal[0, 1]] | None = None,
    initial_fixed_transform: str | None = None,
    initial_moving_transform: typing.Union[RegistrationInitialMovingTransform, RegistrationInitialMovingTransformUseInverse, RegistrationInitialMovingTransformInitializationFeature] | None = None,
    winsorize_image_intensities: RegistrationWinsorizeImageIntensities | None = None,
    masks: RegistrationMasks | None = None,
    minc: typing.Literal[0, 1] | None = None,
    random_seed: int | None = None,
    verbose: typing.Literal[0, 1] | None = None,
    runner: Runner | None = None,
) -> RegistrationOutputs:
    """
    This program is a user-level registration application meant to utilize classes
    in ITK v4.0 and later. The user can specify any number of "stages" where a stage
    consists of a transform; an image metric; and iterations, shrink factors, and
    smoothing sigmas for each level. Note that explicitly setting the
    dimensionality, metric, transform, output, convergence, shrink-factors, and
    smoothing-sigmas parameters is mandatory.
    
    Author: Advanced Normalization Tools (ANTs) Contributors
    
    URL: https://github.com/ANTsX/ANTs
    
    Args:
        stages: Stages of the registration process.
        dimensionality: This option forces the image to be treated as a\
            specified-dimensional image. If not specified, we try to infer the\
            dimensionality from the input image.
        output: Specify the output transform prefix (output format is .nii.gz\
            ). Optionally, one can choose to warp the moving image to the fixed\
            space and, if the inverse transform exists, one can also output the\
            warped fixed image. Note that only the images specified in the first\
            metric call are warped. Use antsApplyTransforms to warp other images\
            using the resultant transform(s). When a composite transform is not\
            specified, linear transforms are specified with a '.mat' suffix and\
            displacement fields with a 'Warp.nii.gz' suffix (and\
            'InverseWarp.nii.gz', when applicable. In addition, for velocity-based\
            transforms, the full velocity field is written to file\
            ('VelocityField.nii.gz') as long as the collapse transforms flag is\
            turned off ('-z 0').
        save_state: Specify the output file for the current state of the\
            registration. The state file is written to an hdf5 composite file. It\
            is specially usefull if we want to save the current state of a SyN\
            registration to the disk, so we can load and restore that later to\
            continue the next registration process directly started from the last\
            saved state. The output file of this flag is the same as the\
            write-composite-transform, unless the last transform is a SyN\
            transform. In that case, the inverse displacement field of the SyN\
            transform is also added to the output composite transform. Again notice\
            that this file cannot be treated as a transform, and restore-state\
            option must be used to load the written file by this flag.
        restore_state: Specify the initial state of the registration which get\
            immediately used to directly initialize the registration process. The\
            flag is mutually exclusive with other intialization flags.If this flag\
            is used, none of the initial-moving-transform and\
            initial-fixed-transform cannot be used.
        write_composite_transform: Boolean specifying whether or not the\
            composite transform (and its inverse, if it exists) should be written\
            to an hdf5 composite file. This is false by default so that only the\
            transform for each stage is written to file.
        print_similarity_measure_interval: Prints out the CC similarity metric\
            measure between the full-size input fixed and the transformed moving\
            images at each iteration a value of 0 (the default) indicates that the\
            full scale computation should not take placeany value greater than 0\
            represents the interval of full scale metric computation.
        write_interval_volumes: Writes out the output volume at each iteration.\
            It helps to present the registration process as a short movie a value\
            of 0 (the default) indicates that this option should not take placeany\
            value greater than 0 represents the interval between the iterations\
            which outputs are written to the disk.
        collapse_output_transforms: Collapse output transforms. Specifically,\
            enabling this option combines all adjacent transforms where possible.\
            All adjacent linear transforms are written to disk in the form of an\
            itk affine transform (called xxxGenericAffine.mat).\
            Similarly, all adjacent displacement field transforms are combined\
            when written to disk (e.g. xxxWarp.nii.gz and xxxInverseWarp.nii.gz\
            (if available)). Also, an output composite transform including the\
            collapsed transforms is written to the disk (called\
            outputCollapsed(Inverse)Composite).
        initialize_transforms_per_stage: Initialize linear transforms from the\
            previous stage. By enabling this option, the current linear stage\
            transform is directly intialized from the previous stage's linear\
            transform; this allows multiple linear stages to be run where each\
            stage directly updates the estimated linear transform from the previous\
            stage. (e.g. Translation -> Rigid -> Affine).
        interpolation: Several interpolation options are available in ITK.\
            These have all been made available. Currently the interpolator choice\
            is only used to warp (and possibly inverse warp) the final output\
            image(s).
        restrict_deformation: This option allows the user to restrict the\
            optimization of the displacement field, translation, rigid or affine\
            transform on a per-component basis. For example, if one wants to limit\
            the deformation or rotation of 3-D volume to the first two dimensions,\
            this is possible by specifying a weight vector of '1x1x0' for a\
            deformation field or '1x1x0x1x1x0' for a rigid transformation.\
            Low-dimensional restriction only works if there are no preceding\
            transformations.All stages up to and including the desired stage must\
            have this option specified,even if they should not be restricted (in\
            which case specify 1x1x1...).
        initial_fixed_transform: Specify the initial fixed transform(s) which\
            get immediately incorporated into the composite transform. The order of\
            the transforms is stack-esque in that the last transform specified on\
            the command line is the first to be applied. In addition to\
            initialization with ITK transforms, the user can perform an initial\
            translation alignment by specifying the fixed and moving images and\
            selecting an initialization feature. These features include using the\
            geometric center of the images (=0), the image intensities (=1), or the\
            origin of the images (=2).
        initial_moving_transform: Specify the initial moving transform(s) which\
            get immediately incorporated into the composite transform. The order of\
            the transforms is stack-esque in that the last transform specified on\
            the command line is the first to be applied. In addition to\
            initialization with ITK transforms, the user can perform an initial\
            translation alignment by specifying the fixed and moving images and\
            selecting an initialization feature. These features include using the\
            geometric center of the images (=0), the image intensities (=1), or the\
            origin of the images (=2).
        winsorize_image_intensities: Winsorize data based on specified\
            quantiles.
        masks: Image masks to limit voxels considered by the metric. Two\
            options are allowed for mask specification: 1) Either the user\
            specifies a single mask to be used for all stages or 2) the user\
            specifies a mask for each stage. With the latter one can select to\
            which stages masks are applied by supplying valid file names. If the\
            file does not exist, a mask will not be used for that stage. Note that\
            we handle the fixed and moving masks separately to enforce this\
            constraint.
        minc: Use MINC file formats for transformations.
        random_seed: Random seed.
        verbose: Verbose output.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `RegistrationOutputs`).
    """
    if write_interval_volumes is not None and not (0 <= write_interval_volumes): 
        raise ValueError(f"'write_interval_volumes' must be greater than 0 <= x but was {write_interval_volumes}")
    if random_seed is not None and not (1 <= random_seed): 
        raise ValueError(f"'random_seed' must be greater than 1 <= x but was {random_seed}")
    runner = runner or get_global_runner()
    execution = runner.start_execution(REGISTRATION_METADATA)
    cargs = []
    cargs.append("antsRegistration")
    if dimensionality is not None:
        cargs.extend([
            "--dimensionality",
            str(dimensionality)
        ])
    if output is not None:
        cargs.extend([
            "-o",
            output
        ])
    if save_state is not None:
        cargs.extend([
            "-j",
            save_state
        ])
    if restore_state is not None:
        cargs.extend([
            "-k",
            restore_state
        ])
    if write_composite_transform is not None:
        cargs.extend([
            "-a",
            str(write_composite_transform)
        ])
    if print_similarity_measure_interval is not None:
        cargs.extend([
            "-p",
            str(print_similarity_measure_interval)
        ])
    if write_interval_volumes is not None:
        cargs.extend([
            "--write-interval-volumes",
            str(write_interval_volumes)
        ])
    if collapse_output_transforms is not None:
        cargs.extend([
            "--collapse-output-transforms",
            str(collapse_output_transforms)
        ])
    if initialize_transforms_per_stage is not None:
        cargs.extend([
            "-i",
            str(initialize_transforms_per_stage)
        ])
    if interpolation is not None:
        cargs.extend([
            "--interpolation",
            interpolation
        ])
    if restrict_deformation is not None:
        cargs.extend([
            "-g",
            "x".join(map(str, restrict_deformation))
        ])
    if initial_fixed_transform is not None:
        cargs.extend([
            "-q",
            initial_fixed_transform
        ])
    if initial_moving_transform is not None:
        cargs.extend([
            "--initial-moving-transform",
            *initial_moving_transform.run(execution)
        ])
    cargs.extend([a for c in [s.run(execution) for s in stages] for a in c])
    if winsorize_image_intensities is not None:
        cargs.extend([
            "--winsorize-image-intensities",
            *winsorize_image_intensities.run(execution)
        ])
    if masks is not None:
        cargs.extend([
            "--masks",
            *masks.run(execution)
        ])
    if minc is not None:
        cargs.extend([
            "--minc",
            str(minc)
        ])
    if random_seed is not None:
        cargs.extend([
            "--random-seed",
            str(random_seed)
        ])
    if verbose is not None:
        cargs.extend([
            "-v",
            str(verbose)
        ])
    ret = RegistrationOutputs(
        root=execution.output_file("."),
        generic_affine=execution.output_file(output + "0GenericAffine.mat") if (output is not None) else None,
        inverse_warped=execution.output_file(output + "InverseWarped.nii.gz") if (output is not None) else None,
        inverse_warp=execution.output_file(output + "1InverseWarp.nii.gz") if (output is not None) else None,
        warped=execution.output_file(output + "Warped.nii.gz") if (output is not None) else None,
        warp=execution.output_file(output + "1Warp.nii.gz") if (output is not None) else None,
    )
    execution.run(cargs)
    return ret


__all__ = [
    "REGISTRATION_METADATA",
    "RegistrationConvergence",
    "RegistrationInitialMovingTransform",
    "RegistrationInitialMovingTransformInitializationFeature",
    "RegistrationInitialMovingTransformUseInverse",
    "RegistrationMasks",
    "RegistrationMetricAntsNeighbourhoodCrossCorrelation",
    "RegistrationMetricDemons",
    "RegistrationMetricEuclideanIcp",
    "RegistrationMetricGlobalCorrelation",
    "RegistrationMetricIgdm",
    "RegistrationMetricJensenHavrdaCharvetTsallis",
    "RegistrationMetricMattes",
    "RegistrationMetricMeanSquares",
    "RegistrationMetricMutualInformation",
    "RegistrationMetricPointSetExpectation",
    "RegistrationOutputs",
    "RegistrationStage",
    "RegistrationTransformAffine",
    "RegistrationTransformBspline",
    "RegistrationTransformBsplineDisplacementField",
    "RegistrationTransformBsplineExponential",
    "RegistrationTransformBsplineSyn",
    "RegistrationTransformCompositeAffine",
    "RegistrationTransformExponential",
    "RegistrationTransformGaussianDisplacementField",
    "RegistrationTransformRigid",
    "RegistrationTransformSimilarity",
    "RegistrationTransformSyn",
    "RegistrationTransformTimeVaryingBsplineVelocityField",
    "RegistrationTransformTimeVaryingVelocityField",
    "RegistrationTransformTranslation",
    "RegistrationWinsorizeImageIntensities",
    "registration",
]
