199f762616
- PipeWire/pw-record backend (no PortAudio needed) - tested mic live - VAD: Energy (immediate) + Silero (torch CPU) switchable - Wakeword: openWakeWord TFLite hey_jarvis 0.4 - STT: faster-whisper base int8 CPU - model loads OK on i3 - SpeakerID: ECAPA embeddings + fallback stats, enrollment store - Vision: OpenCV Haar + face_recognition + webcam cam0 640x480 verified - LLM: ollama/qwen2.5:3b, gemini_cli, hermes_api, echo strategies - TTS: Piper + espeak + echo fallbacks - Core pipeline: IDLE -> WAKE -> RECORDING -> STT -> SpeakerID/FaceID fusion -> LLM -> TTS -> followup window - Config via jarvis.yaml, soul.md persona - CLI tools: run, devices, enroll-speaker, enroll-face, test-vad, test-mic - Systemd user service ready - Tested on iMac 2010 i3 550 15GB Ubuntu 26.04 CPU-only
26 lines
1.0 KiB
Python
26 lines
1.0 KiB
Python
import cv2, numpy as np, logging, os
|
|
from typing import List, Tuple
|
|
log = logging.getLogger(__name__)
|
|
class FaceDetector:
|
|
def __init__(self):
|
|
self.cascade=None
|
|
self._load()
|
|
def _load(self):
|
|
try:
|
|
cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
|
|
if os.path.exists(cascade_path):
|
|
self.cascade = cv2.CascadeClassifier(cascade_path)
|
|
log.info("Haar face detector loaded")
|
|
except Exception as e:
|
|
log.warning(f"Face detector load failed: {e}")
|
|
def detect(self, frame: np.ndarray) -> List[Tuple[int,int,int,int]]:
|
|
if self.cascade is None:
|
|
return []
|
|
try:
|
|
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
|
faces = self.cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(80,80))
|
|
return [(int(x),int(y),int(w),int(h)) for (x,y,w,h) in faces]
|
|
except Exception as e:
|
|
log.debug(f"Detect error: {e}")
|
|
return []
|