The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Advanced 3D Volume Patterns

2026-04-16

This article is now the detailed follow-on to vignette("VolumesAndVectors"). Read that vignette first if you want the shortest introduction to read_vol(), read_vec(), and the basic NeuroVol / NeuroVec mental model.

Use this article when you specifically want deeper 3D-volume details: masks, coordinate conversion, manual construction, and slice-level inspection.

Read one volume to establish context

file_name <- system.file("extdata", "global_mask2.nii.gz", package = "neuroim2")
vol <- read_vol(file_name)

This article assumes you already know the basic NeuroVol story from vignette("VolumesAndVectors"). The remaining sections focus on patterns that are specific to 3D work.

class(vol)
#> [1] "DenseNeuroVol"
#> attr(,"package")
#> [1] "neuroim2"
is.array(vol)
#> [1] TRUE
dim(vol)
#> [1] 64 64 25
vol[1, 1, 1]
#> [1] 0
vol[64, 64, 24]
#> [1] 0

Coordinate conversion and spatial metadata

sp <- space(vol)
sp
#> <NeuroSpace> [3D] 
#> ── Geometry ──────────────────────────────────────────────────────────────────── 
#>   Dimensions    : 64 x 64 x 25
#>   Spacing       : 3.5 x 3.5 x 3.7 mm
#>   Origin        : 112, -108.5, -46.25
#>   Orientation   : LAS
#>   Voxels        : 102,400
dim(vol)
#> [1] 64 64 25
spacing(vol)
#> [1] 3.5 3.5 3.7
origin(vol)
#> [1]  112.00 -108.50  -46.25

You can convert between indices, voxel grid coordinates, and real-world coordinates:

idx <- 1:5
g <- index_to_grid(vol, idx)
w <- index_to_coord(vol, idx)
idx2 <- coord_to_index(vol, w)
all.equal(idx, idx2)
#> [1] "Mean relative difference: 0.3333333"

A numeric image volume can be converted to a binary image as follows:

vol2 <- as.logical(vol)
class(vol2)
#> [1] "LogicalNeuroVol"
#> attr(,"package")
#> [1] "neuroim2"
print(vol2[1, 1, 1])
#> [1] FALSE

Masks and LogicalNeuroVol

Create a mask from a threshold or an explicit set of indices. Masks are LogicalNeuroVol and align with the 3D space.

mask1 <- as.mask(vol > 0.5)
mask1
#> <DenseNeuroVol> [406.6 Kb] 
#> ── Spatial ───────────────────────────────────────────────────────────────────── 
#>   Dimensions    : 64 x 64 x 25
#>   Spacing       : 3.5 x 3.5 x 3.7 mm
#>   Origin        : 112, -108.5, -46.25
#>   Orientation   : LAS
#> ── Data ──────────────────────────────────────────────────────────────────────── 
#>   Range         : [0.000, 1.000]

idx_hi <- which(vol > 0.8)
mask2 <- as.mask(vol, idx_hi)
sum(mask2) == length(idx_hi)
#> [1] TRUE

mean_in_mask <- mean(vol[mask1@.Data])
mean_in_mask
#> [1] 1

Constructing volumes manually

We can also create a NeuroVol instance from an array or numeric vector. First we construct a standard R array:

    x <- array(0, c(64,64,64))

Now we create a NeuroSpace instance that describes the geometry of the image, including at minimum its dimensions and voxel spacing.

    bspace <- NeuroSpace(dim=c(64,64,64), spacing=c(1,1,1))
    vol <- NeuroVol(x, bspace)
    vol
#> <DenseNeuroVol> [2 Mb] 
#> ── Spatial ───────────────────────────────────────────────────────────────────── 
#>   Dimensions    : 64 x 64 x 64
#>   Spacing       : 1 x 1 x 1 mm
#>   Origin        : 0, 0, 0
#>   Orientation   : RAS
#> ── Data ──────────────────────────────────────────────────────────────────────── 
#>   Range         : [0.000, 0.000]

We do not usually have to create NeuroSpace objects by hand because real image files carry this information in their headers. In practice you usually copy an existing space:

    vol2 <- NeuroVol((vol+1)*25, space(vol))
    max(vol2)
#> [1] 25
    space(vol2)
#> <NeuroSpace> [3D] 
#> ── Geometry ──────────────────────────────────────────────────────────────────── 
#>   Dimensions    : 64 x 64 x 64
#>   Spacing       : 1 x 1 x 1 mm
#>   Origin        : 0, 0, 0
#>   Orientation   : RAS
#>   Voxels        : 262,144

Slicing and quick visualization

The easiest way to view a volume is with plot(), which shows a 3 x 3 montage of evenly-spaced axial slices:

plot(vol)
3x3 montage of axial slices.

Default plot() montage

You can also extract a single 2D slice for display using standard array indexing:

    z <- ceiling(dim(vol)[3] / 2)
    image(vol[,,z], main = paste("Slice z=", z))
Mid-slice of example volume (grayscale image).

Mid-slice of example volume

Reorienting and resampling

You can change an image’s orientation and voxel spacing. Use reorient() to remap axes (e.g., to RAS) and resample_to() to match a target space.

    # Reorient the space (LPI -> RAS) and compare coordinate mappings
    sp_lpi <- space(vol)
    sp_ras <- reorient(sp_lpi, c("R","A","S"))
    g     <- t(matrix(c(10, 10, 10)))
    world_lpi <- grid_to_coord(sp_lpi, g)
    world_ras <- grid_to_coord(sp_ras, g)
    # world_lpi and world_ras differ due to axis remapping

Resample to a new spacing or match a target NeuroSpace:

    # Create a target space with 2x finer resolution
    sp  <- space(vol)
    sp2 <- NeuroSpace(sp@dim * c(2,2,2), sp@spacing/2, origin=sp@origin, trans=trans(vol))

    # Resample (trilinear)
    vol_resamp <- resample_to(vol, sp2, method = "linear")
    dim(vol_resamp)

Downsampling

Reduce spatial resolution to speed up downstream operations.

    # Downsample by target spacing
    vol_ds1 <- downsample(vol, spacing = spacing(vol)[1:3] * 2)
    dim(vol_ds1)
#> [1] 32 32 32

    # Or by factor
    vol_ds2 <- downsample(vol, factor = 0.5)
    dim(vol_ds2)
#> [1] 32 32 32

Writing a NIFTI formatted image volume

When we’re ready to write an image volume to disk, we use write_vol

    write_vol(vol2, "output.nii")
    
    ## adding a '.gz' extension results ina gzipped file.
    write_vol(vol2, "output.nii.gz")

You can also write to a temporary file during workflows:

    tmp <- tempfile(fileext = ".nii.gz")
    write_vol(vol2, tmp)
    file.exists(tmp)
#> [1] TRUE
    unlink(tmp)

For reorientation, resampling, and downsampling, use vignette("Resampling"), which now owns that topic directly.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.