Skip to content
This repository was archived by the owner on Dec 22, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ The most up-to-date information can be found at the official BART website: http:
**Quick Installation**
See the [Quick-Install guide](doc/quick-install.md) for quick installation instructions for BART.

## DACH_NORDIC ISMRM 2025
**Title**: Non-Cartesian T1 | Subspace-Constrained Reconstruction
**Meeting**: [2025 DACH-Nordic ISMRM 2025, Kiel, Germany](https://ismrm-dach-2025.moincc.de/)
**Material**: [`./dach_ismrm2025/subspace/`](./dach_ismrm2025/subspace)
**Date**: September 14, 2025

## DS-ISMRM PhD Training 2025
**Title**: Translational cardiovascular MR Imaging
**Meeting**: [2025 DS-ISMRM PhD Training, Ulm, Germany](https://www.uni-ulm.de/einrichtungen/moman/forschungsbereiche/academy/phd-training-2025)
Expand Down
4 changes: 4 additions & 0 deletions dach_ismrm2025/subspace/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.venv
__pycache__
*.hdr
*.cfl
22 changes: 22 additions & 0 deletions dach_ismrm2025/subspace/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Subspace Tutorial for ISMRM DACH-NORDIC 2025
Demonstrate subspace reconstruction for inversion recovery FLASH acquisition both on numerical phantom and data acquired with pulseq sequence generated with `bart seq`.

- [Jupyter Notebook](./tutorial.ipynb)
- [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mrirecon/bart-workshop/blob/master/dach_ismrm2025/subspace/tutorial_colab.ipynb)





## Requirements
```
bart version v0.9.00-1131-g7dd53c3
```

Install python packages in virtual environment.
```
python3 -m venv .venv
source ./venv/bin/activate
pip install -r requirements
```

38 changes: 38 additions & 0 deletions dach_ismrm2025/subspace/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
asttokens==3.0.0
comm==0.2.3
contourpy==1.3.3
cycler==0.12.1
debugpy==1.8.16
decorator==5.2.1
executing==2.2.0
fonttools==4.59.1
ipykernel==6.30.1
ipython==9.4.0
ipython_pygments_lexers==1.1.1
jedi==0.19.2
jupyter_client==8.6.3
jupyter_core==5.8.1
kiwisolver==1.4.9
matplotlib==3.10.5
matplotlib-inline==0.1.7
nest-asyncio==1.6.0
numpy==2.3.2
packaging==25.0
parso==0.8.4
pexpect==4.9.0
pillow==11.3.0
platformdirs==4.3.8
prompt_toolkit==3.0.51
psutil==7.0.0
ptyprocess==0.7.0
pure_eval==0.2.3
Pygments==2.19.2
pyparsing==3.2.3
python-dateutil==2.9.0.post0
pyzmq==27.0.2
six==1.17.0
stack-data==0.6.3
tornado==6.5.2
traitlets==5.14.3
typing_extensions==4.14.1
wcwidth==0.2.13
Empty file.
19 changes: 19 additions & 0 deletions dach_ismrm2025/subspace/src/customStyle.mplstyle
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
savefig.dpi : 300
savefig.bbox : tight
axes.titlepad : 20
axes.facecolor : w
axes.edgecolor : grey
axes.grid : False
grid.color : grey
grid.linewidth : 1
grid.alpha : 0.5
xtick.color : k
ytick.color : k
xtick.bottom : True
xtick.major.size : 5
ytick.major.size : 5
font.size: 12.0

legend.frameon : True
legend.edgecolor : grey
legend.facecolor : w
107 changes: 107 additions & 0 deletions dach_ismrm2025/subspace/src/plotSubspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import matplotlib as mpl
from matplotlib import pyplot as plt
import cfl
import numpy as np




def plotCoefficientMaps():
fig, axs = plt.subplots(1,4, figsize=(12,4))
axs =axs.ravel()
img = absImageFromCFL("subspace_reco")
for i, ax in enumerate(axs):
ax.set_xticks([])
ax.set_yticks([])
ax.set_title("Coefficient # {}".format(i+1), fontdict={'fontsize':12})
ax.imshow(img[:,:,i], cmap=createNewMap("gray"), clim=(None, 1.2))
plt.show()

def plotTemporalEvolution():
fig, axs = plt.subplots(1,4, figsize=(12,4))
axs =axs.ravel()
timesteps=[0,100, 200,600]
img = absImageFromCFL("imgs")
for i, ax in zip(timesteps,axs):
ax.set_xticks([])
ax.set_yticks([])
ax.set_title("t={}".format(i), fontdict={'fontsize':12})
ax.imshow(img[:,:,i], cmap=createNewMap("gray"), clim=(None, 0.125),
origin="upper")

plt.tight_layout()
plt.show()

def plotT1Map():
fig, axs = plt.subplots(1, figsize=(4,3))
axs.set_xticks([])
axs.set_yticks([])
img =absImageFromCFL("t1map")
axs.imshow(img, cmap=createNewMap("gray"),clim=(None,1.5))
plt.show()

def createNewMap(mapName):
newmap = mpl.colormaps[mapName].copy()
newmap.set_bad(color="black")
if mapName == "RdBu_r":
newmap.set_bad(color="white")
return newmap

def realImageFromCFL(filename):
return np.real(cfl.readcfl(filename).squeeze())
def absImageFromCFL(filename):
return np.abs(cfl.readcfl(filename).squeeze())


def plotDict(ax, n=5):
indx = np.random.randint(0,100000, size=n)
filename="./subspace_dict"
signals= realImageFromCFL(filename)[:,indx].T
cmap=mpl.colormaps["Set1"]
ax.set_xlabel("time [s]")
ax.set_ylabel("signal")
ax.set_title("Selection from Simulated Dictionary",fontdict={'fontsize':12})
time =np.arange(0, len(signals[0])) * 6e-3
for i,signal in enumerate(signals):
ax.plot(time, signal, color=cmap(i))

return ax

def plotPCACoeff(ax,nCoef=4,n=30):
ax.set_xlabel("principal component")
ax.set_ylabel("rel. contribution")
ax.set_title("Accumulated PCA Coefficients",fontdict={'fontsize':12})
S = realImageFromCFL("./S")

cumSum = np.cumsum(S)
cumSum /= cumSum[-1]
coeffIndx = np.arange(0,n)
ax.plot(coeffIndx, cumSum[:n], 'ko-')
annotation = "{} coeff. -> {:.3}% contribution".format(nCoef, cumSum[nCoef]*100)

ax.annotate(annotation,xy=(0.1,.5), xycoords="axes fraction")

return ax


def plotTemporalBasis(ax, n=5):
ax.set_title("Temporal Basis",fontdict={'fontsize':12})
ax.set_xlabel("time [s]")
ax.set_ylabel("signal")
U = realImageFromCFL("./U").T[:n]
time =np.arange(0, len(U[0])) * 6e-3
cmap=mpl.colormaps["Set1"]
for i,signal in enumerate(U):
ax.plot(time, signal, color=cmap(i), label="# {}".format(i+1))

ax.legend()


def plotSubspace():
fig, axs = plt.subplots(1,3, figsize = (12,4))
plotDict(axs[0],n=5)
plotPCACoeff(axs[1], nCoef=4, n=17)
plotTemporalBasis(axs[2],n=4)
plt.tight_layout()
plt.show()

Loading