Comptage de personnes avec OpenCV, Python et Ubidots

Le traitement numérique d'images (TNI) connaît une croissance rapide, notamment grâce à l'essor des techniques Machine Learning accessibles aux développeurs via le cloud. Le traitement d'images numériques dans le cloud s'affranchit de tout matériel dédié, faisant du TNI la solution de choix. Méthode de traitement d'images la plus économique et la plus polyvalente, le TNI trouve de nombreuses applications. Parmi les plus courantes figurent la détection et le comptage de piétons, une donnée précieuse pour les aéroports, les gares, les commerces, les stades, les événements publics et les musées.

Les compteurs de personnes traditionnels, disponibles dans le commerce, sont non seulement coûteux, mais les données qu'ils génèrent sont souvent liées à des systèmes propriétaires qui limitent vos options d'extraction de données et d'optimisation des indicateurs clés de performance (KPI). À l'inverse, un système de comptage de personnes intégré, utilisant votre propre caméra et carte SBC, vous permettra non seulement de gagner du temps et de l'argent, mais aussi de personnaliser votre application en fonction des KPI qui vous importent et d'exploiter des informations issues du cloud qui seraient autrement inaccessibles.

L'utilisation du cloud pour votre IoT permet d'améliorer ses fonctionnalités globales. Grâce à des capacités accrues en matière de visualisation , de reporting, d'alertes et de croisement de données avec des sources externes (telles que la météo, les prix des fournisseurs en temps réel ou les systèmes de gestion d'entreprise), DIP offre aux développeurs la liberté dont ils ont besoin.

Imaginez un épicier équipé d'un réfrigérateur à glaces : il souhaite suivre le nombre de clients qui passent devant et choisissent un produit, ainsi que le nombre d'ouvertures de la porte et la température interne du réfrigérateur. À partir de ces quelques données, il peut effectuer une analyse de corrélation pour mieux comprendre et optimiser le prix de ses produits et la consommation énergétique globale du réfrigérateur.

Pour démarrer votre application de traitement d'images numériques, Ubidots a créé le tutoriel suivant sur un système de comptage de personnes utilisant OpenCV et Python pour analyser le nombre de personnes dans une zone donnée . Développez vos applications au-delà du simple comptage de personnes grâce aux ressources supplémentaires de IoT Ubidots . Vous pouvez ici consulter un dashboard de comptage de personnes en temps créé avec Ubidots .

Dans cet article, nous verrons comment implémenter une superposition DIP simple pour créer un compteur de personnes à l'aide d'OpenCV et Ubidots . Cet exemple fonctionne de manière optimale avec toute distribution Linux, ainsi qu'avec un Raspberry Pi, un Orange Pi ou tout autre système embarqué similaire.

Pour toute question supplémentaire concernant l'intégration, contactez le support Ubidots et découvrez comment votre entreprise peut tirer parti de cette technologie à valeur ajoutée.

Table des matières:

  1. Exigences de l'application
  2. Codage – 8 sous-sections
  3. Essai
  4. Créer votre Dashboard
  5. Résultats

1) Exigences de candidature

  • Tout système Linux embarqué avec une version dérivée d'Ubuntu
  • Python 3 ou une version plus récente doit être installé sur votre système d'exploitation.
  • OpenCV 3.0 ou une version ultérieure doit être installée sur votre système d'exploitation. Si vous utilisez Ubuntu ou une distribution dérivée, suivez le tutoriel d'installation officiel ou exécutez la commande ci-dessous :
pip installer opencv-contrib-python
  • Une fois Python 3 et OpenCV installés avec succès, vérifiez votre configuration en exécutant ce petit morceau de code (tapez simplement « python » dans votre terminal) :
importer cv2 cv2.__version__

Vous devriez obtenir une capture d'écran avec votre version d'OpenCV :

  • Installez NumPy en suivant les officielles ou en exécutant simplement la commande ci-dessous :
pip installer numpy
  • Installer imutils
pip installer imutils
  • Demandes d'installation
requêtes d'installation pip

2) Codage

