Skip to content

Add k-space basics tutorial for MRI reconstruction#2056

Open
ViSaReVe wants to merge 6 commits intoProject-MONAI:mainfrom
ViSaReVe:add-kspace-basics-tutorial
Open

Add k-space basics tutorial for MRI reconstruction#2056
ViSaReVe wants to merge 6 commits intoProject-MONAI:mainfrom
ViSaReVe:add-kspace-basics-tutorial

Conversation

@ViSaReVe
Copy link

@ViSaReVe ViSaReVe commented Feb 9, 2026

git push -u origin add-kspace-basics-tutorial
Adds an introductory notebook covering k-space fundamentals, Fourier transform connection, aliasing from undersampling, and MONAI's reconstruction transforms using the fastMRI knee single-coil dataset. Also adds the missing Reconstruction section to the main README.

Fixes # .

Description

A few sentences describing the changes proposed in this pull request.

Checks

  • Avoid including large-size files in the PR.
  • Clean up long text outputs from code cells in the notebook.
  • For security purposes, please check the contents and remove any sensitive info such as user names and private key.
  • Ensure (1) hyperlinks and markdown anchors are working (2) use relative paths for tutorial repo files (3) put figure and graphs in the ./figure folder
  • Notebook runs automatically ./runner.sh -t <path to .ipynb file>

Summary by CodeRabbit

  • New Features

    • Added an MRI Reconstruction tutorial series, including a K‑space basics notebook using fastMRI knee data and demos for U‑Net and VarNet reconstructions.
  • Documentation

    • Main README updated with a new "Reconstruction" section replacing a placeholder.
    • Added a tutorials README describing contents, prerequisites, dataset info, and links to related demos.
  • Chores

    • Test/runner configuration updated to include the new tutorial in the execution list.

git push -u origin add-kspace-basics-tutorial
Adds an introductory notebook covering k-space fundamentals, Fourier
transform connection, aliasing from undersampling, and MONAI's
reconstruction transforms using the fastMRI knee single-coil dataset.
Also adds the missing Reconstruction section to the main README.

Signed-off-by: Vidya Sagar <[email protected]>
@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

Walkthrough

Adds MRI reconstruction tutorial materials: a new k-space fundamentals Jupyter notebook, a tutorials README, an update to the project README adding a "Reconstruction" section, and the notebook added to the runner exemption list.

Changes

Cohort / File(s) Summary
Project README
README.md
Replaced a non‑English placeholder with a new Reconstruction section listing three MRI reconstruction tutorials: K‑Space Basics, U‑Net, and VarNet.
Tutorials README
reconstruction/MRI_reconstruction/tutorials/README.md
Added a folder README describing the MRI reconstruction tutorial series, dataset notes for fastMRI knee (single‑coil), prerequisites, and links to related demos.
Tutorial Notebook
reconstruction/MRI_reconstruction/tutorials/01_kspace_basics_fastmri_knee.ipynb
Added a comprehensive notebook covering k‑space fundamentals, undersampling/aliasing, FastMRIReader data loading, MONAI reconstruction transforms and pipelines (e.g., RandomKspaceMaskd, EquispacedKspaceMaskd), zero‑filled vs. ground‑truth reconstructions, visualizations, and cleanup.
Runner Config
runner.sh
Added the new notebook to the list of files exempted from max_epochs handling in the runner script.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through k-space, masks in tow,
FFTs hum soft in silver glow,
Pixels line up, aliasing hides,
U‑Nets learn where truth resides,
I nibble bugs and patch the code. 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete. While it mentions the tutorial and README updates, all checklist items remain unchecked, and specific details about file sizes, output cleanup, and test execution are not addressed. Complete the checklist by checking applicable items and providing explicit confirmation that the PR meets each requirement, particularly regarding large files, notebook outputs, hyperlinks, and runner.sh testing.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main contribution: adding a k-space basics tutorial for MRI reconstruction, which is reflected in the new notebook and README documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
reconstruction/MRI_reconstruction/tutorials/01_kspace_basics_fastmri_knee.ipynb (2)

458-461: Slice-selection heuristic for multi-slice output could silently pick the wrong slice.

