Skip to content

Commit a75d9c0

Browse files
fixed for review
also added some documentation to base
1 parent 51bd03a commit a75d9c0

File tree

3 files changed

+165
-12
lines changed

3 files changed

+165
-12
lines changed

src/standardized/IAR_LU_biexp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def ivim_fit_full_volume(self, signals, bvalues, **kwargs):
115115

116116
self.IAR_algorithm = IvimModelBiExp(gtab, bounds=self.bounds, initial_guess=self.initial_guess)
117117
b0_index = np.where(bvalues == 0)[0][0]
118-
mask = signals[...,b0_index]>0.01
118+
mask = signals[...,b0_index]>0
119119
fit_results = self.IAR_algorithm.fit(signals, mask=mask)
120120

121121
results = {}

src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def ivim_fit_full_volume(self, signals, bvalues, njobs=4, **kwargs):
127127

128128
b0_index = np.where(bvalues == 0)[0][0]
129129
# Mask of voxels where signal at b=0 >= 0.5
130-
valid_mask = signals[..., b0_index] >= 0.01
130+
valid_mask = signals[..., b0_index] >= 0
131131
# Select only valid voxels for fitting
132132
signals = signals[valid_mask]
133133

src/wrappers/OsipiBase.py

Lines changed: 163 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,89 @@
88

99

1010
class OsipiBase:
11-
"""The base class for OSIPI IVIM fitting"""
11+
"""
12+
Comprehensive base class for OSIPI IVIM fitting algorithms.
13+
14+
The ``OsipiBase`` class defines a standardized framework for fitting
15+
intravoxel incoherent motion (IVIM) models to diffusion-weighted MRI data.
16+
It manages common attributes such as b-values, parameter bounds,
17+
thresholds, and initial guesses; provides requirement-checking utilities;
18+
and offers convenience methods for voxel-wise or full-volume fitting.
19+
Subclasses can implement algorithm-specific behavior while inheriting
20+
these shared tools.
21+
22+
Parameters
23+
----------
24+
bvalues : array-like, optional
25+
Diffusion b-values (s/mm²) matching the last dimension of the input data.
26+
thresholds : array-like, optional
27+
Thresholds used by specific algorithms (e.g., signal cutoffs).
28+
bounds : array-like, optional
29+
Parameter bounds for constrained optimization.
30+
initial_guess : array-like, optional
31+
Initial parameter estimates for the IVIM fit.
32+
algorithm : str, optional
33+
Name of an algorithm module in ``src/standardized`` to load dynamically.
34+
If supplied, the instance is immediately converted to that algorithm’s
35+
subclass via :meth:`osipi_initiate_algorithm`.
36+
**kwargs
37+
Additional keyword arguments forwarded to the selected algorithm’s
38+
initializer if ``algorithm`` is provided.
39+
40+
Attributes
41+
----------
42+
bvalues, thresholds, bounds, initial_guess : np.ndarray or None
43+
Core fitting inputs stored as arrays when provided.
44+
use_bounds, use_initial_guess : bool
45+
Flags controlling whether bounds and initial guesses are applied.
46+
deep_learning, supervised, stochastic : bool
47+
Indicators for the algorithm type; subclasses may set these.
48+
result_keys : list of str, optional
49+
Names of the output parameters (e.g., ["f", "Dp", "D"]).
50+
required_bvalues, required_thresholds, required_bounds,
51+
required_bounds_optional, required_initial_guess,
52+
required_initial_guess_optional : various, optional
53+
Optional attributes used by requirement-checking helpers.
54+
55+
Key Methods
56+
-----------
57+
osipi_initiate_algorithm(algorithm, **kwargs)
58+
Dynamically replace the current instance with the specified
59+
algorithm subclass.
60+
osipi_fit(data, bvalues=None, njobs=1, **kwargs)
61+
Voxel-wise IVIM fitting with optional parallel processing and
62+
automatic signal normalization.
63+
osipi_fit_full_volume(data, bvalues=None, **kwargs)
64+
Full-volume fitting for algorithms that support it.
65+
osipi_print_requirements()
66+
Display algorithm requirements such as needed b-values or bounds.
67+
osipi_accepted_dimensions(), osipi_accepts_dimension(dim)
68+
Query acceptable input dimensionalities.
69+
osipi_check_required_*()
70+
Validate that provided inputs meet algorithm requirements.
71+
osipi_simple_bias_and_RMSE_test(SNR, bvalues, f, Dstar, D, noise_realizations)
72+
Monte-Carlo bias/RMSE evaluation of the current fitting method.
73+
D_and_Ds_swap(results)
74+
Ensure consistency of D and D* estimates by swapping if necessary.
75+
76+
Notes
77+
-----
78+
* This class is typically used as a base or as a dynamic loader for
79+
a specific algorithm implementation.
80+
* Parallel voxel-wise fitting uses :mod:`joblib`.
81+
* Subclasses must implement algorithm-specific methods such as
82+
:meth:`ivim_fit` or :meth:`ivim_fit_full_volume`.
83+
84+
Examples
85+
--------
86+
# Dynamic algorithm loading
87+
model = OsipiBase(bvalues=[0, 50, 200, 800],
88+
algorithm="MyAlgorithm")
89+
90+
# Voxel-wise fitting
91+
results = base.osipi_fit(dwi_data, njobs=4)
92+
f_map = results["f"]
93+
"""
1294

