a b/R/amoment.R
1
2
#' @title Calculate Moments of Neighborhood
3
#'
4
#' @description This function calculates Local Moments (mean, standard deviation, skew) for an array.
5
#' @param image input image
6
#' @param window window (in width) for the neighborhood
7
#' @param nvoxels window (in voxels) for the neighborhood 1 results in a 3x3 cube
8
#' @param moment vector moments taken (1- mean, 2-sd, 3-skew)
9
#' @param mask array or object of class nifti of same size as image
10
#' @param only.mask Should objects outside the mask (i.e. zeros) be
11
#' counted the moment?  Default is FALSE so edges are weighted to 0
12
#' @param center vector of indicator of central moment.
13
#' if TRUE mean image is subtracted.  Same length as moment
14
#' @param invert Standardize the values by the power: 1/moment
15
#' @param mean_image mean image to be subtracted. If not supplied, and central = TRUE, local_moment_edge is run with mom = 1
16
#' @param na.rm remove NAs from the moment image calculation
17
#' @param remask set areas outside of mask to 0
18
#' @param ... Arguments passed to \code{\link{get_neighbors}}
19
#' @importFrom magic ashift
20
#' @importFrom neurobase niftiarr datatyper
21
#' @export
22
#' @return List of arrays same lenght as moment
23
#' @examples
24
#' x = array(rnorm(1000), dim=c(10, 10, 10))
25
#' mask = abs(x) < 1
26
#' mean.x = local_moment(x, nvoxels=1, moment = 1, mask=mask,
27
#' center = FALSE,
28
#' remask = FALSE)[[1]]
29
#' var.x = local_moment(x, nvoxels=1, moment = 2, mask=mask, center = TRUE,
30
#' mean_image = mean.x, remask=FALSE)[[1]]
31
#'
32
#' ### check that x[2,2,2] mean is correct
33
#' check = x[1:3,1:3,1:3]
34
#' ## masking
35
#' vals = check[abs(check) < 1]
36
#' m = mean(vals)
37
#' all.equal(m, mean.x[2,2,2])
38
#' n = length(vals)
39
#' v = var(vals) * (n-1)/n
40
#' var.x[2,2,2]
41
#' all.equal(v, var.x[2,2,2])
42
#'
43
local_moment <- function(
44
  image,
45
  window = NULL,
46
  nvoxels=NULL,
47
  moment,
48
  mask = NULL,
49
  only.mask = FALSE,
50
  center=is.null(mean_image),
51
  invert = FALSE,
52
  # the mean
53
  mean_image=NULL, # mean image to be subtracted.  If not supplied
54
  # local_moment_edge is run with mom = 1
55
  na.rm=TRUE, # remove NAs from the image (mask set to 0),
56
  remask = TRUE,  # set areas outside of mask to 0
57
  ...
58
  ) {
59
60
  is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) {
61
    abs(x - round(x)) < tol
62
  }
63
64
  ## if mask is null - whole image
65
  dimg = dim(image)[1:3]
66
  if (is.null(mask)) {
67
    mask = array(1, dim=dimg)
68
  }
69
70
  lmom = length(moment)
71
  lcent = length(center)
72
  stopifnot(lmom == lcent)
73
74
  neigh = get_neighbors(image, window = window, nvoxels=nvoxels,
75
                mask = mask, ...)
76
77
  neighbors = neigh$neighbors
78
  indices = neigh$indices
79
80
  mn = rowMeans(neighbors, na.rm=na.rm)
81
  img_list = vector("list", length = lmom)
