1
- # ---------------------------------------------------------
2
- # # [SPECTrecon overview](@id 1-overview)
3
- # ---------------------------------------------------------
1
+ #=
2
+ # [SPECTrecon overview](@id 1-overview)
4
3
5
- # This page gives an overview of the Julia package
6
- # [`SPECTrecon`](https://github.com/JuliaImageRecon/SPECTrecon.jl).
4
+ This page gives an overview of the Julia package
5
+ [`SPECTrecon`](https://github.com/JuliaImageRecon/SPECTrecon.jl).
7
6
8
- # ### Setup
7
+ This page was generated from a single Julia file:
8
+ [1-overview.jl](@__REPO_ROOT_URL__/1-overview.jl).
9
+ =#
10
+
11
+ # md # In any such Julia documentation,
12
+ # md # you can access the source code
13
+ # md # using the "Edit on GitHub" link in the top right.
14
+
15
+ # md # The corresponding notebook can be viewed in
16
+ # md # [nbviewer](https://nbviewer.org/) here:
17
+ # md # [`1-overview.ipynb`](@__NBVIEWER_ROOT_URL__/1-overview.ipynb),
18
+ # md # and opened in [binder](https://mybinder.org/) here:
19
+ # md # [`1-overview.ipynb`](@__BINDER_ROOT_URL__/1-overview.ipynb).
20
+
21
+ # ## Setup
9
22
10
23
# Packages needed here.
11
24
@@ -22,46 +35,48 @@ using InteractiveUtils: versioninfo
22
35
23
36
isinteractive () ? jim (:prompt , true ) : prompt (:draw );
24
37
25
- # ### Overview
26
-
27
- # To perform SPECT image reconstruction,
28
- # one must have a model for the imaging system
29
- # encapsulated in a forward projector and back projector.
30
-
31
- # Mathematically, we write the forward projection process in SPECT
32
- # as "y = A * x" where A is a "system matrix"
33
- # that models the physics of the imaging system
34
- # (including depth-dependent collimator/detector response
35
- # and attenuation)
36
- # and "x" is the current guess of the emission image.
37
-
38
- # However, in code we usually cannot literally store "A"
39
- # as dense matrix because it is too large.
40
- # A typical size in SPECT is that
41
- # the image `x` is
42
- # `nx × ny × nz = 128 × 128 × 100`
43
- # and the array of projection views `y` is
44
- # `nx × nz × nview = 128 × 100 × 120`.
45
- # So the system matrix `A` has `1536000 × 1638400` elements
46
- # which is far to many to store,
47
- # even accounting for some sparsity.
48
-
49
- # Instead, we write functions called forward projectors
50
- # that calculate `A * x` "on the fly".
51
-
52
- # Similarly, the operation `A' * y`
53
- # is called "back projection",
54
- # where `A'` denotes the transpose or "adjoint" of `A`.
55
-
56
-
57
- # ### Example
58
-
59
- # To illustrate forward and back projection,
60
- # it is easiest to start with a simulation example
61
- # using a digital phantom.
62
- # The fancy way would be to use a 3D phantom from
63
- # [ImagePhantoms](https://github.com/JuliaImageRecon/ImagePhantoms.jl),
64
- # but instead we just use two simple cubes.
38
+ #=
39
+ ## Overview
40
+
41
+ To perform SPECT image reconstruction,
42
+ one must have a model for the imaging system
43
+ encapsulated in a forward projector and back projector.
44
+
45
+ Mathematically, we write the forward projection process in SPECT
46
+ as "y = A * x" where A is a "system matrix"
47
+ that models the physics of the imaging system
48
+ (including depth-dependent collimator/detector response
49
+ and attenuation)
50
+ and "x" is the current guess of the emission image.
51
+
52
+ However, in code we usually cannot literally store "A"
53
+ as dense matrix because it is too large.
54
+ A typical size in SPECT is that
55
+ the image `x` is
56
+ `nx × ny × nz = 128 × 128 × 100`
57
+ and the array of projection views `y` is
58
+ `nx × nz × nview = 128 × 100 × 120`.
59
+ So the system matrix `A` has `1536000 × 1638400` elements
60
+ which is far to many to store,
61
+ even accounting for some sparsity.
62
+
63
+ Instead, we write functions called forward projectors
64
+ that calculate `A * x` "on the fly".
65
+
66
+ Similarly, the operation `A' * y`
67
+ is called "back projection",
68
+ where `A'` denotes the transpose or "adjoint" of `A`.
69
+
70
+
71
+ ## Example
72
+
73
+ To illustrate forward and back projection,
74
+ it is easiest to start with a simulation example
75
+ using a digital phantom.
76
+ The fancy way would be to use a 3D phantom from
77
+ [ImagePhantoms](https://github.com/JuliaImageRecon/ImagePhantoms.jl),
78
+ but instead we just use two simple cubes.
79
+ =#
65
80
66
81
nx,ny,nz = 128 ,128 ,80
67
82
T = Float32
80
95
jim (mid3 (xtrue), " Middle slices of x" )
81
96
82
97
83
- # ### PSF
98
+ # ## PSF
84
99
85
100
# Create a synthetic depth-dependent PSF for a single view
86
101
px = 11
87
102
psf1 = psf_gauss ( ; ny, px, fwhm_end = 6 )
88
103
jim (psf1, " PSF for each of $ny planes" )
89
104
90
105
91
- # In general the PSF can vary from view to view
92
- # due to non-circular detector orbits.
93
- # For simplicity, here we illustrate the case
94
- # where the PSF is the same for every view.
106
+ #=
107
+ In general the PSF can vary from view to view
108
+ due to non-circular detector orbits.
109
+ For simplicity, here we illustrate the case
110
+ where the PSF is the same for every view.
111
+ =#
95
112
96
113
nview = 60
97
114
psfs = repeat (psf1, 1 , 1 , 1 , nview)
@@ -103,11 +120,13 @@ size(psfs)
103
120
plan = plan_psf ( ; nx, nz, px)
104
121
105
122
106
- # ### Basic SPECT forward projection
123
+ #=
124
+ ## Basic SPECT forward projection
107
125
108
- # Here is a simple illustration
109
- # of a SPECT forward projection operation.
110
- # (This is a memory inefficient way to do it!)
126
+ Here is a simple illustration
127
+ of a SPECT forward projection operation.
128
+ (This is a memory inefficient way to do it!)
129
+ =#
111
130
112
131
dy = 4 # transaxial pixel size in mm
113
132
mumap = zeros (T, size (xtrue)) # μ-map just zero for illustration here
@@ -120,14 +139,16 @@ size(views)
120
139
jim (views[:,:,1 : 4 : end ], " Every 4th of $nview projection views" )
121
140
122
141
123
- # ### Basic SPECT back projection
142
+ #=
143
+ ## Basic SPECT back projection
124
144
125
- # This illustrates an "unfiltered backprojection"
126
- # that leads to a very blurry image
127
- # (again, with a simple memory inefficient usage).
145
+ This illustrates an "unfiltered backprojection"
146
+ that leads to a very blurry image
147
+ (again, with a simple memory inefficient usage).
128
148
129
- # First, back-project two "rays"
130
- # to illustrate the depth-dependent PSF.
149
+ First, back-project two "rays"
150
+ to illustrate the depth-dependent PSF.
151
+ =#
131
152
tmp = zeros (T, size (views))
132
153
tmp[nx÷ 2 , nz÷ 2 , nview÷ 5 ] = 1
133
154
tmp[nx÷ 2 , nz÷ 2 , 1 ] = 1
@@ -141,18 +162,20 @@ back = backproject(views, mumap, psfs, dy)
141
162
jim (mid3 (back), " Back-projection of ytrue" )
142
163
143
164
144
- # ### Memory efficiency
165
+ #=
166
+ ## Memory efficiency
145
167
146
- # For iterative reconstruction,
147
- # one must do forward and back-projection repeatedly.
148
- # It is more efficient to pre-allocate work arrays
149
- # for those operations,
150
- # instead of repeatedly making system calls.
168
+ For iterative reconstruction,
169
+ one must do forward and back-projection repeatedly.
170
+ It is more efficient to pre-allocate work arrays
171
+ for those operations,
172
+ instead of repeatedly making system calls.
151
173
152
- # Here we illustrate the memory efficient versions
153
- # that are recommended for iterative SPECT reconstruction.
174
+ Here we illustrate the memory efficient versions
175
+ that are recommended for iterative SPECT reconstruction.
154
176
155
- # First construction the SPECT plan.
177
+ First construction the SPECT plan.
178
+ =#
156
179
157
180
# viewangle = (0:(nview-1)) * 2π # default
158
181
plan = SPECTplan (mumap, psfs, dy; T)
@@ -168,16 +191,18 @@ project!(tmp, xtrue, plan)
168
191
169
192
tmp = Array {T} (undef, nx, ny, nz)
170
193
backproject! (tmp, views, plan)
171
- @assert tmp == back
194
+ @assert tmp ≈ back
172
195
173
196
174
- # ### Using `LinearMapAA`
197
+ #=
198
+ ## Using `LinearMapAA`
175
199
176
- # Calling `project!` and `backproject!` repeatedly
177
- # leads to application-specific code.
178
- # More general code uses the fact that SPECT projection and back-projection
179
- # are linear operations,
180
- # so we use `LinearMapAA` to define a "system matrix" for these operations.
200
+ Calling `project!` and `backproject!` repeatedly
201
+ leads to application-specific code.
202
+ More general code uses the fact that SPECT projection and back-projection
203
+ are linear operations,
204
+ so we use `LinearMapAA` to define a "system matrix" for these operations.
205
+ =#
181
206
182
207
forw! = (y,x) -> project! (y, x, plan)
183
208
back! = (x,y) -> backproject! (x, y, plan)
@@ -187,27 +212,30 @@ A = LinearMapAA(forw!, back!, (prod(odim),prod(idim)); T, odim, idim)
187
212
188
213
# Simple forward and back-projection:
189
214
@assert A * xtrue == views
190
- @assert A' * views == back
215
+ @assert A' * views ≈ back
191
216
192
217
# Mutating version:
193
218
tmp = Array {T} (undef, nx, nz, nview)
194
219
mul! (tmp, A, xtrue)
195
220
@assert tmp == views
196
221
tmp = Array {T} (undef, nx, ny, nz)
197
222
mul! (tmp, A' , views)
198
- @assert tmp == back
223
+ @assert tmp ≈ back
224
+
199
225
226
+ #=
227
+ ## Units
200
228
201
- # ### Units
229
+ The pixel dimensions `deltas` can (and should!) be values with units.
202
230
203
- # The pixel dimensions `deltas` can (and should!) be values with units.
231
+ Here is an example ... (todo)
232
+ =#
204
233
205
- # Here is an example ... (todo)
206
234
# using UnitfulRecipes
207
235
# using Unitful: mm
208
236
209
237
210
- # ### Projection view animation
238
+ # ## Projection view animation
211
239
212
240
anim = @animate for i in 1 : nview
213
241
ymax = maximum (views)
219
247
gif (anim, " views.gif" , fps = 8 )
220
248
221
249
222
- # ### Reproducibility
250
+ # ## Reproducibility
223
251
224
252
# This page was generated with the following version of Julia:
225
253
0 commit comments