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.
@@ -6,4 +6,4 @@ from .reconstruction_utils import *
6
6
  from .culculate_refractiveindex import *
7
7
  from .evaluation import *
8
8
 
9
- __version__ = '0.0.2'
9
+ __version__ = '0.0.4'
@@ -137,7 +137,7 @@ import mpl_toolkits.axes_grid1
137
137
  # neighbor_density_array = np.asarray(neighbor_density)
138
138
  # return neighbor_density_array
139
139
 
140
- # def _normalize(self,data):
140
+ # def _normalize(data):
141
141
  # """
142
142
  # Min-Maxスケーリングを使用してデータを正規化します。
143
143
  # """
@@ -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 : np.ndarray, exp_array : np.ndarray):
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
- gpass, gstop = 2, 60
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
- order, nyq = 8, 0.5 * 1
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
- sin_ref, cos_ref = ref, np.gradient(ref) / (f1 * 2 * np.pi)
178
- cos_phi, sin_phi = lowpass(sin_ref * exp, f1), lowpass(cos_ref * exp, f1)
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
- f1 = freq_finder(ref_array[:, 100])
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: BOSlib
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: the library of Background Oriented Schlieren
5
5
  Home-page: https://github.com/ogayuuki0202/BOSlib
6
6
  Download-URL: https://github.com/ogayuuki0202/BOSlib
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: BOSlib
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: the library of Background Oriented Schlieren
5
5
  Home-page: https://github.com/ogayuuki0202/BOSlib
6
6
  Download-URL: https://github.com/ogayuuki0202/BOSlib
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes