Provides portable access from 'R' to biomedical image processing toolbox
'ANTs' by Avants et al. (2009)
rpyANTs was detached from a RAVE (Reproducible Analysis and
Visualization of iEEG) module. It is now a
standalone package that connects ANTsPy with R using seamless
shared-memory.
This package was originally created for the following three purposes:
ANTs easily accessible from the latest R and all major
operating systemsRAVE or other code/scripts/frameworks to be reproducible
since the code will be OS-invariantrpyANTs takes less than 10 minutesANTsPy can be executed from rpyANTs and R
with no modificationDisclaimer: This is a third-party maintained R package for
ANTs. If you are looking for theANTsRpackage byB.B Avants, please check here.
The installation requires one-line extra setup
# Install from CRAN
install.packages("rpyANTs")
# Install from nightly dev builder
# install.packages("rpyANTs", repos = "https://dipterix.r-universe.dev")
# set up ANTs
rpyANTs::install_ants()
install_ants creates an isolated Python environment managed by
RAVE. This environment does not conflict nor affect your existing
Python installations.
ANTsTo upgrade ANTs, first update rpyANTs, then upgrade ANTsPyx
install.packages("rpyANTs")
rpymat::add_packages(packages = "antspyx", pip = TRUE)
To load ANTs
library(rpyANTs)
# Whether ANTs is available
ants_available()
# Load ANTs into R
ants
In R, we use $ to get module functions or class members. For example:
ants$add_noise_to_image
#> <ANTs Python Wrapper>
#> Help on function add_noise_to_image in module ants.ops.add_noise_to_image:
#>
#> add_noise_to_image(image, noise_model, noise_parameters)
#> Add noise to an image using additive Gaussian, salt-and-pepper,
#> shot, or speckle noise.
#>
#> Arguments
#> ---------
#> image : ANTsImage
#> scalar image.
#>
#> noise_model : string
#> 'additivegaussian', 'saltandpepper', 'shot', or 'speckle'.
#>
#> noise_parameters : tuple or array or float
#> 'additivegaussian': (mean, standardDeviation)
#> 'saltandpepper': (probability, saltValue, pepperValue)
#> 'shot': scale
#> 'speckle': standardDeviation
#>
#> Returns
#> -------
#> ANTsImage
#>
#> Example
#> -------
#> >>> import ants
#> >>> image = ants.image_read(ants.get_ants_data('r16'))
#> >>> noise_image = ants.add_noise_to_image(image, 'additivegaussian', (0.0, 1.0))
#> >>> noise_image = ants.add_noise_to_image(image, 'saltandpepper', (0.1, 0.0, 100.0))
#> >>> noise_image = ants.add_noise_to_image(image, 'shot', 1.0)
#> >>> noise_image = ants.add_noise_to_image(image, 'speckle', 1.0)
#>
#> *** Above documentation is for Python.
#> *** Please use `$` instead of `.` for modules and functions in R
#> <function add_noise_to_image at 0x121ab40e0>
#> signature: (image, noise_model, noise_parameters)
The following R code translates Python code into R:
# >>> img = ants.image_read(ants.get_ants_data('r16'))
img <- ants$image_read(ants$get_ants_data('r16'))
# >>> noise_image1 = ants.add_noise_to_image(img, 'additivegaussian', (0.0, 1.0))
noise_image1 <- ants$add_noise_to_image(
img, 'additivegaussian',
noise_parameters = tuple(0.0, 1.0)
)
# >>> noise_image2 = ants.add_noise_to_image(img, 'saltandpepper', (0.1, 0.0, 100.0))
noise_image2 <- ants$add_noise_to_image(
img, 'saltandpepper',
noise_parameters = tuple(0.1, 0.0, 100.0)
)
# >>> noise_image3 = ants.add_noise_to_image(img, 'shot', 1.0)
noise_image3 <- ants$add_noise_to_image(
img, 'shot',
noise_parameters = 1.0
)
# >>> noise_image4 = ants.add_noise_to_image(img, 'speckle', 1.0)
noise_image4 <- ants$add_noise_to_image(
img, 'speckle',
noise_parameters = 1.0
)
# >>> trans = ants.create_ants_transform(
# >>> dimension=2, matrix=[[0.707, 0.707], [-.707, 0.707]],
# >>> translation=[-53, 128])
trans <- as_ANTsTransform(matrix(
c(0.707, 0.707, -53,
-0.707, 0.707, 128),
nrow = 2, byrow = TRUE
), dimension = 2)
# >>> noise_image4 = trans.apply_to_image(noise_image4)
noise_image4 <- trans$apply_to_image(noise_image4)
To load imaging data into R
# Use [] to convert ANTsImage into R array
is.array(img[])
#> [1] TRUE
# plot via R
layout(matrix(c(1,1,2,3,1,1,4,5), nrow = 2, byrow = TRUE))
par(mar = c(0.1, 0.1, 0.1, 0.1), bg = "black", fg = "white")
pal <- grDevices::gray.colors(256, start = 0, end = 1)
image(img[], asp = 1, axes = FALSE,
col = pal, zlim = c(0, 255), ylim = c(1, 0))
image(noise_image1[], asp = 1, axes = FALSE,
col = pal, zlim = c(0, 255), ylim = c(1, 0))
image(noise_image2[], asp = 1, axes = FALSE,
col = pal, zlim = c(0, 255), ylim = c(1, 0))
image(noise_image3[], asp = 1, axes = FALSE,
col = pal, zlim = c(0, 255), ylim = c(1, 0))
image(noise_image4[], asp = 1, axes = FALSE,
col = pal, zlim = c(0, 255), ylim = c(1, 0))
Python scriptsrpyANTs ports functions that allows to run Python scripts. For
example:
library(rpyANTs)
script_path <- tempfile(fileext = ".py")
writeLines(con = script_path, text = r"(
# This is Python script
import ants
print(ants.__version__)
)")
run_script(script_path)
#> 0.6.3
You can also run Python interactive in R (yes, you are correct).
Simply run
rpyANTs::repl_python()
The console prefix will change from > to >>>, meaning you are in
Python mode:
> rpyANTs::repl_python()
Python 3.8.16 (/Users/dipterix/Library/r-rpymat/miniconda/envs/rpymat-conda-env/bin/python3.8)
Reticulate 1.26 REPL -- A Python interpreter in R.
Enter 'exit' or 'quit' to exit the REPL and return to R.
>>>
Try some Python code!
>>> import ants
>>> help(ants.registration)
To exit Python mode, type exit (no parenthesis) and hit enter key
>>> exit
>
Native R variables can be easily converted to Python and back via
r_to_py and py_to_r.
For example
# R to Python
r_to_py(1)
#> 1.0
r_to_py(1L)
#> 1
# Python to R
py_obj <- py_list(1:3)
class(py_obj) # <- this is a python object
#> [1] "python.builtin.list" "python.builtin.object"
py_to_r(py_obj)
#> [1] 1 2 3
You can also use variables created in R from Python or vice versa:
In the following example, an R object object_r is created. In Python,
it can be accessed (read-only) via r.object_r
> object_r <- c(1,2,3)
> repl_python()
Python 3.8.16 (/Users/dipterix/Library/r-rpymat/miniconda/envs/rpymat-conda-env/bin/python3.8)
Reticulate 1.26 REPL -- A Python interpreter in R.
Enter 'exit' or 'quit' to exit the REPL and return to R.
>>> r.object_r
[1.0, 2.0, 3.0]
Similarly, a Python object object_py is created, and it can be read
from py$object_py:
>>> import numpy as np
>>> object_py = np.array([2,3,4])
>>> exit
> py$object_py
[1] 2 3 4
R is not a type-rigid language. Some functions in ANTsPy require
specific variable types that are often vague in R. For example the
dimension argument in function ants$create_ants_transform needs to
be an integer, but R’s default numerical values are double. In this
case, variable formats need to be explicitly given.
Here are several examples
# ants$create_ants_transform(dimension = 3) # <- error
ants$create_ants_transform(dimension = 3L) # < XXXL is an explicit integer
Tuple, list, and dictionaryA Python tuple is a vector that cannot alter lengths.
# Wrong as `aff_iterations` needs to be a tuple
# ants$registration(fixed, moving, ..., aff_iterations = c(6L, 4L, 2L, 1L))
ants$registration(fixed, moving, ..., aff_iterations = tuple(6L, 4L, 2L, 1L))
Similar conversions can be done via py_list, py_dict.
TRUE vs. FALSEA Python module can be imported with auto-conversion (argument
convert) set to TRUE or FALSE. When auto-conversion is on, the
Python function results will be converted to R objects automatically.
For example,
np <- import("numpy", convert = TRUE)
np$eye(4L)
#> [,1] [,2] [,3] [,4]
#> [1,] 1 0 0 0
#> [2,] 0 1 0 0
#> [3,] 0 0 1 0
#> [4,] 0 0 0 1
The numpy array is automatically translated as an R matrix. While this
is convenient, this automated conversion could cause some issues when
the function results are further passed into another Python function.
For example, the following code will raise errors.
> np <- import("numpy", convert = TRUE)
> ants <- load_ants()
>
> image <- ants$image_read(ants$get_ants_data('mni'))
> image_array <- np$asarray(list(image, image))
>
> ants$plot_grid(image_array, slices = 100L)
Error in py_call_impl(callable, dots$args, dots$keywords) :
Matrix type cannot be converted to python (only integer, numeric, complex, logical, and character matrixes can be converted
The error is raised because numpy has convert=TRUE, hence
image_array is converted to an R list with each element being a
ANTsImage instance. Calling ants$plot_grid needs R-to-Python
conversion for all input variables, including image_array. However
this conversion makes image_array a Python list instead of numpy
array, violating the input format.
A safer way is to keep in the Python format, i.e. convert=FALSE. In
this mode, function results will not be converted back to R (you need to
manually make conversion by yourself via py_to_r). Now the following
example works.
> np <- import("numpy", convert = TRUE)
> ants <- load_ants()
>
> image <- ants$image_read(ants$get_ants_data('mni'))
> image_array <- np$asarray(list(image, image))
>
> ants$plot_grid(image_array, slices = 100L)
Object
antsinrpyANTsis a non-conversion Python module. Objectpyis a auto-conversion Python module
In Python, operators on ANTsImage, such as img > 5 are defined. Such
operators is being supported in R as S3 generic functions. Don’t worry
if you don’t know what is S3 generic, see the following examples:
library(rpyANTs)
image <- ants$image_read(ants$get_ants_data('mni'))
print(image)
dim(image)
range(image)
y1 <- (image > 10) * 8000
y2 <- image
y2[y2 < 10] <- 4000
y3 <- log(image + 1000)
y3 <- (y3 - min(y3)) / (max(y3) - min(y3)) * 8000
ants_plot_grid(
list(image, y1, y2, y3),
slices = 100, shape = c(1, 4),
vmin = 0, vmax = 8000
)
Although the operator generics have been implemented for common classes
such as ANTsImage and ANTsTransform. Many are still under
development and not supported. In this case, you might want to use the
following workaround methods. You are more than welcome to post a
wish-list or issue ticket to the Github
repository
Alternative version 1: call operators directly
library(rpyANTs)
image <- ants$image_read(ants$get_ants_data('r16'))
# The followings are the same
# threshold <- image > 10
threshold <- image$`__gt__`(10)
ants$plot(threshold)
Work-around version 2: If you don’t know how Python operators work, use Python directly
library(rpyANTs)
image <- ants$image_read(ants$get_ants_data('r16'))
# Create an R variable from Python!
py_run_string("r.threshold = r.image > 10", local = TRUE, convert = FALSE)
ants$plot(threshold)
This is a general citation for ANTs:
Avants, B.B., Tustison, N. and Song, G., 2009. Advanced normalization tools (ANTS). The Insight Journal, 2(365), pp.1-35.
If you are using rpyANTs through RAVE or YAEL, please also cite:
Magnotti, J.F., Wang, Z. and Beauchamp, M.S., 2020. RAVE: Comprehensive open-source software for reproducible analysis and visualization of intracranial EEG data. NeuroImage, 223, p.117341.
This package rpyANTs is released under Apache-2.0 license (Copyright:
Zhengjia Wang). The underlying ANTsPy is released under Apache-2.0
license (Copyright: ANTs contributors).