AOT-biomaps 2.9.186__py3-none-any.whl → 2.9.294__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.

Files changed (28) hide show
  1. AOT_biomaps/AOT_Acoustic/StructuredWave.py +2 -2
  2. AOT_biomaps/AOT_Acoustic/_mainAcoustic.py +11 -6
  3. AOT_biomaps/AOT_Experiment/Tomography.py +74 -4
  4. AOT_biomaps/AOT_Experiment/_mainExperiment.py +95 -55
  5. AOT_biomaps/AOT_Recon/AOT_Optimizers/DEPIERRO.py +48 -13
  6. AOT_biomaps/AOT_Recon/AOT_Optimizers/LS.py +406 -13
  7. AOT_biomaps/AOT_Recon/AOT_Optimizers/MAPEM.py +118 -38
  8. AOT_biomaps/AOT_Recon/AOT_Optimizers/MLEM.py +303 -102
  9. AOT_biomaps/AOT_Recon/AOT_Optimizers/PDHG.py +443 -12
  10. AOT_biomaps/AOT_Recon/AOT_PotentialFunctions/RelativeDifferences.py +10 -14
  11. AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_CSR.py +274 -0
  12. AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_SELL.py +328 -0
  13. AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/__init__.py +2 -0
  14. AOT_biomaps/AOT_Recon/AOT_biomaps_kernels.cubin +0 -0
  15. AOT_biomaps/AOT_Recon/AlgebraicRecon.py +243 -113
  16. AOT_biomaps/AOT_Recon/AnalyticRecon.py +26 -41
  17. AOT_biomaps/AOT_Recon/BayesianRecon.py +81 -146
  18. AOT_biomaps/AOT_Recon/PrimalDualRecon.py +157 -94
  19. AOT_biomaps/AOT_Recon/ReconEnums.py +27 -2
  20. AOT_biomaps/AOT_Recon/ReconTools.py +229 -12
  21. AOT_biomaps/AOT_Recon/__init__.py +1 -0
  22. AOT_biomaps/AOT_Recon/_mainRecon.py +60 -53
  23. AOT_biomaps/__init__.py +4 -69
  24. {aot_biomaps-2.9.186.dist-info → aot_biomaps-2.9.294.dist-info}/METADATA +2 -1
  25. aot_biomaps-2.9.294.dist-info/RECORD +47 -0
  26. aot_biomaps-2.9.186.dist-info/RECORD +0 -43
  27. {aot_biomaps-2.9.186.dist-info → aot_biomaps-2.9.294.dist-info}/WHEEL +0 -0
  28. {aot_biomaps-2.9.186.dist-info → aot_biomaps-2.9.294.dist-info}/top_level.txt +0 -0
@@ -1,39 +1,49 @@
1
+ import concurrent
2
+
3
+ from AOT_biomaps.AOT_Recon.ReconTools import get_apodization_vector_gpu
1
4
  from ._mainRecon import Recon
2
- from .ReconEnums import ReconType, OptimizerType, ProcessType
5
+ from .ReconEnums import ReconType, OptimizerType, ProcessType, SMatrixType
3
6
  from .AOT_Optimizers import MLEM, LS
4
- from .ReconTools import check_gpu_memory, calculate_memory_requirement, mse
5
7
  from AOT_biomaps.Config import config
8
+ from .AOT_SparseSMatrix import SparseSMatrix_CSR, SparseSMatrix_SELL
6
9
 
7
10
  import os
8
- import sys
9
11
  import subprocess
10
- import warnings
11
12
  import numpy as np
12
13
  import matplotlib.pyplot as plt
13
14
  import matplotlib.animation as animation
14
15
  from IPython.display import HTML
15
16
  from datetime import datetime
16
17
  from tempfile import gettempdir
17
- import re
18
-
19
-
18
+ import cupy as cp
19
+ import cupyx.scipy.sparse as cpsparse
20
+ import gc
21
+ from tqdm import trange
20
22
 
21
23
  class AlgebraicRecon(Recon):
22
24
  """
23
25
  This class implements the Algebraic reconstruction process.
24
26
  It currently does not perform any operations but serves as a template for future implementations.
25
27
  """
