View  Edit  Attributes  History  Attach  Print  Search

PYQTLAB

PyQt Lab' : OPenCV + Webcam : Capture simple du flux vidéo

Par X. HINAULT - Juin 2013

Ce que l'on va faire ici

  • Dans ce code PyQt, je montre comment réaliser une capture simple du flux vidéo en provenance d'une webcam et comment l'afficher dans une interface Qt (dans un QLabel via un QImage et Qpixmap).
  • La clé de ce code est une classe de conversion de l'IplImage (format OpenCV) obtenu à partir de la webcam en un QImage. Ensuite, le QImage est transféré dans un QPixmap pour affichage dans un QLabel. Voir section "Dessin" pour les détails à ce sujet.

Pré-requis

  • python 2.7
  • pyqt4.x
  • modules :
    • Opencv

Matériel utilisé

  • Webcam Logitech C270

Manip' utile préalable

  • S'assurer que la webcam fonctionne correctement, en ouvrant par exemple GuvcView sous Gnu/Linux. Une fois fait, bien refermer avant de lancer le code si-dessous par contre... !

Le fichier d'interface *.py

  • Fichier obtenu automatiquement avec l'utilitaire pyuic4 à partir du fichier *.ui créé avec QtDesigner :
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file
#
# Created: Sun Jun 16 07:40:43 2013
#      by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName(_fromUtf8("Form"))
        Form.resize(331, 250)
        self.labelImage = QtGui.QLabel(Form)
        self.labelImage.setGeometry(QtCore.QRect(5, 5, 320, 240))
        self.labelImage.setFrameShape(QtGui.QFrame.Box)
        self.labelImage.setFrameShadow(QtGui.QFrame.Sunken)
        self.labelImage.setText(_fromUtf8(""))
        self.labelImage.setObjectName(_fromUtf8("labelImage"))

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate("Form", "PyQt+Opencv+webcam : Capture image", None, QtGui.QApplication.UnicodeUTF8))


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    Form = QtGui.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

 

Le fichier d'application *Main.py


#!/usr/bin/python
# -*- coding: utf-8 -*-

# Par X. HINAULT - Tous droits réservés - GPLv3
# Déc 2012 - www.mon-club-elec.fr

# --- importation des modules utiles ---
from PyQt4.QtGui import *
from PyQt4.QtCore import * # inclut QTimer..

import os,sys
import time # pour micros

#from cv2 import * # importe module OpenCV qui contient sous-module cv
import cv2.cv as cv # importe sous-module cv du module OpenCV
# import cv2 # importe le module OpenCV

# --- importation du fichier de description GUI ---
from tuto_pyqt_opencv_webcam_capture_image import *

