Skip to content

Commit 719032f

Browse files
committed
Added OGC to testing
1 parent 0830f1d commit 719032f

File tree

8 files changed

+68
-23
lines changed

8 files changed

+68
-23
lines changed

conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def save_file(request):
6868
filename = filename.as_posix()
6969
with open(filename, "w") as csv_file:
7070
writer = csv.writer(csv_file, delimiter=',')
71-
writer.writerow(("Algorithm", "Region", "SNR", "f", "Dp", "D", "f_fitted", "Dp_fitted", "D_fitted"))
71+
writer.writerow(("Algorithm", "Region", "SNR", "index", "f", "Dp", "D", "f_fitted", "Dp_fitted", "D_fitted"))
7272
# writer.writerow(["", datetime.datetime.now()])
7373
return filename
7474

src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class OGC_AmsterdamUMC_Bayesian_biexp(OsipiBase):
2626
required_initial_guess = False
2727
required_initial_guess_optional = True
2828
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+
accepts_priors = True
2930

3031
def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True, thresholds=None, prior_in=None):
3132
"""
@@ -38,7 +39,11 @@ def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.
3839
Args:
3940
datain is a 2D array with values of D, f, D* (and S0) that will form the prior.
4041
"""
41-
super(OGC_AmsterdamUMC_Bayesian_biexp, self).__init__(bvalues, bounds,initial_guess,fitS0,prior_in)
42+
super(OGC_AmsterdamUMC_Bayesian_biexp, self).__init__(bvalues, thresholds, bounds, initial_guess) #, fitS0, prior_in)
43+
self.OGC_algorithm = fit_bayesian
44+
self.initialize(bounds, initial_guess, fitS0, thresholds, prior_in)
45+
46+
def initialize(self, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True, thresholds=None, prior_in=None):
4247
if bounds is None:
4348
self.bounds=([0, 0, 0.005, 0.7],[0.005, 1, 0.2, 1.3])
4449
else:
@@ -50,14 +55,13 @@ def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.
5055
self.neg_log_prior = empirical_neg_log_prior(prior_in[0], prior_in[1], prior_in[2],prior_in[3])
5156
else:
5257
self.neg_log_prior = empirical_neg_log_prior(prior_in[0], prior_in[1], prior_in[2])
53-
self.OGC_algorithm = fit_bayesian
5458
if initial_guess is None:
5559
self.initial_guess = [0.001, 0.5, 0.1, 1]
5660
else:
5761
self.initial_guess = initial_guess
5862
self.fitS0=fitS0
5963

60-
def ivim_fit(self, signals, bvalues=None):
64+
def ivim_fit(self, signals, bounds, initial_guess, bvalues=None):
6165
"""Perform the IVIM fit
6266
6367
Args:

src/standardized/OGC_AmsterdamUMC_biexp.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class OGC_AmsterdamUMC_biexp(OsipiBase):
2626
required_initial_guess = False
2727
required_initial_guess_optional = True
2828
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+
accepts_priors = True
2930

3031
def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3]), initial_guess=None, fitS0=True, thresholds=None):
3132
"""
@@ -37,6 +38,9 @@ def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.
3738
"""
3839
super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds,initial_guess,fitS0)
3940
self.OGC_algorithm = fit_least_squares
41+
self.initialize(bounds, initial_guess, fitS0)
42+
43+
def initialize(self, bounds, initial_guess, fitS0):
4044
if bounds is None:
4145
self.bounds=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3])
4246
else:
@@ -47,7 +51,7 @@ def __init__(self, bvalues=None, bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.
4751
self.initial_guess = initial_guess
4852
self.fitS0=fitS0
4953

50-
def ivim_fit(self, signals, bvalues=None):
54+
def ivim_fit(self, signals, bounds, initial_guess, bvalues=None):
5155
"""Perform the IVIM fit
5256
5357
Args:

src/standardized/OGC_AmsterdamUMC_biexp_segmented.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class OGC_AmsterdamUMC_biexp_segmented(OsipiBase):
2626
required_initial_guess = False
2727
required_initial_guess_optional = True
2828
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+
accepts_priors = True
2930

