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:

face detection
face recognition

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

face und eye detection Fujifilm X-T3

Personen erkennen

Bevor das Gesicht einer Person zugeordnet werden kann, muss es im Bild lokalisiert werden.

face recognition

Filter à la Snapchat

3D-Objekte (z.B. Sonnebrille) oder veränderte Aufnahmen (Make-Up) werden über das Gesicht gelagert

snapchat

Überwachung

Z.B. ungewöhnliche Vorgänge in grossen Menschenmengen erkennen.

crowd analysis

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
1
2
3
4

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)
1
2
3
4

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)
1
2
3
4
5
6
7
8
9

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)
1
2

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)
1
2
3
4
5
6
7
8
9
10

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)
1
2
3
4
5
6
7

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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

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
1
2
3
4
5
6
7
8

Im ersten Beispiel geben wir bereits die Koordinaten der Nasenspitze aus:

mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.NOSE_TIP)
1

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)
1
2
3
4
5
6
7
8
9

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)
1

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))
1

👉 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