From e6ad3fa50f5da93e2626faf37108a2115918425d Mon Sep 17 00:00:00 2001 From: sairamgeethanath Date: Thu, 19 Mar 2026 07:44:36 -0400 Subject: [PATCH 1/6] added devops strategy - contributing.md --- contributing.md | 242 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 contributing.md diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..18edbfb --- /dev/null +++ b/contributing.md @@ -0,0 +1,242 @@ +# Contributing to Delta DIY MRI + +Welcome! 👋 +Thank you for contributing to the Delta DIY MRI ecosystem. + +This project spans multiple repositories and disciplines (hardware: magnet, gradients, RF; firmware and software:spectrometer, acquisition, reconstruction; mechanical designs of parts and phantoms, etc.), and this guide is designed to help **participants and mentors collaborate effectively** during the workshop and beyond. + +👉 Start here: [Step-by-Step Workshop Contribution Guide](#step-by-step-workshop-contribution-guide) +--- + +## Table of Contents + +- Getting Started +- Ways to Contribute +- Branching Strategy +- Development Workflow +- Pull Request Guidelines +- Code Review Process +- Workshop-Specific Guidelines +- After the Workshop +- Multi-Repository Coordination +- Commit Message Guidelines +- Code Style & Best Practices +- Getting Help + +--- + +## Getting Started + +1. Fork the repository (if external contributor) + +2. Clone your fork + git clone + cd + +3. Add upstream + git remote add upstream + +4. Sync with upstream + git fetch upstream + git checkout dev + git merge upstream/dev + +--- + +## Ways to Contribute + +- Hardware design (coils, magnet, electronics) +- RF systems and tuning +- Gradient design and optimization +- Reconstruction algorithms +- Software tools and GUIs +- Documentation and tutorials +- Testing and validation + +--- + +## Branching Strategy + +| Branch Type | Purpose | Stability | +|------------|--------|----------| +| main | Production-ready code | High | +| dev | Active development | Medium | +| feature/* | New features | Low | +| workshop/* | Rapid experiments | Very Low | +| hotfix/* | Urgent fixes | High | + +Core rules: +- No direct commits to main +- All changes via Pull Requests +- Branch from dev (unless hotfix) +- Keep PRs small and focused + +Naming conventions: + +Feature branches: +feature/- + +Workshop branches: +workshop/day1-rf +workshop/team-recon + +Hotfix branches: +hotfix/ + +--- + +## Development Workflow + +1. git checkout dev + git pull origin dev + +2. git checkout -b feature/ + +3. git add . + git commit -m "feat: short description" + +4. git push origin feature/ + +5. Open Pull Request to dev + +--- + +## Pull Request Guidelines + +Each PR should include: +- Description of the change +- Problem being solved +- How it was tested +- Screenshots / plots if applicable + +Checklist: +[ ] Code runs without errors +[ ] No broken dependencies +[ ] Documentation updated + +--- + +## Code Review Process + +- At least one mentor approval required - please refer to https://delta-diy-mri.github.io/ for the appropriate mentor +- Focus on correctness, clarity, reproducibility +- Discussion encouraged - please use GitHub discussions - https://github.com/delta-diy-mri/delta-diy-mri.github.io/discussions + +--- + +## Workshop-Specific Guidelines + +- Prioritize learning and speed of trying out implementations +- Use workshop branches for experiments +- Merge into dev daily +- Coordinate within teams + +--- + +## After the Workshop + +- Reduce workshop branches +- Enforce stricter reviews +- Add CI/testing +- Stabilize main + +--- + +## Multi-Repository Coordination + +- Use consistent branch names +- Reference related PRs: + +Depends on: repo-name#123 +Related to: repo-name#456 + +--- + +## Commit Message Guidelines + +Format: +: + +Types: +- feat +- fix +- docs +- refactor +- test + +--- + +## Code Style & Best Practices + +- Keep code modular and readable +- Comment non-obvious logic +- Prefer clarity over cleverness +- Avoid breaking existing functionality + +--- + +## Getting Help + +- Open a draft PR early +- Ask a mentor +- Use GitHub Issues +- Discuss with your team + +--- + +## Final Notes + +This project enables hands-on MRI system building and interdisciplinary collaboration. + +When in doubt: open a PR early and ask for feedback. + +Happy building! 🧲 + + + +## Overview +This repo uses submodules. Contributions may involve: +- Main repo +- Submodules + +## Workflow Summary +1. Fork repo +2. Clone with submodules +3. Create branch +4. Edit main or submodule +5. Commit, push +6. Open PR + +--- + +# Step-by-Step Workshop Contribution Guide + +## 1. Fork +Click Fork on GitHub + +## 2. Clone +git clone --recurse-submodules + +## 3. Branch +git checkout -b my-feature + +## 4A. Edit Main Repo +git add . +git commit -m "change" + +## 4B. Edit Submodule +cd submodule +git checkout -b change +git commit -m "update" +git push + +## Update pointer +cd .. +git add submodule +git commit -m "update pointer" + +## 5. Push +git push + +## 6. PR +Open PR on GitHub From bfe6c8b26cb7c10a55094653127212cc7364c6d8 Mon Sep 17 00:00:00 2001 From: sairamgeethanath Date: Thu, 19 Mar 2026 07:57:43 -0400 Subject: [PATCH 2/6] submodules updated in readme.md --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/README.md b/README.md index ca34989..2d4e16e 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,56 @@ openmrd validate my_scanner/scanner.yaml ## Status This is a **v0.1** proposal meant to bootstrap community discussion. PRs welcome! + +
+ +
+ + **Building: Repositories list** +1. Magnet: design and simulation +2. Magnet: construction +3. [Field mapping robot](https://github.com/imr-framework/mapping_robot) +4. [Passive shimming including 3D STL file generation](https://github.com/imr-framework/passive_shimming) +5. [Amplifiers: Gradient, RF TX and RF RX - 3rd party - Larry Wald/Martinos/MGH](https://tabletop.martinos.org/index.php?title=Main_Page) +6. [Planar gradient coil design using pycoilgen](https://github.com/sairamgeethanath/pyCoilGen) and [previous effort](https://github.com/imr-framework/planar_gradient_coil_design/tree/main) +7. RF coil: design and simulation +8. RF coil: construction +9. [Spectrometer - 3rd party - FLOCRA; part of console software download](https://github.com/vnegnev) +10. [Pulse sequence design using Pypulseq](https://github.com/imr-framework/pypulseq) +11. [Console software](https://github.com/sairamgeethanath/console) +12. Phantom +13. Scanner log and outputs + + + **Playing: Repositories list** + 1. [Virtual Scanner Tabletop Games](https://github.com/imr-framework/vs-tabletop/tree/delta-diy) + 2. [Virtual Scanner](https://github.com/imr-framework/virtual-scanner/) + +## Our image currently looks like .... + + + + + + +## Potential student projects + +***Hardware** +1. Field mapping robot - Design covers, wiring and a user interface +2. Passive shims - explore new geometries and new optimization methods +3. RF coils - Bat head coil matching the anatomy of its head + +***MR physics*** +1. Pulse sequences - Robust RF frequency finder, refine calibration sequences - RF power, gradient +2. Image reconstruction - Multiple k-space filters + +***Software*** +1. Console - automate install and startup +2. Interface virtual scanner games with the console using the Pulseq format + +***New experiments*** +1. T1 mapping +2. T2 mapping + +**Acknowledgement**: +1. Johns Hopkins Provost DELTA award, 2024 From 31277e9dec5b63b4e531001527421a6188d59f1f Mon Sep 17 00:00:00 2001 From: sairamgeethanath Date: Thu, 19 Mar 2026 08:31:38 -0400 Subject: [PATCH 3/6] setup submodules and test scanner manifest --- .gitmodules | 16 ++++++++++++++++ README.md | 6 +++--- contributing.md | 6 +++++- src/console | 1 + src/mapping_robot | 1 + src/openmrd/demo_openmri_Ctype.py | 12 +++++++++--- src/passive_shimming | 1 + src/pyCoilGen | 1 + 8 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 .gitmodules create mode 160000 src/console create mode 160000 src/mapping_robot create mode 160000 src/passive_shimming create mode 160000 src/pyCoilGen diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d6ff90e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,16 @@ +[submodule "src/mapping_robot"] + path = src/mapping_robot + url = https://github.com/imr-framework/mapping_robot/ + branch = dev_ws_2026 +[submodule "src/passive_shimming"] + path = src/passive_shimming + url = https://github.com/imr-framework/passive_shimming + branch = dev_ws_2026 +[submodule "src/pyCoilGen"] + path = src/pyCoilGen + url = https://github.com/sairamgeethanath/pyCoilGen + branch = dev_ws_2026 +[submodule "src/console"] + path = src/console + url = https://github.com/sairamgeethanath/console + branch = dev_ws_2026 diff --git a/README.md b/README.md index 2d4e16e..bd91f61 100644 --- a/README.md +++ b/README.md @@ -32,13 +32,13 @@ This is a **v0.1** proposal meant to bootstrap community discussion. PRs welcome - **Building: Repositories list** + **Building: Repositories list - please use the dev_ws_2026 branch for all submodules** 1. Magnet: design and simulation 2. Magnet: construction -3. [Field mapping robot](https://github.com/imr-framework/mapping_robot) +3. [Field mapping robot](https://github.com/imr-framework/mapping_robot/) 4. [Passive shimming including 3D STL file generation](https://github.com/imr-framework/passive_shimming) 5. [Amplifiers: Gradient, RF TX and RF RX - 3rd party - Larry Wald/Martinos/MGH](https://tabletop.martinos.org/index.php?title=Main_Page) -6. [Planar gradient coil design using pycoilgen](https://github.com/sairamgeethanath/pyCoilGen) and [previous effort](https://github.com/imr-framework/planar_gradient_coil_design/tree/main) +6. [Planar gradient coil design using pycoilgen](https://github.com/sairamgeethanath/pyCoilGen) or [previous effort](https://github.com/imr-framework/planar_gradient_coil_design/tree/main) 7. RF coil: design and simulation 8. RF coil: construction 9. [Spectrometer - 3rd party - FLOCRA; part of console software download](https://github.com/vnegnev) diff --git a/contributing.md b/contributing.md index 18edbfb..7cdb268 100644 --- a/contributing.md +++ b/contributing.md @@ -212,10 +212,14 @@ This repo uses submodules. Contributions may involve: # Step-by-Step Workshop Contribution Guide ## 1. Fork -Click Fork on GitHub +Click Fork on GitHub - https://github.com/imr-framework/open-mri/ ## 2. Clone +```bash git clone --recurse-submodules +cd +git checkout dev_ws_2026 +``` ## 3. Branch git checkout -b my-feature diff --git a/src/console b/src/console new file mode 160000 index 0000000..109cc05 --- /dev/null +++ b/src/console @@ -0,0 +1 @@ +Subproject commit 109cc0573d9921ac857c5d5925f81f3825b2a71f diff --git a/src/mapping_robot b/src/mapping_robot new file mode 160000 index 0000000..8042276 --- /dev/null +++ b/src/mapping_robot @@ -0,0 +1 @@ +Subproject commit 8042276d0d413d595e6eda678ddb3843a4a0f9fa diff --git a/src/openmrd/demo_openmri_Ctype.py b/src/openmrd/demo_openmri_Ctype.py index bb894a5..e5d6202 100644 --- a/src/openmrd/demo_openmri_Ctype.py +++ b/src/openmrd/demo_openmri_Ctype.py @@ -18,7 +18,7 @@ import argparse, os, sys, json, yaml sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -from src.openmrd.models import ReconstructionManifest, ScannerManifest, GradAxis, Gradients, RFChannel, RF, Magnet, Spectrometer, Console, Subsystems, Metadata +from src.openmrd.models import * TEMPLATES = { "c_type_permanent": { @@ -30,8 +30,14 @@ "y":{"gmax_mTm":25,"slew_Tm_s":35,"resistance_ohm":2.5,"inductance_mH":5.2}, "z":{"gmax_mTm":20,"slew_Tm_s":30,"resistance_ohm":3.0,"inductance_mH":6.1} }}, - "rf": {"tx":{"type":"solenoid","f0_hz":14.9e6,"q_factor":100,"impedance_ohm":50}, - "rx":{"type":"solenoid","f0_hz":14.9e6,"q_factor":150,"impedance_ohm":50}}, + "rf": {"tx":{"type":"solenoid","f0_hz":11.5e6,"q_factor":100,"impedance_ohm":50}, + "rx":{"type":"solenoid","f0_hz":11.5e6,"q_factor":150,"impedance_ohm":50}, + "shielding":"copper"}, + "recon_pipeline": { + "openmrd_version": "0.1", + "metadata": {"name":"C-Type-OpenMRD","organization":"DIY MRI","license":"MIT","contributors":["Researcher1", "Engineer1"],"created":"2025-08-27","description":"C-type permanent magnet design with N52 discs"}, + "reconstruction_parameters": {"algorithm":"GRAPPA","acceleration_factor":2} + }, "spectrometer":{"model":"RedPitaya-122","sampling_rate_hz":122e6,"bit_depth":16,"max_tx_freq_hz":50e6}, "console":{"name":"mri4all","os":"Linux","api":"Python gRPC","pulseq_support":True,"latency_ms":3.0} } diff --git a/src/passive_shimming b/src/passive_shimming new file mode 160000 index 0000000..71cbef2 --- /dev/null +++ b/src/passive_shimming @@ -0,0 +1 @@ +Subproject commit 71cbef2e11c1f86527256e3c8eb29a5813e5b916 diff --git a/src/pyCoilGen b/src/pyCoilGen new file mode 160000 index 0000000..8288115 --- /dev/null +++ b/src/pyCoilGen @@ -0,0 +1 @@ +Subproject commit 82881151fc808b4a6e1f54449becf19bd2d33f11 From 3b410c4a2ebb185ef096284056e4f30ffd5dfb29 Mon Sep 17 00:00:00 2001 From: sairamgeethanath Date: Thu, 19 Mar 2026 08:34:03 -0400 Subject: [PATCH 4/6] change package name --- {default_package => diy_mri_2026_package}/scanner.yaml | 4 ++-- src/openmrd/demo_openmri_Ctype.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename {default_package => diy_mri_2026_package}/scanner.yaml (98%) diff --git a/default_package/scanner.yaml b/diy_mri_2026_package/scanner.yaml similarity index 98% rename from default_package/scanner.yaml rename to diy_mri_2026_package/scanner.yaml index 85dc733..373dfd2 100644 --- a/default_package/scanner.yaml +++ b/diy_mri_2026_package/scanner.yaml @@ -85,7 +85,7 @@ subsystems: rf: tx: type: solenoid - f0_hz: 14900000.0 + f0_hz: 11500000.0 q_factor: 100.0 impedance_ohm: 50.0 pmax_w: null @@ -94,7 +94,7 @@ subsystems: files: {} rx: type: solenoid - f0_hz: 14900000.0 + f0_hz: 11500000.0 q_factor: 150.0 impedance_ohm: 50.0 pmax_w: null diff --git a/src/openmrd/demo_openmri_Ctype.py b/src/openmrd/demo_openmri_Ctype.py index e5d6202..a684d46 100644 --- a/src/openmrd/demo_openmri_Ctype.py +++ b/src/openmrd/demo_openmri_Ctype.py @@ -129,7 +129,7 @@ def main(): """ # Initialize with default arguments class Args: - package = "default_package" + package = "diy_mri_2026_package" template = "c_type_permanent" manifest = os.path.join(package, "scanner.yaml") func = None From 945adf507bf893b6bf80bc9ac1cfd070d4cd24cd Mon Sep 17 00:00:00 2001 From: ajaynice1996 Date: Thu, 19 Mar 2026 11:35:52 -0400 Subject: [PATCH 5/6] change --- src/openmrd/reconstruction.py | 135 ++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/openmrd/reconstruction.py diff --git a/src/openmrd/reconstruction.py b/src/openmrd/reconstruction.py new file mode 100644 index 0000000..0cd9abe --- /dev/null +++ b/src/openmrd/reconstruction.py @@ -0,0 +1,135 @@ +from pydantic import BaseModel, Field +from typing import List, Optional, Dict, Union + +# ========================================================== +# 1️⃣ Acquisition / Contrast Parameters +# ========================================================== +class ContrastParameters(BaseModel): + name: str # T1, T2, FLAIR, Diffusion, etc. + echo_time_ms: Optional[float] = None + repetition_time_ms: Optional[float] = None + flip_angle_deg: Optional[float] = None + b_value: Optional[float] = None # For diffusion + diffusion_directions: Optional[int] = None + orientation: List[str] = Field(default_factory=lambda: ["axial", "coronal", "sagittal"]) + notes: Optional[str] = None # Acquisition-specific info + +# ========================================================== +# 2️⃣ K-space / Reconstruction Parameters (Traditional) +# ========================================================== +class KSpaceReconstruction(BaseModel): + algorithm: str = "FFT" # FFT, NUFFT, SENSE, GRAPPA, etc. + filtering: Optional[str] = None # Hamming, Gaussian, etc. + density_compensation: Optional[bool] = False + oversampling_factor: Optional[float] = 1.0 + orientation_correction: Optional[bool] = True # Rotate to canonical axes + comments: Optional[str] = "Traditional k-space reconstruction settings" + +# ========================================================== +# 3️⃣ Classical Denoising / Preprocessing +# ========================================================== +class ClassicalDenoising(BaseModel): + method: str # Gaussian, Median, NLM, BM3D + parameters: Dict[str, float] = Field(default_factory=dict) + apply_before_reconstruction: Optional[bool] = True + comments: Optional[str] = "Optional classical denoising step" + +# ========================================================== +# 3.1 Motion Correction (Optional) +# ========================================================== +class MotionCorrection(BaseModel): + method: str # e.g., rigid, affine, non-rigid + parameters: Dict[str, float] = Field(default_factory=dict) + apply_before_reconstruction: Optional[bool] = True + comments: Optional[str] = "Optional motion correction step" + +# ========================================================== +# 3.2 Super-resolution (Optional) +# ========================================================== +class SuperResolution(BaseModel): + method: str # e.g., interpolation, DL-based + scale_factor: Optional[float] = 2.0 + comments: Optional[str] = "Optional super-resolution enhancement" + +# ========================================================== +# 4️⃣ Deep Learning Based Reconstruction +# ========================================================== +class DLReconstruction(BaseModel): + model_name: str # nnUNet, UNet, SwinUNet + checkpoint_path: Optional[str] = None + input_type: str = "k-space" # raw or magnitude + output_type: str = "image" + normalization: Optional[str] = "z-score" + augmentation: Optional[Dict[str, Union[bool, float]]] = None + domain_adaptation: Optional[bool] = False + comments: Optional[str] = "DL-based reconstruction and denoising" + +# ========================================================== +# 5️⃣ Post-processing / Visualization +# ========================================================== +class Visualization(BaseModel): + views: List[str] = Field(default_factory=lambda: ["axial", "coronal", "sagittal"]) + fusion: Optional[bool] = False # Combine multiple contrasts + overlay_masks: Optional[bool] = False # Segmentation overlay + windowing: Optional[Dict[str, float]] = None # Window/level + colormap: Optional[str] = "gray" + comments: Optional[str] = "Visualization and multi-orientation display" + +# ========================================================== +# 6️⃣ Evaluation / Metrics +# ========================================================== +class Evaluation(BaseModel): + reference: Optional[str] = None # Ground truth / baseline + metrics: List[str] = Field(default_factory=lambda: ["SSIM", "PSNR", "Dice"]) + segmentation: Optional[Dict[str, str]] = None # Mask files for evaluation + comments: Optional[str] = "Evaluation of image quality and/or segmentation" + +# ========================================================== +# 7️⃣ High-level MRI Reconstruction Pipeline (Updated) +# ========================================================== +class MRIPipelineExtended(BaseModel): + contrast_settings: List[ContrastParameters] + # Traditional reconstruction steps + kspace_recon: Optional[KSpaceReconstruction] = None + classical_denoising: Optional[ClassicalDenoising] = None + motion_correction: Optional[MotionCorrection] = None + super_resolution: Optional[SuperResolution] = None + # DL-based steps + dl_reconstruction: Optional[DLReconstruction] = None + # Visualization & evaluation + visualization: Optional[Visualization] = None + evaluation: Optional[Evaluation] = None + notes: Optional[str] = "High-level MRI pipeline supporting both traditional and DL workflows" + +# ========================================================== +# Example: Creating an extended pipeline +# ========================================================== +example_pipeline_extended = MRIPipelineExtended( + contrast_settings=[ + ContrastParameters(name="T1", echo_time_ms=10, repetition_time_ms=500), + ContrastParameters(name="T2", echo_time_ms=80, repetition_time_ms=3000), + ContrastParameters(name="FLAIR", echo_time_ms=120, repetition_time_ms=9000) + ], + kspace_recon=KSpaceReconstruction(algorithm="FFT", filtering="Hamming"), + classical_denoising=ClassicalDenoising(method="Gaussian", parameters={"sigma": 0.5}), + motion_correction=MotionCorrection(method="rigid"), + super_resolution=SuperResolution(method="DL-based", scale_factor=2.0), + dl_reconstruction=DLReconstruction(model_name="nnUNet", checkpoint_path="/path/to/checkpoint.pth", domain_adaptation=True), + visualization=Visualization(fusion=True, overlay_masks=True), + evaluation=Evaluation(reference="/path/to/ref.nii", metrics=["SSIM", "PSNR", "Dice"]) +) + + +# ✅ Key Updates / Optional Steps + +# MotionCorrection – optional, can be applied before reconstruction. + +# SuperResolution – optional, either classical interpolation or DL-based. + +# DLReconstruction – optional, can replace or complement classical reconstruction. + +# ClassicalDenoising – optional, can apply pre- or post-reconstruction. + +# All steps optional – pipeline is flexible: you can run only traditional steps, only DL, or hybrid. + +# Metadata / Comments – every step has comments field for reproducibility and documentation. \ No newline at end of file From e94fd3338f5cd4ef77d0ff41049388651aa40534 Mon Sep 17 00:00:00 2001 From: ajaynice1996 Date: Thu, 19 Mar 2026 14:31:20 -0400 Subject: [PATCH 6/6] update recon --- .../__pycache__/__init__.cpython-310.pyc | Bin 209 -> 167 bytes .../__pycache__/models.cpython-310.pyc | Bin 5030 -> 8973 bytes .../reconstruction.cpython-310.pyc | Bin 0 -> 4467 bytes src/openmrd/demo_openmri.py | 2 +- src/openmrd/demo_openmri_Ctype.py | 8 +- src/openmrd/models.py | 114 +++++++++++++-- src/openmrd/reconstruction.py | 135 ------------------ 7 files changed, 112 insertions(+), 147 deletions(-) create mode 100644 src/openmrd/__pycache__/reconstruction.cpython-310.pyc delete mode 100644 src/openmrd/reconstruction.py diff --git a/src/openmrd/__pycache__/__init__.cpython-310.pyc b/src/openmrd/__pycache__/__init__.cpython-310.pyc index 2c7657af655f737bc2c1923bc87fbf4eb63e6054..deeb56cbc5b69508b0320fa16a9a19c0765f81af 100644 GIT binary patch delta 43 xcmcb}xSWwEpO=@50SG3D?a7?T6CiA=pOK%Ns-KvZSgG%lpIn-onm2K#1pxXu4I%&l delta 85 zcmZ3^c#)AOpO=@50SFFkSf4SGC%`sdKeRZts93)^F|#N!H$64ABqK2|u_Qy^B|o_| nH#M)MSl>B6ucRn5sk9_BKd)FnBtJi=SU;dBKPxqPV!j0c(%K%q diff --git a/src/openmrd/__pycache__/models.cpython-310.pyc b/src/openmrd/__pycache__/models.cpython-310.pyc index c3129d276f2173d322c9b30f8fc1618d47b38e5c..673fec30b913ebf9497a09032e52dd6b982e7d35 100644 GIT binary patch literal 8973 zcmbVSTXP%9b;fNl00@wvNNGiBC1Kaz*pNa|j$KJAwb^JbMY|hI%!cGT36k=7h-rdj z&0wIOfh00Fr`D;;#ZH{7lh`h&{E({s4asZ%M8D?AZ>ichKjr&Q<3?Omaj2R`pYFN! zIo~CgUW}?ox%YBGDR91(3mldIxY(&dE(b1TSL8Lwm7of_Dsmli zEm(uRCUOIEJ!n8~h`bJYJ=lP}A@T;~X0Qo)Q{*P(OTiZ8Es-}NZwHqlUl#ciyU2;cOc&p`3B^l4|XB%ihMJ; zdzfqQ{e(7Z7ftoceyW}%fr^atC{$5kss~Au$w<3oN{_=dGnL;QWMR_xan475bWG`C z*v;B`lfT{NeP;iP5bonh0fwB)b@D;3Q}`&SimDXk-?qPnw{t-eZ&5PN`$?SiOmQ%X zO<5zu!+XpKq9Y-X}SI z2~U>hr(fJVO=7j@f5Si9dzf^GvFc~(UNTVq-B^cvsqTsp>tGL0QBit#aHfgpGFQN+ zmaF9Jc^O++D(CO>#BZZnvQ!64#!^)ozeOy$)NxfsRd;fb9gNr1T2TH@zEf8XsK+@e z$c!s9>oT*UnyM(Hm6`K5<@`&yMrL{4=-MitQiEdGO!#NL^va^9QrQ>^EX0AXP zNl*1vn4NjhxMvlv;g~tIot}oVm*_xgFC7jBiOy8;N#1M(>ck&LnRnuMvqYbnN>4$j z8G6;&&y)`RC^gPW7^!r>y`>wtnO6?5%z;&*UItxrs#*-xZNa)!hUZb26`4` zUKGY*mU>5Cs&}v;(N}wY&KlMRVvW!9B-%jJPj#CO4WV0XMBB83RsJ%SzlmAu{({P> z^k)9eyyom`l|RJn5|ukpa(SdIsvh1F6rswh;!KT79?gm&pvtnTQk5ay+N_-C=!Miu+*wRv60xfJ1_XJ_7JnNj6Pqb7CEjGw99_NWKg8_PMO&OPSP*CY zwKxOHlrzDZ1Dv@doFPO2ht-KZ4i3bf4JPX|$|CL@!Cm#>(SuV&OBLzMcpG@_J@53~ zRGy8s)(GQ)3dX?OH{}80`NA|(Z{TY`PCfd$X~6Njryk7Qh!VYH^G(~{j$C0iu z|0vQ2MTzWV@0+G->V^K}8utMxMgj*YBq_r1AUrbbgJG0n&7+jLZ)y>=)9;>nak>bL z>C*W^NXrP~`dxG_E<_*8aPg}+h44w4#x@P2vG=a~wt>Ft1XsxZ4Lc?wr0BdAk`O&jJCVi44nW*;&kGeD=Bd}`m+RrfUUvJiDs zoSI8o4OA8qI+L;WlPDaZT48Y;6LgeND%l;;MaM(k%TwV;|Y%=B;J zd7l7YMdSD{!T-rdwbX}|)bE8^=123cHUWx0#KpgZLm#0zzrMJfIgG3&n*7ej4+oW= zO52;ObINb?){TndR)I(6%8XTTp&o(e{LT^iI3Maqt*5&wU#5l0nYU@BD$DR>R6ls4 zl;n$*N`QyYc)Ow~Ry|?w%WU|HwxT&@FJ!aKg2$Nn8jh4zX)RZu*bmm@+)C?x$`~`X z-Z%t+N|l2G+?5oesH&ry#8&vI=||3O(`ds|EVwY!XazQ^!i6J0O~bAqJyiW9OrZ-? zhAW>Yfeni_A=~2HN9MXe7({2@u|ix_-bGcOzcJpKX?4fCqEstlWR~hl*{pz%GGoYD5Lo#AzB_xAfzKq zSOUrSInsQM^IMaJ#&UjZin*C(>V(%W1Hm&+@EEY6wDQf;U&QR9*%7&?mA^IiwDK2Y z!?>>5I2;a?K2YF^LoQ5~GxDa~eN#uqL-e(7bpF<4Rp5_G@1pP@UDi^4RBByC_pl~x zS62tUl#7}My_J@t2L1rYq`nY4adbf&yxV=A`qyYyYQr)|s|{4AfVqV|9b*J}$%pnl zvP7E?QFmZ}q@mX7JUk>H+OW2Ptk)2kVXe`bX2Eq{4=rz)=EKKxA+9D>ifkKVPo#UQ zx=#lQwwF-2W~b-wC={7m*dGitPj4>2l=1?8p~Z7P+L`o zncUHK#hBK*lD!* z>O^Uj)N0|qF4&#*PSKPO4?lQfN*^BXAIMh<^=c&i5DT(M*qT0b4hAk#vkto{Vz-*c zfb+fS1+CH27Yk!E~k-oh``QBGcpD&Sl6~L}tKuJFh{$Dl#L0+u4D9O=Kp3 zjm~w*mNz$L{&mQfCvS>;Vc)qEmsyjrH zfv-c7gVP~u(VBXRJuzx6ZMWtf;5)c}hUq_`$jV&*4I6O))>xLI{vCVbEywKrJsZ+Y z{SRz}{QnQMGPP2x?ZIKa9oT6tsHOFwZqryTX{1izq|Kn2wt`mb2Ci*AwnSYt z{%VPaw7;$g9tz)i+Ja7(zrF5@<^CwyR^aR<09I=~&qUEr?h0rwd9fcs(qJYd`h z-V#@UuP`0}UlrTH+l;q>-x52(JB+UYzb&o-Ut@d~_`0|Oe1q{e@H^rr@J+^ViFXgJ z;jM3pn_=B}pF~>j=RzjNc@)b;81Ep@OCA|EOyl!dm&W~~DC0bfFz0E8j%hrM$K{wF zvHVvD*u$l7p^;W#3oEESwPam3gnhUAt=$Ek_<)9Sp5$qMYU)Linx@L*Fil58$GBmb zMX3x!!72kfw2bCoIj*8M#GfGA=Jd_$I7eqYyQg+16)q8suQMPNW_Uuc{6nrn=Ph=&^NODm5V3r-ryyZ&O3$ znR*O2k+w`jKz8Z|SzN{m)T!PfOukSzscBI&*Fn|0LSt9a(4#fIiCjki2KEhm$Ntb} z>m}#SlSYDD);N!pP0piVIFElDpchF54sEehn!v5V1ztGGlfLla)VA!%7953{Q|I_J zo1No6R*|@W9&3=9M(1IAlp0Sb@@06GhT8O$)L4C#jb)ghrKTHaVlu99`Xs1B_NFz4 zrsM2%1P47AWthZiTODmh&rhBBw2hSd1_@xfXb%vh{~8^ z-n370l}=@98qv92qGB{`uX4fbW-fT4^u?PY1+SGI_>5Ugnqnj7~7g*OW zy3!dX3(^_=Ryu>qq%+r<1D&;|Izx(p4!t>^B?r=7l}vg|WRdP1)}43o=mAn?CKI)d zuR+w_3C~^`_xX&qZk!fU%!D^G&4T3l(sXrLM98%|q&PQS1pfFeM1Faht7l7s>u)BR z!+#fVtyFi1dLL2qMymS>?)V`tP12hMdZjvc0ogaZ;1{s2bpdp@L>O;mOSzidK^vQ6 zY6p)@tvok1bv{DGp3$y~ARPLu>!$Ch(jMn57ShjXY!PznboDW&e28nVt8IJRRy4YK zQJ=%Qc2TcwJ~Lf$P&ph_2q@WJS~h1hEYmfe!vY(nqBJj+ad>Mq10AIWrJPV9?UQc2 zi4pNe)y=EZ-q#d$cVu}Q| zhf8m8|1{H!I%oD-^ewh(;)UX;Wm+gJbdCV1<=WJvB0e&`Vv=aAdAenejGus=Y z`m!t*OTUK?!nTYgu6~7Xr3>a`oi2WfDYQ0Cg>bdv4k?4iH?KE?b&WDscQzzf&N7fW z?{X+j1j^)jcA;$3^ffhn!~`iG6D)<(m@w>E~kz*}wh|2M-5gA6Gb8~1J!rN<(=_XT|jeUWRuy1u%s+p}4m zApchtk%EtKl3{&WaXC7>Cycu+Xdo+Vp zz_Anb=W}SkrdVp5odXF0pbkd$2MO#Wj!vX5zpLN5{gLU@F$O1>@CD97baHc%uB*t~ zLUA}%Q>a-^=DK?AQPp-%>0OUZtHeW**o~{@c%l$o#v7l>@iRS1wejUS5*och4+pEc z`~XZXIfY_{X;nwm<682!)Z=aMchvhmH5{OSpx%m}NJ{EUG{3?1A40xGvDo8y93bZ* z>ydr4-;QBjL?mZ4oKQt=scxskSPh<=)$tUe9_p^@P}UI0`0z%9yoV~j!EsuH*Lvua zw`v@-{^8`fVpp4H%Ezsd=@yaJ@e3K!aY~zhRmv|0ywiETT2m*cqnCGD6!q2qf)fC` zVG$P+DNqhC)m3!hsp^ZbsK@j&+kYZRg(z1Iydm-~p@=YMZY|}1gq5y&5~Mo5Iz~}- zrO39K^8YhbIvfU7s8DEBxC)gXaGyq&LS-P(x@m+zH&`0_ArZj}>LmD!Px# diff --git a/src/openmrd/__pycache__/reconstruction.cpython-310.pyc b/src/openmrd/__pycache__/reconstruction.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45d45885dc42ce000d26684cafe4edc66a461e18 GIT binary patch literal 4467 zcmaJ^%W~vM6;)M9^-w+iXiw7(j3y>}plIBN;1xjAW2TK~Ca_J9?Vg1;8o4s7tg%!Q znW}DFDBNRqCE|ADOn>u7$sUKKm;A zV%xI*Md0S24X}?(e+45fQHrgk4NUoH_Y$CbqP?Zok&r0Q3bnqMnfA6ud#>_1z= zmZd*c{5r5Bs=!so4d9xn1J@b5zzyL7yNsK_P0<2wF>V33MF+UUxDDJDJ>VYW4)BK9 z1m0xa1>O?dz}t*_;@+9n-}yJb!0MMxSBw+>A%bgbgH*AP>pC1oc^<}ZECuP^ zF#rAK-esD|z3|KMdhZ||OcFWH^wJ3rW?S8SjFpg5GaZ=<-izc7^h*bn)IMWrcH+SYAO+U zBXy|N2IT8z8EO~1WZPok`_D7SxmmW2a(dW$Yo zIXpZ!jW8a;-T7s5Vj9CJhG3)d=n#sNV;$w!!5~dQy=G^QuyOqT=R>oVzJjvDB#SY7 zM%=mCxj`Hjs+m%!kIz*oSn6TyUF~YxjCWf+o|buzcWG0{FS6$P9raE8n)()*ZNJjEq!laV01~} zJo8B89Z^P>sL48>HHKx*6qJK(kk*{#6v{bOkkp)I4&)e72T2WNjyTjh8ietI9H)_n zGfWj30D;)my46q}3Kj(_JF@1OOr3ugXma4n)o$80Q_UlyeV+^ojenc|}t=*aaDMnX$ zU#U1YLUvvkm~F6IGu^XECe^9bX*{71xJq#YMbF>z~|`U&Qdi!|l6BAtOJdb;8blEFSM{a2W^-YBYKq5T?1 zNTGX*3|p|~eX7I7yar;Rj2Nj>RLGWW%ML{kT^vJ2V(VhO&6pBf+h11S4y}imyQX(= zbSKHVs1!2h%5h@4gG)KM%2K5IAPe)$X>FE_j2n%!Ngm|anKVw6gd>S6G|A`BrnTv= zhP0$$Eagjw-y`smDw(W;rrM%@HKfa!>X+j;XaToDEmO|(NDi*w#q;7l)NHjt z_Z+u@DP?v{`}0Vn_|M2#+3vg_QeA9U^~l`G0SvjK?_l&}n(+?Ibm!KK7tKnMhn-iE ze60^nwdlx9n`&VkUI$62uQbZ&>u4-en31tZDnwH>NMlk>Hhp~Sy&g#*LFW_g-YA@& zh;)Y6t&v29Hu-BZ+@dUl{Uds|Ov}#+9*|jP?n8oWC9#iDnlY=0l&yQE`-L26Y{kC| zIedZ9r5xrB4ah`AL=Rn?ok$$WqYfG!9w9lD5cM{q-jU8M+N-nd(4v-z=aC%H$M#+0 z{)W0?4mEgL$)Qvdxo~A)w|MV!|7)L}eR^!{7iTA@e7=zXUkPXUph5{-%ip{sgBohH ziUu0FTP}0JbZ@!9ONSok5|3I`G*d+QUaHXREEg0sx9>*&C$s}*bICAMe?$XJ{W0A! zxoasUMSq4%yD+ZRLkYf5C77PCxQ$-Fk4yg@W=SWdY!Knhj#t#5))^Pas|GN|chRqw z5#Kd#bsSL~uc#mG@OW{&>H_x|Q~a0x4d6}2)X$dvE#Pg&MIN{Zyu+9eT@XUc?A%WB z$Kf~{N}M4#a2(;oS1|7;aq7C_cC%=a%+{hI!U1dEWo;C_!;OyB^tkY1(Y#(Gwa0hb zEYp5F622q_#_%#?#A+!jJ@O2PrWS4BwA(Z;k{X^sMGZ>()`lHb{Cy7rGtYb z?{%uKa87xx`TWs;=dLuEe5%w%7yN?mSSjz*9nsA#mO7<7YH1Y9naa63Cqubiy(F_w z=5sQf`}n-Wdb^V+IYpjeo{vk@Aa~5=*|DeG!yRNrGfwEJmR*Z*oJRvip~$ByQ$=mX zR~ORd?Xq-hG=V>ccTc@xv1i^c?{uG@@&4%jcLSHUhGS=OAbeH=(v-`47BZL9YxkPH L2L9`Gx83+3a&+c| literal 0 HcmV?d00001 diff --git a/src/openmrd/demo_openmri.py b/src/openmrd/demo_openmri.py index fb443fb..7aa3350 100644 --- a/src/openmrd/demo_openmri.py +++ b/src/openmrd/demo_openmri.py @@ -3,7 +3,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -from src.openmrd.models import ScannerManifest, GradAxis, Gradients, RFChannel, RF, Magnet, Spectrometer, Console, Subsystems, Metadata +from src.openmrd.models import * TEMPLATES = { "halbach_lowfield": { diff --git a/src/openmrd/demo_openmri_Ctype.py b/src/openmrd/demo_openmri_Ctype.py index a684d46..2a1a06d 100644 --- a/src/openmrd/demo_openmri_Ctype.py +++ b/src/openmrd/demo_openmri_Ctype.py @@ -39,7 +39,13 @@ "reconstruction_parameters": {"algorithm":"GRAPPA","acceleration_factor":2} }, "spectrometer":{"model":"RedPitaya-122","sampling_rate_hz":122e6,"bit_depth":16,"max_tx_freq_hz":50e6}, - "console":{"name":"mri4all","os":"Linux","api":"Python gRPC","pulseq_support":True,"latency_ms":3.0} + "console":{"name":"mri4all","os":"Linux","api":"Python gRPC","pulseq_support":True,"latency_ms":3.0}, + "recon": {"name": "open-lf-recon","framework": "Python","backend": "PyTorch","device": "cuda","latency_ms": 12.5, + "batch_size": 2,"input_format": "kspace","output_format": "image","complex_data": true,"use_kspace_recon": true, + "use_classical_denoising": true,"use_motion_correction": false,"use_super_resolution": true,"use_dl_reconstruction": true, + + "model_name": "3D-UNet-SRR","model_checkpoint": "models/srr_unet.pth","physics_informed": true,"coil_sensitivity_maps": true,"field_strength": 0.05 + } } } } diff --git a/src/openmrd/models.py b/src/openmrd/models.py index 39914ef..74b8fde 100644 --- a/src/openmrd/models.py +++ b/src/openmrd/models.py @@ -1,6 +1,7 @@ from pydantic import BaseModel, Field, RootModel from typing import List, Optional, Union, Dict +from src.openmrd.reconstruction import * class Homog(BaseModel): ppm: float @@ -57,12 +58,6 @@ class Console(BaseModel): pulseq_support: bool = True latency_ms: Optional[float] = None - - - - - - class Metadata(BaseModel): name: str organization: str @@ -78,11 +73,112 @@ class CoordinateSystem(BaseModel): units: str = "SI" scanner_to_lab_transform: List[float] = Field(default_factory=lambda: [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]) +# ========================================================== +# 1️⃣ Acquisition / Contrast Parameters reconstruction and acquision +# ========================================================== + + +class ContrastParameters(BaseModel): + name: str # T1, T2, FLAIR, Diffusion, etc. + echo_time_ms: Optional[float] = None + repetition_time_ms: Optional[float] = None + flip_angle_deg: Optional[float] = None + b_value: Optional[float] = None # For diffusion + diffusion_directions: Optional[int] = None + orientation: List[str] = Field(default_factory=lambda: ["axial", "coronal", "sagittal"]) + notes: Optional[str] = None # Acquisition-specific info + +# ========================================================== +# 2️⃣ K-space / Reconstruction Parameters (Traditional) +# ========================================================== +class KSpaceReconstruction(BaseModel): + algorithm: str = "FFT" # FFT, NUFFT, SENSE, GRAPPA, etc. + filtering: Optional[str] = None # Hamming, Gaussian, etc. + density_compensation: Optional[bool] = False + oversampling_factor: Optional[float] = 1.0 + orientation_correction: Optional[bool] = True # Rotate to canonical axes + comments: Optional[str] = "Traditional k-space reconstruction settings" + +# ========================================================== +# 3️⃣ Classical Denoising / Preprocessing +# ========================================================== +class ClassicalDenoising(BaseModel): + method: str # Gaussian, Median, NLM, BM3D + parameters: Dict[str, float] = Field(default_factory=dict) + apply_before_reconstruction: Optional[bool] = True + comments: Optional[str] = "Optional classical denoising step" + +# ========================================================== +# 3.1 Motion Correction (Optional) +# ========================================================== +class MotionCorrection(BaseModel): + method: str # e.g., rigid, affine, non-rigid + parameters: Dict[str, float] = Field(default_factory=dict) + apply_before_reconstruction: Optional[bool] = True + comments: Optional[str] = "Optional motion correction step" + +# ========================================================== +# 3.2 Super-resolution (Optional) +# ========================================================== +class SuperResolution(BaseModel): + method: str # e.g., interpolation, DL-based + scale_factor: Optional[float] = 2.0 + comments: Optional[str] = "Optional super-resolution enhancement" + +# ========================================================== +# 4️⃣ Deep Learning Based Reconstruction +# ========================================================== +class DLReconstruction(BaseModel): + model_name: str # nnUNet, UNet, SwinUNet + checkpoint_path: Optional[str] = None + input_type: str = "k-space" # raw or magnitude + output_type: str = "image" + normalization: Optional[str] = "z-score" + augmentation: Optional[Dict[str, Union[bool, float]]] = None + domain_adaptation: Optional[bool] = False + comments: Optional[str] = "DL-based reconstruction and denoising" + +# ========================================================== +# 5️⃣ Post-processing / Visualization +# ========================================================== +class Visualization(BaseModel): + views: List[str] = Field(default_factory=lambda: ["axial", "coronal", "sagittal"]) + fusion: Optional[bool] = False # Combine multiple contrasts + overlay_masks: Optional[bool] = False # Segmentation overlay + windowing: Optional[Dict[str, float]] = None # Window/level + colormap: Optional[str] = "gray" + comments: Optional[str] = "Visualization and multi-orientation display" + +# ========================================================== +# 6️⃣ Evaluation / Metrics +# ========================================================== +class Evaluation(BaseModel): + reference: Optional[str] = None # Ground truth / baseline + metrics: List[str] = Field(default_factory=lambda: ["SSIM", "PSNR", "Dice"]) + segmentation: Optional[Dict[str, str]] = None # Mask files for evaluation + comments: Optional[str] = "Evaluation of image quality and/or segmentation" + + class ReconstructionManifest(BaseModel): openmrd_version: str = "0.1" metadata: Metadata coordinate_system: CoordinateSystem = CoordinateSystem() + + contrast_settings: List[ContrastParameters] + reconstruction_parameters: dict + # Traditional reconstruction steps + kspace_recon: Optional[KSpaceReconstruction] = None + classical_denoising: Optional[ClassicalDenoising] = None + motion_correction: Optional[MotionCorrection] = None + super_resolution: Optional[SuperResolution] = None + # DL-based steps + dl_reconstruction: Optional[DLReconstruction] = None + # Visualization & evaluation + visualization: Optional[Visualization] = None + evaluation: Optional[Evaluation] = None + notes: Optional[str] = "High-level MRI pipeline supporting both traditional and DL workflows" + testing: Optional[dict] = None security: Optional[dict] = None checksums: Optional[List[dict]] = None @@ -96,8 +192,7 @@ class Subsystems(BaseModel): rf: RF spectrometer: Spectrometer console: Console - recon_pipeline: ReconstructionManifest = None - + recon_pipeline: ReconstructionManifest class ScannerManifest(BaseModel): openmrd_version: str = "0.1" @@ -107,5 +202,4 @@ class ScannerManifest(BaseModel): testing: Optional[dict] = None security: Optional[dict] = None checksums: Optional[List[dict]] = None - extensions: Optional[dict] = None - + extensions: Optional[dict] = None \ No newline at end of file diff --git a/src/openmrd/reconstruction.py b/src/openmrd/reconstruction.py deleted file mode 100644 index 0cd9abe..0000000 --- a/src/openmrd/reconstruction.py +++ /dev/null @@ -1,135 +0,0 @@ -from pydantic import BaseModel, Field -from typing import List, Optional, Dict, Union - -# ========================================================== -# 1️⃣ Acquisition / Contrast Parameters -# ========================================================== -class ContrastParameters(BaseModel): - name: str # T1, T2, FLAIR, Diffusion, etc. - echo_time_ms: Optional[float] = None - repetition_time_ms: Optional[float] = None - flip_angle_deg: Optional[float] = None - b_value: Optional[float] = None # For diffusion - diffusion_directions: Optional[int] = None - orientation: List[str] = Field(default_factory=lambda: ["axial", "coronal", "sagittal"]) - notes: Optional[str] = None # Acquisition-specific info - -# ========================================================== -# 2️⃣ K-space / Reconstruction Parameters (Traditional) -# ========================================================== -class KSpaceReconstruction(BaseModel): - algorithm: str = "FFT" # FFT, NUFFT, SENSE, GRAPPA, etc. - filtering: Optional[str] = None # Hamming, Gaussian, etc. - density_compensation: Optional[bool] = False - oversampling_factor: Optional[float] = 1.0 - orientation_correction: Optional[bool] = True # Rotate to canonical axes - comments: Optional[str] = "Traditional k-space reconstruction settings" - -# ========================================================== -# 3️⃣ Classical Denoising / Preprocessing -# ========================================================== -class ClassicalDenoising(BaseModel): - method: str # Gaussian, Median, NLM, BM3D - parameters: Dict[str, float] = Field(default_factory=dict) - apply_before_reconstruction: Optional[bool] = True - comments: Optional[str] = "Optional classical denoising step" - -# ========================================================== -# 3.1 Motion Correction (Optional) -# ========================================================== -class MotionCorrection(BaseModel): - method: str # e.g., rigid, affine, non-rigid - parameters: Dict[str, float] = Field(default_factory=dict) - apply_before_reconstruction: Optional[bool] = True - comments: Optional[str] = "Optional motion correction step" - -# ========================================================== -# 3.2 Super-resolution (Optional) -# ========================================================== -class SuperResolution(BaseModel): - method: str # e.g., interpolation, DL-based - scale_factor: Optional[float] = 2.0 - comments: Optional[str] = "Optional super-resolution enhancement" - -# ========================================================== -# 4️⃣ Deep Learning Based Reconstruction -# ========================================================== -class DLReconstruction(BaseModel): - model_name: str # nnUNet, UNet, SwinUNet - checkpoint_path: Optional[str] = None - input_type: str = "k-space" # raw or magnitude - output_type: str = "image" - normalization: Optional[str] = "z-score" - augmentation: Optional[Dict[str, Union[bool, float]]] = None - domain_adaptation: Optional[bool] = False - comments: Optional[str] = "DL-based reconstruction and denoising" - -# ========================================================== -# 5️⃣ Post-processing / Visualization -# ========================================================== -class Visualization(BaseModel): - views: List[str] = Field(default_factory=lambda: ["axial", "coronal", "sagittal"]) - fusion: Optional[bool] = False # Combine multiple contrasts - overlay_masks: Optional[bool] = False # Segmentation overlay - windowing: Optional[Dict[str, float]] = None # Window/level - colormap: Optional[str] = "gray" - comments: Optional[str] = "Visualization and multi-orientation display" - -# ========================================================== -# 6️⃣ Evaluation / Metrics -# ========================================================== -class Evaluation(BaseModel): - reference: Optional[str] = None # Ground truth / baseline - metrics: List[str] = Field(default_factory=lambda: ["SSIM", "PSNR", "Dice"]) - segmentation: Optional[Dict[str, str]] = None # Mask files for evaluation - comments: Optional[str] = "Evaluation of image quality and/or segmentation" - -# ========================================================== -# 7️⃣ High-level MRI Reconstruction Pipeline (Updated) -# ========================================================== -class MRIPipelineExtended(BaseModel): - contrast_settings: List[ContrastParameters] - # Traditional reconstruction steps - kspace_recon: Optional[KSpaceReconstruction] = None - classical_denoising: Optional[ClassicalDenoising] = None - motion_correction: Optional[MotionCorrection] = None - super_resolution: Optional[SuperResolution] = None - # DL-based steps - dl_reconstruction: Optional[DLReconstruction] = None - # Visualization & evaluation - visualization: Optional[Visualization] = None - evaluation: Optional[Evaluation] = None - notes: Optional[str] = "High-level MRI pipeline supporting both traditional and DL workflows" - -# ========================================================== -# Example: Creating an extended pipeline -# ========================================================== -example_pipeline_extended = MRIPipelineExtended( - contrast_settings=[ - ContrastParameters(name="T1", echo_time_ms=10, repetition_time_ms=500), - ContrastParameters(name="T2", echo_time_ms=80, repetition_time_ms=3000), - ContrastParameters(name="FLAIR", echo_time_ms=120, repetition_time_ms=9000) - ], - kspace_recon=KSpaceReconstruction(algorithm="FFT", filtering="Hamming"), - classical_denoising=ClassicalDenoising(method="Gaussian", parameters={"sigma": 0.5}), - motion_correction=MotionCorrection(method="rigid"), - super_resolution=SuperResolution(method="DL-based", scale_factor=2.0), - dl_reconstruction=DLReconstruction(model_name="nnUNet", checkpoint_path="/path/to/checkpoint.pth", domain_adaptation=True), - visualization=Visualization(fusion=True, overlay_masks=True), - evaluation=Evaluation(reference="/path/to/ref.nii", metrics=["SSIM", "PSNR", "Dice"]) -) - - -# ✅ Key Updates / Optional Steps - -# MotionCorrection – optional, can be applied before reconstruction. - -# SuperResolution – optional, either classical interpolation or DL-based. - -# DLReconstruction – optional, can replace or complement classical reconstruction. - -# ClassicalDenoising – optional, can apply pre- or post-reconstruction. - -# All steps optional – pipeline is flexible: you can run only traditional steps, only DL, or hybrid. - -# Metadata / Comments – every step has comments field for reproducibility and documentation. \ No newline at end of file