mrzerocore 0.3.1__tar.gz → 0.3.2__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.3.1 → MRzeroCore-0.3.2}/CHANGELOG.md +4 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/Cargo.lock +1 -1
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/Cargo.toml +1 -1
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/PKG-INFO +1 -1
- MRzeroCore-0.3.2/documentation/playground/templates/binary_masks.ipynb +85 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/sim_data.py +16 -4
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/voxel_grid_phantom.py +97 -24
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/util.py +71 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/.github/workflows/pypi_publish.yml +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/.gitignore +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/.readthedocs.yaml +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/EULA.txt +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/LICENSE +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/README.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/_config.yml +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/_toc.yml +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api/phantom.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api/reco.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api/sequence.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api/simulation/isochromat_sim.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api/simulation/pdg_sim.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api/simulation.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api/util.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/api.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/intro.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/logo.png +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/phantom_generation.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground/generated/seqs/FLASH.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground/quantified_brain.npz +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground/templates/generate.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground/templates/seqs.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground/templates/template_A.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/AdjDataUser2gB0_transversal_0.08moving_average.mat +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/flash.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/flash_DWI.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr00_FLASH_2D_ernstAngle_opt.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_CS_cartesian_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_CS_radial_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_DREAM_STE_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_DREAM_STID_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_DWI_GRE_2D_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_EPI_2D_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_FID_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_FLASH_2D_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_GRE_to_FLASH.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_RARE_2D_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_SE_CPMG_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_STE_3pulses_5echoes_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_bSSFP_2D_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_burst_TSE.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_Fit_T1.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_voxelNN_T1.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_pypulseq_exmpls_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_upload_seq.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/numerical_brain_cropped.mat +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/overview.md +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/ptx_phantom.p +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/pulseq_flash.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/pulseq_rf_shim.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/pulseq_sim_pTx.ipynb +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/seqs/flash pTx CP.seq +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/seqs/flash pTx EP.seq +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/seqs/flash pTx QM.seq +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/subject05.npz +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/requirements.txt +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/pyproject.toml +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/__init__.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/brainweb/__init__.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/brainweb/brainweb_data.json +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/brainweb/brainweb_data_sources.txt +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/brainweb/output/.gitkeep +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/custom_voxel_phantom.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/exporter.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/exporter_v2.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/helpers.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_exporter.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/__init__.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/adc.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/helpers.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulse.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/__init__.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/adc.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/block.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/definitons.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/gradient.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/helpers.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/rf.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/trap.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/spoiler.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/reconstruction.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/sequence.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/simulation/isochromat_sim.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/simulation/main_pass.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/simulation/pre_pass.py +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/src/lib.rs +0 -0
- {MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/src/pre_pass.rs +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
- 0.3.2
|
|
2
|
+
- Added tissue maps to phantom and plotting (#68)
|
|
3
|
+
- Fixed `.plot()`ting of 3D VoxelGridPhantom, added slice parameter
|
|
4
|
+
- Added `simulate_2D` to util for easy simulation of pypulseq sequences
|
|
1
5
|
- 0.3.1
|
|
2
6
|
- WIP .dsv support with pydisseqt 0.1.5
|
|
3
7
|
- changed type annotation in util.imshow for python 3.9 compatibility
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": null,
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"outputs": [],
|
|
8
|
+
"source": [
|
|
9
|
+
"import MRzeroCore as mr0\n",
|
|
10
|
+
"import matplotlib.pyplot as plt"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"cell_type": "markdown",
|
|
15
|
+
"metadata": {},
|
|
16
|
+
"source": [
|
|
17
|
+
"# Generate phantoms with tissue masks"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"cell_type": "code",
|
|
22
|
+
"execution_count": null,
|
|
23
|
+
"metadata": {},
|
|
24
|
+
"outputs": [],
|
|
25
|
+
"source": [
|
|
26
|
+
"\n",
|
|
27
|
+
"mr0.generate_brainweb_phantoms(\"bw_phantoms\" , \"3T\")"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"cell_type": "code",
|
|
32
|
+
"execution_count": null,
|
|
33
|
+
"metadata": {},
|
|
34
|
+
"outputs": [],
|
|
35
|
+
"source": [
|
|
36
|
+
"\n",
|
|
37
|
+
"phantom = mr0.VoxelGridPhantom.brainweb(\"bw_phantoms/subject04_3T.npz\")\n",
|
|
38
|
+
"\n",
|
|
39
|
+
"obj = phantom.interpolate(256, 256, 32).slices([16])\n",
|
|
40
|
+
"obj.plot(plot_masks=True)\n",
|
|
41
|
+
"phantom = obj.build()"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"cell_type": "code",
|
|
46
|
+
"execution_count": null,
|
|
47
|
+
"metadata": {},
|
|
48
|
+
"outputs": [],
|
|
49
|
+
"source": [
|
|
50
|
+
"\n",
|
|
51
|
+
"# get tissue masks from phantom\n",
|
|
52
|
+
"masks = phantom.tissue_masks\n",
|
|
53
|
+
"\n",
|
|
54
|
+
"#plot tissue masks\n",
|
|
55
|
+
"plt.figure()\n",
|
|
56
|
+
"for key, value in masks.items():\n",
|
|
57
|
+
" plt.imshow(value[:, :, 0])\n",
|
|
58
|
+
" plt.colorbar()\n",
|
|
59
|
+
" plt.title(key)\n",
|
|
60
|
+
" plt.show()"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"metadata": {
|
|
65
|
+
"kernelspec": {
|
|
66
|
+
"display_name": "mrzero_source",
|
|
67
|
+
"language": "python",
|
|
68
|
+
"name": "python3"
|
|
69
|
+
},
|
|
70
|
+
"language_info": {
|
|
71
|
+
"codemirror_mode": {
|
|
72
|
+
"name": "ipython",
|
|
73
|
+
"version": 3
|
|
74
|
+
},
|
|
75
|
+
"file_extension": ".py",
|
|
76
|
+
"mimetype": "text/x-python",
|
|
77
|
+
"name": "python",
|
|
78
|
+
"nbconvert_exporter": "python",
|
|
79
|
+
"pygments_lexer": "ipython3",
|
|
80
|
+
"version": "3.10.14"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"nbformat": 4,
|
|
84
|
+
"nbformat_minor": 2
|
|
85
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Callable, Any
|
|
2
|
+
from typing import Callable, Any, Optional, Dict
|
|
3
3
|
import torch
|
|
4
4
|
from numpy import pi
|
|
5
5
|
|
|
@@ -66,7 +66,8 @@ class SimData:
|
|
|
66
66
|
dephasing_func: Callable[[torch.Tensor, torch.Tensor], torch.Tensor],
|
|
67
67
|
recover_func: Callable[[SimData], Any] | None = None,
|
|
68
68
|
phantom_motion=None,
|
|
69
|
-
voxel_motion=None
|
|
69
|
+
voxel_motion=None,
|
|
70
|
+
tissue_masks: Optional[Dict[str,torch.Tensor]] = None,
|
|
70
71
|
) -> None:
|
|
71
72
|
"""Create a :class:`SimData` instance based on the given tensors.
|
|
72
73
|
|
|
@@ -94,6 +95,7 @@ class SimData:
|
|
|
94
95
|
self.D = D.clamp(min=1e-6)
|
|
95
96
|
self.B0 = B0.clone()
|
|
96
97
|
self.B1 = B1.clone()
|
|
98
|
+
self.tissue_masks = tissue_masks
|
|
97
99
|
self.coil_sens = coil_sens.clone()
|
|
98
100
|
self.size = size.clone()
|
|
99
101
|
self.voxel_pos = voxel_pos.clone()
|
|
@@ -126,7 +128,12 @@ class SimData:
|
|
|
126
128
|
self.dephasing_func,
|
|
127
129
|
self.recover_func,
|
|
128
130
|
self.phantom_motion,
|
|
129
|
-
self.voxel_motion
|
|
131
|
+
self.voxel_motion,
|
|
132
|
+
tissue_masks=(
|
|
133
|
+
{k: v.cuda() for k, v in self.tissue_masks.items()}
|
|
134
|
+
if self.tissue_masks is not None
|
|
135
|
+
else None
|
|
136
|
+
),
|
|
130
137
|
)
|
|
131
138
|
|
|
132
139
|
def cpu(self) -> SimData:
|
|
@@ -150,7 +157,12 @@ class SimData:
|
|
|
150
157
|
self.dephasing_func,
|
|
151
158
|
self.recover_func,
|
|
152
159
|
self.phantom_motion,
|
|
153
|
-
self.voxel_motion
|
|
160
|
+
self.voxel_motion,
|
|
161
|
+
tissue_masks=(
|
|
162
|
+
{k: v.cpu() for k, v in self.tissue_masks.items()}
|
|
163
|
+
if self.tissue_masks is not None
|
|
164
|
+
else None
|
|
165
|
+
),
|
|
154
166
|
)
|
|
155
167
|
|
|
156
168
|
@property
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Literal
|
|
2
|
+
from typing import Literal, Optional, Dict
|
|
3
3
|
from warnings import warn
|
|
4
4
|
from scipy import io
|
|
5
5
|
import numpy as np
|
|
@@ -84,6 +84,8 @@ class VoxelGridPhantom:
|
|
|
84
84
|
(coil_count, sx, sy, sz) tensor of coil sensitivities
|
|
85
85
|
size : torch.Tensor
|
|
86
86
|
Size of the data, in meters.
|
|
87
|
+
tissue_masks : Dict[str, torch.Tensor]
|
|
88
|
+
Segmentation masks for different tissues. The keys are the tissue names
|
|
87
89
|
"""
|
|
88
90
|
|
|
89
91
|
def __init__(
|
|
@@ -97,6 +99,7 @@ class VoxelGridPhantom:
|
|
|
97
99
|
B1: torch.Tensor,
|
|
98
100
|
coil_sens: torch.Tensor,
|
|
99
101
|
size: torch.Tensor,
|
|
102
|
+
tissue_masks: Optional[Dict[str,torch.Tensor]] = None,
|
|
100
103
|
) -> None:
|
|
101
104
|
"""Set the phantom attributes to the provided parameters.
|
|
102
105
|
|
|
@@ -110,6 +113,7 @@ class VoxelGridPhantom:
|
|
|
110
113
|
self.D = torch.as_tensor(D, dtype=torch.float32)
|
|
111
114
|
self.B0 = torch.as_tensor(B0, dtype=torch.float32)
|
|
112
115
|
self.B1 = torch.as_tensor(B1, dtype=torch.float32)
|
|
116
|
+
self.tissue_masks = tissue_masks
|
|
113
117
|
self.coil_sens = torch.as_tensor(coil_sens, dtype=torch.float32)
|
|
114
118
|
self.size = torch.as_tensor(size, dtype=torch.float32)
|
|
115
119
|
|
|
@@ -167,7 +171,8 @@ class VoxelGridPhantom:
|
|
|
167
171
|
voxel_pos,
|
|
168
172
|
torch.as_tensor(shape, device=self.PD.device) / 2 / self.size,
|
|
169
173
|
dephasing_func,
|
|
170
|
-
recover_func=lambda data: recover(mask, data)
|
|
174
|
+
recover_func=lambda data: recover(mask, data),
|
|
175
|
+
tissue_masks=self.tissue_masks
|
|
171
176
|
)
|
|
172
177
|
|
|
173
178
|
@classmethod
|
|
@@ -193,7 +198,12 @@ class VoxelGridPhantom:
|
|
|
193
198
|
size = torch.tensor(data['FOV'], dtype=torch.float)
|
|
194
199
|
except KeyError:
|
|
195
200
|
size = torch.tensor([0.192, 0.192, 0.192])
|
|
196
|
-
|
|
201
|
+
|
|
202
|
+
tissue_masks = {
|
|
203
|
+
key: torch.tensor(mask)
|
|
204
|
+
for key, mask in data.items()
|
|
205
|
+
if key.startswith("tissue_")
|
|
206
|
+
}
|
|
197
207
|
if B1.ndim == 3:
|
|
198
208
|
# Add coil-dimension
|
|
199
209
|
B1 = B1[None, ...]
|
|
@@ -201,6 +211,7 @@ class VoxelGridPhantom:
|
|
|
201
211
|
return cls(
|
|
202
212
|
PD, T1, T2, T2dash, D, B0, B1,
|
|
203
213
|
torch.ones(1, *PD.shape), size,
|
|
214
|
+
tissue_masks=tissue_masks
|
|
204
215
|
)
|
|
205
216
|
|
|
206
217
|
@classmethod
|
|
@@ -303,6 +314,9 @@ class VoxelGridPhantom:
|
|
|
303
314
|
select(self.B1).unsqueeze(0),
|
|
304
315
|
select(self.coil_sens).unsqueeze(0),
|
|
305
316
|
self.size.clone(),
|
|
317
|
+
tissue_masks={
|
|
318
|
+
key: mask[..., slices] for key, mask in self.tissue_masks.items()
|
|
319
|
+
},
|
|
306
320
|
)
|
|
307
321
|
|
|
308
322
|
def scale_fft(self, x: int, y: int, z: int) -> VoxelGridPhantom:
|
|
@@ -342,6 +356,7 @@ class VoxelGridPhantom:
|
|
|
342
356
|
scale(self.B1.squeeze()).unsqueeze(0),
|
|
343
357
|
scale(self.coil_sens.squeeze()).unsqueeze(0),
|
|
344
358
|
self.size.clone(),
|
|
359
|
+
tissue_masks= {key: scale(mask) for key, mask in self.tissue_masks.items()}
|
|
345
360
|
)
|
|
346
361
|
|
|
347
362
|
def interpolate(self, x: int, y: int, z: int) -> VoxelGridPhantom:
|
|
@@ -379,6 +394,18 @@ class VoxelGridPhantom:
|
|
|
379
394
|
output[i, ...] = resample(tensor[i, ...])
|
|
380
395
|
return output
|
|
381
396
|
|
|
397
|
+
def resample_masks(tensors: Dict) -> Dict:
|
|
398
|
+
output = {}
|
|
399
|
+
for key, mask in tensors.items():
|
|
400
|
+
# Interpolate the mask
|
|
401
|
+
interpolated_mask = torch.nn.functional.interpolate(
|
|
402
|
+
mask[None, None, ...].float(), size=(x, y, z), mode='area'
|
|
403
|
+
)[0, 0, ...]
|
|
404
|
+
# Store the result
|
|
405
|
+
output[key] = interpolated_mask
|
|
406
|
+
|
|
407
|
+
return output
|
|
408
|
+
|
|
382
409
|
return VoxelGridPhantom(
|
|
383
410
|
resample(self.PD),
|
|
384
411
|
resample(self.T1),
|
|
@@ -389,14 +416,32 @@ class VoxelGridPhantom:
|
|
|
389
416
|
resample_multicoil(self.B1),
|
|
390
417
|
resample_multicoil(self.coil_sens),
|
|
391
418
|
self.size.clone(),
|
|
419
|
+
tissue_masks=resample_masks(self.tissue_masks)
|
|
392
420
|
)
|
|
393
421
|
|
|
394
|
-
def plot(self) -> None:
|
|
395
|
-
"""
|
|
422
|
+
def plot(self, plot_masks=False, plot_slice="center") -> None:
|
|
423
|
+
"""
|
|
424
|
+
Print and plot all data stored in this phantom.
|
|
425
|
+
|
|
426
|
+
Parameters
|
|
427
|
+
----------
|
|
428
|
+
plot_masks : bool
|
|
429
|
+
Plot tissue masks stored in this phantom (assumes they exist)
|
|
430
|
+
slice : str | int
|
|
431
|
+
If int, the specified slice is plotted. "center" plots the center
|
|
432
|
+
slice and "all" plots all slices as a grid.
|
|
433
|
+
"""
|
|
396
434
|
print("VoxelGridPhantom")
|
|
397
435
|
print(f"size = {self.size}")
|
|
398
436
|
# Center slice
|
|
399
|
-
|
|
437
|
+
if plot_slice == "center":
|
|
438
|
+
s = self.PD.shape[2] // 2
|
|
439
|
+
elif plot_slice == "all":
|
|
440
|
+
s = slice(None)
|
|
441
|
+
elif plot_slice is int:
|
|
442
|
+
s = plot_slice
|
|
443
|
+
else:
|
|
444
|
+
raise ValueError("expected plot_slice to be 'all', 'center' or an integer")
|
|
400
445
|
# Warn if we only print a part of all data
|
|
401
446
|
if self.coil_sens.shape[0] > 1:
|
|
402
447
|
print(f"Plotting 1st of {self.coil_sens.shape[0]} coil sens maps")
|
|
@@ -405,40 +450,68 @@ class VoxelGridPhantom:
|
|
|
405
450
|
if self.PD.shape[2] > 1:
|
|
406
451
|
print(f"Plotting slice {s} / {self.PD.shape[2]}")
|
|
407
452
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
453
|
+
# Determine the number of subplots needed
|
|
454
|
+
num_plots = 9 # Base number of plots without masks
|
|
455
|
+
if plot_masks:
|
|
456
|
+
num_masks = len(self.tissue_masks)
|
|
457
|
+
num_plots += num_masks
|
|
458
|
+
|
|
459
|
+
# Calculate the grid size based on the number of plots
|
|
460
|
+
cols = 3
|
|
461
|
+
rows = int(np.ceil(num_plots / cols))
|
|
411
462
|
|
|
412
|
-
|
|
463
|
+
plt.figure(figsize=(12, rows * 3))
|
|
464
|
+
|
|
465
|
+
# Plot the basic maps
|
|
466
|
+
plt.subplot(rows, cols, 1)
|
|
467
|
+
plt.title("PD")
|
|
468
|
+
imshow(self.PD[:, :, s], vmin=0)
|
|
413
469
|
plt.colorbar()
|
|
414
|
-
|
|
470
|
+
|
|
471
|
+
plt.subplot(rows, cols, 2)
|
|
415
472
|
plt.title("T1")
|
|
416
|
-
imshow(self.T1, vmin=0)
|
|
473
|
+
imshow(self.T1[:, :, s], vmin=0)
|
|
417
474
|
plt.colorbar()
|
|
418
|
-
|
|
475
|
+
|
|
476
|
+
plt.subplot(rows, cols, 3)
|
|
419
477
|
plt.title("T2")
|
|
420
|
-
imshow(self.T2, vmin=0)
|
|
478
|
+
imshow(self.T2[:, :, s], vmin=0)
|
|
421
479
|
plt.colorbar()
|
|
422
|
-
|
|
480
|
+
|
|
481
|
+
plt.subplot(rows, cols, 4)
|
|
423
482
|
plt.title("T2'")
|
|
424
|
-
imshow(self.T2dash, vmin=0)
|
|
483
|
+
imshow(self.T2dash[:, :, s], vmin=0)
|
|
425
484
|
plt.colorbar()
|
|
426
|
-
|
|
485
|
+
|
|
486
|
+
plt.subplot(rows, cols, 5)
|
|
427
487
|
plt.title("D")
|
|
428
|
-
imshow(self.D, vmin=0)
|
|
488
|
+
imshow(self.D[:, :, s], vmin=0)
|
|
429
489
|
plt.colorbar()
|
|
430
|
-
|
|
490
|
+
|
|
491
|
+
plt.subplot(rows, cols, 7)
|
|
431
492
|
plt.title("B0")
|
|
432
|
-
imshow(self.B0)
|
|
493
|
+
imshow(self.B0[:, :, s])
|
|
433
494
|
plt.colorbar()
|
|
434
|
-
|
|
495
|
+
|
|
496
|
+
plt.subplot(rows, cols, 8)
|
|
435
497
|
plt.title("B1")
|
|
436
|
-
imshow(self.B1[0,
|
|
498
|
+
imshow(self.B1[0, :, :, s])
|
|
437
499
|
plt.colorbar()
|
|
438
|
-
|
|
500
|
+
|
|
501
|
+
plt.subplot(rows, cols, 9)
|
|
439
502
|
plt.title("coil sens")
|
|
440
|
-
imshow(self.coil_sens[0,
|
|
503
|
+
imshow(self.coil_sens[0, :, :, s], vmin=0)
|
|
441
504
|
plt.colorbar()
|
|
505
|
+
|
|
506
|
+
# Conditionally plot masks if plot_masks is True
|
|
507
|
+
if plot_masks:
|
|
508
|
+
for i, (key, mask) in enumerate(self.tissue_masks.items()):
|
|
509
|
+
plt.subplot(rows, cols, 10 + i)
|
|
510
|
+
plt.title(key)
|
|
511
|
+
imshow(mask)
|
|
512
|
+
plt.colorbar()
|
|
513
|
+
|
|
514
|
+
plt.tight_layout()
|
|
442
515
|
plt.show()
|
|
443
516
|
|
|
444
517
|
def plot3D(self, data2print: int = 0) -> None:
|
|
@@ -342,3 +342,74 @@ def imshow(data: Union[np.ndarray, torch.Tensor], *args, **kwargs):
|
|
|
342
342
|
data[x:x+tmp.shape[0], y:y+tmp.shape[1]] = tmp[:, :, i]
|
|
343
343
|
|
|
344
344
|
plt.imshow(data.T, *args, origin="lower", **kwargs)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def simulate_2d(seq, sim_size=None, noise_level=0, dB0=0, B0_scale=1, B0_polynomial=None):
|
|
348
|
+
# Copied and modified from https://github.com/pulseq/MR-Physics-with-Pulseq
|
|
349
|
+
import urllib
|
|
350
|
+
import MRzeroCore as mr0
|
|
351
|
+
# Download .mat file for phantom if necessary
|
|
352
|
+
sim_url = 'https://github.com/mzaiss/MRTwin_pulseq/raw/mr0-core/data/numerical_brain_cropped.mat'
|
|
353
|
+
sim_filename = os.path.basename(sim_url)
|
|
354
|
+
if not os.path.exists(sim_filename):
|
|
355
|
+
print(f'Downloading {sim_url}...')
|
|
356
|
+
urllib.request.urlretrieve(sim_url, sim_filename)
|
|
357
|
+
|
|
358
|
+
# If seq is not a str, assume it is a sequence object and write to a temporary sequence file
|
|
359
|
+
if not isinstance(seq, str):
|
|
360
|
+
seq.write('tmp.seq')
|
|
361
|
+
seq_filename = 'tmp.seq'
|
|
362
|
+
adc_samples = int(seq.adc_library.data[1][0])
|
|
363
|
+
else:
|
|
364
|
+
seq_filename = seq
|
|
365
|
+
tmp_seq = pp.Sequence()
|
|
366
|
+
tmp_seq.read(seq_filename)
|
|
367
|
+
adc_samples = int(tmp_seq.adc_library.data[1][0])
|
|
368
|
+
|
|
369
|
+
# Create phantom from .mat file
|
|
370
|
+
obj_p = mr0.VoxelGridPhantom.load_mat(sim_filename)
|
|
371
|
+
if sim_size is not None:
|
|
372
|
+
obj_p = obj_p.interpolate(sim_size[0], sim_size[1], 1)
|
|
373
|
+
|
|
374
|
+
# Manipulate loaded data
|
|
375
|
+
obj_p.B0 *= B0_scale
|
|
376
|
+
obj_p.B0 += dB0
|
|
377
|
+
|
|
378
|
+
if B0_polynomial is not None:
|
|
379
|
+
x,y = torch.meshgrid(torch.linspace(-1,1,obj_p.PD.shape[0]),torch.linspace(-1,1,obj_p.PD.shape[1]))
|
|
380
|
+
|
|
381
|
+
obj_p.B0 = B0_polynomial[0]
|
|
382
|
+
if len(B0_polynomial) > 1:
|
|
383
|
+
obj_p.B0 += x * B0_polynomial[1]
|
|
384
|
+
if len(B0_polynomial) > 2:
|
|
385
|
+
obj_p.B0 += y * B0_polynomial[2]
|
|
386
|
+
if len(B0_polynomial) > 3:
|
|
387
|
+
obj_p.B0 += x*x * B0_polynomial[3]
|
|
388
|
+
if len(B0_polynomial) > 4:
|
|
389
|
+
obj_p.B0 += y*y * B0_polynomial[4]
|
|
390
|
+
if len(B0_polynomial) > 5:
|
|
391
|
+
obj_p.B0 += x*y * B0_polynomial[5]
|
|
392
|
+
|
|
393
|
+
obj_p.B0 = obj_p.B0[:,:,None]
|
|
394
|
+
|
|
395
|
+
obj_p.D *= 0
|
|
396
|
+
|
|
397
|
+
# Convert Phantom into simulation data
|
|
398
|
+
obj_p = obj_p.build()
|
|
399
|
+
|
|
400
|
+
# MR zero simulation
|
|
401
|
+
seq0 = mr0.Sequence.import_file(seq_filename)
|
|
402
|
+
|
|
403
|
+
# Remove temporary sequence file
|
|
404
|
+
if not isinstance(seq, str):
|
|
405
|
+
os.unlink('tmp.seq')
|
|
406
|
+
|
|
407
|
+
# Simulate the sequence
|
|
408
|
+
graph = mr0.compute_graph(seq0, obj_p, 200, 1e-5)
|
|
409
|
+
signal = mr0.execute_graph(graph, seq0, obj_p)
|
|
410
|
+
|
|
411
|
+
# Add noise to the simulated data
|
|
412
|
+
if noise_level > 0:
|
|
413
|
+
signal += noise_level * torch.randn(*signal.shape, dtype=signal.dtype)
|
|
414
|
+
|
|
415
|
+
return signal
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_CS_cartesian_seq.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_STE_3pulses_5echoes_seq.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_Fit_T1.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/mr0_pypulseq_exmpls_seq.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/documentation/playground_mr0/numerical_brain_cropped.mat
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/phantom/brainweb/brainweb_data_sources.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/__init__.py
RENAMED
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/adc.py
RENAMED
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/block.py
RENAMED
|
File without changes
|
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/gradient.py
RENAMED
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/helpers.py
RENAMED
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/rf.py
RENAMED
|
File without changes
|
{MRzeroCore-0.3.1 → MRzeroCore-0.3.2}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/trap.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|