Skip to content

Commit 17b98f8

Browse files
authored
Merge branch 'main' into main
2 parents 1f6da96 + 6c8a829 commit 17b98f8

22 files changed

+5954
-3605
lines changed

.github/workflows/website.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,20 @@ jobs:
4646
with:
4747
name: 'Data'
4848

49+
- name: Run the test that generates the plots report.
50+
run: |
51+
pytest tests/IVIMmodels/unit_tests/test_ivim_fit.py --json-report
52+
mv .report.json utilities/
53+
python utilities/report-summary.py .report.json report-summary.json
54+
55+
- name: 'Filter and compress results file.'
56+
run: python utilities/reduce_output_size.py test_output.csv test_output.csv.gz
57+
4958
- name: move data to the dashboard folder
5059
run: |
51-
mv test_output.csv website/dashboard
60+
mv test_output.csv.gz website/dashboard
61+
mv report-summary.json website/dashboard
62+
5263
5364
- name: Build documentation
5465
run: |

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ __pycache__/
1010
*.raw
1111
bvals.txt
1212
download
13+
.Introduction_to_TF24_IVIM-MRI_CodeCollection_github_and_IVIM_Analysis_using_Python.ipynb
14+
.ipynb_checkpoints
1315
md5sums.txt
1416
*.gz
1517
*.zip
@@ -22,3 +24,5 @@ md5sums.txt
2224
nosetests.xml
2325
coverage.xml
2426
*.pyc
27+
phantoms/MR_XCAT_qMRI/*.json
28+
phantoms/MR_XCAT_qMRI/*.txt

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The ISMRM Open Science Initiative for Perfusion Imaging (OSIPI) is an initiative
66
77
This **IVIM code collection** code library is maintained by OSIPI [Taskforce 2.4](https://www.osipi.org/task-force-2-4/) (*currently not available*) and aims to collect, test and share open-source code related to intravoxel incoherent motion (IVIM) analysis of diffusion encoded MRI data to be used in research and software development. Code contributions can include any code related IVIM analysis (denoising, motion correction, model fitting, etc.), but at an initial phase, development of tests and other features of the repository will predominantly focus on fitting algorithms. A goal of the IVIM OSIPI task force is to develop a fully tested and harmonized code library, building upon the contributions obtained through this initiative. Documentation and analysis are available on the [OSIPI TF2.4](https://osipi.github.io/TF2.4_IVIM-MRI_CodeCollection/).
88

9+
We have some useful tools and further documentation on https://osipi.github.io/TF2.4_IVIM-MRI_CodeCollection/.
10+
911
## How to contribute
1012

1113
If you would like to get involve in OSIPI and work within the task force, please email the contacts listed on our website.
@@ -17,6 +19,9 @@ If you would like to contribute with code, please follow the instructions below:
1719
* [Guidelines for IVIM code contribution](doc/guidelines_for_contributions.md)
1820
* [Guidelines to creating a test file](doc/creating_test.md)
1921

22+
If you would like to use code from the repository and/or are new to Github or IVIM, please see the jupyter notebook below:
23+
* [Introduction to TF2.4_IVIM-MRI_CodeCollection github and IVIM Analysis using Python](doc/Introduction_to_TF24_IVIM-MRI_CodeCollection_github_and_IVIM_Analysis_using_Python.ipynb)
24+
2025
## Repository Organization
2126

2227
The repository is organized in four main folders along with configuration files for automated testing.
@@ -32,4 +37,4 @@ The **utils** folder contains various helpful tools.
3237
## View Testing Reports
3338
[![Unit tests](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/unit_test.yml/badge.svg?branch=main)](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/unit_test.yml)
3439
[![Algorithm Analysis](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/analysis.yml/badge.svg?branch=main)](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/analysis.yml)
35-
[![Build & Deploy Documentation](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/documentation.yml/badge.svg?branch=main)](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/documentation.yml)
40+
[![Build & Deploy Website](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/website.yml/badge.svg?branch=main)](https://github.com/OSIPI/TF2.4_IVIM-MRI_CodeCollection/actions/workflows/website.yml)

doc/Introduction_to_TF24_IVIM-MRI_CodeCollection_github_and_IVIM_Analysis_using_Python.ipynb

Lines changed: 1107 additions & 0 deletions
Large diffs are not rendered by default.

phantoms/MR_XCAT_qMRI/sim_ivim_sig.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# code adapted from MAtlab code by Eric Schrauben: https://github.com/schrau24/XCAT-ERIC
1212
# This code generates a 4D IVIM phantom as nifti file
1313

14-
def phantom(bvalue, noise, TR=3000, TE=40, motion=False, rician=False, interleaved=False):
14+
def phantom(bvalue, noise, TR=3000, TE=40, motion=False, rician=False, interleaved=False,T1T2=True):
1515
np.random.seed(42)
1616
if motion:
1717
states = range(1,21)
@@ -23,10 +23,10 @@ def phantom(bvalue, noise, TR=3000, TE=40, motion=False, rician=False, interleav
2323

2424
# Access the variables in the loaded .mat file
2525
XCAT = mat_data['IMG']
26-
XCAT = XCAT[0:-1:2,0:-1:2,10:160:4]
26+
XCAT = XCAT[-1:0:-2,-1:0:-2,10:160:4]
2727

2828
D, f, Ds = contrast_curve_calc()
29-
S, Dim, fim, Dpim, legend = XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds)
29+
S, Dim, fim, Dpim, legend = XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds,T1T2=T1T2)
3030
if state == 1:
3131
Dim_out = Dim
3232
fim_out = fim
@@ -95,15 +95,19 @@ def contrast_curve_calc():
9595
D[8] = 3e-3 # 8 Blood ra
9696
D[10] = 1.37e-3 # 8 Muscle: 10.3389/fresc.2022.910068
9797
D[13] = 1.5e-3 # 13 liver: Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
98+
D[14] = 3.0e-3 # 13 Galbladder
9899
D[17] = 1.67e-3 # 17 esophagus : Huang et al. doi: 10.1259/bjr.20170421
99100
D[18] = 1.67e-3 # 18 esophagus cont : Huang et al. doi: 10.1259/bjr.20170421
100101
D[20] = 1.5e-3 # 20 stomach wall: Li et al. doi: 10.3389/fonc.2022.821586
102+
D[21] = 3.0e-3 # 21 stomach content
101103
D[22] = 1.3e-3 # 22 Pancreas (from literature)
102104
D[23] = 2.12e-3 # 23 right kydney cortex : van Baalen et al. Doi: jmri.25519
103105
D[24] = 2.09e-3 # 23 right kydney medulla : van Baalen et al. Doi: jmri.25519
104106
D[25] = 2.12e-3 # 23 left kydney cortex : van Baalen et al. Doi: jmri.25519
105107
D[26] = 2.09e-3 # 23 left kydney medulla : van Baalen et al. Doi: jmri.25519
106108
D[30] = 1.3e-3 # 30 spleen : Taimouri et al. Doi: 10.1118/1.4915495
109+
D[34] = 4.1e-4 # 34 spinal cord :doi: 10.3389/fonc.2022.961473
110+
D[35] = 0.43e-3 # 35 Bone marrow : https://pubmed.ncbi.nlm.nih.gov/30194746/
107111
D[36] = 3e-3 # 36 artery
108112
D[37] = 3e-3 # 37 vein
109113
D[40] = 1.31e-3 # 40 asc lower intestine : Hai-Jing et al. doi: 10.1097/RCT.0000000000000926
@@ -124,15 +128,19 @@ def contrast_curve_calc():
124128
f[8] = 1.00 # 8 Blood ra
125129
f[10] = 0.10 # 8 Muscle: 10.3389/fresc.2022.910068
126130
f[13] = 0.11 # 13 liver : Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
131+
f[14] = 0 # 13 Gal
127132
f[17] = 0.32 # 17 esophagus : Huang et al. doi: 10.1259/bjr.20170421
128133
f[18] = 0.32 # 18 esophagus cont : Huang et al. doi: 10.1259/bjr.20170421
129134
f[20] = 0.3 # 20 stomach wall: Li et al. doi: 10.3389/fonc.2022.821586
135+
f[21] = 0.0 # 20 stomach content
130136
f[22] = 0.15 # 22 Pancreas (from literature)
131137
f[23] = 0.097 # 23 right kydney cortex : van Baalen et al. Doi: jmri.25519
132138
f[24] = 0.158 # 23 right kydney medulla : van Baalen et al. Doi: jmri.25519
133139
f[25] = 0.097 # 23 left kydney cortex : van Baalen et al. Doi: jmri.25519
134140
f[26] = 0.158 # 23 left kydney medulla : van Baalen et al. Doi: jmri.25519
135141
f[30] = 0.2 # 30 spleen : Taimouri et al. Doi: 10.1118/1.4915495
142+
f[34] = 0.178 # 34 spinal cord :doi: 10.3389/fonc.2022.961473
143+
f[35] = 0.145 # 35 Bone marrow : https://pubmed.ncbi.nlm.nih.gov/30194746/
136144
f[36] = 1.0 # 36 artery
137145
f[37] = 1.0 # 37 vein
138146
f[40] = 0.69 # 40 asc lower intestine : Hai-Jing et al. doi: 10.1097/RCT.0000000000000926
@@ -153,15 +161,19 @@ def contrast_curve_calc():
153161
Ds[8] = 0.1 # 8 Blood ra
154162
Ds[10] = 0.0263 # 8 Muscle: 10.3389/fresc.2022.910068
155163
Ds[13] = 0.1 # 13 liver: Delattre et al. doi: 10.1097/RLI.0b013e31826ef901
164+
Ds[14] = 0.1 # 14 Gal
156165
Ds[17] = 0.03 # 17 esophagus : Huang et al. doi: 10.1259/bjr.20170421
157166
Ds[18] = 0.03 # 18 esophagus cont : Huang et al. doi: 10.1259/bjr.20170421
158167
Ds[20] = 0.012 # 20 stomach wall: Li et al. doi: 10.3389/fonc.2022.821586
168+
Ds[21] = 0.0 # 20 stomach content
159169
Ds[22] = 0.01 # 22 Pancreas (from literature)
160170
Ds[23] = 0.02 # 23 right kydney cortex : van Baalen et al. Doi: jmri.25519
161171
Ds[24] = 0.019 # 23 right kydney medulla : van Baalen et al. Doi: jmri.25519
162172
Ds[25] = 0.02 # 23 left kydney cortex : van Baalen et al. Doi: jmri.25519
163173
Ds[26] = 0.019 # 23 left kydney medulla : van Baalen et al. Doi: jmri.25519
164174
Ds[30] = 0.03 # 30 spleen : Taimouri et al. Doi: 10.1118/1.4915495
175+
Ds[34] = 0.0289 # 34 spinal cord :doi: 10.3389/fonc.2022.961473
176+
Ds[35] = 0.05 # 35 Bone marrow :
165177
Ds[36] = 0.1 # 36 artery
166178
Ds[37] = 0.1 # 37 vein
167179
Ds[40] = 0.029 # 40 asc lower intestine : Hai-Jing et al. doi: 10.1097/RCT.0000000000000926
@@ -175,7 +187,7 @@ def contrast_curve_calc():
175187
return D, f, Ds
176188

177189

178-
def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds, b0=3, ivim_cont = True):
190+
def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds, b0=3, ivim_cont = True, T1T2=True):
179191
###########################################################################################
180192
# This script converts XCAT tissue values to MR contrast based on the SSFP signal equation.
181193
# Christopher W. Roy 2018-12-04 # fetal.xcmr@gmail.com
@@ -285,7 +297,7 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds, b0=3, ivim_cont = True):
285297
Tissue[19] = [1045.5, 37.3, 1201, 44]
286298
Tissue[20] = [981.5, 36, 1232.9, 37.20]
287299
#Tissue[20] = [981.5, 36, 1232.9, 37.20]
288-
Tissue[21] = [0, 0, 0, 0]
300+
Tissue[21] = [2500, 1250, 4000, 2000]
289301
Tissue[22] = [584, 46, 725, 43]
290302
Tissue[23] = [828, 71, 1168, 66]
291303
Tissue[24] = [1412, 85, 1545, 81]
@@ -351,7 +363,6 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds, b0=3, ivim_cont = True):
351363
else:
352364
T1 = Tissue[iTissue, 2]
353365
T2 = Tissue[iTissue, 3]
354-
355366
if ivim_cont and not np.isnan([D[iTissue], f[iTissue], Ds[iTissue]]).any():
356367
# note we are assuming blood fraction has the same T1 as tissue fraction here for simplicity. Can be changed in future.
357368
Dtemp=D[iTissue]
@@ -362,8 +373,11 @@ def XCAT_to_MR_DCE(XCAT, TR, TE, bvalue, D, f, Ds, b0=3, ivim_cont = True):
362373
ftemp=np.random.rand(1)*0.5
363374
Dstemp=5e-3+np.random.rand(1)*1e-1
364375
S0 = ivim(bvalue,Dtemp,ftemp,Dstemp)
365-
if T1 > 0 or T2 > 0:
366-
MR = MR + np.tile(np.expand_dims(XCAT == iTissue,3),len(S0)) * S0 * (1 - 2 * np.exp(-(TR - TE / 2) / T1) + np.exp(-TR / T1)) * np.exp(-TE / T2)
376+
if T1T2:
377+
if T1 > 0 or T2 > 0:
378+
MR = MR + np.tile(np.expand_dims(XCAT == iTissue,3),len(S0)) * S0 * (1 - 2 * np.exp(-(TR - TE / 2) / T1) + np.exp(-TR / T1)) * np.exp(-TE / T2)
379+
else:
380+
MR = MR + np.tile(np.expand_dims(XCAT == iTissue,3),len(S0)) * S0
367381
Dim = Dim + (XCAT == iTissue) * Dtemp
368382
fim = fim + (XCAT == iTissue) * ftemp
369383
Dpim = Dpim + (XCAT == iTissue) * Dstemp
@@ -403,6 +417,7 @@ def parse_bvalues_file(file_path):
403417
parser.add_argument("-n", "--noise", type=float, default=0.0005, help="Noise")
404418
parser.add_argument("-m", "--motion", action="store_true", help="Motion flag")
405419
parser.add_argument("-i", "--interleaved", action="store_true", help="Interleaved flag")
420+
parser.add_argument("-u", "--T1T2", action="store_true", help="weight signal with T1T2") # note, set this to zero when generating test data for unit testing!
406421
args = parser.parse_args()
407422

408423
if args.bvalues_file and args.bvalue:
@@ -420,14 +435,15 @@ def parse_bvalues_file(file_path):
420435
noise = args.noise
421436
motion = args.motion
422437
interleaved = args.interleaved
438+
T1T2 = args.T1T2
423439
download_data()
424440
for key, bvalue in bvalues.items():
425441
bvalue = np.array(bvalue)
426-
sig, XCAT, Dim, fim, Dpim, legend = phantom(bvalue, noise, motion=motion, interleaved=interleaved)
442+
sig, XCAT, Dim, fim, Dpim, legend = phantom(bvalue, noise, motion=motion, interleaved=interleaved,T1T2=T1T2)
427443
# sig = np.flip(sig,axis=0)
428444
# sig = np.flip(sig,axis=1)
429445
res=np.eye(4)
430-
res[2]=2
446+
res[2,2]=2
431447

432448
voxel_selector_fraction = 0.5
433449
D, f, Ds = contrast_curve_calc()

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ tqdm
1414
pandas
1515
sphinx
1616
sphinx_rtd_theme
17+
pytest-json-report

src/wrappers/OsipiBase.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from scipy.stats import norm
44
import pathlib
55
import sys
6+
from tqdm import tqdm
67

78
class OsipiBase:
89
"""The base class for OSIPI IVIM fitting"""
@@ -47,7 +48,7 @@ def initialize(**kwargs):
4748
pass
4849

4950
#def osipi_fit(self, data=None, bvalues=None, thresholds=None, bounds=None, initial_guess=None, **kwargs):
50-
def osipi_fit(self, data, bvalues, **kwargs):
51+
def osipi_fit(self, data, bvalues=None, **kwargs):
5152
"""Fits the data with the bvalues
5253
Returns [S0, f, Dstar, D]
5354
"""
@@ -68,7 +69,6 @@ def osipi_fit(self, data, bvalues, **kwargs):
6869
#kwargs["bvalues"] = use_bvalues
6970

7071
#args = [data, use_bvalues, use_thresholds]
71-
args = [data, use_bvalues]
7272
#if self.required_bounds or self.required_bounds_optional:
7373
#args.append(use_bounds)
7474
#if self.required_initial_guess or self.required_initial_guess_optional:
@@ -83,7 +83,13 @@ def osipi_fit(self, data, bvalues, **kwargs):
8383

8484
#args = [data, use_bvalues, use_initial_guess, use_bounds, use_thresholds]
8585
#args = [arg for arg in args if arg is not None]
86-
results = self.ivim_fit(*args, **kwargs)
86+
87+
# Assuming the last dimension of the data is the signal values of each b-value
88+
results = np.empty(list(data.shape[:-1])+[3]) # Create an array with the voxel dimensions + the ones required for the fit
89+
for ijk in tqdm(np.ndindex(data.shape[:-1]), total=np.prod(data.shape[:-1])):
90+
args = [data[ijk], use_bvalues]
91+
fit = list(self.ivim_fit(*args, **kwargs))
92+
results[ijk] = fit
8793

8894
#self.parameter_estimates = self.ivim_fit(data, bvalues)
8995
return results

tests/IVIMmodels/unit_tests/algorithms.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
},
101101
"ETP_SRI_LinearFitting": {
102102
"xfail_names": {
103-
"Vein": true
103+
"Blood RV": true
104104
},
105105
"options": {
106106
"thresholds": [500]

tests/IVIMmodels/unit_tests/compare.r

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
#Run like this:
44
#Rscript --vanilla tests/IVIMmodels/unit_tests/compare.r test_output.csv test_reference.csv reference_output.csv test_results.csv
55

6+
# If this script fails:
7+
# 1. Save the "Comparison" file from the run on Github, OR run this file directly
8+
# 2. Find the file producted "test_reference.csv" on Github, or whatever the "reference_file" variable was called
9+
# 3. This replaces "tests/IVIMmodels/unit_tests/reference_output.csv" in the repository
10+
# 4. For the algorithm "IAR_LU_modified_mix", replace the "f_f_alpha, Dp_f_alpha, D_f_alpha, f_t_alpha, Dp_t_alpha, D_t_alpha" columns with "0.01,0.01,0.01,0.0,0.0,0.0"
11+
612
args = commandArgs(trailingOnly=TRUE)
713
# Define file paths
814
test_file <- "test_output.csv"

0 commit comments

Comments
 (0)