[242173]: / R / CT_Skull_Strip.R

Download this file

309 lines (284 with data), 9.3 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#' @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)
}