82
  for (imom in seq(lmom)){
83
    cent = center[imom]
84
    mom = moment[imom]
85
    moment_image = neighbors
86
    if (isTRUE(cent)){
87
      if (mom != 1) moment_image = moment_image - mn
88
    }
89
90
    moment_image = moment_image^mom
91
    moment_image = rowMeans(moment_image, na.rm=na.rm)
92
93
    realpow <- function(x, pow) {
94
      sgn = sign(x)
95
      x = abs(x)
96
      x = x^{pow}
97
      x = sgn * x
98
    }
99
    ### put on same scale
100
    if (invert) {
101
      moment_image = realpow(moment_image, pow = 1/mom)
102
    }
103
104
    moment_image = array(moment_image, dim=dimg)
105
    if (remask) {
106
      moment_image = moment_image * mask
107
    }
108
    if ( inherits(image, "nifti") ){
109
      moment_image = niftiarr(image, moment_image)
110
      moment_image = datatyper(moment_image,
111
                              datatype= convert.datatype()$FLOAT32,
112
                              bitpix= convert.bitpix()$FLOAT32)
113
    }
114
    img_list[[imom]] = moment_image
115
  }
116
117
  # moment <- array(moment, dim = dim(image))
118
  return(img_list)
119
}
120
121
122
#' @title Calculate Moments of Neighborhood
123
#'
124
#' @description This function calculates Local Moments (mean, standard deviation, skew) for an array.
125
#' @param image input image
126
#' @param window window (in width) for the neighborhood
127
#' @param nvoxels window (in voxels) for the neighborhood 1 results in a 3x3 cube
128
#' @param mask array or object of class nifti of same size as image
129
#' @param rm.value remove the voxel itself before taking the moment
130
#' @param check.wrap Logical - check wrapround and put \code{rep.value} in
131
#' for the wrapped values
132
#' @param rep.value Replace wrapped values (edge of image) to this value
133
#' @param ... Not used
134
#' @importFrom magic ashift
135
#' @export
136
#' @return Array with same dimensions as image
137
#' @examples
138
#' x = array(rnorm(1000), dim=c(10, 10, 10))
139
#' neigh = get_neighbors(x, nvoxels = 1)
140
get_neighbors <- function(
141
  image,
142
  window = NULL,
143
  nvoxels=NULL,
144
  mask = NULL,
145
  rm.value = FALSE, # remove the voxel itself before returning
146
  check.wrap = TRUE,
147
  rep.value = 0,
148
  ...
149
) {
150
151
  is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) {
152
    abs(x - round(x)) < tol
153
  }
154
155
  ## if mask is null - whole image
156
  dimg = dim(image)[1:3]
157
  if (is.null(mask)) {
158
    mask = array(1, dim=dimg)
159
  }
160
161
  mypermutations = function(winds){
162
    windlist = lapply(1:3, function(x) winds)
163
    eg = expand.grid(windlist)
164
    eg = eg[ order(eg$Var1, eg$Var2, eg$Var3), ]
165
    eg = as.matrix(eg)
166
    rownames(eg) = NULL
167
    colnames(eg) = NULL
168
    eg
169
  }
170
171
  ### initalize array
172
  ### different ways of parameterizing the "window"
173
  if (is.null(nvoxels)) {
174
    stopifnot(!is.null(window))
175
    stopifnot(length(window) == 1)
176
    if ((window %% 2) != 1) {
177
      stop("window must be odd number")
178
    }
179
    winds = (-window/2 + .5):(window/2 - .5)
180
#     indices <- gtools::permutations(window, 3,
181
#                             v= (-window/2 + .5):(window/2 - .5),
182
#                             repeats.allowed=TRUE)
183
  }  else {
184
    stopifnot(is.wholenumber(nvoxels))
185
    stopifnot(is.null(window))
186
187
    winds <- (-nvoxels):nvoxels
188
#     indices <- gtools::permutations(length(winds), 3, v = winds,
189
#                             repeats.allowed=TRUE)
190
  }
191
  indices = mypermutations(winds)
192
193
  if (rm.value){
194
    allzero = apply(indices == 0, 1, all)
195
    indices = indices[!allzero,]
196
  }
197
198
  image = image * mask
199
200
  nruns <- nrow(indices)
