R/CT_Skull_Strip.R

Defines functions CT_Skull_Strip

Documented in CT_Skull_Strip

#' @title CT Skull Stripping within R
#' @description Skull Stripping (using FSL's BET) a CT file using \code{fslr}
#' functions
#' @param img (character) File to be skull stripped or object of class
#' nifti
#' @param outfile (character) output filename
#' @param keepmask (logical) Should we keep the mask?
#' @param maskfile (character) Filename for mask (if \code{keepmask = TRUE}).
#' If \code{NULL}, then will do \code{paste0(outfile, "_Mask")}.
#' @param inskull_mesh (logical) Create inskull_mesh file from bet?
#' (Warning - will take longer)
#' This an exterior surface of the brain. (experimental)
#' Also, if \code{outfile} is \code{NULL}, then this will be created in
#' a temporary directory and not be retrieved.
#' @param retimg (logical) return image of class nifti
#' @param reorient (logical) If retimg, should file be reoriented when read in?
#' Passed to \code{\link{readNIfTI}}.
#' @param intern (logical) pass to \code{\link{system}}
#' @param betcmd (character) bet command to be used, see \code{\link{fslbet}}
#' @param opts (character) additional options to \code{\link{fslbet}}
#' @param presmooth (logical) indicator if pre-smoothing should be
#' done before BET
#' @param remask (logical) Mask the smoothed image with HU mask from initial
#' step?
#' @param refill (logical) indicator to post-smooth mask and then fill
#' @param refill.thresh (numeric) Value to threshold post-smoothed mask
#' @param sigma (integer) size of Gaussian kernel passed to
#' \code{\link{fslsmooth}} if \code{presmooth} is \code{TRUE}
#' @param lthresh (default: 0) Lower value to threshold CT
#' \code{\link{fslthresh}}
#' @param uthresh (default: 100) Upper value to threshold CT
#' \code{\link{fslthresh}}
#' @param verbose (logical) Should diagnostic output be printed?
#' @param ... additional arguments passed to \code{\link{fslbet}}.
#' @param smooth_before_threshold Should the image be smoothed before
#' thresholding?  This can be useful for bone-window scans.
#'
#' @return character or logical depending on intern
#' @importFrom fslr fslthresh fslmaths fslfill fslsmooth fslmask fslbet
#' @importFrom neurobase nii.stub
#' @importFrom fslr get.imgext
#' @export
CT_Skull_Strip <- function(
  img,
  outfile = NULL,
  keepmask = TRUE,
  maskfile = NULL,
  inskull_mesh = FALSE,
  retimg = TRUE,
  reorient = FALSE,
  intern=TRUE,
  betcmd = "bet2",
  opts = "-f 0.01 -v",
  presmooth = TRUE,
  remask = TRUE,
  refill = FALSE,
  refill.thresh = 0.75,
  sigma = 1,
  lthresh = 0,
  uthresh = 100,
  smooth_before_threshold = FALSE,
  verbose=TRUE,
  ...
){
  if (retimg) {
    if (is.null(outfile)) {
      outfile = tempfile()
    }
  } else {
    stopifnot(!is.null(outfile))
  }
  orig_img = img


  if (smooth_before_threshold) {
    if (verbose) {
      message(paste0("# Smoothing before Thresholding"))
    }
    pm_file = tempfile()
    result = fslsmooth(img,
                       outfile = pm_file,
                       sigma = sigma,
                       retimg = FALSE,
                       verbose = verbose)
    img = pm_file
  }

  if (verbose) {
    message(paste0("# Thresholding Image to ",
                   lthresh, "-", uthresh, "\n"))
  }

  tfile = tempfile()
  tcopy = tempfile(fileext = ".nii.gz")
  outfile = nii.stub(outfile)
  run = fslthresh(img, thresh = lthresh, uthresh = uthresh,
                  outfile = tfile,
                  retimg = FALSE,
                  intern = FALSE,
                  verbose = verbose)
  if (!any(c(readnii(tfile)) > 0)) {
    stop("No positive values in the thresholded output!")
  }
  if (verbose) {
    message(paste0("# Thresholding return: ", run, "\n"))
  }

  if (remask) {
    if (verbose) {
      message(paste0("# Absolute value so fslmaths -bin keeps all mask",
                     " even if lthresh < 0\n"))
    }
    absfile = tempfile()
    run = fslmaths(tfile, outfile = absfile,
                   retimg = FALSE,
                   intern = FALSE,
                   opts = "-abs",
                   verbose = verbose)


    if (verbose) {
      message(paste0("# Abs return: ", run, "\n"))
    }


    if (verbose) {
      message(paste0(
        "# Creating binary mask to remask after filling\n"))
    }
    bonefile = tempfile()
    #   fslbin(outfile, retimg = FALSE, outfile = bonefile, intern=FALSE)
    fslfill(file = absfile,
            bin = TRUE,
            outfile = bonefile,
            retimg = FALSE,
            intern = FALSE,
            verbose = verbose)
  }

  ### Must prefill for the presmooth - not REALLY necessary, but if you're
  ### smoothing, you likely have noisy data.

  #   if (isTRUE(presmooth)){
  #     if (!isTRUE(prefill)){
  #       warning("if presmooth, must have prefill = TRUE")
  #       prefill= TRUE
  #     }
  #   }
  #   #### prefill will mask the img
  #   if (isTRUE(prefill)){
  #     if (verbose){
  #       message(paste0("Remasking 0 - 100 after filling holes\n"))
  #     }
  #     run = fslmask(img,
  #                   mask = bonemask,
  #                   outfile = outfile,
  #                   retimg = FALSE,
  #                   intern = FALSE)
  #   }


  if (isTRUE(presmooth)) {

    if (verbose) {
      message(paste0("# Presmoothing image\n"))
    }
    run = fslsmooth(tfile,
                    outfile = tfile,
                    sigma = sigma,
                    retimg = FALSE,
                    intern = FALSE,
                    verbose = verbose)
    if (verbose) {
      message(paste0("# Pre Smoothing Diagnostic: ", run, "\n"))
    }

    if (remask) {
      if (verbose) {
        message(paste0("# Remasking Smoothed Image\n"))
      }
      run = fslmask(tfile,
                    mask = bonefile,
                    outfile = tfile,
                    retimg = FALSE,
                    intern = FALSE,
                    verbose = verbose)
      if (verbose) {
        message(paste0("# Pre Smoothing Diagnostic: ", run, "\n"))
      }

    }
  }
  if (verbose) {
    message(paste0("copied file: ", tcopy))
    file.copy(tfile, tcopy)
    file.copy(paste0(tfile, ".nii"), tcopy, overwrite = FALSE)
    file.copy(paste0(tfile, ".nii.gz"), tcopy, overwrite = FALSE)
  }

  #### Different options for bet
  if (inskull_mesh) {
    opts = paste0(opts, " -A")
    if (betcmd != "bet") {
      warning("Can't use bet2 with inskull mesh, using bet")
    }
    betcmd = "bet"
  }
  betintern = TRUE
  if (verbose) {
    message(paste0("# Running ", betcmd, "\n"))
    betintern = FALSE
  }
  runbet = fslbet(infile = tfile,
                  outfile = tfile,
                  retimg = FALSE, intern = betintern,
                  betcmd = betcmd,
                  opts = opts,
                  verbose = verbose, ...)

  if (verbose) {
    message(paste0("# ", betcmd, " output: ", runbet, "\n"))
  }
  ###############
  # Cleanup
  ##############
  ext = get.imgext()

  if (isTRUE(inskull_mesh)) {
    if (verbose) {
      message("# Deleting extraneous files\n")
    }
    end.names = c("inskull_mask", "outskin_mask", "outskin_mesh",
                  "outskull_mask", "outskull_mesh",
                  "skull_mask")
    end.names = paste0(end.names, ext)
    end.names = c(end.names,
                  "inskull_mesh.off",
                  "outskin_mesh.off",
                  "outskull_mesh.off",
                  "mesh.vtk")
    end.names = paste(tfile, end.names, sep = "_")
    file.remove(end.names)
  }

  #####################
  # Filling in holes of the mask (like choroid plexis and CSF)
  #####################
  if (verbose) {
    message("# Using fslfill to fill in any holes in mask \n")
  }
  if (is.null(maskfile)) {
    maskfile = nii.stub(outfile)
    maskfile = paste0(maskfile, "_Mask")
  }
  stopifnot(inherits(maskfile, "character"))
  # outmask = paste(outfile, "inskull_mask", sep="_")
  # outmask = paste0(outmask, ext)
  # file.rename(outmask, paste0(maskfile, ext))
  absfile = tempfile()
  run = fslmaths(tfile, outfile = absfile,
                 retimg = FALSE,
                 intern = FALSE,
                 verbose = verbose, opts = "-abs")

  runmask = fslfill(file = absfile, bin = TRUE,
                    outfile = maskfile,
                    retimg = FALSE,
                    intern = FALSE,
                    verbose = verbose)
  if (refill) {
    smfile = tempfile()
    fslmaths(maskfile, retimg = FALSE, outfile = smfile,
             opts = "-kernel boxv 7 -fmean",
             verbose = verbose)
    ### smooth and if you add to 0, then > .5 (if 1 before > 1 now, then bin)
    addopt = sprintf(" -add %s -thr %f -bin", smfile, refill.thresh)
    fslmaths(maskfile, retimg = FALSE, outfile = maskfile,
             opts = addopt,
             verbose = verbose)
  }

  if (verbose) {
    message(paste0("# fslfill output: ", runmask, "\n"))
  }

  if (verbose) {
    message("# Using the filled mask to mask original image\n")
  }
  res = fslmask(
    file = orig_img,
    mask = maskfile,
    outfile = outfile,
    retimg = retimg,
    intern = intern,
    reorient = reorient,
    verbose = verbose
  )

  if (!keepmask) {
    if (verbose) {
      message("# Removing Mask File\n")
    }
    maskfile = nii.stub(maskfile)
    maskfile = paste0(maskfile, ext)
    file.remove(maskfile)
  }
  return(res)
}
neuroconductor/ichseg documentation built on Sept. 29, 2020, 2:31 p.m.