Skip to content

Commit ee759f3

Browse files
author
Clea Parcerisas
committed
Changed the SNR detector of impulse detections, added the detection band and alaysis band selection
1 parent 32650ba commit ee759f3

File tree

3 files changed

+65
-33
lines changed

3 files changed

+65
-33
lines changed

pypam/acoustic_file.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ def correlation(self, signal, fs_signal, binsize=1.0):
812812
return fs
813813

814814
def detect_piling_events(self, min_separation, max_duration, threshold, dt, binsize=None,
815-
verbose=False, **kwargs):
815+
verbose=False, save_path=None, band=None, method=None):
816816
"""
817817
Detect piling events
818818
@@ -839,20 +839,19 @@ def detect_piling_events(self, min_separation, max_duration, threshold, dt, bins
839839

840840
detector = impulse_detector.PilingDetector(min_separation=min_separation,
841841
max_duration=max_duration,
842-
threshold=threshold, dt=dt)
842+
threshold=threshold, dt=dt, detection_band=band,
843+
analysis_band=self.band)
843844
total_events = pd.DataFrame()
844-
if 'save_path' in kwargs.keys():
845-
save_path = kwargs['save_path']
846-
else:
847-
save_path = None
848845
for i, block in enumerate(sf.blocks(self.file_path, blocksize=blocksize, start=self._start_frame)):
849846
time_bin = self.time_bin(blocksize, i)
850847
print('bin %s' % time_bin)
851848
signal_upa = self.wav2upa(wav=block)
852849
signal = Signal(signal=signal_upa, fs=self.fs, channel=self.channel)
853850
signal.set_band(band=self.band)
854-
events_df = detector.detect_events(signal, method='snr', verbose=verbose,
855-
save_path=save_path)
851+
events_df = detector.detect_events(signal, method=method, verbose=verbose,
852+
save_path=save_path.joinpath('%s.png' %
853+
datetime.datetime.strftime(time_bin,
854+
"%y%m%d_%H%M%S")))
856855
events_df['datetime'] = pd.to_timedelta(events_df[('temporal', 'start_seconds')],
857856
unit='seconds') + time_bin
858857
events_df = events_df.set_index('datetime')

pypam/acoustic_survey.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ def cut_and_place_files_period(self, period, folder_name, extensions=None):
368368
pass
369369
return 0
370370

371-
def detect_piling_events(self, min_separation, max_duration, threshold, dt=None, verbose=False):
371+
def detect_piling_events(self, min_separation, max_duration, threshold, dt=None, verbose=False, **kwargs):
372372
"""
373373
Return a DataFrame with all the piling events and their rms, sel and peak values
374374
@@ -398,7 +398,7 @@ def detect_piling_events(self, min_separation, max_duration, threshold, dt=None,
398398
threshold=threshold,
399399
max_duration=max_duration,
400400
dt=dt, binsize=self.binsize,
401-
verbose=verbose)
401+
verbose=verbose, **kwargs)
402402
df = df.append(df_output)
403403
return df
404404

pypam/impulse_detector.py

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727

