CT重建(POCS-TVM)matlab实现

 主程序:

clc;clear;close all;
N = 180;  %图像大小
N2 = N^2;
I = phantom(N);  %图像

theta = linspace(0,180,61);  %%算头算尾61个点
theta = theta(1:60);  %投影角度
P_num = 260;
P = medfuncParallelBeamForwardProjection(theta, N, P_num);  %投影数据
%P = radon(I, theta);

delta = 1;
[W_ind, W_dat] = medfuncSystemMatrix(theta, N, P_num, delta);  %投影矩阵

irt_num = 5;
F0 = zeros(N2, 1);
num_TVM = 4;
lambda = 0.25;
alpha = 0;
F = medfuncPOCS_TVM(N, W_ind, W_dat, P, irt_num, F0, num_TVM, lambda, alpha);
F = reshape(F, N, N)';  % F排成矩阵时,一行一行的来

figure(1);
imshow(I);xlabel('(a)180*180头模型图像');
figure(2);
imshow(F);xlabel('(b)POCS-TVM算法重建图像');

投影矩阵计算:

function [W_ind, W_dat] = medfuncSystemMatrix(theta, N, P_num, delta)
% W_fun 计算投影矩阵
%---------------------------------
% 输入参数:
% theta:(平行)投影角度,适用于0=<theta<180
% N:矩阵(图像)大小
% P_num:每个投影角度下的射线条数(探测器通道个数)
% delta:网格大小
%---------------------------------
% 输出参数:
% 以W_ind和W_dat表示的投影矩阵
% W_ind:存储投影射线所穿过网格的编号,M*(2*N)
% W_dat:存储投影射线所穿过网格的长度,M*(2*N)
%=====================================================
N2 = N^2;
M = length(theta)*P_num;  %投影射线总条数
W_ind = zeros(M,2*N);  %存放射线穿过网格的编号
W_dat = zeros(M,2*N);  %存放射线穿过网格的长度
% t_max = sqrt(2)*N*delta;
% t = linspace(-t_max/2, t_max/2, P_num);
t = (-(P_num-1)/2:(P_num-1)/2)*delta;  %探测器坐标
%####当图像较小、射线条数较少时,画出扫描结构图(网格图)####%
if N<=10 && length(theta)<=5
    x = (-N/2:1:N/2)*delta;
    y = (-N/2:1:N/2)*delta;
    plot(x, meshgrid(y,x), 'k','LineStyle','--');
    hold on;
    plot(meshgrid(x,y), y, 'k','LineStyle','--');
    hold on;
    axis([-N/2-5, N/2+5, -N/2-5, N/2+5]);
    text(0, -0.4*delta, '0');
