Logo Mon Club Elec

PyQt Lab’ : PyQt + OpenCV : Capture : Dessin : Dessiner un rectangle sous le curseur de la souris.

PyQt Lab est un outil puissant qui permet aux développeurs de créer des applications graphiques interactives. Il combine PyQt et OpenCV pour offrir une variété de fonctionnalités, notamment la capture et le dessin. Dans cet article, nous allons voir comment utiliser PyQt Lab pour dessiner un rectangle sous le curseur de la souris. Nous verrons comment configurer le logiciel et comment utiliser les outils de dessin pour créer un rectangle. Nous verrons également comment ajuster la taille et la couleur du rectangle.

PyQt Lab’ : PyQt + OpenCV : Capture : Dessin : Dessiner un rectangle sous le curseur de la souris.

Par X. HINAULT – Juin 2013

PyQt Lab’ : PyQt + OpenCV : Capture : Dessin : Dessiner un rectangle sous le curseur de la souris.

Ce que l’on va faire ici

  • Dans ce code PyQt, capture d’une image webcam et dessin sur l’image vidéo d’un rectangle sous la souris en temps réel.

Pré-requis

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

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 – Mai 2013 – Tous droits réservés
# GPLv3 – www.mon-club-elec.fr

# modules a importer
from PyQt4.QtGui import *
from PyQt4.QtCore import *  # inclut QTimer..
import os,sys

import time # temps – pour micros

import numpy as np

#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

from  tuto_pyqt_opencv_webcam_capture_image_mouse_rect import * # fichier obtenu à partir QtDesigner et pyuic4

# +/- variables et objets globaux