2828
class ImpulseDetector:
29-
def __init__(self, min_separation, max_duration, band, threshold=150, dt=None):
29+
def __init__(self, min_separation, max_duration, detection_band, analysis_band, threshold=150, dt=None):
3030
"""
3131
Impulse events detector
3232
@@ -36,6 +36,10 @@ def __init__(self, min_separation, max_duration, band, threshold=150, dt=None):
3636
Minimum separation of the event, in seconds
3737
max_duration : float
3838
Maxium duration of the event, in seconds
39+
detection_band : list
40+
List or tuple of (min_freq, max_freq) of the frequency band used to detect the signals
41+
analysis_band : list
42+
List or tuple of (min_freq, max_freq) of the frequency band used to characterize the impulses
3943
threshold : float
4044
Threshold above ref value which one it is considered piling, in db
4145
dt : float
@@ -46,7 +50,8 @@ def __init__(self, min_separation, max_duration, band, threshold=150, dt=None):
4650
self.max_duration = max_duration
4751
self.threshold = threshold
4852
self.dt = dt
49-
self.band = band
53+
self.detection_band = detection_band
54+
self.analysis_band = analysis_band
5055

5156
def detect_events(self, signal, method='dt', verbose=False, save_path=None):
5257
"""
@@ -89,7 +94,7 @@ def detect_events_dt(self, signal, verbose=False, save_path=None):
8994
save_path : string or Path
9095
Where to save the image. Set to None if it should not be saved
9196
"""
92-
signal.set_band(self.band)
97+
signal.set_band(self.detection_band)
9398
levels = []
9499
blocksize = int(self.dt * signal.fs)
95100
for block in signal.Blocks(blocksize=blocksize):
@@ -116,10 +121,8 @@ def detect_events_envelope(self, signal, verbose=False, save_path=None):
116121
save_path : string or Path
117122
Where to save the image. Set to None if it should not be saved
118123
"""
119-
signal.set_band(band=self.band)
124+
signal.set_band(band=self.detection_band)
120125
envelope = signal.envelope()
121-
envelope = utils.to_db(envelope, ref=1.0, square=True)
122-
123126
times_events = events_times_diff(signal=envelope, fs=signal.fs, threshold=self.threshold,
124127
max_duration=self.max_duration,
125128
min_separation=self.min_separation)
@@ -144,12 +147,12 @@ def detect_events_snr(self, signal, verbose=False, save_path=None):
144147
Where to save the image. Set to None if it should not be saved
145148
"""
146149
blocksize = int(self.dt * signal.fs)
147-
signal.set_band(band=self.band)
150+
signal.set_band(band=self.detection_band)
148151
envelope = signal.envelope()
149-
envelope = utils.to_db(envelope, ref=1.0, square=True)
150152
times_events = events_times_snr(signal=envelope, blocksize=blocksize, fs=signal.fs,
151153
threshold=self.threshold, max_duration=self.max_duration,
152-
min_separation=self.min_separation)
154+
min_separation=self.min_separation, original_sig=signal.signal)
155+
153156
events_df = self.load_all_times_events(times_events, signal, verbose=verbose)
154157

155158
if verbose:
@@ -183,10 +186,11 @@ def load_event(self, s, t, duration, removenoise=True, verbose=False):
183186
n2 = min(int((t + duration + self.min_separation) * s.fs), s.signal.shape[0])
184187
event = Event(s.signal[n1:n2], s.fs)
185188
noise_clip = np.concatenate((s.signal[n1:start_n], s.signal[end_n:n2]))
186-
event.reduce_noise(noise_clip=noise_clip, nfft=512, verbose=verbose)
189+
event.reduce_noise(noise_clip=noise_clip, nfft=512, verbose=False)
187190
event.signal = event.signal[start_n - n1:end_n - n1]
188191
else:
189192
event = Event(s.signal[start_n:end_n], s.fs)
193+
190194
return event
191195

192196
def load_all_times_events(self, times_events, signal, verbose=False):
@@ -202,10 +206,10 @@ def load_all_times_events(self, times_events, signal, verbose=False):
202206
verbose : bool
203207
Set to True to plot all the events of the signal
204208
"""
205-
signal.set_band([10, 20000])
209+
signal.set_band(self.analysis_band)
206210
columns_temp = ['start_seconds', 'end_seconds', 'duration', 'rms', 'sel', 'peak']
207211
columns_df = pd.DataFrame({'variable': 'temporal', 'value': columns_temp})
208-
freq = sci.fft.rfftfreq(128) * 40000
212+
freq = sci.fft.rfftfreq(128) * signal.fs
209213
columns_df = pd.concat([columns_df, pd.DataFrame({'variable': 'psd', 'value': freq})])
210214
columns = pd.MultiIndex.from_frame(columns_df)
211215
events_df = pd.DataFrame(columns=columns)
@@ -231,7 +235,7 @@ def plot_all_events(self, signal, events_df, save_path=None):
231235
save_path : string or Path
232236
Where to save the image. Set to None if it should not be saved
233237
"""
234-
signal.set_band(band=self.band)
238+
signal.set_band(band=self.detection_band)
235239
fbands, t, sxx = signal.spectrogram(nfft=512, scaling='spectrum', db=True, mode='fast')
236240
fig, ax = plt.subplots(3, 1, sharex='col')
237241
im = ax[0].pcolormesh(t, fbands, sxx, shading='auto')
@@ -275,9 +279,13 @@ def plot_all_events(self, signal, events_df, save_path=None):
275279

276280

277281
class PilingDetector(ImpulseDetector):
278-
def __init__(self, min_separation, max_duration, threshold, dt):
282+
def __init__(self, min_separation, max_duration, threshold, dt, detection_band=None, analysis_band=None):
283+
if detection_band is None:
284+
detection_band = [500, 1000]
285+
if analysis_band is None:
286+
analysis_band = [100, 20000]
279287
super().__init__(min_separation=min_separation, max_duration=max_duration,
280-
band=[5000, 10000], threshold=threshold, dt=dt)
288+
detection_band=detection_band, analysis_band=analysis_band, threshold=threshold, dt=dt)
281289

282290

283291
@nb.jit
@@ -304,21 +312,30 @@ def events_times_diff(signal, fs, threshold, max_duration, min_separation):
304312
event_max_val = 0
305313
last_xi = 0
306314
i = 0
315+
threshold_upa = 10 ** (threshold/20.0)
307316
while i < len(signal):
308317
xi = signal[i]
318+
309319
if event_on:
310320
duration = (i - event_start) / fs
311-
if duration >= max_duration or xi < (event_max_val - threshold):
312-
# Event finished, too long! Or event detected!
321+
if duration >= max_duration or (event_max_val - xi) >= threshold_upa:
322+
# Event finished, too long! Or event end detected
313323
event_on = False
314324
event_end = i
315325
times_events.append([event_start / fs, duration, event_end / fs])
316326
i += min_separation_samples
317327
event_max_val = 0
328+
# plt.Figure()
329+
# plt.plot(signal[event_start - min_separation_samples:event_end + min_separation_samples])
330+
# plt.axvline(min_separation_samples, color='green', label='Start')
331+
# plt.axvline(min_separation_samples + event_end - event_start, color='red', label='End')
332+
# plt.show()
333+
# plt.close()
318334
elif xi > event_max_val:
319335
event_max_val = xi
320336
else:
321-
if i != 0 and (xi - last_xi) >= threshold:
337+
if i != 0 and (xi - last_xi) >= threshold_upa:
338+
# There is a big jump, start event!
322339
event_on = True
323340
event_start = i
324341
event_max_val = xi
@@ -328,31 +345,47 @@ def events_times_diff(signal, fs, threshold, max_duration, min_separation):
328345

329346

330347
@nb.jit
331-
def events_times_snr(signal, fs, blocksize, threshold, max_duration, min_separation):
348+
def events_times_snr(signal, fs, blocksize, threshold, max_duration, min_separation, original_sig):
332349
times_events = []
333350
min_separation_samples = int(min_separation * fs)
334351
event_on = False
335352
event_start = 0
336353
event_end = 0
337354
j = 0
355+
threshold_upa = 10 ** (threshold/20.0)
338356
while j < len(signal):
339357
if j + blocksize > len(signal):
340358
blocksize = len(signal) - j
341-
noise = np.mean(signal[j:j + blocksize])
359+
noise = np.sqrt(np.mean(signal[j:j + blocksize]**2))
360+
max_value = noise
342361
for i in np.arange(blocksize - 1) + j:
343362
xi = signal[i]
344-
snr = xi - noise
345363
if event_on:
346364
duration = (i - event_start) / fs
347-
if duration >= max_duration or snr < threshold:
365+
if duration >= max_duration or (xi - noise) < 10**(6.0/20.0):
348366
# Event finished, too long! Or event detected!
349367
event_on = False
350368
event_end = i
351369
times_events.append([event_start / fs, duration, event_end / fs])
370+
# plt.Figure()
371+
# plt.plot(original_sig[event_start-min_separation_samples:event_end+min_separation_samples],
372+
# label='Signal')
373+
# plt.plot(signal[event_start-min_separation_samples:event_end+min_separation_samples],
374+
# label='Envelope')
375+
# plt.axhline(noise + threshold_upa, label='Threshold')
376+
# plt.axhline(noise + threshold_upa/2.0, label='Threshold')
377+
# plt.axvline(min_separation_samples, color='green', label='Start')
378+
# plt.axvline(min_separation_samples+event_end-event_start, color='red', label='End')
379+
# plt.show()
380+
# plt.close()
381+
if xi > max_value:
382+
max_value = xi
352383
else:
353-
if snr >= threshold:
384+
if (xi - noise) >= threshold_upa:
354385
if len(times_events) == 0 or (i - event_end) >= min_separation_samples:
355386
event_on = True
356387
event_start = i
388+
max_value = xi
357389
j += blocksize
390+
358391
return times_events

0 commit comments

Comments
 (0)