Nothing
#' @include all_class.R
NULL
#' @include all_generic.R
NULL
#' Create a Data Reader for Neuroimaging Data
#'
#' @title Create a Data Reader
#' @description Creates a data reader for accessing neuroimaging data from various file formats.
#' The reader provides a unified interface for reading data regardless of the underlying format.
#'
#' @param x An object containing metadata required to create the reader (e.g., file path, format info)
#' @param offset Numeric. Byte offset where data reading should begin. Default is 0.
#'
#' @return A BinaryReader object configured for the specific data format
#'
#' @details
#' The data_reader function is a generic that creates appropriate readers for different
#' neuroimaging formats. It handles:
#' \itemize{
#' \item File format detection and validation
#' \item Endianness configuration
#' \item Data type conversion
#' \item Compression handling (e.g., gzip)
#' \item Proper byte alignment
#' }
#'
#' @examples
#'
#' # Create reader for NIFTI file
#' meta <- read_header(system.file("extdata", "global_mask_v4.nii", package="neuroim2"))
#' reader <- data_reader(meta, offset = 0)
#'
#' # Read first 100 voxels
#' data <- read_elements(reader, 100)
#'
#'
#' @seealso
#' \code{\link{read_header}} for reading headers,
#' \code{\linkS4class{BinaryReader}} for reading binary data
#'
#' @importFrom methods new setGeneric
#'
#' @export
setGeneric("data_reader", function(x, offset) standardGeneric("data_reader"))
#' Get Dimensions of FileMetaInfo Object
#'
#' @param x A FileMetaInfo object
#' @rdname dim-methods
#' @export
setMethod("dim", "FileMetaInfo", function(x) x@dims)
#' @param offset the offset to read from the file
#' @rdname data_reader-methods
setMethod("data_reader", "NIFTIMetaInfo",
function(x, offset = 0) {
assert_that(is.numeric(offset) && length(offset) == 1,
msg = "'offset' must be a single numeric value")
total_offset <- x@data_offset + offset
if (x@descriptor@data_encoding == "gzip") {
con <- tryCatch({
gzfile(x@data_file, "rb")
}, error = function(e) {
stop("Failed to open gzipped file: ", x@data_file, "\n", e$message)
})
} else {
con <- x@data_file
}
BinaryReader(con,
total_offset,
.getRStorage(x@data_type),
x@bytes_per_element,
x@endian,
.isSigned(x@data_type))
})
#' Create Data Reader for AFNI Format
#'
#' @param x AFNIMetaInfo object
#' @param offset Numeric byte offset
#' @return BinaryReader object
#' @rdname data_reader-methods
setMethod("data_reader", "AFNIMetaInfo",
function(x, offset = 0) {
assert_that(is.numeric(offset) && length(offset) == 1,
msg = "'offset' must be a single numeric value")
total_offset <- x@data_offset + offset
if (x@descriptor@data_encoding == "gzip") {
con <- tryCatch({
gzfile(x@data_file, "rb")
}, error = function(e) {
stop("Failed to open gzipped file: ", x@data_file, "\n", e$message)
})
} else {
con <- x@data_file
}
BinaryReader(con,
total_offset,
.getRStorage(x@data_type),
x@bytes_per_element,
x@endian,
.isSigned(x@data_type))
})
#' Get transformation matrix
#' @name trans
#' @rdname trans-methods
#' @aliases trans,MetaInfo-method trans,NIFTIMetaInfo-method
#' @export
setMethod("trans", "MetaInfo",
function(x) {
D <- min(length(x@dims), 3)
trans <- diag(c(x@spacing, 1))
trans[1:D, D + 1] <- x@origin
trans
})
#' Get NIFTI Transformation Matrix
#'
#' @param x NIFTIMetaInfo object
#' @return 4x4 transformation matrix
#' @keywords internal
#' @noRd
setMethod("trans", "NIFTIMetaInfo",
function(x) x@header$qform)
#' Extract NIFTI Dimensions
#'
#' @param nifti_header NIFTI header list
#' @return Vector of image dimensions
#' @keywords internal
#' @noRd
niftiDim <- function(nifti_header) {
dimarray <- nifti_header$dimensions
assert_that(is.numeric(dimarray),
msg = "Invalid dimension array in NIFTI header")
lastidx <- min(which(dimarray == 1)) - 1
assert_that(lastidx >= 1,
msg = "Invalid dimension specification in NIFTI header")
dimarray[2:lastidx]
}
#' Create MetaInfo Object
#'
#' @title Create Neuroimaging Metadata Object
#' @description Creates a MetaInfo object containing essential metadata for neuroimaging data,
#' including dimensions, spacing, orientation, and data type information.
#'
#' @param Dim Integer vector. Image dimensions (e.g., c(64, 64, 32) for 3D).
#' @param spacing Numeric vector. Voxel dimensions in mm.
#' @param origin Numeric vector. Coordinate origin. Default is zero vector.
#' @param data_type Character. Data type (e.g., "FLOAT", "SHORT"). Default is "FLOAT".
#' @param label Character. Image label(s). Default is "".
#' @param spatial_axes Object. Spatial orientation. Default is OrientationList3D$AXIAL_LPI.
#' @param additional_axes Object. Non-spatial axes. Default is NullAxis.
#'
#' @return A MetaInfo object
#'
#' @details
#' The MetaInfo object is fundamental for:
#' \itemize{
#' \item Spatial interpretation of image data
#' \item Data type handling and conversion
#' \item Memory allocation and mapping
#' \item File I/O operations
#' }
#'
#' Input validation ensures:
#' \itemize{
#' \item Dimensions are positive integers
#' \item Spacing values are positive
#' \item Origin coordinates are finite
#' \item Data type is supported
#' }
#'
#' @examples
#' # Create metadata for 3D structural MRI
#' meta <- MetaInfo(
#' Dim = c(256, 256, 180),
#' spacing = c(1, 1, 1),
#' data_type = "FLOAT",
#' label = "T1w"
#' )
#'
#' # Get image dimensions
#' dim(meta)
#'
#' # Get transformation matrix
#' trans(meta)
#'
#' @seealso
#' \code{\linkS4class{NIFTIMetaInfo}}, \code{\linkS4class{AFNIMetaInfo}}
#'
#' @importFrom methods new
#' @importFrom assertthat assert_that
#'
#' @export
MetaInfo <- function(Dim, spacing, origin = rep(0, length(spacing)),
data_type = "FLOAT", label = "",
spatial_axes = OrientationList3D$AXIAL_LPI,
additional_axes = NullAxis) {
assert_that(is.numeric(Dim) && all(Dim > 0) && all(Dim == floor(Dim)),
msg = "'Dim' must be a vector of positive integers")
assert_that(is.numeric(spacing) && all(spacing > 0),
msg = "'spacing' must be a vector of positive numbers")
assert_that(is.numeric(origin) && all(is.finite(origin)),
msg = "'origin' must be a vector of finite numbers")
# Validate data type
valid_types <- c("BYTE", "SHORT", "INT", "FLOAT", "DOUBLE")
assert_that(data_type %in% valid_types,
msg = paste("'data_type' must be one of:", paste(valid_types, collapse = ", ")))
# Create object
new("MetaInfo",
dims = as.integer(Dim),
spacing = as.numeric(spacing),
origin = as.numeric(origin),
data_type = data_type,
label = as.character(label),
spatial_axes = spatial_axes,
additional_axes = additional_axes)
}
#' Create NIFTIMetaInfo Object
#'
#' @title Create NIFTI Format Metadata Object
#' @description Creates a NIFTIMetaInfo object containing format-specific metadata
#' for NIFTI format neuroimaging files.
#'
#' @param descriptor NIFTIFormat object specifying file format details
#' @param nifti_header List containing NIFTI header information
#'
#' @return A NIFTIMetaInfo object
#'
#' @details
#' The NIFTIMetaInfo object extends MetaInfo with NIFTI-specific features:
#' \itemize{
#' \item NIFTI header fields (qform, sform matrices)
#' \item Data scaling (slope, intercept)
#' \item File organization (separate vs. single file)
#' \item Orientation information
#' }
#'
#' Validation ensures:
#' \itemize{
#' \item Valid NIFTI format
#' \item Consistent dimensions
#' \item Valid transformation matrices
#' \item Proper data scaling
#' }
#'
#' @examples
#'
#' # Read NIFTI header
#' header <- read_header(system.file("extdata", "global_mask_v4.nii", package="neuroim2"))
#'
#' # Create format descriptor
#' fmt <- new("NIFTIFormat",
#' file_format = "NIFTI",
#' header_encoding = "raw",
#' header_extension = "nii",
#' data_encoding = "raw",
#' data_extension = "nii")
#'
#' # Create metadata
#' meta <- NIFTIMetaInfo(fmt, header@header)
#'
#' # Check dimensions
#' dim(meta)
#'
#'
#' @seealso
#' \code{\link{MetaInfo}}
#'
#' @export
NIFTIMetaInfo <- function(descriptor, nifti_header) {
# Validate inputs
if (!inherits(descriptor, "NIFTIFormat")) {
stop("'descriptor' must be a NIFTIFormat object")
}
if (!is.list(nifti_header)) {
stop("'nifti_header' must be a list")
}
if (is.null(nifti_header$file_type) || tolower(nifti_header$file_type) != "nifti") {
stop("Invalid NIFTI header: missing or incorrect file_type")
}
# Validate dimensions
dims <- try(niftiDim(nifti_header), silent = TRUE)
if (inherits(dims, "try-error")) {
stop("Invalid dimensions in NIFTI header")
}
# Validate transformation
if (!is.matrix(nifti_header$qform) || nrow(nifti_header$qform) != 4 ||
ncol(nifti_header$qform) != 4) {
stop("Invalid qform matrix in NIFTI header")
}
# Create object with validation
tryCatch({
new("NIFTIMetaInfo",
header_file = header_file(descriptor, nifti_header$file_name),
data_file = data_file(descriptor, nifti_header$file_name),
descriptor = descriptor,
endian = nifti_header$endian,
data_offset = nifti_header$vox_offset,
data_type = nifti_header$data_storage,
bytes_per_element = as.integer(.getDataSize(nifti_header$data_storage)),
dims = dims,
spatial_axes = .nearestAnatomy(nifti_header$qform),
additional_axes = NullAxis,
spacing = nifti_header$pixdim[2:4],
origin = nifti_header$qoffset,
label = strip_extension(descriptor, basename(nifti_header$file_name)),
intercept = nifti_header$scl_intercept,
slope = nifti_header$scl_slope,
header = nifti_header)
}, error = function(e) {
stop("Failed to create NIFTIMetaInfo: ", e$message)
})
}
#' Create AFNIMetaInfo Object
#'
#' @title Create AFNI Format Metadata Object
#' @description Creates an AFNIMetaInfo object containing format-specific metadata
#' for AFNI format neuroimaging files.
#'
#' @param descriptor AFNIFormat object specifying file format details
#' @param afni_header List containing AFNI header information
#'
#' @return An AFNIMetaInfo object
#'
#' @details
#' The AFNIMetaInfo object extends MetaInfo with AFNI-specific features:
#' \itemize{
#' \item AFNI brick structure
#' \item Sub-brick labels and scaling
#' \item Space transformation
#' \item Statistical parameters
#' }
#'
#' The function handles:
#' \itemize{
#' \item Dimension extraction and validation
#' \item Label generation for sub-bricks
#' \item Transformation from AFNI to NIFTI space
#' \item Data type and scaling setup
#' }
#'
#'
#' @keywords internal
AFNIMetaInfo <- function(descriptor, afni_header) {
# Validate inputs
if (!inherits(descriptor, "AFNIFormat")) {
stop("'descriptor' must be an AFNIFormat object")
}
if (!is.list(afni_header)) {
stop("'afni_header' must be a list")
}
# Extract and validate dimensions
if (is.null(afni_header$DATASET_DIMENSIONS) ||
is.null(afni_header$DATASET_DIMENSIONS$content)) {
stop("Missing DATASET_DIMENSIONS in AFNI header")
}
.Dim <- afni_header$DATASET_DIMENSIONS$content[
afni_header$DATASET_DIMENSIONS$content > 0
]
if (length(.Dim) < 3) {
stop("AFNI dataset must have at least 3 dimensions")
}
# Add time dimension if present
if (!is.null(afni_header$DATASET_RANK$content) &&
afni_header$DATASET_RANK$content[2] > 1) {
.Dim <- c(.Dim, afni_header$DATASET_RANK$content[2])
}
# Generate or extract labels
labs <- if (is.null(afni_header$BRICK_LABS$content)) {
paste0("#", seq(0, afni_header$DATASET_RANK$content[2] - 1))
} else {
afni_header$BRICK_LABS$content
}
# Calculate space transformation
Tdicom <- tryCatch({
matrix(afni_header$IJK_TO_DICOM$content, 3, 4, byrow = TRUE)
}, error = function(e) {
stop("Invalid IJK_TO_DICOM transformation in AFNI header")
})
TLPI <- perm_mat(OrientationList3D$AXIAL_RAI) %*% Tdicom[1:3, ]
TLPI <- rbind(TLPI, c(0, 0, 0, 1))
# Determine data type and size
data_type <- switch(as.character(afni_header$BRICK_TYPES$content[1]),
"0" = "BYTE",
"1" = "SHORT",
"3" = "FLOAT",
stop("Unsupported BRICK_TYPE in AFNI header"))
bytes_per_element <- switch(as.character(afni_header$BRICK_TYPES$content[1]),
"0" = 1L,
"1" = 2L,
"3" = 4L)
# Create object with validation
tryCatch({
new("AFNIMetaInfo",
header_file = header_file(descriptor, afni_header$file_name),
data_file = data_file(descriptor, afni_header$file_name),
descriptor = descriptor,
endian = ifelse(afni_header[["BYTEORDER_STRING"]]$content == "MSB_FIRST",
"big", "little"),
data_offset = 0L,
data_type = data_type,
bytes_per_element = as.integer(bytes_per_element),
dims = .Dim,
spatial_axes = .nearestAnatomy(TLPI),
additional_axes = NullAxis,
spacing = abs(afni_header$DELTA$content),
origin = afni_header$ORIGIN$content,
label = labs,
intercept = 0,
slope = ifelse(afni_header$BRICK_FLOAT_FACS$content == 0,
1,
afni_header$BRICK_FLOAT_FACS$content),
header = afni_header)
}, error = function(e) {
stop("Failed to create AFNIMetaInfo: ", e$message)
})
}
#' read header information of an image file
#'
#'
#' @param file_name the name of the file to read
#' @return an instance of class \code{\linkS4class{FileMetaInfo}}
#' @examples
#' header <- read_header(system.file("extdata", "global_mask_v4.nii", package="neuroim2"))
#' @export read_header
read_header <- function(file_name) {
desc <- find_descriptor(file_name)
if (is.null(desc)) {
stop(paste("could not find reader for file: ", file_name))
}
read_meta_info(desc, file_name)
}
#' @export
setAs(from="MetaInfo", to="NIFTIMetaInfo", def=function(from) {
if (inherits(from, "NIFTIMetaInfo")) {
from
} else {
hdr <- as_nifti_header(from)
desc <- find_descriptor(hdr$file_name)
NIFTIMetaInfo(desc, hdr)
}
})
#' @export
#' @rdname show-methods
setMethod(f="show", signature=signature("FileMetaInfo"),
def=function(object) {
cat("an instance of class", class(object), "\n\n")
cat("header_file:", "\t", object@header_file, "\n")
cat("data_file:", "\t", object@data_file, "\n")
cat("endian:", "\t", object@endian, "\n")
cat("data_offset:", "\t", object@data_offset, "\n")
cat("data_type:", "\t", object@data_type, "\n")
cat("dimensions:", "\t", object@dims, "\n")
cat("voxel size:", "\t", object@spacing, "\n")
cat("origin:", "\t", object@origin, "\n")
cat("label(s):", "\t", object@label, "\n")
cat("intercept:", "\t", object@intercept, "\n")
cat("slope:", "\t\t", object@slope, "\n\n")
cat("additional format-specific info may be contained in @header slot", "\n")
})
#' @rdname MetaInfo-methods
#' @aliases trans,MetaInfo-method
#' trans,NIFTIMetaInfo-method
#' data_reader,AFNIMetaInfo-method
#' data_reader,NIFTIMetaInfo-method
#' @export
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.