end  
%##########################################
% ===============投影矩阵的计算================ %
for jj = 1:length(theta)  
    for ii = 1:1:P_num      %每次完成一条射线权因子向量的计算
        u = zeros(1, 2*N);  v = zeros(1, 2*N);
        th = theta(jj);
        if th>=180 || th<0
             error('输入角度必须在0~180之间');
        %==============投影角度等于90时(平行于x轴)=============%
        elseif th == 90
            %##########画出对应的射线图############
            if N<=10 && length(theta)<=5
                xx = (-N/2-2:0.01:N/2+2)*delta;
                yy = t(ii);
                plot(xx, yy, 'b');
                hold on;
            end
            %######################################
            %如果超出网格范围,直接计算下一条
            if t(ii)>= N/2*delta || t(ii)<= -N/2*delta
                continue;
            end
            kout = N*ceil(N/2-t(ii)/delta);  %出射点网格编号
            kk = (kout-(N-1)):1:kout;  
            u(1:N) = kk;
            v(1:N) = ones(1,N)*delta;
        %===========投影角度等于0时==========%
        elseif th == 0
            %##############################
            if N<=10 && length(theta)<=5
                yy = (-N/2-2:0.01:N/2+2)*delta;
                xx = t(ii);
                plot(xx, yy, 'b');
                hold on;
            end
            %##############################
            %如果超出网格范围,直接计算下一条
            if t(ii)>= N/2*delta || t(ii)<= -N/2*delta
                continue;
            end
            kin = ceil(N/2+t(ii)/delta);  %入射点网格编号
            kk = kin:N:(kin+N*(N-1));  
            u(1:N) = kk;
            v(1:N) = ones(1,N)*delta;
        %====当90<th<180时,投影射线的角度(以x正半轴为起点)为th-90===
        %====当 0<th<90 时,与投影射线关于y轴对称的射线的角度为90-th===
        else
            if th>90
                th_temp = th-90;
            elseif th<90
                th_temp = 90-th;
            end
            th_temp = th_temp*pi/180;  %角度转化为弧度
            b = t/cos(th_temp);  
            m = tan(th_temp);  %确定射线y=mx+b的m和b
            y1d = -N/2*delta*m + b(ii);  %入射点y坐标
            y2d = N/2*delta*m + b(ii);  %出射点y坐标
            %################################
            if N<=10 && length(theta)<=5
                xx = (-N/2-2:0.01:N/2+2)*delta;
                if th<90
                    yy = -m*xx+b(ii);
                elseif th>90
                    yy = m*xx+b(ii);
                end
                plot(xx, yy, 'b');
                hold on;
            end
            %###################################
            %如果超出网格范围,直接计算下一条
            if (y1d<-N/2*delta && y2d<-N/2*delta) || (y1d>N/2*delta && y2d>N/2*delta)  %感觉书里误加了一个负号
                continue;
            end
            %确定入射点(xin,yin)、出射点(xout,yout)及参数d1
            if y1d<=N/2*delta && y1d>=-N/2*delta && y2d>N/2*delta
                yin = y1d;
                d1 = yin-floor(yin/delta)*delta;  %floor向下取整
                kin = N*floor(N/2-yin/delta)+1;
                yout = N/2*delta;
                xout = (yout-b(ii))/m;
                kout = ceil(xout/delta)+N/2;  %ceil向上取整
            elseif y1d<=N/2*delta && y1d>=-N/2*delta && y2d>=-N/2*delta && y2d<=N/2*delta
                yin = y1d;
                d1 = yin-floor(yin/delta)*delta;
                kin = N*floor(N/2-yin/delta)+1;
                yout = y2d;
                kout = N*floor(N/2-yout/delta)+N;
            elseif y1d<-N/2*delta && y2d>N/2*delta
                yin = -N/2*delta;
                xin = (yin-b(ii))/m;
                d1 = N/2*delta+(floor(xin/delta)*delta*m+b(ii));
                kin = N*(N-1)+N/2+ceil(xin/delta);
                yout = N/2*delta;
                xout = (yout-b(ii))/m;
                kout = ceil(xout/delta)+N/2;
            elseif y1d<-N/2*delta && y2d>=-N/2*delta && y2d<=N/2*delta
                yin = -N/2*delta;
                xin = (yin-b(ii))/m;
                d1 = N/2*delta+(floor(xin/delta)*delta*m+b(ii));
                kin = N*(N-1)+N/2+ceil(xin/delta);
                yout = y2d;
                kout = N*floor(N/2-yout/delta)+N;
            else
                continue;
            end
            %计算射线i穿过像素的编号和长度
            k = kin;
            c = 0;
            d2 = d1+m*delta;
            while k>=1 && k<=N2
                c = c+1;
                if d1>=0 && d2>delta
                    u(c) = k;
                    v(c) = (delta-d1)*sqrt(m^2+1)/m;
                    if k>N && k~=kout
                        k = k-N;
                        d1 = d1-delta;
                        d2 = d1 +m*delta;
                    else
                        break
                    end
                elseif d1>=0 && d2==delta
                    u(c) = k;
                    v(c) = delta*sqrt(m^2+1);
                    if k>N && k~=kout
                        k = k-N+1;
                        d1 = 0;
                        d2 = d1 +m*delta;
                    else
                        break
                    end
                elseif d1>=0 && d2<delta
                    u(c) = k;
                    v(c) = delta*sqrt(m^2+1);
                    if k~=kout
                        k = k+1;
                        d1 = d2;
                        d2 = d1 +m*delta;
                    else
                        break
                    end
                elseif d1<=0 && d2>=0 && d2<=delta
                    u(c) = k;
                    v(c) = d2*sqrt(m^2+1)/m;
                    if k~=kout
                        k = k+1;
                        d1 = d2;
                        d2 = d1 +m*delta;
                    else
                        break
                    end
                elseif d1<=0 && d2>delta
                    u(c) = k;
                    v(c) = delta*sqrt(m^2+1)/m;
                    if k>N && k~=kout
                        k = k-N;
                        d1 = d1-delta;
                        d2 = d1 +m*delta;
                    else
                        break
                    end
                end
            end
            %%如果投影角度小于90,利用投影射线关于y轴的对称性重新计算编号
            if th<90
                u_temp = zeros(1, 2*N);
                if any(u)==0
                    continue  %如果不经过任何网格,直接计算下一条
                end
                ind = u>0;
                for k=1:length(u(ind))
                    r = rem(u(k), N);
                    if r==0
                        u_temp(k) = u(k)-N+1;
                    else
                        u_temp(k) = u(k)-2*r+N+1;
                    end
                end
                u = u_temp;
            end
        end
        W_ind((jj-1)*P_num+ii, :) = u;
        W_dat((jj-1)*P_num+ii, :) = v;
    end
end

 平行束投影:

function P = medfuncParallelBeamForwardProjection(theta, N, N_d)
% Parallel beam forward projection(只针对头模型的解析算法)
%--------------------------
% 输入参数:
% theta:投影角度
% N:图像大小
% N_d:探测器通道个数
%--------------------------
% 输出参数:
% P:投影矩阵(N_d*theta_num)
%====================================
%
shep = [1     .69      .92     0      0        0
        -.8   .6624    .8740   0      -.0184   0
        -.2   .1100    .3100   .22    0        -18
        -.2   .1600    .4100   -.22   0        18
        .1    .2100    .2500   0      .35      0
        .1    .0460    .0460   0      .1       0
        .1    .0460    .0460   0      -.1      0
        .1    .0460    .0230   -.08   -.605    0
        .1    .0230    .0230   0      -.606    0
        .1    .0230    .0460   .06    -.605    0];
