Skip to content

Commit 38031b1

Browse files
author
IvanARashid
committed
Merge branch 'main' into testing/wrapper
2 parents 1cd67f1 + 973bf64 commit 38031b1

36 files changed

+1313
-118
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
.idea*
22
.github*
33
__pycache__/
4-
5-
4+
*.nii.gz
5+
bvals.txt
66
# Unit test / coverage reports
77
.tox/
88
.coverage

conftest.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import pytest
2+
import pathlib
3+
import json
4+
import csv
5+
# import datetime
6+
7+
8+
def pytest_addoption(parser):
9+
parser.addoption(
10+
"--SNR",
11+
default=[100],
12+
nargs="+",
13+
type=int,
14+
help="Evaluation test SNR",
15+
)
16+
parser.addoption(
17+
"--ricianNoise",
18+
default=False,
19+
type=bool,
20+
help="Use Rician noise, non-rician is gaussian",
21+
)
22+
parser.addoption(
23+
"--algorithmFile",
24+
default="tests/IVIMmodels/unit_tests/algorithms.json",
25+
type=str,
26+
help="Algorithm file name",
27+
)
28+
parser.addoption(
29+
"--dataFile",
30+
default="tests/IVIMmodels/unit_tests/generic.json",
31+
type=str,
32+
help="Default data file name",
33+
)
34+
parser.addoption(
35+
"--saveFileName",
36+
default="",
37+
type=str,
38+
help="Saved results file name",
39+
)
40+
parser.addoption(
41+
"--rtol",
42+
default=1,
43+
type=float,
44+
help="Relative tolerance",
45+
)
46+
parser.addoption(
47+
"--atol",
48+
default=1,
49+
type=float,
50+
help="Absolute tolerance",
51+
)
52+
parser.addoption(
53+
"--fitCount",
54+
default=10,
55+
type=int,
56+
help="Number of fits to perform on the same parameters",
57+
)
58+
parser.addoption(
59+
"--saveDurationFileName",
60+
default="",
61+
type=str,
62+
help="Saved duration results file name",
63+
)
64+
65+
66+
@pytest.fixture(scope="session")
67+
def save_file(request):
68+
filename = request.config.getoption("--saveFileName")
69+
if filename:
70+
current_folder = pathlib.Path.cwd()
71+
filename = current_folder / filename
72+
# print(filename)
73+
# filename.unlink(missing_ok=True)
74+
filename = filename.as_posix()
75+
with open(filename, "w") as csv_file:
76+
writer = csv.writer(csv_file, delimiter=',')
77+
writer.writerow(("Algorithm", "Region", "SNR", "index", "f", "Dp", "D", "f_fitted", "Dp_fitted", "D_fitted"))
78+
# writer.writerow(["", datetime.datetime.now()])
79+
return filename
80+
81+
@pytest.fixture(scope="session")
82+
def save_duration_file(request):
83+
filename = request.config.getoption("--saveDurationFileName")
84+
if filename:
85+
current_folder = pathlib.Path.cwd()
86+
filename = current_folder / filename
87+
# print(filename)
88+
# filename.unlink(missing_ok=True)
89+
filename = filename.as_posix()
90+
with open(filename, "w") as csv_file:
91+
writer = csv.writer(csv_file, delimiter=',')
92+
writer.writerow(("Algorithm", "Region", "SNR", "Duration [us]", "Count"))
93+
# writer.writerow(["", datetime.datetime.now()])
94+
return filename
95+
96+
@pytest.fixture(scope="session")
97+
def rtol(request):
98+
return request.config.getoption("--rtol")
99+
100+
101+
@pytest.fixture(scope="session")
102+
def atol(request):
103+
return request.config.getoption("--atol")
104+
105+
@pytest.fixture(scope="session")
106+
def fit_count(request):
107+
return request.config.getoption("--fitCount")
108+
109+
@pytest.fixture(scope="session")
110+
def rician_noise(request):
111+
return request.config.getoption("--ricianNoise")
112+
113+
114+
def pytest_generate_tests(metafunc):
115+
if "SNR" in metafunc.fixturenames:
116+
metafunc.parametrize("SNR", metafunc.config.getoption("SNR"))
117+
if "ivim_algorithm" in metafunc.fixturenames:
118+
algorithms = algorithm_list(metafunc.config.getoption("algorithmFile"))
119+
metafunc.parametrize("ivim_algorithm", algorithms)
120+
if "algorithm_file" in metafunc.fixturenames:
121+
metafunc.parametrize("algorithm_file", metafunc.config.getoption("algorithmFile"))
122+
if "ivim_data" in metafunc.fixturenames:
123+
data = data_list(metafunc.config.getoption("dataFile"))
124+
metafunc.parametrize("ivim_data", data)
125+
if "data_file" in metafunc.fixturenames:
126+
metafunc.parametrize("data_file", metafunc.config.getoption("dataFile"))
127+
128+
129+
def algorithm_list(filename):
130+
current_folder = pathlib.Path.cwd()
131+
algorithm_path = current_folder / filename
132+
with algorithm_path.open() as f:
133+
algorithm_information = json.load(f)
134+
return algorithm_information["algorithms"]
135+
136+
def data_list(filename):
137+
current_folder = pathlib.Path.cwd()
138+
data_path = current_folder / filename
139+
with data_path.open() as f:
140+
all_data = json.load(f)
141+
142+
bvals = all_data.pop('config')
143+
bvals = bvals['bvalues']
144+
for name, data in all_data.items():
145+
yield name, bvals, data

