From 832a765575f9782d7bbfd2dc5e86d340a59bbe50 Mon Sep 17 00:00:00 2001 From: TimoKurz Date: Sat, 7 Feb 2026 11:27:40 +0100 Subject: [PATCH] - added implementation to create 50sec video files out of camera lifestream for every 5 seconds - added action unit recognition to the camera stream --- .../AU_creation/AU_creation_service.py | 18 +-- .../camera_handling/camera_stream.py | 132 ++++++++++++++++++ 2 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 dataset_creation/camera_handling/camera_stream.py diff --git a/dataset_creation/AU_creation/AU_creation_service.py b/dataset_creation/AU_creation/AU_creation_service.py index 1ed382d..d68c198 100644 --- a/dataset_creation/AU_creation/AU_creation_service.py +++ b/dataset_creation/AU_creation/AU_creation_service.py @@ -3,11 +3,11 @@ from feat.utils.io import get_test_data_path from moviepy.video.io.VideoFileClip import VideoFileClip import os -def extract_aus(path, model): +def extract_aus(path, model, skip_frames): detector = Detector(au_model=model) video_prediction = detector.detect( - path, data_type="video", skip_frames=24*5, face_detection_threshold=0.95 # alle 5 Sekunden einbeziehen - 24 Frames pro Sekunde + path, data_type="video", skip_frames=skip_frames, face_detection_threshold=0.95 # alle 5 Sekunden einbeziehen - 24 Frames pro Sekunde ) return video_prediction.aus.sum() @@ -38,13 +38,13 @@ def split_video(path, chunk_length=120): return output_path -def start(path): - results = [] - clips = split_video(path) +# def start(path): +# results = [] +# clips = split_video(path) - for clip in clips: - results.append(extract_aus(clip, 'svm')) - return results +# for clip in clips: +# results.append(extract_aus(clip, 'svm', 25*5)) +# return results if __name__ == "__main__": results = [] @@ -53,6 +53,6 @@ if __name__ == "__main__": clips = split_video(test_video_path) for clippath in clips: - results.append(extract_aus(clippath, 'svm')) + results.append(extract_aus(clippath, 'svm', 25*5)) print(results) \ No newline at end of file diff --git a/dataset_creation/camera_handling/camera_stream.py b/dataset_creation/camera_handling/camera_stream.py new file mode 100644 index 0000000..d2416a0 --- /dev/null +++ b/dataset_creation/camera_handling/camera_stream.py @@ -0,0 +1,132 @@ +import cv2 +import time +import os +import threading +from datetime import datetime +from feat import Detector +import torch + +# Konfiguration +CAMERA_INDEX = 0 +OUTPUT_DIR = "recordings" +VIDEO_DURATION = 50 # Sekunden +START_INTERVAL = 5 # Sekunden bis zum nächsten Start +FPS = 25.0 # Feste FPS + +if not os.path.exists(OUTPUT_DIR): + os.makedirs(OUTPUT_DIR) + +# Globaler Detector, um ihn nicht bei jedem Video neu laden zu müssen (spart massiv Zeit/Speicher) +print("Initialisiere AU-Detector (bitte warten)...") +detector = Detector(au_model="xgb") + +def extract_aus(path, skip_frames): + + # torch.no_grad() deaktiviert die Gradientenberechnung. + # Das löst den "Can't call numpy() on Tensor that requires grad" Fehler. + with torch.no_grad(): + video_prediction = detector.detect_video( + path, + skip_frames=skip_frames, + face_detection_threshold=0.95 + ) + + # Falls video_prediction oder .aus noch Tensoren sind, + # stellen wir sicher, dass sie korrekt summiert werden. + try: + # Wir nehmen die Summe der Action Units über alle detektierten Frames + res = video_prediction.aus.sum() + return res + except Exception as e: + print(f"Fehler bei der Summenbildung: {e}") + return 0 + +def startAU_creation(video_path): + """Diese Funktion läuft nun in einem eigenen Thread.""" + try: + print(f"\n[THREAD START] Analyse läuft für: {video_path}") + # skip_frames berechnen (z.B. alle 5 Sekunden bei 25 FPS = 125) + output = extract_aus(video_path, skip_frames=int(FPS*5)) + + print(f"\n--- Ergebnis für {os.path.basename(video_path)} ---") + print(output) + print("--------------------------------------------------\n") + except Exception as e: + print(f"Fehler bei der Analyse von {video_path}: {e}") + +class VideoRecorder: + def __init__(self, filename, width, height): + self.filename = filename + fourcc = cv2.VideoWriter_fourcc(*'XVID') + self.out = cv2.VideoWriter(filename, fourcc, FPS, (width, height)) + self.frames_to_record = int(VIDEO_DURATION * FPS) + self.frames_count = 0 + self.is_finished = False + + def write_frame(self, frame): + if self.frames_count < self.frames_to_record: + self.out.write(frame) + self.frames_count += 1 + else: + self.finish() + + def finish(self): + if not self.is_finished: + self.out.release() + self.is_finished = True + abs_path = os.path.abspath(self.filename) + print(f"Video fertig gespeichert: {self.filename}") + + # --- MULTITHREADING HIER --- + # Wir starten die Analyse in einem neuen Thread, damit main() sofort weiter frames lesen kann + analysis_thread = threading.Thread(target=startAU_creation, args=(abs_path,)) + analysis_thread.daemon = True # Beendet sich, wenn das Hauptprogramm schließt + analysis_thread.start() + +def main(): + cap = cv2.VideoCapture(CAMERA_INDEX) + if not cap.isOpened(): + print("Fehler: Kamera konnte nicht geöffnet werden.") + return + + width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + + active_recorders = [] + last_start_time = 0 + + print("Aufnahme läuft. Drücke 'q' zum Beenden.") + + try: + while True: + ret, frame = cap.read() + if not ret: + break + + current_time = time.time() + + if current_time - last_start_time >= START_INTERVAL: + timestamp = datetime.now().strftime("%H%M%S") + filename = os.path.join(OUTPUT_DIR, f"rec_{timestamp}.avi") + new_recorder = VideoRecorder(filename, width, height) + active_recorders.append(new_recorder) + last_start_time = current_time + + for rec in active_recorders[:]: + rec.write_frame(frame) + if rec.is_finished: + active_recorders.remove(rec) + + cv2.imshow('Kamera Livestream', frame) + if cv2.waitKey(1) & 0xFF == ord('q'): + break + + time.sleep(1/FPS) + + finally: + cap.release() + cv2.destroyAllWindows() + print("Programm beendet. Warte ggf. auf laufende Analysen...") + +if __name__ == "__main__": + main() \ No newline at end of file