Reconnaissance faciale – TP1 : La vidéo en python

Lecture du flux dans un Thread parallèle

La théorie

Je suis sûr qu’à la lecture du titre de ce chapitre, tu auras trouvé la solution 🙂 .

Notre script s’exécute dans un unique thread. C’est à dire qu’il exécute ligne par ligne notre code. En fait il faudrait que la récupération de l’image de la webcam se fasse en parallèle de notre script. Pour ça, nous allons devoir créer un autre thread qui permettra d’exécuter d’autres lignes de code en parallèle. On y mettra alors notre méthode read() et elle ne bloquera plus notre thread principal.

Ça donnerait un truc du genre :

Il y aurait un thread qui serait chargé d’aller lire régulièrement l’image de la webcam et de la mettre dans une variable. Et notre thread principal qui à chaque boucle irait lire la photo dans cette variable. Du coup, notre thread principal ne serait plus jamais ralenti puisqu’il ne fait qu’aller prendre la photo dans une variable et non plus auprès de la webcam.

Le code python

Pour cela, on va aller créer une classe qui va venir se coller par-dessus cv2 en quelque sorte.

Tu commences à en avoir l’habitude, tu crées un nouveau fichier que tu appelles webcamThreadParallele.py et tu l’enregistres dans le répertoire outils.

Alors, pour les outils dont on va avoir besoin, simple ce sera Thread et cv2

from threading import Thread
import cv2

On déclare notre classe qu’on va appeler FluxVideoParallele. tous nos attributs seront privés (d’où les __ avant leurs noms).

class FluxVideoParallele:

    def __init__(self,idSourceVideo=0):
              
        ## Variable qui va nous permettre d'arrêter la lecture du flux video
        self.__stop = False
        
        ## L'image en cours venant de la webcam via cv2.read()
        self.__imageWebcam = None
        
        ## Le code retour de la lecture de l'image venant de cv2.read()
        self.__codeRetour = 0

        ## Attribut qui va récupérer la connexion à la webcam
        self.__fluxVideo = None
        
        ## On récupère l'identifiant de la source video
        self.__idSourceVideo = idSourceVideo

Comme il faudra bien donner l’identifiant de la source vidéo à cv2.VideoCapture() , on le passe comme argument et on l’affecte à un attribut.

L’attribut stop nous permettra d’arrêter notre thread parallèle.

on sait que cv2.read() nous renvoie un code retour et une image. Donc, notre méthode devra renvoyer le même type d’information. On crée donc 2 attributs __codeRetour et __imageWebcam.

Et on crée un attribut qui va se charger de récupérer la connexion à la webcam : __fluxVideo.

Maintenant, on va créer la méthode demarrer. Elle sera appelée pour initialiser le flux vidéo venant de la webcam et démarrer le thread parallèle. A notre thread, on lui donnera comme argument la méthode qui sera chargée de lire l’image de la webcam. Ca veut dire qu’on lui donne le code qu’il doit exécuter en parallèle.

    def demarrer(self):
        
        ## On récupère le flux de notre vidéo depuis la webcam
        self.__fluxVideo = cv2.VideoCapture(self.__idSourceVideo)

        ## On lance notre fonction qui va lire en continu notre video
        ## dans un thread parallele
        Thread(target=self._lecture, args=()).start()
        return self

C’est maintenant que l’on va écrire le code qui va s’exécuter en parallèle. Comme ça ne regarde que notre classe et qu’il n y a aucune raison qu’elle puisse être appelée de l’extérieur, cette méthode sera privée. On va l’appeler lecture car c’est celle qui est chargée de lire les images de la webcam.

    def __lecture(self):

        ## Tant qu'on ne demande pas à ce que ça s'arrête, on lit la vidéo
        while not self.__stop:
            
            ## on récupère l'image en cours dans le flux vidéo et le code
            ##retour
            try:
                (codeRetour, imageWebcam) = self.__fluxVideo.read()
                self.__codeRetour = codeRetour
                if codeRetour:
                    self.__imageWebcam = imageWebcam
            except:
                pass
        
        return

