Mich haben zuletzt vermehrt Anfragen erreicht, die GPIOs mit der Gesichtserkennung zu kombinieren. Zum Beispiel als Schaltung für die Garage oder das eine LED für eine bestimmte Zeit aktiviert ist. Zudem wäre eine Benachrichtung aufs Smartphone interessant. Diesen Anfragen möchte ich mit diesem Beitrag gerecht werden. Die Anforderung an mich selber waren, dass für jedes Gesicht, welches die Software im Frame erkennt, das Script eine Nachricht an das Smartphone schicken soll. Für jedes bekannte Gesicht soll zusätzlich die LED für 5 Sekunden laufen. Für die Smartphone Benachrichtigung habe ich Pushbullet verwendet. Pushbullet stellt eine App bereit und es stehen mehrere Python Module zur Verfügung. Ich habe die eigene von Pushbullet verwendet.
sudo pip3 install pushbullet.py
Für die Registrierung benötigt man einen Google oder Facebook Account.
Auf der Seite könnt ihr den API Key unter Access Tokens einsehen. Diesen Schlüssel übertragt ihr bei jeder Push Nachricht mit. Ich empfehle euch, die End-zu-End Verschlüsselung zu aktivieren. Ihr müsst ein einmaliges Passwort eintragen. Jedes Gerät das ihr bei Pushbullet registriert habt, benötigt diesen Schlüssel, um die Nachrichten zu entschlüsseln.
Für die Schaltung habe ich ein Steckbrett mit LED und Widerstand benutzt. Auf dem Pi ist der GPIO Pin 17 angeschlossen. Es ist euch selber überlassen, welchen Pin ihr nehmt, ihr müsst diesen nur vernünftig schalten können.
Damit hätten wir alles Organisatorische geregelt. Kommen wir nun also zum Code, der an die anderen Gesichtserkennungs-Beiträgen angelehnt ist. Ich habe hier zusätzlich Threads mit eingebaut, damit die Abarbeitung der Notifications oder die Schaltung der LED neben dem Main-Thread laufen können. Die Threads rufen jeweils Funktionen auf, daher besteht für die Notification, als auch für die LED Schaltung eine eigenständige Funktion, denen ich Argumente übergebe. Damit ein Thread nicht doppelt aufgerufen werden kann, habe ich mit if tgpio not in threading.enumerate(): eine Verriegelung eingebaut. threading.enumerate() enthält jede zurzeit aktive Thread-ID.
Funktion NotifyPushbullet:
Ein Foto wird zwar außerhalb der Funktion gemacht, aber immer dann, wenn das Script ein Gesicht im Frame entdeckt hat. Das Foto ist lokal gespeichert, bevor die Funktion NotifyPushbullet es verschickt. Ich lege diese Funktion danach noch 30 Sekunden „schlafen“, um eine weitere unnötige Notifications zu vermeiden.
Funktion FaceGPIO:
Die LED wird geschaltet, wenn ein bekanntes Gesicht im Frame ist. Sobald eines erkannt wurde, leuchtet die LED für 12 Sekunden, ehe sie sich selber wieder ausschaltet. Falls es bei den GPIOs zu Fehlern kommt, z.B. eine Doppelbelegung des Pins, löst eine Exception aus und gibt den Fehler im Terminal zurück.
Weiter unten findet ihr den Code. Ich habe einige Einträge kommentiert, die helfen sollen den Code einfacher zu verstehen.
# This program will print out the names of anyone it recognizes to the console. # To run this, you need a Raspberry Pi 2 (or greater) with face_recognition and # the picamera[array] module installed. # You can follow this installation instructions to get your RPi set up: # https://gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65 try: import face_recognition import picamera import numpy as np import os import datetime from pushbullet import Pushbullet import gpiozero from time import sleep import threading except: print("failed to load module") exit() # Initialize some variables tpush = None tgpio = None face_locations = [] face_encodings = [] image = [] known_face_encoding = [] known_faces = [] # Set some variables pb = Pushbullet('apikey') framerate = 15 def NotifyPushbullet(pb,file): #push notification push = pb.push_note("Face detected","Someone was detected") # push image with open(file, "rb") as pic: file_data = pb.upload_file(pic, "picture.jpg") push = pb.push_file(**file_data) # set NotifyPushbullet to sleep print("Push Notification sent") sleep(30) # Function for GPIO def FaceGPIO(timetosleep): try: #initialize LED led = gpiozero.LED(17) # switch LED on led.on() #set to sleep sleep(timetosleep) # switch LED off led.off() # if any error occurs call exception except gpiozero.GPIOZeroError as err: print("Error occured: {0}".format(err)) # Get a reference to the Raspberry Pi camera. # If this fails, make sure you have a camera connected to the RPi and that you # enabled your camera in raspi-config and rebooted first. try: camera = picamera.PiCamera() camera.resolution = (320, 240) camera.framerate = framerate output = np.empty((240, 320, 3), dtype=np.uint8) except: print("something went wrong while initialize camera") exit() # Load pictures and learn how to recognize it. print("Loading known face image(s)") #is loading all images in faces/ and create a stamp try: for faces,i in zip(os.listdir("faces"),range(len(os.listdir("faces")))): known_faces.append("faces/"+faces) image.append(face_recognition.load_image_file("faces/" + faces)) known_face_encoding.append(face_recognition.face_encodings(image[i])[0]) except: print("could not find known pictures") exit() while True: print("Capturing image.") # Grab a single frame of video from the RPi camera as a numpy array camera.capture(output, format="rgb") # Find all the faces and face encodings in the current frame of video face_locations = face_recognition.face_locations(output) print("Found {} faces in image.".format(len(face_locations))) face_encodings = face_recognition.face_encodings(output, face_locations) #print all active threads print(threading.enumerate()) # Loop over each face found in the frame to see if it's someone we know. for face_encoding in face_encodings: # make a picture camera.capture('/home/pi/image.jpg', use_video_port=True) # create a thread to push Notifications # check if such a thread already exists if tpush not in threading.enumerate(): #define thread tpush = threading.Thread(target=NotifyPushbullet,args=(pb,'/home/pi/image.jpg',)) #start thread tpush.start() else: # if a thread was started already and still is running print("Thread tpush already started: {0}".format(str(tpush))) for face, i in zip(os.listdir("faces"), range(len(known_faces))): if face_recognition.compare_faces([known_face_encoding[i]], face_encoding,0.6)[0]: #print found face all upper case print("<found: ".upper() + known_faces[i].upper() + ">") #create a thread to switch the LED on # check if such a thread already exists if tgpio not in threading.enumerate(): #define thread; call function FaceGPIO with argument tgpio = threading.Thread(target=FaceGPIO,args=(5,)) #start thread tgpio.start() else: # if a thread was started already and still is running print("Thread tgpio already started: {0}".format(str(tgpio)))