前几篇的实验都用 scikit-image 的 radon / iradon,或者自己写几十行 NumPy。这样讲原理足够,但一到真实尺寸就不够用了:没有 GPU 加速,没有锥束,也很难嵌进深度学习网络。
上一篇精读的 Learned Primal-Dual,每次迭代里的正投影和反投影,就是 ODL 调用 ASTRA Toolbox 的 GPU 后端完成的。ASTRA 是 CT 重建论文里最常见的底层投影库之一,很多上层框架都建立在它之上。
从这一篇开始,基础系列用几篇的篇幅,一个库一个库地讲常用的 CT 工具。本篇只讲 ASTRA:它是什么、怎么装、几何怎么写、投影器怎么选、自带哪些算法、怎么把它当成一个线性算子来用,以及最容易踩的坑。
文中代码全部原样摘自 ASTRA 官方仓库的 samples/python 和官方文档,没有改动。其中只用 CPU 的示例,我在 CPU 版 ASTRA 2.5.0 上原样跑通了;需要 GPU 的示例会单独注明。
- 官方仓库:https://github.com/astra-toolbox/astra-toolbox
- 官方文档:https://www.astra-toolbox.com/docs/
- 本文对应版本:2.5.0(2026-06-17 发布)
一、ASTRA 是什么
官方的定位是:一个面向 2D 和 3D 断层成像的高性能 GPU 基本运算库,提供 Python 和 MATLAB 接口。按官网列出的特性:
- 几何:2D 平行束、扇束,3D 平行束、锥束,并且源和探测器的位置可以逐角度自由指定;
- 算法:FBP、SIRT、SART、CGLS 等 2D 和 3D 重建算法;
- 正投影和反投影都做了 GPU 加速,可以直接从 Python / MATLAB 调用,用来搭自己的算法;
- 数据放不进显存时自动切分,支持多 GPU;
- 通过 DLPack 和 NumPy、PyTorch、CuPy、JAX 零拷贝交换数据。
它由安特卫普大学 imec-Vision Lab 和荷兰 CWI 开发维护,许可证是 GPLv3。学术研究随便用;如果要把它打包进闭源产品,先看清 GPL 的条款。
一句话概括它的位置:ASTRA 负责"算得快、几何写得灵活"的投影和反投影,本身不提供 TV 之类的正则化,也不提供自动求导。正则化、优化算法、和深度学习框架的衔接,通常交给上层库或者自己的代码。
二、安装
官方 README 给出的安装方式(命令原文照录,中文注释是我加的):
bash
# conda(官方频道,带 CUDA)
conda install -c astra-toolbox -c nvidia astra-toolbox
# conda-forge(含 Linux ARM 和 Apple Silicon 的 CPU 版)
conda install -c conda-forge astra-toolbox
# conda-forge 上的纯 CPU 版(无 GPU 加速,只有 2D 算法)
conda install -c conda-forge astra-toolbox=*=py*
# pip
pip install astra-toolbox
几点说明:
- 纯 CPU 版只有 2D 功能。 官方 README 原话是 "no GPU acceleration and CUDA dependencies, 2D algorithms only"。我在一台 ARM 服务器上装的就是 conda-forge 的 CPU 版:调用
'cuda'投影器会直接报Unknown Projector2D type,3D 正投影报Unknown algorithm type。想做锥束和 FDK,必须有 NVIDIA GPU。 - 老显卡注意 CUDA 版本。 官网提示:CUDA 13 的包不再支持 GTX 9 系和 10 系显卡,这类显卡要用 CUDA 12 的包。
- pip 轮子有平台限制。 2.5.0 在 PyPI 上提供 Linux x86_64 和 Windows 的预编译包(Windows 的 PyPI 发布是 2.5.0 新加的);其他平台走 conda 或源码编译。
装好后可以先确认版本和 GPU 是否可用:astra.__version__、astra.use_cuda()。
三、四个核心概念
ASTRA 的 API 风格和 scikit-image 很不一样:核心接口不是"传数组进去、拿数组出来"的函数,而是先创建一个个对象 ,用整数 ID 引用它们。只要理解下面四个概念,所有示例都能看懂。
| 概念 | 作用 | 典型调用 |
|---|---|---|
| 几何(geometry) | 体数据放在哪、射线源和探测器怎么摆 | create_vol_geom、create_proj_geom |
| 数据对象(data) | 体数据或投影数据,存在 ASTRA 内部,用 ID 引用 | astra.data2d.create、get、delete |
| 投影器(projector) | 用什么模型计算射线和像素的权重 | create_projector |
| 算法(algorithm) | 正投影、反投影、FBP、SIRT 等,用配置字典描述 | astra_dict、algorithm.create、run |
基本流程是:定几何 → 建数据对象 → 选投影器 → 写算法配置 → 运行 → 取结果 → 释放对象(GPU 算法不需要在配置里指定投影器,见下文)。
四、第一个完整例子:CPU 上的 SIRT 重建
下面是官方示例 s004_cpu_reconstruction.py 的全部正文(省略了开头的版权注释),纯 CPU 就能跑:
python
import astra
import numpy as np
import matplotlib.pyplot as plt
plt.gray()
vol_geom = astra.create_vol_geom(256, 256)
proj_geom = astra.create_proj_geom(
'parallel', 1.0, 384, np.linspace(0, np.pi, 180, False)
)
# For CPU-based algorithms, a 'projector' object specifies the projection
# model used. In this case, we use the 'strip' model
proj_id = astra.create_projector('strip', proj_geom, vol_geom)
# Create a sinogram from a phantom
phantom_id, P = astra.data2d.shepp_logan(vol_geom)
sinogram_id, sinogram = astra.create_sino(phantom_id, proj_id)
plt.imshow(P)
plt.figure()
plt.imshow(sinogram)
# Create a data object for the reconstruction
rec_id = astra.data2d.create('-vol', vol_geom)
# Set up the parameters for a reconstruction algorithm using the CPU
# The main difference with the configuration of a GPU algorithm is the
# extra ProjectorId setting
cfg = astra.astra_dict('SIRT')
cfg['ReconstructionDataId'] = rec_id
cfg['ProjectionDataId'] = sinogram_id
cfg['ProjectorId'] = proj_id
# Available algorithms:
# ART, SART, SIRT, CGLS, FBP
# Create the algorithm object from the configuration structure
alg_id = astra.algorithm.create(cfg)
# Run 20 iterations of the algorithm
# This will have a runtime in the order of 10 seconds
astra.algorithm.run(alg_id, 20)
# Get the result
rec = astra.data2d.get(rec_id)
plt.figure()
plt.imshow(rec)
plt.show()
# Clean up.
astra.algorithm.delete(alg_id)
astra.data2d.delete(rec_id)
astra.data2d.delete(sinogram_id)
astra.data2d.delete(phantom_id)
astra.projector.delete(proj_id)
逐段看:
- 几何。
create_vol_geom(256, 256)是 256×256 的图像;create_proj_geom('parallel', 1.0, 384, angles)是平行束,探测器 384 个单元、间距 1.0,180 个角度均匀分布在 [ 0 , π ) [0, \pi) [0,π)。np.linspace(0, np.pi, 180, False)的最后一个参数是endpoint=False,所以不包含 π \pi π。 - 投影器。
'strip'把每条射线看成和探测器单元等宽的条带,权重是条带和像素的相交面积。CPU 算法必须显式指定投影器。 - 正投影。
create_sino返回两个东西:投影数据对象的 ID,以及对应的 NumPy 数组。这里的正弦图形状是(180, 384),即**(角度数,探测器单元数)**。 - 算法配置。
astra_dict('SIRT')生成一个配置字典,填入重建结果、投影数据和投影器三个 ID。按官方文档,ReconstructionDataId里原有的内容会被当作迭代初值,这里data2d.create('-vol', ...)建出来的是全零图像。注释里写了 CPU 版可选的算法:ART、SART、SIRT、CGLS、FBP。 - 运行。
run(alg_id, 20)迭代 20 次。官方注释估计运行时间在 10 秒量级;我在 ARM 服务器的 CPU 版上,整段脚本约 6 秒。 - 释放。 最后五行
delete不能省。ASTRA 的对象不随 Python 变量自动回收:我在函数里创建数据对象后不删除,函数返回、手动gc.collect()之后,astra.data2d.info()里仍然列着它们。循环里反复创建而不释放,内存会一直涨。
SIRT 和 SART 的原理在(二)里推过。这里的区别只是:不用自己写迭代,ASTRA 把整个循环在 C++ / CUDA 里跑完了。
换成 GPU 版要改什么
对比官方的 GPU 示例 s003_gpu_reconstruction.py,算法配置部分是这样的:
python
rec_id = astra.data2d.create('-vol', vol_geom)
# Set up the parameters for a reconstruction algorithm using the GPU
cfg = astra.astra_dict('SIRT_CUDA')
cfg['ReconstructionDataId'] = rec_id
cfg['ProjectionDataId'] = sinogram_id
# Available algorithms:
# SIRT_CUDA, SART_CUDA, EM_CUDA, CGLS_CUDA, FBP_CUDA (see the FBP sample)
# Create the algorithm object from the configuration structure
alg_id = astra.algorithm.create(cfg)
# Run 150 iterations of the algorithm
astra.algorithm.run(alg_id, 150)
和 CPU 版相比只有两处不同:算法名加了 _CUDA 后缀,并且配置里不需要 ProjectorId 。官方文档的说法是:CUDA 算法总是使用 'cuda' 投影器,它对应的是 GPU 加速的 'linear'(Joseph)模型。同一个示例把迭代次数设成 150。
加上取值约束
SIRT 还支持对重建值加上下界(CPU 版和 GPU 版的文档里都有 MinConstraint / MaxConstraint 选项),例如把灰度限制在 0 , 1 0, 1 0,1。官方示例 s013_constraints.py 用的是 GPU 版,50 个角度:
python
# Set up the parameters for a reconstruction algorithm using the GPU
cfg = astra.astra_dict('SIRT_CUDA')
cfg['ReconstructionDataId'] = rec_id
cfg['ProjectionDataId'] = sinogram_id
cfg['option'] = {'MinConstraint': 0, 'MaxConstraint': 1}
# Create the algorithm object from the configuration structure
alg_id = astra.algorithm.create(cfg)
# Run 150 iterations of the algorithm
astra.algorithm.run(alg_id, 150)
五、几何怎么写
1. 2D 几何
| 类型 | 参数 | 说明 |
|---|---|---|
parallel |
探测器间距、单元数、角度 | 平行束 |
fanflat |
探测器间距、单元数、角度、源到旋转中心距离、旋转中心到探测器距离 | 平板探测器扇束 |
parallel_vec / fanflat_vec |
单元数、vectors 矩阵 |
每个角度单独指定位置 |
几个要点(均来自官方文档):
- 角度用弧度。
- 默认像素是边长为 1 的正方形,体数据以原点为中心。 所以探测器间距 1.0 的意思是"和一个像素一样宽",不是 1 mm。要用物理尺寸,就在
create_vol_geom里额外传min_x, max_x, min_y, max_y指定范围,这时所有长度单位要统一。文档同时警告:部分投影器(包括 2D CUDA 投影器)要求像素是正方形。 vec版本的每一行描述一个角度:parallel_vec是(dirX, dirY, dX, dY, uX, uY),分别是射线方向、探测器中心、相邻两个探测器单元中心之间的向量;fanflat_vec把第一项换成射线源位置(srcX, srcY)。普通几何可以用astra.geom_2vec(proj_geom)一键转换。
2. 3D 几何
3D 有 parallel3d、cone 两种常规类型和对应的 vec 版本。官方示例 s005_3d_geometry.py 的锥束部分:
python
# ----------
# Cone beam
# ----------
# Circular
# Parameters: width of detector column, height of detector row, #rows, #columns,
# angles, distance source-origin, distance origin-detector
angles = np.linspace(0, 2 * np.pi, 48, False)
proj_geom = astra.create_proj_geom('cone', 1.0, 1.0, 32, 64, angles, 1000, 0)
# Free
vectors = np.zeros((len(angles), 12))
for i in range(len(angles)):
# source
vectors[i, 0] = np.sin(angles[i]) * 1000
vectors[i, 1] = -np.cos(angles[i]) * 1000
vectors[i, 2] = 0
# center of detector
vectors[i, 3:6] = 0
# vector from detector pixel (0,0) to (0,1)
vectors[i, 6] = np.cos(angles[i])
vectors[i, 7] = np.sin(angles[i])
vectors[i, 8] = 0
# vector from detector pixel (0,0) to (1,0)
vectors[i, 9] = 0
vectors[i, 10] = 0
vectors[i, 11] = 1
# Parameters: #rows, #columns, vectors
proj_geom = astra.create_proj_geom('cone_vec', 32, 64, vectors)
示例开头的注释说得很清楚:常规类型假设绕 z 轴做圆轨迹;vec 类型对源和探测器的位置完全不设限制 。每个角度 12 个数:射线源位置、探测器中心、探测器上 ( 0 , 0 ) → ( 0 , 1 ) (0,0)\to(0,1) (0,0)→(0,1) 的向量 u u u、 ( 0 , 0 ) → ( 1 , 0 ) (0,0)\to(1,0) (0,0)→(1,0) 的向量 v v v。C 形臂、螺旋、带偏移和倾斜的真实扫描几何,都靠 cone_vec 描述。
注意示例里 'cone' 的最后两个参数是 1000, 0:源到旋转中心 1000,旋转中心到探测器 0。也就是说探测器放在了经过旋转中心的平面上,探测器间距因此是按旋转中心处的尺度来算的。真实设备的探测器在后方,放大比等于源到探测器距离除以源到旋转中心距离,要么如实填写两段距离和真实像素尺寸,要么把探测器尺寸除以放大比后令第二段距离为 0,两种写法不要混。
另外,2.4 版本加入了柱面探测器几何 cyl_cone_vec,官方标注为实验性功能。
3. 数组形状
这是最容易出错的地方。我在 2.5.0 上逐一验证过:
| 数据 | NumPy 数组形状 |
|---|---|
| 2D 体数据 | (行数, 列数) |
| 2D 投影数据 | (角度数, 探测器单元数) |
| 3D 体数据 | (切片数, 行数, 列数) |
| 3D 投影数据 | (探测器行数, 角度数, 探测器列数) |
3D 投影数据的角度在中间一维 ,和不少其他库的习惯不同。官方 s007 示例里显示单张投影用的就是 proj_data[:, 20, :]。从别的库导入数据时,先 transpose 对齐。
六、投影器怎么选
投影器决定"一条射线经过一个像素时权重是多少",也就是系统矩阵 A A A 的每个元素怎么算。2D 可选的有:
| 投影器 | 适用几何 | 权重模型 |
|---|---|---|
line |
parallel | 把射线看成零宽度直线,权重是直线穿过像素的长度 |
strip |
parallel | 把射线看成与探测器单元等宽的条带,权重是相交面积 |
linear |
parallel | 逐行或逐列步进,在相邻两个像素之间线性插值(Joseph 模型) |
line_fanflat |
fanflat、fanflat_vec | 扇束版的 line |
strip_fanflat |
fanflat | 扇束版的 strip;官方注明它没有正确考虑扇束的放大效应 |
cuda |
parallel、parallel_vec、fanflat、fanflat_vec | GPU 加速的 linear |
实际使用时记住两点:
- CPU 算法必须指定投影器,GPU 算法只用
cuda。 每个 CPU 投影器只适用于平行束或扇束中的一种。 - 模型不同,数值就不同。 我在 CPU 版上对同一个 Shepp--Logan 体模分别用
line、strip、linear做正投影,三张正弦图并不逐元素相等。GPU 的cuda对应的是linear,所以 CPU 上用strip生成的数据,和 GPU 上算出来的也不会完全一致。做对比实验时,正投影和反投影、训练数据和测试数据,要用同一种投影器。
七、FBP 和 FDK
1. 2D FBP
官方示例 s023_FBP_filters.py 演示了三种传滤波器的方法,第一种是标准的 Ram-Lak 滤波,纯 CPU 可跑:
python
N = 256
vol_geom = astra.create_vol_geom(N, N)
proj_geom = astra.create_proj_geom(
'parallel', 1.0, N, np.linspace(0, np.pi, 180, False)
)
phantom_id, P = astra.data2d.shepp_logan(vol_geom)
proj_id = astra.create_projector('strip', proj_geom, vol_geom)
sinogram_id, sinogram = astra.create_sino(P, proj_id)
rec_id = astra.data2d.create('-vol', vol_geom)
cfg = astra.astra_dict('FBP')
cfg['ReconstructionDataId'] = rec_id
cfg['ProjectionDataId'] = sinogram_id
cfg['ProjectorId'] = proj_id
cfg['option'] = {}
# 1. Use a standard Ram-Lak filter
cfg['option']['FilterType'] = 'ram-lak'
alg_id = astra.algorithm.create(cfg)
astra.algorithm.run(alg_id)
rec_RL = astra.data2d.get(rec_id)
CPU 版 FBP 只支持平行束;扇束 FBP 要用 GPU 版 FBP_CUDA。文档列出的 FilterType 包括 ram-lak(默认)、shepp-logan、cosine、hamming、hann 等十几种窗函数,以及 projection、sinogram、rprojection、rsinogram 四种自定义方式:前两种在频域给滤波器,后两种在空间域给卷积核,projection 类是所有角度共用一个滤波器,sinogram 类是每个角度各自一个。滤波器的原理见(一)。
2. 3D FDK(需要 GPU)
(三)讲过的 FDK,在 ASTRA 里是 FDK_CUDA。下面是官方文档 FDK_CUDA 页面给出的 Python 示例原文。我手上的 CPU 版跑不了它,这里只做解读:
python
import astra
import matplotlib.pyplot as plt
import numpy as np
# Create geometries
N = 256
N_ANGLES = 360
det_spacing = 1.0
angles = np.linspace(0, 2*np.pi, N_ANGLES)
src_obj_dist = 500
obj_det_dist = 0
proj_geom = astra.create_proj_geom('cone', det_spacing, det_spacing, N, N, angles,
src_obj_dist, obj_det_dist)
vol_geom = astra.create_vol_geom(N, N, N)
# Generate phantom image
phantom_id, phantom = astra.data3d.shepp_logan(vol_geom)
# Create forward projection
sinogram_id, sinogram = astra.create_sino3d_gpu(phantom_id, proj_geom, vol_geom)
# Reconstruct
recon_id = astra.data3d.create('-vol', vol_geom)
cfg = astra.astra_dict('FDK_CUDA')
cfg['ProjectionDataId'] = sinogram_id
cfg['ReconstructionDataId'] = recon_id
algorithm_id = astra.algorithm.create(cfg)
astra.algorithm.run(algorithm_id)
reconstruction = astra.data3d.get(recon_id)
plt.imshow(reconstruction[N//2], cmap='gray')
# Clean up
astra.data3d.delete([sinogram_id, recon_id, phantom_id])
astra.algorithm.delete(algorithm_id)
文档里的几点说明:
- 支持
cone和cone_vec;cone_vec如果明显偏离圆轨迹,重建会出现伪影,因为 FDK 本身就是为圆轨迹推导的近似算法(见(三)); - 选项
ShortScan打开 Parker 加权,用于不足 360° 的扫描,要求角度范围至少为 180° 加两倍扇角; - FDK 不支持各向异性体素,会直接抛异常。
八、把 ASTRA 当成矩阵或线性算子
做研究时,经常不想用库里自带的算法,而是只要 A A A 和 A T A^T AT,自己写优化或嵌进网络。ASTRA 有三种方式。
1. 直接导出稀疏系统矩阵(仅 2D CPU 投影器)
官方示例 s009_projection_matrix.py:
python
vol_geom = astra.create_vol_geom(256, 256)
proj_geom = astra.create_proj_geom(
'parallel', 1.0, 384, np.linspace(0, np.pi, 180, False)
)
# For CPU-based algorithms, a 'projector' object specifies the projection
# model used. In this case, we use the 'line' model.
proj_id = astra.create_projector('line', proj_geom, vol_geom)
# Generate the projection matrix for this projection model.
# This creates a matrix W where entry w_{i,j} corresponds to the
# contribution of volume element j to detector element i.
matrix_id = astra.projector.matrix(proj_id)
# Get the projection matrix as a Scipy sparse matrix.
W = astra.matrix.get(matrix_id)
# Manually use this projection matrix to do a projection:
phantom_id, P = astra.data2d.shepp_logan(vol_geom)
s = W.dot(P.ravel())
s = np.reshape(s, (len(proj_geom['ProjectionAngles']), proj_geom['DetectorCount']))
plt.imshow(s)
plt.show()
# Each row of the projection matrix corresponds to a detector element.
# Detector t for angle p is for row 1 + t + p*proj_geom.DetectorCount.
# Each column corresponds to a volume pixel.
# Pixel (x,y) corresponds to column 1 + x + y*vol_geom.GridColCount.
astra.projector.delete(proj_id)
astra.data2d.delete(phantom_id)
astra.matrix.delete(matrix_id)
这里的 W W W 就是(二)里 A x = b Ax=b Ax=b 的 A A A:行数是 180 × 384 = 69120 180 \times 384 = 69120 180×384=69120,列数是 256 × 256 = 65536 256 \times 256 = 65536 256×256=65536,W.dot(P.ravel()) 就是正投影。示例末尾的注释说明了行列的排列顺序(注释里的下标是从 1 开始的 MATLAB 写法)。
文档的提醒是:这个功能只对 2D CPU 投影器实现,大几何下矩阵会占用大量内存。教学和小规模实验很好用,临床尺寸的 3D 就别想了。
2. OpTomo:可以直接交给 SciPy 求解器的线性算子
官方示例 s017_OpTomo.py(这个示例用 'cuda' 投影器,需要 GPU):
python
vol_geom = astra.create_vol_geom(256, 256)
proj_geom = astra.create_proj_geom(
'parallel', 1.0, 384, np.linspace(0, np.pi, 180, False)
)
# As before, create a sinogram from a phantom
phantom_id, P = astra.data2d.shepp_logan(vol_geom)
proj_id = astra.create_projector('cuda', proj_geom, vol_geom)
# construct the OpTomo object
W = astra.OpTomo(proj_id)
sinogram = W * P
sinogram = sinogram.reshape([180, 384])
plt.imshow(P)
plt.figure()
plt.imshow(sinogram)
# Run the lsqr linear solver
output = scipy.sparse.linalg.lsqr(W, sinogram.ravel(), iter_lim=150)
rec = output[0].reshape([256, 256])
plt.figure()
plt.imshow(rec)
plt.show()
# Clean up
astra.projector.delete(proj_id)
astra.data2d.delete(phantom_id)
astra.OpTomo 把投影器包装成 SciPy 的 LinearOperator:W * P 是正投影,W.T 对应反投影,因此能直接传给 scipy.sparse.linalg.lsqr 这类迭代求解器,而不用显式构造矩阵。注意 W * P 返回的是一维向量,示例里手动 reshape 成了 [180, 384]。
3. direct_FP / direct_BP:直接作用在张量上(2.5.0 新增)
官方文档给出的接口:
python
astra.projector.direct_FP(projector_id, volume_array, out=projection_array)
astra.projector.direct_BP(projector_id, projection_array, out=volume_array)
按 2.5.0 的发布说明和文档:这两个函数不需要再创建 ASTRA 的数据对象和算法对象,直接接受支持 DLPack 标准的数组(NumPy、PyTorch、JAX 等),2D 投影器的 DLPack 支持也是 2.5.0 才加上的。GPU 上的张量只能配 'cuda' 投影器;CPU 数组可以配任何投影器。输出数组必须用关键字参数 out= 传入。
对做深度学习 CT 的人来说,这意味着正投影、反投影可以直接作用在 GPU 张量上,省掉来回拷贝。但 ASTRA 本身不提供自动求导 :要让梯度穿过投影算子,需要把 direct_FP 作为前向、direct_BP 作为反向,自己封装成框架里的自定义算子,或者使用已经封装好的上层库。
九、最容易踩的坑
- CPU 版没有 3D,也没有
cuda投影器。 报Unknown Projector2D type或Unknown algorithm type,先检查astra.use_cuda()。 - CPU 算法必须传
ProjectorId。 漏了它,algorithm.create这一步就会报错;GPU 算法则不需要。 - 数组形状。 2D 正弦图是(角度,探测器),3D 投影是(探测器行,角度,探测器列),3D 体数据是(切片,行,列)。
- 角度单位是弧度。 平行束扫 [ 0 , π ) [0, \pi) [0,π) 时用
endpoint=False,避免 0 和 π \pi π 两个方向重复。 - 长度单位。 默认一个像素边长为 1,探测器间距按像素计;锥束里
origin_detector_distance设成 0 时,探测器尺寸要换算到旋转中心处。 - 对象要手动释放。 数据对象占内存,算法对象可能还占着显存,用完
delete。 - 生成数据和重建用的模型要一致。 用
strip生成仿真数据、再用cuda(即linear)重建,等于人为引入了模型不一致,评估结果会受影响。
十、小结
| 适合 | 不适合 |
|---|---|
| 需要快速的 2D/3D 正投影、反投影 | 想要现成的 TV、PnP 等正则化重建 |
锥束、C 形臂等非标准几何(vec 系列) |
想要开箱即用的自动求导 |
| 拿 FBP、FDK、SIRT、CGLS 当基线 | 没有 NVIDIA GPU 却要做 3D |
| 小规模实验时导出系统矩阵 | 闭源商业产品(GPLv3) |
所以 ASTRA 更像一块底层砖:几何和投影交给它,上面的优化、正则化和网络自己搭,或者交给建立在它之上的框架。基础系列后面会讲的 ODL,就是用"算子"的抽象把 ASTRA 这类后端包装起来,LPD 的代码正是这样写的。
下一篇按排期回到论文精读:RED-CNN,2017 年低剂量 CT 去噪的经典网络,正好接上(四)讲过的低剂量噪声模型和 PSNR / SSIM。
ASTRA 和其他 CT 工具包、论文和数据集,我整理在了一个开源列表里,可以按方向筛选、支持中文搜索:
参考文献
- van Aarle W, Palenstijn W J, Cant J, et al. Fast and flexible X-ray tomography using the ASTRA toolbox. Optics Express, 2016, 24(22): 25129--25147.
- ASTRA Toolbox 官方文档(2.5.0):https://www.astra-toolbox.com/docs/
如果这篇对你有帮助,欢迎收藏,有问题可以在评论区讨论。