-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.jl
285 lines (181 loc) · 7.81 KB
/
utils.jl
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
## Plot the Euclidean error between the two trajectories
function plotTrajectoryError(x, y)
figure("Pointwise Trajectory Error")
plot(sqrt.(abs2.(y[1, :]) .+ abs2.(y[2, :])) - sqrt.(abs2.(x[1, :]) + abs2.(x[2, :])))
xlabel("Sample Index")
ylabel("Euclidean Distance between Nominal and Actual Positions")
end
## Show reconstructed image magnitude and phase including normalization if specified
function showReconstructedImage(figID, x, sh, do_normalization)
fig = figure("$figID: Reconstruction", figsize = (10, 4))
x_max = maximum(abs.(x))
reshapedMag = reshape(abs.(x), sh[1], sh[2])
reshapedAngle = reshape(angle.(x), sh[1], sh[2])
## Normalize step:
if do_normalization
reshapedMag = reshapedMag ./ x_max
x_max = 1.0
end
subplot(121)
title("Magnitude")
imshow(reshapedMag, vmin = 0, vmax = x_max, cmap = "gray")
colorbar()
subplot(122)
title("Phase")
imshow(ROMEO.unwrap(reshapedAngle, dims=(1,2)), vmin = -pi, vmax = pi, cmap = "seismic")
colorbar()
end
## Function for plotting the voxel-wise errors between two Complex-valued images x and y of a given shape sh
function plotError(figID, x, y, sh)
fig = figure("Voxel-wise Reconstruction Errors + $figID", figsize = (10, 4))
absErrorTerm = Flux.Losses.mae.(abs.(x), abs.(y)) ./ abs.(x)
angleErrorTerm = Flux.Losses.mae.(angle.(x), angle.(y))
reshapedMag = reshape(absErrorTerm, sh[1], sh[2])
reshapedAngle = reshape(angleErrorTerm, sh[1], sh[2])
subplot(121)
title("Magnitude Error")
imshow(reshapedMag, vmin = 0, vmax = 1.0, cmap = "jet")
colorbar()
subplot(122)
title("Phase Error")
imshow(reshapedAngle, vmin = -pi, vmax = pi, cmap = "Spectral")
colorbar()
end
## Version of Matrix-Vector Multiplication using Tullio.jl. Supposedly very fast and flexible.
function weighted_EMulx_Tullio_Sep(x_re, x_im, nodes, positions, weights)
# Separation of real and imaginary parts to play well with GPU
@tullio RE_E[k, n] := cos <| (-Float32(pi) * 2 * nodes[i, k] * $positions[i, n])
@tullio IM_E[k, n] := sin <| (-Float32(pi) * 2 * nodes[i, k] * $positions[i, n])
@tullio y_re[k] := (weights[k] * RE_E[k, n]) * x_re[n] - (weights[k] * IM_E[k, n]) * x_im[n]
@tullio y_im[k] := (weights[k] * IM_E[k, n]) * x_re[n] + (weights[k] * RE_E[k, n]) * x_im[n]
# w_re = weights .* y_re
# w_im = weights .* y_im
return (y_re, y_im)
end
## Version of Matrix-Vector Multiplication using Tullio.jl. Supposedly very fast and flexible.
function weighted_EMulx_Tullio_Sep(x_re, x_im, nodes, positions, weights, b0_map, times)
# Separation of real and imaginary parts to play well with GPU
@tullio RE_E[k, n] := cos <| (-Float32(pi) * 2 * nodes[i, k] * $positions[i, n] - times[k]*b0_map[n])
@tullio IM_E[k, n] := sin <| (-Float32(pi) * 2 * nodes[i, k] * $positions[i, n] - times[k]*b0_map[n])
@tullio y_re[k] := (weights[k] * RE_E[k, n]) * x_re[n] - (weights[k] * IM_E[k, n]) * x_im[n]
@tullio y_im[k] := (weights[k] * IM_E[k, n]) * x_re[n] + (weights[k] * RE_E[k, n]) * x_im[n]
# w_re = weights .* y_re
# w_im = weights .* y_im
return (y_re, y_im)
end
function get_weights(gradients)
@tullio W[k] := sqrt <| $gradients[i, k] * $gradients[i, k] ## Define weights as magnitude of gradients
W = W ./ maximum(W)
return W
end
## Weighted Version of Matrix-Vector Multiplication using Tullio.jl with real matrices and CUDA compat...
function weighted_EHMulx_Tullio_Sep(x_re, x_im, nodes, positions, weights)
# Separation of real and imaginary parts to play well with GPU
@tullio RE_E[n, k] := cos <| (Float32(pi) * 2 * $positions[i, n] * nodes[i, k])
@tullio IM_E[n, k] := sin <| (Float32(pi) * 2 * $positions[i, n] * nodes[i, k])
@tullio y_re[n] := RE_E[n, k] * ($weights[k]*x_re[k]) - IM_E[n, k] * ($weights[k]*x_im[k])
@tullio y_im[n] := IM_E[n, k] * ($weights[k]*x_re[k]) + RE_E[n, k] * ($weights[k]*x_im[k])
return (y_re, y_im)
end
## Weighted Version of Matrix-Vector Multiplication using Tullio.jl with real matrices and CUDA compat...
function weighted_EHMulx_Tullio_Sep(x_re, x_im, nodes, positions, weights, b0_map, times)
# Separation of real and imaginary parts to play well with GPU
@tullio RE_E[n, k] := cos <| (Float32(pi) * 2 * $positions[i, n] * nodes[i, k] + b0_map[n]*times[k])
@tullio IM_E[n, k] := sin <| (Float32(pi) * 2 * $positions[i, n] * nodes[i, k] + b0_map[n]*times[k])
@tullio y_re[n] := RE_E[n, k] * ($weights[k]*x_re[k]) - IM_E[n, k] * ($weights[k]*x_im[k])
@tullio y_im[n] := IM_E[n, k] * ($weights[k]*x_re[k]) + RE_E[n, k] * ($weights[k]*x_im[k])
return (y_re, y_im)
end
## Get gradients from the trajectory
function nodes_to_gradients(nodes)
newNodes = hcat(CuArray([0; 0]), nodes)
gradients = diff(newNodes, dims = 2)
return gradients
end
## Pad gradients to prepare for Tullio
function pad_gradients(gradients, kernelSize)
padding = CuArray(zeros(Float32, kernelSize[1], kernelSize[2] - 1))
padded = hcat(padding, gradients)
return padded
end
## Filter gradients using Tullio for efficient convolution
function filter_gradients(gradients, kernel)
@tullio d[b, i] := $gradients[b, i+a-1] * kernel[b, a]
return d
end
## Convert gradients to trajectory nodes
function gradients_to_nodes(gradients)
nodes = cumsum(gradients, dims = 2)
return nodes
end
## Efficient function to apply a time domain gradient impulse response function kernel to the trajectory (2D only now)
function apply_td_girf(nodes, kernel)
gradients = nodes_to_gradients(nodes)
padded = pad_gradients(gradients, size(kernel))
filtered = filter_gradients(padded, kernel)
filtered_nodes = gradients_to_nodes(filtered)
return filtered_nodes
end
## Get the padded gradient waveform
function get_padded_gradients(nodes, kernelSize)
g = nodes_to_gradients(nodes)
padded = pad_gradients(g, kernelSize)
return padded
end
## Convert Vector of Vectors to Matrix
function vecvec_to_matrix(vecvec)
dim1 = length(vecvec)
dim2 = length(vecvec[1])
my_array = zeros(Float32, dim1, dim2)
for i = 1:dim1
for j = 1:dim2
my_array[i, j] = vecvec[i][j]
end
end
return my_array
end
## Get the positions corresponding to the strong voxel condition for a given image Shape
function getPositions(sh::Tuple)
# set up positions according to strong voxel condition
x = collect(1:sh[1]) .- sh[1] / 2 .- 1
y = collect(1:sh[2]) .- sh[2] / 2 .- 1
p = Iterators.product(x, y)
positions = collect(Float64.(vecvec_to_matrix(vec(collect.(p))))')
return positions
end
## Helper function for reshaping nodes to size expected by Flux dataloader
function reshapeNodes(x)
s = size(x)
reshape(x, 1, s[2], s[1], 1)
end
## Helper function for undoing the reshaping of nodes to size expected by Flux dataloader
function undoReshape(x)
r = size(x)
reshape(x, r[3], r[2])
end
## Loss function for the minimization -> works over the real and imaginary parts of the image separately
function loss(x, y)
Flux.Losses.mae(x[1],y[1]) + Flux.Losses.mae(x[2],y[2])
end
## Generates ground truth gaussian kernel
# TODO: Add support for variable width
function getGaussianKernel(kernel_length)
## Generate Ground Truth Filtering Kernel
ker = rand(2, kernel_length)
ker[1, :] = exp.(.-(-kernel_length÷2:kernel_length÷2) .^ 2 ./ (50))
ker[2, :] = exp.(.-(-kernel_length÷2:kernel_length÷2) .^ 2 ./ (25))
ker = ker ./ sum(ker, dims = 2)
end
## Generates delay kernel
function deltaKernel(kernel_length, shift)
x = zeros(2,kernel_length)
x[:,kernel_length - shift] .= 1.0
return x
end
function pull_from_gpu(imTuple::Tuple)
cpuTuple = imTuple |> cpu
return complex.(cpuTuple...)
end
function pull_from_gpu(array::CuMatrix)
array |> cpu
end