1395
def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None, **kwargs):
1496
# Define the attributes as numpy arrays only if they are not None
@@ -54,9 +136,48 @@ def initialize(**kwargs):
54136
pass
55137

56138
#def osipi_fit(self, data=None, bvalues=None, thresholds=None, bounds=None, initial_guess=None, **kwargs):
57-
def osipi_fit(self, data, bvalues=None, njobs=8, **kwargs):
58-
"""Fits the data with the bvalues
59-
Returns [S0, f, Dstar, D]
139+
def osipi_fit(self, data, bvalues=None, njobs=1, **kwargs):
140+
"""
141+
Fit multi-b-value diffusion MRI data using the IVIM model.
142+
143+
This function normalizes the input signal to the minimum b-value and fits each voxel's signal to the IVIM model to estimate parameters such as
144+
perfusion fraction (f), pseudo-diffusion coefficient (D*), and tissue diffusion coefficient (D).
145+
Fits can be computed in parallel across voxels using multiple jobs.
146+
147+
Parameters
148+
----------
149+
data : np.ndarray
150+
Multi-dimensional array containing the signal intensities. The last dimension must correspond
151+
to the b-values (diffusion weightings).
152+
bvalues : array-like, optional
153+
Array of b-values corresponding to the last dimension of `data`. If not provided, the method
154+
uses `self.bvalues` set during object initialization.
155+
njobs : int, optional, default=1
156+
Number of parallel jobs to use for voxel-wise fitting. If `njobs` > 1, the fitting will be
157+
distributed across multiple processes. -1 will use all available cpus
158+
**kwargs : dict, optional
159+
Additional keyword arguments to be passed to the underlying `ivim_fit` function.
160+
161+
Returns
162+
-------
163+
results : dict of np.ndarray
164+
Dictionary containing voxel-wise parameter maps. Keys are parameter names ("f", "D", "Dp"),
165+
and values are arrays with the same shape as `data` excluding the last dimension.
166+
167+
Notes
168+
-----
169+
- The signal is normalized to the minimum b-value before fitting.
170+
- Handles NaN values by returning zeros for all parameters in those voxels.
171+
- Parallelization is handled using joblib's `Parallel` and `delayed`.
172+
- If `self.result_keys` is defined, it determines the output parameter names; otherwise, the default
173+
keys are ["f", "Dp", "D"].
174+
- The method swaps D and D* values after fitting using `self.D_and_Ds_swap` to maintain consistency.
175+
176+
Example
177+
-------
178+
>>> results = instance.osipi_fit(data, bvalues=[0, 50, 200, 800], njobs=4)
179+
>>> f_map = results['f']
180+
>>> D_map = results['D']
60181
"""
61182

62183
# We should first check whether the attributes in the __init__ are not None
@@ -159,14 +280,46 @@ def parfun(ijk):
159280

160281

161282
def osipi_fit_full_volume(self, data, bvalues=None, **kwargs):
162-
"""Sends a full volume in one go to the fitting algorithm. The osipi_fit method only sends one voxel at a time.
283+
"""
284+
Fit an entire volume of multi-b-value diffusion MRI data in a single call using the IVIM model.
163285
164-
Args:
165-
data (array): 2D (data x b-values), 3D (single slice) or 4D (multi slice) DWI data, with last dimension the b-value dimension.
166-
bvalues (array, optional): The b-values of the DWI data. Defaults to None.
286+
Unlike `osipi_fit`, which processes one voxel at a time, this method sends the full volume
287+
to the fitting algorithm. This can be more efficient for algorithms that support full-volume fitting.
288+
289+
Parameters
290+
----------
291+
data : np.ndarray
292+
2D (data x b-values), 3D (single slice), or 4D (multi-slice) diffusion-weighted imaging (DWI) data.
293+
The last dimension must correspond to the b-values.
294+
bvalues : array-like, optional
295+
Array of b-values corresponding to the last dimension of `data`. If not provided, the method
296+
uses `self.bvalues` set during object initialization.
297+
**kwargs : dict, optional
298+
Additional keyword arguments to be passed to `ivim_fit_full_volume`.
299+
300+
Returns
301+
-------
302+
results : dict of np.ndarray or bool
303+
Dictionary containing parametric maps for each IVIM parameter (keys from `self.result_keys`,
304+
or defaults ["f", "Dp", "D"]). Each value is an array matching the spatial dimensions of `data`.
305+
Returns `False` if full-volume fitting is not supported by the algorithm.
306+
307+
Notes
308+
-----
309+
- This method does not normalize the input signal to the minimum b-value, unlike `osipi_fit`.
310+
311+
Example
312+
-------
313+
# Standard usage:
314+
results = instance.osipi_fit_full_volume(data, bvalues=[0, 50, 200, 800])
315+
f_map = results['f']
316+
D_map = results['D']
317+
Dp_map = results['Dp']
167318
168-
Returns:
169-
results (dict): Dict with key each containing an array which is a parametric map.
319+
# If the algorithm does not support full-volume fitting:
320+
results = instance.osipi_fit_full_volume(data)
321+
if results is False:
322+
print("Full-volume fitting not supported.")
170323
"""
171324

172325
try:

0 commit comments

Comments
 (0)