|
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 |
|