Feldkamp-Davis-Kress Algorithm Explanation

조회 수: 19 (최근 30일)
Soumi Banerjee
Soumi Banerjee 2020년 4월 9일
편집: Matt J 2020년 4월 13일
function [vol_tmp,H] = ConeBeam_Dem(dir,flprefix,theta,interp,filter,d,N,~,Ro,range_slices,~)
% If N is not specified, the size is determined
%
% [vol,H] = FDKX... (...) returns also the frequency response of the filter
% in the vector H.
%
% Ro is the orbital radius i.e. the distance source to object
% Ds2dct is the distance between source and planar detector
% Ds2dct = Ro + Do2dct = Ds2obj + Do2dct
% pxlsz is the size of the (square) pixel of the detector dct
% Class Support : All input arguments must be of class double.
% The output arguments are of class double.
%[p,theta,filter,d,interp,N] = parse_inputs(varargin{:});
thetag=theta;
theta = pi*theta/180; % from degrees to radiants
A=read_1rad(1.,dir,flprefix); % read in image frame
figure , imshow(A,[]); title(' first projection') % disp first image file
% get size of the data files
[nr_p , nc_p] = size(A) ; % get the number of rows and colums of the data files
%
range_slices=range_slices(range_slices>-nr_p/2 & range_slices<nr_p/2);
% remove slices out of range
% N is a scalar that specifies the number of rows and columns in the
% reconstructed 2D slice images. If N is not specified, the size is determined
% If the user didn't specify the size of the reconstruction, so
% deduce it from the length of projections
% nr_p; % sinogram size i.e. one size of the projection
if N==0 || isempty(N) % if N is not given then ...
N = 2*floor( nc_p /(2*sqrt(2)) ); % take the default value
end
imgDiag = 2*ceil(N/sqrt(2))+1; % largest distance through image.
%np = length(theta);
W=pix_weight(nr_p,nc_p,Ro,0,0); % weights to account for a planar detector
%size(W)
% Design the filter H
len=nc_p;
H = designFilter(filter, len, d);
LH=length(H); % length filter in the line vector H
% Define the x & y axes for the reconstructed image so that the origin
xax = (1:N)-ceil(N/2); x = repmat(xax, N, 1); y= repmat(xax', 1, N);
%y = rot90(x); % x coordinates, the y coordinates are rot90(x)
mask= ((x-0).^2+(y-0).^2) <= (N/2).^2 ; % mask the inner circle of diam N
mask=true(N); % no mask : all the pixels are to be reconstructed
maskV=mask(:); % mask in a vector
costheta = cos(theta); sintheta = sin(theta);
ctrIdx = ceil(len/2); % index of the center of the projections (on the hor axis)
% range slices to be recons
if exist('range_slices','var') && ~isempty(range_slices)
slices=range_slices; % slices to be recons
if isempty( find(slices==0, 1) ) % se non c'e' la slice centrale aggiungila
slices=[slices ,0]; slices=sort(slices) ;
end
else
% if no slices then recns the slice z=0 only
slices=[0];% [-floor(nc_p/2):step:+floor(nc_p/2)];
end
cnt_slc=find(slices==0);
slices;
%vol = repmat(0,[N,N,length(slices)]) ;% allocate 3D volume matrix
vol_tmp=repmat(single(0),[(N.^2),length(slices)]); % allocation
szproj=[nr_p,nc_p;]; % size(proj);
%
% Filtered Back-projection - i.e. from proj pixel (u,v) to voxel (x,y,z)
if strcmp(interp, 'nearest neighbor')
x=x(maskV); y=y(maskV); % mask !
nw_slices = real(sort(complex(slices))) ; % sort and reordering for faster reconstruction
% eg. +5 -5 + 6 -6 ...it allows to change the sign of v rather than recalculating v
ZFlag= abs(nw_slices(2:end)) == nw_slices(1:(end-1)) ; % logical variable
ZFlag=real([0, ZFlag] ); % true if +z is followed by -z and one can exploit symmetry
for ipos=1:length(slices) % right positioning the re-orded slices in the volume
iposz_v(ipos)=find( nw_slices(ipos)== slices) ; % slice correspondence
end
% data type and array allocation statements
proj=zeros(nr_p,nc_p);
tmp=zeros(nnz(mask),1); tmp2=zeros(N.^2,1); % allocate memory to temporary arrays
% tmp1
v0=zeros([size(x)]); v1=v0; u1=v1; % u=u1; v=u;
good_u=logical(v0); idx=zeros(size(v0)); % allocation
for i=1:length(theta) % loop over the projections
% i
dgri=thetag(i); % theta in degrees
proj=read_1rad(i,dir,flprefix); % read in the i-th radiograph
proj = radio2proj(proj,W,H,len); % tranforms radiog. into projection and multiply by W
%%%
proj(:,length(H))=0; % Zero pad projections for
for ir = 1:nr_p % filtering row by row with row vector H
proj(ir,:)= real (ifft ( fft(proj(ir,:)).*H ) ); % fast frequency domain filtering
end
proj(:,len+1:end) = [] ; % Truncate the filtered projections
%%%
s = x.*costheta(i) + y.*sintheta(i); t = -x.*sintheta(i) + y.*costheta(i); % Goddard & Trepanier
% s = x.*costheta(i) + y.*sintheta(i); % also in matlab iradon
R_s = Ro-s; u=round( Ro*t./(R_s) + ctrIdx) ; % u is the 1st projection pixel coordinate
good_u = (u>0 & u<(nc_p+1)) ; % only valid pixels -i.e. within the projection frame
lengoodu=length(good_u);
u1=u(good_u);%
inv_R_s_quad=1./(R_s(good_u).^2); Ro_dev_R_s=Ro./(R_s(good_u)); % for generally .* is faster than ./
iz=0;
id_1_term=(u1-1)*szproj(1); % does not depend up on v
for z=nw_slices % for all the (re-ordered) slices
iz=iz+1; % z slice under reconstruction
if ZFlag(iz)==1 % z symmetry (floting point is faster than logical operators!)
% if ( iz>=2 & ( abs(nw_slices(iz)) == nw_slices(iz-1) ) ) % z symmetry
v0=-v0; % allows calc time saving
else % v is the 2nd proj. pixel coord. (u,v)
v0=Ro_dev_R_s.*z ; %
end
v1=round( v0+nr_p/2 ) ; % coord. correction
%v1=v(good_u); % logical operations i.e. only valid pixels
idx = id_1_term + v1 ; % idx = sub2ind(szproj,u1, v1);
tmp = proj(idx); % just in case
tmp = tmp(:).*inv_R_s_quad; % back-projection ... from pixels to voxels ...!
tmp1(good_u)=tmp; tmp1(lengoodu)=0;%
tmp2(maskV)=tmp1; % prepare for image reconst. within the mask
iposz=iposz_v(iz); % position of the current slice iz in the volume at iposz
vol_tmp(:,iposz)=vol_tmp(:,iposz)+tmp2; % sum up back-projections without reshaping
end % loop over z
end % end loop over theta for the nearest neighbout method
vol_tmp=reshape(vol_tmp,[N,N,length(slices)]) ; % reshape 1D vector into 2D image for each z
% shows central slice only , in figure 10
figure(10), imshow(fliplr(vol_tmp(:,:,cnt_slc)'),[]); % visualize central slice after vol reconstruction
stringa = [' NN back-prj. cntrl slice , radiog. degrees: ' , num2str(dgri)];
title(stringa); colormap(gray(256));
disp(' done cone beam back projection ! - nearest neighbour method')
%***********************************************************
end
if strcmp(interp, 'bilinear')
x=x(mask); y=y(mask); % mask !
temp_1=zeros(nnz(mask(:)),1); temp_2=zeros(nnz(mask(:)),1);
tmp2=zeros(N.^2,1); % viene retroproiettato
for i=1:length(theta) % loop over projections
% i
dgri=thetag(i); % angolo corrente in gradi
proj=read_1rad(i,dir,flprefix); % read in the i-th projection
proj = radio2proj(proj,W,H,len);
proj(:,length(H))=0; % Zero pad projections
for ir = 1:nr_p % filtering row by row
proj(ir,:)=real (ifft ( fft(proj(ir,:)).*H ) ); % fast frequency domain filtering
end % done for each row
proj(:,len+1:end) = []; % Truncate the filtered projections
proj=proj(:);
proj1=[proj(2:end,:); proj(end,:)]; proj1=proj1(:);
% imshow(proj,[ ]) ; title(' log proj');
% proj. is transposed with respect to the original radiograph!
s = x.*costheta(i) + y.*sintheta(i); t = -x.*sintheta(i) + y.*costheta(i); % Goddard&Trepanier
R_s=Ro-s; u=Ro*t./R_s; u=u+ctrIdx; fu=floor(u(:)); %
iz=0;
good=( (fu>0 & fu<nc_p+1) ); lengoodu=length(good);
for z= slices
iz=iz+1 ;%z % slice under reconstruction
v=Ro*z./R_s + nr_p/2; % coord. correction
fv=floor(v(:));
% good=( (fu>0 & fu<=nc_p) & (fv>0 & fv<=nr_p) );
fu1=fu(good); fv1=fv(good);
idx = (fu1-1)*szproj(1)+fv1 ; %idx = sub2ind(size(proj),fu, fv);
temp_1(good) = (u(good)-fu1).*proj1(idx) + (fu1+1-u(good)).*proj(idx);
proj=[proj(:,(2:end)),proj(:,end)]; proj1=[proj(2:end,:);proj(end,:)];
temp_2(good) = (u(good)-fu1).*proj1(idx) + (fu1+1-u(good)).*proj(idx);
R_s1=R_s(:);
tmp =( (v(good)-fv1) .* temp_2(good) + (fv1+1-v(good)) .* temp_1(good) ) ./ (R_s1(good).^2);
tmp1(good)=tmp; tmp1( lengoodu)=0; tmp2(mask(:))=tmp1;
vol_tmp(:,iz)=vol_tmp(:,iz)+tmp2(:); % $$$$$$$$$$$$$
end % loop over z
end % end loop over theta for bilinear method
vol_tmp=reshape(vol_tmp',[N,N,length(slices)]) ; % reshape
% shows central slice in figure 10
figure(10), imshow(fliplr(vol_tmp(:,:,cnt_slc)'),[]); % visualize central slice after vol reconstruction
stringa = [' Bi-LNR bk prj. rad. ' , num2str(i-1)]; title(stringa)
colormap(gray(256)); disp(' done back projection ! - Bilinear method')
end % close else linear
% Filtered Back-projection - i.e. from proj pixel (u,v) to voxel (x,y,z)
if strcmp(interp, 'n_n_demo')
x=x(maskV); y=y(maskV); % mask !
nw_slices = real(sort(complex(slices))) ; % sort and reordering for faster reconstruction
% eg. +5 -5 + 6 -6 ...it allows to change the sign of v rather than recalculating v
ZFlag= abs(nw_slices(2:end)) == nw_slices(1:(end-1)) ; % logical variable
ZFlag=real([0, ZFlag] ); % true if +z is followed by -z and one can exploit symmetry
for ipos=1:length(slices) % right positioning the re-orded slices in the volume
iposz_v(ipos)=find( nw_slices(ipos)== slices) ; % slice correspondence
end
% data type and array allocation statements
proj=zeros(nr_p,nc_p);
tmp=zeros(nnz(mask),1); tmp2=zeros(N.^2,1); % allocate memory to temporary arrays
% tmp1
v0=zeros([size(x)]); v1=v0; u1=v1; % u=u1; v=u;
good_u=logical(v0); idx=zeros(size(v0)); % allocation
for i=1:length(theta) % loop over the projections
% i
dgri=thetag(i); % theta in degrees
proj=read(i,dir,flprefix); % read in the i-th radiograph
proj = radio2proj(proj,W,H,len); % tranforms radiog. into projection and multiply by W
%%%
proj(:,length(H))=0; % Zero pad projections for
for ir = 1:nr_p % filtering row by row with row vector H
proj(ir,:)= real (ifft ( fft(proj(ir,:)).*H ) ); % fast frequency domain filtering
end
proj(:,len+1:end) = [] ; % Truncate the filtered projections
%%%
s = x.*costheta(i) + y.*sintheta(i); t = -x.*sintheta(i) + y.*costheta(i); % Goddard & Trepanier
% s = x.*costheta(i) + y.*sintheta(i); % also in matlab iradon
R_s = Ro-s; u=round( Ro*t./(R_s) + ctrIdx) ; % u is the 1st projection pixel coordinate
good_u = (u>0 & u<(nc_p+1)) ; % only valid pixels -i.e. within the projection frame
lengoodu=length(good_u);
u1=u(good_u);%
inv_R_s_quad=1./(R_s(good_u).^2); Ro_dev_R_s=Ro./(R_s(good_u)); % for generally .* is faster than ./
iz=0;
id_1_term=(u1-1)*szproj(1); % does not depend up on v
for z=nw_slices % for all the (re-ordered) slices
iz=iz+1; % z slice under reconstruction
if ZFlag(iz)==1 % z symmetry (floting point is faster than logical operators!)
% if ( iz>=2 & ( abs(nw_slices(iz)) == nw_slices(iz-1) ) ) % z symmetry
v0=-v0; % allows calc time saving
else % v is the 2nd proj. pixel coord. (u,v)
v0=Ro_dev_R_s.*z ; %
end
v1=round( v0+nr_p/2 ) ; % coord. correction
%v1=v(good_u); % logical operations i.e. only valid pixels
idx = id_1_term + v1 ; % idx = sub2ind(szproj,u1, v1);
tmp = proj(idx); % just in case
tmp = tmp(:).*inv_R_s_quad; % back-projection ... from pixels to voxels ...!
tmp1(good_u)=tmp; tmp1(lengoodu)=0;%
tmp2(maskV)=tmp1; % prepare for image reconst. within the mask
iposz=iposz_v(iz); % position of the current slice iz in the volume at iposz
vol_tmp(:,iposz)=vol_tmp(:,iposz)+tmp2; % sum up back-projections without reshaping
figure(8), imshow(reshape(vol_tmp,[N,N,length(slices)]),[ ]) ;
title('back-projection in progress');
end % loop over z
end % end loop over theta for the nearest neighbout method
vol_tmp=reshape(vol_tmp,[N,N,length(slices)]) ; % reshape 1D vector into 2D image for each z
% shows central slice only , in figure 10
figure(10), imshow(fliplr(vol_tmp(:,:,cnt_slc)'),[ ]); % visualize central slice after vol reconstruction
stringa = [' NN back-prj. cntrl slice , radiog. degrees: ' , num2str(dgri)];
title(stringa); colormap(gray(256));
disp(' done cone beam back projection ! - nearest neighbour method')
%***********************************************************
end
vol_tmp= vol_tmp*Ro.^2*pi/(2*length(theta));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
%%% Sub-Function: designFilter
%%%
function filt = designFilter(filter, len, d)
% Returns the Fourier Transform of the filter which will be
% used to filter the projections
%
% INPUT ARGS: filter - either the string specifying the filter
% len - the length of the projections
% d - the fraction of frequencies below the nyquist
% which we want to pass
%
% OUTPUT ARGS: filt - the filter to use on the projections
order = max(64,2^nextpow2(2*len));
% First create a ramp filter - go up to the next highest
% power of 2.
filt = 2*( 0:(order/2) )./order;
w = 2*pi*(0:size(filt,2)-1)/order; % frequency axis up to Nyquist
switch lower(filter)
case 'ram-lak'
% Do nothing
case 'shepp-logan'
% be careful not to divide by 0:
filt(2:end) = filt(2:end) .* (sin(w(2:end)/(2*d))./(w(2:end)/(2*d)));
case 'cosine'
filt(2:end) = filt(2:end) .* cos(w(2:end)/(2*d));
case 'hamming'
filt(2:end) = filt(2:end) .* (.54 + .46 * cos(w(2:end)/d));
case 'hann'
filt(2:end) = filt(2:end) .*(1+cos(w(2:end)./d)) / 2;
otherwise
filter;
error('Invalid filter selected.');
end
filt(w>pi*d) = 0; % Crop the frequency response
filt = [filt , filt(end-1:-1:2)]; % Symmetry of the filter
return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function W = pix_weight(nr,nc,R,Nofs,Eofs)
% weight for the pixels of the projetions/radiographs
% R is the orbital radius i.e. source to object distance
% off set ... Nofs=Nord off set ; Eofs= East ..
% nr, nc number of rows and columns of W
if nargin <4 % missing off set values
Nofs=0;Eofs=0; % no off set then
end
if abs(Nofs) > floor(nc/2) || abs(Eofs) > floor(nr/2)
disp(' off set overflow in pix_weight')
end
% grid !
[X,Y]=meshgrid([[floor(nc/2):-1:0],[1:(nc-floor(nc/2)-1)]],...
[[floor(nr/2):-1:0],[1:(nr-floor(nr/2)-1)]]);
%W=R.* ( (X.^2 + Y.^2 + R^2).^-0.5 );
W=R* ( ((X-Nofs) .^2 + (Y-Eofs).^2 + R^2).^-0.5 );
%figure, imshow(W,[]); title(' pxls weights')
%figure, surf(W); title(' pixel weighting matrix ')
%colormap(jet)
return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% function y = embed(x, mask)
% %function y = embed(x, mask)
% % embed x in nonzero elements of a logical mask
% % x is a 1D array and mask is 2D
%
% if max(size(mask)) <= 1
% y = x; % peculiar
% else
% good = find(mask(:) ~= 0);
% np = length(good);
% if size(x, 1) ~= np
% error 'bad input size' %
% end
% xdim = size(x);
% y = zeros(prod(size(mask)), prod(xdim(2:end)));
% y(good,:) = x;
% y = reshape(y, size(mask), xdim(2:end));
% end
return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function proj = radio2proj(radio,W,~,~)
% the routine corrects the radiog. for flat and dark images
% weights the radiographs
% log process the radio to generate a projection
%
% num=(radio-dark); den=(flat-dark);
% num(num<=0)=1.E-6; den(den<=0)=1.E-4;
% proj=num./den; % correzione per dark & flat
% proj(proj>1)=1; proj(proj<=0)=1E-6;
% proj=-log(proj); % da radio 2 proj
%size(radio)
proj=radio;
proj=proj.*W; % weighting projection
%return
% filtring projection
% proj(:,length(H))=0; % Zero pad projections
% %proj = fft(proj); % p holds fft of projections
% for ic = 1:size(proj,1)
% % filter column by column i.e. row sino by row sino
% %proj(:,ic) = proj(:,ic).*H(:); % fast frequency domain filtering
% proj(ic,:)=real (ifft ( fft(proj(ic,:)).*H ) );
% end
% %proj = real(ifft(proj)); % p is the filtered projections
% proj(:,len+1:end) = []; % Truncate the filtered projections
%figure(6), imshow(proj,[ ]); title(' proiezione filtrata ')
return
  댓글 수: 1
Cam Salzberger
Cam Salzberger 2020년 4월 13일
I highly suggest checking out this, and then reframing this question.

댓글을 달려면 로그인하십시오.

답변 (0개)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by