3031
def __init__(self, bvalues=None, thresholds=150, bounds=([0, 0, 0.005],[0.005, 0.7, 0.2]), initial_guess=[0.001, 0.01, 0.01,1]):
3132
"""
@@ -37,6 +38,10 @@ def __init__(self, bvalues=None, thresholds=150, bounds=([0, 0, 0.005],[0.005, 0
3738
"""
3839
super(OGC_AmsterdamUMC_biexp_segmented, self).__init__(bvalues,thresholds, bounds, initial_guess)
3940
self.OGC_algorithm = fit_segmented
41+
self.initialize(bounds, initial_guess, thresholds)
42+
43+
44+
def initialize(self, bounds, initial_guess, thresholds):
4045
if bounds is None:
4146
self.bounds=([0, 0, 0.005, 0.7],[0.005, 0.7, 0.2, 1.3])
4247
else:
@@ -47,7 +52,7 @@ def __init__(self, bvalues=None, thresholds=150, bounds=([0, 0, 0.005],[0.005, 0
4752
self.initial_guess = initial_guess
4853
self.thresholds=thresholds
4954

50-
def ivim_fit(self, signals, bvalues=None):
55+
def ivim_fit(self, signals, bounds, initial_guess, bvalues=None):
5156
"""Perform the IVIM fit
5257
5358
Args:

src/wrappers/OsipiBase.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class OsipiBase:
88
"""The base class for OSIPI IVIM fitting"""
99

10-
def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None):
10+
def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None, **kwargs):
1111
# Define the attributes as numpy arrays only if they are not None
1212
self.bvalues = np.asarray(bvalues) if bvalues is not None else None
1313
self.thresholds = np.asarray(thresholds) if thresholds is not None else None
@@ -17,7 +17,7 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non
1717
# If the user inputs an algorithm to OsipiBase, it is intereprete as initiating
1818
# an algorithm object with that name.
1919
if algorithm:
20-
self.osipi_initiate_algorithm(algorithm, bvalues=bvalues, thresholds=thresholds, bounds=bounds, initial_guess=initial_guess)
20+
self.osipi_initiate_algorithm(algorithm, bvalues=bvalues, thresholds=thresholds, bounds=bounds, initial_guess=initial_guess, **kwargs)
2121

2222
def osipi_initiate_algorithm(self, algorithm, **kwargs):
2323
"""Turns the class into a specified one by the input.
@@ -41,6 +41,10 @@ def osipi_initiate_algorithm(self, algorithm, **kwargs):
4141
# Change the class from OsipiBase to the specified algorithm
4242
self.__class__ = getattr(importlib.import_module(import_path), algorithm)
4343
self.__init__(**kwargs)
44+
45+
def initialize(**kwargs):
46+
"""Placeholder for subclass initialization"""
47+
pass
4448

4549
def osipi_fit(self, data=None, bvalues=None, thresholds=None, bounds=None, initial_guess=None, **kwargs):
4650
"""Fits the data with the bvalues
@@ -51,17 +55,17 @@ def osipi_fit(self, data=None, bvalues=None, thresholds=None, bounds=None, initi
5155
# Then check if they are input here, if they are, these should overwrite the attributes
5256
use_bvalues = bvalues if bvalues is not None else self.bvalues
5357
kwargs["bvalues"] = use_bvalues
54-
use_thresholds = thresholds if self.bvalues is None else self.thresholds
55-
use_bounds = bounds if self.bounds is None else self.bounds
56-
use_initial_guess = initial_guess if self.initial_guess is None else self.initial_guess
58+
use_thresholds = thresholds if thresholds is not None else self.thresholds
59+
use_bounds = bounds if bounds is not None else self.bounds
60+
use_initial_guess = initial_guess if initial_guess is not None else self.initial_guess
5761

5862
# Make sure we don't make arrays of None's
5963
if use_bvalues is not None: use_bvalues = np.asarray(use_bvalues)
6064
if use_thresholds is not None: use_thresholds = np.asarray(use_thresholds)
6165
if use_bounds is not None: use_bounds = np.asarray(use_bounds)
6266
if use_initial_guess is not None: use_initial_guess = np.asarray(use_initial_guess)
6367

64-
#args = [data, use_bvalues, use_thresholds]
68+
# args = [data, use_bvalues, use_thresholds]
6569
args = [data, use_thresholds]
6670
if self.required_bounds or self.required_bounds_optional:
6771
args.append(use_bounds)
@@ -77,6 +81,8 @@ def osipi_fit(self, data=None, bvalues=None, thresholds=None, bounds=None, initi
7781

7882
#args = [data, use_bvalues, use_initial_guess, use_bounds, use_thresholds]
7983
args = [arg for arg in args if arg is not None]
84+
# print(args)
85+
# print(kwargs)
8086
results = self.ivim_fit(*args, **kwargs)
8187

8288
#self.parameter_estimates = self.ivim_fit(data, bvalues)

tests/IVIMmodels/unit_tests/algorithms.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
"IAR_LU_modified_topopro",
77
"IAR_LU_segmented_2step",
88
"IAR_LU_segmented_3step",
9-
"IAR_LU_subtracted"
9+
"IAR_LU_subtracted",
10+
"OGC_AmsterdamUMC_Bayesian_biexp",
11+
"OGC_AmsterdamUMC_biexp_segmented",
12+
"OGC_AmsterdamUMC_biexp"
1013
],
1114
"IAR_LU_biexp": {
1215
"tolerances": {

tests/IVIMmodels/unit_tests/analyze.r

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
library(plyr) # order matters, we need dplyr to come later for grouping
2+
library(dplyr)
13
library(tidyverse)
2-
library(plyr)
4+
35

46
plot_ivim <- function(data, fileExtension) {
5-
ggplot(data, aes(x=Algorithm)) + geom_boxplot(aes(y=f_fitted)) + geom_boxplot(color="red", aes(y=f)) + facet_grid(SNR ~ Region) + scale_x_discrete(guide = guide_axis(angle = 90)) + ylim(0, 1) + ggtitle("Perfusion fraction grid") + ylab("Perfusion fraction")
6-
ggsave(paste("f", fileExtension, sep=""), width = 50, height = 50, units = "cm")
7-
ggplot(data, aes(x=Algorithm)) + geom_boxplot(aes(y=D_fitted)) + geom_boxplot(color="red", aes(y=D)) + facet_grid(SNR ~ Region) + scale_x_discrete(guide = guide_axis(angle = 90)) + ggtitle("Diffusion grid") + ylab("Diffusion")
8-
ggsave(paste("D", fileExtension, sep=""), width = 50, height = 50, units = "cm")
9-
ggplot(data, aes(x=Algorithm)) + geom_boxplot(aes(y=Dp_fitted)) + geom_boxplot(color="red", aes(y=Dp)) + facet_grid(SNR ~ Region) + scale_x_discrete(guide = guide_axis(angle = 90)) + ylim(0, 0.25) + ggtitle("Perfusion grid") + ylab("Perfusion")
10-
ggsave(paste("Dp", fileExtension, sep=""), width = 50, height = 50, units = "cm")
7+
f_plot <- ggplot(data, aes(x=Algorithm)) + geom_boxplot(aes(y=f_fitted)) + geom_boxplot(color="red", aes(y=f)) + facet_grid(SNR ~ Region) + scale_x_discrete(guide = guide_axis(angle = 90)) + ylim(0, 1) + ggtitle("Perfusion fraction grid") + ylab("Perfusion fraction")
8+
print(f_plot)
9+
ggsave(paste("f", fileExtension, sep=""), plot=f_plot, width = 50, height = 50, units = "cm")
10+
D_plot <- ggplot(data, aes(x=Algorithm)) + geom_boxplot(aes(y=D_fitted)) + geom_boxplot(color="red", aes(y=D)) + facet_grid(SNR ~ Region) + scale_x_discrete(guide = guide_axis(angle = 90)) + ggtitle("Diffusion grid") + ylab("Diffusion")
11+
print(D_plot)
12+
ggsave(paste("D", fileExtension, sep=""), plot=D_plot, width = 50, height = 50, units = "cm")
13+
Dp_plot <- ggplot(data, aes(x=Algorithm)) + geom_boxplot(aes(y=Dp_fitted)) + geom_boxplot(color="red", aes(y=Dp)) + facet_grid(SNR ~ Region) + scale_x_discrete(guide = guide_axis(angle = 90)) + ylim(0, 0.25) + ggtitle("Perfusion grid") + ylab("Perfusion")
14+
print(Dp_plot)
15+
ggsave(paste("Dp", fileExtension, sep=""), plot=Dp_plot, width = 50, height = 50, units = "cm")
1116
}
1217

1318
data <- read.csv("test_output.csv")
@@ -23,3 +28,17 @@ data_duration$ms <- data_duration$Duration..us./data_duration$Count/1000
2328
ggplot(data_duration, aes(x=Algorithm, y=ms)) + geom_boxplot() + scale_x_discrete(guide = guide_axis(angle = 90)) + ggtitle("Fit Duration") + ylab("Time (ms)")
2429
ggsave("durations.pdf", width = 20, height = 20, units = "cm")
2530

31+
32+
# some kind of black voodoo magic to number unique groups
33+
# data_new <- data %>% group_by(Algorithm, Region, SNR) %>% mutate(index = row_number()) %>% ungroup()
34+
# Then this widens it so we can lm()
35+
data_wide <- data %>% pivot_wider(names_from=Algorithm, values_from=c(f_fitted, Dp_fitted, D_fitted), id_cols=c(Region, SNR, index, f, D, Dp))
36+
# linear fit for f
37+
f_model <- lm(f ~ f_fitted_IAR_LU_biexp + f_fitted_IAR_LU_modified_mix + f_fitted_IAR_LU_modified_topopro + f_fitted_IAR_LU_segmented_2step + f_fitted_IAR_LU_segmented_3step + f_fitted_IAR_LU_subtracted, data=data_wide)
38+
#f_model <- lm(f ~ f_fitted_ETP_SRI_LinearFitting + f_fitted_IAR_LU_biexp + f_fitted_IAR_LU_modified_mix + f_fitted_IAR_LU_modified_topopro + f_fitted_IAR_LU_segmented_2step + f_fitted_IAR_LU_segmented_3step + f_fitted_IAR_LU_subtracted, data=data_new_wide)
39+
D_model <- lm(D ~ D_fitted_ETP_SRI_LinearFitting + D_fitted_IAR_LU_biexp + D_fitted_IAR_LU_modified_topopro + D_fitted_IAR_LU_segmented_2step + D_fitted_IAR_LU_segmented_3step + D_fitted_IAR_LU_subtracted, data=data_wide)
40+
# D_model <- lm(D ~ D_fitted_ETP_SRI_LinearFitting + D_fitted_IAR_LU_biexp + D_fitted_IAR_LU_modified_mix + D_fitted_IAR_LU_modified_topopro + D_fitted_IAR_LU_segmented_2step + D_fitted_IAR_LU_segmented_3step + D_fitted_IAR_LU_subtracted, data=data_new_wide)
41+
Dp_model <- lm(Dp ~ Dp_fitted_IAR_LU_biexp + Dp_fitted_IAR_LU_modified_mix + Dp_fitted_IAR_LU_modified_topopro + Dp_fitted_IAR_LU_segmented_2step + Dp_fitted_IAR_LU_segmented_3step + Dp_fitted_IAR_LU_subtracted, data=data_wide)
42+
# Dp_model <- lm(Dp ~ Dp_fitted_ETP_SRI_LinearFitting + Dp_fitted_IAR_LU_biexp + Dp_fitted_IAR_LU_modified_mix + Dp_fitted_IAR_LU_modified_topopro + Dp_fitted_IAR_LU_segmented_2step + Dp_fitted_IAR_LU_segmented_3step + Dp_fitted_IAR_LU_subtracted, data=data_new_wide)
43+
# predict new data from existing model
44+
predict(object = f_model, newdata = data_wide)

tests/IVIMmodels/unit_tests/test_ivim_synthetic.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ def test_generated(ivim_algorithm, ivim_data, SNR, rtol, atol, fit_count, save_f
2424
f = data["f"]
2525
Dp = data["Dp"]
2626
fit = OsipiBase(algorithm=ivim_algorithm)
27+
# here try a prior, but it's not seeing it, why?
28+
# if hasattr(fit, "accepts_priors") and fit.accepts_priors:
29+
# prior = [np.repeat(D, 10), np.repeat(f, 10), np.repeat(Dp, 10)] # D, f, D*
30+
# fit.initialize(prior_in=prior)
2731
time_delta = datetime.timedelta()
2832
for idx in range(fit_count):
2933
# if "data" not in data:
@@ -34,16 +38,16 @@ def test_generated(ivim_algorithm, ivim_data, SNR, rtol, atol, fit_count, save_f
3438
[f_fit, Dp_fit, D_fit] = fit.osipi_fit(signal, bvals)
3539
time_delta += datetime.datetime.now() - start_time
3640
if save_file:
37-
save_results(save_file, ivim_algorithm, name, SNR, [f, Dp, D], [f_fit, Dp_fit, D_fit])
41+
save_results(save_file, ivim_algorithm, name, SNR, idx, [f, Dp, D], [f_fit, Dp_fit, D_fit])
3842
npt.assert_allclose([f, Dp, D], [f_fit, Dp_fit, D_fit], rtol, atol)
3943
if save_duration_file:
4044
save_duration(save_duration_file, ivim_algorithm, name, SNR, time_delta, fit_count)
4145

4246

43-
def save_results(filename, algorithm, name, SNR, truth, fit):
47+
def save_results(filename, algorithm, name, SNR, idx, truth, fit):
4448
with open(filename, "a") as csv_file:
4549
writer = csv.writer(csv_file, delimiter=',')
46-
data = [algorithm, name, SNR, *truth, *fit]
50+
data = [algorithm, name, SNR, idx, *truth, *fit]
4751
writer.writerow(data)
4852

4953
def save_duration(filename, algorithm, name, SNR, duration, count):

0 commit comments

Comments
 (0)