### Import Libraries and Setup Device Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Sparse Matrix Example.ipynb Imports core libraries for numerical operations, plotting, and TorchKBNufft. Sets up the computation device (GPU if available, otherwise CPU). ```python import time from warnings import filterwarnings import matplotlib.pyplot as plt import numpy as np import torch import torchkbnufft as tkbn from skimage.data import shepp_logan_phantom from mrisensesim import mrisensesim filterwarnings("ignore") # ignore floor divide warnings if torch.cuda.is_available(): device = torch.device("cuda") else: device = torch.device("cpu") ``` -------------------------------- ### Install and Import Required Packages Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Installs necessary libraries and imports them. Includes a fallback to clone the repository and copy a file if the module is not found. ```python # make sure we have all required packages !pip install torchkbnufft matplotlib numpy torch scikit-image try: from mrisensesim import mrisensesim except ModuleNotFoundError: !git clone https://github.com/mmuckley/torchkbnufft.git .tkbn_git !cp .tkbn_git/notebooks/mrisensesim.py . ``` -------------------------------- ### Import Libraries and Setup Device Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Imports essential libraries like torch, numpy, and matplotlib. It also sets up the computation device (GPU if available, otherwise CPU) and filters out potential warnings. ```python from warnings import filterwarnings import matplotlib.pyplot as plt import numpy as np import torch import torchkbnufft as tkbn from skimage.data import shepp_logan_phantom filterwarnings("ignore") # ignore floor divide warnings if torch.cuda.is_available(): device = torch.device("cuda") else: device = torch.device("cpu") ``` -------------------------------- ### Install TorchKBNufft and Dependencies Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Installs the necessary libraries including torchkbnufft, matplotlib, numpy, and scikit-image. Run this command in your environment before proceeding. ```python !pip install torchkbnufft matplotlib numpy scikit-image ``` -------------------------------- ### Install Dependencies and Profile Torchkbnufft Source: https://github.com/mmuckley/torchkbnufft/blob/main/README.md Install necessary development requirements and run the profiling script to benchmark computation times on your machine. ```python pip install -r dev-requirements.txt python profile_torchkbnufft.py ``` -------------------------------- ### Install TorchKbNufft Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/index.md Install the package using pip. TorchKbNufft has numpy, scipy, and torch as dependencies. ```bash pip install torchkbnufft ``` -------------------------------- ### ToepNufft Forward NUFFT Example Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.ToepNufft.md Demonstrates the basic usage of the ToepNufft class for a forward NUFFT operation. It requires an input image, sampling locations (omega), and a pre-calculated Toeplitz kernel. ```python >>> image = torch.randn(1, 1, 8, 8) + 1j * torch.randn(1, 1, 8, 8) >>> omega = torch.rand(2, 12) * 2 * np.pi - np.pi >>> toep_ob = tkbn.ToepNufft() >>> kernel = tkbn.calc_toeplitz_kernel(omega, im_size=(8, 8)) >>> image = toep_ob(image, kernel) ``` -------------------------------- ### Initialize KbNufft with Lower Oversampling Ratio Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/performance.md Reduce memory and computation by using a smaller oversampled grid. Initialize the KbNufft object with a custom grid_size, for example, 1.25-factor oversampling, if accuracy can be sacrificed. ```python grid_size = tuple([int(el * 1.25) for el in im_size]) forw_ob = tkbn.KbNufft(im_size=im_size, grid_size=grid_size) ``` -------------------------------- ### Initialize and Use KbInterp Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbInterp.md Demonstrates how to initialize the KbInterp object with image and grid sizes, and then use it to interpolate gridded image data to specified k-space locations (omega). ```python image = torch.randn(1, 1, 8, 8) + 1j * torch.randn(1, 1, 8, 8) omega = torch.rand(2, 12) * 2 * np.pi - np.pi kb_ob = tkbn.KbInterp(im_size=(8, 8), grid_size=(8, 8)) data = kb_ob(image, omega) ``` -------------------------------- ### Initialize and Use KbNufft Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbNufft.md Demonstrates how to initialize the KbNufft object with image dimensions and then apply it to transform an image to k-space data given a k-space trajectory. ```python >>> image = torch.randn(1, 1, 8, 8) + 1j * torch.randn(1, 1, 8, 8) >>> omega = torch.rand(2, 12) * 2 * np.pi - np.pi >>> kb_ob = tkbn.KbNufft(im_size=(8, 8)) >>> data = kb_ob(image, omega) ``` -------------------------------- ### Initialize and Use KbInterpAdjoint Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbInterpAdjoint.md Demonstrates how to initialize the KbInterpAdjoint layer with image and grid sizes, and then use it to interpolate scattered data to a grid. Ensure necessary imports like torch and numpy are present. ```python import torchkbnufft as tkbn import torch import numpy as np data = torch.randn(1, 1, 12) + 1j * torch.randn(1, 1, 12) omega = torch.rand(2, 12) * 2 * np.pi - np.pi adjkb_ob = tkbn.KbInterpAdjoint(im_size=(8, 8), grid_size=(8, 8)) image = adjkb_ob(data, omega) ``` -------------------------------- ### Initialize and Use KbNufftAdjoint Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbNufftAdjoint.md Demonstrates the basic initialization and usage of the KbNufftAdjoint class. This involves creating an instance with image and grid sizes, and then applying it to sample data and a k-space trajectory. ```python >>> data = torch.randn(1, 1, 12) + 1j * torch.randn(1, 1, 12) >>> omega = torch.rand(2, 12) * 2 * np.pi - np.pi >>> adjkb_ob = tkbn.KbNufftAdjoint(im_size=(8, 8)) >>> image = adjkb_ob(data, omega) ``` -------------------------------- ### Calculate Toeplitz Kernel Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.calc_toeplitz_kernel.md Demonstrates how to calculate the Toeplitz kernel using the calc_toeplitz_kernel function and then apply it using ToepNufft. Requires torch, numpy, and torchkbnufft. ```python import torch import torchkbnufft as tkbn import numpy as np image = torch.randn(1, 1, 8, 8) + 1j * torch.randn(1, 1, 8, 8) omega = torch.rand(2, 12) * 2 * np.pi - np.pi toep_ob = tkbn.ToepNufft() kernel = tkbn.calc_toeplitz_kernel(omega, im_size=(8, 8)) image = toep_ob(image, kernel) ``` -------------------------------- ### Instantiate NUFFT Objects Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Creates instances of the `KbNufft` (forward NUFFT) and `KbNufftAdjoint` (adjoint NUFFT) classes. These objects are configured with the image and grid sizes and moved to the appropriate device. ```python # create NUFFT objects, use 'ortho' for orthogonal FFTs nufft_ob = tkbn.KbNufft( im_size=im_size, grid_size=grid_size, ).to(image) adjnufft_ob = tkbn.KbNufftAdjoint( im_size=im_size, grid_size=grid_size, ).to(image) print(nufft_ob) print(adjnufft_ob) ``` -------------------------------- ### Initialize NUFFT Operators Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Initializes the forward (KbNufft) and adjoint (KbNufftAdjoint) NUFFT operators with specified image and grid sizes, and prints their configurations. ```python # build nufft operators nufft_ob = tkbn.KbNufft(im_size=im_size, grid_size=grid_size).to(image) adjnufft_ob = tkbn.KbNufftAdjoint(im_size=im_size, grid_size=grid_size).to(image) print(nufft_ob) print(adjnufft_ob) ``` -------------------------------- ### KbInterpAdjoint Class Initialization Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbInterpAdjoint.md Initializes the KbInterpAdjoint layer with specified image and grid sizes, interpolation parameters, and device settings. ```APIDOC ## KbInterpAdjoint ### Description Non-uniform Kaiser-Bessel interpolation adjoint layer. This object interpolates off-grid Fourier data to on-grid locations using a Kaiser-Bessel kernel. ### Parameters * **im_size** (Sequence[int]) - Size of image with length being the number of dimensions. * **grid_size** (Sequence[int] | None) - Size of grid to use for interpolation, typically 1.25 to 2 times `im_size`. Default: `2 * im_size` * **numpoints** (int | Sequence[int]) - Number of neighbors to use for interpolation in each dimension. Default: 6 * **n_shift** (Sequence[int] | None) - Size for `fftshift`. Default: `im_size // 2`. * **table_oversamp** (int | Sequence[int]) - Table oversampling factor. Default: 1024 * **kbwidth** (float) - Size of Kaiser-Bessel kernel. Default: 2.34 * **order** (float | Sequence[float]) - Order of Kaiser-Bessel kernel. Default: 0.0 * **dtype** (dtype | None) - Data type for tensor buffers. Default: `torch.get_default_dtype()` * **device** (device | None) - Which device to create tensors on. Default: `torch.device('cpu')` ### Examples ```pycon >>> import torch >>> import torchkbnufft as tkbn >>> import numpy as np >>> data = torch.randn(1, 1, 12) + 1j * torch.randn(1, 1, 12) >>> omega = torch.rand(2, 12) * 2 * np.pi - np.pi >>> adjkb_ob = tkbn.KbInterpAdjoint(im_size=(8, 8), grid_size=(8, 8)) >>> image = adjkb_ob(data, omega) ``` ``` -------------------------------- ### Build NUFFT Operators and Calculate Density Compensation Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Toeplitz Example.ipynb Initializes NUFFT and adjoint NUFFT operators using KbNufft and KbNufftAdjoint. It also initializes a ToepNufft operator and calculates the density compensation function for the given k-space trajectory. ```python # build nufft operators nufft_ob = tkbn.KbNufft(im_size=im_size, grid_size=grid_size).to(image) adjnufft_ob = tkbn.KbNufftAdjoint(im_size=im_size, grid_size=grid_size).to(image) toep_ob = tkbn.ToepNufft() dcomp = tkbn.calc_density_compensation_function(ktraj=ktraj, im_size=im_size) print(nufft_ob) print(adjnufft_ob) print(toep_ob) ``` -------------------------------- ### KbNufft Initialization Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbNufft.md Initializes the KbNufft object with specified image and grid dimensions, interpolation parameters, and device settings. ```APIDOC ## KbNufft ### Description Non-uniform FFT layer. This object applies the FFT and interpolates a grid of Fourier data to off-grid locations using a Kaiser-Bessel kernel. ### Parameters * **im_size** (Sequence[int]) - Size of image with length being the number of dimensions. * **grid_size** (Sequence[int] | None) - Size of grid to use for interpolation, typically 1.25 to 2 times `im_size`. Default: `2 * im_size` * **numpoints** (int | Sequence[int]) - Number of neighbors to use for interpolation in each dimension. Default: 6 * **n_shift** (Sequence[int] | None) - Size for `fftshift`. Default: `im_size // 2`. * **table_oversamp** (int | Sequence[int]) - Table oversampling factor. Default: 1024 * **kbwidth** (float) - Size of Kaiser-Bessel kernel. Default: 2.34 * **order** (float | Sequence[float]) - Order of Kaiser-Bessel kernel. Default: 0.0 * **dtype** (dtype | None) - Data type for tensor buffers. Default: `torch.get_default_dtype()` * **device** (device | None) - Which device to create tensors on. Default: `torch.device('cpu')` ### Example ```python >>> import torch >>> import numpy as np >>> import torchkbnufft as tkbn >>> image = torch.randn(1, 1, 8, 8) + 1j * torch.randn(1, 1, 8, 8) >>> omega = torch.rand(2, 12) * 2 * np.pi - np.pi >>> kb_ob = tkbn.KbNufft(im_size=(8, 8)) >>> data = kb_ob(image, omega) ``` ``` -------------------------------- ### Build Sparse Matrix and Perform NUFFT Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.calc_tensor_spmatrix.md Demonstrates how to use calc_tensor_spmatrix to build interpolation matrices and then apply them with KbNufftAdjoint for a NUFFT operation. Ensure torch and numpy are imported, and data/omega tensors are prepared. ```python import torch import numpy as np import torchkbnufft as tkbn data = torch.randn(1, 1, 12) + 1j * torch.randn(1, 1, 12) omega = torch.rand(2, 12) * 2 * np.pi - np.pi spmats = tkbn.calc_tensor_spmatrix(omega, (8, 8)) adjkb_ob = tkbn.KbNufftAdjoint(im_size=(8, 8)) image = adjkb_ob(data, omega, spmats) ``` -------------------------------- ### Adjoint NUFFT with Density Compensation Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Applies the forward NUFFT, followed by density compensation, and then the adjoint NUFFT. This path is an alternative to direct adjoint NUFFT. Ensure torchkbnufft is imported. ```python import torch import torchkbnufft from torchkbnufft.phantom import phantom # Device device = "cuda" if torch.cuda.is_available() else "cpu" # Create phantom N = 256 phantom_data = phantom(N) phantom_data = torch.complex(phantom_data[0], phantom_data[1]).to(device) # Create KB-NUFFT object kbuff = torchkbnufft.KbNufft(im_size=(N, N), device=device) # Create random non-uniform samples M = 10000 samples = torch.rand(2, M, device=device) * 2 * torch.pi # Forward NUFFT அது = kbuff(phantom_data, samples) # Density compensation dc = kbuff.dc(samples) # Adjoint NUFFT with density compensation image = kbuff.H(அது * dc, samples) # Plotting (optional, requires matplotlib) # import matplotlib.pyplot as plt # plt.figure(figsize=(12, 6)) # plt.subplot(1, 3, 1) # plt.imshow(phantom_data.cpu().numpy().real, cmap='gray') # plt.title('Original Phantom') # plt.subplot(1, 3, 2) # plt.scatter(samples.cpu().numpy()[0], samples.cpu().numpy()[1], s=1) # plt.title('Non-uniform Samples') # plt.subplot(1, 3, 3) # plt.imshow(image.cpu().numpy().real, cmap='gray') # plt.title('Reconstructed Image (with DC)') # plt.show() ``` -------------------------------- ### View KbNufft Documentation Source: https://github.com/mmuckley/torchkbnufft/blob/main/README.md Access the docstrings for the KbNufft class using the help() function in IPython. This allows for in-line documentation viewing. ```python from torchkbnufft import KbNufft help(KbNufft) ``` -------------------------------- ### Import Libraries and Set Device Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Imports core libraries for image processing, plotting, and NUFFT operations. Sets the computation device to CUDA if available, otherwise CPU. ```python from warnings import filterwarnings import matplotlib.pyplot as plt import numpy as np import torch import torchkbnufft as tkbn from skimage.data import shepp_logan_phantom from mrisensesim import mrisensesim filterwarnings("ignore") # ignore floor divide warnings if torch.cuda.is_available(): device = torch.device("cuda") else: device = torch.device("cpu") ``` -------------------------------- ### GPU Acceleration for NUFFT Operations Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/basic.md Shows how to move NUFFT objects and data to the GPU for accelerated computation. Ensure all objects (NUFFT object, data, trajectory) are on the same device and have matching dtypes to avoid errors. ```python adjnufft_ob = adjnufft_ob.to(torch.device('cuda')) kdata = kdata.to(torch.device('cuda')) ktraj = ktraj.to(torch.device('cuda')) image = adjnufft_ob(kdata, ktraj) ``` -------------------------------- ### Adjoint NUFFT Timing Comparison Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Sparse Matrix Example.ipynb Compares the performance of standard interpolation versus sparse matrix interpolation for the adjoint NUFFT problem. Sparse matrices typically offer a speedup in this case. ```python # adjnufft back # method 1: no density compensation (blurry image) # run timing tests here start_normal = time.perf_counter() image_blurry = adjnufft_ob(kdata, ktraj, smaps=smap) end_normal = time.perf_counter() start_spmat = time.perf_counter() image_blurry = adjnufft_ob(kdata, ktraj, interp_mats=interp_mats, smaps=smap) end_spmat = time.perf_counter() # print the timings # usually sparse matrices help here print('normal interpolation time: {}'.format(end_normal-start_normal)) print('sparse matrix interpolation time: {}'.format(end_spmat-start_spmat)) ``` -------------------------------- ### Timing and Difference Analysis Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Toeplitz Example.ipynb Prints the execution times for normal and Toeplitz NUFFT operations and calculates the normalized difference between their outputs. Use this to quantify performance gains and accuracy. ```python # print the timings and normalized differences # blurry (no density compensation) print('forward/backward without dcomp, normal time: {}, toeplitz time: {}'.format( end_normal_blurry-start_normal_blurry, end_toep_blurry-start_toep_blurry)) normdiff_blurry = ( torch.norm(torch.abs(image_blurry-image_blurry_toep)) / torch.norm(torch.abs(image_blurry)) ) print('normalized difference: {}'.format(normdiff_blurry)) # sharp (with density compensation) print('forward/backward with dcomp, normal time: {}, toeplitz time: {}'.format( end_normal_sharp-start_normal_sharp, end_toep_sharp-start_toep_sharp)) normdiff_sharp = ( torch.norm(torch.abs(image_sharp-image_sharp_toep)) / torch.norm(torch.abs(image_sharp)) ) print('normalized difference: {}'.format(normdiff_sharp)) ``` -------------------------------- ### Forward NUFFT Timing Comparison Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Sparse Matrix Example.ipynb Compares the performance of standard interpolation versus sparse matrix interpolation for the forward NUFFT problem. Includes adding noise for robustness testing. ```python # calculate k-space data, run some time tests start_normal = time.perf_counter() kdata = nufft_ob(image, ktraj, smaps=smap) end_normal = time.perf_counter() start_spmat = time.perf_counter() kdata = nufft_ob(image, ktraj, interp_mats=interp_mats, smaps=smap) end_spmat = time.perf_counter() # add some noise (robustness test) siglevel = torch.abs(kdata).mean() kdata = kdata + (siglevel/5) * torch.randn(kdata.shape).to(kdata) # write the timings # for forward problem, sparse matrices often don't help print('normal interpolation time: {}'.format(end_normal-start_normal)) print('sparse matrix interpolation time: {}'.format(end_spmat-start_spmat)) ``` -------------------------------- ### Forward and Adjoint NUFFT Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Applies the forward NUFFT transformation followed by the adjoint NUFFT transformation to a Shepp-Logan phantom. Ensure torchkbnufft is imported. ```python import torch import torchkbnufft from torchkbnufft.phantom import phantom # Device device = "cuda" if torch.cuda.is_available() else "cpu" # Create phantom N = 256 phantom_data = phantom(N) phantom_data = torch.complex(phantom_data[0], phantom_data[1]).to(device) # Create KB-NUFFT object kbuff = torchkbnufft.KbNufft(im_size=(N, N), device=device) # Create random non-uniform samples M = 10000 samples = torch.rand(2, M, device=device) * 2 * torch.pi # Forward NUFFT அது = kbuff(phantom_data, samples) # Adjoint NUFFT image = kbuff.H(அது, samples) # Plotting (optional, requires matplotlib) # import matplotlib.pyplot as plt # plt.figure(figsize=(12, 6)) # plt.subplot(1, 3, 1) # plt.imshow(phantom_data.cpu().numpy().real, cmap='gray') # plt.title('Original Phantom') # plt.subplot(1, 3, 2) # plt.scatter(samples.cpu().numpy()[0], samples.cpu().numpy()[1], s=1) # plt.title('Non-uniform Samples') # plt.subplot(1, 3, 3) # plt.imshow(image.cpu().numpy().real, cmap='gray') # plt.title('Reconstructed Image') # plt.show() ``` -------------------------------- ### Initialize KbNufft with Fewer Interpolation Neighbors Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/performance.md Improve speed by using fewer neighbors for interpolation. Initialize the KbNufft object with a custom numpoints value, such as 4, or specify different values for each dimension if accuracy requirements vary. ```python forw_ob = tkbn.KbNufft(im_size=im_size, numpoints=4) ``` ```python forw_ob = tkbn.KbNufft(im_size=im_size, numpoints=(4, 6, 6)) ``` -------------------------------- ### NUFFT Forward/Backward Operations Comparison Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Toeplitz Example.ipynb Compares standard NUFFT forward/backward passes with and without density compensation (dcomp) against Toeplitz operator equivalents. Use this to benchmark performance differences. ```python # run a forward/backward the normal way without dcomp start_normal_blurry = time.perf_counter() image_blurry = adjnufft_ob( nufft_ob(image, ktraj, smaps=smap, norm=norm), ktraj, smaps=smap, norm=norm, ) end_normal_blurry = time.perf_counter() # run a forward/backward the normal way with dcomp start_normal_sharp = time.perf_counter() image_sharp = adjnufft_ob( dcomp * nufft_ob(image, ktraj, smaps=smap, norm=norm), ktraj, smaps=smap, norm=norm, ) end_normal_sharp = time.perf_counter() # run a forward/backward with Toeplitz without dcomp start_toep_blurry = time.perf_counter() image_blurry_toep = toep_ob(image, normal_kernel, smaps=smap, norm=norm) end_toep_blurry = time.perf_counter() # run a forward/backward with Toeplitz with dcomp start_toep_sharp = time.perf_counter() image_sharp_toep = toep_ob(image, dcomp_kernel, smaps=smap, norm=norm) end_toep_sharp = time.perf_counter() ``` -------------------------------- ### Use Batched K-space Trajectories Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/performance.md Parallelize execution across multiple trajectories by providing a batched k-space input. This is particularly effective when the batch dimension (N) is large, such as in dynamic imaging. ```python import torch import torchkbnufft as tkbn import numpy as np from skimage.data import shepp_logan_phantom batch_size = 12 x = shepp_logan_phantom().astype(np.complex) im_size = x.shape # convert to tensor, unsqueeze batch and coil dimension # output size: (batch_size, 1, ny, nx) x = torch.tensor(x).unsqueeze(0).unsqueeze(0).to(torch.complex64) x = x.repeat(batch_size, 1, 1, 1) klength = 64 ktraj = np.stack( (np.zeros(64), np.linspace(-np.pi, np.pi, klength)) ) # convert to tensor, unsqueeze batch dimension # output size: (batch_size, 2, klength) ktraj = torch.tensor(ktraj).to(torch.float) ktraj = ktraj.unsqueeze(0).repeat(batch_size, 1, 1) nufft_ob = tkbn.KbNufft(im_size=im_size) # outputs a (batch_size, 1, klength) vector of k-space data kdata = nufft_ob(x, ktraj) ``` -------------------------------- ### KbInterp Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbInterp.md Initializes the KbInterp layer for non-uniform Kaiser-Bessel interpolation. This layer interpolates a grid of Fourier data to off-grid locations using a Kaiser-Bessel kernel. Parameters define the kernel properties and interpolation application. ```APIDOC ## Class: KbInterp ### Description Initializes the KbInterp layer for non-uniform Kaiser-Bessel interpolation. This layer interpolates a grid of Fourier data to off-grid locations using a Kaiser-Bessel kernel. Parameters define the kernel properties and interpolation application. ### Parameters * **im_size** (Sequence[int]) - Size of image with length being the number of dimensions. * **grid_size** (Sequence[int] | None) - Size of grid to use for interpolation, typically 1.25 to 2 times `im_size`. Default: `2 * im_size` * **numpoints** (int | Sequence[int]) - Number of neighbors to use for interpolation in each dimension. Default: 6 * **n_shift** (Sequence[int] | None) - Size for `fftshift`. Default: `im_size // 2`. * **table_oversamp** (int | Sequence[int]) - Table oversampling factor. Default: 1024 * **kbwidth** (float) - Size of Kaiser-Bessel kernel. Default: 2.34 * **order** (float | Sequence[float]) - Order of Kaiser-Bessel kernel. Default: 0.0 * **dtype** (dtype | None) - Data type for tensor buffers. Default: `torch.get_default_dtype()` * **device** (device | None) - Which device to create tensors on. Default: `torch.device('cpu')` ### Example ```python image = torch.randn(1, 1, 8, 8) + 1j * torch.randn(1, 1, 8, 8) omega = torch.rand(2, 12) * 2 * np.pi - np.pi kb_ob = tkbn.KbInterp(im_size=(8, 8), grid_size=(8, 8)) data = kb_ob(image, omega) ``` ``` -------------------------------- ### Create and Plot k-space Trajectory Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Generates a spiral k-space trajectory based on the image dimensions and number of spokes. The first 40 spokes are plotted to visualize the trajectory pattern. ```python # create a k-space trajectory and plot it spokelength = image.shape[-1] * 2 grid_size = (spokelength, spokelength) spokes = 405 ga = np.deg2rad(180 / ((1 + np.sqrt(5)) / 2)) kx = np.zeros(shape=(spokelength, nspokes)) ky = np.zeros(shape=(spokelength, nspokes)) ky[:, 0] = np.linspace(-np.pi, np.pi, spokelength) for i in range(1, nspokes): kx[:, i] = np.cos(ga) * kx[:, i - 1] - np.sin(ga) * ky[:, i - 1] ky[:, i] = np.sin(ga) * kx[:, i - 1] + np.cos(ga) * ky[:, i - 1] ky = np.transpose(ky) kx = np.transpose(kx) ktraj = np.stack((ky.flatten(), kx.flatten()), axis=0) # plot the first 40 spokes plt.plot(kx[:40, :].transpose(), ky[:40, :].transpose()) plt.axis('equal') plt.title('k-space trajectory (first 40 spokes)') plt.show() ``` -------------------------------- ### Simulate Sensitivity Maps and Plot Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Simulates sensitivity coil maps using mrisensesim and plots each coil's map. ```python # simulate some sensitivity coils (no phase for simplicity) ncoil = 8 smap = np.absolute(np.stack(mrisensesim(im_size, coil_width=64))).astype(complex) fig, axs = plt.subplots(2, 4) for i, ax in enumerate(axs.flat): ax.imshow(np.absolute(smap[i])) plt.gray() plt.show() ``` -------------------------------- ### Image Visualization and Residuals Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Toeplitz Example.ipynb Visualizes the reconstructed images from Toeplitz operations and their residuals compared to standard NUFFT results. Use this to visually assess the accuracy of the Toeplitz approximation. ```python # show the images from the toeplitz results with residuals to normal image_blurry_numpy = np.squeeze(image_blurry.cpu().numpy()) image_sharp_numpy = np.squeeze(image_sharp.cpu().numpy()) image_blurry_numpy_toep = np.squeeze(image_blurry_toep.cpu().numpy()) image_sharp_numpy_toep = np.squeeze(image_sharp_toep.cpu().numpy()) plt.figure(0) plt.imshow(np.absolute(image_blurry_numpy_toep)) plt.gray() plt.title('blurry image, toeplitz') plt.figure(1) plt.imshow(np.absolute(image_blurry_numpy_toep - image_blurry_numpy)) plt.gray() plt.title('residual, normdiff: {:.6e}'.format(normdiff_blurry)) plt.figure(2) plt.imshow(np.absolute(image_sharp_numpy_toep)) plt.gray() plt.title('sharp image, toeplitz') plt.figure(3) plt.imshow(np.absolute(image_sharp_numpy_toep - image_sharp_numpy)) plt.gray() plt.title('residual, normdiff: {:.6e}'.format(normdiff_sharp)) plt.show() ``` -------------------------------- ### Create and Plot k-space Trajectory Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Generates a k-space trajectory based on a golden-angle radial sampling scheme and plots the first 40 spokes. ```python # create a k-space trajectory and plot it spokelength = image.shape[-1] * 2 grid_size = (spokelength, spokelength) nspokes = 405 ga = np.deg2rad(180 / ((1 + np.sqrt(5)) / 2)) kx = np.zeros(shape=(spokelength, nspokes)) ky = np.zeros(shape=(spokelength, nspokes)) ky[:, 0] = np.linspace(-np.pi, np.pi, spokelength) for i in range(1, nspokes): kx[:, i] = np.cos(ga) * kx[:, i - 1] - np.sin(ga) * ky[:, i - 1] ky[:, i] = np.sin(ga) * kx[:, i - 1] + np.cos(ga) * ky[:, i - 1] ky = np.transpose(ky) kx = np.transpose(kx) ktraj = np.stack((ky.flatten(), kx.flatten()), axis=0) # plot the first 40 spokes plt.plot(kx[:40, :].transpose(), ky[:40, :].transpose()) plt.axis('equal') plt.title('k-space trajectory (first 40 spokes)') plt.show() ``` -------------------------------- ### Display Reconstructed Images Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Visualizes the blurry and sharp reconstructed images. Note that phase errors might occur due to the sensitivity maps. ```python # show the images, some phase errors may occur due to smap image_blurry_numpy = np.squeeze(image_blurry.cpu().numpy()) image_sharp_numpy = np.squeeze(image_sharp.cpu().numpy()) plt.figure(0) plt.imshow(np.absolute(image_blurry_numpy)) plt.gray() plt.title('blurry image') plt.figure(1) plt.imshow(np.absolute(image_sharp_numpy)) plt.gray() plt.title('sharp image (with density compensation)') plt.show() ``` -------------------------------- ### Reconstruct Image using Adjoint NUFFT Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Performs the adjoint NUFFT operation to reconstruct an image from the k-space data. Two methods are shown: one without density compensation (resulting in a blurry image) and one with density compensation (yielding a sharper image). ```python # adjnufft back # method 1: no density compensation (blurry image) image_blurry = adjnufft_ob(kdata, ktraj) # method 2: use density compensation dcomp = tkbn.calc_density_compensation_function(ktraj=ktraj, im_size=im_size) image_sharp = adjnufft_ob(kdata * dcomp, ktraj) ``` -------------------------------- ### NUFFT with Embedded Toeplitz Kernels Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/basic.md Demonstrates using embedded Toeplitz kernels as FFT filters for forward/backward NUFFT operations, which is useful for gradient descent algorithms. The ToepNufft object and precomputed kernel are used for the operation. ```python toep_ob = tkbn.ToepNufft() # precompute the embedded Toeplitz FFT kernel kernel = tkbn.calc_toeplitz_kernel(ktraj, im_size) # use FFT kernel from embedded Toeplitz matrix image = toep_ob(image, kernel) ``` -------------------------------- ### Compute NUFFT with Sparse Matrix Precomputation Source: https://github.com/mmuckley/torchkbnufft/blob/main/README.md Calculates sparse interpolation matrices and uses them with KbNufftAdjoint to compute an image from k-space data. Note: Sparse matrix multiplication is only implemented for real numbers in PyTorch. ```python adjnufft_ob = tkbn.KbNufftAdjoint(im_size=im_size) # precompute the sparse interpolation matrices interp_mats = tkbn.calc_tensor_spmatrix( ktraj, im_size=im_size ) # use sparse matrices in adjoint image = adjnufft_ob(kdata, ktraj, interp_mats) ``` -------------------------------- ### SENSE-NUFFT with Sensitivity Maps Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/basic.md Computes k-space data using SENSE-NUFFT by incorporating sensitivity maps. This involves multiplying by sensitivity coils before computing the radial spoke for each coil. ```python smaps = torch.rand(1, 8, 400, 400) + 1j * torch.rand(1, 8, 400, 400) sense_data = nufft_ob(x, ktraj, smaps=smaps.to(x)) ``` -------------------------------- ### Adjoint NUFFT Reconstruction Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Performs adjoint NUFFT reconstruction. Method 1 shows reconstruction without density compensation, resulting in a blurry image. Method 2 incorporates density compensation for a sharper image. ```python # adjnufft back # method 1: no density compensation (blurry image) image_blurry = adjnufft_ob(kdata, ktraj, smaps=smap) # method 2: use density compensation dcomp = tkbn.calc_density_compensation_function(ktraj=ktraj, im_size=im_size) image_sharp = adjnufft_ob(kdata * dcomp, ktraj, smaps=smap) ``` -------------------------------- ### Calculate k-space Data with Noise Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Computes the k-space data by applying the forward NUFFT operator to the image with sensitivity maps. Adds Gaussian noise to the k-space data for robustness testing. ```python # calculate k-space data kdata = nufft_ob(image, ktraj, smaps=smap) # add some noise (robustness test) siglevel = torch.abs(kdata).mean() kdata = kdata + (siglevel/5) * torch.randn(kdata.shape).to(kdata) ``` -------------------------------- ### Calculate k-space Data with Noise Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Performs the forward NUFFT operation to compute k-space data from the input image. Gaussian noise is added to the k-space data to simulate a more realistic scenario and test robustness. ```python # calculate k-space data kdata = nufft_ob(image, ktraj) # add some noise (robustness test) siglevel = torch.abs(kdata).mean() kdata = kdata + (siglevel/5) * torch.randn(kdata.shape).to(kdata) ``` -------------------------------- ### Plot NUFFT Kernel Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Visualizes the real and imaginary parts of the NUFFT kernel (table_0) used by the `KbNufft` object. This helps in understanding the internal workings of the NUFFT implementation. ```python # plot the kernel fig, axs = plt.subplots(1, 2) axs.flat[0].plot(np.real(nufft_ob.table_0.cpu().numpy())) axs.flat[1].plot(np.imag(nufft_ob.table_0.cpu().numpy())) plt.show() ``` -------------------------------- ### Convert Sensitivity Maps to Tensor Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Converts the simulated sensitivity maps to a PyTorch tensor, adding a batch dimension and moving it to the appropriate device. ```python # convert smaps to tensors, unsqueeze batch dimension smap = torch.tensor(smap).unsqueeze(0).to(image) ``` -------------------------------- ### Calculate Toeplitz Kernels Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Toeplitz Example.ipynb Calculates the Toeplitz kernels for the NUFFT operation, both with and without density compensation. The 'ortho' normalization is used for consistency. ```python # calculate the Toeplitz kernel with and without density compensation normal_kernel = tkbn.calc_toeplitz_kernel(ktraj, im_size, norm="ortho") # without density compensation dcomp_kernel = tkbn.calc_toeplitz_kernel(ktraj, im_size, weights=dcomp, norm="ortho") # with density compensation ``` -------------------------------- ### Display Reconstructed Images Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Displays the absolute values of the reconstructed images, comparing the blurry result obtained without density compensation to the sharper result achieved with it. This highlights the importance of density compensation in NUFFT reconstruction. ```python # show the images image_blurry_numpy = np.squeeze(image_blurry.cpu().numpy()) image_sharp_numpy = np.squeeze(image_sharp.cpu().numpy()) plt.figure(0) plt.imshow(np.absolute(image_blurry_numpy)) plt.gray() plt.title('blurry image') plt.figure(1) plt.imshow(np.absolute(image_sharp_numpy)) plt.gray() plt.title('sharp image (with Pipe dcomp)') plt.show() ``` -------------------------------- ### Convert k-space Trajectory to Tensor and Print Shape Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Converts the generated k-space trajectory to a PyTorch tensor and prints its shape. ```python # convert k-space trajectory to a tensor ktraj = torch.tensor(ktraj, device=device) print('ktraj shape: {}'.format(ktraj.shape)) ``` -------------------------------- ### Convert Phantom to Tensor and Print Shape Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Converts the phantom image to a PyTorch tensor, adding batch and coil dimensions, and prints its shape. ```python # convert the phantom to a tensor and unsqueeze coil and batch dimension image = torch.tensor(image, device=device).unsqueeze(0).unsqueeze(0) print('image shape: {}'.format(image.shape)) ``` -------------------------------- ### Convert k-data to NumPy and Plot Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/SENSE Example.ipynb Converts k-space data to a NumPy array and visualizes the magnitude of each coil's data using a logarithmic scale. Requires `numpy` and `matplotlib`. ```python # convert kdata to numpy and plot kdata_numpy = np.reshape(kdata.cpu().numpy(), (ncoil, nspokes, spokelength)) fig, axs = plt.subplots(2, 4) for i, ax in enumerate(axs.flat): ax.imshow(np.log10(np.absolute(kdata_numpy[i]))) plt.gray() plt.show() ``` -------------------------------- ### Convert k-space Trajectory to Tensor Source: https://github.com/mmuckley/torchkbnufft/blob/main/notebooks/Basic Example.ipynb Converts the generated numpy k-space trajectory to a PyTorch tensor and moves it to the selected device. This tensor will be used as input for the NUFFT operations. ```python # convert k-space trajectory to a tensor ktraj = torch.tensor(ktraj).to(device) print('ktraj shape: {}'.format(ktraj.shape)) ``` -------------------------------- ### Compute Simple Forward NUFFT Source: https://github.com/mmuckley/torchkbnufft/blob/main/README.md Loads a Shepp-Logan phantom and computes a single radial spoke of k-space data using KbNufft. Ensure data is converted to complex64 tensor with batch and coil dimensions. ```python import torch import torchkbnufft as tkbn import numpy as np from skimage.data import shepp_logan_phantom x = shepp_logan_phantom().astype(complex) im_size = x.shape # convert to tensor, unsqueeze batch and coil dimension # output size: (1, 1, ny, nx) x = torch.tensor(x).unsqueeze(0).unsqueeze(0).to(torch.complex64) klength = 64 ktraj = np.stack( (np.zeros(64), np.linspace(-np.pi, np.pi, klength)) ) # convert to tensor, unsqueeze batch dimension # output size: (2, klength) ktraj = torch.tensor(ktraj, dtype=torch.float) nufft_ob = tkbn.KbNufft(im_size=im_size) # outputs a (1, 1, klength) vector of k-space data kdata = nufft_ob(x, ktraj) ``` -------------------------------- ### KbNufftAdjoint Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbNufftAdjoint.md Initializes the KbNufftAdjoint layer. This layer is used for the adjoint NUFFT operation, which interpolates off-grid Fourier data to on-grid locations using a Kaiser-Bessel kernel before performing an inverse DFT. ```APIDOC ## KbNufftAdjoint ### Description Initializes the KbNufftAdjoint layer. This layer is used for the adjoint NUFFT operation, which interpolates off-grid Fourier data to on-grid locations using a Kaiser-Bessel kernel before performing an inverse DFT. ### Parameters * **im_size** (Sequence[int]) - Size of image with length being the number of dimensions. * **grid_size** (Sequence[int] | None, optional) - Size of grid to use for interpolation, typically 1.25 to 2 times `im_size`. Default: `2 * im_size`. * **numpoints** (int | Sequence[int], optional) - Number of neighbors to use for interpolation in each dimension. Default: 6. * **n_shift** (Sequence[int] | None, optional) - Size for `fftshift`. Default: `im_size // 2`. * **table_oversamp** (int | Sequence[int], optional) - Table oversampling factor. Default: 1024. * **kbwidth** (float, optional) - Size of Kaiser-Bessel kernel. Default: 2.34. * **order** (float | Sequence[float], optional) - Order of Kaiser-Bessel kernel. Default: 0.0. * **dtype** (dtype | None, optional) - Data type for tensor buffers. Default: `torch.get_default_dtype()`. * **device** (device | None, optional) - Which device to create tensors on. Default: `torch.device('cpu')`. ### Examples ```py >>> import torch >>> import torchkbnufft as tkbn >>> import numpy as np >>> data = torch.randn(1, 1, 12) + 1j * torch.randn(1, 1, 12) >>> omega = torch.rand(2, 12) * 2 * np.pi - np.pi >>> adjkb_ob = tkbn.KbNufftAdjoint(im_size=(8, 8)) >>> image = adjkb_ob(data, omega) ``` ``` -------------------------------- ### KbNufft.forward Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/generated/torchkbnufft.KbNufft.md Applies the forward non-uniform Fast Fourier Transform, interpolating from gridded data to scattered data points. ```APIDOC ## forward ### Description Apply FFT and interpolate from gridded data to scattered data. Input tensors should be of shape `(N, C) + im_size`, where `N` is the batch size and `C` is the number of sensitivity coils. `omega`, the k-space trajectory, should be of size `(len(grid_size), klength)` or `(N, len(grid_size), klength)`, where `klength` is the length of the k-space trajectory. ### Parameters * **image** (Tensor) - Object to calculate off-grid Fourier samples from. * **omega** (Tensor) - k-space trajectory (in radians/voxel). * **interp_mats** (Tuple[Tensor, Tensor] | None) - 2-tuple of real, imaginary sparse matrices to use for sparse matrix NUFFT interpolation (overrides default table interpolation). * **smaps** (Tensor | None) - Sensitivity maps. If input, these will be multiplied before the forward NUFFT. * **norm** (str | None) - Whether to apply normalization with the FFT operation. Options are `"ortho"` or `None`. ### Returns `Tensor` - `image` calculated at Fourier frequencies specified by `omega`. ``` -------------------------------- ### Compute Radial Spoke with Sparse Matrices Source: https://github.com/mmuckley/torchkbnufft/blob/main/docs/source/basic.md Calculates sparse interpolation matrices and uses them with KbNufftAdjoint to compute a single radial spoke of k-space data. Note that sparse matrix multiplication is primarily for real numbers in PyTorch. ```python adjnufft_ob = tkbn.KbNufftAdjoint(im_size=im_size) # precompute the sparse interpolation matrices interp_mats = tkbn.calc_tensor_spmatrix( ktraj, im_size=im_size ) # use sparse matrices in adjoint image = adjnufft_ob(kdata, ktraj, interp_mats) ```