Source code for nabu.reconstruction.fbp

import numpy as np
import pycuda.driver as cuda
from ..utils import updiv, get_cuda_srcfile
from ..cuda.utils import copy_array, check_textures_availability
from ..cuda.processing import CudaProcessing
from ..cuda.kernel import CudaKernel
from .filtering_cuda import CudaSinoFilter
from .sinogram_cuda import CudaSinoMult
from .fbp_base import BackprojectorBase


[docs] class CudaBackprojector(BackprojectorBase): backend = "cuda" kernel_filename = "backproj.cu" backend_processing_class = CudaProcessing SinoFilterClass = CudaSinoFilter SinoMultClass = CudaSinoMult def _check_textures_availability(self): self._use_textures = self.extra_options.get("use_textures", True) and check_textures_availability() def _get_kernel_signature(self): kern_full_sig = list("PPiifiiffPPPf") if self._axis_correction is None: kern_full_sig[11] = "" if self._use_textures: # texture references - no object is passed (deprecated, removed in Cuda 12) kern_full_sig[1] = "" return "".join(kern_full_sig) def _get_kernel_options(self): super()._get_kernel_options() self._kernel_options.update( { "file_name": get_cuda_srcfile(self.kernel_filename), "kernel_signature": self._get_kernel_signature(), "texture_name": "tex_projections", } ) def _prepare_kernel_args(self): super()._prepare_kernel_args() self.kern_proj_kwargs.update( { "shared_size": self._kernel_options["shared_size"], } ) # texture references - no object is passed (deprecated, removed in Cuda 12) if self._use_textures: self.kern_proj_args.pop(1) else: self._d_sino = self._processing.allocate_array("_d_sino", self.sino_shape) self.kern_proj_args[1] = self._d_sino.gpudata def _prepare_textures(self): if self._use_textures: self.texref_proj = self.gpu_projector.module.get_texref(self._kernel_options["texture_name"]) self.texref_proj.set_filter_mode(cuda.filter_mode.LINEAR) self.gpu_projector.prepare(self._kernel_options["kernel_signature"], [self.texref_proj]) # Bind texture self._d_sino_cua = cuda.np_to_array(np.zeros(self.sino_shape, "f"), "C") self.texref_proj.set_array(self._d_sino_cua) else: # d_sino_ref = self._d_sino.gpudata # self.kern_proj_args.insert(2, d_sino_ref) self.gpu_projector.prepare(self._kernel_options["kernel_signature"], []) def _compile_kernels(self): self._prepare_kernel_args() if self._use_textures: self._kernel_options["sourcemodule_options"].append("-DUSE_TEXTURES") self.gpu_projector = CudaKernel( self._kernel_options["kernel_name"], filename=self._kernel_options["file_name"], options=self._kernel_options["sourcemodule_options"], ) if self.halftomo and self.rot_center < self.dwidth: self.sino_mult = CudaSinoMult(self.sino_shape, self.rot_center, ctx=self._processing.ctx) self._prepare_textures() # has to be done after compilation for Cuda (to bind texture to built kernel) def _transfer_to_texture(self, sino, do_checks=True): if self._use_textures: copy_array(self._d_sino_cua, sino, check=do_checks) else: if id(self._d_sino) == id(sino): return self._d_sino[:] = sino[:]
# COMPAT. Backprojector = CudaBackprojector
[docs] class PolarBackprojector(Backprojector): """ Cuda Backprojector with output in polar coordinates. """ cuda_fname = "backproj_polar.cu" cuda_kernel_name = "backproj_polar" # patch parent method: force slice_shape to (n_angles, n_x) def _set_angles(self, angles, n_angles): Backprojector._set_angles(self, angles, n_angles) self.slice_shape = (self.n_angles, self.n_x) # patch parent method: def _set_slice_roi(self, slice_roi): if slice_roi is not None: raise ValueError("slice_roi is not supported with this class") Backprojector._set_slice_roi(self, slice_roi) # patch parent method: don't do the 4X compute-workload optimization for this kernel def _get_kernel_options(self): Backprojector._get_kernel_options(self) block = self._kernel_options["block"] self._kernel_options["grid"] = (updiv(self.n_x, block[0]), updiv(self.n_y, block[1])) # patch parent method: update kernel args def _compile_kernels(self): n_y = self.n_y self.n_y = self.n_angles Backprojector._compile_kernels(self) self.n_y = n_y