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

import typing
import pathlib
from styxdefs import *

ANTS_JOINT_TENSOR_FUSION_METADATA = Metadata(
    id="5d343b97b1984cf23521db0f60519ddbabba3a2e.boutiques",
    name="antsJointTensorFusion",
    package="ants",
    container_image_tag="antsx/ants:v2.5.3",
)


_AntsJointTensorFusionParamsDictNoTag = typing.TypedDict('_AntsJointTensorFusionParamsDictNoTag', {
    "dimensionality": typing.NotRequired[typing.Literal[2, 3, 4] | None],
    "target_image": list[str],
    "atlas_image": list[str],
    "atlas_segmentation": InputPathType,
    "alpha": typing.NotRequired[float | None],
    "beta": typing.NotRequired[float | None],
    "retain_label_posterior_images": typing.NotRequired[bool | None],
    "retain_atlas_voting_images": typing.NotRequired[bool | None],
    "constrain_nonnegative": typing.NotRequired[bool | None],
    "log_euclidean": 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[str | None],
    "mask_image": typing.NotRequired[InputPathType | None],
    "output": str,
    "verbose": typing.NotRequired[bool | None],
})
AntsJointTensorFusionParamsDictTagged = typing.TypedDict('AntsJointTensorFusionParamsDictTagged', {
    "@type": typing.Literal["ants/antsJointTensorFusion"],
    "dimensionality": typing.NotRequired[typing.Literal[2, 3, 4] | None],
    "target_image": list[str],
    "atlas_image": list[str],
    "atlas_segmentation": InputPathType,
    "alpha": typing.NotRequired[float | None],
    "beta": typing.NotRequired[float | None],
    "retain_label_posterior_images": typing.NotRequired[bool | None],
    "retain_atlas_voting_images": typing.NotRequired[bool | None],
    "constrain_nonnegative": typing.NotRequired[bool | None],
    "log_euclidean": 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[str | None],
    "mask_image": typing.NotRequired[InputPathType | None],
    "output": str,
    "verbose": typing.NotRequired[bool | None],
})
AntsJointTensorFusionParamsDict = _AntsJointTensorFusionParamsDictNoTag | AntsJointTensorFusionParamsDictTagged


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


