|
8 | 8 |
|
9 | 9 |
|
10 | 10 | 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 | + """ |
12 | 94 |
|
13 | 95 | def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None, **kwargs):
|
14 | 96 | # Define the attributes as numpy arrays only if they are not None
|
@@ -54,9 +136,48 @@ def initialize(**kwargs):
|
54 | 136 | pass
|
55 | 137 |
|
56 | 138 | #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'] |
60 | 181 | """
|
61 | 182 |
|
62 | 183 | # We should first check whether the attributes in the __init__ are not None
|
@@ -159,14 +280,46 @@ def parfun(ijk):
|
159 | 280 |
|
160 | 281 |
|
161 | 282 | 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. |
163 | 285 |
|
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'] |
167 | 318 |
|
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.") |
170 | 323 | """
|
171 | 324 |
|
172 | 325 | try:
|
|
0 commit comments