forked from JeffFessler/mirt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfbp_fan_flat.m
141 lines (122 loc) · 4.49 KB
/
fbp_fan_flat.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
function img = fbp_fan_flat(sino, G, window, varargin)
%function img = fbp_fan_flat(sino, G, window, varargin)
%|
%| FBP reconstruction of fan-beam tomography data for a flat detector.
%| (Equidistant case.) Extracts parameters from Gtomo2_dsc object.
%| See fbp_fan_flat_example.m for example.
%|
%| in:
%| sino [nb,na] sinogram (line integrals)
%| G [nd,np] Gtomo2_dscmex object
%| window [npad] 'ramp', or 'hann', or array (default: 'ramp')
%| if array, then use samples [-K/2, K/2)
%| varargin options
%| 'ia_skip' [int] downsample in angle to save time for quick tests
%| out:
%| img [nx,ny] reconstructed image
%|
%| source_offset has the same units (e.g., mm) as pixel_size etc.
%| channel_offset is an integer or fraction thereof, e.g., 0.25.
%| gamma = dgamma * (ib - (nb-1)/2 - channel_offset)
%| channel offset is relative to centerline between two central channels.
%|
%| References:
%| Gullberg et al, T-MI, Mar. 1986 (gullberg:86:raf).
%| Kak & Slaney, p. 77. Fessler tomography chapter.
%|
%| Copyright 2004-5 by Patty Laskowsky, Nicole Caparanis, Taka Masuda
%| and Jeff Fessler, The University of Michigan
if nargin == 1 && streq(sino, 'test'), run_mfile_local('fbp_fan_flat_example'), return, end
if nargin < 2, ir_usage, end
if ~isvar('window') || isempty(window)
window = 'ramp';
end
ia_skip = 1;
while(length(varargin))
arg = varargin{1};
if streq(arg, 'ia_skip')
ia_skip = varargin{2};
varargin = {varargin{3:end}};
else
error 'unknown optional argument'
end
end
% extract arguments from system 14 Gtomo2_dsc object
%nx = G.nx;
%ny = G.ny;
[nx ny] = size(G.arg.mask);
nb = G.arg.nb;
na = G.arg.na;
arg_num = @(G,arg) str2num(arg_get(G.arg.args, arg));
arg_num_def = @(G,arg,def) str2num(arg_get(G.arg.args, arg, def));
orbit = arg_num(G, 'orbit') * pi / 180; % to radians
orbit_start = arg_num(G, 'orbit_start') * pi / 180; % to radians
pixel_size = arg_num(G, 'pixel_size');
ds = arg_num(G, 'ray_spacing'); % sample spacing [distance]
%strip_width = arg_num(G, 'strip_width');
Dsd = arg_num(G, 'src_det_dis'); % dis_src_det
Dod = arg_num(G, 'obj2det_x'); % dis_iso_det
obj2det_y = arg_num(G, 'obj2det_y');
Dfs = arg_num_def(G, 'dis_foc_src', Inf); % Inf for flat det.
center_x = arg_num_def(G, 'center_x', '0'); % center offset in pixel
center_y = arg_num_def(G, 'center_y', '0');
source_offset = arg_num_def(G, 'source_offset', 0); % r_off
channel_offset = arg_num_def(G, 'channel_offset', 0);
flip_y = arg_num_def(G, 'flip_y', '1');
if obj2det_y ~= Dod, error 'only circular orbit implemented', end
%
% fan beam FBP step 1: weight the sinogram
%
nn = [-(nb-1)/2:(nb-1)/2]' - channel_offset;
Dso = Dsd - Dod; % src to "isocenter" distance
ss = ds * nn;
gam = atan(ss / Dsd);
w1 = abs(Dso * cos(gam) - source_offset * sin(gam)) / Dsd; % 1D weighting
sino = sino .* w1(:,ones(1,na));
clear nn gam w1 ss
%
% fan beam FBP step 2: filter the (zero padded) sinogram
% trick: extra zero column saves linear interpolation indexing within loop!
%
sino = fbp2_sino_filter('flat', sino, 'ds', ds, 'extra', 1, 'window', window);
%
% fan beam FBP step 3: backproject the filtered sinogram
%
% precompute as much as possible
wx = (nx+1)/2 - center_x;
wy = (ny+1)/2 - center_y;
[xc yc] = ndgrid(([1:nx]-wx) * pixel_size, -flip_y * ([1:ny]-wy) * pixel_size);
rr = sqrt(xc.^2 + yc.^2); % [nx,ny]
smax = ((nb-1)/2-abs(channel_offset)) * ds; %maximum flat panel detector length
gamma_max = atan(smax / Dsd);
rmax = Dso * sin(gamma_max);
mask = G.mask & (rr < rmax);
xc = xc(mask(:)); % [np] pixels within mask
yc = yc(mask(:));
clear wx wy rr smax rmax
ia = [0:na-1]';
betas = orbit_start + orbit * ia / na; % [na]
img = 0;
% loop over each projection angle
for ia=1:ia_skip:na
ticker(mfilename, ia, na)
beta = betas(ia);
d_loop = Dso + xc * sin(beta) - yc * cos(beta); % Dso - y_beta
x_beta = xc * cos(beta) + yc * sin(beta);
w2 = Dsd^2 ./ d_loop.^2; % [np] back projection weighting
sprime_ds = (Dsd / ds) * (x_beta - source_offset) ./ d_loop; % s' / ds
bb = sprime_ds + ((nb+1)/2 + channel_offset); % [np] bin "index"
% nearest neighbor interpolation:
% ib = round(bb);
% if any(ib < 1 | ib > nb), error 'bug', end
% % trick: make out-of-sinogram indices point to those extra zeros
% ib(ib < 1 | ib > nb) = nb+1;
% img = img + sino(ib, ia) ./ L2;
% linear interpolation:
il = floor(bb); % left bin
% if any(il < 1 | il >= nb), error 'bug', end
wr = bb - il; % left weight
wl = 1 - wr; % right weight
img = img + (wl .* sino(il, ia) + wr .* sino(il+1, ia)) .* w2;
end
img = (0.5 * orbit / (na/ia_skip)) * embed(img, mask);