Donc on fait une boucle dont on ne sort jamais sauf si notre attribut __stop passe à vrai. Dans notre boucle, on récupère l’image depuis la webcam et on la met dans notre attribut __imageWebcam. Et un petit try/catch histoire de protéger tout ça. On n’oublie pas, tout est exécuté en parallèle de notre thread principal.

Ensuite, on va créer la méthode qui sera appelée par notre script pour récupérer l’image qui est présente dans l’attribut __imageWebcam.

    def image(self):
        
        ## lorsque cette fonction est appelée de l'extérieur, elle renvoie
        ## l'image actuellement stockée dans l'attribut __imageWebcam    
        return self.__codeRetour, self.__imageWebcam

Comme il faut bien pouvoir arrêter notre thread parallèle, on crée une méthode qu’on pourra appeler de l’extérieur et qui mettra notre attribut à True.

    def arreter(self):
        ##on arrête la lecture
        self.__stop = True
        self.__fluxVideo.release()

Mise à jour de notre script principal

Maintenant, nous allons aller dans notre script principal pour remplacer cv2 par notre nouvelle classe.

Pour les imports, on importe désormais notre nouvelle classe :

## On importe CV2 et notre nouvelle classe
import cv2
import outils.perfImage as perfImage
from outils.webcamThreadParallele import FluxVideoParallele
import time

On garde time pour le moment car on va ré-exécuter le test qu’on a fait un peu plus tôt. Et surtout, on garde cv2 puisqu’on l’utilise entre autres pour le texte et l’affichage de l’image dans une fenêtre. Et comme je suis un gars sympa, je te rappelle comment importer toutes classes d’un fichier python ou juste une classe spécifique 🙂

On remplace notre connexion à la webcam par la connexion à notre flux vidéo parallèle. On ne touche pas à notre perfImage

## On initialise le flux de capture vidéo
## depuis la webcam ou caméra de surveillance
## videoWebcam = cv2.VideoCapture(0)
videoWebcam = FluxVideoParallele(0).demarrer()

## On instancie notre objet depuis la classe PerfImage
monPerfImage = perfImage.PerfImage(1)

Et on remplace

    ##on récupère la dernière image de la vidéo 
    valeurRetour, imageWebcam = videoWebcam.read()

Par

    ##on récupère la dernière image de la vidéo 
    valeurRetour, imageWebcam = videoWebcam.image()

On enregistre notre travail et on lance notre script en appuyant sur F5.

Alors, pas mal les améliorations hein ? 😉 De mon côté, je passe à 500 FPS. Pour rappel, encore, au cas où, ce n’est pas ma webcam qui est à 500 FPS, mais c’est simplement que je fais en moyenne 500 boucles par seconde.

Et dans ma console, j’ai ceci :

Top départ
récupération de l'image : 0.0000
récupération de la ligne de perf : 0.0000
Ajout de la ligne de perf à l'image : 0.0000
Affichage de l'image : 0.0000
Top départ
récupération de l'image : 0.0000
récupération de la ligne de perf : 0.0000
Ajout de la ligne de perf à l'image : 0.0000
Affichage de l'image : 0.0000
Top départ
récupération de l'image : 0.0000
récupération de la ligne de perf : 0.0000
Ajout de la ligne de perf à l'image : 0.0000
Affichage de l'image : 0.0000

On constate que ma boucle n’est désormais plus ralentie par la lecture depuis la webcam.

2 Comments

  1. Bonjour, j’ai essayé de rentrer le code mais lorsque je dois installer le module outils, je tape pip install outils, je redémarre le kernel mais après, spyder me dit:”No module named ‘outils'”. Quelqu’un saurait ce que je devrais faire?

    • Bonjour Jeremy,

      Effectivement, je ne vois pas la trace du fichier outils.py dans l’article.
      Essaye de remplacer outils.perfImage as perfImage par perfImage
      Cela dépend de la où tu as mis la classe perfImage.

      Ced

Poster un Commentaire

Votre adresse de messagerie ne sera pas publiée.


*


Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.