@@ -66,7 +66,7 @@ def order(Dt, Fp, Dp, S0=None):
66
66
return Dt , Fp , Dp , S0
67
67
68
68
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 ] ):
70
70
"""
71
71
This is an implementation of the segmented fit, in which we first estimate D using a curve fit to b-values>cutoff;
72
72
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,
90
90
try :
91
91
# define the parallel function
92
92
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 )
94
94
95
95
output = Parallel (n_jobs = njobs )(delayed (parfun )(i ) for i in tqdm (range (len (dw_data )), position = 0 , leave = True ))
96
96
Dt , Fp , Dp = np .transpose (output )
@@ -107,11 +107,11 @@ def parfun(i):
107
107
Fp = np .zeros (len (dw_data ))
108
108
for i in tqdm (range (len (dw_data )), position = 0 , leave = True ):
109
109
# 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 )
111
111
return [Dt , Fp , Dp , S0 ]
112
112
113
113
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 ] ):
115
115
"""
116
116
This is an implementation of the segmented fit, in which we first estimate D using a curve fit to b-values>cutoff;
117
117
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
124
124
:return Dp: Fitted Dp
125
125
:return S0: Fitted S0
126
126
"""
127
+ p0 = [p0 [0 ] * 1000 , p0 [1 ] * 10 , p0 [2 ] * 10 , p0 [3 ]]
127
128
try :
128
129
# determine high b-values and data for D
129
130
high_b = bvalues [bvalues >= cutoff ]
130
131
high_dw_data = dw_data [bvalues >= cutoff ]
131
132
# 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 ][
133
134
1 ]]) # By bounding S0 like this, we effectively insert the boundaries of f
134
135
# fit for S0' and D
135
136
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 ),
137
138
bounds = bounds1 )
138
139
Dt , Fp = params [0 ] / 1000 , 1 - params [1 ]
139
140
# remove the diffusion part to only keep the pseudo-diffusion
140
141
dw_data_remaining = dw_data - (1 - Fp ) * np .exp (- bvalues * Dt )
141
142
bounds2 = (bounds [0 ][2 ], bounds [1 ][2 ])
142
143
# 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 )
144
145
Dp = params [0 ]
145
146
return Dt , Fp , Dp
146
147
except :
@@ -150,7 +151,7 @@ def fit_segmented(bvalues, dw_data, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), cu
150
151
151
152
152
153
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 ]):
154
155
"""
155
156
This is an implementation of the conventional IVIM fit. It is fitted in array form.
156
157
:param bvalues: 1D Array with the b-values
@@ -218,7 +219,7 @@ def parfun(i):
218
219
219
220
220
221
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 ]):
222
223
"""
223
224
This is an implementation of the conventional IVIM fit. It fits a single curve
224
225
:param bvalues: Array with the b-values
@@ -236,12 +237,14 @@ def fit_least_squares(bvalues, dw_data, S0_output=False, fitS0=True,
236
237
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting.
237
238
bounds = ([bounds [0 ][0 ] * 1000 , bounds [0 ][1 ] * 10 , bounds [0 ][2 ] * 10 ],
238
239
[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 )
240
242
S0 = 1
241
243
else :
242
244
# bounds are rescaled such that each parameter changes at roughly the same rate to help fitting.
243
245
bounds = ([bounds [0 ][0 ] * 1000 , bounds [0 ][1 ] * 10 , bounds [0 ][2 ] * 10 , bounds [0 ][3 ]],
244
246
[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 ]]
245
248
params , _ = curve_fit (ivimN , bvalues , dw_data , p0 = p0 , bounds = bounds )
246
249
S0 = params [3 ]
247
250
# correct for the rescaling of parameters
0 commit comments