Gesichtserkennung in Echtzeit mit YuNet und CLI-Automatisierung
YuNet ist ein kompakter Gesichtsdetektor, der über OpenCV verfügbar ist. Ein kleines CLI kann ihn nutzen, um lokale Bilder und Videos zu annotieren, mit vorhersehbaren Batch-Ausgaben und geprüften Fehlern. Die Erkennung findet gesichtsähnliche Regionen; sie identifiziert keine Personen und liefert keine Authentifizierungsentscheidung.
YuNets Stärken verstehen
YuNet bietet eine leichtgewichtige, CPU-orientierte Option, um Gesichter und Gesichtsmerkmale zu erkennen. Genauigkeit und Geschwindigkeit hängen von Bildgröße, Gesichtsgröße, Pose, Beleuchtung, Schwellenwert und Hardware ab. Angaben zum Modell und zu seiner veröffentlichten Evaluierung finden Sie in der Modelldokumentation des OpenCV Zoo; messen Sie Ihre eigene Arbeitslast, bevor Sie Garantien zu Echtzeitfähigkeit oder Genauigkeit geben.
YuNet schnell einrichten
Erstellen Sie eine Python-Umgebung und installieren Sie ein OpenCV-Wheel. Dieses CLI öffnet keine
GUI-Fenster und verwendet daher das Headless-Paket; installieren Sie nicht zusätzlich
opencv-python in dieselbe Umgebung.
python3 -m venv venv
. venv/bin/activate
pip install opencv-python-headless==5.0.0.93
Laden Sie die getesteten Modell-Bytes herunter, nicht einen Git-LFS-Zeiger. Dieser Befehl fixiert die Revision des OpenCV Zoo:
curl --fail --location --output face_detection_yunet.onnx \
https://media.githubusercontent.com/media/opencv/opencv_zoo/47534e27c9851bb1128ccc0102f1145e27f23f98/models/face_detection_yunet/face_detection_yunet_2023mar.onnx
Das Modell ist 232.589 Bytes groß und hat den SHA-256-Wert
8f2383e4dd3cfbb4553ea8718107fc0423210dc964f9f4280604804ed2552fa4. Verifizieren Sie es unter
Linux mit sha256sum oder unter macOS mit shasum -a 256. Bewahren Sie das Modell neben den
Skripten auf. Verwenden Sie für das fixierte Wheel eine kompatible, unterstützte Python-Version und
halten Sie die resultierende Umgebung reproduzierbar.
Erkennung für Einzelbilder automatisieren
Speichern Sie diese gemeinsame Implementierung als face_tools.py. Sie verwendet für die Inferenz ein
tatsächlich in der Größe angepasstes Frame und rechnet die Begrenzungsrahmen auf das Originalbild
zurück, anstatt die Größeneinstellung des Detektors zu ändern, ohne dessen Eingabe anzupassen.
from contextlib import contextmanager
import math
import os
from pathlib import Path
from tempfile import TemporaryDirectory
# Set before importing OpenCV so image decoding has an explicit pixel ceiling.
os.environ.setdefault('OPENCV_IO_MAX_IMAGE_PIXELS', '20000000')
import cv2
MODEL_PATH = Path(__file__).with_name('face_detection_yunet.onnx')
def create_detector():
return cv2.FaceDetectorYN.create(str(MODEL_PATH), '', (320, 320), 0.9, 0.3, 5000)
def annotate(detector, image, max_side=960):
height, width = image.shape[:2]
if width * height > 20_000_000:
raise ValueError('Frame exceeds the pixel limit')
scale = min(1.0, max_side / max(width, height))
small = cv2.resize(image, (max(1, round(width * scale)), max(1, round(height * scale))))
detector.setInputSize((small.shape[1], small.shape[0]))
_, faces = detector.detect(small)
count = 0 if faces is None else len(faces)
if faces is not None:
scale_x = width / small.shape[1]
scale_y = height / small.shape[0]
for face in faces:
x, y, w, h = face[:4]
left, top = max(0, round(x * scale_x)), max(0, round(y * scale_y))
right = min(width - 1, round((x + w) * scale_x))
bottom = min(height - 1, round((y + h) * scale_y))
cv2.rectangle(image, (left, top), (right, bottom), (0, 255, 0), 2)
return image, count
@contextmanager
def new_output(path):
output = Path(path).absolute()
if output.exists() or output.is_symlink():
raise ValueError('Output must not exist')
with TemporaryDirectory(prefix='.face-output-', dir=output.parent) as temporary:
candidate = Path(temporary) / ('result' + output.suffix)
yield candidate
if not candidate.is_file() or candidate.stat().st_size == 0:
raise ValueError('No nonempty output produced')
os.link(candidate, output)
def detect_image(detector, source, output):
source = Path(source)
if not source.is_file() or source.stat().st_size > 50 * 1024 * 1024:
raise ValueError('Use a regular image file no larger than 50 MiB')
image = cv2.imread(str(source))
if image is None:
raise ValueError('Cannot decode image')
annotated, count = annotate(detector, image)
with new_output(output) as candidate:
if not cv2.imwrite(str(candidate), annotated):
raise ValueError('Cannot encode output image')
return count
def detect_video(detector, source, output):
if not Path(source).is_file():
raise ValueError('Use a local video file')
capture = cv2.VideoCapture(str(Path(source).absolute()))
writer = None
frames = 0
detections = 0
try:
if not capture.isOpened():
raise ValueError('Cannot open video')
fps = capture.get(cv2.CAP_PROP_FPS)
width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
if not math.isfinite(fps) or fps <= 0 or width <= 0 or height <= 0:
raise ValueError('Invalid video timing or dimensions')
if width * height > 20_000_000 or width % 2 or height % 2:
raise ValueError('Use bounded, even video dimensions')
with new_output(output) as candidate:
writer = cv2.VideoWriter(str(candidate), cv2.VideoWriter_fourcc(*'mp4v'), fps, (width, height))
if not writer.isOpened():
raise ValueError('Cannot open video encoder')
while True:
ok, frame = capture.read()
if not ok:
break
if frame.shape[:2] != (height, width):
raise ValueError('Frame dimensions changed')
annotated, count = annotate(detector, frame)
writer.write(annotated)
frames += 1
detections += count
writer.release()
writer = None
if frames == 0:
raise ValueError('No frames decoded')
return frames, detections
finally:
capture.release()
if writer is not None:
writer.release()
Speichern Sie diesen Aufrufer als detect_faces.py im selben Verzeichnis:
import argparse
import sys
from face_tools import create_detector, detect_image, detect_video
import cv2
parser = argparse.ArgumentParser()
parser.add_argument('mode', choices=['image', 'video'])
parser.add_argument('input')
parser.add_argument('output')
args = parser.parse_args()
try:
detector = create_detector()
if args.mode == 'image':
print(f'Detected {detect_image(detector, args.input, args.output)} faces')
else:
frames, detections = detect_video(detector, args.input, args.output)
print(f'Processed {frames} frames with {detections} face detections')
except (OSError, ValueError, cv2.error):
sys.exit('Face detection failed; check input, model, codec, and destination')
Führen Sie python detect_faces.py image photo.jpg annotated.jpg aus. Ein gültiges Bild ohne erkannte
Gesichter erzeugt weiterhin eine Ausgabe und meldet null Erkennungen. Das Dateisystem der Ausgabe
muss Hardlinks unterstützen; vorhandene Dateien werden bei der Veröffentlichung niemals ersetzt.
Ordner im Batch verarbeiten
Verwenden Sie denselben Detektor sequenziell erneut, anstatt eine nicht picklebare Lambda-Funktion an
einen Prozess-Pool zu senden oder jede Eingabe output.jpg überschreiben zu lassen. Speichern Sie dies als batch_detect.py:
import argparse
from pathlib import Path
import sys
from face_tools import create_detector, detect_image
import cv2
parser = argparse.ArgumentParser()
parser.add_argument('input_directory')
parser.add_argument('output_directory')
args = parser.parse_args()
try:
source = Path(args.input_directory)
images = sorted(path for path in source.iterdir() if path.is_file() and not path.is_symlink()
and path.suffix.lower() in {'.jpg', '.jpeg', '.png'})
if not images:
sys.exit('No supported images found')
detector = create_detector()
output = Path(args.output_directory)
output.mkdir(mode=0o700)
failures = 0
for image in images:
try:
count = detect_image(detector, image, output / (image.name + '.jpg'))
print(f'{image.name}: {count} faces')
except (OSError, ValueError, cv2.error):
failures += 1
print(f'Failed: {image.name}', file=sys.stderr)
sys.exit(1 if failures else 0)
except (OSError, ValueError, cv2.error):
sys.exit('Cannot prepare batch; check model and directories')
Führen Sie python batch_detect.py images new-results aus. Die vollständigen Quelldateinamen bleiben
erhalten, bevor .jpg angehängt wird, sodass photo.jpg und photo.png getrennte Ausgaben erhalten.
Fehlgeschlagene Dateien führen zu einem Batch-Status ungleich null; erfolgreiche Ergebnisse bleiben
verfügbar.
Videostreams verarbeiten
Führen Sie python detect_faces.py video input.mp4 annotated.mp4 für ein lokales Video mit konstanter
Bildrate aus. Die Ausgabe enthält nur annotiertes Video: Audio wird nicht kopiert. Die
Gesamtzahl der Erkennungen zählt Beobachtungen über Frames hinweg, nicht einzelne Personen. Die
Zeitsteuerung bei variabler Bildrate und das erneute Verbinden einer Live-Kamera erfordern einen
anderen Workflow.
Ein fehlgeschlagener Frame-Lesevorgang in OpenCV kann das Dateiende oder ein Decoder-Problem
bedeuten, und VideoWriter.write meldet keinen Erfolg pro Frame. Prüfen Sie die resultierende Anzahl der
Frames, die Dauer und die Wiedergabe, bevor Sie veröffentlichen. Das Beispiel prüft die
Initialisierung und eine nicht leere Ausgabe, nicht die vollständige Medienintegrität.
Für Echtzeitgeschwindigkeit optimieren
Reduzieren Sie max_side in annotate, um die Frames für die Inferenz tatsächlich zu verkleinern, und
messen Sie anschließend den Kompromiss bei kleinen und weit entfernten Gesichtern. Halten Sie einen
Detektor pro Worker-Prozess vor; teilen Sie ihn nicht über gleichzeitige Aufrufe hinweg. Ein
CPU-Wheel bietet nicht automatisch CUDA-Unterstützung. GPU-Beschleunigung erfordert einen
kompatiblen OpenCV-Build, passende Hardware und eine separat validierte Backend-Konfiguration.
Verwenden Sie für nicht vertrauenswürdige Medien isolierte Worker sowie Zeit-, Arbeitsspeicher- und Festplattenlimits für den gesamten Job. Die Byte- und Pixelprüfungen für Bilder begrenzen nicht jede Speicherzuweisung des Video-Decoders oder die gesamte Verarbeitungszeit.
Alternative Detektoren kurz vergleichen
| Detektor | Sinnvoller Ausgangspunkt | Wichtige Einschränkung |
|---|---|---|
| YuNet | Kompakte Gesichtserkennung über OpenCV | Genauigkeit hängt von Maßstab, Pose und Schwellenwert ab |
| OpenCV-DNN-Gesichtsmodelle | Ein anderes vortrainiertes Modell oder Backend | Genaues Modell und Vorverarbeitung verifizieren |
| Haar-Kaskaden | Legacy-Workflows für leichtgewichtige Erkennung | Oft weniger robust gegenüber Pose- und Beleuchtungsvariationen |
Vergleichen Sie Kandidaten anhand derselben repräsentativen Daten, anstatt qualitative Geschwindigkeitsaussagen als Benchmark zu behandeln.
Fazit
Eine gemeinsame Detektor-Hilfsfunktion hält Workflows für Bilder, Ordner und Videos konsistent, bewahrt dabei die Ausgaben und macht Fehler sichtbar. Verifizieren Sie das fixierte Modell und die tatsächlichen Annotationen und messen Sie anschließend die Performance und Genauigkeit, die Ihre Anwendung erfordert. Für verwaltete Vision-Workflows siehe Transloadits Dienst für Künstliche Intelligenz.
