"""Image / Grid Resampling."""
from typing import Literal, Union
import numpy as np
import SimpleITK as sitk
from ._grids import Grid
[docs]
def resample_image(
input_image: sitk.Image,
interpolator=sitk.sitkBSpline,
target_image: sitk.Image = None,
target_grid_spacing: tuple[float, float, float] = None,
target_grid: Grid = None,
extrapolate: Union[float, int, bool, Literal["nearest"]] = "nearest",
) -> sitk.Image: # also accept sitk images and grid points. Can be dealt with with kwargs
"""
Resample an sitk Image.
Use target resolution, reference image, dimensions, or Grid as input.
Parameters
----------
input_image : sitk.Image
The input image to be resampled.
interpolator : sitk.InterpolatorEnum, optional
The interpolator to use for resampling. Default is sitk.sitkBSpline.
extrapolate : Union[float, int, bool, Literal["nearest"], None], optional
The value to use for extrapolation outside the image boundaries. Default is None.
target_image : sitk.Image, optional
The reference image to resample to. Default is None.
target_grid_spacing : tuple[float,float,float], optional
The target grid spacing to resample to. Default is None.
target_grid : Grid, optional
The target grid to resample to. Default is None.
Returns
-------
sitk.Image
The resampled image.
Notes
-----
Exactly one of target_image, target_grid_spacing, or target_grid must be provided.
"""
if [target_image, target_grid_spacing, target_grid].count(None) != 2:
raise TypeError(
"Only one of target_image, target_grid_spacing, or target_grid must be provided."
)
image_grid = Grid.from_sitk_image(input_image)
if target_image is not None:
target_grid = Grid.from_sitk_image(target_image)
elif target_grid_spacing is not None:
target_grid = image_grid.resample(target_grid_spacing)
# BSpline (order 3) requires >= 4 voxels per dimension; fall back to linear for small images
if interpolator == sitk.sitkBSpline and any(s < 4 for s in input_image.GetSize()):
interpolator = sitk.sitkLinear
resample = sitk.ResampleImageFilter()
resample.UseNearestNeighborExtrapolatorOff()
if extrapolate == "nearest":
resample.UseNearestNeighborExtrapolatorOn()
elif isinstance(extrapolate, (int, float)):
resample.SetDefaultPixelValue(extrapolate)
elif extrapolate is False:
resample.SetDefaultPixelValue(0)
elif extrapolate is True:
resample.SetDefaultPixelValue(1)
else:
raise ValueError("Invalid value for 'extrapolate' parameter.")
resample.SetOutputSpacing(target_grid.resolution_vector)
resample.SetSize(target_grid.dimensions)
resample.SetOutputDirection(target_grid.direction.ravel())
resample.SetOutputOrigin(target_grid.origin)
resample.SetInterpolator(interpolator)
resampled_image = resample.Execute(input_image)
return resampled_image
[docs]
def resample_numpy_array(
input_array: np.ndarray,
reference_image: sitk.Image = None,
reference_grid: Grid = None,
interpolator=sitk.sitkBSpline,
target_image: sitk.Image = None,
target_grid_spacing: tuple[float, float, float] = None,
target_grid: Grid = None,
extrapolate: Union[float, int, bool, Literal["nearest"]] = "nearest",
) -> np.ndarray:
"""
Resample a numpy grid.
Use target resolution, reference image, dimensions, or Grid as input.
We convert the numpy array to an sitk.Image, so the dimensions will be
switched. More exactly,
the numpy array will be index i,j,k <-> z,y,x, which will converted to
j,i,k <-> y,x,z in sitk.
This is to be considered when supplying the reference grid or image.
Parameters
----------
input_image : sitk.Image
The input image to be resampled.
reference_image : sitk.Image, optional
The reference image providing spatial information of the array. Default is None.
Be wary of sitk <-> numpy indexing conventions.
reference_grid : Grid, optional
The reference grid of the resampled array. Default is None.
Be wary of sitk <-> numpy indexing conventions.
interpolator : sitk.InterpolatorEnum, optional
The interpolator to use for resampling. Default is sitk.sitkBSpline.
target_image : sitk.Image, optional
The reference image to resample to. Default is None.
target_grid_spacing : tuple[float,float,float], optional
The target grid spacing to resample to. Default is None.
target_grid : Grid, optional
The target grid to resample to. Default is None.
Returns
-------
sitk.Image
The resampled image.
Notes
-----
Exactly one of target_image, target_grid_spacing, or target_grid must be provided.
"""
if [reference_image, reference_grid].count(None) != 1:
raise TypeError("Only one of reference_image or reference_grid must be provided.")
if reference_image is not None:
reference_grid = Grid.from_sitk_image(reference_image)
# I think there is an issue here. The axes are flipped with GetImageFromArray
array_sitk = sitk.GetImageFromArray(input_array)
array_sitk.SetOrigin(reference_grid.origin)
array_sitk.SetSpacing(reference_grid.resolution_vector)
array_sitk.SetDirection(reference_grid.direction.ravel())
resampled_array_sitk = resample_image(
input_image=array_sitk,
interpolator=interpolator,
target_image=target_image,
target_grid_spacing=target_grid_spacing,
target_grid=target_grid,
extrapolate=extrapolate,
)
return sitk.GetArrayFromImage(resampled_array_sitk)