diffct 1.2.3__tar.gz → 1.2.4__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.2.3 → diffct-1.2.4}/PKG-INFO +1 -1
- {diffct-1.2.3 → diffct-1.2.4}/diffct/differentiable.py +80 -80
- {diffct-1.2.3 → diffct-1.2.4}/examples/fbp_fan.py +3 -2
- {diffct-1.2.3 → diffct-1.2.4}/examples/fbp_parallel.py +3 -2
- {diffct-1.2.3 → diffct-1.2.4}/examples/fdk_cone.py +3 -2
- {diffct-1.2.3 → diffct-1.2.4}/pyproject.toml +1 -1
- {diffct-1.2.3 → diffct-1.2.4}/.github/workflows/docs.yml +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/.gitignore +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/LICENSE +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/README.md +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/diffct/__init__.py +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/Makefile +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/_static/.gitkeep +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/api.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/conf.py +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/examples.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/fbp_fan_example.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/fbp_parallel_example.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/fdk_cone_example.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/getting_started.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/index.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/iterative_reco_cone_example.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/iterative_reco_fan_example.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/docs/source/iterative_reco_parallel_example.rst +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/examples/iterative_reco_cone.py +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/examples/iterative_reco_fan.py +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/examples/iterative_reco_parallel.py +0 -0
- {diffct-1.2.3 → diffct-1.2.4}/requirements.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diffct
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
4
4
|
Summary: A CUDA-based library for computed tomography (CT) projection and reconstruction with differentiable operators
|
|
5
5
|
Project-URL: Homepage, https://github.com/sypsyp97/diffct
|
|
6
6
|
Author-email: Yipeng Sun <yipeng.sun@fau.de>
|
|
@@ -135,7 +135,7 @@ class TorchCUDABridge:
|
|
|
135
135
|
return torch.as_tensor(cuda_array, device=tensor_template.device, dtype=tensor_template.dtype)
|
|
136
136
|
|
|
137
137
|
# === GPU-aware Trigonometric Table Generation ===
|
|
138
|
-
def _trig_tables(angles, dtype=_DTYPE):
|
|
138
|
+
def _trig_tables(angles, dtype=_DTYPE, device=None):
|
|
139
139
|
"""Compute cosine and sine tables for input angles.
|
|
140
140
|
|
|
141
141
|
Precompute cosine and sine values and return as torch tensors on the
|
|
@@ -163,7 +163,7 @@ def _trig_tables(angles, dtype=_DTYPE):
|
|
|
163
163
|
device(type='cuda', index=0)
|
|
164
164
|
"""
|
|
165
165
|
if isinstance(angles, torch.Tensor):
|
|
166
|
-
device = angles.device
|
|
166
|
+
device = angles.device if device is None else device
|
|
167
167
|
cos = torch.cos(angles).to(dtype=dtype)
|
|
168
168
|
sin = torch.sin(angles).to(dtype=dtype)
|
|
169
169
|
return cos.to(device), sin.to(device)
|
|
@@ -182,7 +182,10 @@ def _trig_tables(angles, dtype=_DTYPE):
|
|
|
182
182
|
angles_cpu = torch.tensor(angles, dtype=torch_dtype)
|
|
183
183
|
cos_cpu = torch.cos(angles_cpu)
|
|
184
184
|
sin_cpu = torch.sin(angles_cpu)
|
|
185
|
-
|
|
185
|
+
if device is not None:
|
|
186
|
+
return cos_cpu.to(device), sin_cpu.to(device)
|
|
187
|
+
else:
|
|
188
|
+
return cos_cpu, sin_cpu
|
|
186
189
|
|
|
187
190
|
|
|
188
191
|
# ############################################################################
|
|
@@ -191,14 +194,14 @@ def _trig_tables(angles, dtype=_DTYPE):
|
|
|
191
194
|
|
|
192
195
|
def _validate_3d_memory_layout(tensor, expected_order='DHW'):
|
|
193
196
|
"""Validate 3D tensor memory layout to prevent coordinate system inconsistencies.
|
|
194
|
-
|
|
197
|
+
|
|
195
198
|
Parameters
|
|
196
199
|
----------
|
|
197
200
|
tensor : torch.Tensor
|
|
198
201
|
3D tensor to validate
|
|
199
202
|
expected_order : str, optional
|
|
200
203
|
Expected memory order ('DHW', 'VHW', etc.). Default is 'DHW'.
|
|
201
|
-
|
|
204
|
+
|
|
202
205
|
Raises
|
|
203
206
|
------
|
|
204
207
|
ValueError
|
|
@@ -206,53 +209,50 @@ def _validate_3d_memory_layout(tensor, expected_order='DHW'):
|
|
|
206
209
|
"""
|
|
207
210
|
if len(tensor.shape) != 3:
|
|
208
211
|
raise ValueError(f"Expected 3D tensor, got {len(tensor.shape)}D")
|
|
209
|
-
|
|
212
|
+
|
|
210
213
|
# Check if tensor is contiguous to avoid memory duplication
|
|
211
214
|
if not tensor.is_contiguous():
|
|
212
215
|
raise ValueError(
|
|
213
216
|
"Input tensor must be contiguous. Call .contiguous() before passing to "
|
|
214
217
|
"cone beam functions to avoid memory duplication and ensure correct results."
|
|
215
218
|
)
|
|
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
|
-
f"but tensor has shape {actual_str}. Please {fix_str} and ensure "
|
|
254
|
-
f"the tensor is contiguous (.contiguous()) before passing to the function."
|
|
255
|
-
)
|
|
219
|
+
|
|
220
|
+
# Only check memory order for DHW and VHW, not for internal WHD layout
|
|
221
|
+
if expected_order in ('DHW', 'VHW'):
|
|
222
|
+
strides = tensor.stride()
|
|
223
|
+
order_mapping = {
|
|
224
|
+
'DHW': (0, 1, 2), # Depth, Height, Width
|
|
225
|
+
'VHW': (0, 1, 2), # Views, Height, Width (for sinograms)
|
|
226
|
+
}
|
|
227
|
+
if expected_order not in order_mapping:
|
|
228
|
+
raise ValueError(f"Unsupported expected_order: {expected_order}")
|
|
229
|
+
|
|
230
|
+
expected_stride_order = order_mapping[expected_order]
|
|
231
|
+
# Check if actual strides match expected order
|
|
232
|
+
sorted_strides = sorted(enumerate(strides), key=lambda x: x[1], reverse=True)
|
|
233
|
+
actual_order = tuple(idx for idx, _ in sorted_strides)
|
|
234
|
+
|
|
235
|
+
if actual_order != expected_stride_order:
|
|
236
|
+
# Create appropriate error message based on context
|
|
237
|
+
if expected_order == 'VHW':
|
|
238
|
+
actual_str = f"({tensor.shape[0]}, {tensor.shape[1]}, {tensor.shape[2]})"
|
|
239
|
+
expected_str = "(Views, Height, Width)"
|
|
240
|
+
fix_str = "ensure your sinogram has shape (num_views, det_v, det_u)"
|
|
241
|
+
elif expected_order == 'DHW':
|
|
242
|
+
actual_str = f"({tensor.shape[0]}, {tensor.shape[1]}, {tensor.shape[2]})"
|
|
243
|
+
expected_str = "(Depth, Height, Width)"
|
|
244
|
+
fix_str = "ensure your volume has shape (D, H, W)"
|
|
245
|
+
else:
|
|
246
|
+
actual_str = str(tuple(tensor.shape))
|
|
247
|
+
expected_str = expected_order
|
|
248
|
+
fix_str = "check tensor dimensions"
|
|
249
|
+
|
|
250
|
+
raise ValueError(
|
|
251
|
+
f"Memory layout mismatch: expected {expected_str} order, "
|
|
252
|
+
f"but tensor has shape {actual_str}. Please {fix_str} and ensure "
|
|
253
|
+
f"the tensor is contiguous (.contiguous()) before passing to the function."
|
|
254
|
+
)
|
|
255
|
+
# For 'WHD' (internal layout), skip stride check entirely
|
|
256
256
|
|
|
257
257
|
|
|
258
258
|
def _grid_2d(n1, n2, tpb=_TPB_2D):
|
|
@@ -392,7 +392,7 @@ def _parallel_2d_forward_kernel(
|
|
|
392
392
|
cos_a = d_cos[iang] # Precomputed cosine of projection angle
|
|
393
393
|
sin_a = d_sin[iang] # Precomputed sine of projection angle
|
|
394
394
|
# Normalize all physical distances to voxel units
|
|
395
|
-
u = (idet -
|
|
395
|
+
u = (idet - n_det * 0.5) * det_spacing / voxel_spacing # Detector coordinate in voxel units
|
|
396
396
|
|
|
397
397
|
# Define ray direction and starting point for parallel beam geometry
|
|
398
398
|
# Ray direction is perpendicular to detector array (cos_a, sin_a)
|
|
@@ -471,8 +471,8 @@ def _parallel_2d_forward_kernel(
|
|
|
471
471
|
dx, dy = mid_x - ix0, mid_y - iy0 # Fractional parts: distance from base voxel center [0,1]
|
|
472
472
|
|
|
473
473
|
# Clamp indices to stay in-bounds during interpolation
|
|
474
|
-
ix0 = min(ix0, Nx - 2)
|
|
475
|
-
iy0 = min(iy0, Ny - 2)
|
|
474
|
+
ix0 = max(0, min(ix0, Nx - 2))
|
|
475
|
+
iy0 = max(0, min(iy0, Ny - 2))
|
|
476
476
|
|
|
477
477
|
# === BILINEAR INTERPOLATION WEIGHT CALCULATION ===
|
|
478
478
|
# Mathematical basis: Bilinear interpolation formula f(x,y) = Σ f(xi,yi) * wi(x,y)
|
|
@@ -554,7 +554,7 @@ def _parallel_2d_backward_kernel(
|
|
|
554
554
|
cos_a = d_cos[iang] # Precomputed cosine of projection angle
|
|
555
555
|
sin_a = d_sin[iang] # Precomputed sine of projection angle
|
|
556
556
|
# Normalize all physical distances to voxel units
|
|
557
|
-
u = (idet -
|
|
557
|
+
u = (idet - n_det * 0.5) * det_spacing / voxel_spacing # Detector coordinate in voxel units
|
|
558
558
|
|
|
559
559
|
# Define ray direction and starting point for parallel beam geometry
|
|
560
560
|
dir_x, dir_y = cos_a, sin_a
|
|
@@ -599,8 +599,8 @@ def _parallel_2d_backward_kernel(
|
|
|
599
599
|
dx, dy = mid_x - ix0, mid_y - iy0
|
|
600
600
|
|
|
601
601
|
# Clamp indices to stay in-bounds during interpolation
|
|
602
|
-
ix0 = min(ix0, Nx - 2)
|
|
603
|
-
iy0 = min(iy0, Ny - 2)
|
|
602
|
+
ix0 = max(0, min(ix0, Nx - 2))
|
|
603
|
+
iy0 = max(0, min(iy0, Ny - 2))
|
|
604
604
|
|
|
605
605
|
# === ATOMIC BACKPROJECTION WITH BILINEAR WEIGHTS ===
|
|
606
606
|
# Distribute contribution weighted by segment length and interpolation weights
|
|
@@ -686,7 +686,7 @@ def _fan_2d_forward_kernel(
|
|
|
686
686
|
cos_a = d_cos[iang] # Precomputed cosine of projection angle
|
|
687
687
|
sin_a = d_sin[iang] # Precomputed sine of projection angle
|
|
688
688
|
# Normalize all physical distances to voxel units
|
|
689
|
-
u = (idet -
|
|
689
|
+
u = (idet - n_det * 0.5) * det_spacing / voxel_spacing # Detector coordinate in voxel units
|
|
690
690
|
sid_v = sid / voxel_spacing # Source-to-isocenter distance in voxel units
|
|
691
691
|
sdd_v = sdd / voxel_spacing # Source-to-detector distance in voxel units
|
|
692
692
|
|
|
@@ -757,8 +757,8 @@ def _fan_2d_forward_kernel(
|
|
|
757
757
|
dx, dy = mid_x - ix0, mid_y - iy0
|
|
758
758
|
|
|
759
759
|
# Clamp indices to stay in-bounds during interpolation
|
|
760
|
-
ix0 = min(ix0, Nx - 2)
|
|
761
|
-
iy0 = min(iy0, Ny - 2)
|
|
760
|
+
ix0 = max(0, min(ix0, Nx - 2))
|
|
761
|
+
iy0 = max(0, min(iy0, Ny - 2))
|
|
762
762
|
|
|
763
763
|
# Bilinear interpolation (identical to parallel beam)
|
|
764
764
|
val = (
|
|
@@ -839,7 +839,7 @@ def _fan_2d_backward_kernel(
|
|
|
839
839
|
cos_a = d_cos[iang] # Precomputed cosine of projection angle
|
|
840
840
|
sin_a = d_sin[iang] # Precomputed sine of projection angle
|
|
841
841
|
# Normalize all physical distances to voxel units
|
|
842
|
-
u = (idet -
|
|
842
|
+
u = (idet - n_det * 0.5) * det_spacing / voxel_spacing # Detector coordinate in voxel units
|
|
843
843
|
sid_v = sid / voxel_spacing # Source-to-isocenter distance in voxel units
|
|
844
844
|
sdd_v = sdd / voxel_spacing # Source-to-detector distance in voxel units
|
|
845
845
|
|
|
@@ -901,8 +901,8 @@ def _fan_2d_backward_kernel(
|
|
|
901
901
|
dx, dy = mid_x - ix0, mid_y - iy0
|
|
902
902
|
|
|
903
903
|
# Clamp indices to stay in-bounds during interpolation
|
|
904
|
-
ix0 = min(ix0, Nx - 2)
|
|
905
|
-
iy0 = min(iy0, Ny - 2)
|
|
904
|
+
ix0 = max(0, min(ix0, Nx - 2))
|
|
905
|
+
iy0 = max(0, min(iy0, Ny - 2))
|
|
906
906
|
|
|
907
907
|
# === ATOMIC BACKPROJECTION WITH BILINEAR WEIGHTS ===
|
|
908
908
|
# Distribute contribution weighted by segment length and interpolation weights
|
|
@@ -995,8 +995,8 @@ def _cone_3d_forward_kernel(
|
|
|
995
995
|
# === 3D CONE BEAM GEOMETRY SETUP ===
|
|
996
996
|
cos_a, sin_a = d_cos[iview], d_sin[iview] # Projection angle trigonometry
|
|
997
997
|
# Normalize all physical distances to voxel units
|
|
998
|
-
u = (iu -
|
|
999
|
-
v = (iv -
|
|
998
|
+
u = (iu - n_u * 0.5) * du / voxel_spacing # Detector u-coordinate in voxel units
|
|
999
|
+
v = (iv - n_v * 0.5) * dv / voxel_spacing # Detector v-coordinate in voxel units
|
|
1000
1000
|
sid_v = sid / voxel_spacing # Source-to-isocenter distance in voxel units
|
|
1001
1001
|
sdd_v = sdd / voxel_spacing # Source-to-detector distance in voxel units
|
|
1002
1002
|
|
|
@@ -1091,9 +1091,9 @@ def _cone_3d_forward_kernel(
|
|
|
1091
1091
|
dx, dy, dz = mid_x - ix0, mid_y - iy0, mid_z - iz0 # Fractional parts: distance from base voxel center [0,1]
|
|
1092
1092
|
|
|
1093
1093
|
# Clamp indices to stay in-bounds during interpolation
|
|
1094
|
-
ix0 = min(ix0, Nx - 2)
|
|
1095
|
-
iy0 = min(iy0, Ny - 2)
|
|
1096
|
-
iz0 = min(iz0, Nz - 2)
|
|
1094
|
+
ix0 = max(0, min(ix0, Nx - 2))
|
|
1095
|
+
iy0 = max(0, min(iy0, Ny - 2))
|
|
1096
|
+
iz0 = max(0, min(iz0, Nz - 2))
|
|
1097
1097
|
|
|
1098
1098
|
# === TRILINEAR INTERPOLATION WEIGHT CALCULATION ===
|
|
1099
1099
|
# Mathematical basis: Trilinear interpolation formula f(x,y,z) = Σ f(xi,yi,zi) * wi(x,y,z)
|
|
@@ -1196,8 +1196,8 @@ def _cone_3d_backward_kernel(
|
|
|
1196
1196
|
g = d_sino[iview, iu, iv] # Sinogram value to backproject along this ray
|
|
1197
1197
|
cos_a, sin_a = d_cos[iview], d_sin[iview] # Projection angle trigonometry
|
|
1198
1198
|
# Normalize all physical distances to voxel units
|
|
1199
|
-
u = (iu -
|
|
1200
|
-
v = (iv -
|
|
1199
|
+
u = (iu - n_u * 0.5) * du / voxel_spacing # Detector u-coordinate in voxel units
|
|
1200
|
+
v = (iv - n_v * 0.5) * dv / voxel_spacing # Detector v-coordinate in voxel units
|
|
1201
1201
|
sid_v = sid / voxel_spacing # Source-to-isocenter distance in voxel units
|
|
1202
1202
|
sdd_v = sdd / voxel_spacing # Source-to-detector distance in voxel units
|
|
1203
1203
|
|
|
@@ -1281,9 +1281,9 @@ def _cone_3d_backward_kernel(
|
|
|
1281
1281
|
dx, dy, dz = mid_x - ix0, mid_y - iy0, mid_z - iz0 # Fractional parts for 3D weights
|
|
1282
1282
|
|
|
1283
1283
|
# Clamp indices to stay in-bounds during interpolation
|
|
1284
|
-
ix0 = min(ix0, Nx - 2)
|
|
1285
|
-
iy0 = min(iy0, Ny - 2)
|
|
1286
|
-
iz0 = min(iz0, Nz - 2)
|
|
1284
|
+
ix0 = max(0, min(ix0, Nx - 2))
|
|
1285
|
+
iy0 = max(0, min(iy0, Ny - 2))
|
|
1286
|
+
iz0 = max(0, min(iz0, Nz - 2))
|
|
1287
1287
|
|
|
1288
1288
|
# === ATOMIC BACKPROJECTION WITH TRILINEAR WEIGHTS ===
|
|
1289
1289
|
# Distribute contribution weighted by segment length and interpolation weights
|
|
@@ -1408,7 +1408,7 @@ class ParallelProjectorFunction(torch.autograd.Function):
|
|
|
1408
1408
|
sinogram = torch.zeros((n_angles, num_detectors), dtype=image.dtype, device=device)
|
|
1409
1409
|
|
|
1410
1410
|
# Prepare trigonometric tables on the correct device
|
|
1411
|
-
d_cos, d_sin = _trig_tables(angles, dtype=image.dtype)
|
|
1411
|
+
d_cos, d_sin = _trig_tables(angles, dtype=image.dtype, device=device)
|
|
1412
1412
|
|
|
1413
1413
|
# Get Numba CUDA array views for kernel
|
|
1414
1414
|
d_image = TorchCUDABridge.tensor_to_cuda_array(image)
|
|
@@ -1441,7 +1441,7 @@ class ParallelProjectorFunction(torch.autograd.Function):
|
|
|
1441
1441
|
|
|
1442
1442
|
n_angles = angles.shape[0]
|
|
1443
1443
|
grad_image = torch.zeros((Ny, Nx), dtype=grad_sinogram.dtype, device=device)
|
|
1444
|
-
d_cos, d_sin = _trig_tables(angles, dtype=grad_sinogram.dtype)
|
|
1444
|
+
d_cos, d_sin = _trig_tables(angles, dtype=grad_sinogram.dtype, device=device)
|
|
1445
1445
|
|
|
1446
1446
|
d_grad_sino = TorchCUDABridge.tensor_to_cuda_array(grad_sinogram)
|
|
1447
1447
|
d_img_grad = TorchCUDABridge.tensor_to_cuda_array(grad_image)
|
|
@@ -1541,7 +1541,7 @@ class ParallelBackprojectorFunction(torch.autograd.Function):
|
|
|
1541
1541
|
reco = torch.zeros((Ny, Nx), dtype=sinogram.dtype, device=device)
|
|
1542
1542
|
|
|
1543
1543
|
# Prepare trigonometric tables on the correct device
|
|
1544
|
-
d_cos, d_sin = _trig_tables(angles, dtype=sinogram.dtype)
|
|
1544
|
+
d_cos, d_sin = _trig_tables(angles, dtype=sinogram.dtype, device=device)
|
|
1545
1545
|
|
|
1546
1546
|
# Get Numba CUDA array views for kernel
|
|
1547
1547
|
d_sino = TorchCUDABridge.tensor_to_cuda_array(sinogram)
|
|
@@ -1578,7 +1578,7 @@ class ParallelBackprojectorFunction(torch.autograd.Function):
|
|
|
1578
1578
|
grad_sino = torch.zeros((n_ang, n_det), dtype=grad_output.dtype, device=device)
|
|
1579
1579
|
|
|
1580
1580
|
# Prepare trigonometric tables on the correct device
|
|
1581
|
-
d_cos, d_sin = _trig_tables(angles, dtype=grad_output.dtype)
|
|
1581
|
+
d_cos, d_sin = _trig_tables(angles, dtype=grad_output.dtype, device=device)
|
|
1582
1582
|
|
|
1583
1583
|
# Get Numba CUDA array views for kernel
|
|
1584
1584
|
d_grad_out = TorchCUDABridge.tensor_to_cuda_array(grad_output)
|
|
@@ -1679,7 +1679,7 @@ class FanProjectorFunction(torch.autograd.Function):
|
|
|
1679
1679
|
n_ang = angles.shape[0]
|
|
1680
1680
|
|
|
1681
1681
|
sinogram = torch.zeros((n_ang, num_detectors), dtype=image.dtype, device=device)
|
|
1682
|
-
d_cos, d_sin = _trig_tables(angles, dtype=image.dtype)
|
|
1682
|
+
d_cos, d_sin = _trig_tables(angles, dtype=image.dtype, device=device)
|
|
1683
1683
|
|
|
1684
1684
|
d_image = TorchCUDABridge.tensor_to_cuda_array(image)
|
|
1685
1685
|
d_sino = TorchCUDABridge.tensor_to_cuda_array(sinogram)
|
|
@@ -1713,7 +1713,7 @@ class FanProjectorFunction(torch.autograd.Function):
|
|
|
1713
1713
|
|
|
1714
1714
|
n_ang = angles.shape[0]
|
|
1715
1715
|
grad_img = torch.zeros((Ny, Nx), dtype=grad_sinogram.dtype, device=device)
|
|
1716
|
-
d_cos, d_sin = _trig_tables(angles, dtype=grad_sinogram.dtype)
|
|
1716
|
+
d_cos, d_sin = _trig_tables(angles, dtype=grad_sinogram.dtype, device=device)
|
|
1717
1717
|
|
|
1718
1718
|
d_grad_sino = TorchCUDABridge.tensor_to_cuda_array(grad_sinogram)
|
|
1719
1719
|
d_img_grad = TorchCUDABridge.tensor_to_cuda_array(grad_img)
|
|
@@ -1817,7 +1817,7 @@ class FanBackprojectorFunction(torch.autograd.Function):
|
|
|
1817
1817
|
Ny, Nx = H, W
|
|
1818
1818
|
|
|
1819
1819
|
reco = torch.zeros((Ny, Nx), dtype=sinogram.dtype, device=device)
|
|
1820
|
-
d_cos, d_sin = _trig_tables(angles, dtype=sinogram.dtype)
|
|
1820
|
+
d_cos, d_sin = _trig_tables(angles, dtype=sinogram.dtype, device=device)
|
|
1821
1821
|
|
|
1822
1822
|
d_sino = TorchCUDABridge.tensor_to_cuda_array(sinogram)
|
|
1823
1823
|
d_reco = TorchCUDABridge.tensor_to_cuda_array(reco)
|
|
@@ -1851,7 +1851,7 @@ class FanBackprojectorFunction(torch.autograd.Function):
|
|
|
1851
1851
|
Ny, Nx = grad_output.shape
|
|
1852
1852
|
|
|
1853
1853
|
grad_sino = torch.zeros((n_ang, n_det), dtype=grad_output.dtype, device=device)
|
|
1854
|
-
d_cos, d_sin = _trig_tables(angles, dtype=grad_output.dtype)
|
|
1854
|
+
d_cos, d_sin = _trig_tables(angles, dtype=grad_output.dtype, device=device)
|
|
1855
1855
|
|
|
1856
1856
|
d_grad_out = TorchCUDABridge.tensor_to_cuda_array(grad_output)
|
|
1857
1857
|
d_sino_grad = TorchCUDABridge.tensor_to_cuda_array(grad_sino)
|
|
@@ -1959,7 +1959,7 @@ class ConeProjectorFunction(torch.autograd.Function):
|
|
|
1959
1959
|
_validate_3d_memory_layout(volume, expected_order='DHW')
|
|
1960
1960
|
|
|
1961
1961
|
sino = torch.zeros((n_views, det_u, det_v), dtype=volume.dtype, device=device)
|
|
1962
|
-
d_cos, d_sin = _trig_tables(angles, dtype=volume.dtype)
|
|
1962
|
+
d_cos, d_sin = _trig_tables(angles, dtype=volume.dtype, device=device)
|
|
1963
1963
|
|
|
1964
1964
|
volume_perm = volume.permute(2, 1, 0).contiguous()
|
|
1965
1965
|
d_vol = TorchCUDABridge.tensor_to_cuda_array(volume_perm)
|
|
@@ -1997,7 +1997,7 @@ class ConeProjectorFunction(torch.autograd.Function):
|
|
|
1997
1997
|
n_views = angles.shape[0]
|
|
1998
1998
|
|
|
1999
1999
|
grad_vol_perm = torch.zeros((W, H, D), dtype=grad_sinogram.dtype, device=device)
|
|
2000
|
-
d_cos, d_sin = _trig_tables(angles, dtype=grad_sinogram.dtype)
|
|
2000
|
+
d_cos, d_sin = _trig_tables(angles, dtype=grad_sinogram.dtype, device=device)
|
|
2001
2001
|
|
|
2002
2002
|
d_grad_sino = TorchCUDABridge.tensor_to_cuda_array(grad_sinogram)
|
|
2003
2003
|
d_vol_grad = TorchCUDABridge.tensor_to_cuda_array(grad_vol_perm)
|
|
@@ -2116,7 +2116,7 @@ class ConeBackprojectorFunction(torch.autograd.Function):
|
|
|
2116
2116
|
_validate_3d_memory_layout(sinogram, expected_order='VHW')
|
|
2117
2117
|
|
|
2118
2118
|
vol_perm = torch.zeros((W, H, D), dtype=sinogram.dtype, device=device)
|
|
2119
|
-
d_cos, d_sin = _trig_tables(angles, dtype=sinogram.dtype)
|
|
2119
|
+
d_cos, d_sin = _trig_tables(angles, dtype=sinogram.dtype, device=device)
|
|
2120
2120
|
|
|
2121
2121
|
d_sino = TorchCUDABridge.tensor_to_cuda_array(sinogram)
|
|
2122
2122
|
d_reco = TorchCUDABridge.tensor_to_cuda_array(vol_perm)
|
|
@@ -2153,7 +2153,7 @@ class ConeBackprojectorFunction(torch.autograd.Function):
|
|
|
2153
2153
|
n_views = angles.shape[0]
|
|
2154
2154
|
|
|
2155
2155
|
grad_sino = torch.zeros((n_views, n_u, n_v), dtype=grad_output.dtype, device=device)
|
|
2156
|
-
d_cos, d_sin = _trig_tables(angles, dtype=grad_output.dtype)
|
|
2156
|
+
d_cos, d_sin = _trig_tables(angles, dtype=grad_output.dtype, device=device)
|
|
2157
2157
|
|
|
2158
2158
|
grad_output_perm = grad_output.permute(2, 1, 0).contiguous()
|
|
2159
2159
|
d_grad_out = TorchCUDABridge.tensor_to_cuda_array(grad_output_perm)
|
|
@@ -2,6 +2,7 @@ import math
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import torch
|
|
4
4
|
import matplotlib.pyplot as plt
|
|
5
|
+
import torch.nn.functional as F
|
|
5
6
|
from diffct.differentiable import FanProjectorFunction, FanBackprojectorFunction
|
|
6
7
|
|
|
7
8
|
|
|
@@ -76,9 +77,9 @@ def main():
|
|
|
76
77
|
sino_weighted = sinogram * weights
|
|
77
78
|
sinogram_filt = ramp_filter(sino_weighted)
|
|
78
79
|
|
|
79
|
-
reconstruction = FanBackprojectorFunction.apply(sinogram_filt, angles_torch,
|
|
80
|
+
reconstruction = F.relu(FanBackprojectorFunction.apply(sinogram_filt, angles_torch,
|
|
80
81
|
detector_spacing, Ny, Nx,
|
|
81
|
-
sdd, sid, voxel_spacing)
|
|
82
|
+
sdd, sid, voxel_spacing)) # ReLU to ensure non-negativity
|
|
82
83
|
|
|
83
84
|
# --- FBP normalization ---
|
|
84
85
|
# The backprojection is a sum over all angles. To approximate the integral,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import torch
|
|
3
3
|
import matplotlib.pyplot as plt
|
|
4
|
+
import torch.nn.functional as F
|
|
4
5
|
from diffct.differentiable import ParallelProjectorFunction, ParallelBackprojectorFunction
|
|
5
6
|
|
|
6
7
|
|
|
@@ -64,8 +65,8 @@ def main():
|
|
|
64
65
|
|
|
65
66
|
sinogram_filt = ramp_filter(sinogram)
|
|
66
67
|
|
|
67
|
-
reconstruction = ParallelBackprojectorFunction.apply(sinogram_filt, angles_torch,
|
|
68
|
-
detector_spacing, Ny, Nx, voxel_spacing)
|
|
68
|
+
reconstruction = F.relu(ParallelBackprojectorFunction.apply(sinogram_filt, angles_torch,
|
|
69
|
+
detector_spacing, Ny, Nx, voxel_spacing)) # ReLU to ensure non-negativity
|
|
69
70
|
|
|
70
71
|
# --- FBP normalization ---
|
|
71
72
|
# The backprojection is a sum over all angles. To approximate the integral,
|
|
@@ -2,6 +2,7 @@ import math
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import torch
|
|
4
4
|
import matplotlib.pyplot as plt
|
|
5
|
+
import torch.nn.functional as F
|
|
5
6
|
from diffct.differentiable import ConeProjectorFunction, ConeBackprojectorFunction
|
|
6
7
|
|
|
7
8
|
|
|
@@ -110,8 +111,8 @@ def main():
|
|
|
110
111
|
sino_weighted = sinogram * weights
|
|
111
112
|
sinogram_filt = ramp_filter_3d(sino_weighted).contiguous()
|
|
112
113
|
|
|
113
|
-
reconstruction = ConeBackprojectorFunction.apply(sinogram_filt, angles_torch, Nz, Ny, Nx,
|
|
114
|
-
du, dv, sdd, sid, voxel_spacing)
|
|
114
|
+
reconstruction = F.relu(ConeBackprojectorFunction.apply(sinogram_filt, angles_torch, Nz, Ny, Nx,
|
|
115
|
+
du, dv, sdd, sid, voxel_spacing)) # ReLU to ensure non-negativity
|
|
115
116
|
|
|
116
117
|
# --- FDK normalization ---
|
|
117
118
|
# The backprojection is a sum over all angles. To approximate the integral,
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "diffct"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.4"
|
|
8
8
|
description = "A CUDA-based library for computed tomography (CT) projection and reconstruction with differentiable operators"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|