doc/code_contributions_record.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ IVIM,Fitting,Linear fit,Linear fit for D with extrapolation for f. Supports unit
1616
IVIM,Fitting,sIVIM fit,NLLS of the simplified IVIM model (sIVIM). Supports units in mm2/s and µm2/ms,IAR_LundUniversity,TF2.4_IVIM-MRI_CodeCollection/src/original/IAR_LundUniversity/ivim_fit_method_modified_sivim.py,Modified by Ivan A. Rashid,Lund University,IvimModelsIVIM,tba,tbd,
1717
IVIM,Fitting,Segmented NLLS fitting,MATLAB code,OJ_GU,TF2.4_IVIM-MRI_CodeCollection/src/original/OJ_GU/,Oscar Jalnefjord,University of Gothenburg,IVIM_seg,https://doi.org/10.1007/s10334-018-0697-5,tbd,
1818
IVIM,Fitting,Bayesian,MATLAB code,OJ_GU,TF2.4_IVIM-MRI_CodeCollection/src/original/OJ_GU/,Oscar Jalnefjord,University of Gothenburg,IVIM_bayes,https://doi.org/10.1002/mrm.26783,tbd,
19+
IVIM,Fitting,Segmented NLLS fitting,Specifically tailored algorithm for NLLS segmented fitting,OJ_GU,TF2.4_IVIM-MRI_CodeCollection/src/original/OJ_GU/,Oscar Jalnefjord,University of Gothenburg,seg,https://doi.org/10.1007/s10334-018-0697-5,tbd,
1920
IVIM,Fitting,Linear fit,Linear fit for D and D* and f. Intended to be extremely fast but not always accurate,ETP_SRI,TF2.4_IVIM-MRI_CodeCollection/src/original/ETP_SRI/LinearFitting.py,Eric Peterson,SRI International,,,tbd,

phantoms/MR_XCAT_qMRI/sim_ivim_sig.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# code adapted from MAtlab code by Eric Schrauben: https://github.com/schrau24/XCAT-ERIC
99
# This code generates a 4D IVIM phantom as nifti file
1010