26
- def __init__(self, opti = OptimizerType.MLEM, numIterations = 10000, numSubsets = 1, isSavingEachIteration=True, maxSaves = 5000, alpha = None, **kwargs):
28
+ def __init__(self, opti = OptimizerType.MLEM, numIterations = 10000, numSubsets = 1, isSavingEachIteration=True, maxSaves = 5000, alpha = None, denominatorThreshold = 1e-6, smatrixType = SMatrixType.SELL, sparseThreshold=0.1, device = None, **kwargs):
27
29
  super().__init__(**kwargs)
28
30
  self.reconType = ReconType.Algebraic
29
31
  self.optimizer = opti
30
32
  self.reconPhantom = []
31
33
  self.reconLaser = []
34
+ self.indices = []
32
35
  self.numIterations = numIterations
33
36
  self.numSubsets = numSubsets
34
37
  self.isSavingEachIteration = isSavingEachIteration
35
38
  self.maxSaves = maxSaves
39
+ self.denominatorThreshold = denominatorThreshold
36
40
  self.alpha = alpha # Regularization parameter for LS
41
+ self.device = device
42
+ self.SMatrix = None # system matrix
43
+ self.smatrixType = smatrixType # SMatrixType.DENSE if no sparsing, else SMatrixType.SELL or SMatrixType.CSR or SMatrixType.COO
44
+ # Sparse matrix attributes
45
+
46
+ self.sparseThreshold = sparseThreshold
37
47
 
38
48
  if self.numIterations <= 0:
39
49
  raise ValueError("Number of iterations must be greater than 0.")
@@ -45,38 +55,26 @@ class AlgebraicRecon(Recon):
45
55
  raise TypeError("Number of subsets must be an integer.")
46
56
 
47
57
  print("Generating system matrix (processing acoustic fields)...")
48
- self.SMatrix = np.stack([ac_field.field for ac_field in self.experiment.AcousticFields], axis=-1)
58
+ if self.smatrixType == SMatrixType.DENSE:
59
+ self.SMatrix = self._fillDenseSMatrix()
60
+ else:
61
+ self.SMatrix = self._fillSparseSMatrix(isShowLogs=True)
49
62
 
50
63
  # PUBLIC METHODS
51
64
 
52
- def run(self, processType = ProcessType.PYTHON, withTumor= True):
65
+ def run(self, processType = ProcessType.PYTHON, withTumor= True, show_logs=True):
53
66
  """
54
67
  This method is a placeholder for the Algebraic reconstruction process.
55
68
  It currently does not perform any operations but serves as a template for future implementations.
56
69
  """
57
-
58
70
  if(processType == ProcessType.CASToR):
59
- self._AlgebraicReconCASToR(withTumor)
71
+ self._AlgebraicReconCASToR(withTumor=withTumor, show_logs=show_logs)
60
72
  elif(processType == ProcessType.PYTHON):
61
- self._AlgebraicReconPython(withTumor)
73
+ self._AlgebraicReconPython(withTumor=withTumor, show_logs=show_logs)
62
74
  else:
63
75
  raise ValueError(f"Unknown Algebraic reconstruction type: {processType}")
