BOSlib 0.0.2__tar.gz → 0.0.4__tar.gz
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.
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/__init__.py +1 -1
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/evaluation.py +1 -1
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/shift.py +178 -7
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib.egg-info/PKG-INFO +1 -1
- {boslib-0.0.2 → boslib-0.0.4}/PKG-INFO +1 -1
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/culculate_refractiveindex.py +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/reconstruction.py +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/reconstruction_utils.py +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/shift_utils.py +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib/utils.py +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib.egg-info/SOURCES.txt +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib.egg-info/dependency_links.txt +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib.egg-info/requires.txt +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/BOSlib.egg-info/top_level.txt +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/LICENSE +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/README.md +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/setup.cfg +0 -0
- {boslib-0.0.2 → boslib-0.0.4}/setup.py +0 -0
@@ -129,65 +129,236 @@ def SP_BOS(ref_array : np.ndarray, exp_array : np.ndarray, binarization : str ="
|
|
129
129
|
|
130
130
|
return diff_comp
|
131
131
|
|
132
|
-
def S_BOS(ref_array
|
132
|
+
def S_BOS(ref_array: np.ndarray, exp_array: np.ndarray,freq_sample_row : int = 0):
|
133
|
+
"""
|
134
|
+
Compute a 1D BOS displacement field by estimating phase differences
|
135
|
+
between reference and experimental stripe signals.
|
136
|
+
|
137
|
+
This function first identifies the dominant stripe frequency via FFT
|
138
|
+
from a representative row (`freq_sample_area`) of `ref_array`. Then for
|
139
|
+
each column signal it:
|
140
|
+
1. Bandpass-filters around the base frequency.
|
141
|
+
2. Normalizes amplitude and applies a sine-based phase correction.
|
142
|
+
3. Calculates the local phase difference via lowpass filtered
|
143
|
+
sine/cosine products.
|
144
|
+
4. Converts phase shifts to physical displacement values.
|
145
|
+
|
146
|
+
Parameters
|
147
|
+
----------
|
148
|
+
ref_array : np.ndarray, shape (M, N)
|
149
|
+
Reference image signals, with M samples (rows) and N separate
|
150
|
+
stripe‐signal columns.
|
151
|
+
exp_array : np.ndarray, shape (M, N)
|
152
|
+
Experimental image signals matching the dimensions of `ref_array`.
|
153
|
+
freq_sample_row : int
|
154
|
+
Row index in `ref_array` used to detect the dominant stripe frequency
|
155
|
+
for filtering and phase calculation.
|
156
|
+
|
157
|
+
Returns
|
158
|
+
-------
|
159
|
+
delta_h : np.ndarray, shape (M, N)
|
160
|
+
Displacement field (Δh) computed from the phase differences between
|
161
|
+
each column of `ref_array` and `exp_array`. Units are cycles/(2π·f),
|
162
|
+
where f is the dominant stripe frequency.
|
163
|
+
"""
|
133
164
|
def freq_finder(sig):
|
165
|
+
"""
|
166
|
+
Identify the dominant frequency in the signal using the FFT.
|
167
|
+
|
168
|
+
Parameters:
|
169
|
+
-----------
|
170
|
+
sig : np.ndarray
|
171
|
+
1D numpy array representing a signal.
|
172
|
+
|
173
|
+
Returns:
|
174
|
+
--------
|
175
|
+
float
|
176
|
+
The dominant frequency (above 0.01 Hz) based on amplitude.
|
177
|
+
"""
|
178
|
+
# Compute FFT frequencies
|
134
179
|
freq = np.fft.fftfreq(sig.shape[0])
|
180
|
+
# Compute FFT of the signal and normalize the amplitude
|
135
181
|
fk = np.fft.fft(sig)
|
136
182
|
fk = abs(fk / (sig.shape[0] / 2))
|
183
|
+
# Combine frequencies and amplitudes into a DataFrame
|
137
184
|
fk_df = pd.DataFrame(np.vstack([freq, fk]).T, columns=["freq", "amp"])
|
185
|
+
# Sort DataFrame by frequency and keep only non-negative frequencies
|
138
186
|
fk_df = fk_df.sort_values('freq')
|
139
187
|
fk_df = fk_df[fk_df["freq"] >= 0]
|
188
|
+
# Select frequencies above 0.01 Hz and sort by amplitude in descending order
|
140
189
|
freq_search = fk_df[fk_df["freq"] >= 0.01].sort_values('amp', ascending=False)
|
190
|
+
# Return the frequency corresponding to the highest amplitude
|
141
191
|
return freq_search.iloc[0, 0]
|
142
192
|
|
143
193
|
def bandpass(x, fa, fb):
|
144
|
-
|
194
|
+
"""
|
195
|
+
Apply a bandpass Butterworth filter to the signal.
|
196
|
+
|
197
|
+
Parameters:
|
198
|
+
-----------
|
199
|
+
x : np.ndarray
|
200
|
+
Input signal.
|
201
|
+
fa : float
|
202
|
+
Lower cutoff frequency multiplier.
|
203
|
+
fb : float
|
204
|
+
Upper cutoff frequency multiplier.
|
205
|
+
|
206
|
+
Returns:
|
207
|
+
--------
|
208
|
+
np.ndarray
|
209
|
+
The bandpass-filtered signal.
|
210
|
+
"""
|
211
|
+
gpass, gstop = 2, 60 # Passband and stopband gains (dB)
|
145
212
|
fp, fs = np.array([fa, fb]), np.array([fa / 2, fb * 2])
|
146
|
-
fn = 1 / 2
|
213
|
+
fn = 1 / 2 # Nyquist frequency (assuming a normalized sample rate)
|
147
214
|
wp, ws = fp / fn, fs / fn
|
215
|
+
# Determine the minimum filter order that meets the specifications
|
148
216
|
N, Wn = signal.buttord(wp, ws, gpass, gstop)
|
217
|
+
# Get the filter coefficients for a Butterworth filter
|
149
218
|
b, a = signal.butter(N, Wn, "band")
|
219
|
+
# Apply the filter forward and backward to avoid phase distortion
|
150
220
|
return signal.filtfilt(b, a, x)
|
151
221
|
|
152
222
|
def lowpass(x, lowcut):
|
153
|
-
|
223
|
+
"""
|
224
|
+
Apply a lowpass Butterworth filter to the signal.
|
225
|
+
|
226
|
+
Parameters:
|
227
|
+
-----------
|
228
|
+
x : np.ndarray
|
229
|
+
Input signal.
|
230
|
+
lowcut : float
|
231
|
+
The low cutoff frequency.
|
232
|
+
|
233
|
+
Returns:
|
234
|
+
--------
|
235
|
+
np.ndarray
|
236
|
+
The lowpass-filtered signal.
|
237
|
+
"""
|
238
|
+
order, nyq = 8, 0.5 * 1 # Order and Nyquist frequency (assuming sample rate = 1)
|
154
239
|
low = lowcut / nyq
|
240
|
+
# Get the filter coefficients for a lowpass Butterworth filter
|
155
241
|
b, a = signal.butter(order, low, btype='low')
|
242
|
+
# Apply the filter with zero-phase distortion
|
156
243
|
return signal.filtfilt(b, a, x)
|
157
244
|
|
158
245
|
def signal_separate(sig, f1):
|
246
|
+
"""
|
247
|
+
Separate the signal into a constant (mean) component and a bandpass-filtered component.
|
248
|
+
|
249
|
+
Parameters:
|
250
|
+
-----------
|
251
|
+
sig : np.ndarray
|
252
|
+
Input signal.
|
253
|
+
f1 : float
|
254
|
+
Base frequency used to define the bandpass range.
|
255
|
+
|
256
|
+
Returns:
|
257
|
+
--------
|
258
|
+
np.ndarray
|
259
|
+
2D array where the first column is the signal mean and the second column is the bandpass-filtered signal.
|
260
|
+
"""
|
159
261
|
sig_f = np.zeros([sig.shape[0], 2])
|
262
|
+
# First column: constant value equal to the mean of the signal
|
160
263
|
sig_f[:, 0] = sig.mean()
|
264
|
+
# Second column: bandpass filtered signal using a frequency window around f1
|
161
265
|
sig_f[:, 1] = bandpass(sig, f1 * 0.7, f1 * 1.5)
|
162
266
|
return sig_f
|
163
267
|
|
164
268
|
def signal_scale_normalize(sig, f):
|
269
|
+
"""
|
270
|
+
Normalize the signal based on a rolling maximum amplitude and add a sine correction.
|
271
|
+
|
272
|
+
Parameters:
|
273
|
+
-----------
|
274
|
+
sig : np.ndarray
|
275
|
+
Input signal.
|
276
|
+
f : float
|
277
|
+
Frequency used to calculate the sine correction.
|
278
|
+
|
279
|
+
Returns:
|
280
|
+
--------
|
281
|
+
np.ndarray
|
282
|
+
The normalized signal.
|
283
|
+
"""
|
284
|
+
# Calculate the rolling maximum absolute value over a window of 0.5/f samples
|
165
285
|
sig_abs = np.array(pd.Series(abs(sig)).rolling(int(0.5 / f), center=True).max())
|
286
|
+
# Suppress parts of the signal where the amplitude is significantly below the mean
|
166
287
|
sig[sig_abs < np.nanmean(sig_abs) * 0.5] = 0
|
167
288
|
y = np.arange(0, sig.shape[0], 1)
|
289
|
+
# Generate a sine wave for phase correction
|
168
290
|
S = np.sin(2 * np.pi * f * y)
|
291
|
+
# Create a correction term based on the amplitude threshold
|
169
292
|
S1 = (1 - (sig_abs > np.nanmean(sig_abs * 0.5))) * S
|
293
|
+
# Add the correction term to the signal
|
170
294
|
sig = sig + S1
|
295
|
+
# Avoid division by very small numbers
|
171
296
|
sig_abs[sig_abs < np.nanmean(sig_abs * 0.5)] = 1
|
297
|
+
# Normalize the signal
|
172
298
|
sig_norm = sig / sig_abs
|
173
299
|
sig_norm[np.isnan(sig_norm)] = 0
|
174
300
|
return sig_norm
|
175
301
|
|
176
302
|
def phase_calculate(ref, exp, f1):
|
177
|
-
|
178
|
-
|
303
|
+
"""
|
304
|
+
Calculate the phase difference between the reference and experimental signals.
|
305
|
+
|
306
|
+
Parameters:
|
307
|
+
-----------
|
308
|
+
ref : np.ndarray
|
309
|
+
Normalized reference signal.
|
310
|
+
exp : np.ndarray
|
311
|
+
Normalized experimental signal.
|
312
|
+
f1 : float
|
313
|
+
Base frequency.
|
314
|
+
|
315
|
+
Returns:
|
316
|
+
--------
|
317
|
+
np.ndarray
|
318
|
+
The phase difference calculated using lowpass filtered sine and cosine components.
|
319
|
+
"""
|
320
|
+
# Compute sine and its gradient (approximation for cosine)
|
321
|
+
sin_ref = ref
|
322
|
+
cos_ref = np.gradient(ref) / (f1 * 2 * np.pi)
|
323
|
+
# Compute lowpass filtered products to obtain sine and cosine components of the phase difference
|
324
|
+
cos_phi = lowpass(sin_ref * exp, f1)
|
325
|
+
sin_phi = lowpass(cos_ref * exp, f1)
|
326
|
+
# Calculate the phase difference using arctan2 for correct quadrant determination
|
179
327
|
return np.arctan2(sin_phi, cos_phi)
|
180
328
|
|
181
329
|
def phase_1DBOS_process(sig_ref, sig_exp, f1):
|
330
|
+
"""
|
331
|
+
Process a pair of reference and experimental signals to compute the phase difference.
|
332
|
+
|
333
|
+
Parameters:
|
334
|
+
-----------
|
335
|
+
sig_ref : np.ndarray
|
336
|
+
Single column from the reference signal array.
|
337
|
+
sig_exp : np.ndarray
|
338
|
+
Corresponding column from the experimental signal array.
|
339
|
+
f1 : float
|
340
|
+
Base frequency obtained from the reference array.
|
341
|
+
|
342
|
+
Returns:
|
343
|
+
--------
|
344
|
+
np.ndarray
|
345
|
+
The phase difference between the processed reference and experimental signals.
|
346
|
+
"""
|
347
|
+
# Separate the signal into mean and bandpass-filtered components and normalize them
|
182
348
|
separate_sig_ref = signal_scale_normalize(signal_separate(sig_ref, f1)[:, 1], f1)
|
183
349
|
separate_sig_exp = signal_scale_normalize(signal_separate(sig_exp, f1)[:, 1], f1)
|
350
|
+
# Calculate the phase difference between the normalized signals
|
184
351
|
return phase_calculate(separate_sig_ref, separate_sig_exp, f1)
|
185
352
|
|
186
|
-
|
353
|
+
# Determine the dominant frequency from a representative column (column 100) of the reference array
|
354
|
+
f1 = freq_finder(ref_array[:,freq_sample_row])
|
355
|
+
# Initialize a 2D array to store phase differences for each column
|
187
356
|
phi_2D = np.zeros([ref_array.shape[0], ref_array.shape[1]]).astype("float64")
|
188
357
|
|
358
|
+
# Process each column of the reference and experimental arrays
|
189
359
|
for x in range(ref_array.shape[1]):
|
190
360
|
phi_2D[:, x] = phase_1DBOS_process(ref_array[:, x], exp_array[:, x], f1)
|
191
361
|
|
362
|
+
# Convert phase differences into displacement by dividing by (2*pi*f1)
|
192
363
|
delta_h = phi_2D / (2 * np.pi * f1)
|
193
364
|
return delta_h
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|