diff --git a/R/nifti2mgh.R b/R/nifti2mgh.R index e8ffa36..2f2191d 100644 --- a/R/nifti2mgh.R +++ b/R/nifti2mgh.R @@ -5,7 +5,7 @@ # "The world coordinate system is assumed to be ras: +x is Right, +y is Anterior and +z is Superior" -#' @title Turn an `oro.nifti` instance into an `fs.volume` instance with complete header. +#' @title Turn a 3D or 4D `oro.nifti` instance into an `fs.volume` instance with complete header. #' #' @description This is work in progress. This function takes an `oro.nifti` instance and computes the MGH header fields from the NIFTI header data, allowing for proper orientation of the contained image data (see \code{\link[freesurferformats]{mghheader.vox2ras}} and related functions). Currently only few datatypes are supported, and the `sform` header field needs to be present in the NIFTI instance. #' @@ -17,6 +17,8 @@ #' #' @seealso \code{\link[oro.nifti]{readNIfTI}}, \code{\link[freesurferformats]{read.fs.mgh}} #' +#' @note This is not supposed to be used to read 1D morphometry data from NIFTI files generated by FreeSurfer (e.g., by converting `lh.thickness` to NIFTI using `mri_convert`): the FreeSurfer NIFTI hack is not supported by oro.nifti. +#' #' @references \href{https://nifti.nimh.nih.gov/nifti-1/}{NIfTI-1 data format spec} #' #' @examples @@ -37,7 +39,6 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro nifti_img = filepath; if (requireNamespace("oro.nifti", quietly = TRUE)) { - message(sprintf("fs.volume.from.oro.nifti: This is an experimental WIP function. Do NOT use this in production.\n")); if(is.character(nifti_img)) { nifti_img = oro.nifti::readNIfTI(nifti_img); } @@ -65,9 +66,9 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro # The intent code describes how to interprete the data (e.g., that the values describe a certain distribution or whatever). # See https://brainder.org/2012/09/23/the-nifti-file-format/ or the NIFTI spec for details. # There is no field for this in MGH header afaict, so we ignore it. - if(nifti_img@intent_code != 0L) { - message("NIFTI intent_code '%d' ignored.\n", nifti_img@intent_code); - } + #if(nifti_img@intent_code != 0L) { + # message("NIFTI intent_code '%d' ignored.\n", nifti_img@intent_code); + #} ## -----Check the datatype ------ MRI_UCHAR = translate.mri.dtype("MRI_UCHAR"); @@ -90,7 +91,7 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro bytes_per_voxel = mri_dtype_numbytes(dtype); # Note that we store the size in **bytes** per voxel, while the Nifti header uses **bits**. - message(sprintf("Nifti header: Nifti datatype=%d with %d bitpix. MRI datatype '%s' (code %d), with %d bytes per voxel.\n", nifti_img@datatype, nifti_img@bitpix, translate.mri.dtype(dtype), dtype, bytes_per_voxel)); + #message(sprintf("Nifti header: Nifti datatype=%d with %d bitpix. MRI datatype '%s' (code %d), with %d bytes per voxel.\n", nifti_img@datatype, nifti_img@bitpix, translate.mri.dtype(dtype), dtype, bytes_per_voxel)); ## ----- Check image dimensions ----- num_used_dimensions = nifti_img@dim_[1]; @@ -114,7 +115,7 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro # Check for FreeSurfer hack in number of columns (negative column count, true column count stored in @glmin field). # Loading these non-standard NIFTI files will not work anyways with oro.nifti I guess, so this may never happen. if(nifti_img@dim_[2] < 0L) { - message(sprintf("Nifti image with FreeSurfer hack detected, assuming %d columns.\n", nifti_img@glmin)); + #message(sprintf("Nifti image with FreeSurfer hack detected, assuming %d columns.\n", nifti_img@glmin)); ncols = nifti_img@glmin; } else { ncols = nifti_img@dim_[2]; @@ -126,7 +127,7 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro is_ico7 = (ncols * nifti_img@dim_[3] * nifti_img@dim_[4] == ico7_num_vertices); ico7_state_string = ifelse(is_ico7, "looks like", "does NOT look like"); - message(sprintf("Nifti header: Image has %d used dimensions. It %s ico7 morphometry data.\n", nifti_img@dim_[1], ico7_state_string)); + #message(sprintf("Nifti header: Image has %d used dimensions. It %s ico7 morphometry data.\n", nifti_img@dim_[1], ico7_state_string)); ## Compute space and time unit factors from @xyzt_units. # NIFTI can store in different units, while MGH uses fixed units. Depending on the unit used in the NIFTI, we may need to @@ -135,8 +136,8 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro space_unit_factor = space_info$scaling; time_info = nifti.time.info(nifti_img@xyzt_units); time_unit_factor = time_info$scaling; - message(sprintf("Space data in NIFTI file is stored in unit '%s', using scaling factor %f to match FreeSurfer unit 'mm'.\n", space_info$name, space_info$scaling)); - message(sprintf("Time data in NIFTI file is stored in unit '%s', using scaling factor %f to match FreeSurfer unit 'ms'.\n", time_info$name, time_info$scaling)); + #message(sprintf("Space data in NIFTI file is stored in unit '%s', using scaling factor %f to match FreeSurfer unit 'mm'.\n", space_info$name, space_info$scaling)); + #message(sprintf("Time data in NIFTI file is stored in unit '%s', using scaling factor %f to match FreeSurfer unit 'ms'.\n", time_info$name, time_info$scaling)); header = mghheader(c(ncols, nifti_img@dim_[3], nifti_img@dim_[4], num_frames), dtype); @@ -152,7 +153,7 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro # Extract vox2ras matrix from NIFTI sform, then init the MGH header from the vox2ras matrix. vox2ras = rbind(nifti_img@srow_x, nifti_img@srow_y, nifti_img@srow_z, c(0, 0, 0, 1)); header = mghheader.update.from.vox2ras(header, vox2ras); - message(sprintf("Computed transformation matrix into '%s' using sform header data.\n", nifti.transform.type.name(nifti_img@sform_code))); + #message(sprintf("Computed transformation matrix into '%s' using sform header data.\n", nifti.transform.type.name(nifti_img@sform_code))); header$ras_good_flag = 1L; } else if(nifti_img@qform_code != 0L) { # The qform transform is based on the 3 qoffset fields, qfac, and 4 quaternions. 3 of them are in the following header fields, the 4th one needs to be @@ -221,7 +222,7 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro header$ras_good_flag = 1L; - message(sprintf("Computed transformation matrix into '%s' using qform header data.\n", nifti.transform.type.name(nifti_img@qform_code))); + #message(sprintf("Computed transformation matrix into '%s' using qform header data.\n", nifti.transform.type.name(nifti_img@qform_code))); } else { warning("Nifti image does not contain valid sform or qform, orientation cannot be derived and is arbitrary."); # Fill in more or less random orientation values, scale voxel values by xsize, ysize, zsize. @@ -273,7 +274,8 @@ read.fs.volume.nii <- function(filepath, flatten = FALSE, with_header=FALSE, dro header$internal$slice_direction_name = orientation_info$direction_name; data = nifti_img@.Data; - # TODO: Check in which storage ordering the data is saved in the NIFTI image and rotate/permute the array accordingly. + + # Check in which storage ordering the data is saved in the NIFTI image and rotate/permute the array accordingly. # See https://brainder.org/2012/09/23/the-nifti-file-format/ and the official NIFTI standard. data = rotate3D(drop(data), axis=1L, degrees = 90L); diff --git a/man/read.fs.volume.nii.Rd b/man/read.fs.volume.nii.Rd index 546e451..ccc98e3 100644 --- a/man/read.fs.volume.nii.Rd +++ b/man/read.fs.volume.nii.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/nifti2mgh.R \name{read.fs.volume.nii} \alias{read.fs.volume.nii} -\title{Turn an `oro.nifti` instance into an `fs.volume` instance with complete header.} +\title{Turn a 3D or 4D `oro.nifti` instance into an `fs.volume` instance with complete header.} \usage{ read.fs.volume.nii( filepath, @@ -26,6 +26,9 @@ an `fs.volume` instance. The `header` fields are computed from the NIFTI header. \description{ This is work in progress. This function takes an `oro.nifti` instance and computes the MGH header fields from the NIFTI header data, allowing for proper orientation of the contained image data (see \code{\link[freesurferformats]{mghheader.vox2ras}} and related functions). Currently only few datatypes are supported, and the `sform` header field needs to be present in the NIFTI instance. } +\note{ +This is not supposed to be used to read 1D morphometry data from NIFTI files generated by FreeSurfer (e.g., by converting `lh.thickness` to NIFTI using `mri_convert`): the FreeSurfer NIFTI hack is not supported by oro.nifti. +} \examples{ \dontrun{ base_file = "~/data/subject1_only/subject1/mri/brain"; # missing file ext.