mrzerocore 0.2.6__tar.gz → 0.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/Cargo.lock +1 -1
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/Cargo.toml +1 -1
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/PKG-INFO +5 -3
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/flash.ipynb +3 -1
- MRzeroCore-0.2.8/documentation/playground_mr0/mr00_FLASH_2D_ernstAngle_opt.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_CS_cartesian_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_CS_radial_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_DREAM_STE_seq.ipynb +612 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/mr0_DREAM_STID_seq.ipynb +15 -3
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_DWI_GRE_2D_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_EPI_2D_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_FID_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_FLASH_2D_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_GRE_to_FLASH.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_RARE_2D_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_SE_CPMG_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_STE_3pulses_5echoes_seq.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_bSSFP_2D_seq.ipynb +1 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/mr0_burst_TSE.ipynb +14 -3
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_Fit_T1.ipynb +1 -0
- MRzeroCore-0.2.8/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_voxelNN_T1.ipynb +1 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/mr0_pypulseq_exmpls_seq.ipynb +1 -1
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/mr0_upload_seq.ipynb +1 -1
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/overview.md +81 -81
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/pulseq_flash.ipynb +6 -8
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/pulseq_sim_pTx.ipynb +8 -2
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/pyproject.toml +2 -1
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/phantom/brainweb/__init__.py +7 -2
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/phantom/sim_data.py +1 -2
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/phantom/voxel_grid_phantom.py +1 -2
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/simulation/main_pass.py +2 -0
- MRzeroCore-0.2.6/documentation/playground_mr0/mr00_FLASH_2D_ernstAngle_opt.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_CS_cartesian_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_CS_radial_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_DREAM_STE_seq.ipynb +0 -840
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_DWI_GRE_2D_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_EPI_2D_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_FID_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_FLASH_2D_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_GRE_to_FLASH.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_RARE_2D_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_SE_CPMG_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_STE_3pulses_5echoes_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_bSSFP_2D_seq.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_Fit_T1.ipynb +0 -1
- MRzeroCore-0.2.6/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_voxelNN_T1.ipynb +0 -1
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/.github/workflows/pypi_publish.yml +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/.gitignore +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/.readthedocs.yaml +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/EULA.txt +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/LICENSE +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/README.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/_config.yml +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/_toc.yml +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/api/phantom.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/api/reco.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/api/sequence.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/api/simulation/isochromat_sim.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/api/simulation/pdg_sim.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/api/simulation.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/api.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/intro.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/logo.png +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/phantom_generation.md +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/AdjDataUser2gB0_transversal_0.08moving_average.mat +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/numerical_brain_cropped.mat +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/ptx_phantom.p +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/pulseq_rf_shim.ipynb +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/seqs/flash pTx CP.seq +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/seqs/flash pTx EP.seq +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/seqs/flash pTx QM.seq +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/subject05.npz +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/playground_mr0/util.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/requirements.txt +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/resources/logo.blend +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/resources/logo.png +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/documentation/resources/studio_small_09_2k.hdr +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/__init__.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/phantom/brainweb/brainweb_data.json +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/phantom/brainweb/brainweb_data_sources.txt +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/phantom/brainweb/output/.gitkeep +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/phantom/custom_voxel_phantom.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/exporter.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/exporter_v2.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/helpers.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_exporter.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/__init__.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/adc.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/helpers.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulse.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/__init__.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/adc.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/block.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/definitons.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/gradient.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/helpers.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/rf.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/trap.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/pulseq/pulseq_loader/spoiler.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/reconstruction.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/sequence.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/simulation/isochromat_sim.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/python/MRzeroCore/simulation/pre_pass.py +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/src/lib.rs +0 -0
- {MRzeroCore-0.2.6 → MRzeroCore-0.2.8}/src/pre_pass.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: MRzeroCore
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
@@ -10,15 +10,17 @@ Requires-Dist: pypulseq
|
|
|
10
10
|
Requires-Dist: matplotlib>=3.5
|
|
11
11
|
Requires-Dist: scipy>=1.7
|
|
12
12
|
Requires-Dist: requests>=2.20
|
|
13
|
+
Requires-Dist: scikit-image
|
|
14
|
+
Requires-Dist: torchkbnufft
|
|
13
15
|
Requires-Dist: pydisseqt
|
|
14
16
|
License-File: LICENSE
|
|
15
17
|
Summary: Core functionality of MRzero
|
|
16
18
|
Author-email: Jonathan Endres <jonathan.endres@uk-erlangen.de>
|
|
17
19
|
Requires-Python: >=3.9
|
|
18
20
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
19
|
-
Project-URL: Documentation, https://mrzero-core.readthedocs.io/
|
|
20
|
-
Project-URL: Repository, https://github.com/MRsources/MRzero-Core
|
|
21
21
|
Project-URL: MRzero-Paper, https://arxiv.org/abs/2002.04265
|
|
22
|
+
Project-URL: Repository, https://github.com/MRsources/MRzero-Core
|
|
23
|
+
Project-URL: Documentation, https://mrzero-core.readthedocs.io/
|
|
22
24
|
|
|
23
25
|
[](https://mrzero-core.readthedocs.io/en/latest/?badge=latest)
|
|
24
26
|
|
|
@@ -102,8 +102,10 @@
|
|
|
102
102
|
"\n",
|
|
103
103
|
"phantom = mr0.VoxelGridPhantom.brainweb(\"subject05.npz\")\n",
|
|
104
104
|
"phantom = phantom.interpolate(64, 64, 32).slices([16])\n",
|
|
105
|
+
"# The default fov is loaded from the data, but we can change it:\n",
|
|
106
|
+
"phantom.size = torch.tensor([0.15, 0.15, 1])\n",
|
|
107
|
+
"\n",
|
|
105
108
|
"phantom.plot()\n",
|
|
106
|
-
"phantom.fov = torch.tensor([0.15, 0.15, 1])\n",
|
|
107
109
|
"data = phantom.build()"
|
|
108
110
|
]
|
|
109
111
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cells":[{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":401},"executionInfo":{"elapsed":52237,"status":"ok","timestamp":1698742386889,"user":{"displayName":"Moritz Zaiss","userId":"13462394581901772323"},"user_tz":-60},"id":"vTjDmgyofjbF","outputId":"47d34dc6-9bf1-489a-dbd9-0c0ffd9a5374","tags":["hide-cell"]},"outputs":[],"source":["# @title On Google Colab, you need to restart the runtime after executing this cell\n","!pip install numpy==1.24"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":401},"executionInfo":{"elapsed":52237,"status":"ok","timestamp":1698742386889,"user":{"displayName":"Moritz Zaiss","userId":"13462394581901772323"},"user_tz":-60},"id":"vTjDmgyofjbF","outputId":"47d34dc6-9bf1-489a-dbd9-0c0ffd9a5374","tags":["hide-cell"]},"outputs":[],"source":["!pip install pypulseq==1.3.1.post1 &> /dev/null\n","!pip install nevergrad &> /dev/null\n","!pip install MRzeroCore &> /dev/null\n","!wget https://github.com/MRsources/MRzero-Core/raw/main/documentation/playground_mr0/numerical_brain_cropped.mat &> /dev/null\n","!wget https://github.com/MRsources/MRzero-Core/raw/main/documentation/playground_mr0/util.py &> /dev/null"]},{"cell_type":"markdown","metadata":{},"source":["(mr00_FLASH_2D_ernstAngle_opt)=\n","# MR00 2D FLASH Ernst angle optimization"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":400},"executionInfo":{"elapsed":6003,"status":"error","timestamp":1698682962173,"user":{"displayName":"Moritz Zaiss","userId":"13462394581901772323"},"user_tz":-60},"id":"O-_sr6lZjR_n","outputId":"bb12a7b4-904c-476a-db10-675bdc772583","scrolled":true},"outputs":[],"source":["#@title setup basic sequence params & phantom\n","# %% S0. SETUP env\n","import MRzeroCore as mr0\n","import numpy as np\n","\n","import pypulseq as pp\n","import torch\n","import matplotlib.pyplot as plt\n","import util\n","\n","import nevergrad as ng\n","\n","from IPython.display import clear_output\n","\n","plt.rcParams['figure.figsize'] = [10, 5]\n","plt.rcParams['figure.dpi'] = 100 # 200 e.g. is really fine, but slower\n","\n","\n","\n","# %% S1. SETUP sys\n","\n","# choose the scanner limits\n","system = pp.Opts(max_grad=28,grad_unit='mT/m',max_slew=150,slew_unit='T/m/s',\n"," rf_ringdown_time=20e-6,rf_dead_time=100e-6,adc_dead_time=20e-6,grad_raster_time=50*10e-6)\n","\n","# Define FOV and resolution\n","fov = 220e-3\n","slice_thickness = 8e-3\n","sz = (32, 32) # spin system size / resolution\n","Nread = 64 # frequency encoding steps/samples\n","Nphase = 64 # phase encoding steps/samples\n","\n","# %% S4: SETUP SPIN SYSTEM/object on which we can run the MR sequence external.seq from above\n","\n","sz = [64, 64]\n","# (i) load a phantom object from file\n","obj_p = mr0.VoxelGridPhantom.load_mat('numerical_brain_cropped.mat')\n","obj_p = obj_p.interpolate(sz[0], sz[1], 1)\n","# Manipulate loaded data\n","obj_p.T2dash[:] = 30e-3\n","obj_p.D *= 0\n","obj_p.B0 *= 1 # alter the B0 inhomogeneity\n","# Store PD and B0 for comparison\n","PD = obj_p.PD\n","B0 = obj_p.B0\n","obj_p.plot()\n","# Convert Phantom into simulation data\n","obj_p = obj_p.build()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"CUAKk_RaSXaQ"},"outputs":[],"source":["#@title set up functions for generating, simulating and reconstructing FLASH sequence\n","def ifft2d(x):\n"," x = torch.fft.fftshift(x)\n"," x = torch.fft.ifft2(x)\n"," x = torch.fft.ifftshift(x)\n"," return x\n","\n","def fft2d(x, unitary=0):\n"," x = torch.fft.ifftshift(x)\n"," x = torch.fft.fft2(x)\n"," x = torch.fft.fftshift(x)\n"," return x\n","\n","def generate_flash_seq(FA=10, fname='gre.seq', verbose=0):\n","\n"," # %% S2. DEFINE the sequence\n"," seq = pp.Sequence()\n","\n"," # Define rf events\n"," rf1, _, _ = pp.make_sinc_pulse(\n"," flip_angle= FA * np.pi / 180, duration=1e-3,\n"," slice_thickness=slice_thickness, apodization=0.5, time_bw_product=4,\n"," system=system, return_gz=True\n"," )\n"," # rf1 = pp.make_block_pulse(flip_angle=90 * np.pi / 180, duration=1e-3, system=system)\n","\n"," # Define other gradients and ADC events\n"," gx = pp.make_trapezoid(channel='x', flat_area=Nread / fov, flat_time=10e-3, system=system)\n"," adc = pp.make_adc(num_samples=Nread, duration=10e-3, phase_offset=0 * np.pi/180, delay=gx.rise_time, system=system)\n"," gx_pre = pp.make_trapezoid(channel='x', area=-gx.area / 2, duration=5e-3, system=system)\n"," gx_spoil = pp.make_trapezoid(channel='x', area=1.5 * gx.area, duration=2e-3, system=system)\n","\n"," rf_phase = 0\n"," rf_inc = 0\n"," rf_spoiling_inc = 117\n","\n"," # ======\n"," # CONSTRUCT SEQUENCE\n"," # ======\n"," ##linear reordering\n"," phenc = np.arange(-Nphase // 2, Nphase // 2, 1) / fov\n"," permvec =np.arange(0, Nphase, 1)\n"," ## centric reordering\n"," #permvec = sorted(np.arange(len(phenc)), key=lambda x: abs(len(phenc) // 2 - x))\n"," ## random reordering\n"," #perm =np.arange(0, Nphase, 1); permvec = np.random.permutation(perm)\n","\n"," phenc_centr = phenc[permvec]\n","\n"," for ii in range(0, Nphase): # e.g. -64:63\n","\n"," rf1.phase_offset = rf_phase / 180 * np.pi # set current rf phase\n","\n"," adc.phase_offset = rf_phase / 180 * np.pi # follow with ADC\n"," rf_inc = divmod(rf_inc + rf_spoiling_inc, 360.0)[1] # increase increment\n"," # increment additional pahse\n"," rf_phase = divmod(rf_phase + rf_inc, 360.0)[1]\n","\n"," seq.add_block(rf1)\n"," seq.add_block(pp.make_delay(0.005))\n"," gp = pp.make_trapezoid(channel='y', area=phenc_centr[ii], duration=5e-3, system=system)\n"," seq.add_block(gx_pre, gp)\n"," seq.add_block(adc, gx)\n"," gp = pp.make_trapezoid(channel='y', area=-phenc_centr[ii], duration=5e-3, system=system)\n"," seq.add_block(gx_spoil, gp)\n"," if ii < Nphase - 1:\n"," seq.add_block(pp.make_delay(0.01))\n","\n","\n"," # %% S3. CHECK, PLOT and WRITE the sequence as .seq\n"," # Check whether the timing of the sequence is correct\n"," ok, error_report = seq.check_timing()\n"," if ok:\n"," if verbose > 0:\n"," print('Timing check passed successfully')\n"," else:\n"," print('Timing check failed. Error listing follows:')\n"," [print(e) for e in error_report]\n","\n"," # PLOT sequence\n"," if verbose > 0:\n"," sp_adc, t_adc = util.pulseq_plot(seq, clear=False, figid=(11,12))\n","\n"," # Prepare the sequence output for the scanner\n"," seq.set_definition('FOV', [fov, fov, slice_thickness])\n"," seq.set_definition('Name', 'gre')\n"," seq.write(fname)\n","\n"," reco_params = {'permvec': permvec}\n","\n"," return reco_params\n","\n","def simu_seq(fname, obj_p, reco_params, noiselevel=1e-4, verbose=0):\n","\n"," permvec = reco_params['permvec']\n","\n"," # %% S5:. SIMULATE the external.seq file and add acquired signal to ADC plot\n"," # Read in the sequence\n"," seq0 = mr0.Sequence.import_file(fname)\n"," if verbose > 0:\n"," seq0.plot_kspace_trajectory()\n"," # Simulate the sequence\n"," graph = mr0.compute_graph(seq0, obj_p, 200, 1e-3)\n"," signal = mr0.execute_graph(graph, seq0, obj_p, print_progress=False)\n","\n"," # PLOT sequence with signal in the ADC subplot\n"," if verbose > 0:\n"," plt.close(11);plt.close(12)\n"," sp_adc, t_adc = util.pulseq_plot(seq, clear=False, signal=signal.numpy())\n","\n"," # additional noise as simulation is perfect\n"," signal += noiselevel * np.random.randn(signal.shape[0], 2).view(np.complex128)\n","\n","\n"," # %% S6: MR IMAGE RECON of signal ::: #####################################\n"," if verbose > 0:\n"," fig = plt.figure() # fig.clf()\n"," plt.subplot(411)\n"," plt.title('ADC signal')\n"," plt.plot(torch.real(signal), label='real')\n"," plt.plot(torch.imag(signal), label='imag')\n"," # this adds ticks at the correct position szread\n"," major_ticks = np.arange(0, Nphase * Nread, Nread)\n"," ax = plt.gca()\n"," ax.set_xticks(major_ticks)\n"," ax.grid()\n","\n"," kspace = torch.reshape((signal), (Nphase, Nread)).clone().t()\n","\n"," ipermvec = np.argsort(permvec)\n","\n"," kspace=kspace[:,ipermvec]\n","\n"," img = ifft2d(kspace)\n","\n"," return img\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"NwApBQJVSXaR"},"outputs":[],"source":["#@title nevergrad optimization\n","def calc_loss(FA): # loss function\n"," global iter\n"," reco_params = generate_flash_seq(FA=FA, fname='gre.seq')\n"," img = simu_seq('gre.seq', obj_p, reco_params, noiselevel=0*1e-3)\n"," mag = torch.sum(img.flatten().abs()**2)\n","\n"," clear_output(wait=True)\n"," plt.figure(figsize=(13,5))\n"," plt.subplot(1,2,1)\n"," plt.imshow(img.abs()), plt.colorbar()\n"," plt.title(f'iter {iter}: FA={FA[0]:.2f}, MAG={mag.item():.2f}')\n"," plt.subplot(1,2,2)\n"," plt.plot(values,'.-')\n"," plt.xlabel('iteration'), plt.ylabel('loss')\n"," plt.show()\n","\n"," iter += 1\n","\n"," return -mag.item()\n","\n","# def calc_loss(FA):\n","# E1 = np.exp(-20e-3/1)\n","# FArad = np.deg2rad(FA)\n","# S = np.sin(FArad) * (1-E1)/(1-np.cos(FArad)*E1)\n","# return -S\n","\n","def rescale_vars(x, a,b,c,d):\n"," # original range (a,b)\n"," # new range (c,d)\n"," return ((x-a) / (b-a)) * (d-c) + c\n","\n","def obj_fun_rescaled(x1): # rescaled loss functions, shuch that optimizer sees only normrange\n"," if type(x1) == list:\n"," x1 = np.array(x1)\n"," x1r = rescale_vars(x1, *normrange, *valrange)\n"," return calc_loss(x1r)\n","\n","def print_candidate_and_value(optimizer, candidate, value): # callback, print and save intermediate steps\n"," global cands, values, xx\n"," # print('iter', xx, 'cand:', candidate, 'val:', value)\n"," cands.append(candidate)\n"," values.append(value)\n","\n","iter = 0 # global iteration counter\n","\n","# number of cost function evaluations (\"budget\")\n","# Limited for building docs - should be increased\n","niter = 10\n","\n","# this is the range in which the optimizer operates, see https://cma-es.github.io/cmaes_sourcecode_page.html#practical\n","normrange = (-3,3)\n","\n","# boundaries in physical units\n","valrange = (1e-1,180)\n","\n","# initial value (not sure if it has any influence, probably depending on optimizer)\n","init = np.array([5]) # physical units\n","\n","# defining optimizable variables (\"instrumentation\")\n","instrum = ng.p.Instrumentation(\n"," ng.p.Array(init=rescale_vars(init,*valrange,*normrange)).set_bounds(*normrange),\n",")\n","\n","cands = [] # to save all candidates during opt\n","values = [] # to save loss values during opt\n","\n","optimizer = ng.optimizers.NGOpt(parametrization=instrum, budget=niter) # documentation says NGOpt is a good first choice, this is a \"meta-optimizer\" that chooses algorithm based on instrumentation\n","# optimizer = ng.optimizers.registry[\"PSO\"](parametrization=instrum, budget=niter) # particle swarm\n","# optimizer = ng.families.ParametrizedBO()(parametrization=instrum, budget=niter) # Bayesian optimization, this might be good for continuous in dim > 1, dim < ~100 (?)\n","# optimizer = ng.families.NonObjectOptimizer(method='Powell')(parametrization=instrum, budget=niter) # more traditional grad free things\n","# optimizer = ng.families.NonObjectOptimizer(method='NLOPT_GN_DIRECT')(parametrization=instrum, budget=niter) # more traditional grad free things\n","# optimizer = ng.families.ParametrizedCMA()(parametrization=instrum, budget=niter) # only in dim > 1\n","# optimizer = ng.families.RandomSearchMaker()(parametrization=instrum, budget=niter) # random search as baseline\n","\n","optimizer.register_callback(\"tell\", print_candidate_and_value) # set callback\n","\n","recommendation = optimizer.minimize(obj_fun_rescaled) # run opt\n","FAopt = rescale_vars(recommendation[0][0].value, *normrange, *valrange)\n","\n","print(\"final result:\", FAopt) # opt result\n","print(\"used optimizer\", optimizer._optim)\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"executionInfo":{"elapsed":542,"status":"ok","timestamp":1691486955197,"user":{"displayName":"Moritz Zaiss","userId":"13462394581901772323"},"user_tz":-120},"id":"u7jO7VTSSXaT","outputId":"c7a019de-02bd-4496-d552-ab15d3489a24"},"outputs":[],"source":["#@title some details on optimization history\n","x_explored = [cands[ii][0].value[0] for ii in range(len(cands))] # extract optimization history\n","FA_explored = np.array([rescale_vars(x, *normrange, *valrange) for x in x_explored]) # rescale back\n","\n","plt.figure(figsize=(7,12))\n","plt.subplot(4,1,1)\n","plt.plot(FA_explored,values,'.')\n","plt.xlabel('FA [deg]'), plt.ylabel('loss')\n","\n","plt.subplot(4,1,2)\n","plt.plot(FA_explored,'.-')\n","plt.ylabel('FA [deg]')\n","\n","plt.subplot(4,1,3)\n","plt.plot(values,'.-')\n","plt.ylabel('loss')\n","\n","plt.subplot(4,1,4)\n","plt.hist(FA_explored, bins=30)"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":944},"executionInfo":{"elapsed":98948,"status":"ok","timestamp":1691487054133,"user":{"displayName":"Moritz Zaiss","userId":"13462394581901772323"},"user_tz":-120},"id":"ujZRr2ppA9A7","outputId":"561e25e4-0a3f-4b93-a583-2f5fc28b113c"},"outputs":[],"source":["#@title manual line search\n","iter=0\n","FAs = np.linspace(0,90, 10) # Reduced precision for building docs\n","losses = np.zeros(FAs.shape)\n","for ii,FA in enumerate(FAs):\n"," losses[ii] = calc_loss(np.array([FA]))\n","\n","plt.figure()\n","plt.plot(FAs,losses,'.-')\n","plt.xlabel('FA'), plt.ylabel('loss')"]}],"metadata":{"colab":{"provenance":[{"file_id":"1jmZrTCl53SWE05bs-_Z0ge0tfBkgS6uo","timestamp":1691486549168}],"toc_visible":true},"kernelspec":{"display_name":"Python 3 (ipykernel)","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.3"}},"nbformat":4,"nbformat_minor":0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cells":[{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":13863,"status":"ok","timestamp":1678740065139,"user":{"displayName":"Zhengguo Tan","userId":"10291917877743041231"},"user_tz":-60},"id":"vTjDmgyofjbF","outputId":"c702ebb6-274c-4e2e-fec0-c67810ceb02c","tags":["hide-cell"]},"outputs":[],"source":["!pip install pypulseq==1.3.1.post1 &> /dev/null\n","!pip install MRzeroCore &> /dev/null\n","!wget https://github.com/MRsources/MRzero-Core/raw/main/documentation/playground_mr0/numerical_brain_cropped.mat &> /dev/null\n","!wget https://github.com/MRsources/MRzero-Core/raw/main/documentation/playground_mr0/util.py &> /dev/null"]},{"cell_type":"markdown","metadata":{},"source":["(mr0_CS_cartesian_seq)=\n","# Compressed Sensing - cartesian"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"executionInfo":{"elapsed":20513,"status":"ok","timestamp":1678741422978,"user":{"displayName":"Zhengguo Tan","userId":"10291917877743041231"},"user_tz":-60},"id":"O-_sr6lZjR_n","outputId":"d9cd1a67-4a63-44e6-df28-96198556f5d4"},"outputs":[],"source":["#@title generate\n","# %% S0. SETUP env\n","from skimage.restoration import denoise_tv_chambolle\n","import pywt\n","import MRzeroCore as mr0\n","import numpy as np\n","from matplotlib import pyplot as plt\n","plt.rcParams['figure.figsize'] = [10, 5]\n","plt.rcParams['figure.dpi'] = 100 # 200 e.g. is really fine, but slower\n","import pypulseq as pp\n","import torch\n","import util\n","\n","experiment_id = 'exD01_bSSFP_2D'\n","\n","# %% S1. SETUP sys\n","\n","# choose the scanner limits\n","system = pp.Opts(\n"," max_grad=28,\n"," grad_unit='mT/m',\n"," max_slew=150,\n"," slew_unit='T/m/s',\n"," rf_ringdown_time=20e-6,\n"," rf_dead_time=100e-6,\n"," adc_dead_time=20e-6,\n"," grad_raster_time=50*10e-6\n",")\n","\n","# %% S2. DEFINE the sequence \n","seq = pp.Sequence()\n","\n","# Define FOV and resolution\n","fov = 220e-3 \n","slice_thickness=8e-3\n","sz=[128,128] # spin system size / resolution\n","Nread = sz[0] # frequency encoding steps/samples\n","Nphase = sz[1] # phase encoding steps/samples\n","\n","# Define rf events\n","rf1 = pp.make_sinc_pulse(flip_angle=5 * np.pi / 180, duration=1e-3,slice_thickness=slice_thickness, apodization=0.5, time_bw_product=4, system=system)\n","# rf1, _= pp.make_block_pulse(flip_angle=90 * np.pi / 180, duration=1e-3, system=system)\n","\n","# Define other gradients and ADC events\n","gx = pp.make_trapezoid(channel='x', flat_area=Nread / fov, flat_time=10e-3, system=system)\n","adc = pp.make_adc(num_samples=Nread, duration=10e-3, phase_offset=0*np.pi/180,delay=gx.rise_time, system=system)\n","gx_pre = pp.make_trapezoid(channel='x', area=-gx.area / 2, duration=5e-3, system=system)\n","gx_spoil = pp.make_trapezoid(channel='x', area=1.5*gx.area, duration=2e-3, system=system)\n","\n","rf_phase = 0\n","rf_inc = 0\n","rf_spoiling_inc=117\n","\n","phase_enc__gradmoms = (torch.arange(0,Nphase,1)-Nphase//2) / fov\n","\n","# ======\n","# CONSTRUCT SEQUENCE\n","# ======\n","\n","idx = np.random.normal(loc=Nphase//2, scale=Nphase//4, size=(Nphase//2,)).astype(int)\n","idx[idx>=Nphase] = Nphase - 1\n","idx[idx<0] = 0\n","print('idx: ', idx)\n","\n","# idx = [1,5,15,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,45,50,55]\n","#idx = np.linspace(0,63,64)\n","#idx = [5,15,20,27,28,29,30,31,32,33,34,35,36,37,38,39,40,45,50,55]\n","# idx = np.random.poisson(Nread/2,Nread*8)\n","idx = np.unique(idx)\n","for ii in range(0, len(idx)): # e.g. -64:63\n","\n"," rf1.phase_offset = rf_phase / 180 * np.pi # set current rf phase\n"," \n"," adc.phase_offset = rf_phase / 180 * np.pi # follow with ADC\n"," rf_inc = divmod(rf_inc + rf_spoiling_inc, 360.0)[1] # increase increment\n"," rf_phase = divmod(rf_phase + rf_inc, 360.0)[1] # increment additional pahse\n","\n"," seq.add_block(rf1)\n"," gp= pp.make_trapezoid(channel='y', area=phase_enc__gradmoms[int(idx[ii])], duration=5e-3, system=system)\n"," seq.add_block(gx_pre,gp)\n"," seq.add_block(adc,gx)\n"," gp= pp.make_trapezoid(channel='y', area=-phase_enc__gradmoms[int(idx[ii])], duration=5e-3, system=system)\n"," seq.add_block(gx_spoil,gp)\n"," if ii<Nphase-1:\n"," seq.add_block(pp.make_delay(0.001))\n","# %% S3. CHECK, PLOT and WRITE the sequence as .seq\n","# Check whether the timing of the sequence is correct\n","ok, error_report = seq.check_timing()\n","if ok:\n"," print('Timing check passed successfully')\n","else:\n"," print('Timing check failed. Error listing follows:')\n"," [print(e) for e in error_report]\n","\n","# PLOT sequence\n","#sp_adc, t_adc = util.pulseq_plot(seq, clear=False)\n","\n","# Prepare the sequence output for the scanner\n","seq.set_definition('FOV', [fov, fov, slice_thickness])\n","seq.set_definition('Name', 'gre')\n","seq.write(experiment_id + '.seq')\n","# %% S4: SETUP SPIN SYSTEM/object on which we can run the MR sequence external.seq from above\n","\n","if 1:\n"," # (i) load a phantom object from file\n"," obj_p = mr0.VoxelGridPhantom.load_mat('numerical_brain_cropped.mat')\n"," obj_p = obj_p.interpolate(sz[0], sz[1], 1)\n"," # Manipulate loaded data\n"," obj_p.B0 *= 0\n"," obj_p.D *= 0\n","else:\n"," # or (ii) set phantom manually to a pixel phantom. Coordinate system is [-0.5, 0.5]^3\n"," obj_p = mr0.CustomVoxelPhantom(\n"," pos=[[-0.25, -0.25, 0]],\n"," PD=[1.0],\n"," T1=[3.0],\n"," T2=[0.5],\n"," T2dash=[30e-3],\n"," D=[0.0],\n"," B0 =0,\n"," voxel_size=0.1,\n"," voxel_shape=\"box\"\n"," )\n","\n","#obj_p.plot()\n","# Convert Phantom into simulation data\n","obj_p = obj_p.build()\n","\n","# %% S5:. SIMULATE the external.seq file and add acquired signal to ADC plot\n","\n","# Read in the sequence\n","seq0 = mr0.Sequence.import_file(experiment_id + '.seq')\n","#seq0.plot_kspace_trajectory()\n","# Simulate the sequence\n","graph = mr0.compute_graph(seq0, obj_p, 200, 1e-3)\n","signal = mr0.execute_graph(graph, seq0, obj_p, print_progress=False)\n","\n","# PLOT sequence with signal in the ADC subplot\n","#sp_adc, t_adc = util.pulseq_plot(seq, clear=True, signal=signal.numpy())\n","\n","kspace_adc=torch.reshape((signal),(len(idx),Nread)).clone().t()\n","\n","kspace = torch.zeros((Nread,Nread),dtype = torch.complex64)\n","kspace[:,idx] = kspace_adc\n","pattern = torch.zeros((Nread,Nread))\n","pattern[:,idx] = torch.ones(Nread,len(idx))\n","\n","# high frequencies centered as kspace and as FFT needs it\n","pattern = np.fft.fftshift(pattern.numpy())\n","kspace = np.fft.ifftshift(kspace.numpy())\n","\n","# kspace = kspace_full * pattern # apply the undersampling pattern\n","\n","# calculate the actually measured data in percent\n","actual_measured_percent = np.count_nonzero(pattern) / pattern.size * 100\n","\n","# Plotting\n","pattern_vis = np.fft.fftshift(pattern.copy())\n","kspace_vis = np.log(1+abs(np.fft.fftshift((kspace.copy()))))\n","fig = plt.figure(dpi=90)\n","plt.subplot(321)\n","plt.set_cmap(plt.gray())\n","# plt.imshow(abs(recon_nufft))\n","plt.ylabel('recon_full')\n","plt.subplot(322)\n","plt.set_cmap(plt.gray())\n","plt.imshow(abs(pattern_vis))\n","plt.ylabel(\"pattern_vis\")\n","plt.title(\"{:.1f} % sampled\".format(actual_measured_percent))\n","\n","plt.subplot(324)\n","plt.set_cmap(plt.gray())\n","plt.imshow(np.log(1+abs(kspace_adc.numpy().copy())))\n","plt.ylabel('kspace_adc')\n","plt.subplot(326)\n","plt.set_cmap(plt.gray())\n","plt.imshow(kspace_vis)\n","plt.ylabel('kspace*pattern')\n","plt.show()\n","\n","# %% ##########################################################################\n","# S6: compressed sensing MR reconstruction of undersampled signal\n","# S6.1: function definitions\n","\n","def shrink(coeff, epsilon):\n"," shrink_values = (abs(coeff) < epsilon)\n"," high_values = coeff >= epsilon\n"," low_values = coeff <= -epsilon\n"," coeff[shrink_values] = 0\n"," coeff[high_values] -= epsilon\n"," coeff[low_values] += epsilon\n","\n","\n","# help?\n","# https://www2.isye.gatech.edu/~brani/wp/kidsA.pdf\n","#for family in pywt.families():\n","# print(\"%s family: \" % family + ', '.join(pywt.wavelist(family)))\n","\n","\n","def waveletShrinkage(current, epsilon):\n"," # Compute Wavelet decomposition\n"," cA, (cH, cV, cD) = pywt.dwt2(current, 'haar')\n"," # Shrink\n"," shrink(cA, epsilon)\n"," shrink(cH, epsilon)\n"," shrink(cV, epsilon)\n"," shrink(cD, epsilon)\n"," wavelet = cA, (cH, cV, cD)\n"," # return inverse WT\n"," return pywt.idwt2(wavelet, 'haar')\n","\n","\n","def updateData(k_space, pattern, current, step, i):\n"," # go to k-space\n"," update = np.fft.ifft2(np.fft.fftshift(current))\n"," # compute difference\n"," update = k_space - (update * pattern)\n"," #print(\"i: {}, consistency RMSEpc: {:3.6f}\".format(i, np.abs(update[:]).sum() * 100))\n"," # return to image space\n"," update = np.fft.fftshift(np.fft.fft2(update))\n"," # improve current estimation by consitency\n"," update = current + (step * update)\n"," return update\n","\n","# %% S6.3 undersampling and undersampled reconstruction\n","# space= space/ np.linalg.norm(space[:]) # normalization of the data somethimes helps\n","\n","# parameters of iterative reconstructio using total variation denoising\n","denoising_strength = 5e-5\n","number_of_iterations = 4000\n","stepsz = 0.1\n","\n","# actual iterative reconstruction algorithm\n","current = np.zeros(kspace.shape)\n","first = updateData(kspace, pattern, current, 1, 0)\n","current_shrink = first\n","all_iter = np.zeros((kspace.shape[0], kspace.shape[1], number_of_iterations))\n","\n","for i in range(number_of_iterations):\n"," current = updateData(kspace, pattern, current_shrink,stepsz, i)\n","\n"," current_shrink = denoise_tv_chambolle(abs(current), denoising_strength)\n"," # current_shrink = waveletShrinkage(abs(current), denoising_strength)\n","\n"," all_iter[:, :, i] = current\n","\n","\n","plt.subplot(323)\n","plt.set_cmap(plt.gray())\n","plt.imshow(abs(first))\n","plt.ylabel('first iter (=NUFFT)')\n","plt.subplot(325)\n","plt.set_cmap(plt.gray())\n","plt.imshow(abs(current_shrink))\n","plt.ylabel('final recon')\n","\n","\n","# %% Plot all iter\n","# make 25 example iterations\n","idx = np.linspace(1, all_iter.shape[2], 25) - 1\n","# choose them from all iters\n","red_iter = all_iter[:, :, tuple(idx.astype(int))]\n","Tot = red_iter.shape[2]\n","Rows = Tot // 5\n","if Tot % 5 != 0:\n"," Rows += 1\n","Position = range(1, Tot + 1) # Position index\n","\n","fig = plt.figure()\n","for k in range(Tot):\n"," ax = fig.add_subplot(Rows, 5, Position[k])\n"," ax.imshow((abs((red_iter[:, :, k]))))\n"," plt.title('iter {}'.format(idx[k].astype(int)))\n"," print(k)\n","plt.show()"]}],"metadata":{"colab":{"provenance":[{"file_id":"1uTk3lc-O3xZS-rLDSkOLEYVV1rM0UTDJ","timestamp":1676904915190},{"file_id":"1lnFKubthQBxkz19cY7ScS-S0Hj9vHjEj","timestamp":1676708491940}]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.3"}},"nbformat":4,"nbformat_minor":0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cells":[{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":13556,"status":"ok","timestamp":1699010955218,"user":{"displayName":"Zhengguo Tan","userId":"10291917877743041231"},"user_tz":-60},"id":"vTjDmgyofjbF","outputId":"d7a4eee3-c327-4afe-8bc1-69fbb56be643","tags":["hide-cell"]},"outputs":[],"source":["!pip install pypulseq==1.3.1.post1 &> /dev/null\n","!pip install MRzeroCore &> /dev/null\n","!pip install torchkbnufft &> /dev/null\n","!wget https://github.com/MRsources/MRzero-Core/raw/main/documentation/playground_mr0/numerical_brain_cropped.mat &> /dev/null\n","!wget https://github.com/MRsources/MRzero-Core/raw/main/documentation/playground_mr0/util.py &> /dev/null"]},{"cell_type":"markdown","metadata":{},"source":["(mr0_CS_radial_seq)=\n","# Compressed Sensing - radial"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":246},"executionInfo":{"elapsed":258,"status":"error","timestamp":1699010929473,"user":{"displayName":"Zhengguo Tan","userId":"10291917877743041231"},"user_tz":-60},"id":"O-_sr6lZjR_n","outputId":"a3e4234c-f31c-426c-d3f0-4d4e4fdcdbfc"},"outputs":[],"source":["# %% S0. SETUP env\n","from skimage.restoration import denoise_tv_chambolle\n","import pywt\n","import MRzeroCore as mr0\n","import pypulseq as pp\n","import numpy as np\n","import torch\n","from matplotlib import pyplot as plt\n","plt.rcParams['figure.figsize'] = [10, 5]\n","plt.rcParams['figure.dpi'] = 100 # 200 e.g. is really fine, but slower\n","import util\n","\n","experiment_id = 'exF02_undersampled_radial'\n","\n","\n","# %% S1. SETUP sys\n","\n","# choose the scanner limits\n","system = pp.Opts(\n"," max_grad=28, grad_unit='mT/m', max_slew=150, slew_unit='T/m/s',\n"," rf_ringdown_time=20e-6, rf_dead_time=100e-6,\n"," adc_dead_time=20e-6, grad_raster_time=50 * 10e-6\n",")\n","\n","\n","# %% S2. DEFINE the sequence\n","seq = pp.Sequence()\n","\n","# Define FOV and resolution\n","fov = 220e-3\n","slice_thickness = 8e-3\n","sz = [128, 128] # spin system size / resolution\n","Nread = sz[0] # frequency encoding steps/samples\n","Nphase = 61 # phase encoding steps/samples, i.e. number of radial spokes\n","\n","# Define rf events\n","rf1, _, _ = pp.make_sinc_pulse(\n"," flip_angle=6 * np.pi / 180, duration=1e-3,\n"," slice_thickness=slice_thickness, apodization=0.5, time_bw_product=4,\n"," system=system, return_gz=True\n",")\n","# rf1 = pp.make_block_pulse(flip_angle=90 * np.pi / 180, duration=1e-3, system=system)\n","\n","# Define other gradients and ADC events\n","gx = pp.make_trapezoid(channel='x', flat_area=Nread / fov, flat_time=5e-3, system=system)\n","gy = pp.make_trapezoid(channel='y', flat_area=Nread / fov, flat_time=5e-3, system=system)\n","\n","gx_pre = pp.make_trapezoid(channel='x', area=-gx.area / 2, duration=1e-3, system=system)\n","gy_pre = pp.make_trapezoid(channel='y', area=-gy.area / 2, duration=1e-3, system=system)\n","\n","os = 1\n","adc = pp.make_adc(num_samples=Nread * os, duration=5e-3, phase_offset=0 * np.pi / 180, delay=gx.rise_time, system=system)\n","\n","rf_phase = 180\n","rf_inc = 180\n","\n","# ======\n","# CONSTRUCT SEQUENCE\n","# ======\n","sdel = 1e-0\n","\n","rf0, _, _ = pp.make_sinc_pulse(\n"," flip_angle=6 / 2 * np.pi / 180, duration=1e-3,\n"," slice_thickness=slice_thickness, apodization=0.5, time_bw_product=4,\n"," system=system, return_gz=True\n",")\n","seq.add_block(rf0)\n","seq.add_block(pp.make_delay(3e-3))\n","\n","for ii in range(-Nphase // 2, Nphase // 2): # e.g. -64:63\n"," rf1.phase_offset = rf_phase / 180 * np.pi # set current rf phase\n","\n"," adc.phase_offset = rf_phase / 180 * np.pi # follow with ADC\n"," # increment additional pahse\n"," rf_phase = divmod(rf_phase + rf_inc, 360.0)[1]\n","\n"," seq.add_block(rf1)\n","\n"," gx = pp.make_trapezoid(channel='x', flat_area=-Nread * np.sin(ii / Nphase * np.pi) / fov + 1e-9, flat_time=5e-3, system=system)\n"," gy = pp.make_trapezoid(channel='y', flat_area=Nread * np.cos(ii / Nphase * np.pi) / fov + 1e-9, flat_time=5e-3, system=system)\n","\n"," gx_pre = pp.make_trapezoid(channel='x', area=-gx.area / 2, duration=1e-3, system=system)\n"," gy_pre = pp.make_trapezoid(channel='y', area=-gy.area / 2, duration=1e-3, system=system)\n","\n"," seq.add_block(gx_pre, gy_pre)\n"," seq.add_block(adc, gx, gy)\n"," # seq.add_block(adc,gx,gy)\n"," seq.add_block(gx_pre, gy_pre)\n"," # seq.add_block(make_delay(10))\n","\n","\n","# %% S3. CHECK, PLOT and WRITE the sequence as .seq\n","# Check whether the timing of the sequence is correct\n","ok, error_report = seq.check_timing()\n","if ok:\n"," print('Timing check passed successfully')\n","else:\n"," print('Timing check failed. Error listing follows:')\n"," [print(e) for e in error_report]\n","\n","# PLOT sequence\n","sp_adc, t_adc = util.pulseq_plot(seq, clear=False)\n","\n","# Prepare the sequence output for the scanner\n","seq.set_definition('FOV', [fov, fov, slice_thickness])\n","seq.set_definition('Name', 'gre')\n","seq.write(experiment_id + '.seq')\n","\n","\n","# %% S4: SETUP SPIN SYSTEM/object on which we can run the MR sequence external.seq from above\n","\n","if 1:\n"," # (i) load a phantom object from file\n"," obj_p = mr0.VoxelGridPhantom.load_mat('numerical_brain_cropped.mat')\n"," obj_p = obj_p.interpolate(sz[0], sz[1], 1)\n"," # Manipulate loaded data\n"," obj_p.T2dash[:] = 30e-3\n"," obj_p.D *= 0\n"," obj_p.B0 *= 0\n","else:\n"," # or (ii) set phantom manually to a pixel phantom. Coordinate system is [-0.5, 0.5]^3\n"," obj_p = mr0.CustomVoxelPhantom(\n"," pos=[[-0.4, -0.4, 0], [-0.4, -0.2, 0], [-0.3, -0.2, 0], [-0.2, -0.2, 0], [-0.1, -0.2, 0]],\n"," PD=[1.0, 1.0, 0.5, 0.5, 0.5],\n"," T1=1.0,\n"," T2=0.1,\n"," T2dash=0.1,\n"," D=0.0,\n"," voxel_size=0.1,\n"," voxel_shape=\"box\"\n"," )\n","\n","obj_p.plot()\n","# Convert Phantom into simulation data\n","obj_p = obj_p.build()\n","\n","\n","# %% S5:. SIMULATE the external.seq file and add acquired signal to ADC plot\n","\n","# Read in the sequence\n","seq0 = mr0.Sequence.import_file(experiment_id + '.seq')\n","seq0.plot_kspace_trajectory()\n","kspace_loc = seq0.get_kspace()\n","# Simulate the sequence\n","graph = mr0.compute_graph(seq0, obj_p, 200, 1e-3)\n","signal = mr0.execute_graph(graph, seq0, obj_p, print_progress=False)\n","\n","# PLOT sequence with signal in the ADC subplot\n","sp_adc, t_adc = util.pulseq_plot(seq, clear=True, signal=signal.numpy())\n","\n","kspace_adc = torch.reshape((signal), (Nphase, Nread)).clone().t()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"TDylBKLH6smB"},"outputs":[],"source":["print('kspace_loc shape: ', kspace_loc.shape)\n","print('kspace_adc shape: ', kspace_adc.shape)\n","\n","f, ax = plt.subplots(1, 1, figsize=(6, 6))\n","ax.imshow(np.log10(abs(kspace_adc)), cmap='gray')\n","ax.set_xlabel('radial spoke index')\n","ax.set_ylabel('readout')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"0AXGa84z8ncu"},"outputs":[],"source":["traj = torch.reshape(kspace_loc, (Nphase, Nread, kspace_loc.shape[-1]))\n","traj = traj[..., :2]\n","\n","traj_scale = traj/Nread * 2\n","traj_convert = torch.reshape(traj_scale, (-1, 2)).transpose(1, 0)\n","\n","print('traj_scale shape: ', traj_scale.shape)\n","print('traj_convert shape: ', traj_convert.shape)\n","\n","kspace_res = torch.view_as_real(kspace_adc.transpose(1, 0))\n","kspace_res = torch.reshape(kspace_res, (1, 1, -1, 2))\n","\n","print('kspace_res shape: ', kspace_res.shape)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"QfBq01JY6gM8"},"outputs":[],"source":["import scipy.interpolate\n","grid = kspace_loc[:, :2]\n","Nx = sz[0]\n","Ny = sz[1]\n","\n","X, Y = np.meshgrid(np.linspace(0, Nx - 1, Nx) - Nx / 2,\n"," np.linspace(0, Ny - 1, Ny) - Ny / 2)\n","grid = np.double(grid.numpy())\n","grid[np.abs(grid) < 1e-3] = 0\n","\n","# plot every 8 radial spokes\n","f, ax = plt.subplots(1, 1, figsize=(9, 9))\n","ax.plot(traj[::8, :Nx, 0].ravel(), traj[::8, :Nx, 1].ravel(), 'rx', markersize=3)\n","ax.plot(X, Y, 'k.', markersize=2)\n","plt.show()\n","\n","print(np.amin(grid[:, 0]), np.amax(grid[:, 0]))\n","print(np.amin(grid[:, 1]), np.amax(grid[:, 1]))"]},{"cell_type":"markdown","metadata":{"id":"AM6OLNjjtFpF"},"source":["NUFFT recon with density compensation\n","\n"," * Zhengguo Tan <zhengguo.tan@gmail.com>\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"l7fEtzVbK0NO"},"outputs":[],"source":["import torchkbnufft as tkbn\n","\n","# compute density compensation function\n","dcf = (traj_convert[0, ...]**2 + traj_convert[1, ...]**2)**0.5\n","dcf = dcf.reshape(1, -1).repeat(2, 1).transpose(1, 0)\n","\n","print('dcf shape: ', dcf.shape)\n","\n","\n","img_shape = [Nread] * 2\n","\n","\n","# define nufft adjoint operator\n","nufft_adj = tkbn.KbNufftAdjoint(im_size=img_shape)\n","\n","img_dcf_tensor = nufft_adj(kspace_res * dcf, traj_convert)\n","img_tensor = nufft_adj(kspace_res, traj_convert)\n","\n","print('nufft_adj -> image shape: ', img_tensor.shape)\n","\n","\n","# define nufft forward operator\n","nufft_fwd = tkbn.KbNufft(im_size=img_shape)\n","\n","kspace_fwd = nufft_fwd(img_tensor, traj_convert)\n","\n","kspace_fwd_res = torch.reshape(kspace_fwd, (1, 1, Nphase, Nread, 2))\n","\n","ksp_cplx = torch.view_as_complex(kspace_fwd_res).transpose(-1, -2).cpu().detach().numpy()\n","\n","print('nufft_fwd -> kspace shape: ', kspace_fwd.shape)\n","\n","img = img_dcf_tensor.cpu().detach().numpy()\n","\n","img_cplx = img[..., 0] + 1j * img[..., 1]\n","img_cplx = np.flip(np.swapaxes(img_cplx, -1, -2), -2)\n","\n","f, ax = plt.subplots(1, 2, figsize=(12, 6))\n","ax[0].imshow(abs(np.squeeze(img_cplx)), cmap='gray')\n","ax[1].imshow(np.log10(abs(np.squeeze(ksp_cplx))), cmap='gray')\n","plt.show()"]},{"cell_type":"markdown","metadata":{"id":"A0WymLfYtJ8T"},"source":["Compressed sensing recon for radial sampling\n","\n"," * Zhengguo Tan <zhengguo.tan@gmail.com>\n"," "]},{"cell_type":"code","execution_count":null,"metadata":{"id":"iAyDfZWttJOY"},"outputs":[],"source":["def soft_thresh(input, lamda):\n","\n"," abs_input = abs(input)\n","\n"," sign = np.true_divide(input, abs_input,\n"," out=np.zeros_like(input), where=abs_input!=0)\n","\n"," magn = abs_input - lamda\n"," magn = (abs(magn) + magn) / 2\n","\n"," return magn * sign\n","\n","\n","def prox_wav(input, lamda):\n","\n"," if torch.is_tensor(input):\n"," input = input.cpu().detach().numpy()\n","\n"," # pywt outputs numpy arrays\n"," cA, (cH, cV, cD) = pywt.dwt2(input, 'db4')\n","\n"," cA_t = soft_thresh(cA, lamda)\n"," cH_t = soft_thresh(cH, lamda)\n"," cV_t = soft_thresh(cV, lamda)\n"," cD_t = soft_thresh(cD, lamda)\n","\n"," wav_coef = cA_t, (cH_t, cV_t, cD_t)\n","\n"," output = pywt.idwt2(wav_coef, 'db4')\n","\n"," return torch.tensor(output)\n","\n","\n","# compute maximal eigenvalue:\n","x = torch.randn(size=img_tensor.shape, dtype=img_tensor.dtype)\n","for n in range(30):\n"," y = nufft_adj(nufft_fwd(x, traj_convert), traj_convert)\n"," max_eig = torch.linalg.norm(y).ravel()\n"," x = y / max_eig\n","\n"," print(max_eig)\n","\n","\n","# Gradient method\n","Niter = 200\n","\n","alpha = (1 / max_eig).cpu().detach().numpy().item()\n","lamda = 0.001\n","\n","x = torch.zeros_like(img_tensor)\n","\n","for n in range(Niter):\n","\n"," x_old = x.clone()\n","\n"," r = nufft_fwd(x, traj_convert) - kspace_res\n"," g = nufft_adj(r, traj_convert)\n","\n"," x = prox_wav(x - alpha * g, alpha * lamda)\n","\n","\n"," resid = (torch.linalg.norm(x - x_old).ravel()).ravel()\n","\n"," print('> iter ' + str(n).zfill(4) + ' residuum ' + str(resid[0]))"]},{"cell_type":"markdown","metadata":{"id":"ISvqSJv1qNr1"},"source":["Plot results"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"S7_P7m5CmUDk"},"outputs":[],"source":["recon = torch.view_as_complex(x).cpu().detach().numpy()\n","recon = np.flip(np.swapaxes(recon, -1, -2), -2)\n","\n","R1 = abs(np.squeeze(img_cplx))\n","R2 = abs(np.squeeze(recon))\n","\n","f, ax = plt.subplots(1, 2, figsize=(12, 6))\n","ax[0].imshow(R1, cmap='gray', vmin=0) # [32:96, 32:96]\n","ax[0].set_title('NUFFT')\n","\n","ax[1].imshow(R2, cmap='gray', vmin=0)\n","ax[1].set_title('Compressed Sensing')\n","plt.show()"]}],"metadata":{"colab":{"provenance":[{"file_id":"1uTk3lc-O3xZS-rLDSkOLEYVV1rM0UTDJ","timestamp":1676904915190},{"file_id":"1lnFKubthQBxkz19cY7ScS-S0Hj9vHjEj","timestamp":1676708491940}]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.3"}},"nbformat":4,"nbformat_minor":0}
|