From 834d260c9607dfccc317e73a3c2982c20cfaa0b7 Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Sat, 31 May 2025 00:31:16 +0200 Subject: [PATCH] wip --- process_chunks.py | 108 +++++++++---------------------------------- process_chunks_.py | 113 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 85 deletions(-) create mode 100755 process_chunks_.py diff --git a/process_chunks.py b/process_chunks.py index 0f4a5ab..1106c6b 100755 --- a/process_chunks.py +++ b/process_chunks.py @@ -9,104 +9,42 @@ import scipy.signal from scipy.fft import fft, fftfreq import shutil -CHUNK_DIR = "chunks" -PROCESSED_CHUNK_DIR = "chunks/processed" -EVENT_DIR = "events" -SAMPLE_SECONDS = 1 -TOLERANCE = 2 -OVERTONE_TOLERANCE = TOLERANCE * 2 +RECORDINGS_DIR = "recordings" +PROCESSED_RECORDINGS_DIR = "recordings/processed" +DETECTIONS_DIR = "events" +DETECT_FREQUENCY_FROM = 210 +DETECT_FREQUENCY_TO = 212 +CLIP_SECONDS = 1 THRESHOLD_BASE = 0.1 -THRESHOLD_OCT = THRESHOLD_BASE / 10 +OCTAVE_FACTOR = 0.1 CLIP_PADDING_BEFORE = 1 CLIP_PADDING_AFTER = 6 -TARGET_FREQ = 211 -OVERTONE_FREQ = TARGET_FREQ * 2 -NFFT = 32768 -SKIP_SECONDS = 10 + def process_chunk(filename): - input_path = os.path.join(CHUNK_DIR, filename) - print(f"🔍 Verarbeite {input_path}...") + print('processing', filename) + path = os.path.join(RECORDINGS_DIR, filename) + info = soundfile.info(path) + samplerate = info.samplerate + blocksize = int(CLIP_SECONDS * samplerate) - # Frequenzanalyse und Event-Erkennung - data, samplerate = soundfile.read(input_path) - if data.ndim > 1: - data = data[:, 0] # nur Kanal 1 + print(info) - chunk_samples = int(SAMPLE_SECONDS * samplerate) - skip_samples = int(SKIP_SECONDS * samplerate) - padding_before = int(CLIP_PADDING_BEFORE * samplerate) - padding_after = int(CLIP_PADDING_AFTER * samplerate) - - chunk_start_str = os.path.splitext(filename)[0] - chunk_start_dt = datetime.datetime.strptime(chunk_start_str, "%Y%m%d-%H%M%S") - - i = 0 - last_event = -skip_samples - - while i + chunk_samples <= len(data): - clip = data[i:i+chunk_samples] - - freqs, times, Sxx = scipy.signal.spectrogram(clip, samplerate, nperseg=NFFT) - idx_base = np.where((freqs >= TARGET_FREQ - TOLERANCE) & (freqs <= TARGET_FREQ + TOLERANCE))[0] - idx_oct = np.where((freqs >= OVERTONE_FREQ - OVERTONE_TOLERANCE) & (freqs <= OVERTONE_FREQ + OVERTONE_TOLERANCE))[0] - if len(idx_base) == 0 or len(idx_oct) == 0: - return False - base_energy = np.mean(Sxx[idx_base]) - oct_energy = np.mean(Sxx[idx_oct]) - total_energy = np.mean(Sxx[freqs <= 1000, :], axis=0).max() - - fft_vals = np.abs(fft(clip)) - freqs = fftfreq(len(clip), 1/samplerate) - peak_freq = freqs[np.argmax(fft_vals)] - is_peak_near_target = TARGET_FREQ - TOLERANCE <= peak_freq <= TARGET_FREQ + TOLERANCE - - event_detected = is_peak_near_target and base_energy > THRESHOLD_BASE * total_energy and oct_energy > THRESHOLD_OCT * total_energy - - if i - last_event >= skip_samples and event_detected: - clip_start = max(0, i - padding_before) - clip_end = min(len(data), i + chunk_samples + padding_after) - clip = data[clip_start:clip_end] - - event_offset = (i - padding_before) / samplerate - event_time_dt = chunk_start_dt + datetime.timedelta(seconds=event_offset) - event_time = event_time_dt.strftime("%Y%m%d-%H%M%S") - - flac_out = os.path.join(EVENT_DIR, f"{event_time}.flac") - png_out = os.path.join(EVENT_DIR, f"{event_time}.png") - soundfile.write(flac_out, clip, samplerate, format='FLAC') - - plt.figure() - plt.specgram(clip, Fs=samplerate, NFFT=NFFT, noverlap=NFFT//2, cmap='inferno', vmin=-90, vmax=-20) - plt.title(f"Spectrogram: {event_time}") - plt.xlabel("Time (s)") - plt.ylabel("Frequency (Hz)") - plt.colorbar(label="dB") - plt.savefig(png_out) - plt.close() - - print(f"Event: {event_time} peak_freq: {peak_freq:.9f} Hz, base_energy: {base_energy/total_energy:.9f}, oct_energy: {oct_energy/total_energy:.9f}, total_energy: {total_energy:.9f}") - last_event = i - i += skip_samples - else: - i += chunk_samples - - # Datei verschieben - output_path = os.path.join(PROCESSED_CHUNK_DIR, filename) - #shutil.move(input_path, output_path) - print(f"✅ Verschoben nach {output_path}") + for block in soundfile.blocks(path, blocksize=blocksize, overlap=0): + strengths = fft(block) + labels = fftfreq(len(block), d=1/samplerate) + # get the frequency with the highest strength + max_freq = labels[np.argmax(np.abs(strengths))] + print(max_freq) def main(): - os.makedirs(EVENT_DIR, exist_ok=True) - os.makedirs(PROCESSED_CHUNK_DIR, exist_ok=True) + os.makedirs(RECORDINGS_DIR, exist_ok=True) + os.makedirs(PROCESSED_RECORDINGS_DIR, exist_ok=True) - for file in os.listdir(CHUNK_DIR): + for file in os.listdir(RECORDINGS_DIR): if file.endswith(".flac"): process_chunk(file) - # with concurrent.futures.ProcessPoolExecutor() as executor: - # files = [f for f in os.listdir(CHUNK_DIR) if f.endswith(".flac")] - # executor.map(process_chunk, files) if __name__ == "__main__": main() \ No newline at end of file diff --git a/process_chunks_.py b/process_chunks_.py new file mode 100755 index 0000000..38efb68 --- /dev/null +++ b/process_chunks_.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +import os +import concurrent.futures +import datetime +import numpy as np +import matplotlib.pyplot as plt +import soundfile +import scipy.signal +from scipy.fft import fft, fftfreq +import shutil + +CHUNK_DIR = "chunks" +PROCESSED_CHUNK_DIR = "chunks/processed" +EVENT_DIR = "events" +SAMPLE_SECONDS = 1 +TOLERANCE = 2 +OVERTONE_TOLERANCE = TOLERANCE * 2 +THRESHOLD_BASE = 0.1 +THRESHOLD_OCT = THRESHOLD_BASE / 10 +CLIP_PADDING_BEFORE = 1 +CLIP_PADDING_AFTER = 6 +TARGET_FREQ = 211 +OVERTONE_FREQ = TARGET_FREQ * 2 +NFFT = 32768 +SKIP_SECONDS = 10 + +def process_chunk(filename): + input_path = os.path.join(CHUNK_DIR, filename) + print(f"🔍 Verarbeite {input_path}...") + + # Frequenzanalyse und Event-Erkennung + data, samplerate = soundfile.read(input_path) + if data.ndim > 1: + data = data[:, 0] # nur Kanal 1 + + chunk_samples = int(SAMPLE_SECONDS * samplerate) + skip_samples = int(SKIP_SECONDS * samplerate) + padding_before = int(CLIP_PADDING_BEFORE * samplerate) + padding_after = int(CLIP_PADDING_AFTER * samplerate) + + chunk_start_str = os.path.splitext(filename)[0] + chunk_start_dt = datetime.datetime.strptime(chunk_start_str, "%Y%m%d-%H%M%S") + + i = 0 + last_event = -skip_samples + + while i + chunk_samples <= len(data): + clip = data[i:i+chunk_samples] + + freqs, times, Sxx = scipy.signal.spectrogram(clip, samplerate, nperseg=NFFT) + + idx_base = np.where((freqs >= TARGET_FREQ - TOLERANCE) & (freqs <= TARGET_FREQ + TOLERANCE))[0] + idx_oct = np.where((freqs >= OVERTONE_FREQ - OVERTONE_TOLERANCE) & (freqs <= OVERTONE_FREQ + OVERTONE_TOLERANCE))[0] + if len(idx_base) == 0 or len(idx_oct) == 0: + return False + base_energy = np.mean(Sxx[idx_base]) + oct_energy = np.mean(Sxx[idx_oct]) + total_energy = np.mean(Sxx[freqs <= 1000, :], axis=0).max() + + fft_vals = np.abs(fft(clip)) + freqs = fftfreq(len(clip), 1/samplerate) + peak_freq = freqs[np.argmax(fft_vals)] + is_peak_near_target = TARGET_FREQ - TOLERANCE <= peak_freq <= TARGET_FREQ + TOLERANCE + + event_detected = is_peak_near_target and base_energy > THRESHOLD_BASE * total_energy and oct_energy > THRESHOLD_OCT * total_energy + + if i - last_event >= skip_samples and event_detected: + clip_start = max(0, i - padding_before) + clip_end = min(len(data), i + chunk_samples + padding_after) + clip = data[clip_start:clip_end] + + event_offset = (i - padding_before) / samplerate + event_time_dt = chunk_start_dt + datetime.timedelta(seconds=event_offset) + event_time = event_time_dt.strftime("%Y%m%d-%H%M%S") + + flac_out = os.path.join(EVENT_DIR, f"{event_time}.flac") + png_out = os.path.join(EVENT_DIR, f"{event_time}.png") + soundfile.write(flac_out, clip, samplerate, format='FLAC') + + plt.figure() + plt.specgram(clip, Fs=samplerate, NFFT=NFFT, noverlap=NFFT//2, cmap='inferno', vmin=-90, vmax=-20) + plt.title(f"Spectrogram: {event_time}") + plt.xlabel("Time (s)") + plt.ylabel("Frequency (Hz)") + plt.colorbar(label="dB") + plt.savefig(png_out) + plt.close() + + print(f"Event: {event_time} peak_freq: {peak_freq:.9f} Hz, base_energy: {base_energy/total_energy:.9f}, oct_energy: {oct_energy/total_energy:.9f}, total_energy: {total_energy:.9f}") + last_event = i + i += skip_samples + else: + i += chunk_samples + + # Datei verschieben + output_path = os.path.join(PROCESSED_CHUNK_DIR, filename) + #shutil.move(input_path, output_path) + print(f"✅ Verschoben nach {output_path}") + +def main(): + os.makedirs(EVENT_DIR, exist_ok=True) + os.makedirs(PROCESSED_CHUNK_DIR, exist_ok=True) + + for file in os.listdir(CHUNK_DIR): + if file.endswith(".flac"): + process_chunk(file) + + # with concurrent.futures.ProcessPoolExecutor() as executor: + # files = [f for f in os.listdir(CHUNK_DIR) if f.endswith(".flac")] + # executor.map(process_chunk, files) + +if __name__ == "__main__": + main() \ No newline at end of file