diffct 2.0.0__tar.gz → 2.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {diffct-2.0.0 → diffct-2.0.2}/CHANGELOG.md +51 -1
- {diffct-2.0.0 → diffct-2.0.2}/CITATION.cff +1 -1
- {diffct-2.0.0 → diffct-2.0.2}/PKG-INFO +32 -29
- {diffct-2.0.0 → diffct-2.0.2}/README.md +31 -28
- {diffct-2.0.0 → diffct-2.0.2}/diffct/__init__.py +1 -1
- {diffct-2.0.0 → diffct-2.0.2}/diffct/analytical.py +4 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/geometry.py +15 -2
- {diffct-2.0.0 → diffct-2.0.2}/diffct/operators.py +9 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/projectors.py +49 -48
- {diffct-2.0.0 → diffct-2.0.2}/diffct/utils.py +69 -0
- diffct-2.0.2/docs/video/README.md +47 -0
- {diffct-2.0.0 → diffct-2.0.2}/examples/README.md +6 -72
- {diffct-2.0.0 → diffct-2.0.2}/examples/data/NOTICE +4 -4
- {diffct-2.0.0 → diffct-2.0.2}/formal/README.md +1 -1
- {diffct-2.0.0 → diffct-2.0.2}/pyproject.toml +1 -1
- {diffct-2.0.0 → diffct-2.0.2}/tests/benchmarks/README.md +1 -1
- diffct-2.0.2/tests/test_cuda_exit_deallocations.py +83 -0
- diffct-2.0.2/tests/test_multi_gpu_exit.py +88 -0
- diffct-2.0.2/tests/test_release_2_0_1.py +326 -0
- diffct-2.0.0/docs/video/README.md +0 -22
- {diffct-2.0.0 → diffct-2.0.2}/.gitignore +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/LICENSE +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/NOTICE +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/constants.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/differentiable.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/kernels/__init__.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/kernels/cone_beam.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/kernels/fan_beam.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/kernels/geometry_vjp.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/diffct/kernels/parallel_beam.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/pytest.ini +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/__init__.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/_phantoms.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/benchmarks/__init__.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/benchmarks/_common.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/benchmarks/test_bench_cone.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/benchmarks/test_bench_fan.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/benchmarks/test_bench_parallel.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/distributed_projector_check.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_adjoint_inner_product.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_analytical_regressions.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_cone_projector_autograd.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_cuda_smoke.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_fbp_fan_accuracy.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_fbp_fan_offsets.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_fbp_parallel_accuracy.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_fdk_cone_accuracy.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_fdk_cone_offsets.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_geometry_autograd.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_geometry_gradients.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_gradcheck.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_projector_api.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_projector_degenerate_geometry.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_projector_geometry_devices.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_ramp_filter_windows.py +0 -0
- {diffct-2.0.0 → diffct-2.0.2}/tests/test_weights.py +0 -0
|
@@ -5,6 +5,56 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.2] - 2026-10-09
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- The exit-time cleanup from 2.0.1 set numba-cuda's pending-deallocation
|
|
13
|
+
ratio to infinity. numba-cuda converts ``capacity * ratio`` to ``int``, so
|
|
14
|
+
each deallocation queued during shutdown raised ``ValueError`` or
|
|
15
|
+
``OverflowError``. The exit code stayed 0, but stderr showed the error. The
|
|
16
|
+
exit hook now disables numba-cuda's pending-deallocation queues instead of
|
|
17
|
+
changing the limits, so no queue is flushed at exit and no error is raised.
|
|
18
|
+
The hook no longer imports ``numba.cuda.core.config``, which older
|
|
19
|
+
numba-cuda releases do not have.
|
|
20
|
+
|
|
21
|
+
### Documentation
|
|
22
|
+
|
|
23
|
+
- The install instructions state that ``pip install diffct`` without a CUDA
|
|
24
|
+
extra needs a system CUDA Toolkit. Use ``diffct[cu12]`` or ``diffct[cu13]``.
|
|
25
|
+
- README images and links use absolute URLs, so they also work on PyPI.
|
|
26
|
+
|
|
27
|
+
## [2.0.1] - 2026-10-09
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- The low-level projector and backprojector Functions and the analytical
|
|
32
|
+
weighted backprojections run in the CUDA context of their input. Before,
|
|
33
|
+
inputs on another GPU than the current device could use the wrong context
|
|
34
|
+
and stream.
|
|
35
|
+
- A process that ran diffct kernels on several GPUs no longer crashes at
|
|
36
|
+
interpreter exit: numba-cuda's flush of pending CUDA module unloads during
|
|
37
|
+
shutdown is skipped, and process exit releases the modules.
|
|
38
|
+
- ``custom_trajectory_3d`` and ``custom_trajectory_2d_fan`` raise
|
|
39
|
+
``ValueError`` for a source at the isocenter or a non-finite source,
|
|
40
|
+
instead of returning NaN geometry. The figure-8 example in the
|
|
41
|
+
``custom_trajectory_3d`` docstring no longer puts a source at the origin.
|
|
42
|
+
- The class docstring examples of the six projector Functions use the
|
|
43
|
+
per-view trajectory API.
|
|
44
|
+
- ``_trig_tables`` accepts NumPy dtypes for tensor input.
|
|
45
|
+
- Walnut example: ``preprocess_walnut.py`` raises when projection archives or
|
|
46
|
+
views are missing or an air level is not positive, instead of writing NaN
|
|
47
|
+
data. A non-default crop keeps the detector centre offset, and
|
|
48
|
+
``walnut_reconstruction.py`` applies it to the geometry and the FDK
|
|
49
|
+
weights. The default crop has no offset.
|
|
50
|
+
- Examples: ``psnr`` returns infinity for identical images; distributed
|
|
51
|
+
reductions in CGLS, TV and the noise peak use the operator's process
|
|
52
|
+
group.
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- ``Projector.process_group``: the process group of a distributed operator.
|
|
57
|
+
|
|
8
58
|
## [2.0.0] - 2026-10-09
|
|
9
59
|
|
|
10
60
|
Version 2.0.0 replaces the circular-orbit API of 1.x with arbitrary
|
|
@@ -28,7 +78,7 @@ per-view trajectories. It breaks the 1.x interface; see
|
|
|
28
78
|
- Install extras ``diffct[cu12]`` and ``diffct[cu13]`` pull in the matching
|
|
29
79
|
numba-cuda CUDA bindings, so one ``pip install`` gives a working import.
|
|
30
80
|
- Measured walnut example (``examples/walnut_reconstruction.py``), intro video
|
|
31
|
-
and README figures
|
|
81
|
+
and README figures.
|
|
32
82
|
- Geometry gradients. Trajectory tensors may require gradients; ``Projector``
|
|
33
83
|
and the low-level Function classes return gradients for source, detector
|
|
34
84
|
centre and detector axes (parallel: ray direction, detector origin and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: diffct
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Differentiable CUDA projectors for CT: arbitrary trajectories, multi-GPU and multi-node execution, geometry gradients
|
|
5
5
|
Project-URL: Homepage, https://github.com/sypsyp97/diffct
|
|
6
6
|
Project-URL: Documentation, https://sypsyp97.github.io/diffct/
|
|
@@ -31,7 +31,7 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
<h1 align="center">diffct</h1>
|
|
33
33
|
|
|
34
|
-
<p align="center">English · <a href="README.zh.md">简体中文</a></p>
|
|
34
|
+
<p align="center">English · <a href="https://github.com/sypsyp97/diffct/blob/main/README.zh.md">简体中文</a></p>
|
|
35
35
|
|
|
36
36
|
<p align="center">
|
|
37
37
|
Differentiable CUDA projectors for CT: arbitrary trajectories, multi-GPU, multi-node, geometry gradients.
|
|
@@ -43,15 +43,15 @@ Description-Content-Type: text/markdown
|
|
|
43
43
|
<a href="https://pypi.org/project/diffct/"><img src="https://img.shields.io/pypi/v/diffct.svg?style=flat-square&logo=pypi&logoColor=white" alt="PyPI version"></a>
|
|
44
44
|
<a href="https://sypsyp97.github.io/diffct/"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/docs.yml?branch=main&label=docs&style=flat-square" alt="Documentation"></a>
|
|
45
45
|
<a href="https://github.com/sypsyp97/diffct/actions"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/ci.yml?branch=main&label=CI&style=flat-square" alt="CI/CD"></a>
|
|
46
|
-
<a href="https://deepwiki.com/sypsyp97/diffct"><img src="docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
|
|
46
|
+
<a href="https://deepwiki.com/sypsyp97/diffct"><img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
|
|
47
47
|
</p>
|
|
48
48
|
|
|
49
49
|
<p align="center">
|
|
50
|
-
<img src="docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
|
|
50
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
|
|
51
51
|
</p>
|
|
52
52
|
|
|
53
53
|
<p align="center">
|
|
54
|
-
<a href="docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
|
|
54
|
+
<a href="https://github.com/sypsyp97/diffct/blob/main/docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
|
|
55
55
|
<a href="https://www.preprints.org/manuscript/202605.1446/v1">Technical report</a> ·
|
|
56
56
|
<a href="https://doi.org/10.20944/preprints202605.1446.v1">DOI</a>
|
|
57
57
|
</p>
|
|
@@ -65,7 +65,9 @@ Description-Content-Type: text/markdown
|
|
|
65
65
|
<a href="#citation">Citation</a>
|
|
66
66
|
</p>
|
|
67
67
|
|
|
68
|
-
> **
|
|
68
|
+
> **Install** with the CUDA extra, `pip install "diffct[cu12]"` or `pip install "diffct[cu13]"`, after PyTorch. A plain `pip install diffct` needs a system CUDA Toolkit.
|
|
69
|
+
>
|
|
70
|
+
> **Version 2.0** replaces the circular-orbit API of 1.x with per-view trajectories. Code written for 1.x needs changes; see the [migration notes](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md).
|
|
69
71
|
> For Apple Silicon, use the [MLX port by Linda-Sophie Schneider](https://github.com/Linda-SophieSchneider/DiffCT-MLX).
|
|
70
72
|
|
|
71
73
|
## Why diffct
|
|
@@ -76,18 +78,20 @@ To our knowledge, diffct is the only open-source GPU CT library that combines ar
|
|
|
76
78
|
- **Matched operators.** `project()` and `backproject()` form an exact adjoint pair for the cell-constant Siddon model. Both support PyTorch autograd, with volume/sinogram gradients and Hessian-vector products.
|
|
77
79
|
- **Multi-GPU and multi-node.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun and NCCL. Views are partitioned and the volume is replicated. Speedup depends on workload and communication costs.
|
|
78
80
|
- **Analytical helpers.** `diffct.analytical` provides ramp filters (ram-lak, shepp-logan, cosine, hamming, hann), fan, cone and Parker weights, and FBP and FDK backprojection.
|
|
79
|
-
- **Validated.** FDK matches ASTRA 2.5.0 `FDK_CUDA`: PSNR within 0.3 dB. Geometry gradients match an independent float64 reference to about 1e-6. The [recorded A100 validation](docs/VALIDATION.md) reports 229 passing pytest tests. Full notes: [REFERENCE](docs/REFERENCE.md#validation-summary).
|
|
80
81
|
|
|
81
|
-
Capabilities, limits and isocenter rules: [docs/REFERENCE.md](docs/REFERENCE.md#capabilities-and-limits).
|
|
82
|
+
Capabilities, limits and isocenter rules: [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#capabilities-and-limits).
|
|
82
83
|
|
|
83
84
|
## Install
|
|
84
85
|
|
|
85
|
-
You need
|
|
86
|
+
You need an NVIDIA GPU. Install PyTorch for your CUDA version first. Then install diffct with the extra for the same CUDA major version:
|
|
86
87
|
|
|
87
88
|
```bash
|
|
88
|
-
pip install "diffct[cu12]" #
|
|
89
|
+
pip install "diffct[cu12]" # PyTorch built for CUDA 12
|
|
90
|
+
pip install "diffct[cu13]" # PyTorch built for CUDA 13
|
|
89
91
|
```
|
|
90
92
|
|
|
93
|
+
The extra installs the CUDA compiler libraries that Numba CUDA needs to compile the kernels. A plain `pip install diffct` does not install them. Use it only if a CUDA Toolkit is already installed on the system.
|
|
94
|
+
|
|
91
95
|
From source, with the examples:
|
|
92
96
|
|
|
93
97
|
```bash
|
|
@@ -130,11 +134,11 @@ loss = 0.5 * (A.project(x) - sinogram).square().sum()
|
|
|
130
134
|
loss.backward() # x.grad = A^T (A x - y)
|
|
131
135
|
```
|
|
132
136
|
|
|
133
|
-
**Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](docs/REFERENCE.md#custom-or-calibrated-trajectories).
|
|
137
|
+
**Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#custom-or-calibrated-trajectories).
|
|
134
138
|
|
|
135
|
-
**Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](docs/REFERENCE.md#geometry-gradients).
|
|
139
|
+
**Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#geometry-gradients).
|
|
136
140
|
|
|
137
|
-
**Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](docs/DISTRIBUTED.md).
|
|
141
|
+
**Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md).
|
|
138
142
|
|
|
139
143
|
```python
|
|
140
144
|
A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, devices=[0, 1, 2, 3])
|
|
@@ -144,8 +148,8 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
|
|
|
144
148
|
|
|
145
149
|
<p align="center">
|
|
146
150
|
<picture>
|
|
147
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/scaling_dark.png">
|
|
148
|
-
<img src="docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
|
|
151
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png">
|
|
152
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
|
|
149
153
|
</picture>
|
|
150
154
|
</p>
|
|
151
155
|
|
|
@@ -155,7 +159,7 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
|
|
|
155
159
|
| Adjoint (backprojection) | 20.66 ms | 5.89 ms (3.5×) | 4.62 ms (4.5×) |
|
|
156
160
|
| CGLS iteration | 30.08 ms | 8.08 ms (3.7×) | 5.59 ms (5.4×) |
|
|
157
161
|
|
|
158
|
-
Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB
|
|
162
|
+
Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB GPUs. Small volumes scale less: at 64³, one CGLS iteration takes 4.63 ms on 1 GPU, 1.57 ms on 4 and 1.56 ms on 8. Raw data: [docs/assets/scaling.json](https://github.com/sypsyp97/diffct/blob/main/docs/assets/scaling.json).
|
|
159
163
|
|
|
160
164
|
To measure one against several GPUs on your machine (default scan of the script, not the table setup):
|
|
161
165
|
|
|
@@ -168,7 +172,7 @@ python examples/benchmark_projector.py --devices 0 1
|
|
|
168
172
|
**Measured walnut.** 240 measured views, circular cone beam, 256³: FDK (Hann window), SIRT (200 iterations), CGLS (20), TV (300, weight 0.3); axial and coronal centre slices.
|
|
169
173
|
|
|
170
174
|
<p align="center">
|
|
171
|
-
<img src="docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
|
|
175
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
|
|
172
176
|
</p>
|
|
173
177
|
|
|
174
178
|
```bash
|
|
@@ -178,7 +182,7 @@ python examples/walnut_reconstruction.py --figure out.png
|
|
|
178
182
|
**Simulated helical scan of the walnut.** The FDK walnut volume as ground truth, 720 views, 1% noise, 256³. PSNR: FDK 26.67 dB, CGLS (30 iterations) 34.13 dB, SIRT (200) 34.52 dB, TV (200, weight 1.0) 37.77 dB.
|
|
179
183
|
|
|
180
184
|
<p align="center">
|
|
181
|
-
<img src="docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
|
|
185
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
|
|
182
186
|
</p>
|
|
183
187
|
|
|
184
188
|
```bash
|
|
@@ -186,23 +190,22 @@ python examples/walnut_reconstruction.py --algorithms --save-volume walnut256.np
|
|
|
186
190
|
python examples/iterative_reconstruction.py --size 256 --views 720 --trajectory helical --noise 0.01 --phantom walnut256.npy --figure out.png
|
|
187
191
|
```
|
|
188
192
|
|
|
189
|
-
Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](examples/data/NOTICE).
|
|
193
|
+
Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](https://github.com/sypsyp97/diffct/blob/main/examples/data/NOTICE).
|
|
190
194
|
|
|
191
195
|
## Examples
|
|
192
196
|
|
|
193
|
-
`quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes
|
|
197
|
+
`quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes and distributed-loss rules are in [examples/README.md](https://github.com/sypsyp97/diffct/blob/main/examples/README.md).
|
|
194
198
|
|
|
195
199
|
## Documentation
|
|
196
200
|
|
|
197
201
|
| Document | Contents |
|
|
198
202
|
|---|---|
|
|
199
|
-
| [Trajectory guide](docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
|
|
200
|
-
| [docs/REFERENCE.md](docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
|
|
201
|
-
| [docs/DISTRIBUTED.md](docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
|
|
202
|
-
| [docs/
|
|
203
|
-
| [docs/
|
|
204
|
-
| [
|
|
205
|
-
| [CHANGELOG.md](CHANGELOG.md) | Release history |
|
|
203
|
+
| [Trajectory guide](https://github.com/sypsyp97/diffct/blob/main/docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
|
|
204
|
+
| [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
|
|
205
|
+
| [docs/DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
|
|
206
|
+
| [docs/MIGRATION.md](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md) | Moving from the circular-only API |
|
|
207
|
+
| [docs/video/README.md](https://github.com/sypsyp97/diffct/blob/main/docs/video/README.md) | How the intro video is rendered |
|
|
208
|
+
| [CHANGELOG.md](https://github.com/sypsyp97/diffct/blob/main/CHANGELOG.md) | Release history |
|
|
206
209
|
|
|
207
210
|
Run the test suite on a CUDA host with `python -m pytest tests/ -q`.
|
|
208
211
|
|
|
@@ -234,7 +237,7 @@ If you use this library in your research, please cite the software and the techn
|
|
|
234
237
|
|
|
235
238
|
## License and acknowledgements
|
|
236
239
|
|
|
237
|
-
Apache 2.0, see [LICENSE](LICENSE). The project draws on
|
|
240
|
+
Apache 2.0, see [LICENSE](https://github.com/sypsyp97/diffct/blob/main/LICENSE). The project draws on
|
|
238
241
|
[PYRO-NN](https://github.com/csyben/PYRO-NN) and
|
|
239
242
|
[geometry_gradients_CT](https://github.com/mareikethies/geometry_gradients_CT).
|
|
240
|
-
Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](NOTICE).
|
|
243
|
+
Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](https://github.com/sypsyp97/diffct/blob/main/NOTICE).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h1 align="center">diffct</h1>
|
|
2
2
|
|
|
3
|
-
<p align="center">English · <a href="README.zh.md">简体中文</a></p>
|
|
3
|
+
<p align="center">English · <a href="https://github.com/sypsyp97/diffct/blob/main/README.zh.md">简体中文</a></p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
Differentiable CUDA projectors for CT: arbitrary trajectories, multi-GPU, multi-node, geometry gradients.
|
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
<a href="https://pypi.org/project/diffct/"><img src="https://img.shields.io/pypi/v/diffct.svg?style=flat-square&logo=pypi&logoColor=white" alt="PyPI version"></a>
|
|
13
13
|
<a href="https://sypsyp97.github.io/diffct/"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/docs.yml?branch=main&label=docs&style=flat-square" alt="Documentation"></a>
|
|
14
14
|
<a href="https://github.com/sypsyp97/diffct/actions"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/ci.yml?branch=main&label=CI&style=flat-square" alt="CI/CD"></a>
|
|
15
|
-
<a href="https://deepwiki.com/sypsyp97/diffct"><img src="docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
|
|
15
|
+
<a href="https://deepwiki.com/sypsyp97/diffct"><img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
19
|
-
<img src="docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
|
|
19
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
|
|
20
20
|
</p>
|
|
21
21
|
|
|
22
22
|
<p align="center">
|
|
23
|
-
<a href="docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
|
|
23
|
+
<a href="https://github.com/sypsyp97/diffct/blob/main/docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
|
|
24
24
|
<a href="https://www.preprints.org/manuscript/202605.1446/v1">Technical report</a> ·
|
|
25
25
|
<a href="https://doi.org/10.20944/preprints202605.1446.v1">DOI</a>
|
|
26
26
|
</p>
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
<a href="#citation">Citation</a>
|
|
35
35
|
</p>
|
|
36
36
|
|
|
37
|
-
> **
|
|
37
|
+
> **Install** with the CUDA extra, `pip install "diffct[cu12]"` or `pip install "diffct[cu13]"`, after PyTorch. A plain `pip install diffct` needs a system CUDA Toolkit.
|
|
38
|
+
>
|
|
39
|
+
> **Version 2.0** replaces the circular-orbit API of 1.x with per-view trajectories. Code written for 1.x needs changes; see the [migration notes](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md).
|
|
38
40
|
> For Apple Silicon, use the [MLX port by Linda-Sophie Schneider](https://github.com/Linda-SophieSchneider/DiffCT-MLX).
|
|
39
41
|
|
|
40
42
|
## Why diffct
|
|
@@ -45,18 +47,20 @@ To our knowledge, diffct is the only open-source GPU CT library that combines ar
|
|
|
45
47
|
- **Matched operators.** `project()` and `backproject()` form an exact adjoint pair for the cell-constant Siddon model. Both support PyTorch autograd, with volume/sinogram gradients and Hessian-vector products.
|
|
46
48
|
- **Multi-GPU and multi-node.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun and NCCL. Views are partitioned and the volume is replicated. Speedup depends on workload and communication costs.
|
|
47
49
|
- **Analytical helpers.** `diffct.analytical` provides ramp filters (ram-lak, shepp-logan, cosine, hamming, hann), fan, cone and Parker weights, and FBP and FDK backprojection.
|
|
48
|
-
- **Validated.** FDK matches ASTRA 2.5.0 `FDK_CUDA`: PSNR within 0.3 dB. Geometry gradients match an independent float64 reference to about 1e-6. The [recorded A100 validation](docs/VALIDATION.md) reports 229 passing pytest tests. Full notes: [REFERENCE](docs/REFERENCE.md#validation-summary).
|
|
49
50
|
|
|
50
|
-
Capabilities, limits and isocenter rules: [docs/REFERENCE.md](docs/REFERENCE.md#capabilities-and-limits).
|
|
51
|
+
Capabilities, limits and isocenter rules: [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#capabilities-and-limits).
|
|
51
52
|
|
|
52
53
|
## Install
|
|
53
54
|
|
|
54
|
-
You need
|
|
55
|
+
You need an NVIDIA GPU. Install PyTorch for your CUDA version first. Then install diffct with the extra for the same CUDA major version:
|
|
55
56
|
|
|
56
57
|
```bash
|
|
57
|
-
pip install "diffct[cu12]" #
|
|
58
|
+
pip install "diffct[cu12]" # PyTorch built for CUDA 12
|
|
59
|
+
pip install "diffct[cu13]" # PyTorch built for CUDA 13
|
|
58
60
|
```
|
|
59
61
|
|
|
62
|
+
The extra installs the CUDA compiler libraries that Numba CUDA needs to compile the kernels. A plain `pip install diffct` does not install them. Use it only if a CUDA Toolkit is already installed on the system.
|
|
63
|
+
|
|
60
64
|
From source, with the examples:
|
|
61
65
|
|
|
62
66
|
```bash
|
|
@@ -99,11 +103,11 @@ loss = 0.5 * (A.project(x) - sinogram).square().sum()
|
|
|
99
103
|
loss.backward() # x.grad = A^T (A x - y)
|
|
100
104
|
```
|
|
101
105
|
|
|
102
|
-
**Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](docs/REFERENCE.md#custom-or-calibrated-trajectories).
|
|
106
|
+
**Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#custom-or-calibrated-trajectories).
|
|
103
107
|
|
|
104
|
-
**Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](docs/REFERENCE.md#geometry-gradients).
|
|
108
|
+
**Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#geometry-gradients).
|
|
105
109
|
|
|
106
|
-
**Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](docs/DISTRIBUTED.md).
|
|
110
|
+
**Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md).
|
|
107
111
|
|
|
108
112
|
```python
|
|
109
113
|
A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, devices=[0, 1, 2, 3])
|
|
@@ -113,8 +117,8 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
|
|
|
113
117
|
|
|
114
118
|
<p align="center">
|
|
115
119
|
<picture>
|
|
116
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/scaling_dark.png">
|
|
117
|
-
<img src="docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
|
|
120
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png">
|
|
121
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
|
|
118
122
|
</picture>
|
|
119
123
|
</p>
|
|
120
124
|
|
|
@@ -124,7 +128,7 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
|
|
|
124
128
|
| Adjoint (backprojection) | 20.66 ms | 5.89 ms (3.5×) | 4.62 ms (4.5×) |
|
|
125
129
|
| CGLS iteration | 30.08 ms | 8.08 ms (3.7×) | 5.59 ms (5.4×) |
|
|
126
130
|
|
|
127
|
-
Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB
|
|
131
|
+
Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB GPUs. Small volumes scale less: at 64³, one CGLS iteration takes 4.63 ms on 1 GPU, 1.57 ms on 4 and 1.56 ms on 8. Raw data: [docs/assets/scaling.json](https://github.com/sypsyp97/diffct/blob/main/docs/assets/scaling.json).
|
|
128
132
|
|
|
129
133
|
To measure one against several GPUs on your machine (default scan of the script, not the table setup):
|
|
130
134
|
|
|
@@ -137,7 +141,7 @@ python examples/benchmark_projector.py --devices 0 1
|
|
|
137
141
|
**Measured walnut.** 240 measured views, circular cone beam, 256³: FDK (Hann window), SIRT (200 iterations), CGLS (20), TV (300, weight 0.3); axial and coronal centre slices.
|
|
138
142
|
|
|
139
143
|
<p align="center">
|
|
140
|
-
<img src="docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
|
|
144
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
|
|
141
145
|
</p>
|
|
142
146
|
|
|
143
147
|
```bash
|
|
@@ -147,7 +151,7 @@ python examples/walnut_reconstruction.py --figure out.png
|
|
|
147
151
|
**Simulated helical scan of the walnut.** The FDK walnut volume as ground truth, 720 views, 1% noise, 256³. PSNR: FDK 26.67 dB, CGLS (30 iterations) 34.13 dB, SIRT (200) 34.52 dB, TV (200, weight 1.0) 37.77 dB.
|
|
148
152
|
|
|
149
153
|
<p align="center">
|
|
150
|
-
<img src="docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
|
|
154
|
+
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
|
|
151
155
|
</p>
|
|
152
156
|
|
|
153
157
|
```bash
|
|
@@ -155,23 +159,22 @@ python examples/walnut_reconstruction.py --algorithms --save-volume walnut256.np
|
|
|
155
159
|
python examples/iterative_reconstruction.py --size 256 --views 720 --trajectory helical --noise 0.01 --phantom walnut256.npy --figure out.png
|
|
156
160
|
```
|
|
157
161
|
|
|
158
|
-
Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](examples/data/NOTICE).
|
|
162
|
+
Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](https://github.com/sypsyp97/diffct/blob/main/examples/data/NOTICE).
|
|
159
163
|
|
|
160
164
|
## Examples
|
|
161
165
|
|
|
162
|
-
`quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes
|
|
166
|
+
`quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes and distributed-loss rules are in [examples/README.md](https://github.com/sypsyp97/diffct/blob/main/examples/README.md).
|
|
163
167
|
|
|
164
168
|
## Documentation
|
|
165
169
|
|
|
166
170
|
| Document | Contents |
|
|
167
171
|
|---|---|
|
|
168
|
-
| [Trajectory guide](docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
|
|
169
|
-
| [docs/REFERENCE.md](docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
|
|
170
|
-
| [docs/DISTRIBUTED.md](docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
|
|
171
|
-
| [docs/
|
|
172
|
-
| [docs/
|
|
173
|
-
| [
|
|
174
|
-
| [CHANGELOG.md](CHANGELOG.md) | Release history |
|
|
172
|
+
| [Trajectory guide](https://github.com/sypsyp97/diffct/blob/main/docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
|
|
173
|
+
| [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
|
|
174
|
+
| [docs/DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
|
|
175
|
+
| [docs/MIGRATION.md](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md) | Moving from the circular-only API |
|
|
176
|
+
| [docs/video/README.md](https://github.com/sypsyp97/diffct/blob/main/docs/video/README.md) | How the intro video is rendered |
|
|
177
|
+
| [CHANGELOG.md](https://github.com/sypsyp97/diffct/blob/main/CHANGELOG.md) | Release history |
|
|
175
178
|
|
|
176
179
|
Run the test suite on a CUDA host with `python -m pytest tests/ -q`.
|
|
177
180
|
|
|
@@ -203,7 +206,7 @@ If you use this library in your research, please cite the software and the techn
|
|
|
203
206
|
|
|
204
207
|
## License and acknowledgements
|
|
205
208
|
|
|
206
|
-
Apache 2.0, see [LICENSE](LICENSE). The project draws on
|
|
209
|
+
Apache 2.0, see [LICENSE](https://github.com/sypsyp97/diffct/blob/main/LICENSE). The project draws on
|
|
207
210
|
[PYRO-NN](https://github.com/csyben/PYRO-NN) and
|
|
208
211
|
[geometry_gradients_CT](https://github.com/mareikethies/geometry_gradients_CT).
|
|
209
|
-
Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](NOTICE).
|
|
212
|
+
Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](https://github.com/sypsyp97/diffct/blob/main/NOTICE).
|
|
@@ -34,6 +34,7 @@ from .utils import (
|
|
|
34
34
|
_get_numba_external_stream_for,
|
|
35
35
|
_grid_2d,
|
|
36
36
|
_grid_3d,
|
|
37
|
+
_on_device_of,
|
|
37
38
|
)
|
|
38
39
|
from .kernels import (
|
|
39
40
|
_parallel_2d_fbp_backproject_kernel,
|
|
@@ -365,6 +366,7 @@ def _as_contig_f32(t, device):
|
|
|
365
366
|
return DeviceManager.ensure_device(t, device).to(dtype=torch.float32).contiguous()
|
|
366
367
|
|
|
367
368
|
|
|
369
|
+
@_on_device_of("sinogram")
|
|
368
370
|
def parallel_weighted_backproject(sinogram, ray_dir, det_origin, det_u_vec,
|
|
369
371
|
detector_spacing, H, W, voxel_spacing=1.0):
|
|
370
372
|
"""Voxel-driven parallel-beam FBP backprojection with analytical constant.
|
|
@@ -448,6 +450,7 @@ def _analytical_geometry(src_pos, det_center, n, isocenter):
|
|
|
448
450
|
return isocenter, float(sid_n.mean().item()), float(sdd_n.mean().item())
|
|
449
451
|
|
|
450
452
|
|
|
453
|
+
@_on_device_of("sinogram")
|
|
451
454
|
def fan_weighted_backproject(sinogram, src_pos, det_center, det_u_vec,
|
|
452
455
|
detector_spacing, H, W, voxel_spacing=1.0, *, isocenter=None):
|
|
453
456
|
"""Voxel-driven fan-beam FBP backprojection with analytical constant.
|
|
@@ -503,6 +506,7 @@ def fan_weighted_backproject(sinogram, src_pos, det_center, det_u_vec,
|
|
|
503
506
|
return reco * scale
|
|
504
507
|
|
|
505
508
|
|
|
509
|
+
@_on_device_of("sinogram")
|
|
506
510
|
def cone_weighted_backproject(sinogram, src_pos, det_center, det_u_vec, det_v_vec,
|
|
507
511
|
D, H, W, du, dv, voxel_spacing=1.0, *, isocenter=None):
|
|
508
512
|
"""Voxel-driven cone-beam FDK backprojection with analytical constant.
|
|
@@ -13,6 +13,17 @@ from typing import Callable, Tuple
|
|
|
13
13
|
# Trajectory Generation Functions
|
|
14
14
|
# ============================================================================
|
|
15
15
|
|
|
16
|
+
def _check_source_positions(src_pos):
|
|
17
|
+
"""Reject non-finite sources and sources at the isocenter, which have no viewing direction."""
|
|
18
|
+
if not torch.isfinite(src_pos).all():
|
|
19
|
+
raise ValueError("source_path_fn returned non-finite source positions")
|
|
20
|
+
if (torch.linalg.vector_norm(src_pos.detach(), dim=1) == 0).any():
|
|
21
|
+
raise ValueError(
|
|
22
|
+
"source_path_fn returned a source at the isocenter; every source needs a "
|
|
23
|
+
"nonzero distance from the origin"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
16
27
|
def circular_trajectory_3d(n_views, sid, sdd, start_angle=0.0, end_angle=None, device='cuda', dtype=torch.float32):
|
|
17
28
|
"""Generate circular trajectory geometry for cone-beam CT.
|
|
18
29
|
|
|
@@ -500,11 +511,11 @@ def custom_trajectory_3d(n_views, sid, sdd,
|
|
|
500
511
|
|
|
501
512
|
Examples
|
|
502
513
|
--------
|
|
503
|
-
>>> #
|
|
514
|
+
>>> # Circular orbit with a two-lobe (figure-8) vertical motion
|
|
504
515
|
>>> def figure8_path(angles, sid):
|
|
505
516
|
... src_pos = torch.zeros((len(angles), 3), device=angles.device, dtype=angles.dtype)
|
|
506
517
|
... src_pos[:, 0] = -sid * torch.sin(angles)
|
|
507
|
-
... src_pos[:, 1] = sid * torch.cos(angles)
|
|
518
|
+
... src_pos[:, 1] = sid * torch.cos(angles)
|
|
508
519
|
... src_pos[:, 2] = 50 * torch.sin(2 * angles)
|
|
509
520
|
... return src_pos
|
|
510
521
|
>>>
|
|
@@ -523,6 +534,7 @@ def custom_trajectory_3d(n_views, sid, sdd,
|
|
|
523
534
|
if src_pos.shape != (n_views, 3):
|
|
524
535
|
raise ValueError(f"source_path_fn must return tensor of shape ({n_views}, 3), "
|
|
525
536
|
f"got {src_pos.shape}")
|
|
537
|
+
_check_source_positions(src_pos)
|
|
526
538
|
|
|
527
539
|
# Keep the entire frame in the source-path autograd graph. Constructing
|
|
528
540
|
# tensors from scalar tensors detaches them; rewriting view slices in
|
|
@@ -750,6 +762,7 @@ def custom_trajectory_2d_fan(n_views, sid, sdd,
|
|
|
750
762
|
if src_pos.shape != (n_views, 2):
|
|
751
763
|
raise ValueError(f"source_path_fn must return tensor of shape ({n_views}, 2), "
|
|
752
764
|
f"got {src_pos.shape}")
|
|
765
|
+
_check_source_positions(src_pos)
|
|
753
766
|
|
|
754
767
|
# Preallocate remaining position matrices
|
|
755
768
|
det_center = torch.zeros((n_views, 2), device=device, dtype=dtype)
|
|
@@ -523,6 +523,15 @@ class Projector:
|
|
|
523
523
|
*self.detector_spacing, self.voxel_spacing,
|
|
524
524
|
)
|
|
525
525
|
|
|
526
|
+
@property
|
|
527
|
+
def process_group(self):
|
|
528
|
+
"""Process group of a distributed operator, or None for the default group.
|
|
529
|
+
|
|
530
|
+
Pass it as ``group=`` to collectives that combine per-rank results of this
|
|
531
|
+
operator, so they use the same ranks as the operator.
|
|
532
|
+
"""
|
|
533
|
+
return self._process_group
|
|
534
|
+
|
|
526
535
|
def project(self, volume):
|
|
527
536
|
"""Project a ``(H, W)`` or ``(D, H, W)`` CUDA tensor to float32.
|
|
528
537
|
|