64
-
65
- def load_reconCASToR(self,withTumor = True):
66
- if withTumor:
67
- folder = 'results_withTumor'
68
- else:
69
- folder = 'results_withoutTumor'
70
-
71
- for thetaFiles in os.path.join(self.saveDir, folder + '_{}'):
72
- if thetaFiles.endswith('.hdr'):
73
- theta = Recon.load_recon(thetaFiles)
74
- if withTumor:
75
- self.reconPhantom.append(theta)
76
- else:
77
- self.reconLaser.append(theta)
78
-
79
- def plot_MSE(self, isSaving=True, log_scale_x=False, log_scale_y=False):
76
+
77
+ def plot_MSE(self, isSaving=True, log_scale_x=False, log_scale_y=False, show_logs=True):
80
78
  """
81
79
  Plot the Mean Squared Error (MSE) of the reconstruction.
82
80
 
@@ -91,8 +89,8 @@ class AlgebraicRecon(Recon):
91
89
  raise ValueError("MSE is empty. Please calculate MSE first.")
92
90
 
93
91
  best_idx = self.indices[np.argmin(self.MSE)]
94
-
95
- print(f"Lowest MSE = {np.min(self.MSE):.4f} at iteration {best_idx+1}")
92
+ if show_logs:
93
+ print(f"Lowest MSE = {np.min(self.MSE):.4f} at iteration {best_idx+1}")
96
94
  # Plot MSE curve
97
95
  plt.figure(figsize=(7, 5))
98
96
  plt.plot(self.indices, self.MSE, 'r-', label="MSE curve")
@@ -121,17 +119,17 @@ class AlgebraicRecon(Recon):
121
119
  scale_str = "_logy"
122
120
  SavingFolder = os.path.join(self.saveDir, f'{self.SMatrix.shape[3]}_SCANS_MSE_plot_{self.optimizer.name}_{scale_str}{date_str}.png')
123
121
  plt.savefig(SavingFolder, dpi=300)
124
- print(f"MSE plot saved to {SavingFolder}")
122
+ if show_logs:
123
+ print(f"MSE plot saved to {SavingFolder}")
125
124
 
126
125
  plt.show()
127
126
 
128
- def show_MSE_bestRecon(self, isSaving=True):
127
+ def show_MSE_bestRecon(self, isSaving=True, show_logs=True):
129
128
  if not self.MSE:
130
129
  raise ValueError("MSE is empty. Please calculate MSE first.")
131
130
 
132
131
 
133
132
  best_idx = np.argmin(self.MSE)
134
- print(best_idx)
135
133
  best_recon = self.reconPhantom[best_idx]
136
134
 
137
135
  # Crée la figure et les axes
@@ -160,7 +158,6 @@ class AlgebraicRecon(Recon):
160
158
 
161
159
  # Right: Reconstruction at last iteration
162
160
  lastRecon = self.reconPhantom[-1]
163
- print(lastRecon.shape)
164
161
  if self.experiment.OpticImage.phantom.shape != lastRecon.shape:
165
162
  lastRecon = lastRecon.T
166
163
  im2 = axs[2].imshow(lastRecon,
@@ -189,11 +186,12 @@ class AlgebraicRecon(Recon):
189
186
  os.makedirs(savePath)
190
187
  SavingFolder = os.path.join(self.saveDir, f'{self.SMatrix.shape[3]}_SCANS_comparison_MSE_BestANDLastRecon_{self.optimizer.name}_{date_str}.png')
191
188
  plt.savefig(SavingFolder, dpi=300, bbox_inches='tight')
192
- print(f"MSE plot saved to {SavingFolder}")
189
+ if show_logs:
190
+ print(f"MSE plot saved to {SavingFolder}")
193
191
 
194
192
  plt.show()
195
193
 
196
- def show_theta_animation(self, vmin=None, vmax=None, total_duration_ms=3000, save_path=None, max_frames=1000, isPropMSE=True):
194
+ def show_theta_animation(self, vmin=None, vmax=None, total_duration_ms=3000, save_path=None, max_frames=1000, isPropMSE=True, show_logs=True):
197
195
  """
198
196
  Show theta iteration animation with speed proportional to MSE acceleration.
199
197
  In "propMSE" mode: slow down when MSE changes rapidly, speed up when MSE stagnates.
@@ -296,18 +294,19 @@ class AlgebraicRecon(Recon):
296
294
  ani.save(save_path, writer=animation.PillowWriter(fps=100))
297
295
  elif save_path.endswith(".mp4"):
298
296
  ani.save(save_path, writer="ffmpeg", fps=30)
299
- print(f"Animation saved to {save_path}")
297
+ if show_logs:
298
+ print(f"Animation saved to {save_path}")
300
299
 
301
300
  plt.close(fig)
302
301
  return HTML(ani.to_jshtml())
303
302
 
304
- def plot_SSIM(self, isSaving=True, log_scale_x=False, log_scale_y=False):
303
+ def plot_SSIM(self, isSaving=True, log_scale_x=False, log_scale_y=False, show_logs=True):
305
304
  if not self.SSIM:
306
305
  raise ValueError("SSIM is empty. Please calculate SSIM first.")
307
306
 
308
307
  best_idx = self.indices[np.argmax(self.SSIM)]
309
-
310
- print(f"Highest SSIM = {np.max(self.SSIM):.4f} at iteration {best_idx+1}")
308
+ if show_logs:
309
+ print(f"Highest SSIM = {np.max(self.SSIM):.4f} at iteration {best_idx+1}")
311
310
  # Plot SSIM curve
312
311
  plt.figure(figsize=(7, 5))
313
312
  plt.plot(self.indices, self.SSIM, 'r-', label="SSIM curve")
@@ -336,11 +335,12 @@ class AlgebraicRecon(Recon):
336
335
  scale_str = "_logy"