11-
def phantom(bvalue, noise, TR=3000, TE=40, motion=False, rician=False, interleaved=False):
11+
def phantom(bvalue, noise, TR=8000, TE=80, motion=False, rician=False, interleaved=False):
1212
np.random.seed(42)
1313
if motion:
1414
states = range(1,21)
@@ -367,7 +367,8 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds, b0=3, ivim_cont = True):
367367
bvalue = np.array([0., 1, 2, 5, 10, 20, 30, 50, 75, 100, 150, 250, 350, 400, 550, 700, 850, 1000])
368368
noise = 0.0005
369369
motion = False
370-
sig, XCAT, Dim, fim, Dpim, legend = phantom(bvalue, noise, motion=motion, interleaved=False)
370+
interleaved = False
371+
sig, XCAT, Dim, fim, Dpim, legend = phantom(bvalue, noise, motion=motion, interleaved=interleaved)
371372
# sig = np.flip(sig,axis=0)
372373
# sig = np.flip(sig,axis=1)
373374
res=np.eye(4)
@@ -402,12 +403,16 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds, b0=3, ivim_cont = True):
402403
nifti_img = nib.Nifti1Image(sig, affine=res) # Replace affine if necessary
403404
# Save the NIfTI image to a file
404405
nifti_img.header.set_data_dtype(np.float64)
405-
if motion:
406+
if not motion:
407+
output_file = 'output.nii.gz' # Replace with your desired output file name
408+
elif interleaved:
406409
output_file = 'output_resp_int.nii.gz' # Replace with your desired output file name
407410
else:
408-
output_file = 'output.nii.gz' # Replace with your desired output file name
411+
output_file = 'output_resp.nii.gz' # Replace with your desired output file name
412+
409413
nib.save(nifti_img, output_file)
410414

415+
411416
nifti_img = nib.Nifti1Image(XCAT, affine=res) # Replace affine if necessary
412417
# Save the NIfTI image to a file
413418
output_file = 'output_xcat.nii.gz' # Replace with your desired output file name

phantoms/brain/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Code for generating ivim phantoms of the brain
2+
3+
Two sets of ground truth data are used for simulation:
4+
- Simulation in the diffusive regime (IVIM parameters D, f and D*): reference values from Ryghög et al. 2014 and b-values from Federau et al. 2012
5+
- Simulation in the ballistic regime (IVIM parameters D, f and vd): reference values and sequence parameters from Ahlgren et al. 2016
6+
7+
The segmentation used by the simulations is the ICBM 2009a nonlinear symmetric 3T atlas (https://nist.mni.mcgill.ca/icbm-152-nonlinear-atlases-2009/), the same as in e.g. ASLDRO (https://asldro.readthedocs.io/en/stable/).
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0 10 20 30 40 50 60 70 80 90 100 120 140 160 180 200 10 20 30 40 50 60 70 80 90 100 120 140 160 180 200
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.471 0.666 0.816 0.942 1.054 1.154 1.247 1.333 1.414 1.490 1.632 1.763 1.885 1.999 2.107
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import os
2+
import numpy as np
3+
4+
# Data from Ahlgren et al 2016 NMRinBiomed
5+
Delta = 7.5e-3 # 7.5 ms
6+
delta = 7.3e-3 # 7.3 ms
7+
8+
# For DDE sequence we have
9+
# b = 2y^2G^2d^2(D-d/3), c = 2yGdD => c = sqrt(b 2D^2/(D-d/3))
10+
bval_file = os.path.join(os.path.dirname(__file__),'ballistic.bval')
11+
b = np.loadtxt(bval_file)
12+
c = np.sqrt(b*2*Delta**2/(Delta-delta/3))
13+
c[1:(c.size-1)//2+1] = 0 # flow compensated => c = 0
14+
cval_file = bval_file.replace('bval','cval')
15+
np.savetxt(cval_file,c,fmt='%.3f',newline=' ')
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"D":[1.2e-3,0.98e-3,3e-3],
3+
"f":[0.0243,0.0164,0],
4+
"vd":[1.71,1.73,0],
5+
"Db":1.75e-3
6+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0 10 20 30 40 80 110 140 170 200 300 400 500 600 700 800 900

0 commit comments

Comments
 (0)