OpenCV USB camera widget in PyQT

3Both tools OpenCV and PyQT are incredibly useful. One in combination with Qt Designer gives rapid GUI building possibility. Another is used for visual image/video analysis. There was one drawback – very little documentation how to use them into one piece of code. All in the past now!

To be short – sample program I finally wrote starts Qt Designer generated user interface. After you press Start button separate thread (for initializing camera and capturing frames) starts. All new frames are being placed in queue. On the other hand interface checks if there are any frames in the queue and gets them for display. So GUI feels alive and is not occupied with real-time task.

To complement this simple example I added findCirclesGrid for greater visual representation. Provided code lacks this feature, but it is more than easy to implement it by yourself.

PyQT

PyQt is a set of Python bindings for the Qt application framework and runs on all platforms supported by Qt including Windows, OS X, Linux, iOS and Android.

To install download binary package from here.

OpenCV

OpenCV (Open Source Computer Vision Library) is an open source computer vision and machine learning software library. OpenCV was built to provide a common infrastructure for computer vision applications and to accelerate the use of machine perception in the commercial products.

To install read this official tutorial or install wheel from Unofficial Windows Binaries for Python Extension Packages.

Py Qtdesigner

This is rapid GUI development tool. Can be started in a minute by installing it and running with Python (I don’t have Python in my environment location so have to use full path):

c:\Python27\Scripts\pip.exe pip install pyqode.designer
c:\Python27\Scripts\pyqode-designer.exe
2

Qt Designer main view

Example

Once we are done installing required packages our test can be run. Here is quick example what can be expected (as I mentioned before feature extraction is added to make this video clip more appealing, provided code displays raw USB camera feed).

out

Displaying USB camera calibration window in PyQT

Script

