forked from JeffFessler/mirt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrebin_helix.m
227 lines (185 loc) · 5.95 KB
/
rebin_helix.m
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
function [sino orbits used] = rebin_helix(cg, ig, proj, varargin)
%function [sino orbits used] = rebin_helix(cg, ig, proj, varargin)
%|
%| A single-slice rebinning (SSRB) method for cone-beam tomography data
%| collected with a helical source trajectory.
%|
%| in
%| cg ct_geom()
%| ig image_geom()
%| proj [ns nt na] cone-beam projection views (line integrals)
%|
%| option
%| 'type' '' 'linear' (default) or 'nearest' (too crude)
%| 'short' 1|0 1 for short-scan fan beam (default); 0 for 360
%| 'collapse' 1|0 collapse each view to one row first
%| 'chat' 1|0 verbosity
%|
%| out
%| sino [ns na_rebin nz] fan-beam sinograms
%| orbits [nz 2] [orbit orbit_start] for each slice (degrees)
%| used [nt na] show which views were used
%|
%| See rebin_helix_example, for information on how to call
%|
%| Equations used are taken from
%| Noo F, Defrise M, Clackdoyle R and Kudo H; Phys. Med. Biol. 44:561-70, 1999
%| "Single-slice rebinning for helical cone-beam CT"
%| @u doi 10.1088/0031-9155/44/2/019
%|
%| Copyright 2010-07-21, Gregory Handy and Jeff Fessler, University of Michigan
if nargin == 1 && streq(cg, 'test')
run_mfile_local helix_example
return
end
if nargin < 3, ir_usage, end
arg.type = 'linear';
arg.short = true;
arg.chat = false;
arg.collapse = false;
arg.use_mex = []; % see below
arg = vararg_pair(arg, varargin);
if isempty(arg.use_mex)
if arg.short
arg.use_mex = exist('helix_rebin_mex') == 3; % use mex if available
else
arg.use_mex = 0;
end
end
if arg.use_mex && ~arg.short
warn 'mex only for short scan; reverting to matlab'
arg.use_mex = 0;
end
if arg.collapse % collapse each view to a single row by averaging
proj = mean(proj, 2); % [ns 1 na]
cg.dt = cg.dt * cg.nt;
if cg.offset_t, fail 'offset_t needs adjusted for collapse', end
cg.nt = 1;
end
if arg.use_mex
nthread = 1; % for now only 1 thread supported
i = @(x) int32(x);
s = @(x) single(x);
[sino orbits] = helix_rebin_mex('helix,rebin,ssrb', ...
i(ig.nz), s(ig.dz), s(ig.offset_z), ...
s(cg.dso), s(cg.dsd), s(cg.dfs), ...
s([cg.ds cg.dt]), s([cg.offset_s cg.offset_t]), ...
s(cg.pitch), s(cg.source_z0), ...
s(cg.orbit), s(cg.orbit_start), ...
s(proj), i(nthread));
used = [];
else
[sino orbits used] = rebin_helix_do(proj, ig.mask_or, ...
ig.z, ig.dz, ig.dx, ig.nx, ig.ny, ig.nz, ...
cg.na, cg.ns, cg.ds, cg.s, ...
cg.nt, cg.dt, ... % cg.t, ...
cg.dsd, cg.dso, cg.dod, cg.dfs, cg.offset_s, cg.wt, ...
cg.orbit, cg.orbit_start, cg.pitch, cg.rmax, ...
cg.source_zs, cg.gamma, ...
arg.type, arg.short, arg.chat);
end
end % rebin_helix()
% rebin_helix_do()
function [sino orbits used] = rebin_helix_do(proj, mask2, ...
zslice, dz, dx, nx, ny, nz, ...
na, ns, ds, ss, ...
nt, dt, ... % tpoints, ...
dsd, dso, dod, dfs, offset_s, wt, ...
orbit, orbit_start, pitch, rmax, ...
source_zs, gamma, ...
type, short, chat)
if dfs ~= 0 && dfs ~= inf
fail 'only arc and flat done'
end
na1 = na/orbit*360; % # of views in one turn - should be integer!
if abs(round(na1) - na1) > 1e-4
fail 'bad na/orbit'
end
orbits = zeros(nz, 2);
if short
orbit_short_ideal = 180 + 2 * rad2deg(max(abs(gamma)));
na1 = 2 * ceil(orbit_short_ideal / (orbit / na) / 2); % make even
orbit_short = na1 * (orbit / na); % actual
na1_half = na1 / 2; % because even
orbits(:,1) = orbit_short;
else
orbits(:,1) = 360;
na1_half = ceil(na1 / 2);
end
sino = zeros(ns, na1, nz);
used = zeros(nt, na);
%used = zeros(nz, na);
for iz=1:nz % loop over slices (to make one fan-beam sinogram per slice)
ticker(mfilename, iz, nz)
% determine which helix views to use
zmid = zslice(iz); % z coordinate of center of this slice
ia1_middle = imin(abs(zmid - source_zs)); % "1" because matlab indexing
ia1_list = ia1_middle - na1_half + [0:na1-1];
% see if view extrapolation needed
if ia1_list(1) < 1
ia1_list = 1:na1; % resort to first views
elseif ia1_list(end) > na
ia1_list = [1:na1] - na1 + na; % resort to last views
end
orbits(iz,2) = orbit_start + (ia1_list(1)-1) / na * orbit;
for i1=1:na1
ia1 = ia1_list(i1);
zdiff = zmid - source_zs(ia1); % between fan-beam plane and src
% which rows (t coordinate) of helical projection views
% based on point where CB ray hits fan-beam plane
switch dfs
case inf % flat noo:99:ssr (2)
tt = zdiff * (dsd^2 + ss.^2) / (dsd * dso); % [ns]
case 0 % arc
tt = zdiff * (dsd / dso) ./ cos(gamma); % [ns]
otherwise
fail 'bug'
end
itr = tt/dt + wt; % [ns] float
itr = max(itr, 0); % extrapolate using first row
itr = min(itr, nt-1); % extrapolate using last row
switch type
case 'nearest' % nearest neighbor
itn = round(itr); % [ns] nearest neighbor
it1 = 1 + itn; % matlab indexing
it1 = max(it1, 1); % extrapolate using 1st row
it1 = min(it1, nt); % extrapolate using last row
tt = ((it1-1) - wt) * dt; % trick: actual t value!
% todo: scaling may not be quite right if extrapolated!
scale = rebin_helix_scale(dfs, dsd, ss, tt); % [ns]
tmp = [1:ns]' + (it1-1)*ns + (ia1-1)*ns*nt; % [ns]
view = proj(tmp); % [ns]
% view = proj(:, it1, ia1); % [ns]
used(it1, ia1) = 1;
case 'linear'
tt = (itr - wt) * dt; % trick: actual t value!
it0 = floor(itr); % [ns] nearest neighbor
it0 = min(it0, nt-2); % so that it0+1 is ok
frac = itr - it0; % [ns] for linear interpolation
scale = rebin_helix_scale(dfs, dsd, ss, tt); % [ns]
tmp = [1:ns]' + it0*ns + (ia1-1)*ns*nt; % trick
tmp0 = proj(tmp); % [ns]
tmp1 = proj(tmp + ns); % [ns]
% view = (1 - frac) .* tmp0 + frac .* tmp1;
view = tmp0 + frac .* (tmp1 - tmp0);
used([it0+1; it0+2], ia1) = 1;
otherwise
fail 'bad type'
end
sino(:, i1, iz) = scale .* view;
end
% used(iz, ia1_list) = 1;
end
end % rebin_helix_do()
% rebin_helix_scale()
% scaling factor due to different ray lengths
function scale = rebin_helix_scale(dfs, dsd, ss, tt)
switch dfs
case inf % flat noo:99:ssr (1)
scale = sqrt(ss.^2 + tt.^2 + dsd^2) ./ sqrt(tt.^2 + dsd^2);
case 0 % arc
scale = dsd ./ sqrt(tt.^2 + dsd^2);
otherwise
fail 'bad dfs'
end
end % rebin_helix_scale()