from src.wrappers.OsipiBase import OsipiBase
from src.original.TF_reference.segmented_IVIMfit import segmented_IVIM_fit
import numpy as np
[docs]
class TF_reference_IVIMfit(OsipiBase):
"""
Bi-exponential fitting algorithm by IVIM Task force
"""
# I'm thinking that we define default attributes for each submission like this
# And in __init__, we can call the OsipiBase control functions to check whether
# the user inputs fulfil the requirements
# Some basic stuff that identifies the algorithm
id_author = "OSIPI IVIM TF"
id_algorithm_type = "Bi-exponential fit"
id_return_parameters = "f, D*, D"
id_units = "seconds per milli metre squared or milliseconds per micro metre squared"
# Algorithm requirements
required_bvalues = 4
required_thresholds = [0,
1] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds
required_bounds = False
required_bounds_optional = True # Bounds may not be required but are optional
required_initial_guess = False
required_initial_guess_optional = False
accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
# Supported inputs in the standardized class
supported_bounds = True
supported_initial_guess = False
supported_thresholds = True
def __init__(self, bvalues=None, thresholds=200, bounds=None, initial_guess=None):
"""
Everything this algorithm requires should be implemented here.
Number of segmentation thresholds, bounds, etc.
Our OsipiBase object could contain functions that compare the inputs with
the requirements.
"""
super(TF_reference_IVIMfit, self).__init__(bvalues=bvalues, thresholds=thresholds,bounds=bounds,initial_guess=initial_guess)
self.TF_reference_algorithm = segmented_IVIM_fit
self.initialize(bounds, thresholds)
self.use_initial_guess = False
[docs]
def initialize(self, bounds, thresholds):
if bounds is None:
print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005],[0.005, 1.0, 0.2]')
self.bounds=([0, 0, 0.005, 0.8],[0.005, 1.0, 0.2, 1.2])
else:
self.bounds=bounds
self.use_bounds = True
if thresholds is None:
self.thresholds = 200
print('warning, no thresholds were defined, so default threshold are used of 200')
else:
self.thresholds = thresholds
[docs]
def ivim_fit(self, signals, bvalues=None):
"""Perform the IVIM fit
Args:
signals (array-like)
bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None.
Returns:
_type_: _description_
"""
#bvalues = np.array(bvalues)
bvalues = np.array(bvalues)
if np.any(signals < 0):
signals = np.clip(signals,0.01, None)
print('warning, negative values in signal: values clipped to 0.01')
fit_results = self.TF_reference_algorithm(bvalues,signals,b_cutoff=self.thresholds, bounds=self.bounds)
results = {}
results["D"] = fit_results[0]
results["f"] = fit_results[1]
results["Dp"] = fit_results[2]
return results