View  Edit  Attributes  History  Attach  Print  Search

PYQTLAB

PyQt Lab' : Capture Webcam avec OpenCV : évaluation du délai de capture (résultat : 30 fps max et délai capture = 30ms !)

Par X. HINAULT - Juin 2013

Ce que l'on va faire ici

  • Dans ce code PyQt, capture Webcam avec GSVideo : capture avec utilisation du signal "pull-buffer" (résultat : jusqu'à 100 fps !)

Pré-requis

  • python 2.7
  • pyqt4.x
  • modules :
    • python-opencv
    • pygst
    • gst

Téléchargement :

  • Ces codes sont disponibles ici :

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=60 # 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 ("capture :" + 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 ("affichage : " + 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
  • On obtient la fenêtre avec capture vidéo