This commit is contained in:
CroneKorkN 2025-06-01 19:23:40 +02:00
parent 6c06c42e85
commit cf5b1396b1
Signed by: cronekorkn
SSH key fingerprint: SHA256:v0410ZKfuO1QHdgKBsdQNF64xmTxOF8osF1LIqwTcVw

37
process
View file

@ -1,13 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import concurrent.futures
import datetime import datetime
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import soundfile import soundfile
import scipy.signal
from scipy.fft import rfft, rfftfreq from scipy.fft import rfft, rfftfreq
import shutil import shutil
import traceback
RECORDINGS_DIR = "recordings" RECORDINGS_DIR = "recordings"
PROCESSED_RECORDINGS_DIR = "recordings/processed" PROCESSED_RECORDINGS_DIR = "recordings/processed"
@ -23,6 +23,7 @@ DETECTION_DISTANCE_SECONDS = 30 # seconds (minimum time between detections)
DETECTION_DISTANCE_BLOCKS = DETECTION_DISTANCE_SECONDS // BLOCK_SECONDS # number of blocks to skip after a detection DETECTION_DISTANCE_BLOCKS = DETECTION_DISTANCE_SECONDS // BLOCK_SECONDS # number of blocks to skip after a detection
BLOCK_OVERLAP_FACTOR = 0.9 # overlap between blocks (0.2 means 20% overlap) BLOCK_OVERLAP_FACTOR = 0.9 # overlap between blocks (0.2 means 20% overlap)
MIN_SIGNAL_QUALITY = 1000.0 # maximum noise level (relative DB) to consider a detection valid MIN_SIGNAL_QUALITY = 1000.0 # maximum noise level (relative DB) to consider a detection valid
PLOT_PADDING_SECONDS = 2 # seconds (padding before and after the event in the plot)
def process_recording(filename): def process_recording(filename):
print('processing', filename) print('processing', filename)
@ -78,6 +79,8 @@ def process_recording(filename):
current_event = { current_event = {
'start_at': block_date, 'start_at': block_date,
'end_at': block_date, 'end_at': block_date,
'start_sample': sample_num,
'end_sample': sample_num + samples_per_block,
'start_freq': max_freq, 'start_freq': max_freq,
'end_freq': max_freq, 'end_freq': max_freq,
'max_amplitude': max_amplitude, 'max_amplitude': max_amplitude,
@ -86,6 +89,7 @@ def process_recording(filename):
current_event.update({ current_event.update({
'end_at': block_date, 'end_at': block_date,
'end_freq': max_freq, 'end_freq': max_freq,
'end_sample': sample_num + samples_per_block,
'max_amplitude': max(max_amplitude, current_event['max_amplitude']), 'max_amplitude': max(max_amplitude, current_event['max_amplitude']),
}) })
print(f'- {block_date.strftime('%Y-%m-%d %H:%M:%S')}: {max_amplitude:.1f}rDB @ {max_freq:.1f}Hz (signal {signal_quality:.3f}x)') print(f'- {block_date.strftime('%Y-%m-%d %H:%M:%S')}: {max_amplitude:.1f}rDB @ {max_freq:.1f}Hz (signal {signal_quality:.3f}x)')
@ -95,8 +99,7 @@ def process_recording(filename):
duration = (current_event['end_at'] - current_event['start_at']).total_seconds() duration = (current_event['end_at'] - current_event['start_at']).total_seconds()
print(f'🔊 {current_event['start_at'].strftime('%Y-%m-%d %H:%M:%S')} ({duration:.1f}s): {current_event['start_freq']:.1f}Hz->{current_event['end_freq']:.1f}Hz @{current_event['max_amplitude']:.0f}rDB') print(f'🔊 {current_event['start_at'].strftime('%Y-%m-%d %H:%M:%S')} ({duration:.1f}s): {current_event['start_freq']:.1f}Hz->{current_event['end_freq']:.1f}Hz @{current_event['max_amplitude']:.0f}rDB')
write_clip() write_event(current_event=current_event, sound=sound, samplerate=samplerate)
write_plot()
current_event = None current_event = None
sample_num += DETECTION_DISTANCE_BLOCKS * samples_per_block sample_num += DETECTION_DISTANCE_BLOCKS * samples_per_block
@ -105,12 +108,25 @@ def process_recording(filename):
def write_clip(): def write_event(current_event, sound, samplerate):
pass # write a spectrogram using the sound from start to end of the event
event_start_sample = current_event['start_sample'] - samplerate * PLOT_PADDING_SECONDS
event_end_sample = current_event['end_sample'] + samplerate * PLOT_PADDING_SECONDS
def write_plot(): event_clip = sound[event_start_sample:event_end_sample]
pass plot_start_date = current_event['start_at'] - datetime.timedelta(seconds=PLOT_PADDING_SECONDS)
plt.figure(figsize=(8, 6))
plt.specgram(event_clip, Fs=samplerate, NFFT=samplerate, noverlap=samplerate//2, cmap='inferno', vmin=-100, vmax=-10)
plt.title(f"Bootshorn @{plot_start_date.strftime('%Y-%m-%d %H:%M:%S')}")
plt.xlabel("Time (s)")
plt.ylabel("Frequency (Hz)")
plt.colorbar(label="Intensity (dB)")
plt.ylim(50, 1000)
spectrogram_path = os.path.join(DETECTIONS_DIR, f"{current_event['start_at'].strftime('%Y-%m-%d_%H-%M-%S.%f%z')}.png")
plt.savefig(spectrogram_path)
plt.close()
# write flac
flac_path = os.path.join(DETECTIONS_DIR, f"{current_event['start_at'].strftime('%Y-%m-%d_%H-%M-%S.%f%z')}.flac")
soundfile.write(flac_path, event_clip, samplerate, format='FLAC')
def main(): def main():
@ -124,7 +140,6 @@ def main():
except Exception as e: except Exception as e:
print(f"Error processing {filename}: {e}") print(f"Error processing {filename}: {e}")
# print stacktrace # print stacktrace
import traceback
traceback.print_exc() traceback.print_exc()