AOT-biomaps 2.9.167__py3-none-any.whl → 2.9.273__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of AOT-biomaps might be problematic. Click here for more details.
- AOT_biomaps/AOT_Acoustic/StructuredWave.py +2 -2
- AOT_biomaps/AOT_Acoustic/_mainAcoustic.py +14 -7
- AOT_biomaps/AOT_Experiment/Tomography.py +74 -4
- AOT_biomaps/AOT_Experiment/_mainExperiment.py +95 -55
- AOT_biomaps/AOT_Recon/AOT_Optimizers/DEPIERRO.py +48 -13
- AOT_biomaps/AOT_Recon/AOT_Optimizers/LS.py +9 -6
- AOT_biomaps/AOT_Recon/AOT_Optimizers/MAPEM.py +118 -38
- AOT_biomaps/AOT_Recon/AOT_Optimizers/MLEM.py +307 -102
- AOT_biomaps/AOT_Recon/AOT_Optimizers/PDHG.py +1 -1
- AOT_biomaps/AOT_Recon/AOT_PotentialFunctions/RelativeDifferences.py +10 -14
- AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_CSR.py +281 -0
- AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_SELL.py +296 -0
- AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/__init__.py +2 -0
- AOT_biomaps/AOT_Recon/AOT_biomaps_kernels.cubin +0 -0
- AOT_biomaps/AOT_Recon/AlgebraicRecon.py +262 -149
- AOT_biomaps/AOT_Recon/AnalyticRecon.py +27 -42
- AOT_biomaps/AOT_Recon/BayesianRecon.py +84 -151
- AOT_biomaps/AOT_Recon/DeepLearningRecon.py +1 -1
- AOT_biomaps/AOT_Recon/PrimalDualRecon.py +69 -62
- AOT_biomaps/AOT_Recon/ReconEnums.py +27 -2
- AOT_biomaps/AOT_Recon/ReconTools.py +120 -12
- AOT_biomaps/AOT_Recon/__init__.py +1 -0
- AOT_biomaps/AOT_Recon/_mainRecon.py +73 -59
- AOT_biomaps/__init__.py +4 -71
- {aot_biomaps-2.9.167.dist-info → aot_biomaps-2.9.273.dist-info}/METADATA +2 -1
- aot_biomaps-2.9.273.dist-info/RECORD +47 -0
- aot_biomaps-2.9.167.dist-info/RECORD +0 -43
- {aot_biomaps-2.9.167.dist-info → aot_biomaps-2.9.273.dist-info}/WHEEL +0 -0
- {aot_biomaps-2.9.167.dist-info → aot_biomaps-2.9.273.dist-info}/top_level.txt +0 -0
|
@@ -22,7 +22,7 @@ class AnalyticRecon(Recon):
|
|
|
22
22
|
else:
|
|
23
23
|
raise ValueError(f"Unknown analytic reconstruction type: {processType}")
|
|
24
24
|
|
|
25
|
-
def checkExistingFile(self,
|
|
25
|
+
def checkExistingFile(self, date = None):
|
|
26
26
|
raise NotImplementedError("checkExistingFile method is not implemented yet.")
|
|
27
27
|
|
|
28
28
|
def _analyticReconPython(self,withTumor):
|
|
@@ -51,56 +51,41 @@ class AnalyticRecon(Recon):
|
|
|
51
51
|
def _iFourierRecon(self, AOsignal):
|
|
52
52
|
"""
|
|
53
53
|
Reconstruction d'image utilisant la transformation de Fourier inverse.
|
|
54
|
-
|
|
55
|
-
:param AOsignal: Signal dans le domaine temporel.
|
|
54
|
+
:param AOsignal: Signal dans le domaine temporel (shape: N_t, N_theta).
|
|
56
55
|
:return: Image reconstruite dans le domaine spatial.
|
|
57
56
|
"""
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
f_s = np.array([af.f_s for af in self.experiment.AcousticFields]) # spatial freqs (N_theta,)
|
|
63
|
-
f_t = np.fft.fftfreq(AOsignal.shape[0], d=self.experiment.dt) # temporal freqs
|
|
64
|
-
|
|
57
|
+
theta = np.array([af.angle for af in self.experiment.AcousticFields])
|
|
58
|
+
f_s = np.array([af.f_s for af in self.experiment.AcousticFields])
|
|
59
|
+
dt = self.experiment.dt
|
|
60
|
+
f_t = np.fft.fftfreq(AOsignal.shape[0], d=dt) # fréquences temporelles
|
|
65
61
|
x = self.experiment.OpticImage.laser.x
|
|
66
62
|
z = self.experiment.OpticImage.laser.z
|
|
67
|
-
X, Z = np.meshgrid(x, z, indexing='ij') #
|
|
68
|
-
|
|
69
|
-
N_theta = len(theta)
|
|
70
|
-
I_rec = np.zeros((len(x), len(z)), dtype=complex)
|
|
71
|
-
|
|
72
|
-
for i, th in enumerate(trange(N_theta, desc="AOT-BioMaps -- Analytic Recontruction Tomography : iFourier (Processing projection) ---- processing on single CPU ----")):
|
|
73
|
-
fs = f_s[i]
|
|
74
|
-
|
|
75
|
-
# Projection des coordonnées dans le repère tourné
|
|
76
|
-
x_prime = X * np.cos(th) + Z * np.sin(th)
|
|
77
|
-
z_prime = -X * np.sin(th) + Z * np.cos(th)
|
|
78
|
-
|
|
79
|
-
# Signal spectral pour cet angle (1D pour chaque f_t)
|
|
80
|
-
s_angle = s_tilde[:, i] # shape (len(f_t),)
|
|
63
|
+
X, Z = np.meshgrid(x, z, indexing='ij') # grille spatiale (Nx, Nz)
|
|
81
64
|
|
|
82
|
-
|
|
83
|
-
|
|
65
|
+
# Transformée de Fourier du signal
|
|
66
|
+
s_tilde = np.fft.fft(AOsignal, axis=0) # shape: (N_t, N_theta)
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# reshape s_angle to (len(f_t), 1, 1)
|
|
89
|
-
s_angle = s_angle[:, None, None]
|
|
90
|
-
|
|
91
|
-
# Contribution de cet angle
|
|
92
|
-
integrand = s_angle * np.exp(phase)
|
|
93
|
-
|
|
94
|
-
# Intégration sur f_t (somme discrète)
|
|
95
|
-
I_theta = np.sum(integrand, axis=0)
|
|
96
|
-
|
|
97
|
-
# Ajout à la reconstruction
|
|
98
|
-
I_rec += I_theta
|
|
99
|
-
|
|
100
|
-
I_rec /= N_theta
|
|
68
|
+
# Initialisation de l'image reconstruite
|
|
69
|
+
I_rec = np.zeros((len(x), len(z)), dtype=complex)
|
|
101
70
|
|
|
71
|
+
# Boucle sur les angles
|
|
72
|
+
for i, th in enumerate(trange(len(theta), desc="AOT-BioMaps -- iFourier Reconstruction")):
|
|
73
|
+
# Coordonnées tournées
|
|
74
|
+
X_prime = X * np.cos(th) + Z * np.sin(th)
|
|
75
|
+
Z_prime = -X * np.sin(th) + Z * np.cos(th)
|
|
76
|
+
|
|
77
|
+
# Pour chaque fréquence temporelle f_t[j]
|
|
78
|
+
for j in range(len(f_t)):
|
|
79
|
+
# Phase: exp(2jπ (X_prime * f_s[i] + Z_prime * f_t[j]))
|
|
80
|
+
phase = 2j * np.pi * (X_prime * f_s[i] + Z_prime * f_t[j])
|
|
81
|
+
# Contribution de cette fréquence
|
|
82
|
+
I_rec += s_tilde[j, i] * np.exp(phase) * dt # Pondération par dt pour l'intégration
|
|
83
|
+
|
|
84
|
+
# Normalisation
|
|
85
|
+
I_rec /= len(theta)
|
|
102
86
|
return np.abs(I_rec)
|
|
103
87
|
|
|
88
|
+
|
|
104
89
|
def _iRadonRecon(self, AOsignal):
|
|
105
90
|
"""
|
|
106
91
|
Reconstruction d'image utilisant la méthode iRadon.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from AOT_biomaps.AOT_Recon.AlgebraicRecon import AlgebraicRecon
|
|
2
2
|
from AOT_biomaps.AOT_Recon.ReconEnums import ReconType, OptimizerType, PotentialType, ProcessType
|
|
3
3
|
from .ReconTools import check_gpu_memory, calculate_memory_requirement
|
|
4
|
-
from .AOT_Optimizers import MAPEM, DEPIERRO
|
|
4
|
+
from .AOT_Optimizers import MAPEM, MAPEM_STOP, DEPIERRO
|
|
5
5
|
from AOT_biomaps.Config import config
|
|
6
6
|
|
|
7
7
|
import warnings
|
|
@@ -38,7 +38,7 @@ class BayesianRecon(AlgebraicRecon):
|
|
|
38
38
|
if not isinstance(self.potentialFunction, PotentialType):
|
|
39
39
|
raise TypeError(f"Potential functions must be of type PotentialType, got {type(self.potentialFunction)}")
|
|
40
40
|
|
|
41
|
-
def checkExistingFile(self, date=None
|
|
41
|
+
def checkExistingFile(self, date = None):
|
|
42
42
|
"""
|
|
43
43
|
Check if the reconstruction file already exists, based on current instance parameters.
|
|
44
44
|
|
|
@@ -66,27 +66,18 @@ class BayesianRecon(AlgebraicRecon):
|
|
|
66
66
|
dir_name += f'_Beta_{self.beta}_Sigma_{self.sigma}'
|
|
67
67
|
|
|
68
68
|
results_dir = os.path.join(self.saveDir, dir_name)
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if not os.path.exists(results_dir):
|
|
70
|
+
os.makedirs(results_dir)
|
|
71
71
|
|
|
72
|
-
if os.path.exists(
|
|
73
|
-
return (True,
|
|
72
|
+
if os.path.exists(os.path.join(results_dir,"indices.npy")):
|
|
73
|
+
return (True, results_dir)
|
|
74
74
|
|
|
75
|
-
return (False,
|
|
75
|
+
return (False, results_dir)
|
|
76
76
|
|
|
77
|
-
def load(self, withTumor=True, results_date=None, optimizer=None, potential_function=None,
|
|
77
|
+
def load(self, withTumor=True, results_date=None, optimizer=None, potential_function=None, filePath=None, show_logs=True):
|
|
78
78
|
"""
|
|
79
|
-
Load the reconstruction results and indices for Bayesian reconstruction and store them in self.
|
|
80
|
-
|
|
81
|
-
withTumor (bool): If True, loads the reconstruction with tumor; otherwise, loads the reconstruction without tumor.
|
|
82
|
-
results_date (str): Date string (format "ddmm") to specify which results to load. If None, uses the most recent date in saveDir.
|
|
83
|
-
optimizer (OptimizerType): Optimizer type to filter results. If None, uses the current optimizer of the instance.
|
|
84
|
-
potential_function (PotentialType): Potential function type to filter results. If None, uses the current potential function of the instance.
|
|
85
|
-
beta (float): Beta parameter to match the saved directory. If None, skips this filter.
|
|
86
|
-
delta (float): Delta parameter to match the saved directory. If None, skips this filter.
|
|
87
|
-
gamma (float): Gamma parameter to match the saved directory. If None, skips this filter.
|
|
88
|
-
sigma (float): Sigma parameter to match the saved directory. If None, skips this filter.
|
|
89
|
-
filePath (str): Optional. If provided, loads directly from this path (overrides saveDir and results_date).
|
|
79
|
+
Load the reconstruction results and indices as lists of 2D np arrays for Bayesian reconstruction and store them in self.
|
|
80
|
+
If the loaded file is a 3D array, it is split into a list of 2D arrays.
|
|
90
81
|
"""
|
|
91
82
|
if filePath is not None:
|
|
92
83
|
# Mode chargement direct depuis un fichier
|
|
@@ -94,53 +85,55 @@ class BayesianRecon(AlgebraicRecon):
|
|
|
94
85
|
recon_path = filePath
|
|
95
86
|
if not os.path.exists(recon_path):
|
|
96
87
|
raise FileNotFoundError(f"No reconstruction file found at {recon_path}.")
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
# Charge le fichier (3D ou liste de 2D)
|
|
89
|
+
data = np.load(recon_path, allow_pickle=True)
|
|
90
|
+
# Découpe en liste de 2D si c'est un tableau 3D
|
|
91
|
+
if isinstance(data, np.ndarray) and data.ndim == 3:
|
|
92
|
+
if withTumor:
|
|
93
|
+
self.reconPhantom = [data[i, :, :] for i in range(data.shape[0])]
|
|
94
|
+
else:
|
|
95
|
+
self.reconLaser = [data[i, :, :] for i in range(data.shape[0])]
|
|
100
96
|
else:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
# Sinon, suppose que c'est déjà une liste de 2D
|
|
98
|
+
if withTumor:
|
|
99
|
+
self.reconPhantom = data
|
|
100
|
+
else:
|
|
101
|
+
self.reconLaser = data
|
|
102
|
+
# Essayer de charger les indices
|
|
103
|
+
base_dir, _ = os.path.split(recon_path)
|
|
104
|
+
indices_path = os.path.join(base_dir, 'indices.npy')
|
|
107
105
|
if os.path.exists(indices_path):
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if os.path.exists(indices_path):
|
|
112
|
-
self.indices = np.load(indices_path, allow_pickle=True)
|
|
106
|
+
indices_data = np.load(indices_path, allow_pickle=True)
|
|
107
|
+
if isinstance(indices_data, np.ndarray) and indices_data.ndim == 3:
|
|
108
|
+
self.indices = [indices_data[i, :, :] for i in range(indices_data.shape[0])]
|
|
113
109
|
else:
|
|
114
|
-
self.indices =
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
self.indices = indices_data
|
|
111
|
+
else:
|
|
112
|
+
self.indices = None
|
|
113
|
+
if show_logs:
|
|
114
|
+
print(f"Loaded reconstruction results and indices from {recon_path}")
|
|
117
115
|
else:
|
|
118
116
|
# Mode chargement depuis le répertoire de résultats
|
|
119
117
|
if self.saveDir is None:
|
|
120
118
|
raise ValueError("Save directory is not specified. Please set saveDir before loading.")
|
|
121
|
-
|
|
122
119
|
# Use current optimizer and potential function if not provided
|
|
123
120
|
opt_name = optimizer.value if optimizer is not None else self.optimizer.value
|
|
124
121
|
pot_name = potential_function.value if potential_function is not None else self.potentialFunction.value
|
|
125
|
-
|
|
126
122
|
# Build the base directory pattern
|
|
127
123
|
dir_pattern = f'results_*_{opt_name}_{pot_name}'
|
|
128
|
-
|
|
129
124
|
# Add parameters to the pattern based on the optimizer
|
|
130
125
|
if optimizer is None:
|
|
131
126
|
optimizer = self.optimizer
|
|
132
|
-
|
|
133
127
|
if optimizer == OptimizerType.PPGMLEM:
|
|
134
|
-
beta_str = f'_Beta_{
|
|
135
|
-
delta_str = f'_Delta_{
|
|
136
|
-
gamma_str = f'_Gamma_{
|
|
137
|
-
sigma_str = f'_Sigma_{
|
|
128
|
+
beta_str = f'_Beta_{self.beta}'
|
|
129
|
+
delta_str = f'_Delta_{self.delta}'
|
|
130
|
+
gamma_str = f'_Gamma_{self.gamma}'
|
|
131
|
+
sigma_str = f'_Sigma_{self.sigma}'
|
|
138
132
|
dir_pattern += f'{beta_str}{delta_str}{gamma_str}{sigma_str}'
|
|
139
133
|
elif optimizer in (OptimizerType.PGC, OptimizerType.DEPIERRO95):
|
|
140
|
-
beta_str = f'_Beta_{
|
|
141
|
-
sigma_str = f'_Sigma_{
|
|
134
|
+
beta_str = f'_Beta_{self.beta}'
|
|
135
|
+
sigma_str = f'_Sigma_{self.sigma}'
|
|
142
136
|
dir_pattern += f'{beta_str}{sigma_str}'
|
|
143
|
-
|
|
144
137
|
# Find the most recent results directory if no date is specified
|
|
145
138
|
if results_date is None:
|
|
146
139
|
dirs = [d for d in os.listdir(self.saveDir) if os.path.isdir(os.path.join(self.saveDir, d)) and dir_pattern in d]
|
|
@@ -151,33 +144,40 @@ class BayesianRecon(AlgebraicRecon):
|
|
|
151
144
|
else:
|
|
152
145
|
results_dir = os.path.join(self.saveDir, f'results_{results_date}_{opt_name}_{pot_name}')
|
|
153
146
|
if optimizer == OptimizerType.PPGMLEM:
|
|
154
|
-
results_dir += f'_Beta_{
|
|
147
|
+
results_dir += f'_Beta_{self.beta}_Delta_{self.delta}_Gamma_{self.gamma}_Sigma_{self.sigma}'
|
|
155
148
|
elif optimizer in (OptimizerType.PGC, OptimizerType.DEPIERRO95):
|
|
156
|
-
results_dir += f'_Beta_{
|
|
149
|
+
results_dir += f'_Beta_{self.beta}_Sigma_{self.sigma}'
|
|
157
150
|
if not os.path.exists(results_dir):
|
|
158
151
|
raise FileNotFoundError(f"Directory {results_dir} does not exist.")
|
|
159
|
-
|
|
160
152
|
# Load reconstruction results
|
|
161
153
|
recon_key = 'reconPhantom' if withTumor else 'reconLaser'
|
|
162
154
|
recon_path = os.path.join(results_dir, f'{recon_key}.npy')
|
|
163
155
|
if not os.path.exists(recon_path):
|
|
164
156
|
raise FileNotFoundError(f"No reconstruction file found at {recon_path}.")
|
|
165
|
-
|
|
166
|
-
if
|
|
167
|
-
|
|
157
|
+
data = np.load(recon_path, allow_pickle=True)
|
|
158
|
+
if isinstance(data, np.ndarray) and data.ndim == 3:
|
|
159
|
+
if withTumor:
|
|
160
|
+
self.reconPhantom = [data[i, :, :] for i in range(data.shape[0])]
|
|
161
|
+
else:
|
|
162
|
+
self.reconLaser = [data[i, :, :] for i in range(data.shape[0])]
|
|
168
163
|
else:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
if withTumor:
|
|
165
|
+
self.reconPhantom = data
|
|
166
|
+
else:
|
|
167
|
+
self.reconLaser = data
|
|
168
|
+
# Load saved indices as list of 2D arrays
|
|
169
|
+
indices_path = os.path.join(results_dir, 'indices.npy')
|
|
173
170
|
if not os.path.exists(indices_path):
|
|
174
171
|
raise FileNotFoundError(f"No indices file found at {indices_path}.")
|
|
172
|
+
indices_data = np.load(indices_path, allow_pickle=True)
|
|
173
|
+
if isinstance(indices_data, np.ndarray) and indices_data.ndim == 3:
|
|
174
|
+
self.indices = [indices_data[i, :, :] for i in range(indices_data.shape[0])]
|
|
175
|
+
else:
|
|
176
|
+
self.indices = indices_data
|
|
177
|
+
if show_logs:
|
|
178
|
+
print(f"Loaded reconstruction results and indices from {results_dir}")
|
|
175
179
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
print(f"Loaded reconstruction results and indices from {results_dir}")
|
|
179
|
-
|
|
180
|
-
def run(self, processType=ProcessType.PYTHON, withTumor=True):
|
|
180
|
+
def run(self, processType=ProcessType.PYTHON, withTumor=True, show_logs=True):
|
|
181
181
|
"""
|
|
182
182
|
This method is a placeholder for the Bayesian reconstruction process.
|
|
183
183
|
It currently does not perform any operations but serves as a template for future implementations.
|
|
@@ -189,109 +189,42 @@ class BayesianRecon(AlgebraicRecon):
|
|
|
189
189
|
else:
|
|
190
190
|
raise ValueError(f"Unknown Bayesian reconstruction type: {processType}")
|
|
191
191
|
|
|
192
|
-
def _bayesianReconCASToR(self, withTumor):
|
|
192
|
+
def _bayesianReconCASToR(self, show_logs, withTumor):
|
|
193
193
|
raise NotImplementedError("CASToR Bayesian reconstruction is not implemented yet.")
|
|
194
194
|
|
|
195
|
-
def _bayesianReconPython(self, withTumor):
|
|
196
|
-
|
|
195
|
+
def _bayesianReconPython(self, show_logs, withTumor):
|
|
197
196
|
if withTumor:
|
|
198
197
|
if self.experiment.AOsignal_withTumor is None:
|
|
199
198
|
raise ValueError("AO signal with tumor is not available. Please generate AO signal with tumor the experiment first in the experiment object.")
|
|
200
199
|
if self.optimizer.value == OptimizerType.PPGMLEM.value:
|
|
201
|
-
self.reconPhantom, self.indices =
|
|
200
|
+
self.reconPhantom, self.indices = MAPEM_STOP(
|
|
201
|
+
SMatrix=self.SMatrix,
|
|
202
|
+
y=self.experiment.AOsignal_withTumor,
|
|
203
|
+
Omega=self.potentialFunction,
|
|
204
|
+
beta=self.beta,
|
|
205
|
+
delta=self.delta,
|
|
206
|
+
gamma=self.gamma,
|
|
207
|
+
sigma=self.sigma,
|
|
208
|
+
numIterations=self.numIterations,
|
|
209
|
+
isSavingEachIteration=self.isSavingEachIteration,
|
|
210
|
+
withTumor=withTumor,
|
|
211
|
+
device=self.device,
|
|
212
|
+
max_saves=5000,
|
|
213
|
+
show_logs=True)
|
|
202
214
|
elif self.optimizer.value == OptimizerType.PGC.value:
|
|
203
|
-
self.reconPhantom, self.indices =
|
|
215
|
+
self.reconPhantom, self.indices = MAPEM(SMatrix=self.SMatrix, y=self.experiment.AOsignal_withTumor, withTumor=withTumor, show_logs=show_logs)
|
|
204
216
|
elif self.optimizer.value == OptimizerType.DEPIERRO95.value:
|
|
205
|
-
self.reconPhantom, self.indices =
|
|
217
|
+
self.reconPhantom, self.indices = DEPIERRO(SMatrix=self.SMatrix, y=self.experiment.AOsignal_withTumor, withTumor=withTumor, show_logs=show_logs)
|
|
206
218
|
else:
|
|
207
219
|
raise ValueError(f"Unknown optimizer type: {self.optimizer.value}")
|
|
208
220
|
else:
|
|
209
221
|
if self.experiment.AOsignal_withoutTumor is None:
|
|
210
222
|
raise ValueError("AO signal without tumor is not available. Please generate AO signal without tumor the experiment first in the experiment object.")
|
|
211
223
|
if self.optimizer.value == OptimizerType.PPGMLEM.value:
|
|
212
|
-
self.reconLaser, self.indices =
|
|
224
|
+
self.reconLaser, self.indices = MAPEM_STOP(SMatrix=self.SMatrix, y=self.experiment.AOsignal_withoutTumor, withTumor=withTumor, show_logs=show_logs)
|
|
213
225
|
elif self.optimizer.value == OptimizerType.PGC.value:
|
|
214
|
-
self.reconLaser, self.indices =
|
|
226
|
+
self.reconLaser, self.indices = MAPEM(SMatrix=self.SMatrix, y=self.experiment.AOsignal_withoutTumor, withTumor=withTumor, show_logs=show_logs)
|
|
215
227
|
elif self.optimizer.value == OptimizerType.DEPIERRO95.value:
|
|
216
|
-
self.reconLaser, self.indices =
|
|
228
|
+
self.reconLaser, self.indices = DEPIERRO(SMatrix=self.SMatrix, y=self.experiment.AOsignal_withoutTumor, withTumor=withTumor, show_logs=show_logs)
|
|
217
229
|
else:
|
|
218
|
-
raise ValueError(f"Unknown optimizer type: {self.optimizer.value}")
|
|
219
|
-
|
|
220
|
-
def _MAPEM_STOP(self, SMatrix, y, withTumor):
|
|
221
|
-
"""
|
|
222
|
-
This method implements the MAPEM_STOP algorithm using either CPU or single-GPU PyTorch acceleration.
|
|
223
|
-
Multi-GPU and Multi-CPU modes are not implemented for this algorithm.
|
|
224
|
-
"""
|
|
225
|
-
result = None
|
|
226
|
-
required_memory = calculate_memory_requirement(SMatrix, y)
|
|
227
|
-
|
|
228
|
-
if self.isGPU:
|
|
229
|
-
if check_gpu_memory(config.select_best_gpu(), required_memory):
|
|
230
|
-
try:
|
|
231
|
-
result = MAPEM._MAPEM_GPU_STOP(SMatrix=SMatrix, y=y, Omega=self.potentialFunction, numIterations=self.numIterations, beta=self.beta, delta=self.delta, gamma=self.gamma, sigma=self.sigma, isSavingEachIteration=self.isSavingEachIteration, withTumor=withTumor)
|
|
232
|
-
except Exception as e:
|
|
233
|
-
warnings.warn(f"Falling back to CPU implementation due to an error in GPU implementation: {e}")
|
|
234
|
-
else:
|
|
235
|
-
warnings.warn("Insufficient GPU memory for single GPU MAPEM_STOP. Falling back to CPU.")
|
|
236
|
-
|
|
237
|
-
if result is None:
|
|
238
|
-
try:
|
|
239
|
-
result = MAPEM._MAPEM_CPU_STOP(SMatrix=SMatrix, y=y, Omega=self.potentialFunction, numIterations=self.numIterations, beta=self.beta, delta=self.delta, gamma=self.gamma, sigma=self.sigma, isSavingEachIteration=self.isSavingEachIteration, withTumor=withTumor)
|
|
240
|
-
except Exception as e:
|
|
241
|
-
warnings.warn(f"An error occurred in CPU implementation: {e}")
|
|
242
|
-
result = None
|
|
243
|
-
|
|
244
|
-
return result
|
|
245
|
-
|
|
246
|
-
def _MAPEM(self, SMatrix, y, withTumor):
|
|
247
|
-
"""
|
|
248
|
-
This method implements the MAPEM algorithm using either CPU or single-GPU PyTorch acceleration.
|
|
249
|
-
Multi-GPU and Multi-CPU modes are not implemented for this algorithm.
|
|
250
|
-
"""
|
|
251
|
-
result = None
|
|
252
|
-
required_memory = calculate_memory_requirement(SMatrix, y)
|
|
253
|
-
|
|
254
|
-
if self.isGPU:
|
|
255
|
-
if check_gpu_memory(config.select_best_gpu(), required_memory):
|
|
256
|
-
try:
|
|
257
|
-
result = MAPEM._MAPEM_GPU(SMatrix=SMatrix, y=y, Omega=self.potentialFunction, numIterations=self.numIterations, beta=self.beta, delta=self.delta, gamma=self.gamma, sigma=self.sigma, isSavingEachIteration=self.isSavingEachIteration, withTumor=withTumor)
|
|
258
|
-
except Exception as e:
|
|
259
|
-
warnings.warn(f"Falling back to CPU implementation due to an error in GPU implementation: {e}")
|
|
260
|
-
else:
|
|
261
|
-
warnings.warn("Insufficient GPU memory for single GPU MAPEM. Falling back to CPU.")
|
|
262
|
-
|
|
263
|
-
if result is None:
|
|
264
|
-
try:
|
|
265
|
-
result = MAPEM._MAPEM_CPU(SMatrix=SMatrix, y=y, Omega=self.potentialFunction, numIterations=self.numIterations, beta=self.beta, delta=self.delta, gamma=self.gamma, sigma=self.sigma, isSavingEachIteration=self.isSavingEachIteration, withTumor=withTumor)
|
|
266
|
-
except Exception as e:
|
|
267
|
-
warnings.warn(f"An error occurred in CPU implementation: {e}")
|
|
268
|
-
result = None
|
|
269
|
-
|
|
270
|
-
return result
|
|
271
|
-
|
|
272
|
-
def _DEPIERRO(self, SMatrix, y, withTumor):
|
|
273
|
-
"""
|
|
274
|
-
This method implements the DEPIERRO algorithm using either CPU or single-GPU PyTorch acceleration.
|
|
275
|
-
Multi-GPU and Multi-CPU modes are not implemented for this algorithm.
|
|
276
|
-
"""
|
|
277
|
-
result = None
|
|
278
|
-
required_memory = calculate_memory_requirement(SMatrix, y)
|
|
279
|
-
|
|
280
|
-
if self.isGPU:
|
|
281
|
-
if check_gpu_memory(config.select_best_gpu(), required_memory):
|
|
282
|
-
try:
|
|
283
|
-
result = DEPIERRO._DEPIERRO_GPU(SMatrix=SMatrix, y=y, Omega=self.potentialFunction, numIterations=self.numIterations, beta=self.beta, sigma=self.sigma, isSavingEachIteration=self.isSavingEachIteration, withTumor=withTumor)
|
|
284
|
-
except Exception as e:
|
|
285
|
-
warnings.warn(f"Falling back to CPU implementation due to an error in GPU implementation: {e}")
|
|
286
|
-
else:
|
|
287
|
-
warnings.warn("Insufficient GPU memory for single GPU DEPIERRO. Falling back to CPU.")
|
|
288
|
-
|
|
289
|
-
if result is None:
|
|
290
|
-
try:
|
|
291
|
-
result = DEPIERRO._DEPIERRO_CPU(SMatrix=SMatrix, y=y, Omega=self.potentialFunction, numIterations=self.numIterations, beta=self.beta, sigma=self.sigma, isSavingEachIteration=self.isSavingEachIteration, withTumor=withTumor)
|
|
292
|
-
except Exception as e:
|
|
293
|
-
warnings.warn(f"An error occurred in CPU implementation: {e}")
|
|
294
|
-
result = None
|
|
295
|
-
|
|
296
|
-
return result
|
|
297
|
-
|
|
230
|
+
raise ValueError(f"Unknown optimizer type: {self.optimizer.value}")
|
|
@@ -31,5 +31,5 @@ class DeepLearningRecon(Recon):
|
|
|
31
31
|
def _deepLearningReconPython(self):
|
|
32
32
|
pass
|
|
33
33
|
|
|
34
|
-
def checkExistingFile(self,
|
|
34
|
+
def checkExistingFile(self, date = None):
|
|
35
35
|
raise NotImplementedError("checkExistingFile method is not implemented yet.")
|
|
@@ -6,16 +6,16 @@ from AOT_biomaps.AOT_Recon.ReconEnums import OptimizerType
|
|
|
6
6
|
import os
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
import numpy as np
|
|
9
|
+
import re
|
|
9
10
|
|
|
10
11
|
class PrimalDualRecon(AlgebraicRecon):
|
|
11
12
|
"""
|
|
12
13
|
This class implements the convex reconstruction process.
|
|
13
14
|
It currently does not perform any operations but serves as a template for future implementations.
|
|
14
15
|
"""
|
|
15
|
-
def __init__(self,
|
|
16
|
+
def __init__(self, theta=1.0, L=None, **kwargs):
|
|
16
17
|
super().__init__(**kwargs)
|
|
17
18
|
self.reconType = ReconType.Convex
|
|
18
|
-
self.alpha = alpha # regularization parameter
|
|
19
19
|
self.theta = theta # relaxation parameter (between 1 and 2)
|
|
20
20
|
self.L = L # norme spectrale de l'opérateur linéaire défini par les matrices P et P^T
|
|
21
21
|
|
|
@@ -35,40 +35,34 @@ class PrimalDualRecon(AlgebraicRecon):
|
|
|
35
35
|
raise NotImplementedError("CASToR convex reconstruction is not implemented yet.")
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def checkExistingFile(self, date = None
|
|
38
|
+
def checkExistingFile(self, date = None):
|
|
39
39
|
"""
|
|
40
40
|
Check if the file already exists, based on current instance parameters.
|
|
41
41
|
Returns:
|
|
42
42
|
tuple: (bool: whether to save, str: the filepath)
|
|
43
43
|
"""
|
|
44
|
-
date
|
|
44
|
+
if date is None:
|
|
45
|
+
date = datetime.now().strftime("%d%m")
|
|
45
46
|
results_dir = os.path.join(
|
|
46
47
|
self.saveDir,
|
|
47
48
|
f'results_{date}_{self.optimizer.value}_Alpha_{self.alpha}_Theta_{self.theta}_L_{self.L}'
|
|
48
49
|
)
|
|
49
50
|
os.makedirs(results_dir, exist_ok=True)
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
if os.path.exists(os.path.join(results_dir,"indices.npy")):
|
|
53
|
+
return (True, results_dir)
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
return (True, filepath)
|
|
55
|
+
return (False, results_dir)
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def load(self, withTumor=True, results_date=None, optimizer=None, alpha=None, theta=None, L=None, filePath=None):
|
|
57
|
+
def load(self, withTumor=True, results_date=None, optimizer=None, filePath=None, show_logs=True):
|
|
62
58
|
"""
|
|
63
|
-
Load the reconstruction results and indices
|
|
59
|
+
Load the reconstruction results (reconPhantom or reconLaser) and indices as lists of 2D np arrays into self.
|
|
60
|
+
If the loaded file is a 3D array, it is split into a list of 2D arrays.
|
|
64
61
|
Args:
|
|
65
|
-
withTumor
|
|
66
|
-
results_date
|
|
67
|
-
optimizer
|
|
68
|
-
|
|
69
|
-
theta (float): Theta parameter to match the saved directory. If None, uses the current theta of the instance.
|
|
70
|
-
L (float): L parameter to match the saved directory. If None, uses the current L of the instance.
|
|
71
|
-
filePath (str): Optional. If provided, loads directly from this path (overrides saveDir and results_date).
|
|
62
|
+
withTumor: If True, loads reconPhantom (with tumor), else reconLaser (without tumor).
|
|
63
|
+
results_date: Date string (format "ddmm") to specify which results to load. If None, uses the most recent date in saveDir.
|
|
64
|
+
optimizer: Optimizer name (as string or enum) to filter results. If None, uses the current optimizer of the instance.
|
|
65
|
+
filePath: Optional. If provided, loads directly from this path (overrides saveDir and results_date).
|
|
72
66
|
"""
|
|
73
67
|
if filePath is not None:
|
|
74
68
|
# Mode chargement direct depuis un fichier
|
|
@@ -76,73 +70,86 @@ class PrimalDualRecon(AlgebraicRecon):
|
|
|
76
70
|
recon_path = filePath
|
|
77
71
|
if not os.path.exists(recon_path):
|
|
78
72
|
raise FileNotFoundError(f"No reconstruction file found at {recon_path}.")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
# Charge les données
|
|
74
|
+
data = np.load(recon_path, allow_pickle=True)
|
|
75
|
+
# Découpe en liste de 2D si c'est un tableau 3D
|
|
76
|
+
if isinstance(data, np.ndarray) and data.ndim == 3:
|
|
77
|
+
if withTumor:
|
|
78
|
+
self.reconPhantom = [data[i, :, :] for i in range(data.shape[0])]
|
|
79
|
+
else:
|
|
80
|
+
self.reconLaser = [data[i, :, :] for i in range(data.shape[0])]
|
|
82
81
|
else:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
# Sinon, suppose que c'est déjà une liste de 2D
|
|
83
|
+
if withTumor:
|
|
84
|
+
self.reconPhantom = data
|
|
85
|
+
else:
|
|
86
|
+
self.reconLaser = data
|
|
87
|
+
# Essayer de charger les indices
|
|
88
|
+
base_dir, _ = os.path.split(recon_path)
|
|
89
|
+
indices_path = os.path.join(base_dir, "indices.npy")
|
|
92
90
|
if os.path.exists(indices_path):
|
|
93
|
-
|
|
91
|
+
indices_data = np.load(indices_path, allow_pickle=True)
|
|
92
|
+
if isinstance(indices_data, np.ndarray) and indices_data.ndim == 3:
|
|
93
|
+
self.indices = [indices_data[i, :, :] for i in range(indices_data.shape[0])]
|
|
94
|
+
else:
|
|
95
|
+
self.indices = indices_data
|
|
94
96
|
else:
|
|
95
97
|
self.indices = None
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
if show_logs:
|
|
99
|
+
print(f"Loaded reconstruction results and indices from {recon_path}")
|
|
98
100
|
else:
|
|
99
101
|
# Mode chargement depuis le répertoire de résultats
|
|
100
102
|
if self.saveDir is None:
|
|
101
103
|
raise ValueError("Save directory is not specified. Please set saveDir before loading.")
|
|
102
|
-
|
|
103
|
-
# Use current optimizer if not provided
|
|
104
|
+
# Determine optimizer name for path matching
|
|
104
105
|
opt_name = optimizer.value if optimizer is not None else self.optimizer.value
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
# Find the most recent results directory if no date is specified
|
|
107
|
+
dir_pattern = f'results_*_{opt_name}'
|
|
108
|
+
if opt_name == OptimizerType.CP_TV.value or opt_name == OptimizerType.CP_KL.value:
|
|
109
|
+
dir_pattern += f'_Alpha_{self.alpha}_Theta_{self.theta}_L_{self.L}'
|
|
107
110
|
if results_date is None:
|
|
108
|
-
dir_pattern = f'results_*_{opt_name}_Alpha_{alpha if alpha is not None else self.alpha}_Theta_{theta if theta is not None else self.theta}_L_{L if L is not None else self.L}'
|
|
109
111
|
dirs = [d for d in os.listdir(self.saveDir) if os.path.isdir(os.path.join(self.saveDir, d)) and dir_pattern in d]
|
|
110
112
|
if not dirs:
|
|
111
113
|
raise FileNotFoundError(f"No matching results directory found for pattern '{dir_pattern}' in {self.saveDir}.")
|
|
112
114
|
dirs.sort(reverse=True) # Most recent first
|
|
113
115
|
results_dir = os.path.join(self.saveDir, dirs[0])
|
|
114
116
|
else:
|
|
115
|
-
results_dir = os.path.join(self.saveDir, f'results_{results_date}_{opt_name}
|
|
117
|
+
results_dir = os.path.join(self.saveDir, f'results_{results_date}_{opt_name}')
|
|
118
|
+
if opt_name == OptimizerType.CP_TV.value or opt_name == OptimizerType.CP_KL.value:
|
|
119
|
+
results_dir += f'_Alpha_{self.alpha}_Theta_{self.theta}_L_{self.L}'
|
|
116
120
|
if not os.path.exists(results_dir):
|
|
117
121
|
raise FileNotFoundError(f"Directory {results_dir} does not exist.")
|
|
118
|
-
|
|
119
122
|
# Load reconstruction results
|
|
120
123
|
recon_key = 'reconPhantom' if withTumor else 'reconLaser'
|
|
121
124
|
recon_path = os.path.join(results_dir, f'{recon_key}.npy')
|
|
122
125
|
if not os.path.exists(recon_path):
|
|
123
126
|
raise FileNotFoundError(f"No reconstruction file found at {recon_path}.")
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
data = np.load(recon_path, allow_pickle=True)
|
|
128
|
+
# Découpe en liste de 2D si c'est un tableau 3D
|
|
129
|
+
if isinstance(data, np.ndarray) and data.ndim == 3:
|
|
130
|
+
if withTumor:
|
|
131
|
+
self.reconPhantom = [data[i, :, :] for i in range(data.shape[0])]
|
|
132
|
+
else:
|
|
133
|
+
self.reconLaser = [data[i, :, :] for i in range(data.shape[0])]
|
|
127
134
|
else:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
if withTumor:
|
|
136
|
+
self.reconPhantom = data
|
|
137
|
+
else:
|
|
138
|
+
self.reconLaser = data
|
|
139
|
+
# Try to load saved indices (if file exists)
|
|
140
|
+
indices_path = os.path.join(results_dir, 'indices.npy')
|
|
141
|
+
if os.path.exists(indices_path):
|
|
142
|
+
indices_data = np.load(indices_path, allow_pickle=True)
|
|
143
|
+
if isinstance(indices_data, np.ndarray) and indices_data.ndim == 3:
|
|
144
|
+
self.indices = [indices_data[i, :, :] for i in range(indices_data.shape[0])]
|
|
145
|
+
else:
|
|
146
|
+
self.indices = indices_data
|
|
147
|
+
else:
|
|
148
|
+
self.indices = None
|
|
149
|
+
if show_logs:
|
|
150
|
+
print(f"Loaded reconstruction results and indices from {results_dir}")
|
|
138
151
|
|
|
139
152
|
def _convexReconPython(self, withTumor):
|
|
140
|
-
if withTumor:
|
|
141
|
-
y=self.experiment.AOsignal_withTumor
|
|
142
|
-
|
|
143
|
-
else:
|
|
144
|
-
y=self.experiment.AOsignal_withoutTumor
|
|
145
|
-
|
|
146
153
|
if self.optimizer == OptimizerType.CP_TV:
|
|
147
154
|
if withTumor:
|
|
148
155
|
self.reconPhantom, self.indices = CP_TV(
|