Skip to content

Commit d3e2011

Browse files
Added bi-exp and segmented fit to wrapper
minor changes to origin by author
1 parent 6a91e49 commit d3e2011

File tree

3 files changed

+74
-12
lines changed

3 files changed

+74
-12
lines changed

src/original/OGC_AmsterdamUMC/LSQ_fitting.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cu
150150

151151

152152
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])):
153+
bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]),p0=[1, 1, 0.1, 1]):
154154
"""
155155
This is an implementation of the conventional IVIM fit. It is fitted in array form.
156156
:param bvalues: 1D Array with the b-values
@@ -175,7 +175,7 @@ def fit_least_squares_array(bvalues, dw_data, S0_output=True, fitS0=True, njobs=
175175
try:
176176
# defining parallel function
177177
def parfun(i):
178-
return fit_least_squares(bvalues, dw_data[i, :], S0_output=S0_output, fitS0=fitS0, bounds=bounds)
178+
return fit_least_squares(bvalues, dw_data[i, :], S0_output=S0_output, fitS0=fitS0, bounds=bounds,p0=p0)
179179

180180
output = Parallel(n_jobs=njobs)(delayed(parfun)(i) for i in tqdm(range(len(dw_data)), position=0, leave=True))
181181
Dt, Fp, Dp, S0 = np.transpose(output)
@@ -192,14 +192,14 @@ def parfun(i):
192192
# running in a single loop and filling arrays
193193
for i in tqdm(range(len(dw_data)), position=0, leave=True):
194194
Dt[i], Fp[i], Dp[i], S0[i] = fit_least_squares(bvalues, dw_data[i, :], S0_output=S0_output, fitS0=fitS0,
195-
bounds=bounds)
195+
bounds=bounds,p0=p0)
196196
return [Dt, Fp, Dp, S0]
197197
else:
198198
# if S0 is not exported
199199
if njobs > 1:
200200
try:
201201
def parfun(i):
202-
return fit_least_squares(bvalues, dw_data[i, :], fitS0=fitS0, bounds=bounds)
202+
return fit_least_squares(bvalues, dw_data[i, :], fitS0=fitS0, bounds=bounds,p0=p0)
203203

204204
output = Parallel(n_jobs=njobs)(delayed(parfun)(i) for i in tqdm(range(len(dw_data)), position=0, leave=True))
205205
Dt, Fp, Dp = np.transpose(output)
@@ -213,12 +213,12 @@ def parfun(i):
213213
Fp = np.zeros(len(dw_data))
214214
for i in range(len(dw_data)):
215215
Dt[i], Fp[i], Dp[i] = fit_least_squares(bvalues, dw_data[i, :], S0_output=S0_output, fitS0=fitS0,
216-
bounds=bounds)
216+
bounds=bounds,p0=p0)
217217
return [Dt, Fp, Dp]
218218

219219

220220
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])):
221+
bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]),p0=[1, 1, 0.1, 1]):
222222
"""
223223
This is an implementation of the conventional IVIM fit. It fits a single curve
224224
:param bvalues: Array with the b-values
@@ -242,7 +242,7 @@ def fit_least_squares(bvalues, dw_data, S0_output=False, fitS0=True,
242242
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting.
243243
bounds = ([bounds[0][0] * 1000, bounds[0][1] * 10, bounds[0][2] * 10, bounds[0][3]],
244244
[bounds[1][0] * 1000, bounds[1][1] * 10, bounds[1][2] * 10, bounds[1][3]])
245-
params, _ = curve_fit(ivimN, bvalues, dw_data, p0=[1, 1, 0.1, 1], bounds=bounds)
245+
params, _ = curve_fit(ivimN, bvalues, dw_data, p0=p0, bounds=bounds)
246246
S0 = params[3]
247247
# correct for the rescaling of parameters
248248
Dt, Fp, Dp = params[0] / 1000, params[1] / 10, params[2] / 10
@@ -563,7 +563,6 @@ def fit_bayesian_array(bvalues, dw_data, paramslsq, arg):
563563
:return Dp: Array with Dp in each voxel
564564
:return S0: Array with S0 in each voxel
565565
"""
566-
arg = checkarg_lsq(arg)
567566
# fill out missing args
568567
Dt0, Fp0, Dp0, S00 = paramslsq
569568
# determine prior

src/standardized/OGC_AmsterdamUMC_biexp.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,20 @@ 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 = True
27+
required_initial_guess_optional = False
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, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3])):
30+
def __init__(self, bvalues=None, thershold=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3])):
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, None, bounds, None)
38+
super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds)
3939
self.OGC_algorithm = fit_least_squares_array
40+
self.bounds=bounds
4041

4142
def ivim_fit(self, signals, bvalues=None):
4243
"""Perform the IVIM fit
@@ -49,7 +50,7 @@ def ivim_fit(self, signals, bvalues=None):
4950
_type_: _description_
5051
"""
5152

52-
fit_results = self.OGC_algorithm(bvalues, signals)
53+
fit_results = self.OGC_algorithm(bvalues, signals,cutoff=self.thershold, bounds=self.bounds)
5354

5455
D = fit_results[0]
5556
f = fit_results[1]
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from src.wrappers.OsipiBase import OsipiBase
2+
from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_segmented
3+
4+
5+
class OGC_AmsterdamUMC_segbiexp(OsipiBase):
6+
"""
7+
Segmented bi-exponential fitting algorithm by Oliver Gurney-Champion, Amsterdam UMC
8+
"""
9+
10+
# I'm thinking that we define default attributes for each submission like this
11+
# And in __init__, we can call the OsipiBase control functions to check whether
12+
# the user inputs fulfil the requirements
13+
14+
# Some basic stuff that identifies the algorithm
15+
id_author = "Oliver Gurney Champion, Amsterdam UMC"
16+
id_algorithm_type = "Segmented bi-exponential fit"
17+
id_return_parameters = "f, D*, D, S0"
18+
id_units = "seconds per milli metre squared or milliseconds per micro metre squared"
19+
20+
# Algorithm requirements
21+
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
24+
required_bounds = False
25+
required_bounds_optional = True # Bounds may not be required but are optional
26+
required_initial_guess = False
27+
required_initial_guess_optional = True
28+
accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
29+
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):
31+
"""
32+
Everything this algorithm requires should be implemented here.
33+
Number of segmentation thresholds, bounds, etc.
34+
35+
Our OsipiBase object could contain functions that compare the inputs with
36+
the requirements.
37+
"""
38+
super(OGC_AmsterdamUMC_segbiexp, self).__init__(bvalues, bounds, initial_guess)
39+
self.OGC_algorithm = fit_segmented
40+
self.bounds=bounds
41+
self.initial_guess=initial_guess
42+
self.fitS0=fitS0
43+
self.thershold=thershold
44+
45+
def ivim_fit(self, signals, bvalues=None):
46+
"""Perform the IVIM fit
47+
48+
Args:
49+
signals (array-like)
50+
bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None.
51+
52+
Returns:
53+
_type_: _description_
54+
"""
55+
56+
fit_results = self.OGC_algorithm(bvalues, signals,bounds=self.bounds,p0=self.initial_guess,fitS0=self.fitS0)
57+
58+
D = fit_results[0]
59+
f = fit_results[1]
60+
Dstar = fit_results[2]
61+
62+
return f, Dstar, D

0 commit comments

Comments
 (0)