337
336
  SavingFolder = os.path.join(self.saveDir, f'{self.SMatrix.shape[3]}_SCANS_SSIM_plot_{self.optimizer.name}_{scale_str}{date_str}.png')
338
337
  plt.savefig(SavingFolder, dpi=300)
339
- print(f"SSIM plot saved to {SavingFolder}")
338
+ if show_logs:
339
+ print(f"SSIM plot saved to {SavingFolder}")
340
340
 
341
341
  plt.show()
342
342
 
343
- def show_SSIM_bestRecon(self, isSaving=True):
343
+ def show_SSIM_bestRecon(self, isSaving=True, show_logs=True):
344
344
 
345
345
  if not self.SSIM:
346
346
  raise ValueError("SSIM is empty. Please calculate SSIM first.")
@@ -385,10 +385,11 @@ class AlgebraicRecon(Recon):
385
385
  date_str = now.strftime("%Y_%d_%m_%y")
386
386
  SavingFolder = os.path.join(self.saveDir, f'{self.SMatrix.shape[3]}_SCANS_comparison_SSIM_BestANDLastRecon_{self.optimizer.name}_{date_str}.png')
387
387
  plt.savefig(SavingFolder, dpi=300)
388
- print(f"SSIM plot saved to {SavingFolder}")
388
+ if show_logs:
389
+ print(f"SSIM plot saved to {SavingFolder}")
389
390
  plt.show()
390
391
 
391
- def plot_CRC_vs_Noise(self, use_ROI=True, fin=None, isSaving=True):
392
+ def plot_CRC_vs_Noise(self, use_ROI=True, fin=None, isSaving=True, show_logs=True):
392
393
  """
393
394
  Plot CRC (Contrast Recovery Coefficient) vs Noise for each iteration.
394
395
  """
@@ -436,10 +437,11 @@ class AlgebraicRecon(Recon):
436
437
  date_str = now.strftime("%Y_%d_%m_%y")
437
438
  SavingFolder = os.path.join(self.saveDir, f'{self.SMatrix.shape[3]}_SCANS_CRCvsNOISE_{self.optimizer.name}_{date_str}.png')
438
439
  plt.savefig(SavingFolder, dpi=300)
439
- print(f"CRCvsNOISE plot saved to {SavingFolder}")
440
+ if show_logs:
441
+ print(f"CRCvsNOISE plot saved to {SavingFolder}")
440
442
  plt.show()
441
443
 
442
- def show_reconstruction_progress(self, start=0, fin=None, save_path=None, with_tumor=True):
444
+ def show_reconstruction_progress(self, start=0, fin=None, save_path=None, with_tumor=True, show_logs=True):
443
445
  """
444
446
  Show the reconstruction progress for either with or without tumor.
445
447
  If isPropMSE is True, the frame selection is adapted to MSE changes.
@@ -544,7 +546,8 @@ class AlgebraicRecon(Recon):
544
546
  else:
545
547
  save_path = f"{save_path}_{title_suffix}"
546
548
  plt.savefig(save_path, dpi=300)
547
- print(f"Figure saved to: {save_path}")
549
+ if show_logs:
550
+ print(f"Figure saved to: {save_path}")
548
551
 
549
552
  plt.show()
550
553
 
@@ -567,14 +570,15 @@ class AlgebraicRecon(Recon):
567
570
  if not os.path.exists(results_dir):
568
571
  os.makedirs(results_dir)
569
572
 
570
- if os.path.exists(results_dir):
573
+ if os.path.exists(os.path.join(results_dir,"indices.npy")):
571
574
  return (True, results_dir)
572
575
 
573
576
  return (False, results_dir)
574
577
 
575
- def load(self, withTumor=True, results_date=None, optimizer=None, filePath=None):
578
+ def load(self, withTumor=True, results_date=None, optimizer=None, filePath=None, show_logs=True):
576
579
  """
577
- Load the reconstruction results (reconPhantom or reconLaser) and indices into self.
580
+ Load the reconstruction results (reconPhantom or reconLaser) and indices as lists of 2D np arrays into self.
581
+ If the loaded file is a 3D array, it is split into a list of 2D arrays.
578
582
  Args:
579
583
  withTumor: If True, loads reconPhantom (with tumor), else reconLaser (without tumor).
580
584
  results_date: Date string (format "ddmm") to specify which results to load. If None, uses the most recent date in saveDir.
@@ -587,39 +591,68 @@ class AlgebraicRecon(Recon):
587
591
  recon_path = filePath
588
592
  if not os.path.exists(recon_path):
589
593
  raise FileNotFoundError(f"No reconstruction file found at {recon_path}.")
590
- if withTumor:
591
- self.reconPhantom = np.load(recon_path, allow_pickle=True)
594
+ # Charge le fichier (3D ou liste de 2D)
595
+ data = np.load(recon_path, allow_pickle=True)
596
+ # Découpe en liste de 2D si c'est un tableau 3D
597
+ if isinstance(data, np.ndarray) and data.ndim == 3:
598
+ if withTumor:
599
+ self.reconPhantom = [data[i, :, :] for i in range(data.shape[0])]
600
+ else:
601
+ self.reconLaser = [data[i, :, :] for i in range(data.shape[0])]
592
602
  else:
593
- self.reconLaser = np.load(recon_path, allow_pickle=True)
594
- # Essayer de charger les indices (fichier avec suffixe "_indices.npy")
595
- base_dir, file_name = os.path.split(recon_path)
596
- file_base, _ = os.path.splitext(file_name)
597
- indices_path = os.path.join(base_dir, f"{file_base}_indices.npy")
603
+ # Sinon, suppose que c'est déjà une liste de 2D
604
+ if withTumor:
605
+ self.reconPhantom = data
606
+ else:
607
+ self.reconLaser = data
608
+ # Essayer de charger les indices
609
+ base_dir, _ = os.path.split(recon_path)
610
+ indices_path = os.path.join(base_dir, 'indices.npy')
598
611
  if os.path.exists(indices_path):
599
- self.indices = np.load(indices_path, allow_pickle=True)
612
+ indices_data = np.load(indices_path, allow_pickle=True)
613
+ if isinstance(indices_data, np.ndarray) and indices_data.ndim == 3:
614
+ self.indices = [indices_data[i, :, :] for i in range(indices_data.shape[0])]
615
+ else:
616
+ self.indices = indices_data
600
617
  else:
601
618
  self.indices = None
602
- print(f"Loaded reconstruction results and indices from {recon_path}")
619
+
620
+ if show_logs:
621
+ print(f"Loaded reconstruction results and indices from {recon_path}")
603
622
  else:
604
623
  # Mode chargement depuis le répertoire de résultats
605
624
  if self.saveDir is None:
606
625
  raise ValueError("Save directory is not specified. Please set saveDir before loading.")
607
- # Determine optimizer name for path matching
626
+ # Use current optimizer and potential function if not provided
608
627
  opt_name = optimizer.value if optimizer is not None else self.optimizer.value
628
+ # Build the base directory pattern
629
+ dir_pattern = f'results_*_{opt_name}'
630
+ # Add parameters to the pattern based on the optimizer
631
+ if optimizer is None:
632
+ optimizer = self.optimizer
633
+ if optimizer == OptimizerType.PPGMLEM:
634
+ beta_str = f'_Beta_{self.beta}'
635
+ delta_str = f'_Delta_{self.delta}'
636
+ gamma_str = f'_Gamma_{self.gamma}'
637
+ sigma_str = f'_Sigma_{self.sigma}'
638
+ dir_pattern += f'{beta_str}{delta_str}{gamma_str}{sigma_str}'
639
+ elif optimizer in (OptimizerType.PGC, OptimizerType.DEPIERRO95):
640
+ beta_str = f'_Beta_{self.beta}'
641
+ sigma_str = f'_Sigma_{self.sigma}'
642
+ dir_pattern += f'{beta_str}{sigma_str}'
609
643
  # Find the most recent results directory if no date is specified
610
644
  if results_date is None:
611
- # Cherche les dossiers au format results_ddmm_MLEM
612
- dirs = [
613
- d for d in os.listdir(self.saveDir)
614
- if os.path.isdir(os.path.join(self.saveDir, d))
615
- and re.match(r'results_\d{4}_' + re.escape(opt_name) + r'($|_)', d)
616
- ]
645
+ dirs = [d for d in os.listdir(self.saveDir) if os.path.isdir(os.path.join(self.saveDir, d)) and dir_pattern in d]
617
646
  if not dirs:
618
- raise FileNotFoundError(f"No results directory found for optimizer '{opt_name}' in {self.saveDir}.")
619
- dirs.sort(reverse=True) # Most recent first (tri alphabétique inverse)
647
+ raise FileNotFoundError(f"No matching results directory found for pattern '{dir_pattern}' in {self.saveDir}.")
648
+ dirs.sort(reverse=True) # Most recent first
620
649
  results_dir = os.path.join(self.saveDir, dirs[0])
621
650
  else:
622
651
  results_dir = os.path.join(self.saveDir, f'results_{results_date}_{opt_name}')
652
+ if optimizer == OptimizerType.MLEM:
653
+ pass
654
+ elif optimizer == OptimizerType.LS:
655
+ results_dir += f'_Alpha_{self.alpha}'
623
656
  if not os.path.exists(results_dir):
624
657
  raise FileNotFoundError(f"Directory {results_dir} does not exist.")
625
658
  # Load reconstruction results
@@ -627,61 +660,152 @@ class AlgebraicRecon(Recon):
627
660
  recon_path = os.path.join(results_dir, f'{recon_key}.npy')
628
661
  if not os.path.exists(recon_path):
629
662
  raise FileNotFoundError(f"No reconstruction file found at {recon_path}.")
630
- if withTumor:
631
- self.reconPhantom = np.load(recon_path, allow_pickle=True)
663
+ data = np.load(recon_path, allow_pickle=True)
664
+ if isinstance(data, np.ndarray) and data.ndim == 3:
665
+ if withTumor:
666
+ self.reconPhantom = [data[i, :, :] for i in range(data.shape[0])]
667
+ else:
668
+ self.reconLaser = [data[i, :, :] for i in range(data.shape[0])]
632
669
  else:
633
- self.reconLaser = np.load(recon_path, allow_pickle=True)
634
- # Try to load saved indices (if file exists)
635
- indices_path = os.path.join(results_dir, f'{recon_key}_indices.npy')
636
- if os.path.exists(indices_path):
637
- self.indices = np.load(indices_path, allow_pickle=True)
670
+ if withTumor:
671
+ self.reconPhantom = data
672
+ else:
673
+ self.reconLaser = data
674
+ # Load saved indices as list of 2D arrays
675
+ indices_path = os.path.join(results_dir, 'indices.npy')
676
+ if not os.path.exists(indices_path):
677
+ raise FileNotFoundError(f"No indices file found at {indices_path}.")
678
+ indices_data = np.load(indices_path, allow_pickle=True)
679
+ if isinstance(indices_data, np.ndarray) and indices_data.ndim == 3:
680
+ self.indices = [indices_data[i, :, :] for i in range(indices_data.shape[0])]
638
681
  else:
639
- self.indices = None
640
- print(f"Loaded reconstruction results and indices from {results_dir}")
641
-
682
+ self.indices = indices_data
683
+ if show_logs:
684
+ print(f"Loaded reconstruction results and indices from {results_dir}")
685
+
642
686
  def normalizeSMatrix(self):
643
687
  self.SMatrix = self.SMatrix / (float(self.experiment.params.acoustic['voltage'])*float(self.experiment.params.acoustic['sensitivity']))
644
688
 
645
689
  # PRIVATE METHODS
646
690
 
647
- def _AlgebraicReconPython(self,withTumor):
691
+ def _fillDenseSMatrix(self):
692
+ """
693
+ Construit une matrice dense en mémoire.
694
+ """
695
+ T, Z, X = self.experiment.AcousticFields[0].field.shape
696
+ N = len(self.experiment.AcousticFields)
697
+ S = np.empty((T, Z, X, N), dtype=np.float32)
698
+ def copy_block(i):
699
+ np.copyto(S[..., i], self.experiment.AcousticFields[i].field)
700
+ with concurrent.futures.ThreadPoolExecutor() as ex:
701
+ ex.map(copy_block, range(N))
702
+ return S
703
+
704
+
705
+ def _fillSparseSMatrix(self, isShowLogs=True):
706
+ if self.smatrixType == SMatrixType.CSR:
707
+ return self._fillSparseSMatrix_CSR(isShowLogs=isShowLogs)
708
+ if self.smatrixType == SMatrixType.COO:
709
+ raise NotImplementedError("COO sparse matrix not implemented yet.")
710
+ if self.smatrixType == SMatrixType.SELL:
711
+ return self._fillSparseSMatrix_SELL(isShowLogs=isShowLogs)
712
+
713
+ def _fillSparseSMatrix_CSR(self, isShowLogs=True):
714
+ """
715
+ Construit une matrice sparse CSR par morceaux sans concaténation intermédiaire.
716
+ Libère toute la mémoire temporaire à chaque étape.
717
+ """
718
+ sparse_matrix = SparseSMatrix_CSR(self.experiment,relative_threshold=self.sparseThreshold)
719
+ sparse_matrix.allocate()
720
+ if isShowLogs:
721
+ print(f" Sparse matrix size: {sparse_matrix.getMatrixSize()} GB")
722
+ print(f"Sparse matrix density: {sparse_matrix.compute_density()}")
723
+ return sparse_matrix
724
+
725
+ def _fillSparseSMatrix_SELL(self, isShowLogs=True):
726
+ """
727
+ Construit une matrice sparse SELL par morceaux sans concaténation intermédiaire.
728
+ Libère toute la mémoire temporaire à chaque étape.
729
+ """
730
+ sparse_matrix = SparseSMatrix_SELL(self.experiment,relative_threshold=self.sparseThreshold)
731
+ sparse_matrix.allocate()
732
+ # fenetre_gpu = get_apodization_vector_gpu(sparse_matrix)
733
+ # sparse_matrix.apply_apodization_gpu(fenetre_gpu)
734
+ if isShowLogs:
735
+ print(f" Sparse matrix size: {sparse_matrix.getMatrixSize()} GB")
736
+ print(f"Sparse matrix density: {sparse_matrix.compute_density()}")
737
+ return sparse_matrix
738
+
739
+ def _AlgebraicReconPython(self,withTumor, show_logs):
648
740
 
649
741
  if withTumor:
650
742
  if self.experiment.AOsignal_withTumor is None:
651
743
  raise ValueError("AO signal with tumor is not available. Please generate AO signal with tumor the experiment first in the experiment object.")
652
- else:
653
- y = self.experiment.AOsignal_withTumor
654
744
  else:
655
745
  if self.experiment.AOsignal_withoutTumor is None:
656
746
  raise ValueError("AO signal without tumor is not available. Please generate AO signal without tumor the experiment first in the experiment object.")
657
- else:
658
- y = self.experiment.AOsignal_withoutTumor
659
747
 
660
748
  if self.optimizer.value == OptimizerType.MLEM.value:
661
- self.reconPhantom, self.indices = MLEM(SMatrix=self.SMatrix,
662
- y=y,
663
- numIterations=self.numIterations,
664
- isSavingEachIteration=self.isSavingEachIteration,
665
- withTumor=withTumor,
666
- use_multi_gpu= self.isMultiGPU,
667
- use_numba= self.isMultiCPU,
668
- max_saves=self.maxSaves
669
- )
749
+ if withTumor:
750
+ self.reconPhantom, self.indices = MLEM(SMatrix=self.SMatrix,
751
+ y=self.experiment.AOsignal_withTumor,
752
+ numIterations=self.numIterations,
753
+ isSavingEachIteration=self.isSavingEachIteration,
754
+ withTumor=withTumor,
755
+ device=self.device,
756
+ use_numba=self.isMultiCPU,
757
+ denominator_threshold=self.denominatorThreshold,
758
+ max_saves=self.maxSaves,
759
+ show_logs=show_logs,
760
+ smatrixType=self.smatrixType,
761
+ )
762
+ else:
763
+ self.reconLaser, self.indices = MLEM(SMatrix=self.SMatrix,
764
+ y=self.experiment.AOsignal_withoutTumor,
765
+ numIterations=self.numIterations,
766
+ isSavingEachIteration=self.isSavingEachIteration,
767
+ withTumor=withTumor,
768
+ device=self.device,
769
+ use_numba=self.isMultiCPU,
770
+ denominator_threshold=self.denominatorThreshold,
771
+ max_saves=self.maxSaves,
772
+ show_logs=show_logs,
773
+ smatrixType=self.smatrixType,
774
+ )
670
775
  elif self.optimizer.value == OptimizerType.LS.value:
671
776
  if self.alpha is None:
672
777
  raise ValueError("Alpha (regularization parameter) must be set for LS reconstruction.")
673
- self.reconPhantom, self.indices = LS(SMatrix=self.SMatrix,
674
- y=y,
675
- numIterations=self.numIterations,
676
- isSavingEachIteration=self.isSavingEachIteration,
677
- withTumor=withTumor,
678
- alpha=self.alpha,
679
- max_saves=self.maxSaves,
778
+ if withTumor:
779
+ self.reconPhantom, self.indices = LS(SMatrix=self.SMatrix,
780
+ y=self.experiment.AOsignal_withTumor,
781
+ numIterations=self.numIterations,
782
+ isSavingEachIteration=self.isSavingEachIteration,
783
+ withTumor=withTumor,
784
+ device=self.device,
785
+ use_numba=self.isMultiCPU,
786
+ denominator_threshold=self.denominatorThreshold,
787
+ max_saves=self.maxSaves,
788
+ show_logs=show_logs,
789
+ smatrixType=self.smatrixType
790
+ )
791
+ else:
792
+ self.reconLaser, self.indices = LS(SMatrix=self.SMatrix,
793
+ y=self.experiment.AOsignal_withoutTumor,
794
+ numIterations=self.numIterations,
795
+ isSavingEachIteration=self.isSavingEachIteration,
796
+ withTumor=withTumor,
797
+ alpha=self.alpha,
798
+ device=self.device,
799
+ use_numba=self.isMultiCPU,
800
+ denominator_threshold=self.denominatorThreshold,
801
+ max_saves=self.maxSaves,
802
+ show_logs=show_logs,
803
+ smatrixType=self.smatrixType
680
804
  )
681
805
  else:
682
806
  raise ValueError(f"Only MLEM and LS are supported for simple algebraic reconstruction. {self.optimizer.value} need Bayesian reconstruction")
683
807
 
684
- def _AlgebraicReconCASToR(self, withTumor):
808
+ def _AlgebraicReconCASToR(self,withTumor, show_logs):
685
809
  # Définir les chemins
686
810
  smatrix = os.path.join(self.saveDir, "system_matrix")
687
811
  if withTumor:
@@ -691,14 +815,16 @@ class AlgebraicRecon(Recon):
691
815
 
692
816
  # Vérifier et générer les fichiers d'entrée si nécessaire
693
817
  if not os.path.isfile(os.path.join(self.saveDir, fileName)):
694
- print(f"Fichier .cdh manquant. Génération de {fileName}...")
818
+ if show_logs:
819
+ print(f"Fichier .cdh manquant. Génération de {fileName}...")
695
820
  self.experiment.saveAOsignals_Castor(self.saveDir)
696
821
 
697
822
  # Vérifier/générer la matrice système
698
823
  if not os.path.isdir(smatrix):
699
824
  os.makedirs(smatrix, exist_ok=True)
700
825
  if not os.listdir(smatrix):
701
- print("Matrice système manquante. Génération...")
826
+ if show_logs:
827
+ print("Matrice système manquante. Génération...")
702
828
  self.experiment.saveAcousticFields(self.saveDir)
703
829
 
704
830
  # Vérifier que le fichier .cdh existe (redondant mais sûr)
@@ -737,8 +863,9 @@ class AlgebraicRecon(Recon):
737
863
  ]
738
864
 
739
865
  # Afficher la commande (pour débogage)
740
- print("Commande CASToR :")
741
- print(" ".join(cmd))
866
+ if show_logs:
867
+ print("Commande CASToR :")
868
+ print(" ".join(cmd))
742
869
 
743
870
  # Chemin du script temporaire
744
871
  recon_script_path = os.path.join(gettempdir(), 'recon.sh')
@@ -752,17 +879,20 @@ class AlgebraicRecon(Recon):
752
879
 
753
880
  # Rendre le script exécutable et l'exécuter
754
881
  subprocess.run(["chmod", "+x", recon_script_path], check=True)
755
- print(f"Exécution de la reconstruction avec CASToR...")
882
+ if show_logs:
883
+ print(f"Exécution de la reconstruction avec CASToR...")
756
884
  result = subprocess.run(recon_script_path, env=env, check=True, capture_output=True, text=True)
757
885
 
758
886
  # Afficher la sortie de CASToR (pour débogage)
759
- print("Sortie CASToR :")
760
- print(result.stdout)
761
- if result.stderr:
762
- print("Erreurs :")
763
- print(result.stderr)
764
-
765
- print("Reconstruction terminée avec succès.")
887
+ if show_logs:
888
+ print("Sortie CASToR :")
889
+ print(result.stdout)
890
+ if result.stderr:
891
+ print("Erreurs :")
892
+ print(result.stderr)
893
+
894
+ if show_logs:
895
+ print("Reconstruction terminée avec succès.")
766
896
  self.load_reconCASToR(withTumor=withTumor)
767
897
 
768
898
  # STATIC METHODS