Comments ( 20 )

  1. ben
    Hi, Love the article! Not very familiar with python or opencv, but from the code it looks like a thread is created that initializes the camera then grabs frames continuously.. What I'm not following is how that results in the tracking/drawing? Does the post processing happen in the same thread? Thanks!
    • saulius
      Hi Ben, that's right. "grab" initializes camera and grabs frames to queue. Meanwhile "update_frame" takes frames from queue and updated view.
  2. jimperial
    Working like a charm in PyQt5, with some minor changes (PyQt5 refactored the QtGui module and added QtWidgets) from PyQt5 import QtCore, QtGui, uic, QtWidgets class OwnImageWidget(QtWidgets.QWidget): class MyWindowClass(QtWidgets.QMainWindow, form_class): QtWidgets.QMainWindow.__init__(self, parent)
    • saulius
      You are welcome! Did you compiled PyQT5 by yourself or found binary install?
    • Bahadir
      Thank you for your request also we should add app = QtWidgets.QApplication(sys.argv)
  3. Stratoprex
    Hi, one question, can you make an interface directly with QT4, a graphics view and call it with a launcher with functions from other file .py?
    • saulius
      Hi, Stratoprex. As far as I know you can make GUI with pyqteditor and use .ui file or compile it to .py file. Hope I understood your question correctly.
  4. marcoresl
    Thank you very much, this code has been very useful to me. I added a button to stop the thread and I'd like to save the frame when this button is pressed. I thougt to replicate the update_frame callback and change the last line with something like QtGui.QImage.save. I still can't have it working. May I ask you another little help?
    • saulius
      Hi Marcoresl, hard to suggest correct solution without your code. I'd probably implement dirty workaround: update_frame checking for global variable if button is pressed. If variable is true, save picture and change it to false again. Button click changes this global variable to true.
      • marcoresk
        Thank you for the quick response. I will work on that. May I ask for another suggestion: I updated the ui file to add a button that allow me to select the cam, and a global variable does the trick to start a different webcam. Next step would be to allow display a different webcam every time the button is pressed: if a thread can not be stopped / restarted, is in your opinion the creation of many different threads for every webcam the most efficient solution? Thank you again for your code that gave me inspiration.
        • saulius
          Hi Marcoresk, probably for a quick hack I'd create global counter for camera id. When button is pressed this number is incremented (with overflow check). Capture thread will use this camera id for source identification. But as I mentioned this is quick hack, recently I have rewritten most of this code with classes, signals and events.
  5. 101
    Hi thanks for this! i'm just starting to learn cv2 and like a month with python and your code really helps a lot. Tried this on python 3.6.2 and pyQT 5.7 works great withsome minor updates like: some of the changes made to run on python 3.6.* 1. as said by @jimperial from PyQt4 import QtCore, QtGui, uic ------> from PyQt5 import QtCore, QtGui, uic ,QtWidgets class OwnImageWidget(QtGui.QWidget): ---> class OwnImageWidget(QtWidgets.QWidget): class MyWindowClass(QtGui.QMainWindow, form_class): ---->class MyWindowClass(QtWidgets.QMainWindow, form_class): QtGui.QMainWindow.__init__(self, parent) ------> QtWidgets.QMainWindow.__init__(self, parent) 2. import Queue ------> import queue q = Queue.Queue() ------> q = queue.Queue() 3. app = QtGui.QApplication(sys.argv) ------> app = QtWidgets.QApplication(sys.argv) thanks a lot! :)
  6. Daeyeong
    Dear saulius. I working on the pyqt5 and python3 in anaconda. Can you help me? If give to me your email addresses then i will send to more detailed. I'm sorry that not good at English. Best regards.
    • saulius
      Dear Lim, thank you for trusting my experience. Please try to explain your problem below in comments.
  7. daeyoeng lim
    Thank you for you Reply. I'm very great to meet you. For explain my problem, will need some picture. Therefore I did make it and posting my blog. http://blog.naver.com/nicecapjjang/221134726608 I can send to you with pdf files(same blog) if you need. Thank again.
    • saulius
      Thank you for sharing more details. To ease design process first you should learn pyqt design concepts and signals. Unfortunately I believe you will have to write application for your own, I do not provide such an extended support. Or try searching for related answers on http://stackoverflow.com or similar platforms.
  8. anetshia
    work! work! work! Pyqt5 python 3.6 Thank Help and same my time from PyQt5 import QtCore, QtGui, uic,QtWidgets import sys import cv2 import numpy as np import threading import time import queue running = False capture_thread = None form_class = uic.loadUiType("sample.ui")[0] q = queue.Queue() def grab(cam, queue, width, height, fps): global running capture = cv2.VideoCapture(cam) capture.set(cv2.CAP_PROP_FRAME_WIDTH, width) capture.set(cv2.CAP_PROP_FRAME_HEIGHT, height) capture.set(cv2.CAP_PROP_FPS, fps) while (running): frame = {} capture.grab() retval, img = capture.retrieve(0) frame["img"] = img if queue.qsize() < 10: queue.put(frame) else: print queue.qsize() class OwnImageWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(OwnImageWidget, self).__init__(parent) self.image = None def setImage(self, image): self.image = image sz = image.size() self.setMinimumSize(sz) self.update() def paintEvent(self, event): qp = QtGui.QPainter() qp.begin(self) if self.image: qp.drawImage(QtCore.QPoint(0, 0), self.image) qp.end() class MyWindowClass(QtWidgets.QMainWindow, form_class): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) self.setupUi(self) self.startButton.clicked.connect(self.start_clicked) self.window_width = self.ImgWidget.frameSize().width() self.window_height = self.ImgWidget.frameSize().height() self.ImgWidget = OwnImageWidget(self.ImgWidget) self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self.update_frame) self.timer.start(1) def start_clicked(self): global running running = True capture_thread.start() self.startButton.setEnabled(False) self.startButton.setText('Starting...') def update_frame(self): if not q.empty(): self.startButton.setText('Camera is live') frame = q.get() img = frame["img"] img_height, img_width, img_colors = img.shape scale_w = float(self.window_width) / float(img_width) scale_h = float(self.window_height) / float(img_height) scale = min([scale_w, scale_h]) if scale == 0: scale = 1 img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) height, width, bpc = img.shape bpl = bpc * width image = QtGui.QImage(img.data, width, height, bpl, QtGui.QImage.Format_RGB888) self.ImgWidget.setImage(image) def closeEvent(self, event): global running running = False capture_thread = threading.Thread(target=grab, args=(0, q, 1920, 1080, 30)) app = QtWidgets.QApplication(sys.argv) w = MyWindowClass(None) w.setWindowTitle('Kurokesu PyQT OpenCV USB camera test panel') w.show() app.exec_()
  9. yhidiot
    Dear saulius I am coding the CCTV using a Web Cam , the Client-Server Architecture. But when the Client has tried to connect the server , aftee a few seconds the hang happended.. If you give your e-mail, then I would give you my source files. thank you for your kindness!.
    • saulius
      Hi, I can take a look, but can't promise it will help. Please share your code here (or upload to pastebin or other service)
    • lala
      @anetshia, can you please put it in some gist and share url, thanks,haseeb

Leave a reply

Your email address will not be published.