Nothing
#' Memory-Mapped Neuroimaging Vector Class
#'
#' @name MappedNeuroVec-class
#' @description
#' The \code{MappedNeuroVec} class provides memory-efficient access to large neuroimaging
#' datasets through memory mapping. This allows processing of datasets larger than available
#' RAM by keeping data on disk and only loading requested portions into memory.
#'
#' @section Implementation Details:
#' The class uses the \code{mmap} package to establish a memory mapping between the file
#' and memory space. Key features include:
#' \itemize{
#' \item Zero-copy access to file data
#' \item Automatic memory management
#' \item Support for large datasets
#' \item Efficient random access
#' }
#'
#' @seealso \code{\link{MappedNeuroVec}} for creating instances of this class
#'
NULL
#' Create a Memory-Mapped Source for Neuroimaging Data
#'
#' @title Create a Memory-Mapped Data Source
#' @description
#' Creates a \code{\linkS4class{MappedNeuroVecSource}} object that manages the memory
#' mapping between a neuroimaging file and memory space. This is typically used internally
#' by \code{\link{MappedNeuroVec}} but can be created directly for custom access patterns.
#'
#' @param file_name Character string specifying the path to the neuroimaging file.
#' Supported formats include NIFTI (.nii) and ANALYZE (.hdr/.img).
#'
#' @return A new \code{\linkS4class{MappedNeuroVecSource}} object containing:
#' \itemize{
#' \item Meta information about the dataset
#' \item File format details
#' \item Dimensional information
#' }
#'
#' @details
#' The function performs several important checks:
#' \itemize{
#' \item Validates file existence and permissions
#' \item Reads and validates header information
#' \item Ensures proper dimensionality (>= 3D)
#' \item Verifies file format compatibility
#' }
#'
#' @examples
#' \donttest{
#' # Create source from NIFTI file
#' source <- MappedNeuroVecSource(system.file("extdata", "global_mask_v4.nii", package = "neuroim2"))
#'
#' # Check dimensions
#' dim(source@meta_info)
#'
#' # View header information
#' str(source@meta_info)
#' }
#'
#' @seealso
#' \code{\link{MappedNeuroVec}} for the main user interface,
#' \code{\link{read_header}} for header reading details
#'
#' @importFrom methods new
#'
#' @export
#' @rdname MappedNeuroVecSource-class
MappedNeuroVecSource <- function(file_name) {
if (!is.character(file_name) || length(file_name) != 1) {
cli::cli_abort("{.arg file_name} must be a single character string.")
}
if (!file.exists(file_name)) {
cli::cli_abort("File {.path {file_name}} does not exist.")
}
if (file.access(file_name, mode = 4) != 0) {
cli::cli_abort("File {.path {file_name}} is not readable.")
}
# Read and validate header
meta <- read_header(file_name)
if (length(dim(meta)) < 3) {
cli::cli_abort("Input data must be at least 3-dimensional, not {length(dim(meta))}D.")
}
new("MappedNeuroVecSource", meta_info = meta)
}
#' Create a Memory-Mapped Neuroimaging Vector
#'
#' @title Create a Memory-Mapped Vector for Neuroimaging Data
#' @description
#' Creates a \code{\linkS4class{MappedNeuroVec}} object that provides efficient,
#' memory-mapped access to large neuroimaging datasets. This allows processing of
#' data larger than available RAM by keeping it on disk and only loading requested
#' portions into memory.
#'
#' @param file_name Character string specifying the path to the neuroimaging file.
#' Supported formats include NIFTI (.nii) and ANALYZE (.hdr/.img).
#' @param label Optional character string providing a label for the vector
#'
#' @return A new \code{\linkS4class{MappedNeuroVec}} object providing:
#' \itemize{
#' \item Memory-mapped access to the data
#' \item Spatial and temporal indexing
#' \item Efficient data extraction
#' \item Automatic memory management
#' }
#'
#' @details
#' The function implements several key features:
#' \itemize{
#' \item Zero-copy access to file data
#' \item Automatic memory management
#' \item Support for large datasets
#' \item Efficient random access
#' \item Proper cleanup on object deletion
#' }
#'
#' Memory mapping is particularly useful when:
#' \itemize{
#' \item Working with large datasets
#' \item Only portions of data are needed at once
#' \item Random access is required
#' \item Multiple processes need to share data
#' }
#'
#' @examples
#' # Create mapped vector from NIFTI file
#' mvec <- MappedNeuroVec(system.file("extdata", "global_mask_v4.nii", package = "neuroim2"))
#'
#' # Extract first volume
#' vol1 <- mvec[[1]]
#'
#' # Get dimensions
#' dim(mvec)
#'
#' # Access specific timepoint
#' timepoint <- mvec[, , , 2]
#'
#'
#' @seealso
#
#' \code{\link[mmap]{mmap}} for memory mapping details
#'
#' @importFrom mmap mmap mmapFlags
#' @importFrom methods new
#'
#' @export
#' @rdname MappedNeuroVec-class
MappedNeuroVec <- function(file_name, label = basename(file_name)) {
if (!is.character(file_name) || length(file_name) != 1) {
cli::cli_abort("{.arg file_name} must be a single character string.")
}
if (!file.exists(file_name)) {
cli::cli_abort("File {.path {file_name}} does not exist.")
}
# Create source and load data
src <- MappedNeuroVecSource(file_name)
load_data(src)
}
#' @rdname load_data-methods
#' @export
setMethod(f = "load_data",
signature = "MappedNeuroVecSource",
def = function(x) {
# Extract meta information
meta <- x@meta_info
# Create memory mapping
tryCatch({
fmap <- mmap::mmap(meta@data_file,
mode = .getMMapMode(meta@data_type),
prot = mmap::mmapFlags("PROT_READ"))
}, error = function(e) {
stop("Failed to create memory mapping: ", e$message)
})
# Calculate data offset
offset <- meta@data_offset / .getDataSize(meta@data_type)
# Create space object
bspace <- NeuroSpace(dim(meta),
meta@spacing,
meta@origin,
meta@spatial_axes,
trans = trans(meta))
# Create mapped vector with basename as label
new("MappedNeuroVec",
space = bspace,
filemap = fmap,
offset = as.integer(offset),
label = basename(meta@data_file),
volume_labels = nifti_volume_labels(meta@header, expected_length = meta@dims[4]))
})
#' Linear Access to Memory-Mapped Data
#'
#' @description
#' Internal method providing linear access to memory-mapped data.
#'
#' @param x A MappedNeuroVec object
#' @param i Numeric vector of indices
#'
#'
#' @rdname linear_access-methods
setMethod(f = "linear_access",
signature = signature(x = "MappedNeuroVec", i = "numeric"),
def = function(x, i) {
if (!is.numeric(i) || any(is.na(i))) {
cli::cli_abort("{.arg i} must be a numeric vector without NA values.")
}
# Calculate adjusted indices
idx <- i + x@offset
# Bounds checking
if (!all(idx >= 1) || !all(idx <= length(x@filemap))) {
cli::cli_abort("Index out of bounds: indices must be in [1, {length(x@filemap)}].")
}
# Access mapped data
x@filemap[idx]
})
#' @export
#' @rdname show-methods
setMethod("show", "MappedNeuroVec", function(object) {
d <- dim(object)
show_header("MappedNeuroVec", format_mem(object))
show_rule("Spatial")
show_field("Dimensions", paste(d[1:3], collapse = " x "))
show_field("Time Points", d[4])
show_field("Spacing", paste(spacing(object)[1:3], collapse = " x "))
show_field("Origin", paste(round(origin(object)[1:3], 2), collapse = ", "))
show_rule("Mapping")
show_field("Offset", object@offset)
if (nchar(object@label) > 0) show_field("Label", object@label)
if (length(volume_labels(object)) > 0L) {
show_field("Volume Labels", sum(nzchar(volume_labels(object))), paste0("/", d[4], " named"))
}
})
#' @export
#' @rdname as.matrix-methods
setMethod("as.matrix", "MappedNeuroVec",
function(x) {
dm <- dim(x)
d123 <- prod(dm[1:3])
d4 <- dm[4]
tot <- d123 * d4
vals <- linear_access(x, seq_len(tot))
matrix(vals, d123, d4)
}
)
#' @rdname mask-methods
#' @export
setMethod("mask", "MappedNeuroVec",
function(x) {
x@mask
})
#' Convert to memory-mapped NeuroVec
#'
#' @description
#' Methods for the \code{\link{as_mmap}} generic, which convert various
#' neuroimaging vector types to a \code{\linkS4class{MappedNeuroVec}} backed
#' by an on-disk NIfTI file.
#'
#' @param x A neuroimaging vector (\code{NeuroVec}, \code{MappedNeuroVec},
#' or \code{FileBackedNeuroVec}).
#' @param file Optional output file name. If \code{NULL}, a temporary file
#' with extension \code{.nii} is created.
#' @param ... Additional arguments passed to methods (e.g. \code{data_type},
#' \code{overwrite}).
#'
#' @return A \code{\linkS4class{MappedNeuroVec}} (or \code{x} itself if it is
#' already memory-mapped).
#'
#' @name as_mmap-methods
#' @aliases as_mmap,MappedNeuroVec-method
#' as_mmap,FileBackedNeuroVec-method
#' as_mmap,NeuroVec-method
#' as_mmap,SparseNeuroVec-method
#' @export
#' @rdname as_mmap
setMethod("as_mmap", signature(x = "MappedNeuroVec"),
function(x, file = NULL, ...) {
x
})
#' @export
#' @rdname as_mmap
setMethod("as_mmap", signature(x = "FileBackedNeuroVec"),
function(x, file = NULL, ...) {
fn <- x@meta@data_file
if (endsWith(fn, ".gz")) {
stop("as_mmap: cannot memory-map gzipped file; re-save as uncompressed NIfTI with write_vec().")
}
MappedNeuroVec(fn)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.