# Working with Image Volumes

#### 2025-04-09

## Reading a NIFTI formatted image volume

The way to read an volumetric image file is to use `read_vol`:

        library(neuroim2)
    #> Loading required package: Matrix
    #>
    #> Attaching package: 'neuroim2'
    #> The following object is masked from 'package:base':
    #>
    #>     scale
        file_name <- system.file("extdata", "global_mask2.nii.gz", package="neuroim2")
        vol <- read_vol(file_name)

## Working with image volumes

Information about the geometry of the image volume is shown here:

        print(vol)
    #>
    #>  === NeuroVol Object ===
    #>
    #> * Basic Information
    #>   Type:      DenseNeuroVol
    #>   Dimensions: 64 x 64 x 25 (406.6 Kb)
    #>   Total Voxels: 102,400
    #>
    #> * Data Properties
    #>   Value Range: [0.00, 1.00]
    #>
    #> * Spatial Properties
    #>   Spacing: 3.50 x 3.50 x 3.70 mm
    #>   Origin:  112.0, -108.5, -46.2 mm
    #>   Axes:    Right-to-Left x Posterior-to-Anterior x Inferior-to-Superior
    #>
    #> ======================================
    #>
    #>  Access Methods:
    #>   .  Get Slice:   slice(object, zlevel=10)
    #>   .  Get Value:   object[i, j, k]
    #>   .  Plot:       plot(object)  # shows multiple slices

`read_vol` returns an object of class `NeuroVol` object which extends an R `array` and has 3 dimensions (x,y,z).

        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

Arithmetic can be performed on images as if they were ordinary `array`s:

        vol2 <- vol + vol
        sum(vol2) == 2 * sum(vol)
    #> [1] TRUE

        vol3 <- vol2 - 2*vol
        all(vol3 == 0)
    #> [1] TRUE

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

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

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

Now we reate 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
    #>
    #>  === NeuroVol Object ===
    #>
    #> * Basic Information
    #>   Type:      DenseNeuroVol
    #>   Dimensions: 64 x 64 x 64 (2 Mb)
    #>   Total Voxels: 262,144
    #>
    #> * Data Properties
    #>   Value Range: [0.00, 0.00]
    #>
    #> * Spatial Properties
    #>   Spacing: 1.00 x 1.00 x 1.00 mm
    #>   Origin:  0.0, 0.0, 0.0 mm
    #>   Axes:    Left-to-Right x Posterior-to-Anterior x Inferior-to-Superior
    #>
    #> ======================================
    #>
    #>  Access Methods:
    #>   .  Get Slice:   slice(object, zlevel=10)
    #>   .  Get Value:   object[i, j, k]
    #>   .  Plot:       plot(object)  # shows multiple slices

We do not usually have to create `NeuroSpace` objects, because geometric information about an image is automatically determined from information stored in the image file header. Thus, `NeuroSpace` objects are usually copied from existing images using the `space` extractor function when needed:

        vol2 <- NeuroVol((vol+1)*25, space(vol))
        max(vol2)
    #> [1] 25
        space(vol2)
    #>
    #>  NeuroSpace Object
    #>
    #>  >> Dimensions
    #>   Grid Size: 64 x 64 x 64
    #>   Memory:   5.9 KB
    #>
    #>  >> Spatial Properties
    #>   Spacing:   1.00 x 1.00 x 1.00 mm
    #>   Origin:    0.00 x 0.00 x 0.00 mm
    #>
    #>  >> Anatomical Orientation
    #>   X: Left-to-Right  |  Y: Posterior-to-Anterior  |  Z: Inferior-to-Superior
    #>
    #>  >> World Transformation
    #>   Forward (Voxel to World):
    #>     1.000  0.000  0.000  0.000
    #> 0.000  1.000  0.000  0.000
    #> 0.000  0.000  1.000  0.000
    #> 0.000  0.000  0.000  1.000
    #>   Inverse (World to Voxel):
    #>     1.000  0.000  0.000  0.000
    #> 0.000  1.000  0.000  0.000
    #> 0.000  0.000  1.000  0.000
    #> 0.000  0.000  0.000  1.000
    #>
    #>  >> Bounding Box
    #>   Min Corner: 0.0, 0.0, 0.0 mm
    #>   Max Corner: 63.0, 63.0, 63.0 mm
    #>
    #> ==================================================

## 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")