def ants_joint_tensor_fusion_params(
    target_image: list[str],
    atlas_image: list[str],
    atlas_segmentation: InputPathType,
    output: str,
    dimensionality: typing.Literal[2, 3, 4] | None = None,
    alpha: float | None = None,
    beta: float | None = None,
    retain_label_posterior_images: bool | None = None,
    retain_atlas_voting_images: bool | None = None,
    constrain_nonnegative: bool | None = None,
    log_euclidean: bool | None = None,
    patch_radius: str | None = None,
    patch_metric: typing.Literal["PC", "MSQ"] | None = None,
    search_radius: str | None = None,
    exclusion_image: str | None = None,
    mask_image: InputPathType | None = None,
    verbose: bool | None = None,
) -> AntsJointTensorFusionParamsDictTagged:
    """
    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.
        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.
        retain_label_posterior_images: Retain label posterior probability\
            images. Requires atlas segmentations to be specified. Default = false.
        retain_atlas_voting_images: Retain atlas voting images. Default = false.
        constrain_nonnegative: Constrain solution to non-negative weights.
        log_euclidean: Use log Euclidean space for tensor math.
        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.
        search_radius: Search radius for similarity measures. Default = 3x3x3.
        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/antsJointTensorFusion",
        "target_image": target_image,
        "atlas_image": atlas_image,
        "atlas_segmentation": atlas_segmentation,
        "output": output,
    }
    if dimensionality is not None:
        params["dimensionality"] = dimensionality
    if alpha is not None:
        params["alpha"] = alpha
    if beta is not None:
        params["beta"] = beta
    if retain_label_posterior_images is not None:
        params["retain_label_posterior_images"] = retain_label_posterior_images
    if retain_atlas_voting_images is not None:
        params["retain_atlas_voting_images"] = retain_atlas_voting_images
    if constrain_nonnegative is not None:
        params["constrain_nonnegative"] = constrain_nonnegative
    if log_euclidean is not None:
        params["log_euclidean"] = log_euclidean
    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_tensor_fusion_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `AntsJointTensorFusionParamsDict` 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("dimensionality", None) is not None:
        if not isinstance(params["dimensionality"], int):
            raise StyxValidationError(f'`dimensionality` has the wrong type: Received `{type(params.get("dimensionality", None))}` expected `typing.Literal[2, 3, 4] | None`')
        if params["dimensionality"] not in [2, 3, 4]:
            raise StyxValidationError("Parameter `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[str]`')
    for e in params["target_image"]:
        if not isinstance(e, str):
            raise StyxValidationError(f'`target_image` has the wrong type: Received `{type(params.get("target_image", None))}` expected `list[str]`')
    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[str]`')
    for e in params["atlas_image"]:
        if not isinstance(e, str):
            raise StyxValidationError(f'`atlas_image` has the wrong type: Received `{type(params.get("atlas_image", None))}` expected `list[str]`')
    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("retain_label_posterior_images", None) is not None:
        if not isinstance(params["retain_label_posterior_images"], bool):
            raise StyxValidationError(f'`retain_label_posterior_images` has the wrong type: Received `{type(params.get("retain_label_posterior_images", None))}` expected `bool | None`')
    if params.get("retain_atlas_voting_images", None) is not None:
        if not isinstance(params["retain_atlas_voting_images"], bool):
            raise StyxValidationError(f'`retain_atlas_voting_images` has the wrong type: Received `{type(params.get("retain_atlas_voting_images", None))}` expected `bool | 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("log_euclidean", None) is not None:
        if not isinstance(params["log_euclidean"], bool):
            raise StyxValidationError(f'`log_euclidean` has the wrong type: Received `{type(params.get("log_euclidean", 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"], str):
            raise StyxValidationError(f'`exclusion_image` has the wrong type: Received `{type(params.get("exclusion_image", None))}` expected `str | 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_tensor_fusion_cargs(
    params: AntsJointTensorFusionParamsDict,
    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("antsJointTensorFusion")
    if params.get("dimensionality", None) is not None:
        cargs.extend([
            "--image-dimensionality",
            str(params.get("dimensionality", None))
        ])
    cargs.extend([
        "-t",
        ",".join(params.get("target_image", None))
    ])
    cargs.extend([
        "-g",
        ",".join(params.get("atlas_image", None))
    ])
    cargs.extend([
        "-l",
        execution.input_file(params.get("atlas_segmentation", None))
    ])
    if params.get("alpha", None) is not None:
        cargs.extend([
            "-a",
            str(params.get("alpha", None))
        ])
    if params.get("beta", None) is not None:
        cargs.extend([
            "-b",
            str(params.get("beta", None))
        ])
    if params.get("retain_label_posterior_images", None) is not None:
        cargs.extend([
            "-r",
            ("1" if params.get("retain_label_posterior_images", None) else "0")
        ])
    if params.get("retain_atlas_voting_images", None) is not None:
        cargs.extend([
            "-f",
            ("1" if params.get("retain_atlas_voting_images", None) else "0")
        ])
    if params.get("constrain_nonnegative", None) is not None:
        cargs.extend([
            "-c",
            ("1" if params.get("constrain_nonnegative", None) else "0")
        ])
    if params.get("log_euclidean", None) is not None:
        cargs.extend([
            "-u",
            ("1" if params.get("log_euclidean", None) else "0")
        ])
    if params.get("patch_radius", None) is not None:
        cargs.extend([
            "-p",
            params.get("patch_radius", None)
        ])
    if params.get("patch_metric", None) is not None:
        cargs.extend([
            "-m",
            params.get("patch_metric", None)
        ])
    if params.get("search_radius", None) is not None:
        cargs.extend([
            "-s",
            params.get("search_radius", None)
        ])
    if params.get("exclusion_image", None) is not None:
        cargs.extend([
            "-e",
            params.get("exclusion_image", None)
        ])
    if params.get("mask_image", None) is not None:
        cargs.extend([
            "-x",
            execution.input_file(params.get("mask_image", None))
        ])
    cargs.extend([
        "-o",
        params.get("output", None)
    ])
    if params.get("verbose", None) is not None:
        cargs.extend([
            "-v",
            ("1" if params.get("verbose", None) else "0")
        ])
    return cargs


def ants_joint_tensor_fusion_outputs(
    params: AntsJointTensorFusionParamsDict,
    execution: Execution,
) -> AntsJointTensorFusionOutputs:
    """
    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 = AntsJointTensorFusionOutputs(
        root=execution.output_file("."),
        label_fusion_image=execution.output_file(params.get("output", None) + "_LabelFusion.nii.gz"),
        intensity_fusion_image=execution.output_file(params.get("output", None) + "_IntensityFusion.nii.gz"),
        label_posterior_probability_image=execution.output_file(params.get("output", None) + "_LabelPosterior.nii.gz"),
        atlas_voting_weight_image=execution.output_file(params.get("output", None) + "_AtlasVoting.nii.gz"),
    )
    return ret


