diffct 1.3.2__tar.gz → 1.3.3__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-1.3.2 → diffct-1.3.3}/CHANGELOG.md +434 -363
- {diffct-1.3.2 → diffct-1.3.3}/PKG-INFO +1 -1
- {diffct-1.3.2 → diffct-1.3.3}/diffct/differentiable.py +464 -433
- {diffct-1.3.2 → diffct-1.3.3}/examples/data/NOTICE +20 -15
- {diffct-1.3.2 → diffct-1.3.3}/examples/data/preprocess_walnut.py +32 -7
- {diffct-1.3.2 → diffct-1.3.3}/examples/data/walnut_cone.npz +0 -0
- diffct-1.3.3/examples/data/walnut_reco.png +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/examples/fbp_fan.py +3 -1
- {diffct-1.3.2 → diffct-1.3.3}/examples/fbp_parallel.py +3 -1
- {diffct-1.3.2 → diffct-1.3.3}/examples/fdk_cone.py +3 -1
- {diffct-1.3.2 → diffct-1.3.3}/examples/iterative_reco_cone.py +4 -2
- {diffct-1.3.2 → diffct-1.3.3}/examples/iterative_reco_fan.py +4 -2
- {diffct-1.3.2 → diffct-1.3.3}/examples/iterative_reco_parallel.py +4 -2
- {diffct-1.3.2 → diffct-1.3.3}/examples/realdata_fbp_fan.py +10 -7
- {diffct-1.3.2 → diffct-1.3.3}/examples/realdata_fbp_parallel.py +3 -1
- {diffct-1.3.2 → diffct-1.3.3}/examples/realdata_fdk_cone.py +10 -8
- {diffct-1.3.2 → diffct-1.3.3}/examples/realdata_walnut_fdk.py +35 -22
- {diffct-1.3.2 → diffct-1.3.3}/pyproject.toml +2 -2
- diffct-1.3.2/examples/data/walnut_reco.png +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/.git +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/.github/workflows/docs.yml +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/.github/workflows/release.yml +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/.gitignore +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/LICENSE +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/README.md +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/README.zh.md +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/diffct/__init__.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/Makefile +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/_static/.gitkeep +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/api.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/conf.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/examples.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/fbp_fan_example.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/fbp_parallel_example.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/fdk_cone_example.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/getting_started.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/index.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/iterative_reco_cone_example.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/iterative_reco_fan_example.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/docs/source/iterative_reco_parallel_example.rst +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/pytest.ini +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/requirements.txt +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/benchmarks/README.md +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/benchmarks/__init__.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/benchmarks/_common.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/benchmarks/test_bench_cone.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/benchmarks/test_bench_fan.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/benchmarks/test_bench_parallel.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_adjoint_inner_product.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_cone_projector_autograd.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_cuda_smoke.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_fbp_fan_accuracy.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_fbp_fan_offsets.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_fbp_parallel_accuracy.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_fdk_cone_accuracy.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_fdk_cone_offsets.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_gradcheck.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_ramp_filter_windows.py +0 -0
- {diffct-1.3.2 → diffct-1.3.3}/tests/test_weights.py +0 -0
|
@@ -1,363 +1,434 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project are documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
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
|
-
the
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
## [1.
|
|
148
|
-
|
|
149
|
-
### Added
|
|
150
|
-
- **
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
`
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
`
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
`
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
###
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
-
|
|
255
|
-
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
to
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
- `
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
`
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.3.3] - 2026-04-23
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Kernel numerical consistency and speed.** The CUDA kernels in
|
|
12
|
+
`diffct/differentiable.py` are now consistently `float32` end to end —
|
|
13
|
+
literal constants (`0.0`, `0.5`, `1.0`, `-inf`, ...) and integer-to-float
|
|
14
|
+
casts inside the Siddon and SF kernels are routed through typed `_DTYPE`
|
|
15
|
+
helpers so numba no longer implicitly promotes intermediates to `float64`.
|
|
16
|
+
This removes a silent precision mismatch that was slowing down every
|
|
17
|
+
projector/backprojector kernel; the library runs substantially faster
|
|
18
|
+
with no change in public-API behavior.
|
|
19
|
+
- **Thread indexing and grid configuration** in the 3D cone Siddon
|
|
20
|
+
forward / backward kernels and the 2D fan SF backward kernel. The
|
|
21
|
+
`cuda.grid(...)` unpack order and the matching `_grid_2d` / `_grid_3d`
|
|
22
|
+
launch arguments are aligned so the warp-adjacent axis matches the
|
|
23
|
+
stride-1 axis of the output buffer: `(iv, iu, iview)` for 3D cone
|
|
24
|
+
(`d_sino[view, u, v]` / `d_vol[ix, iy, iz]`) and `(ix, iy)` for 2D fan
|
|
25
|
+
SF. Fixes mismatched launches between `ConeProjectorFunction`,
|
|
26
|
+
`ConeBackprojectorFunction`, `FanProjectorFunction`, and
|
|
27
|
+
`FanBackprojectorFunction` and their kernels.
|
|
28
|
+
- **Walnut rotation-axis correction** in the shipped preprocessing and
|
|
29
|
+
sample data. `examples/data/preprocess_walnut.py` now applies the
|
|
30
|
+
Zenodo record's recommended 5-raw-pixel left shift before binning and
|
|
31
|
+
cropping, records the correction in the `.npz` metadata, and the
|
|
32
|
+
shipped `examples/data/walnut_cone.npz` / `walnut_reco.png` are
|
|
33
|
+
regenerated accordingly. `examples/realdata_walnut_fdk.py` and
|
|
34
|
+
`examples/data/NOTICE` are updated to surface the correction and the
|
|
35
|
+
richer walnut metadata.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- **Examples now require CUDA explicitly.** `examples/fbp_parallel.py`,
|
|
39
|
+
`fbp_fan.py`, `fdk_cone.py`, `iterative_reco_*.py`, `realdata_fbp_*.py`,
|
|
40
|
+
`realdata_fdk_cone.py`, and `realdata_walnut_fdk.py` assert
|
|
41
|
+
`torch.cuda.is_available()` and initialize the CUDA device up front
|
|
42
|
+
instead of silently falling back to CPU (which the numba-cuda kernels
|
|
43
|
+
never supported anyway).
|
|
44
|
+
|
|
45
|
+
## [1.3.2] - 2026-04-23
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
- **Expanded angular-weight regression tests** in
|
|
49
|
+
`tests/test_weights.py`, covering periodic `pi` half scans,
|
|
50
|
+
downsampled full scans, endpoint-included 721-view full scans, and
|
|
51
|
+
open short scans.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
- **`angular_integration_weights` is now coverage-aware and order-stable.**
|
|
55
|
+
The helper sorts angles internally, detects `pi`- and `2*pi`-periodic
|
|
56
|
+
sampling more robustly, handles endpoint-included trajectories, and
|
|
57
|
+
maps the computed weights back to the caller's original angle order.
|
|
58
|
+
- **Walnut tooling is now configurable from the command line.**
|
|
59
|
+
`examples/data/preprocess_walnut.py` gained CLI switches for binning,
|
|
60
|
+
cropping, view stride, angle handedness, storage dtype, and archive
|
|
61
|
+
discovery; `examples/realdata_walnut_fdk.py` gained CLI controls for
|
|
62
|
+
backend, output volume size, voxel scale, filter window, output file
|
|
63
|
+
writing, and display suppression.
|
|
64
|
+
- **README polish for bilingual navigation and repo layout.**
|
|
65
|
+
`README.md` now exposes the Chinese README link near the top and
|
|
66
|
+
clarifies the file tree entries for `README.md` / `README.zh.md`.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
- **Angular quadrature artifacts** in analytical reconstructions when
|
|
70
|
+
full scans were endpoint-included or angularly downsampled. These
|
|
71
|
+
trajectories now receive periodic integration weights instead of being
|
|
72
|
+
misclassified as open scans.
|
|
73
|
+
- **Walnut example angle metadata and scan handedness.**
|
|
74
|
+
The walnut preprocessing and FDK example now use the dataset's
|
|
75
|
+
scanner-to-diffct angle convention explicitly and preserve the full
|
|
76
|
+
721-view `0 .. 360 deg` acquisition description in the stored
|
|
77
|
+
metadata.
|
|
78
|
+
|
|
79
|
+
## [1.3.1] - 2026-04-15
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
- **Real-data walnut FDK example** at `examples/realdata_walnut_fdk.py`,
|
|
83
|
+
running the full analytical FDK pipeline on a preprocessed subset of
|
|
84
|
+
the Helsinki walnut cone-beam CT dataset ([Meaney 2022, Zenodo
|
|
85
|
+
10.5281/zenodo.6986012](https://doi.org/10.5281/zenodo.6986012),
|
|
86
|
+
CC-BY 4.0). The 241-view, 256×256-per-view sinogram is shipped at
|
|
87
|
+
`examples/data/walnut_cone.npz` (~25 MB, float16, 8× binned, 256²
|
|
88
|
+
center crop) together with a `preprocess_walnut.py` regenerator
|
|
89
|
+
script, an attribution `NOTICE`, and a `walnut_reco.png` sample
|
|
90
|
+
montage. Reconstructs at half the nominal voxel size on a 512³ grid
|
|
91
|
+
using `backend="sf_tr"` + Hamming ramp window.
|
|
92
|
+
- **Synthetic "real-data" pipeline examples** for all three geometries
|
|
93
|
+
(`examples/realdata_fbp_parallel.py`, `realdata_fbp_fan.py`,
|
|
94
|
+
`realdata_fdk_cone.py`) that run a Beer-Lambert + Poisson + `-log`
|
|
95
|
+
forward model on Shepp-Logan and reconstruct with the analytical
|
|
96
|
+
pipeline, showing the proper place for `-log` preprocessing.
|
|
97
|
+
- **Chinese README** at `README.zh.md`, mirroring `README.md` with
|
|
98
|
+
bidirectional language links in both files.
|
|
99
|
+
- **Core Algorithm section** in the READMEs (EN + ZH) explaining the
|
|
100
|
+
Siddon + bilinear/trilinear interpolation design choice, the
|
|
101
|
+
trade-off (slight blur at nominal voxel size), and when to pick
|
|
102
|
+
SF vs VD honestly (see "Changed" below).
|
|
103
|
+
|
|
104
|
+
### Changed
|
|
105
|
+
- **SF FBP / FDK backprojection kernels rewritten in LEAP's
|
|
106
|
+
chord-weighted matched-adjoint form** (from `projectors_SF.cu`).
|
|
107
|
+
The three analytical SF gather kernels
|
|
108
|
+
(`_fan_2d_sf_fbp_backproject_kernel`,
|
|
109
|
+
`_cone_3d_sf_tr_fdk_backproject_kernel`,
|
|
110
|
+
`_cone_3d_sf_tt_fdk_backproject_kernel`) now use the per-voxel
|
|
111
|
+
chord through the unit voxel instead of a `(sid/U)²`-plus-footprint-
|
|
112
|
+
area form, with a matching `sqrt(1+(v/sdd)²)` axial chord correction
|
|
113
|
+
on the cone side. Amplitude is calibrated against Siddon VD at
|
|
114
|
+
every voxel / detector-pitch combination tested (nominal, 0.64×
|
|
115
|
+
sub-nominal, 0.40× sub-nominal, supra-nominal) via the new
|
|
116
|
+
`det_spacing/(voxel·2π)` fan scale and
|
|
117
|
+
`(du·dv·sid)/(2π·sdd·voxel²)` cone scale constants.
|
|
118
|
+
- **Honest README Core Algorithm section on SF vs VD.** The earlier
|
|
119
|
+
"SF lowers MSE by ~17 %" and "SF is measurably sharper at sub-
|
|
120
|
+
nominal" claims in 1.3.0 are walked back: on Shepp-Logan and the
|
|
121
|
+
walnut dataset, LEAP-form SF and Siddon VD produce visually
|
|
122
|
+
indistinguishable edge profiles at typical CBCT magnifications,
|
|
123
|
+
and MSE differences are fractions of a percent. SF's real value
|
|
124
|
+
is its **mass-conserving cell-integrated forward model**, which
|
|
125
|
+
matters for iterative reconstruction, learned priors, and
|
|
126
|
+
sinogram-level losses — not as a drop-in sharpness knob on
|
|
127
|
+
analytical FBP / FDK. The shipped `fbp_fan.py`, `fdk_cone.py`,
|
|
128
|
+
and `realdata_walnut_fdk.py` example comments are updated to
|
|
129
|
+
match.
|
|
130
|
+
|
|
131
|
+
### Fixed
|
|
132
|
+
- **SF FBP / FDK weight normalization** when the voxel's trapezoidal
|
|
133
|
+
footprint straddles the default 4-corner sort. The 1.3.0 SF
|
|
134
|
+
backprojection kernels used `fdk_w / (span · v_span)` (bounding-box
|
|
135
|
+
area) which under-amplified oblique views by up to 2× per axis and
|
|
136
|
+
produced a dim, angularly-modulated reconstruction. Diagnosed by
|
|
137
|
+
inspecting a Shepp-Logan SF reconstruction, corrected first to
|
|
138
|
+
`fdk_w / ((span+plateau)/2 · v_span)` (true trapezoid area) and
|
|
139
|
+
then fully replaced by the LEAP chord-weighted form above.
|
|
140
|
+
|
|
141
|
+
### Acknowledgements
|
|
142
|
+
- [LEAP](https://github.com/LLNL/LEAP) (LLNL / Hyojin Kim et al.)
|
|
143
|
+
added to `README.md` / `README.zh.md` Acknowledgements.
|
|
144
|
+
`projectors_SF.cu` is the reference implementation the new
|
|
145
|
+
analytical SF backprojection kernels are ported from.
|
|
146
|
+
|
|
147
|
+
## [1.3.0] - 2026-04-14
|
|
148
|
+
|
|
149
|
+
### Added
|
|
150
|
+
- **Separable-footprint (SF) projector backends** for 2D fan and 3D cone
|
|
151
|
+
beam geometries, following Long-Fessler-Balter (IEEE TMI 2010).
|
|
152
|
+
`FanProjectorFunction` / `FanBackprojectorFunction` now accept
|
|
153
|
+
`backend="siddon"` (default, unchanged) or `backend="sf"`.
|
|
154
|
+
`ConeProjectorFunction` / `ConeBackprojectorFunction` accept
|
|
155
|
+
`backend="siddon"` (default), `"sf_tr"` (trapezoid in transaxial,
|
|
156
|
+
rectangle in axial) or `"sf_tt"` (trapezoid in both directions with
|
|
157
|
+
axial rise/fall from `U_near`/`U_far` across the voxel). Each SF
|
|
158
|
+
backend is implemented as a matched scatter/gather pair of CUDA
|
|
159
|
+
kernels so both forward and adjoint are available — autograd works
|
|
160
|
+
end-to-end for every backend, and the standalone Backprojector
|
|
161
|
+
Function can also be driven in SF mode for iterative reconstruction.
|
|
162
|
+
- **Adjoint inner-product tests** for every new SF backend in
|
|
163
|
+
`tests/test_adjoint_inner_product.py`, plus `torch.autograd.gradcheck`
|
|
164
|
+
coverage in `tests/test_gradcheck.py`. Test count grew from 57 to 66.
|
|
165
|
+
|
|
166
|
+
### Changed
|
|
167
|
+
- **Internal grid-point convention** is now uniformly `x_v = ix - cx`
|
|
168
|
+
(matching `_cone_3d_fdk_backproject_kernel`). The SF kernels use the
|
|
169
|
+
same convention so voxel centers align with where the Siddon/FDK
|
|
170
|
+
paths interpret `d_vol[ix, iy, iz]`.
|
|
171
|
+
- **`_TPB_SF_3D = (4, 4, 4)`** introduced for the 3D SF kernels. SF-TT
|
|
172
|
+
in particular exceeds the 128-register-per-thread budget allowed by
|
|
173
|
+
the default `(8, 8, 8)` launch; the smaller block gives each thread
|
|
174
|
+
~1024 registers and avoids `CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES`.
|
|
175
|
+
|
|
176
|
+
### Notes
|
|
177
|
+
- **Raw forward accuracy vs analytical Radon**: on both 2D fan
|
|
178
|
+
(512² Shepp-Logan) and 3D cone (96³ and 128³ 3D Shepp-Logan) phantoms,
|
|
179
|
+
measured against the analytical ellipse / ellipsoid Radon transform,
|
|
180
|
+
the existing Siddon projector with bilinear / trilinear interpolation
|
|
181
|
+
is slightly more accurate (~3% better RMSE, ~15% better max error)
|
|
182
|
+
than SF-TR / SF-TT at the thin-ray sampling level. The classic SF
|
|
183
|
+
advantage reported in Long et al. is over the piecewise-constant DD
|
|
184
|
+
projector; diffct's Siddon uses smooth interpolation, which covers
|
|
185
|
+
most of the gap SF was designed to close.
|
|
186
|
+
- **Full analytical reconstruction (forward + FBP/FDK)**: despite the
|
|
187
|
+
raw-forward result above, in a complete analytical pipeline
|
|
188
|
+
(`FanProjectorFunction` / `ConeProjectorFunction` -> `ramp_filter_1d`
|
|
189
|
+
-> `fan_weighted_backproject` / `cone_weighted_backproject`) SF
|
|
190
|
+
**measurably improves reconstruction quality**. On the 256²
|
|
191
|
+
Shepp-Logan `fbp_fan.py` example the raw MSE drops from
|
|
192
|
+
`0.00216` (Siddon) to `0.00178` (SF-TR), a ~17% improvement.
|
|
193
|
+
On the 128³ `fdk_cone.py` example it drops from `0.00325` (Siddon)
|
|
194
|
+
to `0.00271` (SF-TR), again ~17%. The mechanism is that SF's
|
|
195
|
+
mass-conserving cell-integrated forward pairs more naturally with
|
|
196
|
+
the voxel-driven FBP/FDK gather backprojector than Siddon's
|
|
197
|
+
thin-ray sampling, which feeds extra high-frequency ringing into
|
|
198
|
+
the ramp filter.
|
|
199
|
+
- **Performance**: SF-TR is ~2× and SF-TT ~2.5–4× slower than Siddon for
|
|
200
|
+
cone beam, and ~3× slower than Siddon for fan beam. The extra cost
|
|
201
|
+
comes from voxel-driven scatter with atomic adds (forward) versus
|
|
202
|
+
ray-driven parallelism in Siddon.
|
|
203
|
+
- **Recommendation**: `backend="siddon"` remains the default for
|
|
204
|
+
backward compatibility and raw speed. For **analytical FBP/FDK
|
|
205
|
+
reconstruction** where reconstruction MSE matters more than forward
|
|
206
|
+
runtime, `"sf"` / `"sf_tr"` is a quantifiably better choice — at
|
|
207
|
+
2–3× forward cost you get ~17% lower reconstruction MSE on standard
|
|
208
|
+
phantoms. For **iterative reconstruction with autograd**, either
|
|
209
|
+
backend is valid; SF has the theoretical advantage of exact voxel-
|
|
210
|
+
scale mass conservation on both forward and adjoint, but in practice
|
|
211
|
+
Siddon's smoothing usually helps optimizer convergence. SF-TT's
|
|
212
|
+
axial-trapezoid refinement over SF-TR improves RMSE only by ~0.001
|
|
213
|
+
and is not worth the extra cost for typical cone angles; use SF-TR
|
|
214
|
+
by default and reserve SF-TT for extreme cone-angle research.
|
|
215
|
+
|
|
216
|
+
## [1.2.11] - 2026-04-14
|
|
217
|
+
|
|
218
|
+
### Added
|
|
219
|
+
- **Parker short-scan demos** in `examples/fdk_cone.py` and `examples/fbp_fan.py`.
|
|
220
|
+
Both examples now expose an `apply_parker` switch that not only multiplies
|
|
221
|
+
by `parker_weights` but also samples angles on the minimal short-scan range
|
|
222
|
+
`pi + 2*gamma_max`, where `gamma_max` is computed from the detector half-width
|
|
223
|
+
and `sdd`. (Before this change the `apply_parker` flag in `fbp_fan.py` was
|
|
224
|
+
silently a no-op because the angular range stayed at `2*pi`.)
|
|
225
|
+
- **`tests/test_gradcheck.py`** — `torch.autograd.gradcheck` coverage for
|
|
226
|
+
`ParallelProjectorFunction`, `FanProjectorFunction`, and `ConeProjectorFunction`.
|
|
227
|
+
Compares the analytical backward Jacobian to a finite-difference numerical
|
|
228
|
+
Jacobian over small inputs with float32-calibrated tolerances. Strongest
|
|
229
|
+
autograd-correctness guard beyond the existing inner-product adjoint tests.
|
|
230
|
+
- **`tests/test_ramp_filter_windows.py`** — 27 parametrised tests covering every
|
|
231
|
+
`ramp_filter_1d` window option (`None`/`"ram-lak"`, `"hann"`, `"hanning"`,
|
|
232
|
+
`"hamming"`, `"cosine"`, `"shepp-logan"`) at three layers: direct `_ramp_window`
|
|
233
|
+
helper (DC gain, Nyquist value, monotonicity), full `ramp_filter_1d` end-to-end
|
|
234
|
+
(shape, DC annihilation, pad correctness, rfft vs complex-fft parity,
|
|
235
|
+
`sample_spacing` scaling), and a high-frequency attenuation sanity check on a
|
|
236
|
+
step input.
|
|
237
|
+
- **`CHANGELOG.md`** — new Keep-a-Changelog file at the repo root.
|
|
238
|
+
- **`tests/benchmarks/`** — opt-in `pytest-benchmark` suite covering every CUDA
|
|
239
|
+
kernel in the library (forward projector, pure-adjoint backprojector, and
|
|
240
|
+
analytical FBP/FDK pipeline) across three sizes for each of the three
|
|
241
|
+
geometries. 27 benchmarks total. Excluded from the default `pytest tests/`
|
|
242
|
+
run via `--ignore=tests/benchmarks` in `pytest.ini` so the normal test suite
|
|
243
|
+
stays fast; run explicitly with `pytest tests/benchmarks/ --benchmark-only`.
|
|
244
|
+
Enables before/after perf comparisons via `--benchmark-save=` /
|
|
245
|
+
`--benchmark-compare=`.
|
|
246
|
+
|
|
247
|
+
### Changed
|
|
248
|
+
- **`_cone_3d_fdk_backproject_kernel`** memory-access layout. The kernel now
|
|
249
|
+
unpacks `cuda.grid(3)` as `(iz, iy, ix)` instead of `(ix, iy, iz)`, and
|
|
250
|
+
`cone_weighted_backproject` launches the grid as `_grid_3d(D, H, W)` instead
|
|
251
|
+
of `_grid_3d(W, H, D)`. This makes `iz` warp-adjacent, which matches the
|
|
252
|
+
innermost stride-1 axis of the WHD output buffer, so the final `d_vol` write
|
|
253
|
+
becomes coalesced. Measured speedup on a 64^3–160^3 sweep: 1.0x–1.14x,
|
|
254
|
+
within run-to-run noise (the kernel is read-bound on the sinogram bilinear
|
|
255
|
+
samples, not write-bound, so L2 absorbed the old uncoalesced writes). The
|
|
256
|
+
fix is kept as the right default and future-proofs the kernel against
|
|
257
|
+
edits that shift work toward the write path. Matching coalescing comments
|
|
258
|
+
were added to the 2D `_parallel_2d_fbp_backproject_kernel` and
|
|
259
|
+
`_fan_2d_fbp_backproject_kernel` kernels, which were already coalesced
|
|
260
|
+
(their first grid index is also warp-adjacent and matches the `Nx` innermost
|
|
261
|
+
axis of their `(Ny, Nx)` output buffers).
|
|
262
|
+
|
|
263
|
+
### Notes
|
|
264
|
+
- No public-API changes. Pure-adjoint kernels and autograd Function classes
|
|
265
|
+
are untouched, so the autograd and iterative-reconstruction paths are
|
|
266
|
+
byte-for-byte equivalent to 1.2.10.
|
|
267
|
+
|
|
268
|
+
## [1.2.10] - 2026-04-14
|
|
269
|
+
|
|
270
|
+
This release is a correctness overhaul of every analytical reconstruction path
|
|
271
|
+
(parallel / fan / cone) **and** of the autograd adjoint path shared by the
|
|
272
|
+
iterative examples. It also ships a unified architecture where each geometry
|
|
273
|
+
has its own dedicated voxel-driven FBP/FDK gather kernel, separate from the
|
|
274
|
+
pure Siddon adjoint used by autograd.
|
|
275
|
+
|
|
276
|
+
### Fixed
|
|
277
|
+
|
|
278
|
+
#### Analytical FBP / FDK amplitude bugs
|
|
279
|
+
|
|
280
|
+
Prior to this release, all three analytical reconstruction examples produced
|
|
281
|
+
results that were off by large constant factors:
|
|
282
|
+
|
|
283
|
+
| Example | Pre-fix reco range | Post-fix reco range | MSE improvement |
|
|
284
|
+
|---|---|---|---|
|
|
285
|
+
| `fbp_parallel.py` | [0, 6.52] | [−0.02, 1.01] | ~580× |
|
|
286
|
+
| `fbp_fan.py` | [0, 6.33] | [−0.08, 1.01] | ~900× |
|
|
287
|
+
| `fdk_cone.py` | [0, 10.08] | [−0.08, 1.00] | ~600× |
|
|
288
|
+
|
|
289
|
+
Root causes:
|
|
290
|
+
- Cone and fan used `(sdd/U)^2` with `U = sdd + x·sin − y·cos` instead of
|
|
291
|
+
the correct FDK weight `(sid/U)^2` with `U = sid + x·sin − y·cos`.
|
|
292
|
+
- All three geometries were missing the `1/(2π)` Fourier-convention
|
|
293
|
+
constant from the FBP/FDK reconstruction formula.
|
|
294
|
+
- Cone and fan used a Siddon ray-driven scatter for the analytical path,
|
|
295
|
+
which is neither a true adjoint nor a classical FBP/FDK gather.
|
|
296
|
+
|
|
297
|
+
#### Autograd adjoint bug (cone + fan)
|
|
298
|
+
|
|
299
|
+
`ConeProjectorFunction.backward`, `ConeBackprojectorFunction.forward`,
|
|
300
|
+
`FanProjectorFunction.backward` and `FanBackprojectorFunction.forward`
|
|
301
|
+
were all passing `distance_weight=1.0` to the shared Siddon backward
|
|
302
|
+
kernel. This meant the autograd backward was **not the true adjoint** `P^T`
|
|
303
|
+
of the forward projector — it had a `(sdd/U)^2` per-voxel factor baked in,
|
|
304
|
+
biasing the gradient by ~2–3× depending on voxel position. Any iterative
|
|
305
|
+
reconstruction that relied on autograd (including `iterative_reco_cone.py`
|
|
306
|
+
and `iterative_reco_fan.py`) was running on a biased gradient flow.
|
|
307
|
+
|
|
308
|
+
Parallel beam was unaffected (its backward kernel had no `distance_weight`
|
|
309
|
+
parameter to begin with).
|
|
310
|
+
|
|
311
|
+
This release removes the `distance_weight` parameter from both
|
|
312
|
+
`_cone_3d_backward_kernel` and `_fan_2d_backward_kernel` entirely, so the
|
|
313
|
+
dead code path cannot be accidentally reintroduced, and fixes the four
|
|
314
|
+
autograd call sites to use the pure adjoint.
|
|
315
|
+
|
|
316
|
+
### Added
|
|
317
|
+
|
|
318
|
+
#### Voxel-driven gather kernels for every geometry
|
|
319
|
+
|
|
320
|
+
Three new CUDA kernels, all under a dedicated `fastmath=False` decorator
|
|
321
|
+
for FDK-grade accuracy:
|
|
322
|
+
|
|
323
|
+
- `_parallel_2d_fbp_backproject_kernel` — no distance weighting (no source).
|
|
324
|
+
- `_fan_2d_fbp_backproject_kernel` — `(sid/U)^2` + linear detector interp.
|
|
325
|
+
- `_cone_3d_fdk_backproject_kernel` — `(sid/U)^2` + bilinear detector interp.
|
|
326
|
+
|
|
327
|
+
Each kernel is voxel-driven: one thread per output pixel/voxel, loops over
|
|
328
|
+
views, computes the projected detector coordinate, interpolates the filtered
|
|
329
|
+
sinogram, weights and accumulates.
|
|
330
|
+
|
|
331
|
+
#### `parallel_weighted_backproject` (new public helper)
|
|
332
|
+
|
|
333
|
+
Mirrors `fan_weighted_backproject` and `cone_weighted_backproject`. Applies
|
|
334
|
+
the `1/(2π)` Fourier-convention constant so a unit-density disk reconstructs
|
|
335
|
+
to amplitude 1. Exported from `diffct`.
|
|
336
|
+
|
|
337
|
+
#### `ramp_filter_1d` — new backward-compatible kwargs
|
|
338
|
+
|
|
339
|
+
- `sample_spacing` (default `1.0`): physical detector cell pitch. Output is
|
|
340
|
+
rescaled by `1/sample_spacing` for physical-unit correctness.
|
|
341
|
+
- `pad_factor` (default `1`): zero-pad to `pad_factor * N` before the FFT to
|
|
342
|
+
suppress circular-convolution wrap-around. `2` is recommended for FBP/FDK.
|
|
343
|
+
- `window` (default `None`): frequency-domain apodization. Options: `None` /
|
|
344
|
+
`"ram-lak"`, `"hann"`, `"hamming"`, `"cosine"`, `"shepp-logan"`.
|
|
345
|
+
- `use_rfft` (default `True`): faster real-FFT path for real-valued inputs.
|
|
346
|
+
|
|
347
|
+
Existing `ramp_filter_1d(sino, dim=1)` calls keep working unchanged.
|
|
348
|
+
|
|
349
|
+
#### Analytical FBP / FDK scale factors
|
|
350
|
+
|
|
351
|
+
Each analytical helper now applies the correct analytical constant
|
|
352
|
+
automatically so reconstructions are already amplitude-calibrated:
|
|
353
|
+
|
|
354
|
+
- `parallel_weighted_backproject`: `1 / (2π)`.
|
|
355
|
+
- `fan_weighted_backproject`: `sdd / (2π · sid)`.
|
|
356
|
+
- `cone_weighted_backproject`: `sdd / (2π · sid)`.
|
|
357
|
+
|
|
358
|
+
### Tests
|
|
359
|
+
|
|
360
|
+
Test suite expanded from 6 to **27 tests**:
|
|
361
|
+
|
|
362
|
+
- `tests/test_adjoint_inner_product.py` — the definitive check. For random `x`
|
|
363
|
+
and `y`, asserts `⟨A x, y⟩ = ⟨x, A^T y⟩` for parallel, fan and cone autograd
|
|
364
|
+
pairs. Permanently guards against any regression that would reintroduce the
|
|
365
|
+
`distance_weight=1.0` bug.
|
|
366
|
+
- `tests/test_fdk_cone_accuracy.py` — 128³ Shepp-Logan RMSE / amplitude bounds.
|
|
367
|
+
- `tests/test_fdk_cone_offsets.py` — detector, center and combined offsets with
|
|
368
|
+
amplitude assertions.
|
|
369
|
+
- `tests/test_cone_projector_autograd.py` — gradient finiteness and non-zero
|
|
370
|
+
sanity.
|
|
371
|
+
- `tests/test_fbp_fan_accuracy.py` — 256×256 Shepp-Logan RMSE.
|
|
372
|
+
- `tests/test_fbp_fan_offsets.py` — three offset configurations.
|
|
373
|
+
- `tests/test_fbp_parallel_accuracy.py` — parallel FBP RMSE + offset.
|
|
374
|
+
|
|
375
|
+
### Examples
|
|
376
|
+
|
|
377
|
+
`examples/fdk_cone.py`, `examples/fbp_fan.py` and `examples/fbp_parallel.py`
|
|
378
|
+
have been rewritten with a consistent 8-step layout and detailed inline
|
|
379
|
+
comments documenting every geometry variable (what it is, units, typical
|
|
380
|
+
values, available options). Each example prints raw MSE, clamped MSE, and the
|
|
381
|
+
reconstruction / phantom data ranges.
|
|
382
|
+
|
|
383
|
+
The iterative reconstruction examples (`iterative_reco_parallel.py`,
|
|
384
|
+
`iterative_reco_fan.py`, `iterative_reco_cone.py`) are unchanged — they
|
|
385
|
+
silently benefit from the fixed autograd adjoint.
|
|
386
|
+
|
|
387
|
+
### Documentation
|
|
388
|
+
|
|
389
|
+
- `docs/source/api.rst`: adds autofunction entries for
|
|
390
|
+
`parallel_weighted_backproject`, documents the new `ramp_filter_1d`
|
|
391
|
+
options, and adds an "Analytical FBP / FDK architecture" section
|
|
392
|
+
explaining the scale-factor derivation.
|
|
393
|
+
- `docs/source/fdk_cone_example.rst`: formula updated to reflect the
|
|
394
|
+
voxel-gather kernel and `(sid/U)^2` weight.
|
|
395
|
+
- `docs/source/fbp_fan_example.rst` and `fbp_parallel_example.rst`: completely
|
|
396
|
+
rewritten to reference the new analytical helpers and fix a sign-convention
|
|
397
|
+
inconsistency in the math section.
|
|
398
|
+
|
|
399
|
+
### Compatibility
|
|
400
|
+
|
|
401
|
+
This release is source-compatible with 1.2.9 for every public entry point. The
|
|
402
|
+
`distance_weight` parameter was internal and is removed from the private
|
|
403
|
+
backward kernels; no public API touched that argument.
|
|
404
|
+
|
|
405
|
+
## [1.2.9] - 2026-02-14
|
|
406
|
+
|
|
407
|
+
Restored `main` to the state of 1.2.9 by reverting an accidental merge.
|
|
408
|
+
See [GitHub release v1.2.9](https://github.com/sypsyp97/diffct/releases/tag/v1.2.9).
|
|
409
|
+
|
|
410
|
+
## [1.2.8] - 2026-02-14
|
|
411
|
+
|
|
412
|
+
See [GitHub release v1.2.8](https://github.com/sypsyp97/diffct/releases/tag/v1.2.8)
|
|
413
|
+
and the commit history for details.
|
|
414
|
+
|
|
415
|
+
## [1.2.7] - 2025-08-05
|
|
416
|
+
|
|
417
|
+
See [GitHub release v1.2.7](https://github.com/sypsyp97/diffct/releases/tag/v1.2.7).
|
|
418
|
+
|
|
419
|
+
## Earlier versions
|
|
420
|
+
|
|
421
|
+
Releases 1.2.0 through 1.2.6 and the 1.1.x / 1.0.x lines are tracked on
|
|
422
|
+
[GitHub releases](https://github.com/sypsyp97/diffct/releases). This CHANGELOG
|
|
423
|
+
was introduced in 1.2.10 and does not back-fill detailed notes for earlier
|
|
424
|
+
versions beyond pointers to the GitHub release pages.
|
|
425
|
+
|
|
426
|
+
[1.3.3]: https://github.com/sypsyp97/diffct/releases/tag/v1.3.3
|
|
427
|
+
[1.3.2]: https://github.com/sypsyp97/diffct/releases/tag/v1.3.2
|
|
428
|
+
[1.3.1]: https://github.com/sypsyp97/diffct/releases/tag/v1.3.1
|
|
429
|
+
[1.3.0]: https://github.com/sypsyp97/diffct/releases/tag/v1.3.0
|
|
430
|
+
[1.2.11]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.11
|
|
431
|
+
[1.2.10]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.10
|
|
432
|
+
[1.2.9]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.9
|
|
433
|
+
[1.2.8]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.8
|
|
434
|
+
[1.2.7]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.7
|