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

import typing
import pathlib
from styxdefs import *

ANTS_JOINT_FUSION_METADATA = Metadata(
    id="b57f087fd4ad3a9b8192927bfb3d0e52df02a072.boutiques",
    name="antsJointFusion",
    package="ants",
    container_image_tag="antsx/ants:v2.5.3",
)


_AntsJointFusionParamsDictNoTag = typing.TypedDict('_AntsJointFusionParamsDictNoTag', {
    "image_dimensionality": typing.NotRequired[typing.Literal[2, 3, 4] | None],
    "target_image": list[InputPathType],
    "atlas_image": list[InputPathType],
    "atlas_segmentation": InputPathType,
    "alpha": typing.NotRequired[float | None],
    "beta": typing.NotRequired[float | None],
    "constrain_nonnegative": typing.NotRequired[bool | None],
    "patch_radius": typing.NotRequired[str | None],
    "patch_metric": typing.NotRequired[typing.Literal["PC", "MSQ"] | None],
    "search_radius": typing.NotRequired[str | None],
    "exclusion_image": typing.NotRequired[InputPathType | None],
    "mask_image": typing.NotRequired[InputPathType | None],
    "output": str,
    "verbose": typing.NotRequired[bool | None],
})
AntsJointFusionParamsDictTagged = typing.TypedDict('AntsJointFusionParamsDictTagged', {
    "@type": typing.Literal["ants/antsJointFusion"],
    "image_dimensionality": typing.NotRequired[typing.Literal[2, 3, 4] | None],
    "target_image": list[InputPathType],
    "atlas_image": list[InputPathType],
    "atlas_segmentation": InputPathType,
    "alpha": typing.NotRequired[float | None],
    "beta": typing.NotRequired[float | None],
    "constrain_nonnegative": typing.NotRequired[bool | None],
    "patch_radius": typing.NotRequired[str | None],
    "patch_metric": typing.NotRequired[typing.Literal["PC", "MSQ"] | None],
    "search_radius": typing.NotRequired[str | None],
    "exclusion_image": typing.NotRequired[InputPathType | None],
    "mask_image": typing.NotRequired[InputPathType | None],
    "output": str,
    "verbose": typing.NotRequired[bool | None],
})
AntsJointFusionParamsDict = _AntsJointFusionParamsDictNoTag | AntsJointFusionParamsDictTagged


class AntsJointFusionOutputs(typing.NamedTuple):
    """
    Output object returned when calling `AntsJointFusionParamsDict(...)`.
    """
    root: OutputPathType
    """Output root folder. This is the root folder for all outputs."""
    label_fusion_image: OutputPathType
    """The output label fusion image."""
    intensity_fusion_image: OutputPathType
    """The output intensity fusion image format."""
    label_posterior_probability_image: OutputPathType
    """The output label posterior probability image format."""
    atlas_voting_weight_image: OutputPathType
    """The output atlas voting weight image format."""


