CUDA Zeng forward and back projectors in RTK - #977
Conversation
|
Hi @CoConstan , thanks for this nice contribution! I tested your branch and made three small changes locally:
|
| recon.SetAlphaPSF(args_info.alphapsf) | ||
| if args_info.attenuationmap is not None: | ||
| recon.SetAttenuationMap(attenuation_map) | ||
|
|
There was a problem hiding this comment.
This code duplicates "Zeng", I guess you can just add an or in the Zeng condition above
| ) | ||
| rtkprojectors_group.add_argument( | ||
| "--sigmazero", | ||
| help="PSF value at a distance of 0 meter of the detector (Zeng only)", |
| ) | ||
| rtkprojectors_group.add_argument( | ||
| "--attenuationmap", | ||
| help="Attenuation map relative to the volume to perfom the attenuation correction (JosephAttenuated and Zeng)", |
| if args_info.alphapsf is not None: | ||
| recon.SetAlphaPSF(args_info.alphapsf) | ||
| if args_info.attenuationmap is not None: | ||
| recon.SetAttenuationMap(attenuation_map) |
There was a problem hiding this comment.
Idem, duplicates Zeng code and can be mutualized.
| ) | ||
| rtkprojectors_group.add_argument( | ||
| "--alphapsf", | ||
| help="Slope of the PSF against the detector distance (Zeng only)", |
| rotatedImage->SetOrigin(origin); | ||
|
|
||
| const MatrixType matrix = GetPhysicalPointToIndexMatrix(this->GetInput(1)).GetVnlMatrix() * | ||
| TransformMatrix(transform).GetVnlMatrix() * |
There was a problem hiding this comment.
Called once, you can just do it here without function
| } | ||
| } // namespace | ||
|
|
||
| CudaZengForwardProjectionImageFilter::CudaZengForwardProjectionImageFilter() { this->InPlaceOff(); } |
There was a problem hiding this comment.
Why default it to InPlaceOff? I'd keep the regular expected default
| if (!transform->GetInverse(inverse)) | ||
| itkGenericExceptionMacro(<< "Could not invert Zeng rotation transform."); | ||
| const MatrixType matrix = GetPhysicalPointToIndexMatrix(rotatedImage.GetPointer()).GetVnlMatrix() * | ||
| TransformMatrix(inverse).GetVnlMatrix() * |
There was a problem hiding this comment.
Called once, you can just do it here without function
| const MatrixType matrix = GetPhysicalPointToIndexMatrix(this->GetInput(1)).GetVnlMatrix() * | ||
| TransformMatrix(transform).GetVnlMatrix() * | ||
| GetIndexToPhysicalPointMatrix(rotatedImage.GetPointer()).GetVnlMatrix(); | ||
| StoreTextureMatrix(matrix, volumeRegion.GetIndex(), matrices.data() + 12 * local); |
There was a problem hiding this comment.
Called once, you can just do it here without function
| } | ||
| } // namespace | ||
|
|
||
| CudaZengBackProjectionImageFilter::CudaZengBackProjectionImageFilter() { this->InPlaceOff(); } |
There was a problem hiding this comment.
Why default it to InPlaceOff? I'd keep the regular expected default
Add CUDA Zeng forward and back projectors
Summary
This PR adds CUDA implementations of RTK's rotation-based Zeng forward and
back projectors for SPECT reconstruction. The implementation reproduces the
CPU Zeng slice recurrence, including the depth-dependent Gaussian point spread
function (PSF) and optional attenuation correction.
The new projectors operate on
itk::CudaImage<float, 3>and keep their maininputs and outputs on the GPU. They are available from C++, the command-line
applications, iterative reconstruction filters, and Python wrapping.
Main changes
rtk::CudaZengForwardProjectionImageFilter.rtk::CudaZengBackProjectionImageFilter.implementation, including Bessel-based coefficients, normalization, and
constant-zero boundary behavior.
outputs GPU-resident between CUDA filters.
iterative reconstruction subsets.
CudaZengto the forward/back projector selectors used by:rtkforwardprojections;rtkbackprojections;Automated tests
The existing CUDA Zeng tests now cover:
This PR also adds
rtkZengProjectionComparisonCudaTest, which performs directCPU/CUDA comparisons using separate
itk::Imageanditk::CudaImageinputs.It checks:
SigmaZero=1.5andAlpha=0.016;0.01;The comparison tolerances are:
5e-55e-55e-42e-3Measured values on the development system were:
1.31e-51.30e-52.31e-41.23e-3Compute Sanitizer was also run on the forward projector, backprojector, and a
short OSEM reconstruction. It reported zero memory errors and zero leaked
bytes.
Performance
Application-level measurements were made on an NVIDIA RTX 3500 Ada Laptop GPU
using a
64 x 64 x 64volume, 60 projections of64 x 64pixels, 4 mmspacing,
SigmaZero=1.5, andAlpha=0.016.An OSEM benchmark with five iterations and ten projections per subset produced
a median runtime of approximately 5.23 seconds. Persistent workspaces and
shared-memory convolution preserve bit-for-bit identical OSEM output compared
with the previous CUDA implementation.
Validation limitation of the local environment
The complete
RTKCudaTestDrivercannot currently be linked in the developmentenvironment because the installed older CudaCommon version fails while
compiling the unrelated 4D ROOSTER CUDA test. The new Zeng comparison and
adjointness test translation units compile successfully. A standalone driver
was used to execute the new comparison test, and all four comparisons passed.
This limitation is unrelated to the CUDA Zeng implementation and should not be
present with a current compatible ITK/CudaCommon build.