1+ from src .wrappers .OsipiBase import OsipiBase
2+ from src .original .TF_reference .segmented_IVIMfit import segmented_IVIM_fit
3+ import numpy as np
4+
5+ class TF_reference_IVIMfit (OsipiBase ):
6+ """
7+ Bi-exponential fitting algorithm by IVIM Task force
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 = "OSIPI IVIM TF"
16+ id_algorithm_type = "Bi-exponential fit"
17+ id_return_parameters = "f, D*, D"
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+ 1 ] # 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 = False
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+ # Supported inputs in the standardized class
31+ supported_bounds = True
32+ supported_initial_guess = False
33+ supported_thresholds = True
34+
35+ def __init__ (self , bvalues = None , thresholds = 200 , bounds = None , initial_guess = None ):
36+ """
37+ Everything this algorithm requires should be implemented here.
38+ Number of segmentation thresholds, bounds, etc.
39+
40+ Our OsipiBase object could contain functions that compare the inputs with
41+ the requirements.
42+ """
43+ super (TF_reference_IVIMfit , self ).__init__ (bvalues = bvalues , thresholds = thresholds ,bounds = bounds ,initial_guess = initial_guess )
44+ self .TF_reference_algorithm = segmented_IVIM_fit
45+ self .initialize (bounds , thresholds )
46+ self .use_initial_guess = False
47+
48+
49+ def initialize (self , bounds , thresholds ):
50+ if bounds is None :
51+ print ('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005],[0.005, 1.0, 0.2]' )
52+ self .bounds = ([0 , 0 , 0.005 , 0.8 ],[0.005 , 1.0 , 0.2 , 1.2 ])
53+ else :
54+ self .bounds = bounds
55+ self .use_bounds = True
56+ if thresholds is None :
57+ self .thresholds = 200
58+ print ('warning, no thresholds were defined, so default threshold are used of 200' )
59+ else :
60+ self .thresholds = thresholds
61+
62+
63+ def ivim_fit (self , signals , bvalues = None ):
64+ """Perform the IVIM fit
65+
66+ Args:
67+ signals (array-like)
68+ bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None.
69+
70+ Returns:
71+ _type_: _description_
72+ """
73+ #bvalues = np.array(bvalues)
74+ bvalues = np .array (bvalues )
75+
76+ if np .any (signals < 0 ):
77+ signals = np .clip (signals ,0.01 , None )
78+ print ('warning, negative values in signal: values clipped to 0.01' )
79+
80+
81+ fit_results = self .TF_reference_algorithm (bvalues ,signals ,b_cutoff = self .thresholds , bounds = self .bounds )
82+
83+ results = {}
84+ results ["D" ] = fit_results [0 ]
85+ results ["f" ] = fit_results [1 ]
86+ results ["Dp" ] = fit_results [2 ]
87+
88+ return results
0 commit comments