API Reference
This section provides documentation for the differentiable CT operators
and analytical reconstruction helpers in diffct. Each autograd class
is a PyTorch torch.autograd.Function that runs a CUDA kernel under
the hood, enabling gradient propagation through the full projection /
backprojection pipeline. The analytical helpers (ramp_filter_1d,
cone_cosine_weights, cone_weighted_backproject and friends) are
intentionally kept as plain functions so they can be composed with the
autograd operators or used directly in one-shot FBP / FDK pipelines.
Overview
The library exposes three families of operators, grouped by geometry:
Parallel beam (2D): parallel-beam geometry for 2D reconstruction.
Fan beam (2D): divergent fan-beam geometry.
Cone beam (3D): full 3D cone-beam geometry for volumetric reconstruction with a 2D flat-panel detector.
Each geometry ships a differentiable forward projector and a
differentiable backprojector that are adjoints of each other (the
backprojector is the gradient of the projector). For cone-beam the
analytical FDK path is routed through a dedicated voxel-driven gather
kernel; see the cone_weighted_backproject notes below.
Parallel Beam Operators
The parallel beam geometry assumes parallel X-ray beams, commonly used in synchrotron CT and some medical CT scanners.
- class diffct.differentiable.ParallelProjectorFunction(*args, **kwargs)[source]
Bases:
FunctionSummary
PyTorch autograd function for differentiable 2D parallel beam forward projection.
Notes
Provides a differentiable interface to the CUDA-accelerated Siddon ray-tracing method with interpolation for parallel beam CT geometry. The forward pass computes the sinogram from a 2D image using parallel beam geometry. The backward pass computes gradients using the adjoint backprojection operation. Requires CUDA-capable hardware and a properly configured CUDA environment; all input tensors must reside on the same CUDA device.
Examples
>>> import torch >>> from diffct.differentiable import ParallelProjectorFunction >>> >>> # Create a 2D image with gradient tracking >>> image = torch.randn(128, 128, device='cuda', requires_grad=True) >>> # Define projection parameters >>> angles = torch.linspace(0, torch.pi, 180, device='cuda') >>> num_detectors = 128 >>> detector_spacing = 1.0 >>> # Compute forward projection >>> projector = ParallelProjectorFunction.apply >>> sinogram = projector(image, angles, num_detectors, detector_spacing) >>> # Compute loss and gradients >>> loss = sinogram.sum() >>> loss.backward() >>> print(f"Gradient shape: {image.grad.shape}") # (128, 128)
- static backward(ctx, grad_sinogram)[source]
Define a formula for differentiating the operation with backward mode automatic differentiation.
This function is to be overridden by all subclasses. (Defining this function is equivalent to defining the
vjpfunction.)It must accept a context
ctxas the first argument, followed by as many outputs as theforward()returned (None will be passed in for non tensor outputs of the forward function), and it should return as many tensors, as there were inputs toforward(). Each argument is the gradient w.r.t the given output, and each returned value should be the gradient w.r.t. the corresponding input. If an input is not a Tensor or is a Tensor not requiring grads, you can just pass None as a gradient for that input.The context can be used to retrieve tensors saved during the forward pass. It also has an attribute
ctx.needs_input_gradas a tuple of booleans representing whether each input needs gradient. E.g.,backward()will havectx.needs_input_grad[0] = Trueif the first input toforward()needs gradient computed w.r.t. the output.
- static forward(ctx, image, angles, num_detectors, detector_spacing=1.0, voxel_spacing=1.0, detector_offset=0.0, center_offset_x=0.0, center_offset_y=0.0)[source]
Compute the 2D parallel beam forward projection (Radon transform) of an image using CUDA acceleration.
- Parameters:
image (torch.Tensor) – 2D input image tensor of shape (H, W), must be on a CUDA device and of type float32.
angles (torch.Tensor) – 1D tensor of projection angles in radians, shape (num_angles,), must be on the same CUDA device as image.
num_detectors (int) – Number of detector elements in the sinogram (columns).
detector_spacing (float, optional) – Physical spacing between detector elements (default: 1.0).
voxel_spacing (float, optional) – Physical size of one voxel (in same units as detector_spacing, default: 1.0).
- Returns:
sinogram – 2D tensor of shape (num_angles, num_detectors) containing the forward projection (sinogram) on the same device as image.
- Return type:
torch.Tensor
Notes
All input tensors must be on the same CUDA device.
The operation is fully differentiable and supports autograd.
Uses the Siddon method with interpolation for accurate ray tracing and bilinear interpolation.
Examples
>>> image = torch.randn(128, 128, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, torch.pi, 180, device='cuda') >>> sinogram = ParallelProjectorFunction.apply( ... image, angles, 128, 1.0 ... )
- class diffct.differentiable.ParallelBackprojectorFunction(*args, **kwargs)[source]
Bases:
FunctionSummary
PyTorch autograd function for differentiable 2D parallel beam backprojection.
Notes
Provides a differentiable interface to the CUDA-accelerated Siddon ray-tracing method with interpolation for parallel beam backprojection. The forward pass computes a 2D reconstruction from sinogram data using parallel beam backprojection, and the backward pass computes gradients via forward projection as the adjoint operation. Requires CUDA-capable hardware and consistent device placements.
Examples
>>> import torch >>> from diffct.differentiable import ParallelBackprojectorFunction >>> >>> sinogram = torch.randn(180, 128, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, torch.pi, 180, device='cuda') >>> recon = ParallelBackprojectorFunction.apply(sinogram, angles, 1.0, 128, 128) >>> loss = recon.sum() >>> loss.backward() >>> print(sinogram.grad.shape) # (180, 128)
- static backward(ctx, grad_output)[source]
Define a formula for differentiating the operation with backward mode automatic differentiation.
This function is to be overridden by all subclasses. (Defining this function is equivalent to defining the
vjpfunction.)It must accept a context
ctxas the first argument, followed by as many outputs as theforward()returned (None will be passed in for non tensor outputs of the forward function), and it should return as many tensors, as there were inputs toforward(). Each argument is the gradient w.r.t the given output, and each returned value should be the gradient w.r.t. the corresponding input. If an input is not a Tensor or is a Tensor not requiring grads, you can just pass None as a gradient for that input.The context can be used to retrieve tensors saved during the forward pass. It also has an attribute
ctx.needs_input_gradas a tuple of booleans representing whether each input needs gradient. E.g.,backward()will havectx.needs_input_grad[0] = Trueif the first input toforward()needs gradient computed w.r.t. the output.
- static forward(ctx, sinogram, angles, detector_spacing=1.0, H=128, W=128, voxel_spacing=1.0, detector_offset=0.0, center_offset_x=0.0, center_offset_y=0.0)[source]
Compute the 2D parallel beam backprojection (adjoint Radon transform) of a sinogram using CUDA acceleration.
- Parameters:
sinogram (torch.Tensor) – 2D input sinogram tensor of shape (num_angles, num_detectors), must be on a CUDA device and of type float32.
angles (torch.Tensor) – 1D tensor of projection angles in radians, shape (num_angles,), must be on the same CUDA device as sinogram.
detector_spacing (float, optional) – Physical spacing between detector elements (default: 1.0).
H (int, optional) – Height of the output reconstruction image (default: 128).
W (int, optional) – Width of the output reconstruction image (default: 128).
voxel_spacing (float, optional) – Physical size of one voxel (in same units as detector_spacing, default: 1.0).
- Returns:
reco – 2D tensor of shape (H, W) containing the reconstructed image on the same device as sinogram.
- Return type:
torch.Tensor
Notes
All input tensors must be on the same CUDA device.
The operation is fully differentiable and supports autograd.
Uses the Siddon method with interpolation for accurate ray tracing and bilinear interpolation.
Examples
>>> sinogram = torch.randn(180, 128, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, torch.pi, 180, device='cuda') >>> reco = ParallelBackprojectorFunction.apply( ... sinogram, angles, 1.0, 128, 128 ... )
Fan Beam Operators
Fan beam geometry uses a point X-ray source with a fan-shaped beam, typical in medical CT scanners.
- class diffct.differentiable.FanProjectorFunction(*args, **kwargs)[source]
Bases:
FunctionSummary
PyTorch autograd function for differentiable 2D fan beam forward projection.
Notes
Provides a differentiable interface to the CUDA-accelerated Siddon ray-tracing method with interpolation for fan beam geometry, where rays diverge from a point X-ray source to a linear detector array. The forward pass computes sinograms using divergent beam geometry. The backward pass returns the pure adjoint
P^T(Siddon scatter, no distance weighting) so that it is the correct gradient ofy = P(x)with respect tox. Analytical fan-beam FBP distance weighting is handled infan_weighted_backproject, not here.Examples
>>> import torch >>> from diffct.differentiable import FanProjectorFunction >>> >>> image = torch.randn(256, 256, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2 * torch.pi, 360, device='cuda') >>> sinogram = FanProjectorFunction.apply(image, angles, 512, 1.0, 1500.0, 1000.0) >>> loss = sinogram.sum() >>> loss.backward() >>> print(image.grad.shape) # (256, 256)
- static backward(ctx, grad_sinogram)[source]
Define a formula for differentiating the operation with backward mode automatic differentiation.
This function is to be overridden by all subclasses. (Defining this function is equivalent to defining the
vjpfunction.)It must accept a context
ctxas the first argument, followed by as many outputs as theforward()returned (None will be passed in for non tensor outputs of the forward function), and it should return as many tensors, as there were inputs toforward(). Each argument is the gradient w.r.t the given output, and each returned value should be the gradient w.r.t. the corresponding input. If an input is not a Tensor or is a Tensor not requiring grads, you can just pass None as a gradient for that input.The context can be used to retrieve tensors saved during the forward pass. It also has an attribute
ctx.needs_input_gradas a tuple of booleans representing whether each input needs gradient. E.g.,backward()will havectx.needs_input_grad[0] = Trueif the first input toforward()needs gradient computed w.r.t. the output.
- static forward(ctx, image, angles, num_detectors, detector_spacing, sdd, sid, voxel_spacing=1.0, detector_offset=0.0, center_offset_x=0.0, center_offset_y=0.0, backend='siddon')[source]
Compute the 2D fan beam forward projection of an image using CUDA acceleration.
- Parameters:
backend (str, optional) – Forward projector backend.
"siddon"(default) is the existing ray-driven Siddon kernel with bilinear interpolation at segment midpoints."sf"is a prototype separable-footprint (SF-TR) voxel-driven projector that projects each voxel’s footprint as a trapezoid on the detector and integrates it closed-form over each detector cell; it is mass-conserving and closer to the physical finite-width-cell integral, at the cost of being forward-only (no autograd backward yet).image (torch.Tensor) – 2D input image tensor of shape (H, W), must be on a CUDA device and of type float32.
angles (torch.Tensor) – 1D tensor of projection angles in radians, shape (num_angles,), must be on the same CUDA device as image.
num_detectors (int) – Number of detector elements in the sinogram (columns).
detector_spacing (float) – Physical spacing between detector elements.
sdd (float) – Source-to-Detector Distance (SDD). The total distance from the X-ray source to the detector, passing through the isocenter.
sid (float) – Source-to-Isocenter Distance (SID). The distance from the X-ray source to the center of rotation (isocenter).
voxel_spacing (float, optional) – Physical size of one voxel (in same units as detector_spacing, sdd, sid, default: 1.0).
- Returns:
sinogram – 2D tensor of shape (num_angles, num_detectors) containing the fan beam sinogram on the same device as image.
- Return type:
torch.Tensor
Notes
All input tensors must be on the same CUDA device.
The operation is fully differentiable and supports autograd.
Fan beam geometry uses divergent rays from a point source to the detector.
Uses the Siddon method with interpolation for accurate ray tracing and bilinear interpolation.
Examples
>>> image = torch.randn(256, 256, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2 * torch.pi, 360, device='cuda') >>> sinogram = FanProjectorFunction.apply( ... image, angles, 512, 1.0, 1500.0, 1000.0 ... )
- class diffct.differentiable.FanBackprojectorFunction(*args, **kwargs)[source]
Bases:
FunctionSummary
PyTorch autograd function for differentiable 2D fan beam backprojection.
Notes
Provides a differentiable interface to the CUDA-accelerated Siddon ray-tracing method with interpolation for fan beam backprojection. The forward pass runs the pure adjoint
P^Tof the fan forward projector (Siddon scatter with bilinear interpolation, no distance weighting), and the backward pass computes gradients via forward projection. The forward/backward pair is therefore a self-consistent autograd adjoint pair. Analytical FBP distance weighting lives infan_weighted_backprojectand is not applied here.Examples
>>> import torch >>> from diffct.differentiable import FanBackprojectorFunction >>> >>> sinogram = torch.randn(360, 512, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2 * torch.pi, 360, device='cuda') >>> recon = FanBackprojectorFunction.apply(sinogram, angles, 1.0, 256, 256, 1500.0, 1000.0) >>> loss = recon.sum() >>> loss.backward() >>> print(sinogram.grad.shape) # (360, 512)
- static backward(ctx, grad_output)[source]
Define a formula for differentiating the operation with backward mode automatic differentiation.
This function is to be overridden by all subclasses. (Defining this function is equivalent to defining the
vjpfunction.)It must accept a context
ctxas the first argument, followed by as many outputs as theforward()returned (None will be passed in for non tensor outputs of the forward function), and it should return as many tensors, as there were inputs toforward(). Each argument is the gradient w.r.t the given output, and each returned value should be the gradient w.r.t. the corresponding input. If an input is not a Tensor or is a Tensor not requiring grads, you can just pass None as a gradient for that input.The context can be used to retrieve tensors saved during the forward pass. It also has an attribute
ctx.needs_input_gradas a tuple of booleans representing whether each input needs gradient. E.g.,backward()will havectx.needs_input_grad[0] = Trueif the first input toforward()needs gradient computed w.r.t. the output.
- static forward(ctx, sinogram, angles, detector_spacing, H, W, sdd, sid, voxel_spacing=1.0, detector_offset=0.0, center_offset_x=0.0, center_offset_y=0.0, backend='siddon')[source]
Compute the 2D fan beam backprojection of a sinogram using CUDA acceleration.
- Parameters:
backend (str, optional) – Adjoint backend, matches
FanProjectorFunction."siddon"(default) uses the ray-driven Siddon scatter kernel,"sf"uses the voxel-driven gather adjoint of the SF-TR projector.sinogram (torch.Tensor) – 2D input fan beam sinogram tensor of shape (num_angles, num_detectors), must be on a CUDA device and of type float32.
angles (torch.Tensor) – 1D tensor of projection angles in radians, shape (num_angles,), must be on the same CUDA device as sinogram.
detector_spacing (float) – Physical spacing between detector elements.
H (int) – Height of the output reconstruction image.
W (int) – Width of the output reconstruction image.
sdd (float) – Source-to-Detector Distance (SDD). The total distance from the X-ray source to the detector, passing through the isocenter.
sid (float) – Source-to-Isocenter Distance (SID). The distance from the X-ray source to the center of rotation (isocenter).
voxel_spacing (float, optional) – Physical size of one voxel (in same units as detector_spacing, sdd, sid, default: 1.0).
- Returns:
reco – 2D tensor of shape (H, W) containing the reconstructed image on the same device as sinogram.
- Return type:
torch.Tensor
Notes
All input tensors must be on the same CUDA device.
The operation is fully differentiable and supports autograd.
Fan beam geometry uses divergent rays from a point source to the detector.
Uses the Siddon method with interpolation for accurate ray tracing and bilinear interpolation.
Examples
>>> sinogram = torch.randn(360, 512, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2*torch.pi, 360, device='cuda') >>> reco = FanBackprojectorFunction.apply( ... sinogram, angles, 1.0, 256, 256, 1000.0, 500.0 ... )
Cone Beam Operators
Cone beam geometry extends fan beam to 3D with a cone-shaped X-ray beam
and a 2D detector. ConeProjectorFunction and
ConeBackprojectorFunction are exact adjoints of each other (Siddon
traversal with trilinear interpolation) and drive the iterative cone
example. For analytical FDK, use cone_weighted_backproject — it
dispatches to a dedicated voxel-driven gather kernel with the classical
(sid/U)^2 distance weighting.
Projector backends
Both the fan and cone Projector / Backprojector Function pairs accept a
backend keyword that selects the underlying CUDA kernel family:
"siddon"(default)Ray-driven Siddon traversal with bilinear (2D) / trilinear (3D) interpolation. Fastest, byte-for-byte unchanged from 1.2.11 and earlier, and is the right default for the overwhelming majority of use cases.
"sf"(fan only)Voxel-driven separable-footprint projector (Long-Fessler-Balter, IEEE TMI 2010). Each voxel’s footprint is a trapezoid built from the four projected corners and is closed-form integrated over each detector cell. Matched forward/adjoint kernels so autograd and the standalone Backprojector both work; use for iterative reconstruction under a mass-conserving cell-integral forward model.
"sf_tr"(cone only)3D voxel-driven SF with a trapezoidal transaxial footprint and a rectangular axial footprint evaluated at voxel-centre magnification.
"sf_tt"(cone only)Same transaxial trapezoid as SF-TR but the axial footprint is also a trapezoid built from the four
(U_near, U_far) × (z_bot, z_top)projections. Captures axial magnification variation inside a single voxel at large cone angles. Strictly more expressive than SF-TR but 2–4× slower.
All three SF backends go through matched scatter/gather kernel pairs
with byte-accurate adjoints (verified by test_adjoint_inner_product
and test_gradcheck).
Which backend to pick. At the thin-ray sampling level, measured
against the analytical ellipse / ellipsoid Radon transform, Siddon is
slightly more accurate than SF (~3 % better RMSE, ~15 % better max
error) because diffct’s Siddon uses smooth interpolation rather than
nearest-neighbour sampling. However, in a complete analytical
reconstruction pipeline (forward projection -> ramp filter ->
fan_weighted_backproject / cone_weighted_backproject) SF
measurably improves the reconstruction MSE: on the standard 256²
examples/fbp_fan.py phantom the raw MSE drops from 0.00216
(Siddon) to 0.00178 (SF), a ~17 % improvement; on the 128³
examples/fdk_cone.py phantom it drops from 0.00325 (Siddon)
to 0.00271 (SF-TR), again ~17 %. The mechanism is that SF’s
mass-conserving cell-integrated sinogram pairs more naturally with
the voxel-driven FBP / FDK gather backprojector than Siddon’s
thin-ray sampling, which feeds extra high-frequency ringing into
the ramp filter.
So:
"siddon"— default for backward compatibility and raw forward speed. Best choice when you only need a forward projection and not a reconstruction."sf"/"sf_tr"— recommended for analytical FBP / FDK reconstruction when reconstruction quality matters more than forward-projection runtime. ~2–3× slower forward, ~17 % lower reconstruction MSE on standard CT phantoms."sf_tt"— strictly more expressive than SF-TR but its axial-trapezoid refinement improves RMSE by only ~0.001 at the cost of another ~40 % forward runtime; use for extreme cone angles or algorithm research only.
For iterative reconstruction with autograd, any backend is valid; SF has the theoretical advantage of exact voxel-scale mass conservation on both forward and adjoint, but in practice Siddon’s smoothing helps optimizer convergence.
- class diffct.differentiable.ConeProjectorFunction(*args, **kwargs)[source]
Bases:
FunctionSummary
PyTorch autograd function for differentiable 3D cone beam forward projection.
Notes
Provides a differentiable interface to the CUDA-accelerated Siddon ray-tracing method with interpolation for 3D cone beam geometry. Rays emanate from a point X-ray source to a 2D detector array capturing volumetric projection data. The forward pass computes 3D projections. The backward pass returns the pure adjoint
P^Tof the forward projector - a Siddon ray-driven scatter with trilinear interpolation weights and no distance weighting - so thatP^Tis the mathematically correct gradient ofy = P(x)with respect tox. Analytical FDK distance weighting is handled separately incone_weighted_backproject, not here.Examples
>>> import torch >>> from diffct.differentiable import ConeProjectorFunction >>> >>> volume = torch.randn(128, 128, 128, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2 * torch.pi, 360, device='cuda') >>> projections = ConeProjectorFunction.apply(volume, angles, 256, 256, 1.0, 1.0, 1500.0, 1000.0) >>> loss = projections.sum() >>> loss.backward() >>> print(volume.grad.shape) # (128, 128, 128)
- static backward(ctx, grad_sinogram)[source]
Define a formula for differentiating the operation with backward mode automatic differentiation.
This function is to be overridden by all subclasses. (Defining this function is equivalent to defining the
vjpfunction.)It must accept a context
ctxas the first argument, followed by as many outputs as theforward()returned (None will be passed in for non tensor outputs of the forward function), and it should return as many tensors, as there were inputs toforward(). Each argument is the gradient w.r.t the given output, and each returned value should be the gradient w.r.t. the corresponding input. If an input is not a Tensor or is a Tensor not requiring grads, you can just pass None as a gradient for that input.The context can be used to retrieve tensors saved during the forward pass. It also has an attribute
ctx.needs_input_gradas a tuple of booleans representing whether each input needs gradient. E.g.,backward()will havectx.needs_input_grad[0] = Trueif the first input toforward()needs gradient computed w.r.t. the output.
- static forward(ctx, volume, angles, det_u, det_v, du, dv, sdd, sid, voxel_spacing=1.0, detector_offset_u=0.0, detector_offset_v=0.0, center_offset_x=0.0, center_offset_y=0.0, center_offset_z=0.0, backend='siddon')[source]
Compute the 3D cone beam forward projection of a volume using CUDA acceleration.
- Parameters:
backend (str, optional) – Forward projector backend.
"siddon"(default) is the existing ray-driven trilinear Siddon kernel."sf_tr"and"sf_tt"are separable-footprint voxel-driven projectors (Long et al., IEEE TMI 2010). SF-TR uses a trapezoidal transaxial footprint and a rectangular axial footprint evaluated at voxel-centre magnification; SF-TT uses trapezoids in BOTH directions, with the axial trapezoid built fromU_nearandU_faracross the voxel to capture axial magnification variation at large cone angles. Both SF backends have matched voxel-driven gather adjoint kernels and support autograd.volume (torch.Tensor) – 3D input volume tensor of shape (D, H, W), must be on a CUDA device and of type float32.
angles (torch.Tensor) – 1D tensor of projection angles in radians, shape (num_views,), must be on the same CUDA device as volume.
det_u (int) – Number of detector elements along the u-axis (width).
det_v (int) – Number of detector elements along the v-axis (height).
du (float) – Physical spacing between detector elements along the u-axis.
dv (float) – Physical spacing between detector elements along the v-axis.
sdd (float) – Source-to-Detector Distance (SDD). The total distance from the X-ray source to the detector, passing through the isocenter.
sid (float) – Source-to-Isocenter Distance (SID). The distance from the X-ray source to the center of rotation (isocenter).
voxel_spacing (float, optional) – Physical size of one voxel (in same units as du, dv, sdd, sid, default: 1.0).
- Returns:
sino – 3D tensor of shape (num_views, det_u, det_v) containing the cone beam projections on the same device as volume.
- Return type:
torch.Tensor
Notes
All input tensors must be on the same CUDA device.
The operation is fully differentiable and supports autograd.
Cone beam geometry uses a point source and a 2D detector array.
Uses the Siddon method with interpolation for accurate 3D ray tracing and trilinear interpolation.
Examples
>>> volume = torch.randn(128, 128, 128, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2*torch.pi, 360, device='cuda') >>> sino = ConeProjectorFunction.apply( ... volume, angles, 256, 256, 1.0, 1.0, 1500.0, 1000.0 ... )
- class diffct.differentiable.ConeBackprojectorFunction(*args, **kwargs)[source]
Bases:
FunctionSummary
PyTorch autograd function for differentiable 3D cone beam backprojection.
Notes
Provides a differentiable interface to the CUDA-accelerated Siddon ray-tracing method with interpolation for 3D cone beam backprojection. The forward pass runs the pure adjoint
P^Tof the cone forward projector: a Siddon ray-driven scatter with trilinear interpolation weights and no distance weighting. The backward pass computes gradients via 3D cone beam forward projection, which is exactly the adjoint of this pureP^T- so the forward/backward pair is self-consistent for autograd. Analytical FDK distance weighting belongs incone_weighted_backprojectand is not applied here. Requires CUDA-capable hardware and consistent device placements.This operation may be memory- and computationally-intensive due to 3D geometry. Consider using gradient checkpointing, smaller volumes, or distributed computing for large-scale applications, and ensure sufficient GPU memory is available.
Examples
>>> import torch >>> from diffct.differentiable import ConeBackprojectorFunction >>> >>> projections = torch.randn(360, 256, 256, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2 * torch.pi, 360, device='cuda') >>> D, H, W = 128, 128, 128 >>> du, dv = 1.0, 1.0 >>> sdd, sid = 1500.0, 1000.0 >>> backprojector = ConeBackprojectorFunction.apply >>> volume = backprojector(projections, angles, D, H, W, du, dv, sdd, sid) >>> loss = volume.sum() >>> loss.backward() >>> print(f"Projection gradient shape: {projections.grad.shape}") # (360, 256, 256)
- static backward(ctx, grad_output)[source]
Define a formula for differentiating the operation with backward mode automatic differentiation.
This function is to be overridden by all subclasses. (Defining this function is equivalent to defining the
vjpfunction.)It must accept a context
ctxas the first argument, followed by as many outputs as theforward()returned (None will be passed in for non tensor outputs of the forward function), and it should return as many tensors, as there were inputs toforward(). Each argument is the gradient w.r.t the given output, and each returned value should be the gradient w.r.t. the corresponding input. If an input is not a Tensor or is a Tensor not requiring grads, you can just pass None as a gradient for that input.The context can be used to retrieve tensors saved during the forward pass. It also has an attribute
ctx.needs_input_gradas a tuple of booleans representing whether each input needs gradient. E.g.,backward()will havectx.needs_input_grad[0] = Trueif the first input toforward()needs gradient computed w.r.t. the output.
- static forward(ctx, sinogram, angles, D, H, W, du, dv, sdd, sid, voxel_spacing=1.0, detector_offset_u=0.0, detector_offset_v=0.0, center_offset_x=0.0, center_offset_y=0.0, center_offset_z=0.0, backend='siddon')[source]
Compute the 3D cone beam backprojection of a projection sinogram using CUDA acceleration.
- Parameters:
backend (str, optional) – Adjoint backend, matches
ConeProjectorFunction."siddon"(default),"sf_tr", or"sf_tt". SeeConeProjectorFunctionfor details.sinogram (torch.Tensor) – 3D input cone beam projection tensor of shape (num_views, det_u, det_v), must be on a CUDA device and of type float32.
angles (torch.Tensor) – 1D tensor of projection angles in radians, shape (num_views,), must be on the same CUDA device as sinogram.
D (int) – Depth (z-dimension) of the output reconstruction volume.
H (int) – Height (y-dimension) of the output reconstruction volume.
W (int) – Width (x-dimension) of the output reconstruction volume.
du (float) – Physical spacing between detector elements along the u-axis.
dv (float) – Physical spacing between detector elements along the v-axis.
sdd (float) – Source-to-Detector Distance (SDD). The total distance from the X-ray source to the detector, passing through the isocenter.
sid (float) – Source-to-Isocenter Distance (SID). The distance from the X-ray source to the center of rotation (isocenter).
voxel_spacing (float, optional) – Physical size of one voxel (in same units as du, dv, sdd, sid, default: 1.0).
- Returns:
vol – 3D tensor of shape (D, H, W) containing the reconstructed volume on the same device as sinogram.
- Return type:
torch.Tensor
Notes
All input tensors must be on the same CUDA device.
The operation is fully differentiable and supports autograd.
Cone beam geometry uses a point source and a 2D detector array.
Uses the Siddon method with interpolation for accurate 3D ray tracing and trilinear interpolation.
Examples
>>> projections = torch.randn(360, 256, 256, device='cuda', requires_grad=True) >>> angles = torch.linspace(0, 2*torch.pi, 360, device='cuda') >>> vol = ConeBackprojectorFunction.apply( ... projections, angles, 128, 128, 128, 1.0, 1.0, 1500.0, 1000.0 ... )
Analytical Reconstruction Helpers
These helpers build the per-view pre-weights, angle-integration weights, filter, and backprojection pieces of an analytical FBP/FDK pipeline. They are plain functions (no autograd state) and can be freely mixed with the autograd operators above.
- diffct.differentiable.detector_coordinates_1d(num_detectors, detector_spacing, detector_offset=0.0, device=None, dtype=torch.float32)[source]
Return centered detector coordinates in physical units.
Coordinates follow the convention
(i - (N-1)/2) * spacing + offset. This avoids a half-pixel center bias for even detector counts.
- diffct.differentiable.angular_integration_weights(angles, redundant_full_scan=True)[source]
Compute per-view integration weights from the provided angle samples.
- Parameters:
angles (torch.Tensor) – 1D projection angles in radians.
redundant_full_scan (bool, optional) – If
True, applies a 0.5 factor for near-2*piscans to account for view redundancy in reconstruction formulas using full circular data.
- diffct.differentiable.fan_cosine_weights(num_detectors, detector_spacing, sdd, detector_offset=0.0, device=None, dtype=torch.float32)[source]
Return fan-beam cosine pre-weights
cos(gamma)for each detector bin.
- diffct.differentiable.cone_cosine_weights(det_u, det_v, du, dv, sdd, detector_offset_u=0.0, detector_offset_v=0.0, device=None, dtype=torch.float32)[source]
Return FDK cosine pre-weights
D/sqrt(D^2 + u^2 + v^2)on a 2D detector.
- diffct.differentiable.parker_weights(angles, num_detectors, detector_spacing, sdd, detector_offset=0.0)[source]
Return Parker redundancy weights for fan/cone short-scan geometries.
For full scans (near
2*pi), this returns all ones.
- diffct.differentiable.ramp_filter_1d(sinogram_tensor, dim=-1, sample_spacing=1.0, pad_factor=1, window=None, use_rfft=True)[source]
Apply a 1D ramp filter along
dimusing FFT.- Parameters:
sinogram_tensor (torch.Tensor) – Real-valued sinogram tensor.
dim (int, optional) – Axis along which to filter. Default
-1.sample_spacing (float, optional) – Physical spacing between detector samples along
dim. The continuous ramp filter has units of1/length, so the FFT output is rescaled by1/sample_spacingto respect physical units. Default1.0(pure sample-unit behavior, matching the historical call signature).pad_factor (int, optional) – Zero-pad the signal to
pad_factor * Nalongdimbefore the FFT to suppress circular-convolution wrap-around artifacts. Default1(no padding).window (str or None, optional) – Frequency-domain apodization:
None/"ram-lak"(unwindowed),"hann","hamming","cosine", or"shepp-logan". DefaultNone.use_rfft (bool, optional) – Use the real-valued FFT path when
True. DefaultTrue.
Notes
Pre-existing callers that pass only
(sinogram_tensor, dim)keep the same filter shape because the defaults preserve the original behavior up to the rfft path (which is numerically equivalent for real inputs) and the1/sample_spacingscale (which is exactly 1 whensample_spacing=1.0).
- diffct.differentiable.parallel_weighted_backproject(sinogram, angles, detector_spacing, H, W, voxel_spacing=1.0, detector_offset=0.0, center_offset_x=0.0, center_offset_y=0.0)[source]
Parallel-beam backprojection for analytical FBP pipelines.
Dispatches to the dedicated parallel-beam FBP voxel-driven gather kernel (
_parallel_2d_fbp_backproject_kernel). The shared Siddon-based parallel backward kernel continues to serve the autograd adjoint path used byParallelProjectorFunction/ParallelBackprojectorFunction; this function is only the analytical FBP path. There is no distance weighting in parallel geometry (no source, no magnification), so the kernel simply gathers one linearly-interpolated detector sample per view per pixel and sums. The analytical1/(2*pi)FBP constant is applied inside the wrapper so the returned image is already amplitude-calibrated - a unit-density disk reconstructs to amplitude 1.
- diffct.differentiable.fan_weighted_backproject(sinogram, angles, detector_spacing, H, W, sdd, sid, voxel_spacing=1.0, detector_offset=0.0, center_offset_x=0.0, center_offset_y=0.0, backend='siddon')[source]
Fan-beam weighted backprojection for analytical FBP pipelines.
Dispatches to one of two gather kernels based on
backend:"siddon"(default): the bilinear voxel-driven fan FBP gather_fan_2d_fbp_backproject_kernel— fastest, bilinearly samples the filtered sinogram at each pixel’s projected u-coordinate."sf": the separable-footprint fan FBP gather_fan_2d_sf_fbp_backproject_kernel— integrates the filtered sinogram over each pixel’s trapezoidal footprint. At nominal voxel size (detector_spacing * sid / sdd) this gives nearly the same MTF as the bilinear gather; at sub-nominal voxel sizes it gives measurably higher spatial resolution, and at supra- nominal it gives higher SNR (see LEAP’s SF vs VD analysis).
Both paths apply the
(SID / U)^2FBP weight per view inside the kernel and thesdd / (2 * pi * sid)analytical FBP scale in Python, so amplitude calibration is preserved across backends. The Siddon-based fan autograd adjoint (_fan_2d_backward_kernel) and the SF-matched adjoint (_fan_2d_sf_backward_kernel) stay unchanged — they continue to serveFanProjectorFunction/FanBackprojectorFunctionand are not touched by this analytical wrapper.
- diffct.differentiable.cone_weighted_backproject(sinogram, angles, D, H, W, du, dv, sdd, sid, voxel_spacing=1.0, detector_offset_u=0.0, detector_offset_v=0.0, center_offset_x=0.0, center_offset_y=0.0, center_offset_z=0.0, backend='siddon')[source]
Cone-beam weighted backprojection for analytical FDK pipelines.
Dispatches to one of three gather kernels based on
backend:"siddon"(default): the bilinear voxel-driven FDK gather_cone_3d_fdk_backproject_kernel— fastest, bilinearly samples the filtered sinogram at each voxel’s projected(u, v)."sf_tr": the separable-footprint FDK gather_cone_3d_sf_tr_fdk_backproject_kernel— integrates the filtered sinogram over each voxel’s transaxial trapezoidal footprint and axial rectangular footprint at the voxel-centre magnification."sf_tt": the separable-footprint FDK gather_cone_3d_sf_tt_fdk_backproject_kernel— same transaxial trapezoid as"sf_tr"but the axial footprint is also a trapezoid built from four z-corner projections, which more faithfully captures the axial magnification variation inside one voxel at large cone angles.
At nominal voxel size (
du * sid / sdd) the three backends give nearly identical MTFs; at sub-nominal voxels the SF variants give measurably higher spatial resolution, and at supra-nominal voxels they give higher SNR (see LEAP’s SF vs VD analysis). All three paths apply the(SID / U)^2FDK weight per view inside the kernel and thesdd / (2 * pi * sid)analytical FDK scale in Python, so amplitude calibration is preserved across backends. The Siddon-based cone autograd adjoint (_cone_3d_backward_kernel) and the SF-matched adjoints (_cone_3d_sf_tr_backward_kernel/_cone_3d_sf_tt_backward_kernel) stay unchanged — they continue to serveConeProjectorFunction/ConeBackprojectorFunctionand are not touched by this wrapper.
Ramp Filter Options
ramp_filter_1d is a generic 1D ramp filter used by every analytical
reconstruction example. Its call signature is:
ramp_filter_1d(sinogram_tensor, dim=-1, sample_spacing=1.0,
pad_factor=1, window=None, use_rfft=True)
sample_spacingPhysical detector-cell spacing along
dim(e.g.dufor the cone-beam case). The filter is rescaled by1 / sample_spacinginternally so the output is in physical units and the reconstruction amplitude stays calibrated when detector pitch changes. Pass1.0to reproduce the historical sample-unit behavior.pad_factorZero-pad the signal to
pad_factor * Nsamples alongdimbefore the FFT. Common values:1(default): no padding, fastest but prone to circular convolution wrap-around at the detector edges.2: recommended for cone-beam FDK, good trade-off.4: stricter padding for objects close to the detector edge.
windowFrequency-domain apodization multiplied onto the bare Ram-Lak ramp:
Noneor"ram-lak": unwindowed Ram-Lak, sharpest, highest noise."hann": Hann window, strong high-frequency suppression."hamming": slightly milder than Hann."cosine": half-cosine rolloff."shepp-logan":sincrolloff, classical choice.
use_rfftUse
torch.fft.rfft/irfftwhen the input is real. Defaults toTrue; set toFalseonly if you need the complex FFT path.
Analytical FBP / FDK architecture
Each of the three analytical backprojection helpers dispatches to a
dedicated voxel-driven gather kernel (parallel / fan / cone), separate
from the Siddon-based ray-driven scatter kernels that drive autograd.
The autograd kernels are the pure adjoints P^T of the forward
projectors (no distance weighting, no magnification scale), so the
autograd classes form self-consistent forward/backward adjoint pairs.
The analytical helpers on top apply the appropriate FBP/FDK distance
weighting and the analytical scale so the returned image is already
amplitude-calibrated.
Scale factors applied inside the analytical helpers:
parallel_weighted_backproject: multiplies by1 / (2 * pi)(Fourier-convention constant; parallel beam has no source and no magnification).fan_weighted_backproject: multiplies bysdd / (2 * pi * sid)(Fourier constant times physical-detector-to-isocenter-plane magnification).cone_weighted_backproject: multiplies bysdd / (2 * pi * sid)(same derivation as the fan case; the extra third dimension does not change the ramp-filter convention).
All three helpers use (sid / U)^2 as the FDK/FBP distance weight
for the divergent-beam cases (fan and cone), where
U = sid + x*sin(phi) - y*cos(phi) is the distance from the source
to the pixel/voxel along the central ray direction. Parallel beam has
no distance weighting.
Usage Notes
Memory Management
All operators work on CUDA tensors for optimal performance.
Ensure sufficient GPU memory for the chosen volume / sinogram size.
Use
torch.cuda.empty_cache()to release cached allocations when switching to a large job.
Gradient Computation
All autograd operators support automatic differentiation.
Gradients flow through both forward and adjoint paths.
Set
requires_grad=Trueon input tensors to enable gradients.Analytical helpers (
ramp_filter_1d,angular_integration_weights,cone_weighted_backproject, …) are also differentiable in the torch sense (they are pure tensor ops / autograd.Functions), so you can use them inside a loss.
Performance Considerations
Pass contiguous tensors with the expected dimension order (
(D, H, W)for 3D volumes,(num_views, det_u, det_v)for cone sinograms). The library validates layout and will raise if a non-contiguous or transposed tensor is passed.Use
pad_factor=2inramp_filter_1dfor cone-beam FDK; the extra FFT cost is usually negligible compared to the backprojection.
Coordinate Systems
Image / volume coordinates: integer indices,
(0, 0, 0)at the corner of the array.Detector coordinates: centered at the detector array center, with
uin-plane andvaxial for cone beam.Rotation: counter-clockwise around the z-axis (right-hand rule).