Skip to content

Commit 69aec08

Browse files
finished wrappers
Now runs!
1 parent e4b26e8 commit 69aec08

File tree

3 files changed

+39
-29
lines changed

3 files changed

+39
-29
lines changed

src/original/OGC_AmsterdamUMC/LSQ_fitting.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def order(Dt, Fp, Dp, S0=None):
6666
return Dt, Fp, Dp, S0
6767

6868

69-
def fit_segmented_array(bvalues, dw_data, njobs=4, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cutoff=75):
69+
def fit_segmented_array(bvalues, dw_data, njobs=4, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cutoff=75,p0=[0.001, 0.1, 0.01,1]):
7070
"""
7171
This is an implementation of the segmented fit, in which we first estimate D using a curve fit to b-values>cutoff;
7272
then estimate f from the fitted S0 and the measured S0 and finally estimate D* while fixing D and f. This fit
@@ -90,7 +90,7 @@ def fit_segmented_array(bvalues, dw_data, njobs=4, bounds=([0, 0, 0.005],[0.005,
9090
try:
9191
# define the parallel function
9292
def parfun(i):
93-
return fit_segmented(bvalues, dw_data[i, :], bounds=bounds, cutoff=cutoff)
93+
return fit_segmented(bvalues, dw_data[i, :], bounds=bounds, cutoff=cutoff,p0=p0)
9494

9595
output = Parallel(n_jobs=njobs)(delayed(parfun)(i) for i in tqdm(range(len(dw_data)), position=0, leave=True))
9696
Dt, Fp, Dp = np.transpose(output)
@@ -107,11 +107,11 @@ def parfun(i):
107107
Fp = np.zeros(len(dw_data))
108108
for i in tqdm(range(len(dw_data)), position=0, leave=True):
109109
# fill arrays with fit results on a per voxel base:
110-
Dt[i], Fp[i], Dp[i] = fit_segmented(bvalues, dw_data[i, :], bounds=bounds, cutoff=cutoff)
110+
Dt[i], Fp[i], Dp[i] = fit_segmented(bvalues, dw_data[i, :], bounds=bounds, cutoff=cutoff,p0=p0)
111111
return [Dt, Fp, Dp, S0]
112112

113113

114-
def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cutoff=75):
114+
def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cutoff=75,p0=[0.001, 0.1, 0.01,1]):
115115
"""
116116
This is an implementation of the segmented fit, in which we first estimate D using a curve fit to b-values>cutoff;
117117
then estimate f from the fitted S0 and the measured S0 and finally estimate D* while fixing D and f.
@@ -124,23 +124,24 @@ def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cu
124124
:return Dp: Fitted Dp
125125
:return S0: Fitted S0
126126
"""
127+
p0 = [p0[0] * 1000, p0[1] * 10, p0[2] * 10, p0[3]]
127128
try:
128129
# determine high b-values and data for D
129130
high_b = bvalues[bvalues >= cutoff]
130131
high_dw_data = dw_data[bvalues >= cutoff]
131132
# correct the bounds. Note that S0 bounds determine the max and min of f
132-
bounds1 = ([bounds[0][0] * 1000., 1 - bounds[1][1]], [bounds[1][0] * 1000., 1. - bounds[0][
133+
bounds1 = ([bounds[0][0] * 1000., 0.7 - bounds[1][1]], [bounds[1][0] * 1000., 1.3 - bounds[0][
133134
1]]) # By bounding S0 like this, we effectively insert the boundaries of f
134135
# fit for S0' and D
135136
params, _ = curve_fit(lambda b, Dt, int: int * np.exp(-b * Dt / 1000), high_b, high_dw_data,
136-
p0=(1, 1),
137+
p0=(p0[0], p0[3]-p0[1]/10),
137138
bounds=bounds1)
138139
Dt, Fp = params[0] / 1000, 1 - params[1]
139140
# remove the diffusion part to only keep the pseudo-diffusion
140141
dw_data_remaining = dw_data - (1 - Fp) * np.exp(-bvalues * Dt)
141142
bounds2 = (bounds[0][2], bounds[1][2])
142143
# fit for D*
143-
params, _ = curve_fit(lambda b, Dp: Fp * np.exp(-b * Dp), bvalues, dw_data_remaining, p0=(0.1), bounds=bounds2)
144+
params, _ = curve_fit(lambda b, Dp: Fp * np.exp(-b * Dp), bvalues, dw_data_remaining, p0=(p0[2]), bounds=bounds2)
144145
Dp = params[0]
145146
return Dt, Fp, Dp
146147
except:
@@ -150,7 +151,7 @@ def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cu
150151

151152

152153
def fit_least_squares_array(bvalues, dw_data, S0_output=True, fitS0=True, njobs=4,
153-
bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]),p0=[1, 1, 0.1, 1]):
154+
bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]),p0=[0.001, 0.1, 0.01, 1]):
154155
"""
155156
This is an implementation of the conventional IVIM fit. It is fitted in array form.
156157
:param bvalues: 1D Array with the b-values
@@ -218,7 +219,7 @@ def parfun(i):
218219

219220