La procédure complète de détection et d'envoi des données est disponible ici . Afin de mieux comprendre notre code, nous l'avons divisé en huit sections pour vous expliquer chaque aspect en détail.

Section 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 # Définissez cette valeur sur False si vous êtes un utilisateur du secteur de l'éducation TOKEN = "..." # Insérez ici votre jeton Ubidots DEVICE = "detector" # Appareil sur lequel le résultat sera stocké VARIABLE = "people" # Variable sur laquelle le résultat sera stocké # SVM pré-entraîné OpenCV avec les caractéristiques HOG des personnes HOGCV = cv2.HOGDescriptor() HOGCV.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

Dans la section 1, nous importons les bibliothèques nécessaires à la mise en œuvre de notre détecteur : imutils est un outil de bibliothèque utile pour DIP et nous permettra d’effectuer différentes transformations sur nos résultats ; cv2 est notre wrapper Python pour OpenCV ; requests nous permettra d’envoyer nos données/résultats via HTTP à Ubidots ; et argparse nous permettra de lire les commandes de notre terminal de commande dans notre script.

IMPORTANT : N'oubliez pas de mettre à jour ce code avec votre jeton de compte Ubidots , et si vous êtes un utilisateur du secteur de l'éducation , assurez-vous de définir INDUSTRIAL_USER sur FALSE .

Une fois la bibliothèque importée, nous initialiserons notre descripteur d'objets orienté histogramme (HOG). HOG est l'une des techniques les plus populaires pour la détection d'objets et a été implémentée avec succès dans de nombreuses applications. De plus, OpenCV permet déjà de combiner efficacement l'algorithme HOG avec une machine à vecteurs de support (SVM), une machine learning pour la prédiction.

Cette déclaration : cv2.HOGDescriptor_getDefaultPeopleDetector() fait appel au modèle pré-entraîné pour la détection de personnes d’OpenCV et alimentera notre fonction d’évaluation des caractéristiques de la machine à vecteurs de support.

Section 2

def detector(image): ''' @image est un tableau numpy ''' 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) # Applique la suppression des valeurs non maximales du package imutils pour identifier les boîtes superposées # 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

dans la `detect()` que la magie opère : elle reçoit une image RGB décomposée en trois canaux de couleur. Pour optimiser les performances, nous redimensionnons l'image avec `imutils` puis appelons la detectMultiScale()` de notre objet HOG. Cette méthode nous permet ensuite d'analyser l'image et de déterminer la présence d'une personne grâce au résultat de la classification de notre SVM. Les paramètres de cette méthode dépassent le cadre de ce tutoriel ; pour en savoir plus, consultez la documentation officielle d'OpenCV ou l'excellente explication d'Adrian Rosebrock .

L'analyse HOG génère des boîtes de capture (objets détectés), mais il arrive que ces boîtes se chevauchent, ce qui entraîne des faux positifs ou des erreurs de détection. Pour éviter ces problèmes, nous utiliserons l'utilitaire de suppression des non-maxima de la imutils afin de supprimer les boîtes qui se chevauchent, comme illustré ci-dessous :

Images reproduites à partir de https://www.pyimagesearch.com

Section 3 :

def localDetect(image_path): result = [] image = cv2.imread(image_path) if len(image) <= 0: print("[ERREUR] Impossible de lire votre image locale") return result print("[INFO] Détection de personnes") result = detector(image) # affiche le résultat 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)

Dans cette partie du code, nous devons définir une fonction qui lit une image depuis un fichier local et y détecte les personnes. Pour ce faire, j'ai simplement appelé la fonction `detector()` et ajouté une boucle pour dessiner les rectangles de détection. Cette fonction renvoie le nombre de rectangles détectés et l'image avec les zones de détection. Il suffit ensuite d'afficher le résultat dans une nouvelle fenêtre du système d'exploitation.

Section 4 :

