Einführung
Wir befassen uns primär mit Gesichtserkennung im Sinne von «in einem Bild ein Gesicht finden». Im Deutschen ist der Begriff nicht ganz eindeutig – man kann ja auch dank des Gesichts die Person erkennen.
Im Englischen gibt es für beides eindeutige Begriffe:
Anwendungen
Gesichtserkennung (face detection) wird vielerorts seit vielen Jahren eingesetzt. Einige Beispiele wären:
Kamera Autofokus
Die Kamera stellt automatisch auf das erkannte Gesicht scharf
Personen erkennen
Bevor das Gesicht einer Person zugeordnet werden kann, muss es im Bild lokalisiert werden.
Filter à la Snapchat
3D-Objekte (z.B. Sonnebrille) oder veränderte Aufnahmen (Make-Up) werden über das Gesicht gelagert
Überwachung
Z.B. ungewöhnliche Vorgänge in grossen Menschenmengen erkennen.
MediaPipe
Google bietet mit MediaPipe eine Plattform-übergreifende Lösung für Gesichts-Erkennung und anderes. Das Paket muss nicht trainiert werden – es werden trainierte Modelle für gängige Probleme mitgeliefert.
Für Python bestehen Lösungen für die folgenden Aufgaben:
👉 https://google.github.io/mediapipe/getting_started/python.html
Anleitung
Wir analysieren das von Google veröffentliche Python-Skript für die Gesichtserkennung mit mediapipe.
👉 https://google.github.io/mediapipe/solutions/face_detection#python-solution-api
Installation
In Thonny können die Python-Packages einfach installiert werden:
- gehe auf Tools->manage packages…
- suche nach
mediapipe
- installiere das Package
Wenn die Installation nicht klappt, könnte der verwendete Python-Interpreter ein Grund sein. Sofern ein Python im System verfügbar ist, kann man in Thonny dieses Executable auswählen. Es wird Python 3.9 oder 3.10 vorausgesetzt.
Am Besten wählt man in Thonny unter Tools->Open system shell… und installiert dort mediapipe mit pip3 install mediapipe
.
Vorbereitung
Wir laden cv2 und mediapipe. cv2 ist ein weit verbreitetes Python-Paket für computer vision von OpenCV.
Anschliessend machen wir uns zwei Variablen. Eine zur Gesichtserkennungs-Lösung von mediapipe und eine zu den Zeichnungs-Tools, damit wir im Bild das erkannte Gesicht markieren können.
import cv2
import mediapipe as mp
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
Bild-Quellen
Als Quelle für die Bilder können klassische Bilddateien dienen, man kann aber auch gleich den Input der Webcam weiterverarbeiten. In beiden Fällen (Liste von Dateien, Webcam-Input) werden die Bilder in einer Schleife abgearbeitet:
Liste von Dateien
Wir gehen die Liste der Bilddateien in einer for-Schleife durch. Wir lesen die einzelnen Dateien und verwandeln diese vom Farbmodell BGR (so liest cv2) nach RGB.
IMAGE_FILES = ["selfie.jpg"]
for file in IMAGE_FILES:
image = cv2.imread(file)
converted = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
Webcam-Input
Wir öffnen die erste Webcam des Systems. Anschliessend holen wir in der while-Schleife einzelne Bilder und bereiten diese vor.
cap = cv2.VideoCapture(0)
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
continue
converted = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
Gesichter erkennen
Nun wird ein neues Objekt für die Gesichtserkennung erzeugt. Dem Konstruktor von FaceDetection
können diverse Argumente mitgeliefert werden.
with mp_face_detection.FaceDetection(model_selection=1, min_detection_confidence=0.5) as face_detection:
results = face_detection.process(image)
Wenn wir dann ein image
haben, können wir darauf die Erkennung anwenden:
Ausgabe sichtbar machen
Als Bilddatei speichern
Falls nichts erkannt wurde soll nichts getan werden. Sonst wird das eingelesene Bild kopiert und für jedes erkannte Gesicht werden die Koordinaten der Nasenspitze in der Konsole ausgegeben und die Erkennung mit der vordefinierten Funktion draw_detection
eingezeichnet.
if not results.detections:
continue
annotated_image = image.copy()
for detection in results.detections:
print(mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.NOSE_TIP))
mp_drawing.draw_detection(annotated_image, detection)
cv2.imwrite('output.png', annotated_image)
Schlussendlich wird das kopierte Bild mit den erkannten Gesichtern als Bilddatei abgespeichert.
Direkt anzeigen (Webcam)
Fürs Anzeigen mit cv2
wird das Bild wieder ins BGR-Format konvertiert. Anschliessend wird für jedes erkannte Gesicht eine Markierung ins Bild gezeichnet. Das Bild wird schlussendlich angezeigt.
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
if results.detections:
for detection in results.detections:
mp_drawing.draw_detection(image, detection)
cv2.imshow('MediaPipe Face Detection', image)
Kommentierte Beispiele
Ich habe das Google-Beispiel auseinandergenommen (Bilddatei/Webcam), leicht angepasst und korrigiert. Das Original-Beispiel findet man online.
👉 https://google.github.io/mediapipe/solutions/face_detection#python-solution-api
Aus Bilddatei
import cv2
import mediapipe as mp
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
# For static images:
IMAGE_FILES = ["selfie.jpg"]
with mp_face_detection.FaceDetection(model_selection=1, min_detection_confidence=0.5) as face_detection:
for file in IMAGE_FILES:
image = cv2.imread(file)
# Convert the BGR image to RGB and process it with MediaPipe Face Detection.
converted = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = face_detection.process(converted)
# Draw face detections of each face.
if not results.detections:
continue
annotated_image = image.copy()
for detection in results.detections:
print('Nose tip:')
print(mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.NOSE_TIP))
mp_drawing.draw_detection(annotated_image, detection)
cv2.imwrite(file.split(".")[0] + '_output.png', annotated_image)
Von Webcam
import cv2
import mediapipe as mp
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
# For webcam input:
cap = cv2.VideoCapture(0)
with mp_face_detection.FaceDetection(model_selection=0, min_detection_confidence=0.5) as face_detection:
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
# If loading a video, use 'break' instead of 'continue'.
continue
# To improve performance, optionally mark the image as not writeable to
# pass by reference.
image.flags.writeable = False
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = face_detection.process(image)
# Draw the face detection annotations on the image.
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
if results.detections:
for detection in results.detections:
mp_drawing.draw_detection(image, detection)
# Flip the image horizontally for a selfie-view display.
cv2.imshow('MediaPipe Face Detection', cv2.flip(image, 1))
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
Python Solution
Das Beispiel verwendet diverse Funktionen der sogenannten Python-Solution zur Gesichtserkennung mit mediapipe. Da der Code dazu öffentlich auf github liegt, können wir diese Skripte analysieren und weitere hilfreiche Informationen gewinnen.
👉 https://github.com/google/mediapipe/tree/master/mediapipe/python/solutions
Keypoints
Im Skript face_detection.py
finden wir ein Enum
der keypoints:
class FaceKeyPoint(enum.IntEnum):
"""The enum type of the six face detection key points."""
RIGHT_EYE = 0
LEFT_EYE = 1
NOSE_TIP = 2
MOUTH_CENTER = 3
RIGHT_EAR_TRAGION = 4
LEFT_EAR_TRAGION = 5
Im ersten Beispiel geben wir bereits die Koordinaten der Nasenspitze aus:
mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.NOSE_TIP)
Analog können wir auf die anderen keypoints zugreifen.
mit OpenCV zeichnen
Die im Beispiel verwendete Funktion draw_detection
zeichnet einen Rahmen und die erkannten keypoints. Möchte man etwas anderes Zeichnen, so muss man das selbst tun.
Wir finden die Funktion im Skript drawing_utils.py
. Gezeichnet wird mit den Zeichenbefehlen von OpenCV, doch zuerst müssen die relativen Koordinaten der keypoints mit einem Dreisatz in Pixel-Koordinaten konvertiert werden.
Hier eine leicht vereinfachte Variante der im Skript gefundenen Funktion _normalized_to_pixel_coordinates()
:
def to_pixel(normalized_x, normalized_y, image):
"""Converts normalized value pair to pixel coordinates."""
image_height, image_width, _ = image.shape
x_px = min(math.floor(normalized_x * image_width), image_width - 1)
y_px = min(math.floor(normalized_y * image_height), image_height - 1)
return x_px, y_px
keypoint = {"x": 0.3, "y": 0.93} # fiktiver keypoint erzeugen
pixel = to_pixel(keypoint.x, keypoint.y, image_cols, image)
Die Funktion erhält als Argumente zwei relative Koordinaten und das Bild (wegen der Bildgrösse). Zurückgeliefert wird das Tupel der errechneten Pixel-Koordinaten, welches direkt in den OpenCV-Zeichenfunktionen verwendet werden kann:
cv2.circle(image, keypoint, 20, [255,0,0], 2)
Die Zeichenbefehle von OpenCV findet man hier:
👉 https://docs.opencv.org/4.x/dc/da5/tutorial_py_drawing_functions.html
OpenCV weiteres
Bildausschnitt
Man kann einen rechteckigen Ausschnitt des Bildes weiterbearbeiten. OpenCV behandelt Bilder wie zwei-Dimensionale Listen – man wählt einfach eine Unterliste (y-Koordinate zuerst):
👉 https://docs.opencv.org/4.x/d3/df2/tutorial_py_basic_ops.html
Bildbearbeitung
Z.B. können Bilder geglättet werden:
image = cv2.blur(image,(10,10))
👉 https://docs.opencv.org/4.x/d2/d96/tutorial_py_table_of_contents_imgproc.html
Aufgaben
Aufgabe: Installation
Installiere mediapipe in Thonny
Aufgabe: Bilddatei
Teste das obenstehende Beispiel mit dem folgenden Bild.
- wie viele Gesichter werden erkannt?
- verändere den Wert
min_detection_confidence
im Konstruktor. Kannst du die Erkennung verbessern?
Aufgabe: Webcam
Teste das Beispiel mit der Webcam.
Projekt-Ideen
Zensur
Gesicht erkennen und unkenntlich machen (verschwommen, schwarzer Balken, …)
Sonnebrille/Hut
Gesicht erkennen und eine Sonnenbrille oder einen Hut hinzeichnen
eine andere Anwendung
Etwas mit face mesh oder hand tracking machen.
Quellen
👉 https://learnopencv.com/what-is-face-detection-the-ultimate-guide/
👉 https://google.github.io/mediapipe/solutions/face_detection.html