220221
def fit_least_squares(bvalues, dw_data, S0_output=False, fitS0=True,
221-
bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]),p0=[1, 1, 0.1, 1]):
222+
bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), p0=[0.001, 0.1, 0.01, 1]):
222223
"""
223224
This is an implementation of the conventional IVIM fit. It fits a single curve
224225
:param bvalues: Array with the b-values
@@ -236,12 +237,14 @@ def fit_least_squares(bvalues, dw_data, S0_output=False, fitS0=True,
236237
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting.
237238
bounds = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10],
238239
[bounds[1][0] * 1000, bounds[1][1] * 10, bounds[1][2] * 10])
239-
params, _ = curve_fit(ivimN_noS0, bvalues, dw_data, p0=[1, 1, 0.1], bounds=bounds)
240+
p0=[p0[0]*1000,p0[1]*10,p0[2]*10]
241+
params, _ = curve_fit(ivimN_noS0, bvalues, dw_data, p0=p0, bounds=bounds)
240242
S0 = 1
241243
else:
242244
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting.
243245
bounds = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10, bounds[0][3]],
244246
[bounds[1][0] * 1000, bounds[1][1] * 10, bounds[1][2] * 10, bounds[1][3]])
247+
p0=[p0[0]*1000,p0[1]*10,p0[2]*10,p0[3]]
245248
params, _ = curve_fit(ivimN, bvalues, dw_data, p0=p0, bounds=bounds)
246249
S0 = params[3]
247250
# correct for the rescaling of parameters

src/standardized/OGC_AmsterdamUMC_biexp.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from src.wrappers.OsipiBase import OsipiBase
2-
from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_least_squares_array
3-
2+
from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_least_squares
3+
import numpy as np
44

55
class OGC_AmsterdamUMC_biexp(OsipiBase):
66
"""
@@ -24,20 +24,22 @@ class OGC_AmsterdamUMC_biexp(OsipiBase):
2424
required_bounds = False
2525
required_bounds_optional = True # Bounds may not be required but are optional
2626
required_initial_guess = False
27-
required_initial_guess_optional = False
27+
required_initial_guess_optional = True
2828
accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
2929

30-
def __init__(self, bvalues=None, thershold=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3])):
30+
def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True, thresholds=None):
3131
"""
3232
Everything this algorithm requires should be implemented here.
3333
Number of segmentation thresholds, bounds, etc.
3434
3535
Our OsipiBase object could contain functions that compare the inputs with
3636
the requirements.
3737
"""
38-
super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds)
39-
self.OGC_algorithm = fit_least_squares_array
38+
super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds,initial_guess,fitS0)
39+
self.OGC_algorithm = fit_least_squares
4040
self.bounds=bounds
41+
self.initial_guess=initial_guess
42+
self.fitS0=fitS0
4143

4244
def ivim_fit(self, signals, bvalues=None):
4345
"""Perform the IVIM fit
@@ -49,8 +51,8 @@ def ivim_fit(self, signals, bvalues=None):
4951
Returns:
5052
_type_: _description_
5153
"""
52-
53-
fit_results = self.OGC_algorithm(bvalues, signals,cutoff=self.thershold, bounds=self.bounds)
54+
bvalues=np.array(bvalues)
55+
fit_results = self.OGC_algorithm(bvalues, signals, p0=self.initial_guess, bounds=self.bounds,fitS0=self.fitS0)
5456

5557
D = fit_results[0]
5658
f = fit_results[1]

src/standardized/OGC_AmsterdamUMC_biexp_segmented.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from src.wrappers.OsipiBase import OsipiBase
22
from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_segmented
3-
3+
import numpy as np
44

55
class OGC_AmsterdamUMC_biexp_segmented(OsipiBase):
66
"""
@@ -19,28 +19,33 @@ class OGC_AmsterdamUMC_biexp_segmented(OsipiBase):
1919

2020
# Algorithm requirements
2121
required_bvalues = 4
22-
required_thresholds = [0,
23-
0] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds
22+
required_thresholds = [1,
23+
1] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds
2424
required_bounds = False
2525
required_bounds_optional = True # Bounds may not be required but are optional
2626
required_initial_guess = False
2727
required_initial_guess_optional = True
2828
accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
2929

30-
def __init__(self, bvalues=None, thershold=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True):
30+
def __init__(self, bvalues=None, thresholds=75, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), initial_guess=[0.001, 0.01, 0.01,1]):
3131
"""
3232
Everything this algorithm requires should be implemented here.
3333
Number of segmentation thresholds, bounds, etc.
3434
3535
Our OsipiBase object could contain functions that compare the inputs with
3636
the requirements.
3737
"""
38-
super(OGC_AmsterdamUMC_biexp_segmented, self).__init__(bvalues, bounds, initial_guess)
38+
super(OGC_AmsterdamUMC_biexp_segmented, self).__init__(bvalues,thresholds, bounds, initial_guess)
3939
self.OGC_algorithm = fit_segmented
40-
self.bounds=bounds
41-
self.initial_guess=initial_guess
42-
self.fitS0=fitS0
43-
self.thershold=thershold
40+
if bounds is None:
41+
self.bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3])
42+
else:
43+
self.bounds=bounds
44+
if initial_guess is None:
45+
self.initial_guess = [0.001, 0.001, 0.01, 1]
46+
else:
47+
self.initial_guess = initial_guess
48+
self.thresholds=thresholds
4449

4550
def ivim_fit(self, signals, bvalues=None):
4651
"""Perform the IVIM fit
@@ -52,8 +57,8 @@ def ivim_fit(self, signals, bvalues=None):
5257
Returns:
5358
_type_: _description_
5459
"""
55-
56-
fit_results = self.OGC_algorithm(bvalues, signals,bounds=self.bounds,p0=self.initial_guess,fitS0=self.fitS0)
60+
bvalues=np.array(bvalues)
61+
fit_results = self.OGC_algorithm(bvalues, signals,bounds=self.bounds,cutoff=self.thresholds,p0=self.initial_guess)
5762

5863
D = fit_results[0]
5964
f = fit_results[1]

0 commit comments

Comments
 (0)