tests/testthat/test-nifti-roundtrip.R

test_that("NIfTI round-trip preserves DenseNeuroVol data and affine", {
  vol <- make_vol(c(10, 10, 10), spacing = c(2, 2, 2), origin = c(-10, -10, -10))
  tmp <- tempfile(fileext = ".nii.gz")
  on.exit(unlink(tmp))

  write_vol(vol, tmp)
  vol2 <- read_vol(tmp)

  expect_equal(as.numeric(vol@.Data), as.numeric(vol2@.Data), tolerance = 1e-5)
  expect_equal(dim(vol), dim(vol2))
  expect_equal(spacing(space(vol)), spacing(space(vol2)), tolerance = 1e-5)
  expect_equal(trans(space(vol)), trans(space(vol2)), tolerance = 1e-5)
})

test_that("NIfTI round-trip preserves DenseNeuroVol with non-unit spacing", {
  vol <- make_vol(c(8, 8, 8), spacing = c(3, 3, 4), origin = c(-12, -12, -16))
  tmp <- tempfile(fileext = ".nii")
  on.exit(unlink(tmp))

  write_vol(vol, tmp)
  vol2 <- read_vol(tmp)

  expect_equal(as.numeric(vol@.Data), as.numeric(vol2@.Data), tolerance = 1e-5)
  expect_equal(spacing(space(vol)), spacing(space(vol2)), tolerance = 1e-5)
})

test_that("NIfTI round-trip preserves sform affine", {
  # Create a volume with oblique affine (sform should be preferred)
  aff <- matrix(c(1.5, 0.1, 0, -80,
                   0.1, 2.0, 0, -120,
                   0, 0, 2.5, -60,
                   0, 0, 0, 1), 4, 4, byrow = TRUE)
  sp <- NeuroSpace(c(10, 10, 10), trans = aff)
  vol <- DenseNeuroVol(rnorm(1000), sp)
  tmp <- tempfile(fileext = ".nii.gz")
  on.exit(unlink(tmp))

  write_vol(vol, tmp)
  vol2 <- read_vol(tmp)

  expect_equal(trans(space(vol)), trans(space(vol2)), tolerance = 1e-4)
})

test_that("NIfTI round-trip works for gzipped vs uncompressed", {
  vol <- make_vol(c(8, 8, 8))

  tmp_gz <- tempfile(fileext = ".nii.gz")
  tmp_nii <- tempfile(fileext = ".nii")
  on.exit(unlink(c(tmp_gz, tmp_nii)))

  write_vol(vol, tmp_gz)
  write_vol(vol, tmp_nii)

  vol_gz <- read_vol(tmp_gz)
  vol_nii <- read_vol(tmp_nii)

  expect_equal(as.numeric(vol_gz@.Data), as.numeric(vol_nii@.Data), tolerance = 1e-6)
  expect_equal(as.numeric(vol@.Data), as.numeric(vol_gz@.Data), tolerance = 1e-5)
})

test_that("NIfTI round-trip preserves DenseNeuroVec data", {
  skip_on_cran()
  vec <- make_vec(c(8, 8, 8), ntime = 5)
  tmp <- tempfile(fileext = ".nii.gz")
  on.exit(unlink(tmp))

  write_vec(vec, tmp)
  vec2 <- read_vec(tmp)

  expect_equal(dim(vec), dim(vec2))
  # Compare raw numeric data, ignoring attributes
  expect_equal(as.numeric(vec@.Data), as.numeric(vec2@.Data), tolerance = 1e-5)
})

test_that("NIfTI round-trip preserves 4D spatial affine", {
  skip_on_cran()
  sp4 <- NeuroSpace(c(8L, 8L, 8L, 5L), spacing = c(2, 2, 2))
  vec <- DenseNeuroVec(array(rnorm(8*8*8*5), c(8,8,8,5)), sp4)
  tmp <- tempfile(fileext = ".nii.gz")
  on.exit(unlink(tmp))

  write_vec(vec, tmp)
  vec2 <- read_vec(tmp)

  # 3D spatial affine should be preserved
  tx1 <- trans(space(vec))
  tx2 <- trans(space(vec2))
  expect_equal(tx1[1:3, 1:3], tx2[1:3, 1:3], tolerance = 1e-5)
})

test_that("NIfTI round-trip preserves LogicalNeuroVol as binary mask", {
  mask <- make_mask(c(10, 10, 10), frac = 0.4)
  tmp <- tempfile(fileext = ".nii.gz")
  on.exit(unlink(tmp))

  write_vol(mask, tmp)
  vol2 <- read_vol(tmp)

  # Logical -> numeric on disk; compare binary values
  expect_equal(as.numeric(mask@.Data), as.numeric(vol2@.Data), tolerance = 1e-6)
})

test_that("NIfTI round-trip preserves data range (no accidental scaling)", {
  # Use a known range to catch slope/intercept issues
  vol <- make_vol(c(8, 8, 8), data = seq_len(512) * 0.1)
  tmp <- tempfile(fileext = ".nii.gz")
  on.exit(unlink(tmp))

  write_vol(vol, tmp)
  vol2 <- read_vol(tmp)

  expect_equal(range(vol@.Data), range(vol2@.Data), tolerance = 1e-4)
})

test_that("read_vol with extdata file works", {
  f <- system.file("extdata", "global_mask_v4.nii", package = "neuroim2")
  skip_if(f == "", message = "extdata not available")
  vol <- read_vol(f)
  expect_s4_class(vol, "NeuroVol")
  expect_equal(length(dim(vol)), 3)
})

Try the neuroim2 package in your browser

Any scripts or data that you put into this service are public.

neuroim2 documentation built on April 16, 2026, 5:07 p.m.