201
  mat = matrix(NA, nrow=prod(dimg), ncol=nruns)
202
  for ( i in 1:nruns){
203
    shifter <- ashift(image, indices[i,])
204
    mat[, i] = shifter
205
  }
206
207
  if (check.wrap){
208
    tall.ind = t(as.matrix(expand.grid(dim1=seq(dimg[1]),
209
                                      dim2=seq(dimg[2]),
210
                                      dim3=seq(dimg[3]))))
211
    dimg.mat = matrix(dimg, ncol=prod(dimg), nrow = 3)
212
    for ( i in 1:nruns){
213
      all.ind2 = tall.ind + indices[i,]
214
      outside = {all.ind2 < 1} + all.ind2 > dimg.mat
215
      any.outside = colSums(outside) > 0
216
      mat[any.outside,i] = rep.value
217
    }
218
  }
219
220
  return(list(neighbors=mat, indices = indices))
221
}
222
223
224
#' @title Calculate Mean Image by FFT
225
#'
226
#' @description This function calculates the mean image using fft
227
#' @param x 3D array
228
#' @param nvoxels window (in voxels) for the neighborhood
229
#' (1 results in a 3x3 cube)
230
#' @param shift Should results be shifted back?
231
#' @param verbose Diagnostic outputing
232
#' @importFrom magic ashift
233
#' @importFrom stats fft
234
#' @export
235
#' @return Array of size of x
236
mean_image = function(x, nvoxels, shift = TRUE, verbose = TRUE){
237
238
  is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) {
239
    abs(x - round(x)) < tol
240
  }
241
  stopifnot(length(nvoxels) %in% c(1, 3))
242
243
  if (length(nvoxels) == 1){
244
    stopifnot(is.wholenumber(nvoxels))
245
    n <- length((-nvoxels):nvoxels)
246
247
    h = array(1, dim=c(n, n, n))
248
    h = h / length(h)
249
  }
250
  if (length(nvoxels) == 3){
251
    dims = (nvoxels*2) + 1
252
    stopifnot(is.wholenumber(dims))
253
    h = array(1, dim=dims)
254
    h = h / length(h)
255
  }
256
  if (verbose){
257
    cat(paste0("Dimension of kernel is ", paste(dim(h), collapse = "x"), "\n"))
258
  }
259
260
  # % x - 3dim matrix
261
  # % h - smoothing kernel - 3dim matrix size(h) =< size(x)
262
263
  x[is.na(x)]=0;
264
265
  dx = dim(x)
266
  RX = dx[1];
267
  CX = dx[2];
268
  SX = dx[3];
269
270
  dh = dim(h)
271
  RH = dh[1];
272
  CH = dh[2];
273
  SH = dh[3];
274
275
  z = array(0, dim = dx);
276
  z[1:RH,1:CH,1:SH] = h;
277
278
  if (verbose){
279
    cat("Creating Kernel fft\n")
280
  }
281
  H = fft(z);
282
  rm(z)
283
  if (verbose){
284
    cat("Creating Image fft\n")
285
  }
286
  X = fft(x);
287
  rm(x)
288
  if (verbose){
289
    cat("Convolution\n")
290
  }
291
  y = H * X
292
  rm(H)
293
  rm(X)
294
  y = fft(y, inverse = TRUE)
295
  y = y / length(y)
296
  y = Re(y)
297
298
#   print(ceiling(RH/2))
299
#   print(ceiling(CH/2))
300
#   print(ceiling(SH/2))
301
  #   [p+1:m 1:p]
302
  if (verbose){
303
    cat("Shifting\n")
304
  }
305
  if (shift) {
306
    y = ashift(y, v = -(ceiling(dim(h)/2)-1))
307
  }
308
309
  #   y=circshift(y,c(-ceiling(RH/2), -ceiling(CH/2), -ceiling(SH/2)))
310
  # compensation for group delay
311
  return(y)
312
}
313