The ndim == 3 check assumes the first axis is always the slice dimension, but if the MONAI transform returns output with a channel-first layout (e.g., (C, H, W)) or a batch dimension, indexing shape[0] // 2 would select from the wrong axis. This was partially addressed from the prior review, but the assumption that ndim == 3 ⟹ first axis is slices is fragile.

Consider adding a shape comment or assertion to make the assumption explicit, e.g.:

Suggested clarification
 rand_recon = as_numpy(random_result["kspace_masked_ifft"])
 if rand_recon.ndim == 3:
+    # First axis is the slice dimension (S, H, W) from FastMRIReader
     rand_recon = rand_recon[rand_recon.shape[0] // 2]

Also applies to: 477-479


542-545: np.asarray on a torch complex tensor may raise or silently lose the imaginary part.

complex_to_magnitude calls np.asarray(x), but if x is a torch.complex64 tensor on CPU, np.asarray can fail depending on the PyTorch/NumPy version (older combos don't support complex conversion). In this pipeline flow, the preceding Lambdad at line 570 already converts complex torch tensors to real via torch.abs, so this function should only ever see real data — making it a harmless safety net. A brief comment would clarify that intent and prevent future confusion.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@README.md`:
- Line 397: Remove the garbled terminal/encoding artifact "ECHO�� �����Ǿ�
�ֽ��ϴ�." found at the end of README.md (the text shown in the diff) so the file
ends cleanly; ensure you delete that line, trim any trailing blank lines or
whitespace, and save the README.md with UTF-8 encoding to prevent reintroducing
encoding artifacts.

In
`@reconstruction/MRI_reconstruction/tutorials/01_kspace_basics_fastmri_knee.ipynb`:
- Around line 150-159: Check that data_path exists and contains .h5 files before
using os.listdir and indexing sample_files: verify os.path.exists(data_path) and
os.path.isdir(data_path), catch FileNotFoundError from os.listdir (or test
existence first), build sample_files = [...] and then if not sample_files raise
a clear RuntimeError or ValueError explaining the dataset is missing and how to
download it, otherwise set sample_file = os.path.join(data_path,
sample_files[0]) and print as before; reference symbols: data_path, os.listdir,
sample_files, sample_file.
🧹 Nitpick comments (2)
reconstruction/MRI_reconstruction/tutorials/01_kspace_basics_fastmri_knee.ipynb (2)

462-466: Duplicate utility functions: as_numpy and to_numpy.

as_numpy (line 462) and to_numpy (line 561) are functionally identical — both convert a torch tensor or array to numpy. Consider defining one and reusing it.

Also applies to: 561-565


47-51: Minor: spaces in pip extras specifier.

"monai-weekly[pillow, tqdm]" has a space after the comma in the extras specifier. While modern pip handles this, it's unconventional and could cause issues with older pip versions. Consider "monai-weekly[pillow,tqdm]".

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@reconstruction/MRI_reconstruction/tutorials/01_kspace_basics_fastmri_knee.ipynb`:
- Around line 753-755: The cleanup currently removes root_dir even when a temp
directory was created because MONAI_DATA_DIRECTORY is unset; instead, fail early
when MONAI_DATA_DIRECTORY is not provided so users know to set it (or explicitly
opt into using a temp dir). Update the notebook logic around the
directory/root_dir creation (the variable directory and root_dir) to check the
MONAI_DATA_DIRECTORY env var and raise a clear error if it's None (with guidance
to set MONAI_DATA_DIRECTORY to the fastMRI data path), and remove the
unconditional shutil.rmtree(root_dir) cleanup for the fallback case; ensure the
error is raised before any downstream operations (the cells that previously
error out) so the user gets a clear prompt rather than the notebook silently
creating and later deleting a useless temp dir.
- Around line 456-478: The MONAI pipeline returns a full volume but the code
uses ground_truth[slice_idx] (single slice); to fix, extract the same slice_idx
from the MONAI outputs before squeezing: when building rand_recon, index
random_result["kspace_masked_ifft"] with [slice_idx] (or appropriate
channel/slice axis) then call as_numpy(...).squeeze() and take abs; do the same
for equi_recon (and any other recon variables) so their shapes match
ground_truth and imshow renders the correct middle slice; reference variables:
as_numpy, random_result["kspace_masked_ifft"], rand_recon, equi_recon,
ground_truth, slice_idx.
🧹 Nitpick comments (2)
reconstruction/MRI_reconstruction/tutorials/01_kspace_basics_fastmri_knee.ipynb (2)

549-553: Duplicate helper: to_numpy is identical to as_numpy (line 456).

Three near-identical tensor-to-numpy helpers are defined across the notebook (as_numpy, to_numpy, as_numpy_2d). Consider defining a single utility once (e.g., in the imports cell) and reusing it, adding the 2D squeeze variant as an optional parameter or a second thin wrapper.


212-212: Non-standard FFT shift ordering (works for even dimensions only).

The standard convention for MRI reconstruction is fftshift(ifft2(ifftshift(kspace))), but this line uses ifftshift(ifft2(fftshift(kspace))) — the shifts are swapped. For even-length dimensions (typical for fastMRI data), fftshift and ifftshift are equivalent, so results are correct. However, since this is a pedagogical tutorial on k-space fundamentals, using the standard ordering would avoid confusing learners who cross-reference with textbooks or other MRI reconstruction code.

Suggested fix
-image_from_kspace = np.fft.ifftshift(np.fft.ifft2(np.fft.fftshift(kspace_slice)))
+image_from_kspace = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift(kspace_slice)))

@@ -0,0 +1,779 @@
{
Copy link
Member

@ericspod ericspod Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line #1.    directory = os.environ.get("MONAI_DATA_DIRECTORY")

As was commented by coderabbit, this cell isn't needed since the directory the user will save the validation set to will already exist.


Reply via ReviewNB

@@ -0,0 +1,779 @@
{
Copy link
Member

@ericspod ericspod Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead this can be data_path = os.path.join(YOUR_DIR_HERE, "knee_singlecoil_val") to indicate the directory the user has downloaded to has to be put here.


Reply via ReviewNB

@@ -0,0 +1,779 @@
{
Copy link
Member

@ericspod ericspod Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line #1.    def as_numpy(x):

We have https://github.com/Project-MONAI/MONAI/blob/dev/monai/utils/type_conversion.py#L196 for this if you want.


Reply via ReviewNB

@@ -0,0 +1,779 @@
{
Copy link
Member

@ericspod ericspod Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line #16.    def ensure_channel_first_2d(x):

We have EnsureChannelFirstd transform for this.


Reply via ReviewNB

@@ -0,0 +1,779 @@
{
Copy link
Member

@ericspod ericspod Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated this cell isn't needed if the cell creating root_dir is removed.


Reply via ReviewNB

@ericspod
Copy link
Member

Hi @ViSaReVe thanks for the notebook, I think it looks good though I haven't run it myself and it can't as yet be automatically tested. Please look at the comments I made and coderabbit to update a few things. The errors that have come up from the CI jobs is related to this I think and we're looking into it.

ericspod and others added 4 commits February 14, 2026 05:32
- Replace custom as_numpy() with monai.utils.convert_data_type
- Replace custom ensure_channel_first_2d() with EnsureChannelFirstd
- Remove MONAI_DATA_DIRECTORY/tempfile pattern, use direct path
- Add error handling for missing data directory and .h5 files
- Fix Part 4 slice mismatch for multi-slice MONAI output
- Remove cleanup cell (no longer needed)

Signed-off-by: Vidya Sagar <[email protected]>
@ViSaReVe
Copy link
Author

Thanks for the review @ericspod! I've addressed all comments:

Removed MONAI_DATA_DIRECTORY / root_dir / cleanup cells — now uses data_path = os.path.join("YOUR_DIR_HERE", "knee_singlecoil_val") with error handling
Replaced custom as_numpy() with monai.utils.type_conversion.convert_data_type
Replaced custom ensure_channel_first_2d() with EnsureChannelFirstd(channel_dim="no_channel")
Fixed slice mismatch in Part 4 visualization for multi-slice output

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants