AOT-biomaps 2.9.138__py3-none-any.whl → 2.9.279__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/AcousticTools.py +35 -115
- AOT_biomaps/AOT_Acoustic/StructuredWave.py +2 -2
- AOT_biomaps/AOT_Acoustic/_mainAcoustic.py +22 -18
- AOT_biomaps/AOT_Experiment/Tomography.py +74 -4
- AOT_biomaps/AOT_Experiment/_mainExperiment.py +102 -68
- AOT_biomaps/AOT_Optic/_mainOptic.py +124 -58
- AOT_biomaps/AOT_Recon/AOT_Optimizers/DEPIERRO.py +72 -108
- AOT_biomaps/AOT_Recon/AOT_Optimizers/LS.py +474 -289
- AOT_biomaps/AOT_Recon/AOT_Optimizers/MAPEM.py +173 -68
- AOT_biomaps/AOT_Recon/AOT_Optimizers/MLEM.py +360 -154
- AOT_biomaps/AOT_Recon/AOT_Optimizers/PDHG.py +150 -111
- 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 +328 -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 +359 -238
- AOT_biomaps/AOT_Recon/AnalyticRecon.py +29 -41
- AOT_biomaps/AOT_Recon/BayesianRecon.py +165 -91
- AOT_biomaps/AOT_Recon/DeepLearningRecon.py +4 -1
- AOT_biomaps/AOT_Recon/PrimalDualRecon.py +175 -31
- AOT_biomaps/AOT_Recon/ReconEnums.py +38 -3
- AOT_biomaps/AOT_Recon/ReconTools.py +184 -77
- AOT_biomaps/AOT_Recon/__init__.py +1 -0
- AOT_biomaps/AOT_Recon/_mainRecon.py +144 -74
- AOT_biomaps/__init__.py +4 -36
- {aot_biomaps-2.9.138.dist-info → aot_biomaps-2.9.279.dist-info}/METADATA +2 -1
- aot_biomaps-2.9.279.dist-info/RECORD +47 -0
- aot_biomaps-2.9.138.dist-info/RECORD +0 -43
- {aot_biomaps-2.9.138.dist-info → aot_biomaps-2.9.279.dist-info}/WHEEL +0 -0
- {aot_biomaps-2.9.138.dist-info → aot_biomaps-2.9.279.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from AOT_biomaps.Config import config
|
|
2
2
|
|
|
3
|
+
from scipy.signal import hilbert
|
|
3
4
|
import os
|
|
4
5
|
import h5py
|
|
5
6
|
import numpy as np
|
|
@@ -109,136 +110,55 @@ def reshape_field(field, factor, device=None):
|
|
|
109
110
|
if isinstance(field, np.ndarray):
|
|
110
111
|
return downsampled.cpu().numpy()
|
|
111
112
|
else:
|
|
112
|
-
return downsampled
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def CPU_hilbert(signal, axis=0):
|
|
116
|
-
"""
|
|
117
|
-
Compute the Hilbert transform of a real signal using NumPy.
|
|
118
|
-
|
|
119
|
-
Parameters:
|
|
120
|
-
- signal: Input real signal (numpy.ndarray).
|
|
121
|
-
- axis: Axis along which to compute the Hilbert transform.
|
|
122
|
-
|
|
123
|
-
Returns:
|
|
124
|
-
- analytic_signal: The analytic signal of the input.
|
|
125
|
-
"""
|
|
126
|
-
fft_signal = np.fft.fftn(signal, axes=[axis])
|
|
127
|
-
h = np.zeros_like(signal)
|
|
128
|
-
|
|
129
|
-
if axis == 0:
|
|
130
|
-
h[0 : signal.shape[0] // 2 + 1, ...] = 1
|
|
131
|
-
h[signal.shape[0] // 2 + 1 :, ...] = 2
|
|
132
|
-
else:
|
|
133
|
-
raise ValueError("Axis not supported for this implementation.")
|
|
134
|
-
|
|
135
|
-
analytic_signal = np.fft.ifftn(fft_signal * h, axes=[axis])
|
|
136
|
-
return analytic_signal
|
|
137
|
-
|
|
138
|
-
def GPU_hilbert(signal, axis=0):
|
|
139
|
-
"""
|
|
140
|
-
Compute the Hilbert transform of a real signal using PyTorch.
|
|
141
|
-
|
|
142
|
-
Parameters:
|
|
143
|
-
- signal: Input real signal (torch.Tensor).
|
|
144
|
-
- axis: Axis along which to compute the Hilbert transform.
|
|
113
|
+
return downsampled.cpu().numpy()
|
|
145
114
|
|
|
146
|
-
|
|
147
|
-
- analytic_signal: The analytic signal of the input.
|
|
115
|
+
def calculate_envelope_squared(field):
|
|
148
116
|
"""
|
|
149
|
-
|
|
150
|
-
h = torch.zeros_like(signal)
|
|
151
|
-
if axis == 0:
|
|
152
|
-
h[0 : signal.shape[0] // 2 + 1, ...] = 1
|
|
153
|
-
h[signal.shape[0] // 2 + 1 :, ...] = 2
|
|
154
|
-
else:
|
|
155
|
-
raise ValueError("Axis not supported for this implementation.")
|
|
117
|
+
Calcule l'enveloppe au carré du champ acoustique en utilisant scipy.signal.hilbert (CPU uniquement).
|
|
156
118
|
|
|
157
|
-
|
|
158
|
-
|
|
119
|
+
Args:
|
|
120
|
+
field: Champ acoustique (numpy.ndarray) de forme (T, X, Z) ou (T, X, Y, Z).
|
|
159
121
|
|
|
160
|
-
def calculate_envelope_squared(field, isGPU):
|
|
161
|
-
"""
|
|
162
|
-
Calculate the analytic envelope of the acoustic field using either CPU or GPU with PyTorch.
|
|
163
|
-
Parameters:
|
|
164
|
-
- field: Input acoustic field (numpy.ndarray or torch.Tensor).
|
|
165
|
-
- isGPU (bool): If True, use GPU for computation. Otherwise, use CPU.
|
|
166
122
|
Returns:
|
|
167
|
-
|
|
123
|
+
envelope (numpy.ndarray): Enveloppe au carré du champ acoustique.
|
|
168
124
|
"""
|
|
169
125
|
try:
|
|
170
126
|
if field is None:
|
|
171
|
-
raise ValueError("
|
|
127
|
+
raise ValueError("Le champ acoustique n'est pas généré. Veuillez d'abord générer le champ.")
|
|
172
128
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
else:
|
|
177
|
-
acoustic_field = field.detach().clone().to(dtype=torch.float32)
|
|
178
|
-
|
|
179
|
-
# Handle GPU/CPU transfer
|
|
180
|
-
if isGPU:
|
|
181
|
-
if not torch.cuda.is_available():
|
|
182
|
-
print("CUDA is not available, falling back to CPU.")
|
|
183
|
-
isGPU = False
|
|
129
|
+
if not isinstance(field, np.ndarray):
|
|
130
|
+
if hasattr(field, 'cpu'):
|
|
131
|
+
field = field.cpu().numpy() # Si c'est un tenseur PyTorch sur GPU/CPU
|
|
184
132
|
else:
|
|
185
|
-
#
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
elif len(acoustic_field.shape) == 4:
|
|
208
|
-
if isGPU:
|
|
209
|
-
return torch.stack([
|
|
210
|
-
torch.abs(GPU_hilbert(slice_data[:, y, z], axis=0))**2
|
|
211
|
-
for y in range(slice_data.shape[1])
|
|
212
|
-
for z in range(slice_data.shape[2])
|
|
213
|
-
]).reshape(slice_data.shape[1], slice_data.shape[2], -1).permute(2, 0, 1)
|
|
214
|
-
else:
|
|
215
|
-
envelope = torch.zeros_like(slice_data)
|
|
216
|
-
for y in range(slice_data.shape[1]):
|
|
217
|
-
for z in range(slice_data.shape[2]):
|
|
218
|
-
envelope[:, y, z] = torch.from_numpy(
|
|
219
|
-
np.abs(CPU_hilbert(slice_data[:, y, z].cpu().numpy(), axis=0))**2
|
|
220
|
-
)
|
|
221
|
-
return envelope
|
|
222
|
-
|
|
223
|
-
# Process slices
|
|
224
|
-
num_slices = acoustic_field.shape[0]
|
|
225
|
-
slice_indices = range(num_slices)
|
|
226
|
-
|
|
227
|
-
if isGPU:
|
|
228
|
-
envelopes = [process_slice(i) for i in slice_indices]
|
|
229
|
-
else:
|
|
230
|
-
with ThreadPoolExecutor() as executor:
|
|
231
|
-
envelopes = list(executor.map(process_slice, slice_indices))
|
|
232
|
-
|
|
233
|
-
# Combine results
|
|
234
|
-
envelope = torch.stack(envelopes, axis=0)
|
|
235
|
-
return envelope.cpu().numpy() if isGPU else envelope.numpy()
|
|
133
|
+
field = np.array(field) # Conversion générique
|
|
134
|
+
|
|
135
|
+
if len(field.shape) not in [3, 4]:
|
|
136
|
+
raise ValueError("Le champ acoustique doit être un tableau 3D (T, X, Z) ou 4D (T, X, Y, Z).")
|
|
137
|
+
|
|
138
|
+
# Calcul de l'enveloppe avec scipy.signal.hilbert
|
|
139
|
+
if len(field.shape) == 3:
|
|
140
|
+
T, X, Z = field.shape
|
|
141
|
+
envelope = np.zeros_like(field)
|
|
142
|
+
for x in range(X):
|
|
143
|
+
for z in range(Z):
|
|
144
|
+
envelope[:, x, z] = np.abs(hilbert(field[:, x, z], axis=0)) ** 2
|
|
145
|
+
elif len(field.shape) == 4:
|
|
146
|
+
T, X, Y, Z = field.shape
|
|
147
|
+
envelope = np.zeros_like(field)
|
|
148
|
+
for x in range(X):
|
|
149
|
+
for y in range(Y):
|
|
150
|
+
for z in range(Z):
|
|
151
|
+
envelope[:, x, y, z] = np.abs(hilbert(field[:, x, y, z], axis=0)) ** 2
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
return envelope
|
|
236
155
|
|
|
237
156
|
except Exception as e:
|
|
238
|
-
print(f"
|
|
157
|
+
print(f"Erreur dans calculate_envelope_squared: {e}")
|
|
239
158
|
raise
|
|
240
159
|
|
|
241
160
|
|
|
161
|
+
|
|
242
162
|
def getPattern(pathFile):
|
|
243
163
|
"""
|
|
244
164
|
Get the pattern from a file path.
|
|
@@ -292,8 +292,8 @@ class StructuredWave(AcousticField):
|
|
|
292
292
|
f"!number format := short float\n"
|
|
293
293
|
f"!number of bytes per pixel := 4\n"
|
|
294
294
|
f"scaling factor (mm/pixel) [1] := {self.params['dx'] * 1000}\n"
|
|
295
|
-
f"scaling factor (mm/pixel) [2] := {self.params['
|
|
296
|
-
f"scaling factor (s/pixel) [3] := {1 / self.params['
|
|
295
|
+
f"scaling factor (mm/pixel) [2] := {self.params['dz'] * 1000}\n"
|
|
296
|
+
f"scaling factor (s/pixel) [3] := {1 / self.params['f_saving']}\n"
|
|
297
297
|
f"first pixel offset (mm) [1] := {self.params['Xrange'][0] * 1e3}\n"
|
|
298
298
|
f"first pixel offset (mm) [2] := {self.params['Zrange'][0] * 1e3}\n"
|
|
299
299
|
f"first pixel offset (s) [3] := 0\n"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import AOT_biomaps.Settings
|
|
2
2
|
from AOT_biomaps.Config import config
|
|
3
|
-
from AOT_biomaps.AOT_Acoustic.AcousticTools import calculate_envelope_squared,
|
|
3
|
+
from AOT_biomaps.AOT_Acoustic.AcousticTools import calculate_envelope_squared, loadmat
|
|
4
4
|
from .AcousticTools import next_power_of_2, reshape_field
|
|
5
5
|
from .AcousticEnums import TypeSim, Dim, FormatSave, WaveType
|
|
6
6
|
|
|
@@ -19,10 +19,12 @@ from kwave.kspaceFirstOrder3D import kspaceFirstOrder3D
|
|
|
19
19
|
from kwave.kspaceFirstOrder2D import kspaceFirstOrder2D
|
|
20
20
|
from kwave.options.simulation_options import SimulationOptions
|
|
21
21
|
from kwave.options.simulation_execution_options import SimulationExecutionOptions
|
|
22
|
+
from AOT_biomaps.Settings import Params
|
|
22
23
|
|
|
23
24
|
from tempfile import gettempdir
|
|
24
25
|
from math import ceil
|
|
25
26
|
from abc import ABC, abstractmethod
|
|
27
|
+
import logging
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
|
|
@@ -75,7 +77,7 @@ class AcousticField(ABC):
|
|
|
75
77
|
print(f"Initialization error: {e}")
|
|
76
78
|
raise
|
|
77
79
|
if params != None:
|
|
78
|
-
if type(params) !=
|
|
80
|
+
if type(params) != Params:
|
|
79
81
|
raise TypeError("params must be an instance of the Params class")
|
|
80
82
|
|
|
81
83
|
self.params = {
|
|
@@ -225,6 +227,7 @@ class AcousticField(ABC):
|
|
|
225
227
|
Generate the acoustic field based on the specified simulation type and parameters.
|
|
226
228
|
"""
|
|
227
229
|
try:
|
|
230
|
+
logging.getLogger('root').setLevel(logging.ERROR)
|
|
228
231
|
if self.params['typeSim'] == TypeSim.FIELD2.value:
|
|
229
232
|
raise NotImplementedError("FIELD2 simulation is not implemented yet.")
|
|
230
233
|
elif self.params['typeSim'] == TypeSim.KWAVE.value:
|
|
@@ -233,10 +236,10 @@ class AcousticField(ABC):
|
|
|
233
236
|
field = self._generate_acoustic_field_KWAVE_2D(isGpu, show_log)
|
|
234
237
|
except Exception as e:
|
|
235
238
|
raise RuntimeError(f"Failed to generate 2D acoustic field: {e}")
|
|
236
|
-
self.field = calculate_envelope_squared(field,
|
|
239
|
+
self.field = reshape_field(calculate_envelope_squared(field),[self.factorT, self.factorX, self.factorZ])
|
|
237
240
|
elif self.params["dim"] == Dim.D3.value:
|
|
238
241
|
field = self._generate_acoustic_field_KWAVE_3D(isGpu, show_log)
|
|
239
|
-
self.field = calculate_envelope_squared(field,
|
|
242
|
+
self.field = reshape_field(calculate_envelope_squared(field),[self.factorT, self.factorX, self.factorZ])
|
|
240
243
|
elif self.params['typeSim'] == TypeSim.HYDRO.value:
|
|
241
244
|
raise ValueError("Cannot generate field for Hydrophone simulation, load exciting acquisitions.")
|
|
242
245
|
else:
|
|
@@ -265,7 +268,7 @@ class AcousticField(ABC):
|
|
|
265
268
|
print(f"Error in save_field method: {e}")
|
|
266
269
|
raise
|
|
267
270
|
|
|
268
|
-
def load_field(self, folderPath, formatSave=FormatSave.HDR_IMG):
|
|
271
|
+
def load_field(self, folderPath, formatSave=FormatSave.HDR_IMG, nameBlock=None):
|
|
269
272
|
"""
|
|
270
273
|
Load the acoustic field from a file in the specified format.
|
|
271
274
|
|
|
@@ -286,7 +289,7 @@ class AcousticField(ABC):
|
|
|
286
289
|
raise NotImplementedError("3D KWAVE field loading is not implemented yet.")
|
|
287
290
|
elif formatSave.value == FormatSave.H5.value:
|
|
288
291
|
if self.params["dim"] == Dim.D2.value:
|
|
289
|
-
self._load_field_h5(folderPath)
|
|
292
|
+
self._load_field_h5(folderPath,nameBlock)
|
|
290
293
|
elif self.params["dim"] == Dim.D3.value:
|
|
291
294
|
raise NotImplementedError("H5 KWAVE field loading is not implemented yet.")
|
|
292
295
|
elif formatSave.value == FormatSave.NPY.value:
|
|
@@ -502,7 +505,7 @@ class AcousticField(ABC):
|
|
|
502
505
|
try:
|
|
503
506
|
# --- 1. Grid setup ---
|
|
504
507
|
dx = self.params['dx']
|
|
505
|
-
if dx >= self.params['element_width']:
|
|
508
|
+
if dx >= self.params['element_width']*2:
|
|
506
509
|
dx = self.params['element_width'] / 2
|
|
507
510
|
Nx = int(round((self.params['Xrange'][1] - self.params['Xrange'][0]) / dx))
|
|
508
511
|
Nz = int(round((self.params['Zrange'][1] - self.params['Zrange'][0]) / dx))
|
|
@@ -511,9 +514,9 @@ class AcousticField(ABC):
|
|
|
511
514
|
Nz = self.params['Nz']
|
|
512
515
|
|
|
513
516
|
# --- 2. Time and space factors ---
|
|
514
|
-
factorT = int(np.ceil(self.params['f_AQ'] / self.params['f_saving']))
|
|
515
|
-
factorX = int(np.ceil(Nx / self.params['Nx']))
|
|
516
|
-
factorZ = int(np.ceil(Nz / self.params['Nz']))
|
|
517
|
+
self.factorT = int(np.ceil(self.params['f_AQ'] / self.params['f_saving']))
|
|
518
|
+
self.factorX = int(np.ceil(Nx / self.params['Nx']))
|
|
519
|
+
self.factorZ = int(np.ceil(Nz / self.params['Nz']))
|
|
517
520
|
|
|
518
521
|
# --- 3. Grid and source initialization ---
|
|
519
522
|
kgrid = kWaveGrid([Nx, Nz], [dx, dx])
|
|
@@ -531,6 +534,8 @@ class AcousticField(ABC):
|
|
|
531
534
|
total_size_z = next_power_of_2(Nz)
|
|
532
535
|
pml_x_size = (total_size_x - Nx) // 2
|
|
533
536
|
pml_z_size = (total_size_z - Nz) // 2
|
|
537
|
+
pml_x_size = max(pml_x_size, 50) # Ensure a minimum PML size of 50 grid points to avoid parasitic reflections
|
|
538
|
+
pml_z_size = max(pml_z_size, 50) # Ensure a minimum PML size of 50 grid points to avoid parasitic reflections
|
|
534
539
|
|
|
535
540
|
# --- 6. Simulation options ---
|
|
536
541
|
simulation_options = SimulationOptions(
|
|
@@ -549,7 +554,7 @@ class AcousticField(ABC):
|
|
|
549
554
|
)
|
|
550
555
|
|
|
551
556
|
# --- 7. Call specialized function to set up source.p_mask and source.p ---
|
|
552
|
-
self._SetUpSource(source, Nx, dx, factorT)
|
|
557
|
+
self._SetUpSource(source, Nx, dx, self.factorT)
|
|
553
558
|
|
|
554
559
|
# --- 8. Run simulation ---
|
|
555
560
|
sensor_data = kspaceFirstOrder2D(
|
|
@@ -563,10 +568,7 @@ class AcousticField(ABC):
|
|
|
563
568
|
|
|
564
569
|
# --- 9. Post-process results ---
|
|
565
570
|
data = sensor_data['p'].reshape(kgrid.Nt, Nz, Nx)
|
|
566
|
-
|
|
567
|
-
return reshape_field(data, [factorT, factorX, factorZ])
|
|
568
|
-
else:
|
|
569
|
-
return data
|
|
571
|
+
return data
|
|
570
572
|
|
|
571
573
|
except Exception as e:
|
|
572
574
|
print(f"Error generating 2D acoustic field: {e}")
|
|
@@ -653,7 +655,7 @@ class AcousticField(ABC):
|
|
|
653
655
|
"""
|
|
654
656
|
pass
|
|
655
657
|
|
|
656
|
-
def _load_field_h5(self, filePath):
|
|
658
|
+
def _load_field_h5(self, filePath,nameBlock):
|
|
657
659
|
"""
|
|
658
660
|
Load the 2D acoustic field from an H5 file.
|
|
659
661
|
|
|
@@ -664,8 +666,10 @@ class AcousticField(ABC):
|
|
|
664
666
|
- field (numpy.ndarray): The loaded acoustic field.
|
|
665
667
|
"""
|
|
666
668
|
try:
|
|
667
|
-
|
|
668
|
-
|
|
669
|
+
if nameBlock is None:
|
|
670
|
+
nameBlock = 'data'
|
|
671
|
+
with h5py.File(os.path.join(filePath, self.getName_field()+".h5"), 'r') as f:
|
|
672
|
+
self.field = f[nameBlock][:]
|
|
669
673
|
except Exception as e:
|
|
670
674
|
print(f"Error in _load_field_h5 method: {e}")
|
|
671
675
|
raise
|
|
@@ -7,6 +7,8 @@ import psutil
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import matplotlib.pyplot as plt
|
|
9
9
|
from tqdm import trange
|
|
10
|
+
import h5py
|
|
11
|
+
from scipy.io import loadmat
|
|
10
12
|
|
|
11
13
|
class Tomography(Experiment):
|
|
12
14
|
def __init__(self, **kwargs):
|
|
@@ -47,7 +49,7 @@ class Tomography(Experiment):
|
|
|
47
49
|
return False, f"OpticImage phantom shape {self.OpticImage.phantom.shape} does not match AcousticFields shape {self.AcousticFields[0].field.shape[1:]}."
|
|
48
50
|
return True, "Experiment is correctly initialized."
|
|
49
51
|
|
|
50
|
-
def generateAcousticFields(self, fieldDataPath=None, show_log=True):
|
|
52
|
+
def generateAcousticFields(self, fieldDataPath=None, show_log=True, nameBlock=None):
|
|
51
53
|
"""
|
|
52
54
|
Generate the acoustic fields for simulation.
|
|
53
55
|
Args:
|
|
@@ -57,7 +59,7 @@ class Tomography(Experiment):
|
|
|
57
59
|
systemMatrix: A numpy array of the generated fields.
|
|
58
60
|
"""
|
|
59
61
|
if self.TypeAcoustic.value == WaveType.StructuredWave.value:
|
|
60
|
-
self.AcousticFields = self._generateAcousticFields_STRUCT_CPU(fieldDataPath, show_log)
|
|
62
|
+
self.AcousticFields = self._generateAcousticFields_STRUCT_CPU(fieldDataPath, show_log, nameBlock)
|
|
61
63
|
else:
|
|
62
64
|
raise ValueError("Unsupported wave type.")
|
|
63
65
|
|
|
@@ -389,7 +391,7 @@ class Tomography(Experiment):
|
|
|
389
391
|
return True
|
|
390
392
|
|
|
391
393
|
# PRIVATE METHODS
|
|
392
|
-
def _generateAcousticFields_STRUCT_CPU(self, fieldDataPath=None, show_log=False):
|
|
394
|
+
def _generateAcousticFields_STRUCT_CPU(self, fieldDataPath=None, show_log=False, nameBlock=None):
|
|
393
395
|
if self.patterns is None:
|
|
394
396
|
raise ValueError("patterns is not initialized. Please load or generate the active list first.")
|
|
395
397
|
listAcousticFields = []
|
|
@@ -415,7 +417,7 @@ class Tomography(Experiment):
|
|
|
415
417
|
if pathField is not None and os.path.exists(pathField):
|
|
416
418
|
progress_bar.set_postfix_str(f"Loading field - {AcousticField.getName_field()} -- Memory used: {memory.percent}%")
|
|
417
419
|
try:
|
|
418
|
-
AcousticField.load_field(fieldDataPath, self.FormatSave)
|
|
420
|
+
AcousticField.load_field(fieldDataPath, self.FormatSave,nameBlock)
|
|
419
421
|
except:
|
|
420
422
|
progress_bar.set_postfix_str(f"Error loading field -> Generating field - {AcousticField.getName_field()} -- Memory used: {memory.percent}% ---- processing on {config.get_process().upper()} ----")
|
|
421
423
|
AcousticField.generate_field(show_log=show_log)
|
|
@@ -433,3 +435,71 @@ class Tomography(Experiment):
|
|
|
433
435
|
listAcousticFields.append(AcousticField)
|
|
434
436
|
progress_bar.set_postfix_str("")
|
|
435
437
|
return listAcousticFields
|
|
438
|
+
|
|
439
|
+
def load_experimentalAO(self, pathAO, withTumor = True, h5name='AOsignal'):
|
|
440
|
+
"""
|
|
441
|
+
Load experimental AO signals from specified file paths.
|
|
442
|
+
Args:
|
|
443
|
+
path_withTumor: Path to the AO signal with tumor.
|
|
444
|
+
path_withoutTumor: Path to the AO signal without tumor.
|
|
445
|
+
"""
|
|
446
|
+
if not os.path.exists(pathAO):
|
|
447
|
+
raise FileNotFoundError(f"File {pathAO} not found.")
|
|
448
|
+
|
|
449
|
+
if pathAO.endswith('.npy'):
|
|
450
|
+
ao_signal = np.load(pathAO)
|
|
451
|
+
elif pathAO.endswith('.h5'):
|
|
452
|
+
with h5py.File(pathAO, 'r') as f:
|
|
453
|
+
if h5name not in f:
|
|
454
|
+
raise KeyError(f"Dataset '{h5name}' not found in the HDF5 file.")
|
|
455
|
+
ao_signal = f[h5name][:]
|
|
456
|
+
elif pathAO.endswith('.mat'):
|
|
457
|
+
mat_data = loadmat(pathAO)
|
|
458
|
+
if h5name not in mat_data:
|
|
459
|
+
raise KeyError(f"Dataset '{h5name}' not found in the .mat file.")
|
|
460
|
+
ao_signal = mat_data[h5name]
|
|
461
|
+
elif pathAO.endswith('.hdr'):
|
|
462
|
+
ao_signal = self._loadAOSignal(pathAO)
|
|
463
|
+
else:
|
|
464
|
+
raise ValueError("Unsupported file format. Supported formats are: .npy, .h5, .mat, .hdr")
|
|
465
|
+
|
|
466
|
+
if withTumor:
|
|
467
|
+
self.AOsignal_withTumor = ao_signal
|
|
468
|
+
else:
|
|
469
|
+
self.AOsignal_withoutTumor = ao_signal
|
|
470
|
+
|
|
471
|
+
def check_experimentalAO(self, activeListPath, withTumor=True):
|
|
472
|
+
"""
|
|
473
|
+
Check if the experimental AO signals are correctly initialized.
|
|
474
|
+
"""
|
|
475
|
+
if withTumor:
|
|
476
|
+
if self.AOsignal_withTumor is None:
|
|
477
|
+
raise ValueError("Experimental AOsignal with tumor is not initialized. Please load the experimental AO signal with tumor first.")
|
|
478
|
+
else:
|
|
479
|
+
if self.AOsignal_withoutTumor is None:
|
|
480
|
+
raise ValueError("Experimental AOsignal without tumor is not initialized. Please load the experimental AO signal without tumor first.")
|
|
481
|
+
if self.AcousticFields is not None:
|
|
482
|
+
# get min time shape between all AO signals
|
|
483
|
+
print()
|
|
484
|
+
|
|
485
|
+
if self.AcousticFields[0].field.shape[0] > self.AOsignal_withTumor.shape[0]:
|
|
486
|
+
self.cutAcousticFields(max_t=self.AOsignal_withTumor.shape[0]/float(self.params.acoustic['f_saving']))
|
|
487
|
+
else:
|
|
488
|
+
for i in range(len(self.AcousticFields)):
|
|
489
|
+
min_time_shape = min(self.AcousticFields[i].field.shape[0])
|
|
490
|
+
if withTumor:
|
|
491
|
+
self.AOsignal_withTumor = self.AOsignal_withTumor[:min_time_shape, :]
|
|
492
|
+
else:
|
|
493
|
+
self.AOsignal_withoutTumor = self.AOsignal_withoutTumor[:min_time_shape, :]
|
|
494
|
+
|
|
495
|
+
for field in self.AcousticFields:
|
|
496
|
+
if activeListPath is not None:
|
|
497
|
+
with open(activeListPath, 'r') as file:
|
|
498
|
+
lines = file.readlines()
|
|
499
|
+
expected_name = lines[self.AcousticFields.index(field)].strip()
|
|
500
|
+
nameField = field.getName_field()
|
|
501
|
+
if nameField.startswith("field_"):
|
|
502
|
+
nameField = nameField[len("field_"):]
|
|
503
|
+
if nameField != expected_name:
|
|
504
|
+
raise ValueError(f"Field name {nameField} does not match the expected name {expected_name} from the active list.")
|
|
505
|
+
print("Experimental AO signals are correctly initialized.")
|