def cameraDetect(token, device, variable, sample_time=5): cap = cv2.VideoCapture(0) init = time.time() # Le temps d'échantillonnage autorisé pour Ubidots est de 1 point/seconde si sample_time < 1: sample_time = 1 while(True): # Capture image par image ret, frame = cap.read() frame = imutils.resize(frame, width=min(400, frame.shape[1])) result = detector(frame.copy()) # Affiche le résultat for (xA, yA, xB, yB) in result: cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2) cv2.imshow('frame', frame) # Envoie les résultats si time.time() - init >= sample_time: print("[INFO] Envoi des résultats de l'image réelle") # Convertit l'image en base 64 et l'ajoute au contexte 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 # Une fois terminé, libérer le 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')

À l'instar de la fonction de la section 3 , cette de la section 4 appelle la `detector()` et dessine des boîtes. L'image est récupérée directement depuis la webcam grâce à la `VideoCapture()` d'OpenCV. Nous avons également légèrement modifié le officiel pour obtenir les images de la caméra et envoyer les résultats à un Ubidots toutes les « n » secondes (la Ubidots sendToUbidots ()` sera présentée plus loin dans ce tutoriel). La fonction `convert_to_base64()` convertit l'image en une chaîne de caractères en base 64. Cette chaîne est essentielle pour visualiser les résultats dans Ubidots à l'aide de code JavaScript intégré à un widget Canvas HTML.

Section 5 :

def detectPeople(args): image_path = args["image"] camera = True if str(args["camera"]) == 'true' else False # Routine pour lire l'image locale si image_path != None et not camera: print("[INFO] Chemin de l'image fourni, tentative de lecture de l'image") (result, image) = localDetect(image_path) print("[INFO] Envoi des résultats") # Convertit l'image en base 64 et l'ajoute au contexte b64 = convert_to_base64(image) context = {"image": b64} # Envoie le résultat req = sendToUbidots(TOKEN, DEVICE, VARIABLE, len(result), context=context) if req.status_code >= 400: print("[ERROR] Impossible d'envoyer les données à Ubidots") return req # Routine pour lire les images de la webcam si camera: print("[INFO] Lecture des images de la caméra") cameraDetect(TOKEN, DEVICE, VARIABLE)

Cette méthode a pour but de recevoir les arguments insérés via votre terminal et de déclencher une routine qui recherche des personnes dans un fichier image stocké localement ou via votre webcam.

Section 6 :

def buildPayload(variable, value, context): return {variable: {"value": value, "context": context}} def sendToUbidots(token, device, variable, value, context={}, industrial=True): # Construit l'URL du point de terminaison 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

Ces deux fonctions de la section 6 constituent le moyen d'envoyer vos résultats à Ubidots pour l'analyse et la visualisation de vos données. La première fonction, `buildPayload`, construit la charge utile au sein de la requête, tandis que la seconde, `sendToUbidots` Ubidots reçoit vos Ubidots (le jeton, la variable et les étiquettes du périphérique) afin d'y stocker les résultats. Dans ce cas précis, il s'agit de la longueur des boîtes rondes détectées par OpenCV. En option, un contexte peut également être envoyé pour stocker les résultats sous forme d'image base64 et pouvoir les récupérer ultérieurement.

Section 7 :

def argsParser(): ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default=None, help="chemin vers le répertoire des fichiers de test d'images") ap.add_argument("-c", "--camera", default=False, help="Définir sur true si vous souhaitez utiliser l'appareil photo") args = vars(ap.parse_args()) return args

Nous arrivons au terme de notre analyse de code dans la section 7. `argsParser()` analyse et renvoie sous forme de dictionnaire les arguments transmis à notre script via votre terminal. L'analyseur syntaxique prend deux arguments :

  • image : Chemin d’accès au fichier image dans votre système
  • caméra : une variable qui, si elle est définie sur « true », appellera la méthode cameraDetect().

Section 8 :

def main(): args = argsParser() detectPeople(args) if __name__ == '__main__': main()

La section 8 et la dernière partie de notre code sont la main() qui appelle simplement les arguments depuis la console et lance la routine spécifiée.

N'oubliez pas que le code complet peut être récupéré depuis Github ici .

3) Tests