# --- classe principale du programme pyQt contenant le code actif ---
class myApp(QWidget, Ui_Form):
# la classe reçoit le Qwidget principal ET la classe Ui_Form définie dans *.py obtenu avec pyuic4

        # Note : ici self représente la classe

        def __init__(self, parent=None):
                QWidget.__init__(self) # initialise le Qwidget principal
                self.setupUi(parent) # Obligatoire

                #-- variables utiles --

                #========= paramètres graphiques généraux ========
                self.framerate=30 # nombre d'image par secondes (rafraîchissement de l'affichage)              
                self.widthCapture=320 # largeur image capture en pixels
                self.heightCapture=240 # hauteur image capture en pixels
                # la résolution utilisée doit être supportée par la webcam - 320x240 est un bon compromis -            

                # --- Paramétrage des widgets de l'interface GUI si nécessaire ---
                # ..

                # --- Connexions entre signaux des widgets et fonctions
                # connecte chaque signal utilisé des objets à l'appel de la fonction voulue
                # ..

                #--- code actif initial

                #======== config capture openCV ==========
                #-- initialisation de la caméra
                self.indexCam=0 # index de la webcam à utiliser - voir ls /dev/video* - utiliser -1 si pas d'indice
                self.webcam=cv.CaptureFromCAM(self.indexCam) # déclare l'objet capture sans désigner la caméra - remplace CreateCameraCapture
                #print (self.webcam) # debug
                cv.SetCaptureProperty(self.webcam,cv.CV_CAP_PROP_FRAME_HEIGHT,240) # définit hauteur image capture
                cv.SetCaptureProperty(self.webcam,cv.CV_CAP_PROP_FRAME_WIDTH,320) # définit largeur image capture
                #cv.SetCaptureProperty(self.webcam,cv.CV_CAP_PROP_FPS,30) # fixe le framerate hardware- pas toujours supporté par webcam

                #self.iplImgSrc=cv.QueryFrame(self.webcam) # récupère un IplImage en provenance de la webcam # +/- premiere capture

        # -- Activation d'un Timer pour affichage image - fixe le fps d'affichage en fait --
                self.timerRefresh=QTimer() # déclare un timer Qt
                self.connect(self.timerRefresh, SIGNAL("timeout()"), self.timerRefreshEvent) # connecte le signal timeOut de l'objet timer à l'appel de la fonction voulue
                self.timerRefresh.start(int(1000/self.framerate)) # lance le timer - mettre délai assez court pour fps élevé           

        # les fonctions appelées, utilisées par les signaux
        #def pushButtonClicked(self):
                #print("Bouton appuyé")

        # --- les fonctions appelées, utilisées par les signaux hors widgets ---

        def timerRefreshEvent(self): # fonction appelée lors de la survenue d'un évènement Timer - nom fonction indiférrent
                print("Capture Image webcam")

                micros0=self.micros()

                #self.iplImgSrc=cv.QueryFrame(self.webcam) # récupère un IplImage en provenance de la webcam - Grab + retrieve

                if cv.GrabFrame(self.webcam) : # si une image est dispo
                        print ("frame dispo")
                        self.iplImgSrc=cv.RetrieveFrame(self.webcam) # récupère un IplImage en provenance de la webcam

                print (str(self.micros()-micros0) + " us")             

                micros0=self.micros()

                # affichage de l'image webcam
                self.qImage = IplQImage(self.iplImgSrc) # récupère le IplImage dans un QImage
                self.qPixmap=QPixmap.fromImage(self.qImage) # récupère le QImage dans QPixmap
                self.labelImage.setPixmap(self.qPixmap) # affiche le QPixmap dans le QLabel

                print (str(self.micros()-micros0) + " us")             

        # autres fonctions actives

        # fonction micros : renvoie le nombre de microsecondes courant de l'horloge système
        def micros(self):
                return(int(round(time.time() * 1000000))) # microsecondes de l'horloge système

# --- classes utiles ---

# --- classe de conversion d'une iplImage en QImage
class IplQImage(QImage): # fonction qui étend la classe QImage
# IplQImage est une classe qui transforme un iplImage (OpenCV) en un QImage (Qt)
    """
http://matthewshotton.wordpress.com/2011/03/31/python-opencv-iplimage-to-pyqt-qimage/
A class for converting iplimages to qimages
"""


    def __init__(self,iplimage):
        # Rough-n-ready but it works dammit
        alpha = cv.CreateMat(iplimage.height,iplimage.width, cv.CV_8UC1)
        cv.Rectangle(alpha, (0, 0), (iplimage.width,iplimage.height), cv.ScalarAll(255) ,-1)
        rgba = cv.CreateMat(iplimage.height, iplimage.width, cv.CV_8UC4)
        cv.Set(rgba, (1, 2, 3, 4))
        cv.MixChannels([iplimage, alpha],[rgba], [
        (0, 0), # rgba[0] -> bgr[2]
        (1, 1), # rgba[1] -> bgr[1]
        (2, 2), # rgba[2] -> bgr[0]
        (3, 3) # rgba[3] -> alpha[0]
        ])
        self.__imagedata = rgba.tostring()
        super(IplQImage,self).__init__(self.__imagedata, iplimage.width, iplimage.height, QImage.Format_RGB32)

# -- fin class IplQImage

# fonction principale exécutant l'application Qt                       
def main(args):
        a=QApplication(args) # crée l'objet application
        f=QWidget() # crée le QWidget racine
        c=myApp(f) # appelle la classe contenant le code de l'application
        f.show() # affiche la fenêtre QWidget
        r=a.exec_() # lance l'exécution de l'application
        return r

# pour rendre le fichier *.py exécutable
if __name__=="__main__": # pour rendre le code exécutable
        main(sys.argv) # appelle la fonction main

 

Utilisation

  • Les 2 fichiers suivants sont à enregistrer dans un même répertoire, l'un en nom.py et l'autre en nomMain.py.
  • Puis lancer l'application depuis Geany ou équivalent, en exécutant le fichier nomMain.py
  • Le flux de la webcam doit s'afficher dans la fenêtre.