def ants_joint_fusion_params(
    target_image: list[InputPathType],
    atlas_image: list[InputPathType],
    atlas_segmentation: InputPathType,
    output: str,
    image_dimensionality: typing.Literal[2, 3, 4] | None = None,
    alpha: float | None = None,
    beta: float | None = None,
    constrain_nonnegative: bool | None = None,
    patch_radius: str | None = None,
    patch_metric: typing.Literal["PC", "MSQ"] | None = None,
    search_radius: str | None = None,
    exclusion_image: InputPathType | None = None,
    mask_image: InputPathType | None = None,
    verbose: bool | None = None,
) -> AntsJointFusionParamsDictTagged:
    """
    Build parameters.
    
    Args:
        target_image: The target image (or multimodal target images) assumed to\
            be aligned to a common image domain.
        atlas_image: The atlas image (or multimodal atlas images) assumed to be\
            aligned to a common image domain.
        atlas_segmentation: The atlas segmentation images. For performing label\
            fusion the number of specified segmentations should be identical to the\
            number of atlas image sets.
        output: The output is the intensity and/or label fusion image.\
            Additional optional outputs include the label posterior probability\
            images and the atlas voting weight images.
        image_dimensionality: This option forces the image to be treated as a\
            specified-dimensional image. If not specified, the program tries to\
            infer the dimensionality from the input image.
        alpha: Regularization term added to matrix Mx for calculating the\
            inverse. Default = 0.1.
        beta: Exponent for mapping intensity difference to the joint error.\
            Default = 2.0.
        constrain_nonnegative: Constrain solution to non-negative weights.
        patch_radius: Patch radius for similarity measures. Default = 2x2x2.
        patch_metric: Metric to be used in determining the most similar\
            neighborhood patch. Options include Pearson's correlation (PC) and mean\
            squares (MSQ). Default = PC (Pearson correlation).
        search_radius: Search radius for similarity measures. Default = 3x3x3.\
            One can also specify an image where the value at the voxel specifies\
            the isotropic search radius at that voxel.
        exclusion_image: Specify an exclusion region for the given label.
        mask_image: If a mask image is specified, fusion is only performed in\
            the mask region.
        verbose: Verbose output.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "ants/antsJointFusion",
        "target_image": target_image,
        "atlas_image": atlas_image,
        "atlas_segmentation": atlas_segmentation,
        "output": output,
    }
    if image_dimensionality is not None:
        params["image_dimensionality"] = image_dimensionality
    if alpha is not None:
        params["alpha"] = alpha
    if beta is not None:
        params["beta"] = beta
    if constrain_nonnegative is not None:
        params["constrain_nonnegative"] = constrain_nonnegative
    if patch_radius is not None:
        params["patch_radius"] = patch_radius
    if patch_metric is not None:
        params["patch_metric"] = patch_metric
    if search_radius is not None:
        params["search_radius"] = search_radius
    if exclusion_image is not None:
        params["exclusion_image"] = exclusion_image
    if mask_image is not None:
        params["mask_image"] = mask_image
    if verbose is not None:
        params["verbose"] = verbose
    return params


def ants_joint_fusion_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `AntsJointFusionParamsDict` object.
    
    Args:
        params: The parameters object to validate.
    """
    if params is None or not isinstance(params, dict):
        raise StyxValidationError(f'Params object has the wrong type \'{type(params)}\'')
    if params.get("image_dimensionality", None) is not None:
        if not isinstance(params["image_dimensionality"], int):
            raise StyxValidationError(f'`image_dimensionality` has the wrong type: Received `{type(params.get("image_dimensionality", None))}` expected `typing.Literal[2, 3, 4] | None`')
        if params["image_dimensionality"] not in [2, 3, 4]:
            raise StyxValidationError("Parameter `image_dimensionality` must be one of [2, 3, 4]")
    if params.get("target_image", None) is None:
        raise StyxValidationError("`target_image` must not be None")
    if not isinstance(params["target_image"], list):
        raise StyxValidationError(f'`target_image` has the wrong type: Received `{type(params.get("target_image", None))}` expected `list[InputPathType]`')
    for e in params["target_image"]:
        if not isinstance(e, (pathlib.Path, str)):
            raise StyxValidationError(f'`target_image` has the wrong type: Received `{type(params.get("target_image", None))}` expected `list[InputPathType]`')
    if params.get("atlas_image", None) is None:
        raise StyxValidationError("`atlas_image` must not be None")
    if not isinstance(params["atlas_image"], list):
        raise StyxValidationError(f'`atlas_image` has the wrong type: Received `{type(params.get("atlas_image", None))}` expected `list[InputPathType]`')
    for e in params["atlas_image"]:
        if not isinstance(e, (pathlib.Path, str)):
            raise StyxValidationError(f'`atlas_image` has the wrong type: Received `{type(params.get("atlas_image", None))}` expected `list[InputPathType]`')
    if params.get("atlas_segmentation", None) is None:
        raise StyxValidationError("`atlas_segmentation` must not be None")
    if not isinstance(params["atlas_segmentation"], (pathlib.Path, str)):
        raise StyxValidationError(f'`atlas_segmentation` has the wrong type: Received `{type(params.get("atlas_segmentation", None))}` expected `InputPathType`')
    if params.get("alpha", None) is not None:
        if not isinstance(params["alpha"], (float, int)):
            raise StyxValidationError(f'`alpha` has the wrong type: Received `{type(params.get("alpha", None))}` expected `float | None`')
    if params.get("beta", None) is not None:
        if not isinstance(params["beta"], (float, int)):
            raise StyxValidationError(f'`beta` has the wrong type: Received `{type(params.get("beta", None))}` expected `float | None`')
    if params.get("constrain_nonnegative", None) is not None:
        if not isinstance(params["constrain_nonnegative"], bool):
            raise StyxValidationError(f'`constrain_nonnegative` has the wrong type: Received `{type(params.get("constrain_nonnegative", None))}` expected `bool | None`')
    if params.get("patch_radius", None) is not None:
        if not isinstance(params["patch_radius"], str):
            raise StyxValidationError(f'`patch_radius` has the wrong type: Received `{type(params.get("patch_radius", None))}` expected `str | None`')
    if params.get("patch_metric", None) is not None:
        if not isinstance(params["patch_metric"], str):
            raise StyxValidationError(f'`patch_metric` has the wrong type: Received `{type(params.get("patch_metric", None))}` expected `typing.Literal["PC", "MSQ"] | None`')
        if params["patch_metric"] not in ["PC", "MSQ"]:
            raise StyxValidationError("Parameter `patch_metric` must be one of [\"PC\", \"MSQ\"]")
    if params.get("search_radius", None) is not None:
        if not isinstance(params["search_radius"], str):
            raise StyxValidationError(f'`search_radius` has the wrong type: Received `{type(params.get("search_radius", None))}` expected `str | None`')
    if params.get("exclusion_image", None) is not None:
        if not isinstance(params["exclusion_image"], (pathlib.Path, str)):
            raise StyxValidationError(f'`exclusion_image` has the wrong type: Received `{type(params.get("exclusion_image", None))}` expected `InputPathType | None`')
    if params.get("mask_image", None) is not None:
        if not isinstance(params["mask_image"], (pathlib.Path, str)):
            raise StyxValidationError(f'`mask_image` has the wrong type: Received `{type(params.get("mask_image", None))}` expected `InputPathType | None`')
    if params.get("output", None) is None:
        raise StyxValidationError("`output` must not be None")
    if not isinstance(params["output"], str):
        raise StyxValidationError(f'`output` has the wrong type: Received `{type(params.get("output", None))}` expected `str`')
    if params.get("verbose", None) is not None:
        if not isinstance(params["verbose"], bool):
            raise StyxValidationError(f'`verbose` has the wrong type: Received `{type(params.get("verbose", None))}` expected `bool | None`')


