[PYTHON] Katze zurück mit OpenCV erkennen

Einführung

Wenn Sie sich auf Ihre Schreibtischarbeit konzentrieren, erhalten Sie möglicherweise eine Katze zurück, ohne es zu merken. Dies gilt umso mehr für die Fernarbeit, da keine Augen einer anderen Person vorhanden sind.

Also habe ich einen Mechanismus entwickelt, der Sie benachrichtigt, wenn sich Ihre Haltung verschlechtert!

Umgebung

Python 3.7.4 Webkamera (Logitech HD Webcam C615)

OpenCV

Installation

$ pip install opencv-python

Zuerst werden wir das Kamerabild aufnehmen und die Augen erkennen.

Das Folgende ist eine Referenz für die Objekterkennungsmethode in OpenCV.

"Gesichtserkennung mit Haar Cascades" http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html#face-detection

Da wir diesmal die Augen erkennen möchten, verwenden wir "haarcascade_eye.xml" als Klassifikator. Sie können es über den unten stehenden Link herunterladen.

https://github.com/opencv/opencv/tree/master/data/haarcascades

capture.py


import numpy as np
import cv2

#Kameragerätnummer
#Verfügbare Kameras sind ab 0 bestellt
DEVICE_ID = 0

#Klassifikator auswählen
cascade = cv2.CascadeClassifier('haarcascade_eye.xml')

#Nehmen Sie Kameramaterial auf
cap = cv2.VideoCapture(DEVICE_ID, cv2.CAP_DSHOW)

while cap.isOpened():
    #Holen Sie sich Rahmen
    ret, frame = cap.read()

    #In Graustufen konvertieren
    img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    #Auge erkennen
    eyes = cascade.detectMultiScale(img_gray, minSize=(30, 30))

    #Schließen Sie das erkannte Auge in ein Quadrat ein
    for (x, y, w, h) in eyes:
        color = (255, 0, 0)
        cv2.rectangle(img_gray, (x, y), (x+w, y+h), color, thickness=3)

    #Anzeige
    cv2.imshow('capture', img_gray)

    #Verlassen Sie die Schleife mit der ESC-Taste
    if cv2.waitKey(1) & 0xFF == 27:
        break

#Verarbeitung beenden
cv2.destroyAllWindows()
cap.release()

Zeile 25, "y", ist die erfasste Augenhöheninformation. Daher ist es in Ordnung, wenn Sie diesen Wert aufzeichnen und sehen, wie er fällt!

gleitender Durchschnitt

Nun, ich habe die Augenhöhe, aber es ist nicht so gut, diesen Wert zu verwenden, wie er ist. Es ist nicht so, dass es als Rücken einer Katze beurteilt wird, wenn man nur einen Moment nach unten schaut, also werde ich zu diesem Zweck den Durchschnittswert für einen bestimmten Zeitraum behandeln.

Hier kommt der "gleitende Durchschnitt" ins Spiel. Diejenigen, die Aktien und Devisen machen, sind vielleicht vertraut. Dieses Mal habe ich den einfachsten "__ einfachen gleitenden Durchschnitt __" unter den gleitenden Durchschnitten verwendet.

Simple Moving Average (SMA) ist ein ungewichteter einfacher Durchschnitt der letzten n Daten. Beispielsweise ist der einfache gleitende Durchschnitt der Schlusskurse für 10 Tage der Durchschnitt der Schlusskurse der letzten 10 Tage. Setzen Sie ihre Schlusskurse auf $ {\ displaystyle p_ {M}} p_ {{M}}, {\ displaystyle p_ {M-1}} p_ {{M-1}}, ..., {\ displaystyle p_ {M-9 }} p_ {{M-9}} $, dann lautet die Formel für den einfachen gleitenden Durchschnitt SMA (p, 10):

{\text{SMA}}{M}={p{M}+p_{M-1}+\cdots +p_{M-9} \over 10}


 > Um den einfachen gleitenden Durchschnitt des nächsten Tages zu ermitteln, fügen Sie einen neuen Schlusskurs hinzu und entfernen Sie den ältesten Schlusskurs. Mit anderen Worten, bei dieser Berechnung ist es nicht erforderlich, die Summe neu zu berechnen.