Ouvrez votre éditeur de texte préféré (Sublime Text, Notepad, Nano, etc.) et copiez-collez le code complet disponible ici . Remplacez le code par votre jeton Ubidots et enregistrez votre fichier sous le nom « peopleCounter.py ».

Une fois votre code correctement enregistré, testons les quatre images aléatoires suivantes sélectionnées à partir des ensembles de données publics Caltech Dataset et Pexels :

Pour analyser ces images, vous devez d'abord les enregistrer sur votre ordinateur portable ou PC et suivre le chemin d'accès pour les analyser.

python peopleCounter.py CHEMIN_VERS_LE_FICHIER_IMAGE

Dans mon cas, j'ai stocké les images dans un dossier nommé « dataset ». Pour exécuter une commande valide, exécutez la commande ci-dessous en remplaçant « dataset » par le chemin d'accès à vos images.

python peopleCounter.py -i dataset/image_1.png

Si vous souhaitez utiliser les images de votre appareil photo plutôt qu'un fichier local, il vous suffit d'exécuter la commande ci-dessous :

python peopleCounter.py -c true

Résultats des tests :

En plus de ces contrôles, vous verrez également, en temps réel, les résultats de ces tests enregistrés dans votre compte Ubidots :

4) Création de votre Dashboard

Nous utiliserons un élément Canvas HTML pour visualiser nos résultats en temps réel. Ce tutoriel ne concerne pas les widgets Canvas HTML ; si vous ne savez pas comment les utiliser, veuillez consulter les articles ci-dessous :

Nous utiliserons l'exemple de base en temps réel, légèrement modifié, pour visualiser nos images. Vous trouverez ci-dessous un extrait de code du widget

HTML

<img id="img" width="400px" height="auto"/>

JS

var socket; var srv = "ubidots"; // var srv = "ubidots" // Décommentez cette ligne si vous êtes un utilisateur éducatif var VAR_ID = "5ab402dabbddbd3476d85967"; // Insérez ici votre ID de variable var TOKEN = "" // Insérez ici votre jeton $(document).ready(function() { function renderImage(imageBase64){ if (!imageBase64) return; $('#img').attr('src', 'data:image/png;base64, ' + imageBase64); } // Fonction pour récupérer la dernière valeur, elle ne s'exécute qu'une seule fois function getDataFromVariable(variable, token, callback) { var url = 'ubidots' + 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(); } }); } // Implémente la connexion au serveur socket = io.connect("https://"+ srv, {path: '/notifications'}); var subscribedVars = []; // Fonction pour publier l'ID de la variable var subscribeVariable = function (variable, callback) { // Publie l'ID de la variable qui souhaite écouter socket.emit('rt/variables/id/last_value', { variable: variable }); // Écoute les changements socket.on('rt/variables/' + variable + '/last_value', callback); subscribedVars.push(variable); }; // Fonction pour se désabonner de l'écoute 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 (){ // Implémente la connexion socket 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 }); }); }); } /* Routine principale */ getDataFromVariable(VAR_ID, TOKEN, function(){ connectSocket(); }); connectSocket(); //connectSocket(); // Abonnez-vous à la variable avec votre propre code. 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); }) });

N'oubliez pas d'indiquer votre jeton et l' identifiant de la variable au début de l'extrait de code.

BIBLIOTHÈQUES TIERS

Ajoutez les bibliothèques tierces suivantes :

Une fois votre widget enregistré, vous devriez obtenir un résultat similaire à celui ci-dessous :

5) Résultats

Vous pouvez consulter les dashboards avec les résultats en suivant ce lien .

Dans cet article, nous avons exploré la création d'un IoT utilisant le traitement d'images (DIP), OpenCV et Ubidots . Grâce à ces services, votre application DIP est bien plus précise que PIR ou autres capteurs optiques pour détecter et identifier les différences entre personnes, lieux ou objets, vous offrant ainsi un compteur de personnes performant sans les interférences liées au traitement initial des données.

Faites-nous part de votre avis en laissant un commentaire sur les forums de notre communauté Ubidots simplement Ubidots Facebook , Twitter ou Hackster .

Bon piratage !

Articles suggérés