def ants_joint_fusion_cargs(
    params: AntsJointFusionParamsDict,
    execution: Execution,
) -> list[str]:
    """
    Build command-line arguments from parameters.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Command-line arguments.
    """
    cargs = []
    cargs.append("antsJointFusion")
    if params.get("image_dimensionality", None) is not None:
        cargs.extend([
            "--image-dimensionality",
            str(params.get("image_dimensionality", None))
        ])
    cargs.extend([
        "--target-image",
        *[execution.input_file(f) for f in params.get("target_image", None)]
    ])
    cargs.extend([
        "--atlas-image",
        *[execution.input_file(f) for f in params.get("atlas_image", None)]
    ])
    cargs.extend([
        "--atlas-segmentation",
        execution.input_file(params.get("atlas_segmentation", None))
    ])
    if params.get("alpha", None) is not None:
        cargs.extend([
            "--alpha",
            str(params.get("alpha", None))
        ])
    if params.get("beta", None) is not None:
        cargs.extend([
            "--beta",
            str(params.get("beta", None))
        ])
    if params.get("constrain_nonnegative", None) is not None:
        cargs.extend([
            "--constrain-nonnegative",
            ("1" if params.get("constrain_nonnegative", None) else "0")
        ])
    if params.get("patch_radius", None) is not None:
        cargs.extend([
            "--patch-radius",
            params.get("patch_radius", None)
        ])
    if params.get("patch_metric", None) is not None:
        cargs.extend([
            "--patch-metric",
            params.get("patch_metric", None)
        ])
    if params.get("search_radius", None) is not None:
        cargs.extend([
            "--search-radius",
            params.get("search_radius", None)
        ])
    if params.get("exclusion_image", None) is not None:
        cargs.extend([
            "--exclusion-image",
            execution.input_file(params.get("exclusion_image", None))
        ])
    if params.get("mask_image", None) is not None:
        cargs.extend([
            "--mask-image",
            execution.input_file(params.get("mask_image", None))
        ])
    cargs.extend([
        "--output",
        params.get("output", None)
    ])
    if params.get("verbose", None) is not None:
        cargs.extend([
            "--verbose",
            ("1" if params.get("verbose", None) else "0")
        ])
    return cargs


def ants_joint_fusion_outputs(
    params: AntsJointFusionParamsDict,
    execution: Execution,
) -> AntsJointFusionOutputs:
    """
    Build outputs object containing output file paths and possibly stdout/stderr.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Outputs object.
    """
    ret = AntsJointFusionOutputs(
        root=execution.output_file("."),
        label_fusion_image=execution.output_file("[LABELFUSIONIMAGE]"),
        intensity_fusion_image=execution.output_file("[INTENSITYFUSIONIMAGEFILENAMEFORMAT]"),
        label_posterior_probability_image=execution.output_file("[LABELPOSTERIORPROBABILITYIMAGEFILENAMEFORMAT]"),
        atlas_voting_weight_image=execution.output_file("[ATLASVOTINGWEIGHTIMAGEFILENAMEFORMAT]"),
    )
    return ret