>```math
{\text{SMA}}_{{\mathrm  {today}}}={\text{SMA}}_{{\mathrm  {yesterday}}}-{p_{{M-n+1}} \over n}+{p_{{M+1}} \over n}

Der Punkt ist, dass Sie die letzten n Teile jedes Elements der Daten mitteln können.

Wenn die Differenz zwischen dem Anfang und dem Ende der Zeitreihendaten dieses einfachen gleitenden Durchschnitts größer als ein bestimmter Schwellenwert ist, kann dies als Katzenrückstand beurteilt werden.

Projektionskonvertierung

Die Augenhöheninformation "y", über die ich bisher gesprochen habe, ist nur die Positionsinformation der Pixel des Kamerabildes. Daher ist es notwendig, separat zu berechnen, wie viel cm die Änderung des Wertes von "y" in der realen Welt entspricht.

Daher werde ich überlegen, was als Projektionskonvertierung bezeichnet wird. Da das Bild von der Kamera zweidimensional ist, muss die dreidimensionale Welt bei der Visualisierung auf eine zweidimensionale Ebene projiziert werden. Betrachten Sie es diesmal als perspektivische Projektionskonvertierung.

Perspektivische Projektionstransformationen sind $ \ left (\ frac {x} {z}, \ frac {y} {z}, 0 \ right) $ für dreidimensionale Koordinaten $ (x, y, z) $. Konvertieren. Ich denke, es ist leicht zu verstehen, weil es der Intuition entspricht, dass "entfernte Dinge klein aussehen".

Daher ist die Beziehung zwischen dem Höhenunterschied des Kamerabildes $ \ Delta y_d $ (px) und dem Höhenunterschied des realen $ \ Delta y_v $ (cm) wie folgt.

\begin{equation}
  \frac{\Delta y_d}{f} = \frac{\Delta y_v}{z_v} \tag{1}
\end{equation}

$ z_v $ ist die Entfernung zwischen der Kamera und dem Objekt und $ f $ ist die Brennweite der Kamera. Angesichts der Ähnlichkeit von Dreiecken ist es einfach.

diagram-20200929_2.png

Da die Brennweite der Kamera je nach Modell der Kamera variiert, muss sie durch Kalibrierung ermittelt werden. Die Kamerakalibrierung wird auch von OpenCV bereitgestellt.

"Kamerakalibrierung" http://whitewell.sakura.ne.jp/OpenCV/py_tutorials/py_calib3d/py_calibration/py_calibration.html

Der Inhalt der durch Kalibrierung erhaltenen internen Parameter der Kamera ist wie folgt, und die Brennweite kann von hier aus ermittelt werden.

K = \left[
      \begin{array}{ccc}
        f & 0 & x_c \\
        0 & f & y_c \\
        0 & 0 & 1
      \end{array}
    \right]

$ x_c, y_c $ sind die Mittelpunkte der Projektionsebene.
Diese Kalibrierung ist jedoch etwas mühsam, da ich ein Bild des Schachbretts erstellen muss, das tatsächlich mit der Kamera aufgenommen wurde. .. .. Die von mir verwendete Webkamera (Logitech HD Webcam C615) kostet ungefähr 500 $ f $, also als Referenz.

Beurteilung der Katze zurück

Sobald Sie $ f $ erhalten, sind Sie fertig.

In den Zeitreihendaten des einfachen gleitenden Durchschnitts auf Augenhöhe beträgt der Unterschied zwischen Anfang und Ende $ \ Delta y_d $, und es ist nur erforderlich, nach Gleichung (1) zu urteilen. Der Abstand $ z_v $ von der Kamera zum Computer beträgt ca. 45 cm. Außerdem ist der Schwellenwert $ \ Delta y_v $ für den Unterschied in der Augenhöhe in der realen Welt auf 3 cm festgelegt.

Ganzer Code

Gezeichnet mit Pyplot zur Datenvisualisierung.

Außerdem verwenden Warnungen die Nachrichtenbox von Tkinter.

detect_posture.py


import numpy as np
from matplotlib import pyplot as plt
import cv2
import tkinter as tk
from tkinter import messagebox


WINDOW_NAME = "capture"     # Videcapute window name
CAP_FRAME_WIDTH = 640       # Videocapture width
CAP_FRAME_HEIGHT = 480      # Videocapture height
CAP_FRAME_FPS = 30          # Videocapture fps (depends on user camera)

DEVICE_ID = 0               # Web camera id

SMA_SEC = 10                        # SMA seconds
SMA_N = SMA_SEC * CAP_FRAME_FPS     # SMA n

PLOT_NUM = 20                   # Plot points number
PLOT_DELTA = 1/CAP_FRAME_FPS    # Step of X axis

Z = 45                  # (cm) Distance from PC to face 
D = 3                   # (cm) Limit of lowering eyes
F = 500                 # Focal length


def simple_moving_average(n, data):
    """ Return simple moving average """
    result = []
    for m in range(n-1, len(data)):
        total = sum([data[m-i] for i in range(n)])
        result.append(total/n)
    return result

def add_simple_moving_average(smas, n, data):
    """ Add simple moving average """
    total = sum([data[-1-i] for i in range(n)])
    smas.append(total/n)


if __name__ == '__main__':
    # Not show tkinter window
    root = tk.Tk()
    root.iconify()

    # Chose cascade
    cascade = cv2.CascadeClassifier("haarcascade_eye.xml")

    # Capture setup
    cap = cv2.VideoCapture(DEVICE_ID, cv2.CAP_DSHOW)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, CAP_FRAME_WIDTH)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, CAP_FRAME_HEIGHT)
    cap.set(cv2.CAP_PROP_FPS, CAP_FRAME_FPS)

    # Prepare windows
    cv2.namedWindow(WINDOW_NAME)

    # Time series data of eye height
    eye_heights = []
    sma_eye_heights = []

    # Plot setup
    ax = plt.subplot()
    graph_x = np.arange(0, PLOT_NUM*PLOT_DELTA, PLOT_DELTA)
    eye_y = [0] * PLOT_NUM
    sma_eye_y = [0] * PLOT_NUM
    eye_lines, = ax.plot(graph_x, eye_y, label="realtime")
    sma_eye_lines, = ax.plot(graph_x, sma_eye_y, label="SMA")
    ax.legend()

    while cap.isOpened():
        # Get a frame
        ret, frame = cap.read()

        # Convert image to gray scale
        img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        
        # Detect human eyes
        eyes = cascade.detectMultiScale(img_gray, minSize=(30, 30))

        # Mark on the detected eyes
        for (x, y, w, h) in eyes:
            color = (255, 0, 0)
            cv2.rectangle(img_gray, (x, y), (x+w, y+h), color, thickness=3)
        
        # Store eye heights
        if len(eyes) > 0:
            eye_average_height = CAP_FRAME_HEIGHT - sum([y for _, y, _, _ in eyes]) / len(eyes)
            eye_heights.append(eye_average_height)

            if len(eye_heights) == SMA_N:
                sma_eye_heights = simple_moving_average(SMA_N, eye_heights)
            elif len(eye_heights) > SMA_N:
                add_simple_moving_average(sma_eye_heights, SMA_N, eye_heights)
            

        # Detect bad posture
        if sma_eye_heights and (sma_eye_heights[0] - sma_eye_heights[-1] > F * D / Z):
            res = messagebox.showinfo("BAD POSTURE!", "Sit up straight!\nCorrect your posture, then click ok.")
            if res == "ok":
                # Initialize state, and restart from begening
                eye_heights = []
                sma_eye_heights = []
                graph_x = np.arange(0, PLOT_NUM*PLOT_DELTA, PLOT_DELTA)
                continue

        # Plot eye heights
        graph_x += PLOT_DELTA
        ax.set_xlim((graph_x.min(), graph_x.max()))
        ax.set_ylim(0, CAP_FRAME_HEIGHT)

        if len(eye_heights) >= PLOT_NUM:
            eye_y = eye_heights[-PLOT_NUM:]
            eye_lines.set_data(graph_x, eye_y)
            plt.pause(.001)
        
        if len(sma_eye_heights) >= PLOT_NUM:
            sma_eye_y = sma_eye_heights[-PLOT_NUM:]
            sma_eye_lines.set_data(graph_x, sma_eye_y)
            plt.pause(.001)

        
        # Show result
        cv2.imshow(WINDOW_NAME, img_gray)

        # Quit with ESC Key
        if cv2.waitKey(1) & 0xFF == 27:
            break
    
    # End processing
    cv2.destroyAllWindows()
    cap.release()

Referenz

https://cvml-expertguide.net/2019/08/17/term-camera-model/

Recommended Posts

Katze zurück mit OpenCV erkennen
Ich möchte Objekte mit OpenCV erkennen
Binarisierung mit OpenCV / Python
Ich habe versucht, mit OpenCV Bewegungen schnell zu erkennen
Sprites mit OpenCV drehen
Datenerweiterung mit openCV
Versuchen Sie, Fische mit Python + OpenCV2.4 (unvollendet) zu erkennen.
Einfache TopView mit OpenCV
Stolpern Sie mit opencv3 von Homebrew
Ermitteln Sie den Tragezustand der Maske mit OpenCV und Raspberry Pi
Gesichtserkennung mit OpenCV von Python
"Apple-Verarbeitung" mit OpenCV3 + Python3
Bildbearbeitung mit Python OpenCV
Kameraerfassung mit Python + OpenCV
[Python] Verwenden von OpenCV mit Python (Basic)
Binarisieren Sie Fotodaten mit OpenCV
Loop-Video-Laden mit opencv
Echtzeit-Kantenerkennung mit OpenCV
Gesichtserkennung mit Python + OpenCV
Holen Sie sich Bildfunktionen mit OpenCV
Gesichtserkennung / Schneiden mit OpenCV
Probieren Sie OpenCV mit Google Colaboratory aus
Erstellung eines Kaskadenklassifikators mit opencv
Verwenden von OpenCV mit Python @Mac
Bilderkennung mit Keras + OpenCV
Anime-Gesichtserkennung mit OpenCV
Leuchtendes Leben mit Python und OpenCV
Neuronales Netzwerk mit OpenCV 3 und Python 3
[Python] Verwenden von OpenCV mit Python (Bildtransformation)
[Python] Verwenden von OpenCV mit Python (Kantenerkennung)
Löschen Sie bestimmte Farben mit OpenCV + PySimpleGUI
Einführung von OpenCV in Mac mit Homebrew
Einfache Python + OpenCV-Programmierung mit Canopy
Erkennen Sie englische Grammatikfehler mit Link-Grammatik
Mit OpenCV die einfachsten Fehler finden
[OpenCV] Persönliche Identifikation mit Gesichtsfoto
Versuchen Sie die Gesichtserkennung mit Python + OpenCV
Erkennen Sie Videoobjekte mit der Video Intelligence-API
Sprites mit OpenCV # 2 drehen ~ Mastering cv2.warpAffine () ~
Schneiden Sie das Gesicht mit Python + OpenCV aus
Gesichtserkennung mit Kamera mit opencv3 + python2.7
OpenCV-Funktionserkennung mit Google Colaboratory
Suchen Sie mit opencv nach phasenidentischen zusammengesetzten Wörtern
Laden Sie das GIF-Bild mit Python + OpenCV
Katzenerkennung mit OpenCV (Modellverteilung)
Finden Sie Bildähnlichkeit mit Python + OpenCV
Versuchen Sie, das Bild mit opencv2 zu verwischen
Verwenden Sie OpenCV mit Python 3 in Window
Zeichnen Sie eine Illustration mit Python + OpenCV
Verfolgen Sie Baseballbälle mit Python + OpenCV
Graphbasierte Segmentierung mit Python + OpenCV
Objekterkennung mit openCV durch traincascade
Zeichnen Sie Figuren mit OpenCV und PIL
Ich habe versucht, das Gesicht mit OpenCV zu erkennen
Implementierte Inter-Frame-Differenzmethode mit OpenCV
Zeichnen Sie einen Pfeil (Vektor) mit opencv / python
Grundlegendes Studium von OpenCV mit Python
Gesichtserkennung mit Python + OpenCV (invariante Rotation)
Cv2.imshow funktioniert jetzt ordnungsgemäß mit OpenCV3 von Homebrew
Speichern Sie Videos Frame für Frame mit Python OpenCV
Versuchen Sie, die Kamera mit Pythons OpenCV zu verwenden