A Deep Nonlinear Subspace Modeling and Reconstruction for Diffusion-Weighted Imaging Using Denoising Auto-Encoder.
The 5 matches
- [1] § Methods › Bias and Precision Analysis ↔ figures/fig5/create_violin_plots.py, lines 350–437 · score 0.87 · Cortico Spinal Tract, Corpus Callosum, CST, CC, crossing, precision
- [2] § Methods › Latent‐Space Decoded Reconstruction ↔ sigpy/mri/app.py, lines 723–817 · score 0.75 · coil sensitivity maps, Fourier transform, phase maps, shot phases, SMS, operator
- [3] § Methods › Latent‐Space Decoded Reconstruction ↔ code/laser/reconstruction/reconstruction.py, lines 447–555 · score 0.65 · phase encoding, diffusion weightings, multi shot, collapsed, readout, SMS
- [4] § Methods › Latent‐Space Decoded Reconstruction ↔ sigpy/mri/muse.py, lines 230–300 · score 0.65 · phase encoding, diffusion weightings, multi shot, collapsed, readout, SMS
- [5] § Methods › Reconstruction Comparison ↔ sigpy/mri/app.py, lines 723–817 · score 0.59 · low rank, Shot phases, block, TV, stride, LLR
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 1,151 lines · 36 KB · BSD-3-Clause · 2 matches
- # -*- coding: utf-8 -*-
- """MRI applications.
- """
- import numpy as np
- import sigpy as sp
- from sigpy.mri import linop, nlop
- from sigpy.mri.dims import *
- __all__ = ['SenseRecon', 'L1WaveletRecon', 'TotalVariationRecon',
- 'JsenseRecon', 'EspiritCalib', 'HighDimensionalRecon',
- 'ModelDiffRecon']
- def _estimate_weights(y, weights, coord, coil_dim=0):
- if weights is None and coord is None:
- with sp.get_device(y):
- weights = (sp.rss(y, axes=(coil_dim, ),
- keepdims=True) > 0).astype(y.dtype)
- if weights is None and coord is not None:
- with sp.get_device(y):
- weights = (sp.rss(y, axes=(coil_dim, ),
- keepdims=True) > 0).astype(y.dtype)
- return weights
- class SenseRecon(sp.app.LinearLeastSquares):
- r"""SENSE Reconstruction.
- Considers the problem
- .. math::
- \min_x \frac{1}{2} \| P F S x - y \|_2^2 +
- \frac{\lambda}{2} \| x \|_2^2
- where P is the sampling operator, F is the Fourier transform operator,
- S is the SENSE operator, x is the image, and y is the k-space measurements.
- Args:
- y (array): k-space measurements.
- mps (array): sensitivity maps.
- lamda (float): regularization parameter.
- weights (float or array): weights for data consistency.
- tseg (None or Dictionary): parameters for time-segmented off-resonance
- correction. Parameters are 'b0' (array), 'dt' (float),
- 'lseg' (int), and 'n_bins' (int). Lseg is the number of
- time segments used, and n_bins is the number of histogram bins.
- coord (None or array): coordinates.
- device (Device): device to perform reconstruction.
- coil_batch_size (int): batch size to process coils.
- Only affects memory usage.
- comm (Communicator): communicator for distributed computing.
- **kwargs: Other optional arguments.
- References:
- Pruessmann, K. P., Weiger, M., Scheidegger, M. B., & Boesiger, P.
- (1999).
- SENSE: sensitivity encoding for fast MRI.
- Magnetic resonance in medicine, 42(5), 952-962.
- Pruessmann, K. P., Weiger, M., Bornert, P., & Boesiger, P. (2001).
- Advances in sensitivity encoding with arbitrary k-space trajectories.
- Magnetic resonance in medicine, 46(4), 638-651.
- """
- def __init__(
- self,
- y,
- mps,
- lamda=0,
- weights=None,
- tseg=None,
- coord=None,
- device=sp.cpu_device,
- coil_batch_size=None,
- comm=None,
- show_pbar=True,
- transp_nufft=False,
- **kwargs
- ):
- weights = _estimate_weights(y, weights, coord)
- if weights is not None:
- y = sp.to_device(y * weights**0.5, device=device)
- else:
- y = sp.to_device(y, device=device)
- A = linop.Sense(
- mps,
- coord=coord,
- weights=weights,
- tseg=tseg,
- coil_batch_size=coil_batch_size,
- comm=comm,
- transp_nufft=transp_nufft,
- )
- if comm is not None:
- show_pbar = show_pbar and comm.rank == 0
- super().__init__(A, y, lamda=lamda, show_pbar=show_pbar, **kwargs)
- class L1WaveletRecon(sp.app.LinearLeastSquares):
- r"""L1 Wavelet regularized reconstruction.
- Considers the problem
- .. math::
- \min_x \frac{1}{2} \| P F S x - y \|_2^2 + \lambda \| W x \|_1
- where P is the sampling operator, F is the Fourier transform operator,
- S is the SENSE operator, W is the wavelet operator,
- x is the image, and y is the k-space measurements.
- Args:
- y (array): k-space measurements.
- mps (array): sensitivity maps.
- lamda (float): regularization parameter.
- weights (float or array): weights for data consistency.
- coord (None or array): coordinates.
- wave_name (str): wavelet name.
- device (Device): device to perform reconstruction.
- coil_batch_size (int): batch size to process coils.
- Only affects memory usage.
- comm (Communicator): communicator for distributed computing.
- **kwargs: Other optional arguments.
- References:
- Lustig, M., Donoho, D., & Pauly, J. M. (2007).
- Sparse MRI: The application of compressed sensing for rapid MR imaging.
- Magnetic Resonance in Medicine, 58(6), 1082-1195.
- """
- def __init__(
- self,
- y,
- mps,
- lamda,
- weights=None,
- coord=None,
- wave_name="db4",
- device=sp.cpu_device,
- coil_batch_size=None,
- comm=None,
- show_pbar=True,
- transp_nufft=False,
- **kwargs
- ):
- weights = _estimate_weights(y, weights, coord)
- if weights is not None:
- y = sp.to_device(y * weights**0.5, device=device)
- else:
- y = sp.to_device(y, device=device)
- A = linop.Sense(
- mps,
- coord=coord,
- weights=weights,
- comm=comm,
- coil_batch_size=coil_batch_size,
- transp_nufft=transp_nufft,
- )
- img_shape = mps.shape[1:]
- W = sp.linop.Wavelet(img_shape, wave_name=wave_name)
- proxg = sp.prox.UnitaryTransform(sp.prox.L1Reg(W.oshape, lamda), W)
- def g(input):
- device = sp.get_device(input)
- xp = device.xp
- with device:
- return lamda * xp.sum(xp.abs(W(input))).item()
- if comm is not None:
- show_pbar = show_pbar and comm.rank == 0
- super().__init__(A, y, proxg=proxg, g=g, show_pbar=show_pbar, **kwargs)
- class TotalVariationRecon(sp.app.LinearLeastSquares):
- r"""Total variation regularized reconstruction.
- Considers the problem:
- .. math::
- \min_x \frac{1}{2} \| P F S x - y \|_2^2 + \lambda \| G x \|_1
- where P is the sampling operator, F is the Fourier transform operator,
- S is the SENSE operator, G is the gradient operator,
- x is the image, and y is the k-space measurements.
- Args:
- y (array): k-space measurements.
- mps (array): sensitivity maps.
- lamda (float): regularization parameter.
- weights (float or array): weights for data consistency.
- coord (None or array): coordinates.
- device (Device): device to perform reconstruction.
- coil_batch_size (int): batch size to process coils.
- Only affects memory usage.
- comm (Communicator): communicator for distributed computing.
- **kwargs: Other optional arguments.
- References:
- Block, K. T., Uecker, M., & Frahm, J. (2007).
- Undersampled radial MRI with multiple coils.
- Iterative image reconstruction using a total variation constraint.
- Magnetic Resonance in Medicine, 57(6), 1086-1098.
- """
- def __init__(
- self,
- y,
- mps,
- lamda,
- weights=None,
- coord=None,
- device=sp.cpu_device,
- coil_batch_size=None,
- comm=None,
- show_pbar=True,
- transp_nufft=False,
- **kwargs
- ):
- weights = _estimate_weights(y, weights, coord)
- if weights is not None:
- y = sp.to_device(y * weights**0.5, device=device)
- else:
- y = sp.to_device(y, device=device)
- A = linop.Sense(
- mps,
- coord=coord,
- weights=weights,
- comm=comm,
- coil_batch_size=coil_batch_size,
- transp_nufft=transp_nufft,
- )
- G = sp.linop.FiniteDifference(A.ishape)
- proxg = sp.prox.L1Reg(G.oshape, lamda)
- def g(x):
- device = sp.get_device(x)
- xp = device.xp
- with device:
- return lamda * xp.sum(xp.abs(x)).item()
- if comm is not None:
- show_pbar = show_pbar and comm.rank == 0
- super().__init__(
- A, y, proxg=proxg, g=g, G=G, show_pbar=show_pbar, **kwargs
- )
- class JsenseRecon(sp.app.App):
- r"""JSENSE/NLINV reconstruction.
- Considers the problem
- .. math::
- \min_{l, r} \frac{1}{2} \| l \ast r - y \|_2^2 +
- \frac{\lambda}{2} (\| l \|_2^2 + \| r \|_2^2)
- where :math:`\ast` is the convolution operator.
- This formulation with regularization corresponds to the version
- described in the NLINV paper. Without regularization (which is the
- default) this corresponds to the version from the JSENSE paper but using a
- truncated Fourier representation of the coils (as in NLINV) instead
- of polynomials.
- Args:
- y (array): k-space measurements.
- mps_ker_width (int): sensitivity maps kernel width.
- ksp_calib_width (int): k-space calibration width.
- lamda (float): regularization parameter.
- device (Device): device to perform reconstruction.
- weights (float or array): weights for data consistency.
- coord (None or array): coordinates.
- img_shape (None or list): Image shape.
- grd_shape (None or list): Shape of grid.
- max_iter (int): Maximum number of iterations.
- max_inner_iter (int): Maximum number of inner iterations.
- References:
- Ying, L., & Sheng, J. (2007).
- Joint image reconstruction and sensitivity estimation in SENSE
- (JSENSE).
- Magnetic Resonance in Medicine, 57(6), 1196-1202.
- Uecker, M., Hohage, T., Block, K. T., & Frahm, J. (2008).
- Image reconstruction by regularized nonlinear inversion-
- joint estimation of coil sensitivities and image content.
- Magnetic Resonance in Medicine, 60(#), 674-682.
- """
- def __init__(
- self,
- y,
- mps_ker_width=16,
- ksp_calib_width=24,
- lamda=0,
- device=sp.cpu_device,
- comm=None,
- weights=None,
- coord=None,
- img_shape=None,
- grd_shape=None,
- max_iter=10,
- max_inner_iter=10,
- normalize=True,
- show_pbar=True,
- ):
- self.y = y
- self.mps_ker_width = mps_ker_width
- self.ksp_calib_width = ksp_calib_width
- self.lamda = lamda
- self.weights = weights
- self.coord = coord
- self.img_shape = img_shape
- self.grd_shape = grd_shape
- self.max_iter = max_iter
- self.max_inner_iter = max_inner_iter
- self.normalize = normalize
- self.device = sp.Device(device)
- self.comm = comm
- self.dtype = y.dtype
- self.num_coils = len(y)
- if comm is not None:
- show_pbar = show_pbar and comm.rank == 0
- self._get_data()
- self._get_vars()
- self._get_alg()
- super().__init__(self.alg, show_pbar=show_pbar)
- def _get_data(self):
- if self.coord is None:
- self.img_shape = list(self.y.shape[1:])
- ndim = len(self.img_shape)
- self.y = sp.resize(
- self.y, [self.num_coils] + ndim * [self.ksp_calib_width]
- )
- if self.weights is not None:
- self.weights = sp.resize(
- self.weights, ndim * [self.ksp_calib_width]
- )
- else:
- if self.img_shape is None:
- self.img_shape = sp.estimate_shape(self.coord)
- else:
- self.img_shape = list(self.img_shape)
- calib_idx = (
- np.amax(np.abs(self.coord), axis=-1) < self.ksp_calib_width / 2
- )
- self.coord = self.coord[calib_idx]
- self.y = self.y[:, calib_idx]
- if self.weights is not None:
- self.weights = self.weights[calib_idx]
- if self.weights is None:
- self.y = sp.to_device(self.y, self.device)
- else:
- self.y = sp.to_device(self.weights**0.5 * self.y, self.device)
- if self.coord is not None:
- self.coord = sp.to_device(self.coord, self.device)
- if self.weights is not None:
- self.weights = sp.to_device(self.weights, self.device)
- self.weights = _estimate_weights(self.y, self.weights, self.coord)
- if self.normalize:
- xp = self.device.xp
- with self.device:
- self.y = self.y / xp.linalg.norm(self.y)
- def _get_vars(self):
- ndim = len(self.img_shape)
- mps_ker_shape = [self.num_coils] + [self.mps_ker_width] * ndim
- if self.coord is None:
- img_ker_shape = [
- i + self.mps_ker_width - 1 for i in self.y.shape[1:]
- ]
- else:
- if self.grd_shape is None:
- self.grd_shape = sp.estimate_shape(self.coord)
- img_ker_shape = [
- i + self.mps_ker_width - 1 for i in self.grd_shape
- ]
- self.img_ker = sp.dirac(
- img_ker_shape, dtype=self.dtype, device=self.device
- )
- with self.device:
- self.mps_ker = self.device.xp.zeros(
- mps_ker_shape, dtype=self.dtype
- )
- def _get_alg(self):
- def min_mps_ker():
- self.A_mps_ker = linop.ConvImage(
- self.mps_ker.shape,
- self.img_ker,
- coord=self.coord,
- weights=self.weights,
- )
- sp.app.LinearLeastSquares(
- self.A_mps_ker,
- self.y,
- self.mps_ker,
- lamda=self.lamda,
- max_iter=self.max_inner_iter,
- show_pbar=False,
- ).run()
- def min_img_ker():
- self.A_img_ker = linop.ConvSense(
- self.img_ker.shape,
- self.mps_ker,
- coord=self.coord,
- weights=self.weights,
- comm=self.comm,
- )
- sp.app.LinearLeastSquares(
- self.A_img_ker,
- self.y,
- self.img_ker,
- lamda=self.lamda,
- max_iter=self.max_inner_iter,
- show_pbar=False,
- ).run()
- self.alg = sp.alg.AltMin(
- min_mps_ker, min_img_ker, max_iter=self.max_iter
- )
- def _output(self):
- xp = self.device.xp
- # Normalize by root-sum-of-squares.
- with self.device:
- rss = 0
- mps = np.empty([self.num_coils] + self.img_shape, dtype=self.dtype)
- for c in range(self.num_coils):
- mps_c = sp.ifft(sp.resize(self.mps_ker[c], self.img_shape))
- rss += xp.abs(mps_c) ** 2
- sp.copyto(mps[c], mps_c)
- rss = sp.to_device(rss)
- if self.comm is not None:
- self.comm.allreduce(rss)
- rss = rss**0.5
- mps /= rss
- return mps
- class EspiritCalib(sp.app.App):
- """ESPIRiT calibration.
- Currently only supports outputting one set of maps.
- Args:
- ksp (array): k-space array of shape [num_coils, n_ndim, ..., n_1]
- calib (tuple of ints): length-2 image shape.
- thresh (float): threshold for the calibration matrix.
- kernel_width (int): kernel width for the calibration matrix.
- max_power_iter (int): maximum number of power iterations.
- device (Device): computing device.
- crop (int): cropping threshold.
- Returns:
- array: ESPIRiT maps of the same shape as ksp.
- References:
- Martin Uecker, Peng Lai, Mark J. Murphy, Patrick Virtue, Michael Elad,
- John M. Pauly, Shreyas S. Vasanawala, and Michael Lustig
- ESPIRIT - An Eigenvalue Approach to Autocalibrating Parallel MRI:
- Where SENSE meets GRAPPA.
- Magnetic Resonance in Medicine, 71:990-1001 (2014)
- """
- def __init__(
- self,
- ksp,
- calib_width=24,
- thresh=0.02,
- kernel_width=6,
- crop=0.95,
- max_iter=100,
- sets=1,
- device=sp.cpu_device,
- output_eigenvalue=False,
- show_pbar=True,
- ):
- self.device = sp.Device(device)
- self.output_eigenvalue = output_eigenvalue
- self.crop = crop
- img_ndim = ksp.ndim - 1
- num_coils = len(ksp)
- with sp.get_device(ksp):
- # Get calibration region
- calib_shape = [num_coils] + [calib_width] * img_ndim
- calib = sp.resize(ksp, calib_shape)
- calib = sp.to_device(calib, device)
- xp = self.device.xp
- with self.device:
- # Get calibration matrix.
- # Shape [num_coils] + num_blks + [kernel_width] * img_ndim
- mat = sp.array_to_blocks(
- calib, [kernel_width] * img_ndim, [1] * img_ndim
- )
- mat = mat.reshape([num_coils, -1, kernel_width**img_ndim])
- mat = mat.transpose([1, 0, 2])
- mat = mat.reshape([-1, num_coils * kernel_width**img_ndim])
- # Perform SVD on calibration matrix
- _, S, VH = xp.linalg.svd(mat, full_matrices=False)
- VH = VH[S > thresh * S.max(), :]
- # Get kernels
- num_kernels = len(VH)
- kernels = VH.reshape(
- [num_kernels, num_coils] + [kernel_width] * img_ndim
- )
- img_shape = ksp.shape[1:]
- # Get covariance matrix in image domain
- AHA = xp.zeros(
- img_shape[::-1] + (num_coils, num_coils), dtype=ksp.dtype
- )
- for kernel in kernels:
- img_kernel = sp.ifft(
- sp.resize(kernel, ksp.shape), axes=range(-img_ndim, 0)
- )
- aH = xp.expand_dims(img_kernel.T, axis=-1)
- a = xp.conj(aH.swapaxes(-1, -2))
- AHA += aH @ a
- AHA *= sp.prod(img_shape) / kernel_width**img_ndim
- self.mps = xp.ones(ksp.shape[::-1] + (1,), dtype=ksp.dtype)
- def forward(x):
- with sp.get_device(x):
- return AHA @ x
- def normalize(x):
- with sp.get_device(x):
- return (
- xp.sum(xp.abs(x) ** 2, axis=-2, keepdims=True) ** 0.5
- )
- alg = sp.alg.PowerMethod(
- forward, self.mps, norm_func=normalize, max_iter=max_iter
- )
- super().__init__(alg, show_pbar=show_pbar)
- self.max_eig = np.array([alg.max_eig])
- self.sets = sets
- if self.sets > 1:
- U, S, VH = xp.linalg.svd(AHA, full_matrices=False)
- print(U.shape, S.shape, VH.shape)
- self.mps = U[..., :self.sets]
- self.max_eig = S[..., :self.sets]
- # print('self.mps ', self.mps.shape)
- # print('self.max_eig ', self.max_eig.shape)
- def _output(self):
- xp = self.device.xp
- with self.device:
- mps = []
- max_eig = []
- for s in range(self.mps.shape[-1]):
- # Normalize phase with respect to first channel
- c = self.mps.T[s]
- c *= xp.conj(c[0] / xp.abs(c[0]))
- # Crop maps by thresholding eigenvalue
- me = self.max_eig.T[s]
- c *= me > self.crop
- mps.append( c )
- max_eig.append( me )
- mps = xp.array(mps)
- max_eig = xp.array(max_eig)
- if self.output_eigenvalue:
- return mps, max_eig
- else:
- return mps
- def _get_regularization(ishape, regu='TIK', lamda=0,
- regu_kspace=False,
- regu_axes=[-2, -1],
- blk_shape=(8, 8),
- blk_strides=(8, 8),
- normalization=False,
- thresh='soft',
- deep_model=None,
- ro_extend_fold=1):
- """
- This function constructs regularization terms.
- Author:
- Zhengguo Tan <[email hidden]>
- """
- idx = []
- for n in range(-len(ishape), -1, 1):
- idx.append(slice(0, ishape[n], 1))
- Nx_ext = ishape[-1]
- Nx = Nx_ext // ro_extend_fold
- Slices = []
- for n in range(ro_extend_fold):
- idn = idx.copy()
- idn.append(slice(n * Nx, (n+1) * Nx, 1))
- Slices.append(sp.linop.Slice(ishape, tuple(idn)))
- trafos = sp.linop.Vstack(Slices, axis=0)
- if regu_kspace is True:
- trafos = sp.linop.FFT(trafos.oshape, axes=[-2, -1]) * trafos
- else:
- trafos = sp.linop.Identity(trafos.oshape) * trafos
- if regu == 'TIK':
- proxg = None
- strength = lamda
- elif regu == 'LLR':
- blk_shape = list(blk_shape)
- blk_strides = list(blk_strides)
- for ind in range(len(blk_shape)):
- if ishape[ind-2] <= blk_shape[ind-2]:
- blk_shape[ind-2] = ishape[ind-2] // 3
- blk_strides[ind-2] = ishape[ind-2] // 3
- proxg = sp.prox.LLRL1Reg(trafos.oshape, lamda,
- blk_shape=blk_shape,
- blk_strides=blk_strides,
- normalization=normalization)
- # the lamda is passed into prox,
- # so no need for strength here.
- strength = 0
- elif regu == 'SLR':
- blk_shape = list(blk_shape)
- blk_strides = list(blk_strides)
- proxg = sp.prox.SLRMCReg(trafos.oshape, lamda,
- blk_shape=blk_shape,
- blk_strides=blk_strides,
- thresh=thresh, verbose=True)
- strength = 0
- elif regu == 'TV':
- T = sp.linop.FiniteDifference(trafos.oshape, axes=regu_axes)
- trafos = T * trafos
- proxg = sp.prox.L1Reg(trafos.oshape, lamda)
- strength = 0
- elif (regu == 'DAE') and (deep_model is not None):
- proxg = sp.prox.DAEReg(trafos.oshape, lamda, deep_model)
- strength = 0
- g = None
- # TODO: It is difficult to define g here, because
- # proxg functions like SLR and LLR contain linear transformations
- # inside their prox implementation.
- # if proxg is None:
- # g = None
- # else:
- # def g(input):
- # device = sp.get_device(input)
- # xp = device.xp
- # with device:
- # return lamda * xp.sum(xp.abs(input)).item()
- return trafos, proxg, g, strength
- class HighDimensionalRecon(sp.app.LinearLeastSquares):
- r"""High-Dimensional MRI Reconstruction.
- Considers the problem
- .. math::
- \min_x \frac{1}{2} \| P F S M x - y \|_2^2 +
- \frac{\lambda}{2} R(x)
- where P is the sampling operator,
- F is the Fourier transform operator,
- S is the SENSE operator (multiplication with coil sensitivity maps),
- M is the modeling operator, which can be
- - subspace matrix,
- - phase matrix,
- - etc.
- x is the subspace coefficient maps, and
- y is the k-space measurements.
- R(x) is the regularization term.
- (1) TIK: Tikhonov L2.
- R(x) = \| x \|_2^2
- (2) LLR: Locally Low Rank.
- R(x) = \| G(x) \|_1,
- where G is the transformation function.
- (3) SLR: Structured Low Rank.
- (4) TV: total variation.
- Args:
- y (array): measured k-space data.
- mps (array): coil sensitivity maps.
- weights (array): forward model weighting.
- coord (array): k-space non-Cartesian trajectory.
- model_water_fat (bool): model water/fat 2-compartment signal. [default: False]
- B0 (float): B0 field strength (T). [default: 3.0]
- TE (1D array): echo time array (ms).
- basis (2D array): temporal subspace matrix.
- phase_shot (array): shot-to-shot phase maps.
- combine_shot (bool): reconstructed images (x) are shot-combined,
- i.e., x.shape[DIM_SEG] = 1. [default: False]
- phase_echo (array): echo-to-echo phase maps.
- combine_echo (bool): reconstructed images (x) are echo-combined,
- i.e., x.shape[DIM_ECHO] = 1. [default: False]
- phase_sms (array): multi-band phase maps.
- scale (float): scaling factor of y. [default: 0 - no scaling]
- regu (str): regularization type.
- [choices: 'TIK' (default), 'LLR', 'TV']
- regu_kspace (bool): regularize the FFT of x. [default: False]
- This is meant for structural low-rank regularization
- regu_axes (list of int): on which axes the regularization is applied.
- [default: [-2, -1]]
- x (array): initialization.
- blk_shape (list of int): block shape in LLR. [default: [8, 8]]
- blk_strides (list of int): block strides in LLR. [default: [8, 8]]
- normalization (bool): apply normalization in LLR. [default: False]
- thresh (str): threshold type. [choices: 'soft' (default), 'hard']
- max_iter (int): maximal number of iterations (ADMM outer iterations). [default: 50]
- max_cg_iter (int): maximal number of CG iterations. [default: 30]
- solver (str): solver. [choices: 'ADMM', None]
- ro_extend_fold (int): reconstruct SMS data using the readout-extended-FOV concept.
- Please refer to https://doi.org/10.1002/mrm.25897 and sigpy.mri.muse.
- device (Device): [choices: sp.Device(0) - GPU, sp.Device(-1) - CPU]
- **kwargs: ...
- Author:
- Zhengguo Tan <[email hidden]>
- References:
- * Lustig M, Donoho DL, Pauly JM.
- Sparse MRI: The application of compressed sensing for rapid MRI.
- Magn Reson Med 2007;58:1182-1195.
- * Block KT, Uecker M, Frahm J.
- Undersampled radial MRI with multiple coils.
- Iterative image reconstruction using a total variation constraint.
- Magn Reson Med 2007;57:1086-1098.
- * Lustig M, Donoho DL, Santos JM, Pauly JM.
- Compressed sensing MRI.
- IEEE Signal Process Mag 2008;25:72-82.
- """
- def __init__(self, y, mps, lamda=0,
- weights=None, coord=None,
- model_water_fat: bool = False,
- B0: float = 3.0, TE=None,
- basis=None,
- phase_shot=None, combine_shot=False,
- phase_echo=None, combine_echo=False,
- phase_sms=None,
- scale=0, regu='TIK', regu_kspace=False,
- regu_axes=[-2, -1], x=None,
- blk_shape=(8, 8), blk_strides=(8, 8),
- normalization=False,
- deep_model=None,
- thresh='soft', max_iter=50,
- ro_extend_fold=1, solver=None,
- device=sp.cpu_device, show_pbar=True,
- **kwargs):
- if y.ndim == 6:
- y = y[:, :, None, :, :, :, :]
- print('> reshape y to 7 dimensions: ', y.shape)
- if weights is not None and weights.ndim == 6:
- weights = weights[:, :, None, :, :, :, :]
- print('> reshape weights to 7 dimensions: ', weights.shape)
- # k-space data in accordance with sigpy/mri/dims.py
- Ntime, Nseg, Necho, Ncoil, Nz = y.shape[:-2]
- Ny, Nx = mps.shape[-2:]
- assert(1 == Nz) # deal with collapsed y even for SMS
- if phase_sms is not None:
- MB = phase_sms.shape[DIM_Z]
- else:
- MB = 1
- # start to construct image shape
- img_shape = [1] + [MB] + [Ny] + [Nx]
- if model_water_fat is True:
- zm = nlop.calc_fat_modu(np.array(TE) * 1e-3, B0=B0)
- zm = sp.to_device(zm, device)
- N_zm_echo, N_zm_parm = zm.shape
- ishape = [N_zm_parm] + img_shape
- else:
- if combine_echo is True:
- ishape = [1] + img_shape
- else:
- ishape = [Necho] + img_shape
- if combine_shot is True:
- ishape = [1] + ishape
- else:
- ishape = [Nseg] + ishape
- if basis is not None:
- N_basis_contrast, N_basis_coef = basis.shape
- assert(N_basis_contrast == Ntime)
- ishape = [N_basis_coef] + ishape
- else:
- ishape = [Ntime] + ishape
- # %% construct MRI forward model
- #### case 0. water/fat modeling
- if model_water_fat is True:
- T1 = sp.linop.Transpose(ishape, [-5, -6, -7, -4, -3, -2, -1])
- R1 = sp.linop.Reshape([T1.oshape[0], np.prod(T1.oshape[1:])], T1.oshape)
- MM = sp.linop.MatMul(R1.oshape, zm)
- R2 = sp.linop.Reshape([N_zm_echo] + list(T1.oshape[1:]), MM.oshape)
- T2 = sp.linop.Transpose(R2.oshape, [-5, -6, -7, -4, -3, -2, -1])
- WF = T2 * R2 * MM * R1 * T1
- else:
- WF = sp.linop.Identity(ishape)
- #### case 1. subspace modeling
- if basis is not None:
- sub_ishape = [N_basis_coef] + [np.prod(WF.oshape[1:])]
- # TODO: may require more Reshape linops to split between time and echo
- sub_oshape = [Ntime] + list(WF.oshape[1:])
- B1 = sp.linop.Reshape(sub_ishape, WF.oshape)
- B2 = sp.linop.MatMul(B1.oshape, basis)
- B3 = sp.linop.Reshape(sub_oshape, B2.oshape)
- B = B3 * B2 * B1
- else:
- B = sp.linop.Identity(WF.oshape)
- #### case 2. echo phase modeling
- if phase_echo is not None:
- assert Necho == phase_echo.shape[DIM_ECHO]
- ECO = sp.linop.Multiply(B.oshape, phase_echo)
- else:
- # assert Necho == 1
- ECO = sp.linop.Identity(B.oshape)
- #### case 3. shot/segment phase modeling
- if phase_shot is not None:
- assert Nseg == phase_shot.shape[DIM_SEG]
- SEG = sp.linop.Multiply(ECO.oshape, phase_shot)
- else:
- SEG = sp.linop.Identity(ECO.oshape)
- #### parallel imaging modeling
- # only one set of coil sensitivity maps for all images
- assert(y.shape[DIM_COIL] == mps.shape[DIM_COIL])
- S = sp.linop.Multiply(SEG.oshape, mps)
- # FFT
- if coord is None:
- self._check_two_shape(list(y.shape[DIM_Y:]), mps.shape[DIM_Y:])
- F = sp.linop.FFT(S.oshape, axes=range(-2, 0))
- else:
- F = sp.linop.HDNUFFT(S.oshape, coord)
- # SMS
- if phase_sms is not None:
- self._check_two_shape(list(phase_sms.shape), mps.shape[DIM_Z:])
- PHI = sp.linop.Multiply(F.oshape, phase_sms)
- SUM = sp.linop.Sum(PHI.oshape, axes=(DIM_Z, ), keepdims=True)
- M = SUM * PHI
- else:
- M = sp.linop.Identity(F.oshape)
- # compute k-space sampling mask
- if weights is None:
- weights = _estimate_weights(y, weights, coord, coil_dim=DIM_COIL)
- y = sp.to_device(y * weights, device=device)
- W = sp.linop.Multiply(M.oshape, weights)
- # sum along the echo dimension
- if Necho == 1 and phase_echo is not None:
- SUM = sp.linop.Sum(W.oshape, axes=(DIM_ECHO, ),
- keeydims=True)
- else:
- SUM = sp.linop.Identity(W.oshape)
- #### chain models
- A = SUM * W * M * F * S * SEG * ECO * B * WF
- print('>>> A oshape: ', A.oshape, ' ishape: ', A.ishape)
- # %% scale y
- if scale == 0:
- device = sp.get_device(y)
- xp = device.xp
- with device:
- x0 = A.H(y)
- x1 = np.linalg.norm(x0, axis=0)
- x2 = np.linalg.norm(x1, axis=0)
- x1 = xp.sort(x2.flatten())
- x_med = abs(x1[len(x1)//2])
- x_p90 = abs(x1[int(len(x1) * 0.9)])
- x_max = abs(x1[-1])
- if (x_max - x_p90) < 2 * (x_p90 - x_med):
- scale = x_p90
- else:
- scale = x_max
- # print('scale: ' + str(scale))
- y /= scale
- # %% initialization
- if x is not None:
- x = sp.to_device(x, device=device)
- # %% regularization
- trafos, proxg, g, strength = _get_regularization(
- A.ishape, regu=regu, lamda=lamda,
- regu_kspace=regu_kspace, regu_axes=regu_axes,
- blk_shape=blk_shape, blk_strides=blk_strides,
- normalization=normalization,
- thresh=thresh, deep_model=deep_model,
- ro_extend_fold=ro_extend_fold)
- if solver == 'ADMM':
- show_pbar = False
- super().__init__(A, y, x=x, lamda=strength,
- G=trafos, proxg=proxg, g=g, scale=scale,
- max_iter=max_iter, solver=solver,
- show_pbar=show_pbar, **kwargs)
- def _check_two_shape(self, ref_shape, dst_shape):
- for i1, i2 in zip(ref_shape, dst_shape):
- if (i1 != i2):
- raise ValueError('shape mismatch for ref {ref}, got {dst}'.format(
- ref=ref_shape, dst=dst_shape))
- class ModelDiffRecon(sp.app.NonLinearLeastSquares):
- r"""Model-based Diffusion Reconstruction.
- Consider the problem
- .. math::
- \min_x \frac{1}{2} \| P F S B x - y \|_2^2 +
- \frac{\lambda}{2} R(x)
- where P is the sampling operator,
- F is the Fourier transform operator,
- S is the SENSE operator (multiplication with coil sensitivity maps),
- B is the non-linear diffusion model,
- x is the diffusion model parameters, [b0, DTI or DKI], and
- y is the k-space measurements.
- Therefore, The operation B x outputs
- non-diffusion-weighted image (b0) and
- diffusion-weighted images (DWI).
- Args:
- y (array): Observation.
- image_shape (list): Shape of Solution.
- coil (array): Coil sensitivity maps.
- coil_dim (int): Dimension of coil sensitivity in y.
- coord (array): Sampling trajectory.
- x (array): Solution.
- x0 (array): Bias for regularization.
- dwi_phase (array): Phase of diffusion weighted images.
- weights (array): Sampling pattern.
- encode_matrix (array): Diffusion encoding matrix (B).
- max_iter (int): Maximum number of iterations.
- lamda (float): Regularization strength (\rho in ADMM).
- redu (float): Reduction factor along iterations.
- gn_iter (int): Gauss-Newton iterations.
- inner_iter (int): Inner iterations.
- inner_tol (float): Inner tolerance.
- G (None or Linop): Regularization linear operator.
- proxg (None or Prox): Proximal operator for regularization.
- device (Device): Use CPU or GPU.
- Author:
- Zhengguo Tan <[email hidden]>
- References:
- Welsh C. L., DiBella E. V. R., Adluru G., Hsu E. W. (2013).
- Model-based reconstruction of undersampled diffusion tensor k-space data.
- Magn. Reson. Med., 70, 429-440.
- Knoll F., Raya J. G., Halloran R. O., Baete S., Sigmund E., Bammer R., Block K. T., Otazo R., Sodickson D. K. (2015).
- A model-based reconstruction for undersampled radial spin-echo DTI with variational penalties on the diffusion tensor.
- Magn. Reson. Med., 28, 353-366.
- Dong Z., Dai E., Wang F., Zhang Z., Ma X., Yuan C., Guo H. (2018).
- Model-based reconstruction for simultaneous multislice and parallel imaging accelerated multishot DTI.
- Med. Phys., 45, 3196-3204.
- """
- def __init__(self, y, image_shape,
- coil=None, coil_dim=-3, coord=None,
- x=None, x0=None,
- dwi_phase=None, weights=None,
- encode_matrix=None,
- max_iter=6, lamda=1, redu=2,
- gn_iter=6, inner_iter=100, inner_tol=0.01,
- G=None, proxg=None,
- device=sp.cpu_device,
- **kwargs):
- y = sp.to_device(y, device=device)
- xp = device.xp
- with device:
- weights = _estimate_weights(y, weights, coord, coil_dim=coil_dim)
- A = nlop.Diffusion(image_shape, encode_matrix, coil,
- dwi_phase=dwi_phase, weights=weights)
- if x is None:
- with device:
- x_b0 = xp.ones([1] + list(image_shape[1:]), dtype=y.dtype) * 1E-5
- x_D = xp.zeros([image_shape[0]-1] + list(image_shape[1:]), dtype=y.dtype)
- x = xp.concatenate((x_b0, x_D))
- else:
- with device:
- x = sp.to_device(x, device=device)
- if x0 is None:
- with device:
- x0 = 0.9 * x
- else:
- with device:
- x0 = sp.to_device(x0, device=device)
- super().__init__(A, y, x=x, x0=x0,
- max_iter=max_iter,
- lamda=lamda, redu=redu,
- gn_iter=gn_iter,
- inner_iter=inner_iter,
- inner_tol=inner_tol,
- G=G, proxg=proxg,
- **kwargs)
app.py at commit b757125, under BSD-3-Clause · at the source
Overview
- Institute of Radiology, University Hospital Erlangen, Friedrich‐Alexander‐Universität Erlangen‐Nürnberg, Erlangen, Germany
- Michigan Institute for Imaging Technology and Translation (MIITT), Radiology, University of Michigan, Ann Arbor, Michigan, USA
- Department Artificial Intelligence in Biomedical Engineering (AIBE), Friedrich‐Alexander‐Universität Erlangen‐Nürnberg, Erlangen, Germany
Abstract
Purpose: To present a novel, nonlinear subspace modeling and joint k–q‐space reconstruction technique for high‐resolution, multi‐band, multi‐shell diffusion‐weighted imaging (DWI).
Methods: High b‐value (> 1000 s/
Results: Comparing the reconstructed data using the proposed method shows improved noise suppression compared to MUSE and more details than LLR‐reconstructed images. Specifically, in the higher b‐value domain, the reconstruction results show improved detectability of small structures. This bias and precision analysis showed minimal bias introduction, but higher precision with the proposed method.
Conclusion: Our method combines deep learning, latent signal modeling, joint k–q‐space reconstruction, and biophysical simulation for diffusion data and shows strong noise suppression and a high degree of detail in reconstructed diffusion‐weighted images.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 5 matches between paragraphs and lines of code.
ZhengguoTan/sigpy
b7571257012512926172df00e66537fc618d90b7, 22 September 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
105 files
- conda.recipe/
build.sh , Shell, 1 line - conda.recipe/
install.sh , Shell, 27 lines - docs/
conf.py , Python, 167 lines - run_tests.sh, Shell, 7 lines
- setup.py, Python, 3 lines
- sigpy/
__init__.py , Python, 39 lines - sigpy/
alg.py , Python, 999 lines - sigpy/
app.py , Python, 778 lines - sigpy/
backend.py , Python, 366 lines - sigpy/
block.py , Python, 533 lines - sigpy/
config.py , Python, 62 lines - sigpy/
conv.py , Python, 550 lines - sigpy/
coord.py , Python, 28 lines - sigpy/
extract_dicom.py , Python, 132 lines - sigpy/
fourier.py , Python, 327 lines - sigpy/
interp.py , Python, 894 lines - sigpy/
linop.py , Python, 2,307 lines - sigpy/
mri/ , Python, 21 lines__init__.py - sigpy/
mri/ , Python, 1,151 lines, 2 matchesapp.py - sigpy/
mri/ , Python, 173 linescc.py - sigpy/
mri/ , Python, 263 linesdce.py - sigpy/
mri/ , Python, 74 linesdcf.py - sigpy/
mri/ , Python, 33 linesdiff.py - sigpy/
mri/ , Python, 10 linesdims.py - sigpy/
mri/ , Python, 131 linesdvs.py - sigpy/
mri/ , Python, 348 linesepi.py - sigpy/
mri/ , Python, 70 linesgmap.py - sigpy/
mri/ , Python, 149 linesgrappa.py - sigpy/
mri/ , Python, 229 lineslinop.py - sigpy/
mri/ , Python, 476 lines, 1 matchmuse.py - sigpy/
mri/ , Python, 139 linesmussels.py - sigpy/
mri/ , Python, 467 linesnlop.py - sigpy/
mri/ , Python, 87 linesnlrecon.py - sigpy/
mri/ , Python, 169 linesprecond.py - sigpy/
mri/ , Python, 159 linesretro.py - sigpy/
mri/ , Python, 56 linesrf/ __init__.py - sigpy/
mri/ , Python, 196 linesrf/ adiabatic.py - sigpy/
mri/ , Python, 303 linesrf/ b1sel.py - sigpy/
mri/ , Python, 211 linesrf/ io.py - sigpy/
mri/ , Python, 134 linesrf/ linop.py - sigpy/
mri/ , Python, 330 linesrf/ multiband.py - sigpy/
mri/ , Python, 120 linesrf/ optcont.py - sigpy/
mri/ , Python, 302 linesrf/ ptx.py - sigpy/
mri/ , Python, 122 linesrf/ shim.py - sigpy/
mri/ , Python, 269 linesrf/ sim.py - sigpy/
mri/ , Python, 888 linesrf/ slr.py - sigpy/
mri/ , Python, 999 linesrf/ trajgrad.py - sigpy/
mri/ , Python, 41 linesrf/ util.py - sigpy/
mri/ , Python, 322 linessamp.py - sigpy/
mri/ , Python, 207 linessim.py - sigpy/
mri/ , Python, 331 linessms.py - sigpy/
mri/ , Python, 125 linesutil.py - sigpy/
nlls.py , Python, 192 lines - sigpy/
nlop.py , Python, 369 lines - sigpy/
nn/ , Python, 92 linestorch_dce.py - sigpy/
plot.py , Python, 1,333 lines - sigpy/
prox.py , Python, 614 lines - sigpy/
pytorch.py , Python, 135 lines - sigpy/
sim.py , Python, 177 lines - sigpy/
thresh.py , Python, 211 lines - sigpy/
util.py , Python, 476 lines - sigpy/
version.py , Python, 1 line - sigpy/
wavelet.py , Python, 67 lines - tests/
__init__.py , Python, 1 line - tests/
learn/ , Python, 69 linestest_app.py - tests/
learn/ , Python, 25 linestest_util.py - tests/
mri/ , Python, 1 line__init__.py - tests/
mri/ , Python, 1 linerf/ __init__.py - tests/
mri/ , Python, 79 linesrf/ test_adiabatic.py - tests/
mri/ , Python, 67 linesrf/ test_b1sel.py - tests/
mri/ , Python, 50 linesrf/ test_linop.py - tests/
mri/ , Python, 76 linesrf/ test_multiband.py - tests/
mri/ , Python, 233 linesrf/ test_ptx.py - tests/
mri/ , Python, 34 linesrf/ test_sim.py - tests/
mri/ , Python, 135 linesrf/ test_slr.py - tests/
mri/ , Python, 47 linesrf/ test_trajgrad.py - tests/
mri/ , Python, 199 linestest_app.py - tests/
mri/ , Python, 52 linestest_dce.py - tests/
mri/ , Python, 28 linestest_dcf.py - tests/
mri/ , Python, 134 linestest_epi.py - tests/
mri/ , Python, 193 linestest_linop.py - tests/
mri/ , Python, 189 linestest_nlop.py - tests/
mri/ , Python, 174 linestest_precond.py - tests/
mri/ , Python, 38 linestest_retro.py - tests/
mri/ , Python, 45 linestest_samp.py - tests/
mri/ , Python, 37 linestest_sim.py - tests/
mri/ , Python, 59 linestest_sms.py - tests/
test_alg.py , Python, 253 lines - tests/
test_app.py , Python, 159 lines - tests/
test_block.py , Python, 163 lines - tests/
test_conv.py , Python, 444 lines - tests/
test_coord.py , Python, 22 lines - tests/
test_fourier.py , Python, 284 lines - tests/
test_interp.py , Python, 113 lines - tests/
test_linop.py , Python, 643 lines - tests/
test_nlls.py , Python, 228 lines - tests/
test_nlop.py , Python, 155 lines - tests/
test_prox.py , Python, 122 lines - tests/
test_pytorch.py , Python, 138 lines - tests/
test_thresh.py , Python, 73 lines - tests/
test_util.py , Python, 131 lines - tests/
test_version.py , Python, 11 lines - tests/
test_wavelet.py , Python, 29 lines - LICENSE, License, 13 lines
- README.rst, Text, 84 lines
JuliusGlaser/LASER
dbc143a981c36efb75f4215ae1e62e9e7fe5fc10, 4 February 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
39 files
- code/
laser/ , Python, 505 linesbootstrapping/ bootstrapping_analysis_m rtrix.py - code/
laser/ , Python, 29 linesbootstrapping/ create_bootstrapping_lis t.py - code/
laser/ , Python, 252 linesdenoising/ denoising_comp.py - code/
laser/ , Python, 1,202 lines, 1 matchreconstruction/ reconstruction.py - code/
laser/ , Python, 1 linetraining/ __init__.py - code/
laser/ , Python, 87 linestraining/ linsub.py - code/
laser/ , Python, 1 linetraining/ models/ __init__.py - code/
laser/ , Python, 9 linestraining/ models/ dims.py - code/
laser/ , Python, 153 linestraining/ models/ mri.py - code/
laser/ , Python, 1 linetraining/ models/ nn/ __init__.py - code/
laser/ , Python, 307 linestraining/ models/ nn/ autoencoder.py - code/
laser/ , Python, 197 linestraining/ models/ nn/ net.py - code/
laser/ , Python, 1 linetraining/ sim/ __init__.py - code/
laser/ , Python, 348 linestraining/ sim/ backend.py - code/
laser/ , Python, 60 linestraining/ sim/ config.py - code/
laser/ , Python, 108 linestraining/ sim/ dataset.py - code/
laser/ , Python, 346 linestraining/ sim/ dwi.py - code/
laser/ , Python, 332 linestraining/ sim/ epi.py - code/
laser/ , Python, 313 linestraining/ sim/ fourier.py - code/
laser/ , Python, 845 linestraining/ sim/ interp.py - code/
laser/ , Python, 36 linestraining/ sim/ t2.py - code/
laser/ , Python, 454 linestraining/ sim/ util.py - code/
laser/ , Python, 422 linestraining/ train.py - code/
laser/ , Python, 212 linestraining/ util_classes.py - code/
laser/ , Python, 173 linesutility/ combine_and_fit.py - code/
laser/ , Python, 42 linesutility/ convert_to_nifti.py - code/
laser/ , Python, 37 linesutility/ fix_PF.py - code/
laser/ , Python, 63 linesutility/ h5_to_nii.py - code/
laser/ , Python, 28 linesutility/ nii_to_h5.py - code/
laser/ , Python, 108 linesutility/ retro_us.py - code/
laser/ , Python, 262 linesutility/ util.py - data/
raw/ , Python, 94 linesload.py - figures/
fig1/ , Jupyter, 204 linesBAS_simulation.ipynb - figures/
fig1/ , Jupyter, 208 linesDT_simulation.ipynb - figures/
fig2/ , Python, 214 linesnew_images/ get_images.py - figures/
fig3/ , Python, 119 linesplot.py - figures/
fig4/ , Python, 134 linesget_images.py - figures/
fig5/ , Python, 440 lines, 1 matchcreate_violin_plots.py - README.md, Text, 263 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 141 scripts, each with its path and the digest of its content;
- 5 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data Availability Statement
The data that support the findings of this study are available on request from the corresponding author. The data are not publicly available due to privacy or ethical restrictions. Our source code is a publicly available under https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 3, 28 September 2026
- Publisher: n/a → Wiley
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 5 keywords, 8 MeSH terms, 2 funders, 38 references.
Cite
This paper
Glaser, J., Tan, Z., Hofmann, A., Laun, F. B., & Knoll, F. (2026). A Deep Nonlinear Subspace Modeling and Reconstruction for Diffusion-Weighted Imaging Using Denoising Auto-Encoder. Magnetic resonance in medicine, 96(5), 2359-2369. https://
BibTeX
@article{glaser2026deep,
author = {Glaser, Julius and Tan, Zhengguo and Hofmann, Annika and Laun, Frederik Bernd and Knoll, Florian},
title = {{A Deep Nonlinear Subspace Modeling and Reconstruction for Diffusion-Weighted Imaging Using Denoising Auto-Encoder}},
journal = {Magnetic resonance in medicine},
year = {2026},
month = aug,
volume = {96},
number = {5},
pages = {2359--2369},
publisher = {Wiley},
issn = {0740-3194},
doi = {10.1002/
url = {https://
pmid = {42548198},
pmcid = {PMC13527252}
}
RIS
TY - JOUR
AU - Glaser, Julius
AU - Tan, Zhengguo
AU - Hofmann, Annika
AU - Laun, Frederik Bernd
AU - Knoll, Florian
TI - A Deep Nonlinear Subspace Modeling and Reconstruction for Diffusion-Weighted Imaging Using Denoising Auto-Encoder
T2 - Magnetic resonance in medicine
J2 - Magn Reson Med
PY - 2026
DA - 2026/
VL - 96
IS - 5
SP - 2359
EP - 2369
SN - 0740-3194
PB - Wiley
DO - 10.1002/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1002/
"type": "article-journal",
"title": "A Deep Nonlinear Subspace Modeling and Reconstruction for Diffusion-Weighted Imaging Using Denoising Auto-Encoder",
"container-title": "Magnetic resonance in medicine",
"author": [
{
"family": "Glaser",
"given": "Julius"
},
{
"family": "Tan",
"given": "Zhengguo"
},
{
"family": "Hofmann",
"given": "Annika"
},
{
"family": "Laun",
"given": "Frederik Bernd"
},
{
"family": "Knoll",
"given": "Florian"
}
],
"container-title-short":
"volume": "96",
"issue": "5",
"page": "2359-2369",
"DOI": "10.1002/
"PMID": "42548198",
"PMCID": "PMC13527252",
"ISSN": "0740-3194",
"publisher": "Wiley",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
4
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1002/mrm.70336 [code]
- Offline Reconstruction of Diffusion MRI Acquisitions for Comparison Between Complex PCA-Based and AI-Based Denoising.Journal: Magnetic resonance in medicineIn common: DIPY, MRtrix3, NiBabel, 3 other tools, methods / tools, structural MRI / diffusion, 6 references
- [2] doi:10.1038/s41592-026-03057-2 [code]
- CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species.Journal: Nature methodsIn common: PyWavelets, CuPy, Numba, 6 other tools, methods / tools
- [3] doi:10.1038/s41598-026-39162-7 [code]
- White matter microstructure differences in obstructive sleep apnea severity groups assessed by diffusion tensor metrics and biophysical modeling.Journal: Scientific reportsIn common: DIPY, MRtrix3, Numba, 3 other tools, structural MRI / diffusion, 2 references
- [4] doi:10.1162/imag.a.1341 [code]
- Massively parallelized brain tractography using compute clusters, supercomputers, and graphics processing units.Journal: Imaging neuroscience (Cambridge, Mass.)In common: DIPY, Numba, NiBabel, 3 other tools, methods / tools, structural MRI / diffusion, 3 references
- [5] doi:10.1016/j.isci.2026.116671 [code]
- A high-resolution functional network-organized atlas of human superficial white matter from ultra-high-field diffusion MRI.Journal: iScienceIn common: DIPY, MRtrix3, h5py, 5 other tools, methods / tools, structural MRI / diffusion
- [6] doi:10.1162/imag.a.1276 [code]
- High-resolution whole-brain magnetic resonance spectroscopic imaging in youth at risk for psychosis.Journal: Imaging neuroscience (Cambridge, Mass.)In common: CuPy, DIPY, Numba, 5 other tools
- [7] doi:10.1371/journal.pone.0349951
- Disentangling crossing fibers with advanced dMRI methods reveals bundle-specific degeneration across the visual system in asymmetric glaucoma.Journal: PloS oneIn common: structural MRI / diffusion, 8 references
- [8] doi:10.1371/journal.pcbi.1014555 [code]
- Body surface potential driven personalisation of electrophysiological digital twins in hypertrophic cardiomyopathy.Journal: PLoS computational biologyIn common: CuPy, pydicom, Pillow, 5 other tools, structural MRI / diffusion
- [9] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: CuPy, pydicom, Numba, 5 other tools
- [10] doi:10.64898/2026.03.04.709502 [code]
- Distributed hierarchical filters partition the Danionella vocal repertoireJournal: bioRxiv (preprint)In common: CuPy, pydicom, Numba, 5 other tools
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 141 scripts, and 5 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:f69bd00f7fba497d…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
