wip
This commit is contained in:
parent
f86cd966b0
commit
834d260c96
2 changed files with 136 additions and 85 deletions
|
@ -9,104 +9,42 @@ import scipy.signal
|
||||||
from scipy.fft import fft, fftfreq
|
from scipy.fft import fft, fftfreq
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
CHUNK_DIR = "chunks"
|
RECORDINGS_DIR = "recordings"
|
||||||
PROCESSED_CHUNK_DIR = "chunks/processed"
|
PROCESSED_RECORDINGS_DIR = "recordings/processed"
|
||||||
EVENT_DIR = "events"
|
DETECTIONS_DIR = "events"
|
||||||
SAMPLE_SECONDS = 1
|
DETECT_FREQUENCY_FROM = 210
|
||||||
TOLERANCE = 2
|
DETECT_FREQUENCY_TO = 212
|
||||||
OVERTONE_TOLERANCE = TOLERANCE * 2
|
CLIP_SECONDS = 1
|
||||||
THRESHOLD_BASE = 0.1
|
THRESHOLD_BASE = 0.1
|
||||||
THRESHOLD_OCT = THRESHOLD_BASE / 10
|
OCTAVE_FACTOR = 0.1
|
||||||
CLIP_PADDING_BEFORE = 1
|
CLIP_PADDING_BEFORE = 1
|
||||||
CLIP_PADDING_AFTER = 6
|
CLIP_PADDING_AFTER = 6
|
||||||
TARGET_FREQ = 211
|
|
||||||
OVERTONE_FREQ = TARGET_FREQ * 2
|
|
||||||
NFFT = 32768
|
|
||||||
SKIP_SECONDS = 10
|
|
||||||
|
|
||||||
def process_chunk(filename):
|
def process_chunk(filename):
|
||||||
input_path = os.path.join(CHUNK_DIR, filename)
|
print('processing', filename)
|
||||||
print(f"🔍 Verarbeite {input_path}...")
|
path = os.path.join(RECORDINGS_DIR, filename)
|
||||||
|
info = soundfile.info(path)
|
||||||
|
samplerate = info.samplerate
|
||||||
|
blocksize = int(CLIP_SECONDS * samplerate)
|
||||||
|
|
||||||
# Frequenzanalyse und Event-Erkennung
|
print(info)
|
||||||
data, samplerate = soundfile.read(input_path)
|
|
||||||
if data.ndim > 1:
|
|
||||||
data = data[:, 0] # nur Kanal 1
|
|
||||||
|
|
||||||
chunk_samples = int(SAMPLE_SECONDS * samplerate)
|
for block in soundfile.blocks(path, blocksize=blocksize, overlap=0):
|
||||||
skip_samples = int(SKIP_SECONDS * samplerate)
|
strengths = fft(block)
|
||||||
padding_before = int(CLIP_PADDING_BEFORE * samplerate)
|
labels = fftfreq(len(block), d=1/samplerate)
|
||||||
padding_after = int(CLIP_PADDING_AFTER * samplerate)
|
# get the frequency with the highest strength
|
||||||
|
max_freq = labels[np.argmax(np.abs(strengths))]
|
||||||
chunk_start_str = os.path.splitext(filename)[0]
|
print(max_freq)
|
||||||
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():
|
def main():
|
||||||
os.makedirs(EVENT_DIR, exist_ok=True)
|
os.makedirs(RECORDINGS_DIR, exist_ok=True)
|
||||||
os.makedirs(PROCESSED_CHUNK_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"):
|
if file.endswith(".flac"):
|
||||||
process_chunk(file)
|
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__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
113
process_chunks_.py
Executable file
113
process_chunks_.py
Executable file
|
@ -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()
|
Loading…
Reference in a new issue