class myApp(QWidget, Ui_Form): # la classe reçoit le Qwidget principal ET la classe définie dans test.py obtenu avec pyuic4
        def __init__(self, parent=None):
                QWidget.__init__(self) # initialise le Qwidget principal
                self.setupUi(parent) # Obligatoire

                # — Variables de classe
                self.xMouse=None
                self.yMouse=None

                #—- 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 – 320×240 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=1 # 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.capture) # debug
                cv.SetCaptureProperty(self.webcam,cv.CV_CAP_PROP_FRAME_WIDTH,self.widthCapture) # fixe largeur de capture
                cv.SetCaptureProperty(self.webcam,cv.CV_CAP_PROP_FRAME_HEIGHT,self.heightCapture) # fixe hauteur de capture
                #cv.SetCaptureProperty(self.webcam,cv.CV_CAP_PROP_FPS,125) # fixe le framerate hardware- pas toujours supporté par webcam

                # création d’un QImage RGB (permettant l’accès aux pixels) pour affichage OpenCV dans Pyqt
                self.imageCV=QImage(self.labelImage.size(),QImage.Format_RGB32) # crée image RGB 32 bits même taille que label

                # Dessin avec QPixmap (affichage) et QImage (I/O, accès pixels)

                # création d’un QImage RGB permettant l’accès aux pixels
                self.image=QImage(self.labelImage.size(),QImage.Format_RGB32) # crée image RGB 32 bits même taille que label

                #– initialisation du QImage
                self.image.fill(QColor(255,255,255)) # fond blanc

                # coordonnées centre du QPixmap (même taille que label)
                #xo=self.image.width()/2
                #yo=self.image.height()/2

                #trace le point
                #self.image.setPixel(xo,yo,qRgb(0,0,0)) # modifie la couleur du pixel x,y – noter qRgb renvoie valeur RGB 0xFFRRGGBB

                #— dessin sur QImage —
                self.painterImage=QPainter(self.image) # associe QPainter (classe de dessin) au Qimage

                # paramètres détaillés crayon dessin
                #self.painterImage.setPen(QPen(Qt.SolidLine)) # fixe la couleur du crayon pour le dessin – moins propre…
                #self.pen=QPen(Qt.SolidLine) # crayon par défaut pour le painter – ligne continue
                #self.pen.setWidth(3) # fixe largeur crayon
                #self.pen.setColor(QColor(0,0,255)) # fixe couleur crayon              
                #self.painterImage.setPen(self.pen) # affecte le crayon defini au dessin – les paramètres s’appliquent seulement une fois setPen() appelé

                # tracé de formes
                #self.painterImage.setPen(QPen(QColor(0,0,255),2)) # fixe la couleur du crayon et la largeur pour le dessin – forme compactée  
                #self.painterImage.setBrush(QBrush(QColor(255,255,255),Qt.SolidPattern)) # fixe la couleur et le style du remplissage (brosse)  
                #self.painterImage.drawRect(10,10,50,50) # dessin rectangle : drawRect (self, int x, int y, int w, int h)
                #self.painterImage.drawPoint(xo,yo) # trace un point drawPoint (self, int x, int y)
                #self.painterImage.fillRect(150,150,30,30,QColor(255,255,0)) # fillRect (self, int x, int y, int w, int h, QColor b)
                #self.painterImage.drawEllipse(xo,yo,50,50) # dessin cercle – x-y = coin sup gauche rect encadrant : drawEllipse (self, int x, int y, int w, int h)
                #self.painterImage.drawEllipse(QPointF(xo,yo),50,50) # dessin cercle avec x,y centre et rayon : drawEllipse (self, QPointF center, float rx, float ry)
                #self.painterImage.drawLine(0,0,xo*2,yo) # trace une ligne : drawLine (self, int x1, int y1, int x2, int y2)
                #self.painterImage.drawText(QPointF(xo/2, yo/2), « Hello ») # drawText (self, QPointF p, QString s)

                # il existe d’autres possibilités de dessin (polygone, chemin, etc..) voir : http://pyqt.sourceforge.net/Docs/PyQt4/qpainter.html

                self.painterImage.end() # ferme le painter – n’est plus accessible après

                # — fin dessin sur QImage

                #– Initialisation du QPixmap
                self.pixmap=QPixmap.fromImage(self.image) # initialise  le QPixmap…

                self.updatePixmap(self.image) # affichage initial du QPixmap

                #—– initialisation capture évènement souris du Qlabel
                self.labelImage.setMouseTracking(True) # active le suivi position souris
                self.labelImage.installEventFilter(self) # défini un objet dans cette classe pour « filtrer » les évènements en provenance de l’objet label – méthode classe Object

        # — Activation d’un Timer pour affichage image OpenCV- 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 des widgets —

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

        #—– fonction de classe : affichage d’une nouvelle image webcam —
        def timerRefreshEvent(self): # fonction appelée lors de la survenue d’un évènement Timer = rafraîchissement image – nom fonction indiférrent

                #self.timerRefresh.stop() # +/- stoppe Timer pendant traitement

                print(« Capture Image webcam »)
                #print cv.GetCaptureProperty(self.webcam,cv.CV_CAP_PROP_FPS) # affiche propriété

                micros0=self.micros()

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

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

                # affichage de l’image webcam
                self.imageCV = IplQImage(self.iplImgSrc) # récupère le IplImage dans un QImage
                # durée = 1,5ms

                self.updatePixmap(self.imageCV) # met à jour le pixmap en utilisant imageCV

                #print (str(self.micros()-micros0) +  » us »)    

                #print (str(self.micros()-micros0) +  » us »)            

        # — fin timerRefreshEvent

        # fonction pour gérer filtrage évènements
        #def eventFilter(self, _, event): # fonction pour gérer filtrage évènements
        def eventFilter(self, srcEvent, event): # fonction pour gérer filtrage évènements – reçoit l’objet source de l’évènement
                print (« event « )
                #print srcEvent

                if srcEvent==self.labelImage: # teste si la source de l’évènement est bien le label – utile si plusieurs sources d’évènements activées
                        if event.type() == QEvent.MouseMove: # si évènement « mouvement de la souris »
                                self.xMouse=event.pos().x() # coordonnée souris au moment évènement dans le widget source de l’évènement =
                                self.yMouse=event.pos().y()
                                print (« MouseMove : x= »+ str(self.xMouse) +  » y= «  + str(self.yMouse)) # affiche coordonnées

                                self.updatePixmap(self.imageCV) # met à jour le pixmap en utilisant imageCV

                return False # obligatoire…

        # — fonctions de classes autres—    
        # 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

        # fonction de MAJ du QPixmap : chargement QImage +/- dessin + affichage dans QLabel
        def updatePixmap(self, imageIn):

                # chargement du QImage dans le QPixmap
                self.pixmap.convertFromImage(imageIn) # recharge le QImage dans le QPixmap existant – met à jour le QPixmap

                # +/- dessin sur le Pixmap – ne modifie pas le QImage chargé
                self.painterPixmap=QPainter(self.pixmap) # associe QPainter (classe de dessin) au QPixmap

                if self.xMouse and self.yMouse: # si les coordonnées existent
                        # tracé rectangle sur le Pixmap = laise QImage inchangé ++
                        self.painterPixmap.setPen(QPen(QColor(255,0,0),2)) # fixe la couleur du crayon et la largeur pour le dessin – forme compactée
                        cote=20 # coté du carré
                        self.painterPixmap.drawRect(self.xMouse(cote/2),self.yMouse(cote/2),cote,cote) # dessin rectangle

                # il existe d’autres possibilités de dessin (polygone, chemin, etc..) voir : http://pyqt.sourceforge.net/Docs/PyQt4/qpainter.html

                self.painterPixmap.end() # ferme le painter – n’est plus accessible après

                #– affichage du QPixmap dans QLabel
                self.labelImage.setPixmap(self.pixmap) # met à jour le qpixmap affiché dans le qlabel  

# — Autres 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)
# source : 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

# — Classe principale (lancement)  —
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

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 rectangle s’affiche sur l’image lorsque l’on déplace la souris.
Noter cet article

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Archive Mon Club Elec

Articles populaires

Newsletter

Inscrivez-vous maintenant et bénéficiez d'un soutien continu pour réaliser vos travaux électriques en toute sécurité.