theta_num = length(theta);
P = zeros(N_d, theta_num);
rho = shep(:,1).';  %椭圆对应密度
ae = 0.5*N*shep(:,2).';  %椭圆短半轴
be = 0.5*N*shep(:,3).';  %椭圆长半轴
xe = 0.5*N*shep(:,4).';  %椭圆中心x坐标
ye = 0.5*N*shep(:,5).';  %椭圆中心y坐标
alpha = shep(:,6).';  %椭圆旋转角度
alpha = alpha*pi/180;
theta = theta*pi/180;  %角度换算为弧度
TT = -(N_d-1)/2:(N_d-1)/2;
for k1 = 1:theta_num
    P_theta = zeros(1, N_d);
    for k2 = 1:max(size(xe))
        a = (ae(k2)*cos(theta(k1)-alpha(k2)))^2+(be(k2)*sin(theta(k1)-alpha(k2)))^2;
        temp = a-(TT-xe(k2)*cos(theta(k1))-ye(k2)*sin(theta(k1))).^2;
        ind = temp>0;  %根号内值需为非负
        P_theta(ind) = P_theta(ind)+rho(k2)*(2*ae(k2)*be(k2)*sqrt(temp(ind)))./a;
    end
    P(:, k1) = P_theta;
end

end

 POCS-TVM算法:

function F = medfuncPOCS_TVM(N, W_ind, W_dat, P, irt_num, F0, num_TVM, lambda, alpha)
% POCS-TVM算法
%-----------------------------
%输入参数:
% N:图像大小
% W_ind:射线所穿过网格的编号,M*(2*N)
% w_dat:射线所穿过网格的长度,M*(2*N)
% P:投影数据
% irt_num:算法的总迭代次数
% F0:初始图像向量
% num_TVM:全变分最小化(TVM)过程的迭代次数,默认值为6
% lambda:松弛因子,默认值为0.25
% alpha:调节因子,默认值为0.2
%------------------------------
%输出参数:
% F:重建图像
%==============================
if nargin<6, F0 = zeros(N^2, 1); end
if nargin<7, num_TVM = 6; end
if nargin<8, lambda = 0.25; end
if nargin<9, alpha = 0.2; end
F = F0;
N2 = N^2;
[P_num, theta_num] = size(P);  % P_num:每个投影的射线条数;theta_num:投影个数
e = 0.00000001;
k1 = 0;  %循环控制变量
while(k1<irt_num)
    TEMP1 = F;
    %----------------------------
    for j = 1:theta_num
        for i = 1:1:P_num
             %取得一条射线所穿过的网格编号和长度  W行向量
             u = W_ind((j-1)*P_num+i,:);  %编号
             v = W_dat((j-1)*P_num+i,:);  %长度
             %如果射线不经过任何像素,不做计算
             if any(u) == 0;
                 continue;
             end
             %恢复投影矩阵中与这一条射线对应的行向量w
             w = zeros(1,N2);
             ind = u>0;
             w(u(ind)) = v(ind);
             %图像进行一次ART迭代
             PP = w*F;  %前向投影
             C = (P(i,j)-PP)/sum(w.^2)*w';  %修正项
             F = F+lambda*C;
        end
    end
    F(F<0) = 0;  %非负约束
    %------------------------------
    d = sqrt((TEMP1-F)'*(TEMP1-F));  %增量因子
    k2 = 0;
    while(k2<num_TVM)
        G = zeros(N2, 1);
        for i=2:N-1
            for j=2:N-1
                G((j-1)*N+i) = (2*F((j-1)*N+i)-F((j-1)*N+i-1)-F((j-2)*N+i))/sqrt(e+(F((j-1)*N+i)-F((j-1)*N+i-1))^2+(F((j-1)*N+i)-F((j-2)*N+i))^2)-...
                               F(j*N+i)-F((j-1)*N+i)/sqrt(e+(F(j*N+i)-F((j-1)*N+i))^2+(F(j*N+i)-F(j*N+i-1))^2)-...
                               F((j-1)*N+i+1)-F((j-1)*N+i)/sqrt(e+(F((j-1)*N+i+1)-F((j-1)*N+i))^2+(F((j-1)*N+i+1)-F((j-2)*N+i+1))^2);
            end
        end
        G = G/sqrt(G'*G);
        F = F-alpha*d*G;
        k2 = k2+1;
    end
    k1 = k1+1;
end

end

理论略解以后会传(大概不会了,我好懒),大家也可以去读黄力宇老师出的《医学断层图像重建仿真实验》,写的真的很好!!文章代码全来自此书,根据我的理解,对一两处印刷错误进行了改进。

欢迎大家纠错与讨论(〃'▽'〃)

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值