def ants_joint_fusion_execute(
    params: AntsJointFusionParamsDict,
    runner: Runner | None = None,
) -> AntsJointFusionOutputs:
    """
    antsJointFusion
    
    antsJointFusion is an image fusion algorithm developed by Hongzhi Wang and
    Paul Yushkevich. This implementation is based on Paul's original ITK-style
    implementation and Brian's ANTsR implementation. The original label fusion
    framework was extended to accommodate intensities.
    
    Author: ANTs Developers
    
    URL: https://github.com/ANTsX/ANTs
    
    Args:
        params: The parameters.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `AntsJointFusionOutputs`).
    """
    ants_joint_fusion_validate(params)
    runner = runner or get_global_runner()
    execution = runner.start_execution(ANTS_JOINT_FUSION_METADATA)
    params = execution.params(params)
    cargs = ants_joint_fusion_cargs(params, execution)
    ret = ants_joint_fusion_outputs(params, execution)
    execution.run(cargs)
    return ret


def ants_joint_fusion(
    target_image: list[InputPathType],
    atlas_image: list[InputPathType],
    atlas_segmentation: InputPathType,
    output: str,
    image_dimensionality: typing.Literal[2, 3, 4] | None = None,
    alpha: float | None = None,
    beta: float | None = None,
    constrain_nonnegative: bool | None = None,
    patch_radius: str | None = None,
    patch_metric: typing.Literal["PC", "MSQ"] | None = None,
    search_radius: str | None = None,
    exclusion_image: InputPathType | None = None,
    mask_image: InputPathType | None = None,
    verbose: bool | None = None,
    runner: Runner | None = None,
) -> AntsJointFusionOutputs:
    """
    antsJointFusion
    
    antsJointFusion is an image fusion algorithm developed by Hongzhi Wang and
    Paul Yushkevich. This implementation is based on Paul's original ITK-style
    implementation and Brian's ANTsR implementation. The original label fusion
    framework was extended to accommodate intensities.
    
    Author: ANTs Developers
    
    URL: https://github.com/ANTsX/ANTs
    
    Args:
        target_image: The target image (or multimodal target images) assumed to\
            be aligned to a common image domain.
        atlas_image: The atlas image (or multimodal atlas images) assumed to be\
            aligned to a common image domain.
        atlas_segmentation: The atlas segmentation images. For performing label\
            fusion the number of specified segmentations should be identical to the\
            number of atlas image sets.
        output: The output is the intensity and/or label fusion image.\
            Additional optional outputs include the label posterior probability\
            images and the atlas voting weight images.
        image_dimensionality: This option forces the image to be treated as a\
            specified-dimensional image. If not specified, the program tries to\
            infer the dimensionality from the input image.
        alpha: Regularization term added to matrix Mx for calculating the\
            inverse. Default = 0.1.
        beta: Exponent for mapping intensity difference to the joint error.\
            Default = 2.0.
        constrain_nonnegative: Constrain solution to non-negative weights.
        patch_radius: Patch radius for similarity measures. Default = 2x2x2.
        patch_metric: Metric to be used in determining the most similar\
            neighborhood patch. Options include Pearson's correlation (PC) and mean\
            squares (MSQ). Default = PC (Pearson correlation).
        search_radius: Search radius for similarity measures. Default = 3x3x3.\
            One can also specify an image where the value at the voxel specifies\
            the isotropic search radius at that voxel.
        exclusion_image: Specify an exclusion region for the given label.
        mask_image: If a mask image is specified, fusion is only performed in\
            the mask region.
        verbose: Verbose output.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `AntsJointFusionOutputs`).
    """
    params = ants_joint_fusion_params(
        image_dimensionality=image_dimensionality,
        target_image=target_image,
        atlas_image=atlas_image,
        atlas_segmentation=atlas_segmentation,
        alpha=alpha,
        beta=beta,
        constrain_nonnegative=constrain_nonnegative,
        patch_radius=patch_radius,
        patch_metric=patch_metric,
        search_radius=search_radius,
        exclusion_image=exclusion_image,
        mask_image=mask_image,
        output=output,
        verbose=verbose,
    )
    return ants_joint_fusion_execute(params, runner)


__all__ = [
    "ANTS_JOINT_FUSION_METADATA",
    "AntsJointFusionOutputs",
    "AntsJointFusionParamsDict",
    "AntsJointFusionParamsDictTagged",
    "ants_joint_fusion",
    "ants_joint_fusion_execute",
    "ants_joint_fusion_params",
]
