From eabef6f677542f997936440cbad457085e7b5321 Mon Sep 17 00:00:00 2001 From: Nicholas McKibben Date: Sun, 22 Sep 2024 15:29:25 -0700 Subject: [PATCH] MAINT: trigger building of updated binaries; deprecate python 3.8 (#105) * MAINT: trigger building of updated binaries; deprecate python 3.8 * MAINT: update github action versions * MAINT: incremental removal of cgrappa; small formatting/type hinting updates * STY: flake8 adjustments * BLD: make build release by default --- .github/workflows/build-python-package.yml | 6 +-- .github/workflows/github-deploy.yml | 20 ++++---- INSTALLATION.rst | 2 + meson.build | 50 +++++++++++++------- pygrappa/__init__.py | 2 +- pygrappa/cgsense.py | 24 ++++------ pygrappa/coils.py | 8 ++-- pygrappa/examples/basic_cgrappa.py | 3 +- pygrappa/examples/basic_cgsense.py | 3 +- pygrappa/examples/basic_gfactor.py | 3 +- pygrappa/examples/basic_grappa.py | 3 +- pygrappa/examples/basic_grappaop.py | 8 ++-- pygrappa/examples/basic_gridding.py | 4 +- pygrappa/examples/basic_hpgrappa.py | 3 +- pygrappa/examples/basic_igrappa.py | 7 +-- pygrappa/examples/basic_mdgrappa.py | 3 +- pygrappa/examples/basic_nlgrappa.py | 8 ++-- pygrappa/examples/basic_nlgrappa_matlab.py | 3 +- pygrappa/examples/basic_pars.py | 2 +- pygrappa/examples/basic_radialgrappaop.py | 4 +- pygrappa/examples/basic_seggrappa.py | 4 +- pygrappa/examples/basic_sense1d.py | 3 +- pygrappa/examples/basic_slicegrappa.py | 8 ++-- pygrappa/examples/basic_splitslicegrappa.py | 8 ++-- pygrappa/examples/basic_tgrappa.py | 8 ++-- pygrappa/examples/basic_ttgrappa.py | 2 +- pygrappa/examples/basic_vcgrappa.py | 3 +- pygrappa/examples/inverse_grog.py | 2 +- pygrappa/examples/md_cgsense.py | 3 +- pygrappa/examples/primefac_grog.py | 3 +- pygrappa/examples/tikhonov_regularization.py | 5 +- pygrappa/examples/use_memmap.py | 5 +- pygrappa/find_acs.py | 8 ++-- pygrappa/gfactor.py | 20 ++++---- pygrappa/grappa.py | 18 +++---- pygrappa/grappaop.py | 14 ++---- pygrappa/grog.py | 16 +++---- pygrappa/hpgrappa.py | 18 +++---- pygrappa/igrappa.py | 12 ++--- pygrappa/kernels.py | 8 ++-- pygrappa/kspa.py | 8 ++-- pygrappa/lustig_grappa.py | 30 +++++------- pygrappa/mdgrappa.py | 20 ++++---- pygrappa/ncgrappa.py | 8 ++-- pygrappa/nlgrappa.py | 18 +++---- pygrappa/nlgrappa_matlab.py | 12 ++--- pygrappa/pars.py | 14 ++---- pygrappa/pruno.py | 8 ++-- pygrappa/radialgrappaop.py | 18 +++---- pygrappa/seggrappa.py | 14 ++---- pygrappa/sense1d.py | 12 ++--- pygrappa/simple_pruno.py | 6 +-- pygrappa/slicegrappa.py | 14 ++---- pygrappa/splitslicegrappa.py | 10 ++-- pygrappa/tests/test_vcgrappa.py | 6 +-- pygrappa/tgrappa.py | 17 +++---- pygrappa/ttgrappa.py | 16 +++---- pygrappa/utils/cythoner.py | 28 ----------- pygrappa/utils/disjoint_csm.py | 12 ++--- pygrappa/utils/gaussian_csm.py | 14 +++--- pygrappa/utils/gridder.py | 16 +++---- pygrappa/utils/meson.build | 1 - pygrappa/vcgrappa.py | 19 +++----- pyproject.toml | 12 ++--- 64 files changed, 290 insertions(+), 377 deletions(-) delete mode 100644 pygrappa/utils/cythoner.py diff --git a/.github/workflows/build-python-package.yml b/.github/workflows/build-python-package.yml index 04d1975..37bcc4f 100644 --- a/.github/workflows/build-python-package.yml +++ b/.github/workflows/build-python-package.yml @@ -16,12 +16,12 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [3.8, 3.9, '3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Lint with flake8 diff --git a/.github/workflows/github-deploy.yml b/.github/workflows/github-deploy.yml index 5d9dc15..a20ccf0 100644 --- a/.github/workflows/github-deploy.yml +++ b/.github/workflows/github-deploy.yml @@ -12,7 +12,7 @@ on: workflow_dispatch # - published env: - CIBW_SKIP: cp27-* pp27-* pp37-* pp38-* pp39-* pp310-* cp35-* cp36-* cp37-* cp*-win32 cp*-manylinux_i686 cp*-musl* cp312-* # skip 3.12 for now until numpy releases + CIBW_SKIP: cp27-* pp27-* pp37-* pp38-* pp39-* pp310-* cp35-* cp36-* cp37-* cp38-* cp*-win32 cp*-manylinux_i686 cp*-musl* jobs: build_wheels: @@ -20,15 +20,15 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 name: Install Python with: - python-version: '3.8' + python-version: '3.12' - name: Install cibuildwheel run: | @@ -37,7 +37,7 @@ jobs: - name: Enable Developer Command Prompt if: runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1.12.0 + uses: ilammy/msvc-dev-cmd@v1 - name: Build wheels run: | @@ -51,12 +51,12 @@ jobs: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 name: Install Python with: - python-version: '3.8' + python-version: '3.12' - name: Install dependencies run: | @@ -79,7 +79,7 @@ jobs: id-token: write if: github.event_name == 'workflow_dispatch' steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4.1.7 with: name: artifact path: dist diff --git a/INSTALLATION.rst b/INSTALLATION.rst index 481cf2b..8712eab 100644 --- a/INSTALLATION.rst +++ b/INSTALLATION.rst @@ -1,3 +1,5 @@ +WARNING: this may contain outdated information. + Windows 10 Installation ======================= diff --git a/meson.build b/meson.build index a756553..e9192bb 100644 --- a/meson.build +++ b/meson.build @@ -1,11 +1,12 @@ project( 'pygrappa', - 'c', 'cpp', - version: '0.26.2', - license: '', - meson_version: '>= 0.64.0', + 'c', 'cpp', 'cython', + version: '0.26.3', + license: 'MIT', + meson_version: '>= 1.5.0', default_options: [ - 'buildtype=debugoptimized', + 'buildtype=release', + 'b_ndebug=if-release', 'c_std=c99', 'cpp_std=c++14', ], @@ -13,32 +14,47 @@ project( cc = meson.get_compiler('c') cpp = meson.get_compiler('cpp') -cython = find_program('cython') +cy = meson.get_compiler('cython') +cython = find_program(cy.cmd_array()[0]) +if not cy.version().version_compare('>=3.0.8') + error('SciPy requires Cython >= 3.0.8') +endif is_windows = host_machine.system() == 'windows' # https://mesonbuild.com/Python-module.html -py_mod = import('python') -py3 = py_mod.find_installation(pure: false) +py3 = import('python').find_installation(pure: false) py3_dep = py3.dependency() # NumPy include directory - needed in all submodules incdir_numpy = run_command(py3, - [ - '-c', - 'import os; os.chdir(".."); import numpy; print(numpy.get_include())' - ], - check: true + [ + '-c', + '''import os +#os.chdir(os.path.join("..", "tools")) +import numpy as np +try: + incdir = os.path.relpath(np.get_include()) +except Exception: + incdir = np.get_include() +print(incdir) + ''' + ], + check: true ).stdout().strip() +message(incdir_numpy) inc_np = include_directories(incdir_numpy) numpy_nodepr_api = '-DNPY_NO_DEPRECATED_API=NPY_1_9_API_VERSION' +cython_args = ['-3', '--fast-fail', '--output-file', '@OUTPUT@', '--include-dir', '@BUILD_ROOT@', '@INPUT@'] +if cy.version().version_compare('>=3.1.0') + cython_args += ['-Xfreethreading_compatible=True'] +endif +cython_cplus_args = ['--cplus'] + cython_args -cython_cli = find_program('pygrappa/utils/cythoner.py') - -cython_gen_cpp = generator(cython_cli, - arguments : ['@INPUT@', '@OUTPUT@', '--cplus'], +cython_gen_cpp = generator(cython, + arguments : cython_cplus_args, output : '@BASENAME@.cpp', depends : []) diff --git a/pygrappa/__init__.py b/pygrappa/__init__.py index f3e5aa4..2b2db7d 100644 --- a/pygrappa/__init__.py +++ b/pygrappa/__init__.py @@ -1,4 +1,4 @@ -'''Bring functions up to the correct level.''' +"""Bring functions up to the correct level.""" # GRAPPA from .mdgrappa import mdgrappa # NOQA diff --git a/pygrappa/cgsense.py b/pygrappa/cgsense.py index 796b667..8d3606f 100644 --- a/pygrappa/cgsense.py +++ b/pygrappa/cgsense.py @@ -1,4 +1,4 @@ -'''Python implementation of iterative and CG-SENSE.''' +"""Python implementation of iterative and CG-SENSE.""" from time import time import logging @@ -8,8 +8,8 @@ def _fft(x0, axes=None): - '''Utility Forward FFT function. - ''' + """Utility Forward FFT function. + """ if axes is None: axes = np.arange(x0.ndim-1) return np.fft.fftshift(np.fft.fftn(np.fft.ifftshift( @@ -17,16 +17,16 @@ def _fft(x0, axes=None): def _ifft(x0, axes=None): - '''Utility Inverse FFT function. - ''' + """Utility Inverse FFT function. + """ if axes is None: axes = np.arange(x0.ndim-1) return np.fft.ifftshift(np.fft.ifftn(np.fft.fftshift( x0, axes=axes), axes=axes), axes=axes) -def cgsense(kspace, sens, coil_axis=-1): - '''Conjugate Gradient SENSE for arbitrary Cartesian acquisitions. +def cgsense(kspace, sens, coil_axis: int = -1): + """Conjugate Gradient SENSE for arbitrary Cartesian acquisitions. Parameters ---------- @@ -65,7 +65,7 @@ def cgsense(kspace, sens, coil_axis=-1): Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 46.4 (2001): 638-651. - ''' + """ # Make sure coils are in the back kspace = np.moveaxis(kspace, coil_axis, -1) @@ -98,13 +98,13 @@ def cgsense(kspace, sens, coil_axis=-1): # => E : (sx*sy, sx*sy) def _AH(x0): - '''kspace -> imspace''' + """kspace -> imspace""" x0 = np.reshape(x0, kspace.shape) res = np.sum(sens.conj()*_ifft(x0), axis=-1) return np.reshape(res, (-1,)) def _A(x0): - '''imspace -> kspace''' + """imspace -> kspace""" res = np.reshape(x0, dims) res = _fft(res[..., None]*sens)*mask[..., None] return np.reshape(res, (-1,)) @@ -122,7 +122,3 @@ def E(x0): logging.info('CG-SENSE took %g sec', (time() - t0)) return np.reshape(x, dims).astype(tipe) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/coils.py b/pygrappa/coils.py index 970a521..1ca55f2 100644 --- a/pygrappa/coils.py +++ b/pygrappa/coils.py @@ -1,12 +1,12 @@ -'''Coil estimation strategies.''' +"""Coil estimation strategies.""" import numpy as np from scipy.linalg import eigh from skimage.filters import threshold_li -def walsh(imspace, mask=None, coil_axis=-1): - '''Stochastic matched filter coil combine. +def walsh(imspace, mask=None, coil_axis: int = -1): + """Stochastic matched filter coil combine. Parameters ---------- @@ -31,7 +31,7 @@ def walsh(imspace, mask=None, coil_axis=-1): imagery." Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 43.5 (2000): 682-690. - ''' + """ imspace = np.moveaxis(imspace, coil_axis, -1) ncoils = imspace.shape[-1] ns = np.prod(imspace.shape[:-1]) diff --git a/pygrappa/examples/basic_cgrappa.py b/pygrappa/examples/basic_cgrappa.py index ffdddb2..a9e7849 100644 --- a/pygrappa/examples/basic_cgrappa.py +++ b/pygrappa/examples/basic_cgrappa.py @@ -1,4 +1,4 @@ -'''Basic CGRAPPA example using Shepp-Logan phantom.''' +"""Basic CGRAPPA example using Shepp-Logan phantom.""" import numpy as np import matplotlib.pyplot as plt @@ -6,6 +6,7 @@ from pygrappa import cgrappa + if __name__ == '__main__': # Generate fake sensitivity maps: mps diff --git a/pygrappa/examples/basic_cgsense.py b/pygrappa/examples/basic_cgsense.py index f762be5..98e7655 100644 --- a/pygrappa/examples/basic_cgsense.py +++ b/pygrappa/examples/basic_cgsense.py @@ -1,4 +1,4 @@ -'''Basic usage of CG-SENSE implementation.''' +"""Basic usage of CG-SENSE implementation.""" import numpy as np import matplotlib.pyplot as plt @@ -8,6 +8,7 @@ from pygrappa import cgsense from pygrappa.utils import gaussian_csm + if __name__ == '__main__': N, nc = 128, 4 diff --git a/pygrappa/examples/basic_gfactor.py b/pygrappa/examples/basic_gfactor.py index 772251b..0314fd8 100644 --- a/pygrappa/examples/basic_gfactor.py +++ b/pygrappa/examples/basic_gfactor.py @@ -1,4 +1,4 @@ -'''Simple g-factor maps.''' +"""Simple g-factor maps.""" import numpy as np import matplotlib.pyplot as plt @@ -6,6 +6,7 @@ from pygrappa import gfactor, gfactor_single_coil_R2 from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # Make circle diff --git a/pygrappa/examples/basic_grappa.py b/pygrappa/examples/basic_grappa.py index 902380e..b8f5438 100644 --- a/pygrappa/examples/basic_grappa.py +++ b/pygrappa/examples/basic_grappa.py @@ -1,4 +1,4 @@ -'''Basic GRAPPA example using Shepp-Logan phantom.''' +"""Basic GRAPPA example using Shepp-Logan phantom.""" import numpy as np import matplotlib.pyplot as plt @@ -6,6 +6,7 @@ from pygrappa import grappa + if __name__ == '__main__': # Generate fake sensitivity maps: mps diff --git a/pygrappa/examples/basic_grappaop.py b/pygrappa/examples/basic_grappaop.py index 5d7ca79..49156ee 100644 --- a/pygrappa/examples/basic_grappaop.py +++ b/pygrappa/examples/basic_grappaop.py @@ -1,4 +1,4 @@ -'''Basic usage of the GRAPPA operator.''' +"""Basic usage of the GRAPPA operator.""" import numpy as np import matplotlib.pyplot as plt @@ -8,7 +8,7 @@ except ImportError: from skimage.measure import compare_nrmse -from pygrappa import cgrappa, grappaop +from pygrappa import mdgrappa, grappaop from pygrappa.utils import gaussian_csm @@ -51,7 +51,7 @@ def normalize(x0): kspace4x1[3::4, ...] = 0 # Compare to regular ol' GRAPPA - grecon4x1 = cgrappa(kspace4x1, calib, kernel_size=(4, 5)) + grecon4x1 = mdgrappa(kspace4x1, calib, kernel_size=(4, 5)) # Get a GRAPPA operator and do the recon Gx, Gy = grappaop(calib) @@ -65,7 +65,7 @@ def normalize(x0): kspace2x2 = kspace.copy() kspace2x2[1::2, ...] = 0 kspace2x2[:, 1::2, :] = 0 - grecon2x2 = cgrappa(kspace2x2, calib, kernel_size=(4, 5)) + grecon2x2 = mdgrappa(kspace2x2, calib, kernel_size=(4, 5)) recon2x2 = kspace2x2.copy() recon2x2[1::2, ...] = recon2x2[::2, ...] @ Gx recon2x2[:, 1::2, :] = recon2x2[:, ::2, :] @ Gy diff --git a/pygrappa/examples/basic_gridding.py b/pygrappa/examples/basic_gridding.py index 8b55b8b..8f73a51 100644 --- a/pygrappa/examples/basic_gridding.py +++ b/pygrappa/examples/basic_gridding.py @@ -1,4 +1,4 @@ -'''Demonstrate how to grid non-Cartesian data. +"""Demonstrate how to grid non-Cartesian data. Notes ----- @@ -22,7 +22,7 @@ NFFT 3---a software library for various nonequispaced fast Fourier transforms." ACM Transactions on Mathematical Software (TOMS) 36.4 (2009): 19. -''' +""" from time import time diff --git a/pygrappa/examples/basic_hpgrappa.py b/pygrappa/examples/basic_hpgrappa.py index 38bbb4c..a52a15a 100644 --- a/pygrappa/examples/basic_hpgrappa.py +++ b/pygrappa/examples/basic_hpgrappa.py @@ -1,4 +1,4 @@ -'''Basic hp-GRAPPA usage.''' +"""Basic hp-GRAPPA usage.""" import numpy as np from mpl_toolkits.mplot3d import Axes3D # pylint: disable=W0611 # NOQA @@ -8,6 +8,7 @@ from pygrappa import hpgrappa, mdgrappa from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # The much abused Shepp-Logan phantom diff --git a/pygrappa/examples/basic_igrappa.py b/pygrappa/examples/basic_igrappa.py index f5169ee..50a46d7 100644 --- a/pygrappa/examples/basic_igrappa.py +++ b/pygrappa/examples/basic_igrappa.py @@ -1,12 +1,13 @@ -'''Demonstrate usage of iGRAPPA.''' +"""Demonstrate usage of iGRAPPA.""" import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan -from pygrappa import igrappa, cgrappa +from pygrappa import igrappa, mdgrappa from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # Simple phantom @@ -31,7 +32,7 @@ kspace[1::2, ::2, :] = 0 # Reconstruct using both GRAPPA and iGRAPPA - res_grappa = cgrappa(kspace, calib) + res_grappa = mdgrappa(kspace, calib) res_igrappa, mse = igrappa(kspace, calib, ref=ref) # Bring back to image space diff --git a/pygrappa/examples/basic_mdgrappa.py b/pygrappa/examples/basic_mdgrappa.py index deaaabb..e0b953f 100644 --- a/pygrappa/examples/basic_mdgrappa.py +++ b/pygrappa/examples/basic_mdgrappa.py @@ -1,4 +1,4 @@ -'''Basic usage of multidimensional GRAPPA.''' +"""Basic usage of multidimensional GRAPPA.""" from time import time @@ -9,6 +9,7 @@ from pygrappa import mdgrappa from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # Generate fake sensitivity maps: mps diff --git a/pygrappa/examples/basic_nlgrappa.py b/pygrappa/examples/basic_nlgrappa.py index aa9c5b0..58e6957 100644 --- a/pygrappa/examples/basic_nlgrappa.py +++ b/pygrappa/examples/basic_nlgrappa.py @@ -1,4 +1,4 @@ -'''Basic usage of NL-GRAPPA.''' +"""Basic usage of NL-GRAPPA.""" import numpy as np import matplotlib.pyplot as plt @@ -8,11 +8,11 @@ from skimage.measure import compare_nrmse from phantominator import shepp_logan -from pygrappa import nlgrappa, cgrappa +from pygrappa import nlgrappa, mdgrappa from pygrappa.utils import gaussian_csm -if __name__ == '__main__': +if __name__ == '__main__': N, nc = 256, 16 ph = shepp_logan(N)[..., None]*gaussian_csm(N, N, nc) @@ -32,7 +32,7 @@ kspace3x1[2::3, ...] = 0 # Reconstruct using both GRAPPA and VC-GRAPPA - res_grappa = cgrappa(kspace3x1.copy(), calib) + res_grappa = mdgrappa(kspace3x1.copy(), calib) res_nlgrappa = nlgrappa( kspace3x1.copy(), calib, ml_kernel='polynomial', ml_kernel_args={'cross_term_neighbors': 0}) diff --git a/pygrappa/examples/basic_nlgrappa_matlab.py b/pygrappa/examples/basic_nlgrappa_matlab.py index f8f6cd4..9a63694 100644 --- a/pygrappa/examples/basic_nlgrappa_matlab.py +++ b/pygrappa/examples/basic_nlgrappa_matlab.py @@ -1,4 +1,4 @@ -'''Show basic usage of NL-GRAPPA MATLAB port.''' +"""Show basic usage of NL-GRAPPA MATLAB port.""" import numpy as np import matplotlib.pyplot as plt @@ -7,6 +7,7 @@ from pygrappa import nlgrappa_matlab from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # Generate data diff --git a/pygrappa/examples/basic_pars.py b/pygrappa/examples/basic_pars.py index 94ed29b..641822c 100644 --- a/pygrappa/examples/basic_pars.py +++ b/pygrappa/examples/basic_pars.py @@ -1,4 +1,4 @@ -'''Demo of Non-Cartesian GRAPPA using PARS.''' +"""Demo of Non-Cartesian GRAPPA using PARS.""" import numpy as np import matplotlib.pyplot as plt diff --git a/pygrappa/examples/basic_radialgrappaop.py b/pygrappa/examples/basic_radialgrappaop.py index 11a5d93..02c1aaf 100644 --- a/pygrappa/examples/basic_radialgrappaop.py +++ b/pygrappa/examples/basic_radialgrappaop.py @@ -1,4 +1,4 @@ -'''Basic usage of Radial GRAPPA operator.''' +"""Basic usage of Radial GRAPPA operator.""" from time import time @@ -95,7 +95,7 @@ def sos(x0): plt.imshow(true - scgrog) plt.title('Residual') nrmse = compare_nrmse(true, scgrog) - ssim = compare_ssim(true, scgrog) + ssim = compare_ssim(true, scgrog, data_range=np.max(true.flatten()) - np.min(true.flatten())) plt.xlabel('NRMSE: %g, SSIM: %g' % (nrmse, ssim)) # print(nrmse, ssim) diff --git a/pygrappa/examples/basic_seggrappa.py b/pygrappa/examples/basic_seggrappa.py index 342b652..9ab6fe2 100644 --- a/pygrappa/examples/basic_seggrappa.py +++ b/pygrappa/examples/basic_seggrappa.py @@ -8,7 +8,7 @@ except ImportError: from skimage.measure import compare_nrmse -from pygrappa import cgrappa, seggrappa +from pygrappa import mdgrappa, seggrappa from pygrappa.utils import gaussian_csm if __name__ == '__main__': @@ -38,7 +38,7 @@ res_seg = seggrappa(kspace, [calib_lower, calib_upper]) # Reconstruct using single calibration region at the center - res_grappa = cgrappa(kspace, calib) + res_grappa = mdgrappa(kspace, calib) # Into image space imspace_seg = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift( diff --git a/pygrappa/examples/basic_sense1d.py b/pygrappa/examples/basic_sense1d.py index e1cf1e0..314d184 100644 --- a/pygrappa/examples/basic_sense1d.py +++ b/pygrappa/examples/basic_sense1d.py @@ -1,4 +1,4 @@ -'''Show basic usage of 1D SENSE.''' +"""Show basic usage of 1D SENSE.""" import numpy as np import matplotlib.pyplot as plt @@ -7,6 +7,7 @@ from pygrappa import sense1d from pygrappa.utils import gaussian_csm + if __name__ == '__main__': N, nc = 128, 8 im = shepp_logan(N) diff --git a/pygrappa/examples/basic_slicegrappa.py b/pygrappa/examples/basic_slicegrappa.py index 8860124..961d7be 100644 --- a/pygrappa/examples/basic_slicegrappa.py +++ b/pygrappa/examples/basic_slicegrappa.py @@ -1,4 +1,4 @@ -'''Basic demo of Slice-GRAPPA.''' +"""Basic demo of Slice-GRAPPA.""" import numpy as np from phantominator import shepp_logan @@ -8,8 +8,8 @@ from pygrappa import slicegrappa from pygrappa.utils import gaussian_csm -if __name__ == '__main__': +if __name__ == '__main__': # Get slices of 3D Shepp-Logan phantom N = 128 ns = 2 @@ -55,12 +55,12 @@ ax = plt.imshow(np.abs(res0[..., 0]), cmap='gray') def init(): - '''Initialize ax data.''' + """Initialize ax data.""" ax.set_array(np.abs(res0[..., 0])) return (ax,) def animate(frame): - '''Update frame.''' + """Update frame.""" ax.set_array(np.abs(res0[..., frame])) return (ax,) diff --git a/pygrappa/examples/basic_splitslicegrappa.py b/pygrappa/examples/basic_splitslicegrappa.py index c549b3d..9312317 100644 --- a/pygrappa/examples/basic_splitslicegrappa.py +++ b/pygrappa/examples/basic_splitslicegrappa.py @@ -1,4 +1,4 @@ -'''Basic demo of Split-Slice-GRAPPA.''' +"""Basic demo of Split-Slice-GRAPPA.""" import numpy as np from phantominator import shepp_logan @@ -8,8 +8,8 @@ from pygrappa import splitslicegrappa from pygrappa.utils import gaussian_csm -if __name__ == '__main__': +if __name__ == '__main__': # Get slices of 3D Shepp-Logan phantom N = 128 ns = 2 @@ -55,12 +55,12 @@ ax = plt.imshow(np.abs(res0[..., 0]), cmap='gray') def init(): - '''Initialize ax data.''' + """Initialize ax data.""" ax.set_array(np.abs(res0[..., 0])) return (ax,) def animate(frame): - '''Update frame.''' + """Update frame.""" ax.set_array(np.abs(res0[..., frame])) return (ax,) diff --git a/pygrappa/examples/basic_tgrappa.py b/pygrappa/examples/basic_tgrappa.py index 5de5e83..b0cd678 100644 --- a/pygrappa/examples/basic_tgrappa.py +++ b/pygrappa/examples/basic_tgrappa.py @@ -1,4 +1,4 @@ -'''Example demonstrating how to use TGRAPPA.''' +"""Example demonstrating how to use TGRAPPA.""" import numpy as np from phantominator import dynamic @@ -32,7 +32,7 @@ kspace[0::2, 1::2, :, 0::2] = 0 kspace[1::2, 0::2, :, 1::2] = 0 - # Reconstuct using TGRAPPA algorithm: + # Reconstruct using TGRAPPA algorithm: # Use 20x20 calibration region # Kernel size: (4, 5) res = tgrappa(kspace, calib_size=(20, 20), kernel_size=(4, 5)) @@ -52,12 +52,12 @@ ax = plt.imshow(np.abs(res0[..., 0]), cmap='gray') def init(): - '''Initialize ax data.''' + """Initialize ax data.""" ax.set_array(np.abs(res0[..., 0])) return (ax,) def animate(frame): - '''Update frame.''' + """Update frame.""" ax.set_array(np.abs(res0[..., frame])) return (ax,) diff --git a/pygrappa/examples/basic_ttgrappa.py b/pygrappa/examples/basic_ttgrappa.py index 3bb45b6..ab5d616 100644 --- a/pygrappa/examples/basic_ttgrappa.py +++ b/pygrappa/examples/basic_ttgrappa.py @@ -1,4 +1,4 @@ -'''Demo of Non-Cartesian GRAPPA.''' +"""Demo of Non-Cartesian GRAPPA.""" import numpy as np import matplotlib.pyplot as plt diff --git a/pygrappa/examples/basic_vcgrappa.py b/pygrappa/examples/basic_vcgrappa.py index 726efc0..662c35a 100644 --- a/pygrappa/examples/basic_vcgrappa.py +++ b/pygrappa/examples/basic_vcgrappa.py @@ -1,4 +1,4 @@ -'''Demonstrate usage of VC-GRAPPA.''' +"""Demonstrate usage of VC-GRAPPA.""" import numpy as np import matplotlib.pyplot as plt @@ -11,6 +11,7 @@ from pygrappa import vcgrappa, grappa from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # Simple phantom diff --git a/pygrappa/examples/inverse_grog.py b/pygrappa/examples/inverse_grog.py index 769bd90..bc71f85 100644 --- a/pygrappa/examples/inverse_grog.py +++ b/pygrappa/examples/inverse_grog.py @@ -1,4 +1,4 @@ -'''Do Cartesian to radial gridding.''' +"""Do Cartesian to radial gridding.""" from time import time diff --git a/pygrappa/examples/md_cgsense.py b/pygrappa/examples/md_cgsense.py index 05c854e..751ac22 100644 --- a/pygrappa/examples/md_cgsense.py +++ b/pygrappa/examples/md_cgsense.py @@ -1,4 +1,4 @@ -'''Multidimensional CG-SENSE.''' +"""Multidimensional CG-SENSE.""" from time import time @@ -9,6 +9,7 @@ from pygrappa import cgsense from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # Generate fake sensitivity maps: mps diff --git a/pygrappa/examples/primefac_grog.py b/pygrappa/examples/primefac_grog.py index 2e53ea2..8ac70d7 100644 --- a/pygrappa/examples/primefac_grog.py +++ b/pygrappa/examples/primefac_grog.py @@ -1,5 +1,4 @@ -'''ISMRM abstract code for prime factorization speed-up for SC-GROG. -''' +"""ISMRM abstract code for prime factorization speed-up for SC-GROG.""" from time import time diff --git a/pygrappa/examples/tikhonov_regularization.py b/pygrappa/examples/tikhonov_regularization.py index 18507a2..430a376 100644 --- a/pygrappa/examples/tikhonov_regularization.py +++ b/pygrappa/examples/tikhonov_regularization.py @@ -1,4 +1,4 @@ -'''Demonstrate the effect of Tikhonov regularization.''' +"""Demonstrate the effect of Tikhonov regularization.""" import numpy as np import matplotlib.pyplot as plt @@ -9,9 +9,10 @@ from skimage.measure import compare_nrmse from tqdm import tqdm -from pygrappa import cgrappa as grappa +from pygrappa import mdgrappa as grappa from pygrappa.utils import gaussian_csm + if __name__ == '__main__': # Simple phantom diff --git a/pygrappa/examples/use_memmap.py b/pygrappa/examples/use_memmap.py index e8a0c4b..3a765fd 100644 --- a/pygrappa/examples/use_memmap.py +++ b/pygrappa/examples/use_memmap.py @@ -1,4 +1,4 @@ -'''Example demonstrating how process datasets stored in memmap.''' +"""Example demonstrating how process datasets stored in memmap.""" from tempfile import NamedTemporaryFile as NTF @@ -8,6 +8,7 @@ from pygrappa import grappa + if __name__ == '__main__': # Generate fake sensitivity maps: mps @@ -65,7 +66,7 @@ # reconstruct, write res out to a memmap with name res_file grappa( kspace, calib, kernel_size, coil_axis=-1, lamda=0.01, - memmap=True, memmap_filename=res_file) + memmap=True, memmap_filename=res_file.name) # Take a look by opening up the memmap res = np.memmap( diff --git a/pygrappa/find_acs.py b/pygrappa/find_acs.py index 9795b14..08dddcd 100644 --- a/pygrappa/find_acs.py +++ b/pygrappa/find_acs.py @@ -1,4 +1,4 @@ -'''Automated location of a rectangular ACS.''' +"""Automated location of a rectangular ACS.""" from time import time import logging @@ -6,8 +6,8 @@ import numpy as np -def find_acs(kspace, coil_axis=-1): - '''Find the largest centered hyper-rectangle possible. +def find_acs(kspace, coil_axis: int = -1): + """Find the largest centered hyper-rectangle possible. Parameters ---------- @@ -32,7 +32,7 @@ def find_acs(kspace, coil_axis=-1): smaller than the entirety of the data. It grows a hyper- rectangle from the center and checks to see if there are any new holes in the region each time it expands. - ''' + """ kspace = np.moveaxis(kspace, coil_axis, -1) mask = np.abs(kspace[..., 0]) > 0 diff --git a/pygrappa/gfactor.py b/pygrappa/gfactor.py index 2de8103..2168f23 100644 --- a/pygrappa/gfactor.py +++ b/pygrappa/gfactor.py @@ -1,10 +1,10 @@ -'''Calculate g-factor maps.''' +"""Calculate g-factor maps.""" import numpy as np -def gfactor(coils, Rx, Ry, coil_axis=-1, tol=1e-6): - '''Compute g-factor map for coil sensitities and accelerations. +def gfactor(coils, Rx: int, Ry: int, coil_axis: int = -1, tol: float = 1e-6): + """Compute g-factor map for coil sensitities and accelerations. Parameters ---------- @@ -31,7 +31,7 @@ def gfactor(coils, Rx, Ry, coil_axis=-1, tol=1e-6): ---------- .. [1] https://web.stanford.edu/class/ee369c/restricted/ Solutions/assignment_4_solns.pdf - ''' + """ # Coils to da back coils = np.moveaxis(coils, coil_axis, -1) @@ -54,7 +54,7 @@ def gfactor(coils, Rx, Ry, coil_axis=-1, tol=1e-6): ndx = int(np.mod(ii + LX*nrx, nx)) ndy = int(np.mod(jj + LY*nry, ny)) CT = coils[ndx, ndy, :] - if ((LX == 0) and (LY == 0)): + if (LX == 0) and (LY == 0): s.append(CT) elif sos[ndx, ndy] > tol: s.append(CT) @@ -67,8 +67,8 @@ def gfactor(coils, Rx, Ry, coil_axis=-1, tol=1e-6): return g -def gfactor_single_coil_R2(coil, Rx=2, Ry=1): - '''Specific example of a single homogeneous coil, R=2. +def gfactor_single_coil_R2(coil, Rx: int = 2, Ry: int = 1): + """Specific example of a single homogeneous coil, R=2. Parameters ---------- @@ -100,7 +100,7 @@ def gfactor_single_coil_R2(coil, Rx=2, Ry=1): Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 61.1 (2009): 93-102. - ''' + """ assert coil.ndim == 2, 'Must be single coil!' assert (Rx == 2 and Ry == 1) or (Rx == 1 and Ry == 2), ( @@ -113,7 +113,3 @@ def gfactor_single_coil_R2(coil, Rx=2, Ry=1): shifted = np.fft.fftshift(np.angle(coil), axes=1) return mask/np.sin(np.abs(np.angle(coil) - shifted)) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/grappa.py b/pygrappa/grappa.py index c8bbd4c..4488fdc 100644 --- a/pygrappa/grappa.py +++ b/pygrappa/grappa.py @@ -1,4 +1,4 @@ -'''Python GRAPPA implementation. +"""Python GRAPPA implementation. More efficient Python implementation of GRAPPA. @@ -20,10 +20,10 @@ kernel geometries simultaneously if possible, or at least have an option to do chunks at a time. -Currently each hole in kspace is being looped over when applying +Currently, each hole in kspace is being looped over when applying weights for a single kernel type. It would be nice to apply the weights for all corresponding holes simultaneously. -''' +""" from time import time from tempfile import NamedTemporaryFile as NTF @@ -33,9 +33,9 @@ def grappa( - kspace, calib, kernel_size=(5, 5), coil_axis=-1, lamda=0.01, - memmap=False, memmap_filename='out.memmap', silent=True): - '''GeneRalized Autocalibrating Partially Parallel Acquisitions. + kspace, calib, kernel_size=(5, 5), coil_axis: int = -1, lamda: float = 0.01, + memmap: bool = False, memmap_filename: str = 'out.memmap', silent: bool = True): + """GeneRalized Autocalibrating Partially Parallel Acquisitions. Parameters ---------- @@ -80,7 +80,7 @@ def grappa( Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 47.6 (2002): 1202-1210. - ''' + """ # Remember what shape the final reconstruction should be fin_shape = kspace.shape[:] @@ -255,7 +255,3 @@ def grappa( return np.moveaxis( (recon[:] + kspace)[kx2:-kx2, ky2:-ky2, :], -1, coil_axis) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/grappaop.py b/pygrappa/grappaop.py index 1ec556a..7049201 100644 --- a/pygrappa/grappaop.py +++ b/pygrappa/grappaop.py @@ -1,10 +1,10 @@ -'''Python implementation of the GRAPPA operator formalism.''' +"""Python implementation of the GRAPPA operator formalism.""" import numpy as np -def grappaop(calib, coil_axis=-1, lamda=0.01): - '''GRAPPA operator for Cartesian calibration datasets. +def grappaop(calib, coil_axis: int = -1, lamda: int = 0.01): + """GRAPPA operator for Cartesian calibration datasets. Parameters ---------- @@ -35,7 +35,7 @@ def grappaop(calib, coil_axis=-1, lamda=0.01): .. [1] Griswold, Mark A., et al. "Parallel magnetic resonance imaging using the GRAPPA operator formalism." Magnetic resonance in medicine 54.6 (2005): 1553-1556. - ''' + """ # Coil axis in the back calib = np.moveaxis(calib, coil_axis, -1) @@ -60,8 +60,4 @@ def grappaop(calib, coil_axis=-1, lamda=0.01): lamda0 = lamda*np.linalg.norm(Syh)/Syh.shape[0] Gy = np.linalg.solve( Syh @ Sy + lamda0*np.eye(Syh.shape[0]), Syh @ Ty) - return (Gx, Gy) - - -if __name__ == '__main__': - pass + return Gx, Gy diff --git a/pygrappa/grog.py b/pygrappa/grog.py index e4c04b8..29b0a7c 100644 --- a/pygrappa/grog.py +++ b/pygrappa/grog.py @@ -1,4 +1,4 @@ -'''Python implmentation of the GROG algorithm.''' +"""Python implmentation of the GROG algorithm.""" from time import time @@ -12,16 +12,16 @@ grog_gridding_double, grog_gridding_float) # pylint: disable=E0611 -def _make_key(key, precision): - '''Dictionary keys.''' +def _make_key(key, precision: int): + """Dictionary keys.""" return np.around(key, decimals=int(precision)) def grog( - kx, ky, k, N, M, Gx, Gy, precision=2, radius=.75, Dx=None, - Dy=None, coil_axis=-1, ret_image=False, ret_dicts=False, - use_primefac=False, remove_os=True, inverse=False): - '''GRAPPA operator gridding. + kx, ky, k, N: int, M: int, Gx, Gy, precision: int = 2, radius: float = .75, Dx: dict = None, + Dy: dict = None, coil_axis: int = -1, ret_image: bool = False, ret_dicts: bool = False, + use_primefac: bool = False, remove_os: bool = True, inverse: bool = False): + """GRAPPA operator gridding. Parameters ---------- @@ -83,7 +83,7 @@ def grog( Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 59.4 (2008): 930-935. - ''' + """ # Make sure types are consistent before calling grog funcs assert kx.dtype == ky.dtype, ( diff --git a/pygrappa/hpgrappa.py b/pygrappa/hpgrappa.py index e39f27d..bed1ede 100644 --- a/pygrappa/hpgrappa.py +++ b/pygrappa/hpgrappa.py @@ -1,4 +1,4 @@ -'''Python implementation of hp-GRAPPA.''' +"""Python implementation of hp-GRAPPA.""" import numpy as np @@ -6,9 +6,9 @@ def hpgrappa( - kspace, calib, fov, kernel_size=(5, 5), w=None, c=None, - ret_filter=False, coil_axis=-1, lamda=0.01, silent=True): - '''High-pass GRAPPA. + kspace, calib, fov, kernel_size=(5, 5), w: float = None, c: float = None, + ret_filter: bool = False, coil_axis: int = -1, lamda: float = 0.01): + """High-pass GRAPPA. Parameters ---------- @@ -37,7 +37,7 @@ def hpgrappa( imaging." Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 59.3 (2008): 642-649. - ''' + """ # Pass GRAPPA arguments forward grappa_args = { @@ -91,12 +91,12 @@ def hpgrappa( # Return the filter if user asked for it if ret_filter: - return (res.astype(tipe), F2) + return res.astype(tipe), F2 return res.astype(tipe) -def _filter_parameters(ncoils, num_acs_lines): - '''Table 1: predefined filter parameters from [1]_. +def _filter_parameters(ncoils: int, num_acs_lines: int): + """Table 1: predefined filter parameters from [1]_. Parameters ---------- @@ -110,7 +110,7 @@ def _filter_parameters(ncoils, num_acs_lines): ------- (w, c) : tuple Filter parameters. - ''' + """ LESS_THAN_8 = True MORE_THAN_8 = False diff --git a/pygrappa/igrappa.py b/pygrappa/igrappa.py index c1971b3..085a414 100644 --- a/pygrappa/igrappa.py +++ b/pygrappa/igrappa.py @@ -1,4 +1,4 @@ -'''Python implementation of the iGRAPPA algorithm.''' +"""Python implementation of the iGRAPPA algorithm.""" import numpy as np from tqdm import trange @@ -11,9 +11,9 @@ def igrappa( - kspace, calib, kernel_size=(5, 5), k=0.3, coil_axis=-1, - lamda=0.01, ref=None, niter=10, silent=True, backend=mdgrappa): - '''Iterative GRAPPA. + kspace, calib, kernel_size=(5, 5), k: float = 0.3, coil_axis: int = -1, + lamda: float = 0.01, ref=None, niter: int = 10, silent: bool = True, backend=mdgrappa): + """Iterative GRAPPA. Parameters ---------- @@ -68,7 +68,7 @@ def igrappa( Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 59.4 (2008): 903-907. - ''' + """ # Make sure k has a reasonable value assert 0 < k < 1, 'Parameter k should be in (0, 1)!' @@ -167,5 +167,5 @@ def range_fun(x): # otherwise, just return reconstruction kIm = np.moveaxis(kIm, -1, coil_axis) if ref is not None: - return (kIm.astype(tipe), mse) + return kIm.astype(tipe), mse return kIm.astype(tipe) diff --git a/pygrappa/kernels.py b/pygrappa/kernels.py index fb06ec5..28d36e2 100644 --- a/pygrappa/kernels.py +++ b/pygrappa/kernels.py @@ -1,18 +1,18 @@ -'''Machine learning kernel functions.''' +"""Machine learning kernel functions.""" import numpy as np # from sklearn.preprocessing import PolynomialFeatures -def polynomial_kernel(X, cross_term_neighbors=2): - '''Computes polynomial kernel. +def polynomial_kernel(X, cross_term_neighbors: int = 2): + """Computes polynomial kernel. Parameters ---------- X : array_like of shape (sx, sy, nc) Features to map to high dimensional feature-space. - ''' + """ _sx, _sy, nc = X.shape[:] diff --git a/pygrappa/kspa.py b/pygrappa/kspa.py index 11fb542..f19e144 100644 --- a/pygrappa/kspa.py +++ b/pygrappa/kspa.py @@ -1,4 +1,4 @@ -'''Python implementation of the kSPA algorithm.''' +"""Python implementation of the kSPA algorithm.""" from time import time @@ -9,8 +9,8 @@ def kspa( - kx, ky, k, sens, coil_axis=-1, sens_coil_axis=-1): - '''Recon for arbitrary trajectories using k‐space sparse matrices. + kx, ky, k, sens, coil_axis: int = -1, sens_coil_axis: int = -1): + """Recon for arbitrary trajectories using k‐space sparse matrices. Parameters ---------- @@ -29,7 +29,7 @@ def kspa( Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 58.6 (2007): 1171-1181. - ''' + """ # Move coils to the back sens = np.moveaxis(sens, sens_coil_axis, -1) diff --git a/pygrappa/lustig_grappa.py b/pygrappa/lustig_grappa.py index bb28bac..bd733bc 100644 --- a/pygrappa/lustig_grappa.py +++ b/pygrappa/lustig_grappa.py @@ -1,13 +1,13 @@ -'''Reference GRAPPA implementation ported to python.''' +"""Reference GRAPPA implementation ported to python.""" import numpy as np from tqdm import trange def lustig_grappa( - kspace, calib, kernel_size=(5, 5), coil_axis=-1, lamda=0.01, - disp=False, memmap=False, memmap_filename='out.memmap'): - '''GeneRalized Autocalibrating Partially Parallel Acquisitions. + kspace, calib, kernel_size=(5, 5), coil_axis: int = -1, lamda: float = 0.01, + disp: bool = False, memmap: bool = False, memmap_filename: str = "out.memmap"): + """GeneRalized Autocalibrating Partially Parallel Acquisitions. Parameters ---------- @@ -65,7 +65,7 @@ def lustig_grappa( International Society for Magnetic Resonance in Medicine 47.6 (2002): 1202-1210. .. [2] https://people.eecs.berkeley.edu/~mlustig/Software.html - ''' + """ # Put the coil dimension at the end kspace = np.moveaxis(kspace, coil_axis, -1) @@ -112,8 +112,8 @@ def lustig_grappa( return res -def ARC(kspace, AtA, kernel_size, c, lamda): - '''ARC.''' +def ARC(kspace, AtA, kernel_size, c, lamda: float): + """ARC.""" sx, sy, ncoils = kspace.shape[:] kx, ky = kernel_size[:] @@ -176,8 +176,8 @@ def ARC(kspace, AtA, kernel_size, c, lamda): def dat2AtA(data, kernel_size): - '''Computes the calibration matrix from calibration data. - ''' + """Computes the calibration matrix from calibration data. + """ tmp = im2row(data, kernel_size) tsx, tsy, tsz = tmp.shape[:] @@ -186,7 +186,7 @@ def dat2AtA(data, kernel_size): def im2row(im, win_shape): - '''res = im2row(im, winSize)''' + """res = im2row(im, winSize)""" sx, sy, sz = im.shape[:] wx, wy = win_shape[:] sh = (sx-wx+1)*(sy-wy+1) @@ -204,8 +204,8 @@ def im2row(im, win_shape): def calibrate(AtA, kernel_size, ncoils, coil, lamda, sampling=None): - '''Calibrate. - ''' + """Calibrate. + """ kx, ky = kernel_size[:] @@ -240,8 +240,4 @@ def calibrate(AtA, kernel_size, ncoils, coil, lamda, sampling=None): kernel[idxA_flat] = rawkernel.squeeze() kernel = np.reshape(kernel, sampling.shape, order='F') - return (kernel, rawkernel) - - -if __name__ == '__main__': - pass + return kernel, rawkernel diff --git a/pygrappa/mdgrappa.py b/pygrappa/mdgrappa.py index f03b81d..c83f593 100644 --- a/pygrappa/mdgrappa.py +++ b/pygrappa/mdgrappa.py @@ -1,4 +1,4 @@ -'''Python implementation of multidimensional GRAPPA.''' +"""Python implementation of multidimensional GRAPPA.""" from collections import defaultdict from time import time @@ -15,11 +15,11 @@ def mdgrappa( kspace, calib=None, kernel_size=None, - coil_axis=-1, - lamda=0.01, - weights=None, - ret_weights=False): - '''GeneRalized Autocalibrating Partially Parallel Acquisitions. + coil_axis: int = -1, + lamda: float = 0.01, + weights: dict = None, + ret_weights: bool = False): + """GeneRalized Autocalibrating Partially Parallel Acquisitions. Parameters ---------- @@ -65,7 +65,7 @@ def mdgrappa( Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 47.6 (2002): 1202-1210. - ''' + """ # coils to the back kspace = np.moveaxis(kspace, coil_axis, -1) @@ -159,12 +159,8 @@ def _apply_weights(holes, p0, np0, Ws0): if ret_weights: if weights: - return (recon, weights) + return recon, weights return (recon, {k: Ws[ii, :np.sum(np.frombuffer(k, dtype=bool))*nc, :] for ii, k in enumerate(P)}) return recon - - -if __name__ == '__main__': - pass diff --git a/pygrappa/ncgrappa.py b/pygrappa/ncgrappa.py index 9944d93..1607a7f 100644 --- a/pygrappa/ncgrappa.py +++ b/pygrappa/ncgrappa.py @@ -1,11 +1,11 @@ -'''Python implementation of Non-Cartesian GRAPPA.''' +"""Python implementation of Non-Cartesian GRAPPA.""" import numpy as np from scipy.spatial import cKDTree # pylint: disable=E0611 -def ncgrappa(kx, ky, k, cx, cy, calib, kernel_size, coil_axis=-1): - '''Non-Cartesian GRAPPA. +def ncgrappa(kx, ky, k, cx, cy, calib, kernel_size, coil_axis: int = -1): + """Non-Cartesian GRAPPA. Parameters ---------- @@ -34,7 +34,7 @@ def ncgrappa(kx, ky, k, cx, cy, calib, kernel_size, coil_axis=-1): 2D/3D non‐Cartesian sampling trajectories with rapid calibration." Magnetic resonance in medicine 82.3 (2019): 1101-1112. - ''' + """ # Assume k has coil at end unless user says it's upfront. We # want to assume that coils are in the back of calib and k: diff --git a/pygrappa/nlgrappa.py b/pygrappa/nlgrappa.py index acaf559..aea044b 100644 --- a/pygrappa/nlgrappa.py +++ b/pygrappa/nlgrappa.py @@ -1,16 +1,16 @@ -'''Python implementation of Non-Linear GRAPPA.''' +"""Python implementation of Non-Linear GRAPPA.""" from functools import partial import numpy as np -from pygrappa import cgrappa +from pygrappa import mdgrappa from pygrappa.kernels import polynomial_kernel def nlgrappa( - kspace, calib, kernel_size=(5, 5), ml_kernel='polynomial', - ml_kernel_args=None, coil_axis=-1): - '''NL-GRAPPA. + kspace, calib, kernel_size=(5, 5), ml_kernel: str = "polynomial", + ml_kernel_args: dict = None, coil_axis: int = -1): + """NL-GRAPPA. Parameters ---------- @@ -46,7 +46,7 @@ def nlgrappa( .. [1] Chang, Yuchou, Dong Liang, and Leslie Ying. "Nonlinear GRAPPA: A kernel approach to parallel MRI reconstruction." Magnetic resonance in medicine 68.3 (2012): 730-740. - ''' + """ raise NotImplementedError("NL-GRAPPA is not currently working!") @@ -80,11 +80,7 @@ def nlgrappa( # Pass onto cgrappa for the heavy lifting return np.moveaxis( - cgrappa( + mdgrappa( vkspace, vcalib, kernel_size=kernel_size, coil_axis=-1, nc_desired=nc, lamda=0), -1, coil_axis) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/nlgrappa_matlab.py b/pygrappa/nlgrappa_matlab.py index dc1feff..3863a56 100644 --- a/pygrappa/nlgrappa_matlab.py +++ b/pygrappa/nlgrappa_matlab.py @@ -1,4 +1,4 @@ -'''Python port of MATLAB script.''' +"""Python port of MATLAB script.""" import numpy as np from tqdm import trange @@ -7,7 +7,7 @@ def nlgrappa_matlab( reduced_fourier_data, ORF, pe_loc, acs_data, acs_line_loc, num_block, num_column, times_comp): - '''Python port of original NL-GRAPPA script. + """Python port of original NL-GRAPPA script. Parameters ---------- @@ -56,7 +56,7 @@ def nlgrappa_matlab( .. [1] Y. Chang, D. Liang, L. Ying, "Nonlinear GRAPPA: A Kernel Approach to Parallel MRI Reconstruction". Magn. Reson. Med. 2012 - ''' + """ # Get dimensions and initialization d1_reduced, d2, num_coil = reduced_fourier_data.shape[:] @@ -351,8 +351,4 @@ def nlgrappa_matlab( coef0 = fit_coef - return (full_fourier_data, rec_img, coef0) - - -if __name__ == '__main__': - pass + return full_fourier_data, rec_img, coef0 diff --git a/pygrappa/pars.py b/pygrappa/pars.py index 11280f1..e5caea3 100644 --- a/pygrappa/pars.py +++ b/pygrappa/pars.py @@ -1,4 +1,4 @@ -'''Python implementation of the PARS algorithm.''' +"""Python implementation of the PARS algorithm.""" from time import time @@ -9,9 +9,9 @@ def pars( - kx, ky, k, sens, tx=None, ty=None, kernel_size=25, - kernel_radius=None, coil_axis=-1): - '''Parallel MRI with adaptive radius in k‐space. + kx, ky, k, sens, tx=None, ty=None, kernel_size: int = 25, + kernel_radius: float = None, coil_axis: int = -1): + """Parallel MRI with adaptive radius in k‐space. Parameters ---------- @@ -55,7 +55,7 @@ def pars( radiofrequency coil encoded data." Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 53.6 (2005): 1383-1392. - ''' + """ # Move coil axis to the back k = np.moveaxis(k, coil_axis, -1) @@ -121,7 +121,3 @@ def pars( return np.moveaxis(np.fft.fftshift(np.fft.ifft2( np.fft.ifftshift(np.reshape(res, (sx, sy, nc), 'F'), axes=ax), axes=ax), axes=ax)[sx4:-sx4, sx4:-sx4, :], -1, coil_axis) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/pruno.py b/pygrappa/pruno.py index 3dd1a20..9ccbb52 100644 --- a/pygrappa/pruno.py +++ b/pygrappa/pruno.py @@ -1,4 +1,4 @@ -'''Python implementation of the PRUNO algorithm.''' +"""Python implementation of the PRUNO algorithm.""" import numpy as np from skimage.util import pad, view_as_windows @@ -10,8 +10,8 @@ from tqdm import trange -def pruno(kspace, calib, kernel_size=(5, 5), coil_axis=-1): - '''Parallel Reconstruction Using Null Operations (PRUNO). +def pruno(kspace, calib, kernel_size=(5, 5), coil_axis: int = -1): + """Parallel Reconstruction Using Null Operations (PRUNO). Parameters ---------- @@ -24,7 +24,7 @@ def pruno(kspace, calib, kernel_size=(5, 5), coil_axis=-1): .. [1] Zhang, Jian, Chunlei Liu, and Michael E. Moseley. "Parallel reconstruction using null operations." Magnetic resonance in medicine 66.5 (2011): 1241-1253. - ''' + """ # Coils to da back kspace = np.moveaxis(kspace, coil_axis, -1) diff --git a/pygrappa/radialgrappaop.py b/pygrappa/radialgrappaop.py index bd0c2d2..e2369e4 100644 --- a/pygrappa/radialgrappaop.py +++ b/pygrappa/radialgrappaop.py @@ -1,14 +1,14 @@ -'''Python implementation of Radial GRAPPA operator.''' +"""Python implementation of Radial GRAPPA operator.""" import numpy as np from scipy.linalg import expm, logm def radialgrappaop( - kx, ky, k, nspokes=None, spoke_axis=-2, coil_axis=-1, - spoke_axis_coord=-1, lamda=0.01, ret_lGtheta=False, - traj_warn=True): - '''Non-Cartesian Radial GRAPPA operator. + kx, ky, k, nspokes: int = None, spoke_axis: int = -2, coil_axis: int = -1, + spoke_axis_coord: int = -1, lamda: float = 0.01, ret_lGtheta: bool = False, + traj_warn: bool = True): + """Non-Cartesian Radial GRAPPA operator. Parameters ---------- @@ -64,7 +64,7 @@ def radialgrappaop( Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 59.4 (2008): 930-935. - ''' + """ # Move coils and spoke_axis to the back: if k.ndim == 2: @@ -138,8 +138,4 @@ def radialgrappaop( # Take matrix exponential to get from (lGx, lGy) -> (Gx, Gy) # and we're done! - return (expm(lGx), expm(lGy)) - - -if __name__ == '__main__': - pass + return expm(lGx), expm(lGy) diff --git a/pygrappa/seggrappa.py b/pygrappa/seggrappa.py index e0bbce5..9cdb9c6 100644 --- a/pygrappa/seggrappa.py +++ b/pygrappa/seggrappa.py @@ -1,12 +1,12 @@ -'''Python implementation of the Segmented GRAPPA algorithm.''' +"""Python implementation of the Segmented GRAPPA algorithm.""" import numpy as np -from pygrappa import cgrappa +from pygrappa import mdgrappa def seggrappa(kspace, calibs, *args, **kwargs): - '''Segmented GRAPPA. + """Segmented GRAPPA. See pygrappa.grappa() for full list of arguments. @@ -29,14 +29,10 @@ def seggrappa(kspace, calibs, *args, **kwargs): variable density sampling." Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 53.1 (2005): 186-193. - ''' + """ # Do the reconstruction for each of the calibration regions - recons = [cgrappa(kspace, c, *args, **kwargs) for c in calibs] + recons = [mdgrappa(kspace, c, *args, **kwargs) for c in calibs] # Average all the reconstructions return np.mean(recons, axis=0) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/sense1d.py b/pygrappa/sense1d.py index d989702..55b9d76 100644 --- a/pygrappa/sense1d.py +++ b/pygrappa/sense1d.py @@ -1,12 +1,12 @@ -'''Python implementation of SENSE.''' +"""Python implementation of SENSE.""" from time import time import numpy as np -def sense1d(im, sens, Rx=1, Ry=1, coil_axis=-1, imspace=True): - '''Sensitivity Encoding for Fast MRI (SENSE) along one dimension. +def sense1d(im, sens, Rx: int = 1, Ry: int = 1, coil_axis: int = -1, imspace: bool = True): + """Sensitivity Encoding for Fast MRI (SENSE) along one dimension. Parameters ---------- @@ -49,7 +49,7 @@ def sense1d(im, sens, Rx=1, Ry=1, coil_axis=-1, imspace=True): Resonance in Medicine 42.5 (1999): 952-962. .. [2] https://users.fmrib.ox.ac.uk/~mchiew/docs/ SENSE_tutorial.html - ''' + """ # We can only handle unwrapping one dimension: assert Rx == 1 or Ry == 1, 'One of Rx, Ry must be 1!' @@ -101,7 +101,3 @@ def sense1d(im, sens, Rx=1, Ry=1, coil_axis=-1, imspace=True): if flip_xy: res = np.moveaxis(res, 1, 0) return np.moveaxis(res, -1, coil_axis) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/simple_pruno.py b/pygrappa/simple_pruno.py index de84098..574a107 100644 --- a/pygrappa/simple_pruno.py +++ b/pygrappa/simple_pruno.py @@ -1,4 +1,4 @@ -'''Naive implementation to make sure we know what's going on.''' +"""Naive implementation to make sure we know what's going on.""" import numpy as np from skimage.util import view_as_windows @@ -10,9 +10,9 @@ def simple_pruno( - kspace, calib, kernel_size=(5, 5), coil_axis=-1, + kspace, calib, kernel_size=(5, 5), coil_axis: int = -1, sens=None, ph=None, kspace_ref=None): - '''PRUNO.''' + """PRUNO.""" # Coils to da back kspace = np.moveaxis(kspace, coil_axis, -1) diff --git a/pygrappa/slicegrappa.py b/pygrappa/slicegrappa.py index 1b575f3..697e264 100644 --- a/pygrappa/slicegrappa.py +++ b/pygrappa/slicegrappa.py @@ -1,4 +1,4 @@ -'''Python implementation of the Slice-GRAPPA algorithm.''' +"""Python implementation of the Slice-GRAPPA algorithm.""" import numpy as np from skimage.util import view_as_windows @@ -6,9 +6,9 @@ def slicegrappa( - kspace, calib, kernel_size=(5, 5), prior='sim', coil_axis=-2, - time_axis=-1, slice_axis=-1, lamda=0.01, split=False): - '''(Split)-Slice-GRAPPA for SMS reconstruction. + kspace, calib, kernel_size=(5, 5), prior: str = "sim", coil_axis: int = -2, + time_axis: int = -1, slice_axis: int = -1, lamda: float = 0.01, split: bool = False): + """(Split)-Slice-GRAPPA for SMS reconstruction. Parameters ---------- @@ -80,7 +80,7 @@ def slicegrappa( reduction technique for simultaneous multislice acquisitions." Magnetic resonance in medicine 72.1 (2014): 93-102. - ''' + """ # Make sure we know how to construct the sources: if prior not in ['sim', 'kspace']: @@ -169,7 +169,3 @@ def slicegrappa( # Return results in fixed order: (nx, ny, nc, nt, cs) return res - - -if __name__ == '__main__': - pass diff --git a/pygrappa/splitslicegrappa.py b/pygrappa/splitslicegrappa.py index b4c4fa5..d60de68 100644 --- a/pygrappa/splitslicegrappa.py +++ b/pygrappa/splitslicegrappa.py @@ -1,22 +1,18 @@ -'''Python implementation of Split-Slice-GRAPPA.''' +"""Python implementation of Split-Slice-GRAPPA.""" from pygrappa import slicegrappa def splitslicegrappa(*args, **kwargs): - '''Split-Slice-GRAPPA. + """Split-Slice-GRAPPA. Notes ----- This is an alias for pygrappa.slicegrappa(split=True). See pygrappa.slicegrappa() for more information. - ''' + """ # Make sure that the 'split' argument is set to True if 'split' not in kwargs or not kwargs['split']: kwargs['split'] = True return slicegrappa(*args, **kwargs) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/tests/test_vcgrappa.py b/pygrappa/tests/test_vcgrappa.py index f9ad84d..4514b49 100644 --- a/pygrappa/tests/test_vcgrappa.py +++ b/pygrappa/tests/test_vcgrappa.py @@ -1,15 +1,15 @@ -'''Unit tests for VC-GRAPPA.''' +"""Unit tests for VC-GRAPPA.""" import unittest from pygrappa import vcgrappa -from .helpers import make_base_test_case_2d +from pygrappa.tests.helpers import make_base_test_case_2d class TestVCGRAPPA(make_base_test_case_2d( vcgrappa, ssim_thresh=.90)): - # TODO: adjust/improve ssim_thesh; right now less than mdgrappa + # TODO: adjust/improve ssim_thresh; right now less than mdgrappa pass diff --git a/pygrappa/tgrappa.py b/pygrappa/tgrappa.py index f247711..1ae5271 100644 --- a/pygrappa/tgrappa.py +++ b/pygrappa/tgrappa.py @@ -1,16 +1,15 @@ -'''TGRAPPA implementation.''' +"""TGRAPPA implementation.""" import numpy as np from tqdm import tqdm -# from pygrappa import grappa -from pygrappa import cgrappa as grappa # need for speed! +from pygrappa import mdgrappa as grappa def tgrappa( kspace, calib_size=(20, 20), kernel_size=(5, 5), - coil_axis=-2, time_axis=-1): - '''Temporal GRAPPA. + coil_axis: int = -2, time_axis: int = -1): + """Temporal GRAPPA. Parameters ---------- @@ -56,7 +55,7 @@ def tgrappa( Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 53.4 (2005): 981-985. - ''' + """ # Move coil and time axes to a place we can find them kspace = np.moveaxis(kspace, (coil_axis, time_axis), (-2, -1)) @@ -74,7 +73,7 @@ def tgrappa( raise ValueError('Full ACS region cannot be found!') # To avoid running GRAPPA more than once on one time frame, - # we'll keep track of which frames have been reconstruced: + # we'll keep track of which frames have been reconstructed: completed_tframes = np.zeros(st, dtype=bool) # Initialize the progress bar @@ -148,7 +147,3 @@ def tgrappa( # Move axes back to where the user had them return np.moveaxis(res, (-1, -2), (time_axis, coil_axis)) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/ttgrappa.py b/pygrappa/ttgrappa.py index 21f3523..e284126 100644 --- a/pygrappa/ttgrappa.py +++ b/pygrappa/ttgrappa.py @@ -1,4 +1,4 @@ -'''Python implementation of through-time GRAPPA.''' +"""Python implementation of through-time GRAPPA.""" from time import time @@ -8,10 +8,10 @@ def ttgrappa( - kx, ky, kspace, cx, cy, calib, kernel_size=25, - kernel_radius=None, max_kernel_size=25, coil_axis=-1, - time_axis=-2, lamda=0.01): - '''Through-time GRAPPA. + kx, ky, kspace, cx, cy, calib, kernel_size: int = 25, + kernel_radius: float = None, max_kernel_size: int = 25, coil_axis: int = -1, + time_axis: int = -2, lamda: float = 0.01): + """Through-time GRAPPA. Parameters ---------- @@ -70,7 +70,7 @@ def ttgrappa( 2D/3D non‐Cartesian sampling trajectories with rapid calibration." Magnetic resonance in medicine 82.3 (2019): 1101-1112. - ''' + """ # Move da coils to da back and time_axis to the middle kspace = np.moveaxis(kspace, coil_axis, -1) @@ -165,7 +165,3 @@ def ttgrappa( # Fill in the known samples and return res[sampled, :] = kspace[sampled, :] return np.moveaxis(res, -1, coil_axis) - - -if __name__ == '__main__': - pass diff --git a/pygrappa/utils/cythoner.py b/pygrappa/utils/cythoner.py deleted file mode 100644 index d36a916..0000000 --- a/pygrappa/utils/cythoner.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 - -# from https://github.com/scipy/scipy/blob/main/scipy/_build_utils/cythoner.py - -""" Scipy variant of Cython command -Cython, as applied to single pyx file. -Expects two arguments, infile and outfile. -Other options passed through to cython command line parser. -""" - -import os -import os.path as op -import sys -import subprocess as sbp - - -def main(): - in_fname, out_fname = (op.abspath(p) for p in sys.argv[1:3]) - - sbp.run(['cython', '-3', '--fast-fail', - '--output-file', out_fname, - '--include-dir', os.getcwd()] + - sys.argv[3:] + [in_fname], - check=True) - - -if __name__ == '__main__': - main() diff --git a/pygrappa/utils/disjoint_csm.py b/pygrappa/utils/disjoint_csm.py index cffaafb..a8dae71 100644 --- a/pygrappa/utils/disjoint_csm.py +++ b/pygrappa/utils/disjoint_csm.py @@ -1,10 +1,10 @@ -'''Too-good-to-be-real coil sensitivity maps.''' +"""Too-good-to-be-real coil sensitivity maps.""" import numpy as np -def disjoint_csm(sx, sy, ncoil): - '''Make ncoil partitions of (sx, sy) box for coil sensitivities. +def disjoint_csm(sx: int, sy: int, ncoil: int): + """Make ncoil partitions of (sx, sy) box for coil sensitivities. Parameters ---------- @@ -17,7 +17,7 @@ def disjoint_csm(sx, sy, ncoil): ------- csm : array_like Simulated coil sensitivity maps. - ''' + """ blocks = np.ones((sx, sy)) blocks = np.array_split(blocks, ncoil, axis=0) @@ -28,7 +28,3 @@ def disjoint_csm(sx, sy, ncoil): csm[idx:idx+sh, :, ii] = blocks[ii] idx += sh return csm - - -if __name__ == '__main__': - pass diff --git a/pygrappa/utils/gaussian_csm.py b/pygrappa/utils/gaussian_csm.py index 538458a..9f1f701 100644 --- a/pygrappa/utils/gaussian_csm.py +++ b/pygrappa/utils/gaussian_csm.py @@ -1,11 +1,11 @@ -'''Simple coil sensitivity maps.''' +"""Simple coil sensitivity maps.""" import numpy as np from scipy.stats import multivariate_normal -def gaussian_csm(sx, sy, ncoil, sigma=1): - '''Make a 2D Gaussian walk in a circle for coil sensitivities. +def gaussian_csm(sx: int, sy: int, ncoil: int, sigma: float = 1.0): + """Make a 2D Gaussian walk in a circle for coil sensitivities. Parameters ---------- @@ -13,12 +13,14 @@ def gaussian_csm(sx, sy, ncoil, sigma=1): Height and width of coil images. ncoil : int Number of coils to be simulated. + sigma : float + Diagonal entries in covariance matrix. Returns ------- csm : array_like Simulated coil sensitivity maps. - ''' + """ X, Y = np.meshgrid( np.linspace(-1, 1, sy), np.linspace(-1, 1, sx)) @@ -29,7 +31,3 @@ def gaussian_csm(sx, sy, ncoil, sigma=1): mu = [np.cos(ii/ncoil*np.pi*2), np.sin(ii/ncoil*2*np.pi)] csm[..., ii] = multivariate_normal(mu, cov).pdf(pos) return csm + 1j*csm - - -if __name__ == '__main__': - pass diff --git a/pygrappa/utils/gridder.py b/pygrappa/utils/gridder.py index 835e28c..f41594a 100644 --- a/pygrappa/utils/gridder.py +++ b/pygrappa/utils/gridder.py @@ -1,18 +1,18 @@ -'''Simple gridding for non-Cartesian kspace.''' +"""Simple gridding for non-Cartesian kspace.""" import numpy as np from scipy.interpolate import griddata def gridder( - kx, ky, k, sx, sy, coil_axis=-1, ifft=True, os=2, - method='linear'): - '''Helper function to grid non-Cartesian data. + kx, ky, k, sx: int, sy: int, coil_axis: int = -1, ifft: bool = True, os: float = 2.0, + method: str = "linear"): + """Helper function to grid non-Cartesian data. Parameters ---------- kx, ky : array_like - 1D arrays of (kx, ky) coordinates cooresponding to + 1D arrays of (kx, ky) coordinates corresponding to measurements, k. k : array_like k-space measurements corresponding to spatial frequencies @@ -37,7 +37,7 @@ def gridder( If ifft=True. kspace : array_like, optional If ifft=False. - ''' + """ # Move coil data to the back k = np.moveaxis(k, coil_axis, -1) @@ -54,7 +54,3 @@ def gridder( np.fft.ifftshift(np.nan_to_num(grid_kspace), axes=(0, 1)), axes=(0, 1)), axes=(0, 1))[padx:-padx, pady:-pady, :] return grid_kspace - - -if __name__ == '__main__': - pass diff --git a/pygrappa/utils/meson.build b/pygrappa/utils/meson.build index 5218a58..3415723 100644 --- a/pygrappa/utils/meson.build +++ b/pygrappa/utils/meson.build @@ -1,6 +1,5 @@ py3.install_sources([ '__init__.py', - 'cythoner.py', 'disjoint_csm.py', 'gaussian_csm.py', 'gridder.py' diff --git a/pygrappa/vcgrappa.py b/pygrappa/vcgrappa.py index 9a4e8ee..7623a60 100644 --- a/pygrappa/vcgrappa.py +++ b/pygrappa/vcgrappa.py @@ -1,12 +1,12 @@ -'''Python implementation of VC-GRAPPA.''' +"""Python implementation of VC-GRAPPA.""" import numpy as np -from pygrappa import cgrappa as grappa +from pygrappa import mdgrappa as grappa -def vcgrappa(kspace, calib, *args, coil_axis=-1, **kwargs): - '''Virtual Coil GRAPPA. +def vcgrappa(kspace, calib, *args, coil_axis: int = -1, **kwargs): + """Virtual Coil GRAPPA. See pygrappa.grappa() for argument list. @@ -28,7 +28,7 @@ def vcgrappa(kspace, calib, *args, coil_axis=-1, **kwargs): Magnetic Resonance in Medicine: An Official Journal of the International Society for Magnetic Resonance in Medicine 61.1 (2009): 93-102. - ''' + """ # Move coil axis to end kspace = np.moveaxis(kspace, coil_axis, -1) @@ -39,9 +39,6 @@ def vcgrappa(kspace, calib, *args, coil_axis=-1, **kwargs): # to complex128 regardless of what we started with tipe = kspace.dtype - # We will return twice the number of coils we started with - nc = kspace.shape[-1] - # In and out of kspace to get conjugate coils vc_kspace = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift( kspace, axes=ax), axes=ax), axes=ax) @@ -60,9 +57,5 @@ def vcgrappa(kspace, calib, *args, coil_axis=-1, **kwargs): # Pass through to GRAPPA return grappa( - kspace, calib, coil_axis=-1, nc_desired=2*nc, + kspace, calib, coil_axis=-1, *args, **kwargs).astype(tipe) - - -if __name__ == '__main__': - pass diff --git a/pyproject.toml b/pyproject.toml index a74fd03..9628fbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,22 +4,16 @@ build-backend = "mesonpy" [project] name = "pygrappa" -version = "0.26.2" +version = "0.26.3" description = "GeneRalized Autocalibrating Partially Parallel Acquisitions." readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.9" keywords = ["mri", "grappa", "sense"] license = {text = "MIT"} classifiers = [ "Programming Language :: Python :: 3", ] -dependencies = [ - "numpy", - "scipy", - "scikit-image", - "tqdm", -] [project.optional-dependencies] test = ["pytest", "flake8", "phantominator"] -examples = ["matplotlib"] +examples = ["matplotlib", "sckikit-image", "tqdm", "phantominator"]