def ants_joint_tensor_fusion_execute(
    params: AntsJointTensorFusionParamsDict,
    runner: Runner | None = None,
) -> AntsJointTensorFusionOutputs:
    """
    antsJointTensorFusion
    
    antsJointTensorFusion is an image fusion algorithm developed by Hongzhi Wang
    and Paul Yushkevich which won segmentation challenges at MICCAI 2012 and
    MICCAI 2013. The original label fusion framework was extended to accommodate
    intensities by Brian Avants. This implementation is based on the original
    ITK-style implementation and ANTsR implementation.
    
    Author: ANTs Developers
    
    URL: https://github.com/ANTsX/ANTs
    
    Args:
        params: The parameters.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `AntsJointTensorFusionOutputs`).
    """
    ants_joint_tensor_fusion_validate(params)
    runner = runner or get_global_runner()
    execution = runner.start_execution(ANTS_JOINT_TENSOR_FUSION_METADATA)
    params = execution.params(params)
    cargs = ants_joint_tensor_fusion_cargs(params, execution)
    ret = ants_joint_tensor_fusion_outputs(params, execution)
    execution.run(cargs)
    return ret


def ants_joint_tensor_fusion(
    target_image: list[str],
    atlas_image: list[str],
    atlas_segmentation: InputPathType,
    output: str,
    dimensionality: typing.Literal[2, 3, 4] | None = None,
    alpha: float | None = None,
    beta: float | None = None,
    retain_label_posterior_images: bool | None = None,
    retain_atlas_voting_images: bool | None = None,
    constrain_nonnegative: bool | None = None,
    log_euclidean: bool | None = None,
    patch_radius: str | None = None,
    patch_metric: typing.Literal["PC", "MSQ"] | None = None,
    search_radius: str | None = None,
    exclusion_image: str | None = None,
    mask_image: InputPathType | None = None,
    verbose: bool | None = None,
    runner: Runner | None = None,
) -> AntsJointTensorFusionOutputs:
    """
    antsJointTensorFusion
    
    antsJointTensorFusion is an image fusion algorithm developed by Hongzhi Wang
    and Paul Yushkevich which won segmentation challenges at MICCAI 2012 and
    MICCAI 2013. The original label fusion framework was extended to accommodate
    intensities by Brian Avants. This implementation is based on the original
    ITK-style implementation and ANTsR implementation.
    
    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.
        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.
        retain_label_posterior_images: Retain label posterior probability\
            images. Requires atlas segmentations to be specified. Default = false.
        retain_atlas_voting_images: Retain atlas voting images. Default = false.
        constrain_nonnegative: Constrain solution to non-negative weights.
        log_euclidean: Use log Euclidean space for tensor math.
        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.
        search_radius: Search radius for similarity measures. Default = 3x3x3.
        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 `AntsJointTensorFusionOutputs`).
    """
    params = ants_joint_tensor_fusion_params(
        dimensionality=dimensionality,
        target_image=target_image,
        atlas_image=atlas_image,
        atlas_segmentation=atlas_segmentation,
        alpha=alpha,
        beta=beta,
        retain_label_posterior_images=retain_label_posterior_images,
        retain_atlas_voting_images=retain_atlas_voting_images,
        constrain_nonnegative=constrain_nonnegative,
        log_euclidean=log_euclidean,
        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_tensor_fusion_execute(params, runner)


__all__ = [
    "ANTS_JOINT_TENSOR_FUSION_METADATA",
    "AntsJointTensorFusionOutputs",
    "AntsJointTensorFusionParamsDict",
    "AntsJointTensorFusionParamsDictTagged",
    "ants_joint_tensor_fusion",
    "ants_joint_tensor_fusion_execute",
    "ants_joint_tensor_fusion_params",
]
