Skip to content

Commit a7e1da3

Browse files
author
IvanARashid
committed
Did some work on PV_MUMC. It now runs but the estimation errors are large. Needs investigation
1 parent b494bed commit a7e1da3

File tree

3 files changed

+101
-40
lines changed

3 files changed

+101
-40
lines changed

src/original/PV_MUMC/two_step_IVIM_fit.py

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -75,45 +75,48 @@ def fit_least_squares(bvalues, dw_data, IR=False, S0_output=False, fitS0=True,
7575
:return Dmv: scalar with Dmv of the specific voxel
7676
"""
7777

78-
try:
79-
def monofit(bvalues, Dpar):
80-
return np.exp(-bvalues * Dpar)
81-
82-
high_b = bvalues[bvalues >= cutoff]
83-
high_dw_data = dw_data[bvalues >= cutoff]
84-
boundspar = ([bounds[0][1]], [bounds[1][1]])
85-
params, _ = curve_fit(monofit, high_b, high_dw_data, p0=[(bounds[1][1]-bounds[0][1])/2], bounds=boundspar)
86-
Dpar1 = params[0]
78+
#try:
79+
def monofit(bvalues, Dpar):
80+
return np.exp(-bvalues * Dpar)
81+
82+
high_b = bvalues[bvalues >= cutoff]
83+
high_dw_data = dw_data[bvalues >= cutoff]
84+
boundspar = ([bounds[0][1]], [bounds[1][1]])
85+
params, _ = curve_fit(monofit, high_b, high_dw_data, p0=[(bounds[1][1]-bounds[0][1])/2], bounds=boundspar)
86+
Dpar = params[0]
8787

88-
if not fitS0:
89-
boundsupdated=([Dpar1 , bounds[0][2] , bounds[0][3] ],
90-
[Dpar1 , bounds[1][2] , bounds[1][3] ])
91-
params, _ = curve_fit(two_exp_noS0, bvalues, dw_data, p0=[Dpar1, (bounds[0][2]+bounds[1][2])/2, (bounds[0][3]+bounds[1][3])/2], bounds=boundsupdated)
92-
Dpar, Fmv, Dmv = params[0], params[1], params[2]
93-
#when the fraction of a compartment equals zero (or very very small), the corresponding diffusivity is non-existing (=NaN)
94-
if Fmv < 1e-4:
95-
Dmv = float("NaN")
88+
if not fitS0:
89+
boundsupdated=([Dpar1 , bounds[0][2] , bounds[0][3] ],
90+
[Dpar1 , bounds[1][2] , bounds[1][3] ])
91+
params, _ = curve_fit(two_exp_noS0, bvalues, dw_data, p0=[Dpar1, (bounds[0][2]+bounds[1][2])/2, (bounds[0][3]+bounds[1][3])/2], bounds=boundsupdated)
92+
Dpar1, Fmv, Dmv = params[0], params[1], params[2]
93+
#when the fraction of a compartment equals zero (or very very small), the corresponding diffusivity is non-existing (=NaN)
94+
if Fmv < 1e-4:
95+
Dmv = float("NaN")
96+
97+
else:
98+
#boundsupdated = ([bounds[0][0] , Dpar1 , bounds[0][2] , bounds[0][3] ],
99+
# [bounds[1][0] , Dpar1, bounds[1][2] , bounds[1][3] ])
100+
#params, _ = curve_fit(two_exp, bvalues, dw_data, p0=[1, Dpar1, (bounds[0][2]+bounds[1][2])/2, (bounds[0][3]+bounds[1][3])/2], bounds=boundsupdated)
101+
boundsupdated = ([bounds[0][0] , bounds[0][2] , bounds[0][3] ],
102+
[bounds[1][0] , bounds[1][2] , bounds[1][3] ])
103+
params, _ = curve_fit(lambda bvalues, S0, Fmv, Dmv: two_exp(bvalues, S0, Dpar, Fmv, Dmv), bvalues, dw_data, p0=[1, (bounds[0][2]+bounds[1][2])/2, (bounds[0][3]+bounds[1][3])/2], bounds=boundsupdated)
104+
S0 = params[0]
105+
Fmv, Dmv = params[1] , params[2]
106+
#when the fraction of a compartment equals zero (or very very small), the corresponding diffusivity is non-existing (=NaN)
107+
if Fmv < 1e-4:
108+
Dmv = float("NaN")
96109

97-
else:
98-
boundsupdated = ([bounds[0][0] , Dpar1 , bounds[0][2] , bounds[0][3] ],
99-
[bounds[1][0] , Dpar1, bounds[1][2] , bounds[1][3] ])
100-
params, _ = curve_fit(two_exp, bvalues, dw_data, p0=[1, Dpar1, (bounds[0][2]+bounds[1][2])/2, (bounds[0][3]+bounds[1][3])/2], bounds=boundsupdated)
101-
S0 = params[0]
102-
Dpar, Fmv, Dmv = params[1] , params[2] , params[3]
103-
#when the fraction of a compartment equals zero (or very very small), the corresponding diffusivity is non-existing (=NaN)
104-
if Fmv < 1e-4:
105-
Dmv = float("NaN")
106-
107-
if S0_output:
108-
return Dpar, Fmv, Dmv, S0
109-
else:
110-
return Dpar, Fmv, Dmv
111-
except:
110+
if S0_output:
111+
return Dpar, Fmv, Dmv, S0
112+
else:
113+
return Dpar, Fmv, Dmv
114+
#except:
112115

113-
if S0_output:
114-
return 0, 0, 0, 0, 0, 0
115-
else:
116-
return 0, 0, 0, 0, 0
116+
if S0_output:
117+
return 0, 0, 0, 0, 0, 0
118+
else:
119+
return 0, 0, 0, 0, 0
117120

118121

119122

src/standardized/PV_MUMC_biexp.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import numpy as np
2+
from src.wrappers.OsipiBase import OsipiBase
3+
from src.original.PV_MUMC.two_step_IVIM_fit import fit_least_squares_array, fit_least_squares
4+
5+
6+
class PV_MUMC_biexp(OsipiBase):
7+
"""
8+
Bi-exponential fitting algorithm by Paulien Voorter, Maastricht University
9+
"""
10+
11+
# Some basic stuff that identifies the algorithm
12+
id_author = "Paulien Voorter MUMC"
13+
id_algorithm_type = "Bi-exponential fit"
14+
id_return_parameters = "f, D*, D"
15+
id_units = "seconds per milli metre squared or milliseconds per micro metre squared"
16+
17+
# Algorithm requirements
18+
required_bvalues = 4
19+
required_thresholds = [0,0] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds
20+
required_bounds = False
21+
required_bounds_optional = True # Bounds may not be required but are optional
22+
required_initial_guess = False
23+
required_initial_guess_optional = True
24+
accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
25+
26+
def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, weighting=None, stats=False):
27+
"""
28+
Everything this algorithm requires should be implemented here.
29+
Number of segmentation thresholds, bounds, etc.
30+
31+
Our OsipiBase object could contain functions that compare the inputs with
32+
the requirements.
33+
"""
34+
super(PV_MUMC_biexp, self).__init__(bvalues=bvalues, thresholds=None, bounds=bounds, initial_guess=None)
35+
self.PV_algorithm = fit_least_squares
36+
37+
38+
def ivim_fit(self, signals, bvalues=None):
39+
"""Perform the IVIM fit
40+
41+
Args:
42+
signals (array-like)
43+
bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None.
44+
45+
Returns:
46+
_type_: _description_
47+
"""
48+
49+
50+
fit_results = self.PV_algorithm(bvalues, signals)
51+
52+
f = fit_results[1]
53+
Dstar = fit_results[2]
54+
D = fit_results[0]
55+
56+
return f, Dstar, D

tests/IVIMmodels/unit_tests/simple_test_run_of_algorithm.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
import os
33
from pathlib import Path
44
#from src.standardized.ETP_SRI_LinearFitting import ETP_SRI_LinearFitting
5-
from src.standardized.IAR_LU_biexp import IAR_LU_biexp
5+
#from src.standardized.IAR_LU_biexp import IAR_LU_biexp
6+
from src.standardized.PV_MUMC_biexp import PV_MUMC_biexp
67

78
## Simple test code...
89
# Used to just do a test run of an algorithm during development
910
def dev_test_run(model, **kwargs):
10-
bvalues = np.array([0, 200, 500, 800])
11+
bvalues = np.array([0, 50, 100, 150, 200, 500, 800])
1112

12-
def ivim_model(b, S0=1, f=0.1, Dstar=0.03, D=0.001):
13+
def ivim_model(b, S0=1, f=0.1, Dstar=0.01, D=0.001):
1314
return S0*(f*np.exp(-b*Dstar) + (1-f)*np.exp(-b*D))
1415

1516
signals = ivim_model(bvalues)
@@ -23,7 +24,8 @@ def ivim_model(b, S0=1, f=0.1, Dstar=0.03, D=0.001):
2324
#test = model.osipi_simple_bias_and_RMSE_test(SNR=20, bvalues=bvalues, f=0.1, Dstar=0.03, D=0.001, noise_realizations=10)
2425

2526
#model1 = ETP_SRI_LinearFitting(thresholds=[200])
26-
model2 = IAR_LU_biexp()
27+
#model2 = IAR_LU_biexp()
28+
model2 = PV_MUMC_biexp()
2729

2830
#dev_test_run(model1, linear_fit_option=True)
2931
dev_test_run(model2)

0 commit comments

Comments
 (0)