diff --git a/src/original/ETP_SRI/__pycache__/LinearFitting.cpython-39.pyc b/src/original/ETP_SRI/__pycache__/LinearFitting.cpython-39.pyc index cff98ae..f356058 100644 Binary files a/src/original/ETP_SRI/__pycache__/LinearFitting.cpython-39.pyc and b/src/original/ETP_SRI/__pycache__/LinearFitting.cpython-39.pyc differ diff --git a/src/standardized/ETP_SRI_LinearFitting.py b/src/standardized/ETP_SRI_LinearFitting.py index a4fb30c..0c65a50 100644 --- a/src/standardized/ETP_SRI_LinearFitting.py +++ b/src/standardized/ETP_SRI_LinearFitting.py @@ -8,9 +8,50 @@ class ETP_SRI_LinearFitting(OsipiBase): Implementation and execution of the submitted algorithm """ - def ivim_fit(self, signals, b_bvalues): - ETP_object = LinearFit(self.thresholds[0]) + # 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 + author = "Eric T. Peterson, SRI" + algorithm_type = "Linear fit" + return_parameters = "f, D*, D" + units = "seconds per milli metre squared" + + # Algorithm requirements + b_values_required = 3 + thresholds_required = [1,1] # Interval from 1 to 1, in case submissions allow a custom number of thresholds + bounds_required = False + accepted_dimensions = 1 + + def __init__(self, thresholds, weighting=None, stats=False): + """ + Everything this method requires should be implemented here. + Number of segmentation thresholds, bounds, etc. + + Our OsipiBase object could contain functions compare the inputs with + the requirements. + """ + self.thresholds = thresholds + # Could be a good idea to have all the submission-specfic variable be + # defined with initials? + self.ETP_weighting = weighting + # Check the inputs + self.check_thresholds_required_osipi() + + + def ivim_fit(self, signals, b_values): + """ + We may want this function to be as simple as + data and b-values in -> parameter estimates out. + Everything else such as segmentation thresholds, bounds, etc. should + be object attributes. + + This makes the execution of submissions easy and predictable. + All execution stepts requires should be performed here. + """ + ETP_object = LinearFit(self.thresholds[0]) f, D, Dstar = ETP_object.ivim_fit(b_values, signals) @@ -27,6 +68,6 @@ def ivim_model(b, S0=1, f=0.1, Dstar=0.03, D=0.001): signals = ivim_model(b_values) -model = ETP_SRI_LinearFitting() -model.thresholds = [200] -results = model.ivim_fit(b_values, signals) \ No newline at end of file +model = ETP_SRI_LinearFitting([200]) +results = model.ivim_fit(signals, b_values) +test = model.simple_bias_and_RMSE_test(SNR=20, b_values=b_values, f=0.1, Dstar=0.03, D=0.001, noise_realizations=10) \ No newline at end of file diff --git a/src/wrappers/OsipiBase.py b/src/wrappers/OsipiBase.py index 3903a46..78f38fc 100644 --- a/src/wrappers/OsipiBase.py +++ b/src/wrappers/OsipiBase.py @@ -1,4 +1,5 @@ - +import numpy as np +from scipy.stats import norm class OsipiBase: @@ -29,22 +30,57 @@ def accepts_dimension_osipi(self, dim): return False return accepted[dim] - def thresholds_required_osipi(): + def check_thresholds_required_osipi(self): """How many segmentation thresholds does it require?""" - return 0 + if (len(self.thresholds) < self.thresholds_required[0]) or (len(self.thresholds) > self.thresholds_required[1]): + print("Conformance error: Number of thresholds.") + return False + return True - def guess_required_osipi(): + def check_guess_required_osipi(): """Does it require an initial guess?""" return False - def bounds_required_osipi(): + def check_bounds_required_osipi(): """Does it require bounds?""" return False + + def check_b_values_required_osipi(): + """Minimum number of b-values required""" + pass def author_osipi(): """Author identification""" return '' - def simple_test(): - pass + def simple_bias_and_RMSE_test(self, SNR, b_values, f, Dstar, D, noise_realizations=100): + # Generate signal + b_values = np.asarray(b_values) + signals = f*np.exp(-b_values*Dstar) + (1-f)*np.exp(-b_values*D) + + f_estimates = np.zeros(noise_realizations) + Dstar_estimates = np.zeros(noise_realizations) + D_estimates = np.zeros(noise_realizations) + for i in range(noise_realizations): + # Add some noise + sigma = signals[0]/SNR + noised_signal = np.array([norm.rvs(signal, sigma) for signal in signals]) + + # Perform fit with the noised signal + f_estimates[i], Dstar_estimates[i], D_estimates[i] = self.ivim_fit(noised_signal, b_values) + + # Calculate bias + f_bias = np.mean(f_estimates) - f + Dstar_bias = np.mean(Dstar_estimates) - Dstar + D_bias = np.mean(D_estimates) - D + + # Calculate RMSE + f_RMSE = np.sqrt(np.var(f_estimates) + f_bias**2) + Dstar_RMSE = np.sqrt(np.var(Dstar_estimates) + Dstar_bias**2) + D_RMSE = np.sqrt(np.var(D_estimates) + D_bias**2) + + print(f"f bias:\t{f_bias}\nf RMSE:\t{f_RMSE}") + print(f"Dstar bias:\t{Dstar_bias}\nDstar RMSE:\t{Dstar_RMSE}") + print(f"D bias:\t{D_bias}\nD RMSE:\t{D_RMSE}") + diff --git a/src/wrappers/__pycache__/OsipiBase.cpython-39.pyc b/src/wrappers/__pycache__/OsipiBase.cpython-39.pyc index c997aa7..b1b369b 100644 Binary files a/src/wrappers/__pycache__/OsipiBase.cpython-39.pyc and b/src/wrappers/__pycache__/OsipiBase.cpython-39.pyc differ