Personenzählung mit OpenCV, Python und Ubidots
Die digitale Bildverarbeitung (DIP) erlebt ein rasantes Wachstum, vor allem dank der zunehmenden Verfügbarkeit von Machine-Learning-Techniken , auf die Entwickler über die Cloud zugreifen können. Die Verarbeitung digitaler Bilder über die Cloud umgeht den Bedarf an dedizierter Hardware und macht DIP damit zur bevorzugten Lösung. Als kostengünstigste und vielseitigste Methode zur Bildverarbeitung findet DIP in unterschiedlichsten Bereichen Anwendung. Eine der häufigsten Anwendungen ist die Fußgängererkennung und -zählung – eine nützliche Kennzahl für Flughäfen, Bahnhöfe, Einzelhandelsgeschäfte, Stadien, öffentliche Veranstaltungen und Museen.
Herkömmliche, handelsübliche Personenzähler sind nicht nur teuer – die von ihnen generierten Daten sind oft an proprietäre Systeme gebunden, was die Möglichkeiten zur Datenextraktion und KPI-Optimierung einschränkt. Im Gegensatz dazu spart ein eingebettetes DIP mit eigener Kamera und eigenem SBC nicht nur Zeit und Geld, sondern ermöglicht auch die individuelle Anpassung der Anwendung an die relevanten KPIs und die Gewinnung von Erkenntnissen aus der Cloud, die sonst nicht möglich wären.
Die Nutzung der Cloud für Ihre DIP IoT Anwendung ermöglicht eine verbesserte Gesamtfunktionalität. Dank erweiterter Funktionen wie Visualisierungen , Berichten, Benachrichtigungen und dem Abruf externer Datenquellen (z. B. Wetterdaten, aktuelle Preise von Anbietern oder Geschäftsprozessmanagementsysteme) bietet DIP Entwicklern die gewünschte Freiheit.
Stellen Sie sich einen Lebensmittelhändler mit einer Kühltheke für Eiscreme vor: Er möchte erfassen, wie viele Kunden vorbeigehen und ein Produkt auswählen, wie oft die Tür geöffnet wurde und wie hoch die Innentemperatur der Kühltheke ist. Allein anhand dieser wenigen Datenpunkte kann der Händler eine Korrelationsanalyse durchführen, um seine Produktpreise und den Gesamtenergieverbrauch der Kühltheke besser zu verstehen und zu optimieren.
Um Ihnen den Einstieg in die digitale Bildverarbeitung zu erleichtern, Ubidots das folgende Tutorial zur Personenzählung mit OpenCV und Python erstellt. Damit können Sie die Anzahl der Personen in einem bestimmten Bereich analysieren . Erweitern Sie Ihre Anwendungen über die reine Personenzählung hinaus mit den zusätzlichen Ressourcen der Ubidots IoT Entwicklungsplattform. Hier sehen Sie ein dashboard zur Personenzählung, das mit Ubidots .
OpenCV und Ubidots ein einfaches DIP-Overlay zur Erstellung eines Personenzählers implementieren . Dieses Beispiel eignet sich optimal für alle Linux-basierten Distributionen und läuft auch auf Raspberry Pi, Orange Pi oder ähnlichen eingebetteten Systemen.
Bei weiteren Fragen zur Integration wenden Sie sich bitte an Ubidots -Support und erfahren Sie, wie Ihr Unternehmen von dieser wertschöpfenden Technologie profitieren kann.
Inhaltsverzeichnis:
- Bewerbungsvoraussetzungen
- Codierung – 8 Unterabschnitte
- Testen
- Erstellen Ihres Dashboard
- Ergebnisse
1) Bewerbungsvoraussetzungen
- Jedes Embedded-Linux-System mit einer abgeleiteten Version von Ubuntu
- Python 3 oder neuer muss auf Ihrem Betriebssystem installiert sein.
- OpenCV 3.0 oder höher muss auf Ihrem Betriebssystem installiert sein. Wenn Sie Ubuntu oder eine davon abgeleitete Version verwenden, folgen Sie der offiziellen Installationsanleitung oder führen Sie den unten stehenden Befehl aus:
pip install opencv-contrib-python
- Sobald Sie Python 3 und OpenCV erfolgreich installiert haben, überprüfen Sie Ihre Konfiguration, indem Sie diesen kleinen Code ausführen (geben Sie einfach 'python' in Ihrem Terminal ein):
import cv2 cv2.__version__
Sie sollten einen Screenshot Ihrer OpenCV-Version erstellen:
- Installieren Sie Numpy, indem Sie den offiziellen Anweisungen folgen oder einfach den unten stehenden Befehl ausführen:
pip install numpy
- Installieren Sie imutils
pip install imutils
- Installationsanfragen
pip install requests
2) Codierung
Die gesamte Routine zur Datenerkennung und -übertragung finden Sie hier . Um unseren Code besser zu erläutern, unterteilen wir ihn in acht Abschnitte, um Ihnen das Verständnis zu erleichtern.
Abschnitt 1:
from imutils.object_detection import non_max_suppression import numpy as np import imutils import cv2 import requests import time import argparse URL_EDUCATIONAL = "ubidots" URL_INDUSTRIAL = "ubidots" INDUSTRIAL_USER = True # Auf False setzen, wenn Sie ein Bildungsnutzer sind TOKEN = "...." # Hier Ihr Ubidots -Token einfügen DEVICE = "detector" # Gerät, auf dem das Ergebnis gespeichert wird VARIABLE = "people" # Variable, in der das Ergebnis gespeichert wird # OpenCV-vortrainierte SVM mit HOG-Personenmerkmalen HOGCV = cv2.HOGDescriptor() HOGCV.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())In Abschnitt 1 importieren wir die notwendigen Bibliotheken zur Implementierung unseres Detektors. imutils ist ein nützliches Bibliothekstool für DIP und ermöglicht uns, verschiedene Transformationen an unseren Ergebnissen durchzuführen. cv2 ist unser OpenCV Python-Wrapper. requests Ubidots zu senden , und argparse ermöglicht es uns, Befehle aus unserem Befehlsterminal innerhalb unseres Skripts zu lesen.
WICHTIG: Vergessen Sie nicht, diesen Code mit Ihrem Ubidots -Konto-Token , und wenn Sie ein Benutzer im Bildungsbereich , stellen Sie sicher, dass INDUSTRIAL_USER auf FALSE .
Nach dem Import der Bibliothek initialisieren wir unseren Histogramm-orientierten Objektdeskriptor (HOG). HOG ist eine der beliebtesten Techniken zur Objekterkennung und wurde bereits erfolgreich in verschiedenen Anwendungen eingesetzt. Glücklicherweise bietet OpenCV bereits eine effiziente Möglichkeit, den HOG-Algorithmus mit einer Support-Vektor-Maschine (SVM) zu kombinieren – einer klassischen Methode des maschinellen Lernens für Vorhersagezwecke.
Diese Deklaration: cv2.HOGDescriptor_getDefaultPeopleDetector() ruft das vortrainierte Modell zur Personenerkennung von OpenCV auf und dient als Grundlage für unsere Support-Vector-Machine-Feature-Evaluierungsfunktion.
Abschnitt 2
def detector(image): ''' @image ist ein NumPy-Array ''' image = imutils.resize(image, width=min(400, image.shape[1])) clone = image.copy() (rects, weights) = HOGCV.detectMultiScale(image, winStride=(8, 8), padding=(32, 32), scale=1.05) # Wendet Non-Max-Suppression aus dem imutils-Paket an, um überlappende # Boxen zu entfernen rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects]) result = non_max_suppression(rects, probs=None, overlapThresh=0.65) return result
Die `detector()` ist der Kern der Sache. Sie erhält ein RGB-Bild, das in drei Farbkanäle aufgeteilt ist. Um Performance-Probleme zu vermeiden, skalieren wir das Bild mit `imutils` und rufen anschließend die detectMultiScale()` unseres HOG-Objekts auf. Mit dieser Methode können wir das Bild analysieren und anhand des Klassifizierungsergebnisses unserer SVM feststellen, ob eine Person darauf zu sehen ist. Die Parameter dieser Methode werden in diesem Tutorial nicht behandelt. Weitere Informationen finden Sie in der offiziellen OpenCV-Dokumentation oder in Adrian Rosebrocks hervorragender Erklärung .
Die HOG-Analyse erzeugt einige Erfassungsrahmen (erkannte Objekte), die sich jedoch manchmal überlappen und dadurch zu Fehlalarmen oder Erkennungsfehlern führen. Um diese Probleme zu beheben, verwenden wir das Hilfsprogramm zur Unterdrückung von Nicht-Maxima aus der imutils , um die überlappenden Rahmen zu entfernen – wie unten dargestellt:
Bilder reproduziert von https://www.pyimagesearch.com
Abschnitt 3:
def localDetect(image_path): result = [] image = cv2.imread(image_path) if len(image) <= 0: print("[ERROR] konnte Ihr lokales Bild nicht lesen") return result print("[INFO] Personenerkennung") result = detector(image) # zeigt das Ergebnis an for (xA, yA, xB, yB) in result: cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0), 2) cv2.imshow("result", image) cv2.waitKey(0) cv2.destroyAllWindows() return (result, image)
In diesem Codeabschnitt definieren wir eine Funktion, die ein Bild aus einer lokalen Datei einliest und darin Personen erkennt. Dazu rufe ich einfach die Funktion `detector()` auf und füge eine Schleife hinzu, um die Kreise für die Erkennung zu zeichnen. Die Funktion gibt die Anzahl der erkannten Kreise und das Bild mit den markierten Bereichen zurück. Anschließend können Sie das Ergebnis in einem neuen Fenster Ihres Betriebssystems anzeigen.
Abschnitt 4:
def cameraDetect(token, device, variable, sample_time=5): cap = cv2.VideoCapture(0) init = time.time() # Zulässige Abtastzeit für Ubidots : 1 Punkt/Sekunde if sample_time < 1: sample_time = 1 while(True): # Einzelbildaufnahme ret, frame = cap.read() frame = imutils.resize(frame, width=min(400, frame.shape[1])) result = detector(frame.copy()) # Ergebnis anzeigen for (xA, yA, xB, yB) in result: cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2) cv2.imshow('frame', frame) # Ergebnisse senden if time.time() - init >= sample_time: print("[INFO] Sende aktuelle Einzelbildergebnisse") # Bild in Base64 konvertieren und hinzufügen zum Kontext b64 = convert_to_base64(frame) context = {"image": b64} sendToUbidots(token, device, variable, len(result), context=context) init = time.time() if cv2.waitKey(1) & 0xFF == ord('q'): break # Wenn alles fertig ist, die Aufnahme freigeben cap.release() cv2.destroyAllWindows() def convert_to_base64(image): image = imutils.resize(image, width=400) img_str = cv2.imencode('.png', image)[1].tostring() b64 = base64.b64encode(img_str) return b64.decode('utf-8')
Ähnlich wie in Abschnitt 3 ruft auch diese Abschnitt 4 `detector()` , zeichnet Rahmen und erfasst das Bild direkt von der Webcam mithilfe der `VideoCapture()` von OpenCV. Wir haben OpenCV leicht modifiziert, um Bilder von der Kamera zu erhalten und die Ergebnisse alle „n“ Sekunden Ubidots sendToUbidots ()` wird später in diesem Tutorial erläutert). Die Funktion Ubidots ()` wandelt das Bild in einen Base64-String um. Dieser String ist wichtig, um die Ergebnisse in Ubidots mithilfe von JavaScript-Code in einem HTML-Canvas-Widget anzuzeigen.
Abschnitt 5:
def detectPeople(args): image_path = args["image"] camera = True if str(args["camera"]) == 'true' else False # Routine zum Lesen des lokalen Bildes if image_path != None and not camera: print("[INFO] Bildpfad angegeben, versuche Bild zu lesen") (result, image) = localDetect(image_path) print("[INFO] Ergebnisse werden gesendet") # Konvertiert das Bild in Base64 und fügt es dem Kontext hinzu b64 = convert_to_base64(image) context = {"image": b64} # Sendet das Ergebnis req = sendToUbidots(TOKEN, DEVICE, VARIABLE, len(result), context=context) if req.status_code >= 400: print("[ERROR] Daten konnten nicht an Ubidotsgesendet werden ") return req # Routine zum Lesen von Bildern von der Webcam if camera: print("[INFO] Kamerabilder werden gelesen") cameraDetect(TOKEN, GERÄT, VARIABLE)
Diese Methode dient dazu, die Argumente über Ihr Terminal einzufügen und eine Routine auszulösen, die in einer lokal gespeicherten Bilddatei oder über Ihre Webcam nach Personen sucht.
Abschnitt 6:
def buildPayload(variable, value, context): return {variable: {"value": value, "context": context}} def sendToUbidots(token, device, variable, value, context={}, industrial=True): # Erstellt die Endpunkt-URL = URL_INDUSTRIAL if industrial else URL_EDUCATIONAL url = "{}/api/v1.6/devices/{}".format(url, device) payload = buildPayload(variable, value, context) headers = {"X-Auth-Token": token, "Content-Type": "application/json"} attempts = 0 status = 400 while status >= 400 and attempts <= 5: req = requests.post(url=url, headers=headers, json=payload) status = req.status_code attempts += 1 time.sleep(1) return req
Diese beiden Funktionen aus Abschnitt 6 dienen als Schnittstelle, um Ihre Ergebnisse an Ubidots und Ihre Daten dort zu analysieren und zu visualisieren. Die erste Funktion `def buildPayload` erstellt die Nutzdaten innerhalb der Anfrage, während die zweite Funktion `def sendTo Ubidots Ihre Ubidots Parameter (TOKEN, die Variable und Gerätebezeichnungen) empfängt, um die Ergebnisse zu speichern. In diesem Fall handelt es sich um die Länge der von OpenCV erkannten runden Bereiche. Optional kann auch ein Kontext gesendet werden, um die Ergebnisse als Base64-Bild zu speichern und später abzurufen.
Abschnitt 7:
def argsParser(): ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default=None, help="Pfad zum Verzeichnis der Bildtestdateien") ap.add_argument("-c", "--camera", default=False, help="Auf true setzen, wenn Sie die Kamera verwenden möchten") args = vars(ap.parse_args()) return args
In Abschnitt 7 nähern wir uns dem Ende unserer Codeanalyse. Die Funktion `argsParser()` analysiert die über Ihr Terminal an unser Skript übergebenen Argumente und gibt sie als Dictionary zurück. Der Parser benötigt zwei Argumente:
- Bild: Der Pfad zur Bilddatei in Ihrem System
- camera: Eine Variable, die, wenn sie auf 'true' gesetzt wird, die Methode cameraDetect() aufruft.
Abschnitt 8:
def main(): args = argsParser() detectPeople(args) if __name__ == '__main__': main()
Abschnitt 8 und der letzte Teil unseres Codes ist die main()- Funktion, die einfach die Argumente von der Konsole aufruft und die angegebene Routine startet.
Nicht vergessen: Der gesamte Code kann hier von GitHub .
3) Testen
Öffnen Sie Ihr bevorzugtes Textverarbeitungsprogramm (Sublime Text, Notepad, Nano usw.) und kopieren Sie den hier . Ersetzen Sie den Code durch Ihr spezifisches Ubidots Token und speichern Sie die Datei als „peopleCounter.py“.
Nachdem Ihr Code ordnungsgemäß gespeichert wurde, testen wir nun die nächsten vier zufällig ausgewählten Bilder aus dem Caltech-Datensatz und dem öffentlichen Pexels-Datensatz:
Um diese Bilder zu analysieren, müssen Sie sie zunächst auf Ihrem Laptop oder PC speichern und den Pfad zur Analyse der Bilder verfolgen.
python peopleCounter.py PFAD_ZUR_BILDDATEI
In meinem Fall habe ich die Bilder in einem Pfad mit der Bezeichnung „dataset“ gespeichert. Um einen gültigen Befehl auszuführen, führen Sie den unten stehenden Befehl aus, jedoch mit dem Pfad zu Ihren Bildern.
python peopleCounter.py -i dataset/image_1.png
Wenn Sie Bilder von Ihrer Kamera anstatt aus einer lokalen Datei verwenden möchten, führen Sie einfach den folgenden Befehl aus:
python peopleCounter.py -c true
Testergebnisse:
Zusätzlich zu diesen Testprüfungen sehen Sie die Ergebnisse dieser Tests auch in Echtzeit in Ihrem Ubidots Konto gespeichert:
4) Erstellen Ihres Dashboard
Wir verwenden ein HTML Canvas, um unsere Ergebnisse in Echtzeit zu beobachten. Dieses Tutorial ist nicht für HTML Canvas-Widgets gedacht. Wenn Sie also nicht wissen, wie man sie verwendet, lesen Sie bitte die folgenden Artikel:
- Canvas-Widget-Beispiele
- Einführungsdemo für das Canvas-Widget
- Canvas: Erstellen eines Echtzeit-Widgets
Wir werden das einfache Echtzeitbeispiel mit geringfügigen Anpassungen verwenden, um unsere Bilder zu betrachten. Unten sehen Sie den Codeausschnitt des Widgets
HTML
<img id="img" width="400px" height="auto"/>
JS
var socket; var srv = "ubidots"; // var srv = "ubidots" // Diese Zeile auskommentieren, wenn Sie ein Benutzer im Bildungsbereich sind var VAR_ID = "5ab402dabbddbd3476d85967"; // Hier Ihre var Id einfügen var TOKEN = "" // Hier Ihr Token einfügen $( document ).ready(function() { function renderImage(imageBase64){ if (!imageBase64) return; $('#img').attr('src', 'data:image/png;base64, ' + imageBase64); } // Funktion zum Abrufen des letzten Werts, wird nur einmal ausgeführt function getDataFromVariable(variable, token, callback) { var url = 'https://things.ubidots.com/api/v1.6/variables/' + variable + '/values'; var headers = { 'X-Auth-Token': token, 'Content-Type': 'application/json' }; $.ajax({ url: url, method: 'GET', headers: headers, data : { page_size: 1 }, success: function (res) { if (res.results.length > 0){ renderImage(res.results[0].context.image); } callback(); } }); } // Implementiert die Verbindung zum Server socket = io.connect("https://"+ srv, {path: '/notifications'}); var subscribedVars = []; // Funktion zum Veröffentlichen der Variablen-ID var subscribeVariable = function (variable, callback) { // Veröffentlicht die Variablen-ID, die auf Änderungen warten soll socket.emit('rt/variables/id/last_value', { variable: variable }); // Lauscht auf Änderungen socket.on('rt/variables/' + variable + '/last_value', callback); subscribedVars.push(variable); }; // Funktion zum Abmelden vom Abonnieren var unSubscribeVariable = function (variable) { socket.emit('unsub/rt/variables/id/last_value', { variable: variable }); var pst = subscribedVars.indexOf(variable); if (pst !== -1){ subscribedVars.splice(pst, 1); } }; var connectSocket = function (){ // Implementiert die Socket-Verbindung socket.on('connect', function(){ console.log('connect'); socket.emit('authentication', {token: TOKEN}); }); window.addEventListener('online', function () { console.log('online'); socket.emit('authentication', {token: TOKEN}); }); socket.on('authenticated', function () { console.log('authenticated'); subscribedVars.forEach(function (variable_id) { socket.emit('rt/variables/id/last_value', { variable: variable_id }); }); }); } /* Hauptroutine */ getDataFromVariable(VAR_ID, TOKEN, function(){ connectSocket(); }); connectSocket(); //connectSocket(); // Variable mit eigenem Code abonnieren. subscribeVariable(VAR_ID, function(value){ var parsedValue = JSON.parse(value); console.log(parsedValue); //$('#img').attr('src', 'data:image/png;base64, ' + parsedValue.context.image); renderImage(parsedValue.context.image); }) });
Token und die Variablen-ID an den Anfang des Code-Snippets einzufügen
BIBLIOTHEKEN DRITTER
Fügen Sie die folgenden Drittanbieterbibliotheken hinzu:
Sobald Sie Ihr Widget gespeichert haben, sollte es in etwa so aussehen wie unten dargestellt:
5) Ergebnisse
Link können Sie sich die dashboards mit den Ergebnissen .
In diesem Artikel haben wir untersucht, wie man mit DIP (Bildverarbeitung), OpenCV und Ubidots IoT . Dank dieser Dienste ist Ihre DIP-Anwendung bei der Erkennung und Unterscheidung von Personen, Orten oder Objekten deutlich genauer als PIR- oder andere optische Sensoren – so erhalten Sie einen effizienten Personenzähler ohne die üblichen Probleme der Datenaufbereitung.
Teilt uns eure Meinung mit, indem ihr Ubidots einen Kommentar in unseren Community-Foren hinterlasst oder einfach über Facebook , Twitter oder Hackster Ubidots .
Viel Spaß beim Hacken!