PyQt5 Paint Circle Over Video in QLabel - python

I want to draw a circle that displays over a video at the cursor location when pressing the mouse. The video is playing in a QLabel object that is in a MainWindow. I’m using OpenCV to read frames from the webcam at 10 fps. I’m converting the frames to QPixmap and displaying them in the QLabel object (self.vidWindow).
In the code below, the circle is painted immediately when the MainWindow is launched (not what I want) and is then covered up by the video stream. Text displays in the mask Qlabel object and a message is printed in the MainWindow when the mouse button is pressed.
Can I draw a circle in a QLabel object? If so, should I use the mask QLabel object or is there a way to overlay directly over the video in the self.vidWindow?
In the minimalized version of the code, the video displays, but an error is triggered when I try to draw the ellipse.
import sys, cv2
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QFont
from PyQt5.QtCore import QTimer, Qt, QCoreApplication
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.statusBar().showMessage('Ready')
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle('Statusbar')
self.vidWindow = QLabel(self)
self.vidWindow.setGeometry(20, 20, 640, 480)
self.maskWindow = QLabel(self)
self.maskWindow.setGeometry(20, 20, 640, 480)
self.maskWindow.setStyleSheet('background-color: rgba(0,0,0,0%)')
font = QFont()
font.setPointSize(18)
font.setBold(True)
font.setWeight(75)
self.maskWindow.setFont(font)
self.maskWindow.setText('Message is on the mask Qlabel object')
self.msgLabel = QLabel(self)
self.msgLabel.setGeometry(675, 300, 100, 20)
self.cap = cv2.VideoCapture(0)
self.pix = QImage()
self.timer = QTimer()
self.frame_rate = 5
self.show()
self.start()
def nextFrameSlot(self):
ret, frame = self.cap.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame,frame.shape[1], frame.shape[0], QImage.Format_RGB888)
img = img.scaled(640, 480, Qt.KeepAspectRatio)
self.pix = QPixmap.fromImage(img)
self.vidWindow.setPixmap(self.pix)
def mousePressEvent(self, QMouseEvent):
self.msgLabel.setText('Mouse Clicked!')
def paintEvent(self, QMouseEvent):
e = QMouseEvent
painter = QPainter(self)
painter.setPen(QPen(Qt.green, 4, Qt.SolidLine))
painter.drawEllipse(e.x(), e.y(), 100)
def start(self):
rate = int(1000.0 / self.frame_rate)
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.nextFrameSlot)
self.timer.start(rate)
def closeEvent(self, event):
if self.cap.isOpened():
self.cap.release()
self.vidWindow.clear()
QCoreApplication.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())

Although QPainter is used to paint a widget it will not work for this case since it paints the "MainWindow" that is below its children as the QLabels. There are at least 2 possible solutions:
Create a custom QLabel and detect the click and paint the circle,
Create a QLabel that shows a QPixmap that has the circle and move it based on the mouse information.
In this case I will implement the second method:
import sys, cv2
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QFont
from PyQt5.QtCore import QTimer, Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.statusBar().showMessage("Ready")
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle("Statusbar")
self.vidWindow = QLabel(self)
self.vidWindow.setGeometry(20, 20, 640, 480)
self.maskWindow = QLabel(self)
self.maskWindow.setGeometry(20, 20, 640, 480)
self.maskWindow.setStyleSheet("background-color: rgba(0,0,0,0%)")
font = QFont()
font.setPointSize(18)
font.setBold(True)
font.setWeight(75)
self.maskWindow.setFont(font)
self.maskWindow.setText("Message is on the mask Qlabel object")
self.msgLabel = QLabel(self)
self.msgLabel.setGeometry(675, 300, 100, 20)
self.marker_label = QLabel(self)
pixmap = QPixmap(100, 100)
pixmap.fill(Qt.transparent)
painter = QPainter(pixmap)
painter.setPen(QPen(Qt.green, 4, Qt.SolidLine))
painter.drawEllipse(pixmap.rect().adjusted(4, 4, -4, -4))
painter.end()
self.marker_label.setPixmap(pixmap)
self.marker_label.adjustSize()
self.marker_label.hide()
self.marker_label.raise_()
self.cap = cv2.VideoCapture(0)
self.timer = QTimer()
self.frame_rate = 5
self.show()
self.start()
def nextFrameSlot(self):
ret, frame = self.cap.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
img = img.scaled(640, 480, Qt.KeepAspectRatio)
pix = QPixmap.fromImage(img)
self.vidWindow.setPixmap(pix)
def mousePressEvent(self, event):
self.msgLabel.setText("Mouse Clicked!")
if self.vidWindow.rect().contains(event.pos()):
self.marker_label.move(event.pos() - self.marker_label.rect().center())
self.marker_label.show()
super().mousePressEvent(event)
def start(self):
rate = int(1000.0 / self.frame_rate)
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.nextFrameSlot)
self.timer.start(rate)
def closeEvent(self, event):
if self.cap.isOpened():
self.cap.release()
super().closeEvent(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())

Related

How to display live camera video from opencv in pyqt5? [duplicate]

Try to link PyQt and Opencv video feed, can't understand how to apply while loop for continuously streaming video. It just take a still picture.Please can anyone help to solve the problem.
PtQt=5
Python=3.6.1
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 Video'
self.left = 100
self.top = 100
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(1800, 1200)
#create a label
label = QLabel(self)
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0],
QtGui.QImage.Format_RGB888)
convertToQtFormat = QtGui.QPixmap.fromImage(convertToQtFormat)
pixmap = QPixmap(convertToQtFormat)
resizeImage = pixmap.scaled(640, 480, QtCore.Qt.KeepAspectRatio)
QApplication.processEvents()
label.setPixmap(resizeImage)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
The problem is that the function that obtains the image is executed only once and not updating the label.
The correct way is to place it inside a loop, but it will result in blocking the main window. This blocking of main window can be solved by using the QThread class and send through a signal QImage to update the label. For example:
import cv2
import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QImage, QPixmap
class Thread(QThread):
changePixmap = pyqtSignal(QImage)
def run(self):
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if ret:
# https://stackoverflow.com/a/55468544/6622587
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgbImage.shape
bytesPerLine = ch * w
convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
class App(QWidget):
def __init__(self):
super().__init__()
[...]
self.initUI()
#pyqtSlot(QImage)
def setImage(self, image):
self.label.setPixmap(QPixmap.fromImage(image))
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(1800, 1200)
# create a label
self.label = QLabel(self)
self.label.move(280, 120)
self.label.resize(640, 480)
th = Thread(self)
th.changePixmap.connect(self.setImage)
th.start()
self.show()
Updating this for PySide2 and qimage2ndarray
from PySide2.QtCore import *
from PySide2.QtGui import *
import cv2 # OpenCV
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting
# Minimal implementation...
def displayFrame():
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
label.setPixmap(QPixmap.fromImage(image))
app = QApplication([])
window = QWidget()
# OPENCV
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
# timer for getting frames
timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec_()
# See also: https://gist.github.com/bsdnoobz/8464000
Thank you Taimur Islam for the question.
Thank you eyllanesc for wonderful answering and I have modified your code little bit. I used PtQt=4 Python=2.7 and I didn't use opencv
import sys
import numpy as np
import flycapture2 as fc2
from PyQt4.QtCore import (QThread, Qt, pyqtSignal)
from PyQt4.QtGui import (QPixmap, QImage, QApplication, QWidget, QLabel)
class Thread(QThread):
changePixmap = pyqtSignal(QImage)
def __init__(self, parent=None):
QThread.__init__(self, parent=parent)
self.cameraSettings()
def run(self):
while True:
im = fc2.Image()
self.c.retrieve_buffer(im)
a = np.array(im)
rawImage = QImage(a.data, a.shape[1], a.shape[0], QImage.Format_Indexed8)
self.changePixmap.emit(rawImage)
def cameraSettings(self):
print(fc2.get_library_version())
self.c = fc2.Context()
numberCam = self.c.get_num_of_cameras()
print(numberCam)
self.c.connect(*self.c.get_camera_from_index(0))
print(self.c.get_camera_info())
m, f = self.c.get_video_mode_and_frame_rate()
print(m, f)
print(self.c.get_property_info(fc2.FRAME_RATE))
p = self.c.get_property(fc2.FRAME_RATE)
print(p)
self.c.set_property(**p)
self.c.start_capture()
class App(QWidget):
def __init__(self):
super(App,self).__init__()
self.title = 'PyQt4 Video'
self.left = 100
self.top = 100
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(800, 600)
# create a label
self.label = QLabel(self)
self.label.move(0, 0)
self.label.resize(640, 480)
th = Thread(self)
th.changePixmap.connect(lambda p: self.setPixMap(p))
th.start()
def setPixMap(self, p):
p = QPixmap.fromImage(p)
p = p.scaled(640, 480, Qt.KeepAspectRatio)
self.label.setPixmap(p)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())

Video Playback speed is faster than expected pyQt5 and Opencv

i am working on an Opencv project and when trying to add the gui for it, i wanted to use PyQt because i am familiar with it.
in this code
import cv2
import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QImage, QPixmap
class Thread(QThread):
changePixmap = pyqtSignal(QImage)
def run(self):
cap = cv2.VideoCapture("../Testing-Video.mp4")
while True:
ret, frame = cap.read()
if ret:
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgbImage.shape
bytesPerLine = ch * w
convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine,
QImage.Format_RGB888)
p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
class App(QWidget):
def __init__(self):
super().__init__()
self.left=0
self.top=0
self.width=600
self.height=800
self.initUI()
#pyqtSlot(QImage)
def setImage(self, image):
self.label.setPixmap(QPixmap.fromImage(image))
def initUI(self):
self.setWindowTitle("Testing PyQt")
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(800, 600)
# create a label
self.label = QLabel(self)
self.label.resize(640, 480)
th = Thread(self)
th.changePixmap.connect(self.setImage)
th.start()
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
which is something i didn't write myself, it's slightly adjusted from here , anyways using the Camera video capture, it works perfectly but when using any other video media, the playback is extremely fast,
should i use a timer to call the frames constantly? better than using signal and slots?

Pyqt crashes when trying to show opencv videostream

I tried code from this answer and it crashes with error Process finished with exit code -1073740771 (0xC000041D) after some time (2-10 sec.) and sometimes with 0xC0000005. It crashes immediately if I try to drag the window.
However when I put time.sleep(0.1) in run it works fine. If I use sleeps shorter than 0.1 it crashes again.
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel,QMessageBox
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, Qt
import cv2
import sys
import time
class CamThread(QThread):
changemap = pyqtSignal('QImage')
def run(self):
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while True:
ret, img_rgb = cap.read()
if ret:
self.rgb = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2RGB)
self.convert = QImage(self.rgb.data, self.rgb.shape[1], self.rgb.shape[0], QImage.Format_RGB888)
self.p = self.convert.scaled(640, 480, Qt.KeepAspectRatio)
self.changemap.emit(self.p)
#time.sleep(0.1)
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'webcam'
self.initUI()
#pyqtSlot('QImage')
def setImage(self, image):
self.label.setPixmap(QPixmap.fromImage(image))
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(100, 100, 640, 480)
self.resize(640, 480)
self.label = QLabel(self)
self.label.resize(640, 480)
thr = CamThread(self)
thr.changemap.connect(self.setImage)
thr.start()
app = QApplication(sys.argv)
win = App()
#win.setAttribute(Qt.WA_DeleteOnClose, True)
win.show()
app.exit(app.exec_())
I thought that the problem is somewhere in signals/slots but haven't been able to find anything relevant.
Windows 10
Python - 3.7
Pyqt - 5.12
OpenCV - 3.4.5.20
Fixed it using QMutex and QWaitCondition to prevent update call while main thread is already updating. Apparently, issue was in that. eyllanesc, I'm new here as you see, should I make an answer in original thread?
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel, QMessageBox
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, Qt, QMutex, QWaitCondition
import cv2
import sys
import time
class CamThread(QThread):
changemap = pyqtSignal('QImage')
def __init__(self, mutex, condition):
super().__init__()
self.mutex = mutex
self.condition = condition
def run(self):
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while True:
try:
ret, img_rgb = cap.read()
if ret:
rgb = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2RGB)
#any other image processing here
convert = QImage(rgb.data, rgb.shape[1], rgb.shape[0], QImage.Format_RGB888)
p = convert.scaled(640, 480, Qt.KeepAspectRatio)
self.changemap.emit(p)
self.condition.wait(self.mutex)
except:
print('error')
class App(QWidget):
time = 0
def __init__(self):
super().__init__()
self.title = 'webcam'
self.mutex = QMutex()
self.condition = QWaitCondition()
self.initUI()
#pyqtSlot('QImage')
def setImage(self, image):
self.mutex.lock()
try:
self.label.setPixmap(QPixmap.fromImage(image))
finally:
self.mutex.unlock()
self.condition.wakeAll()
def initUI(self):
self.mutex.lock()
self.setWindowTitle(self.title)
self.setGeometry(100, 100, 640, 480)
self.resize(640, 480)
self.label = QLabel(self)
self.label.resize(640, 480)
self.thr = CamThread(mutex = self.mutex,condition=self.condition)
self.thr.changemap.connect(self.setImage)
self.thr.start()
app = QApplication(sys.argv)
win = App()
win.show()
app.exit(app.exec_())
N.B. You still need to properly stop thread and close camera connection in this example.

Pixmap image size in Qlabel

I'm trying to get an image to fit my label entirely without using setScaledContents(True) since I'd like my ImageGrab to have the exact same dimensions as the QLabel space.
I use a PIL ImageGrab with a bbox. if I chance the parameters to a wider width and higher height it will crash the program without any errors. I have attached a picture where the Image in the Label is localed left center. I'd like it to the top left. So that it can expand down and to the right if I increased the size.
But I am curious as to why I am unable to increase the size of the bbox. (0, 0, 400, 220) works fine but (0, 0, 420, 220) will not upload the Image and closes down the GUI without any errors.
I'd like to have a ImageGrab with bbox (0, 0, 800, 700) and a QLabel with size(800, 700) so it can fit it perfectly.
class Main(QMainWindow):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setGeometry(200, 200, 1000, 700)
self.setWindowTitle('threads')
self.mainFrame = QFrame(self)
self.mainFrame.resize(1000, 650)
self.mainFrame.move(0, 50)
self.mainFrame.setStyleSheet("background-color: rbg(50, 50, 50)")
self.testButton = QPushButton("Click", self)
self.testButton.resize(500,30)
self.connect(self.testButton, SIGNAL("clicked()"), self.Capture)
self.label_ = QLabel(self.mainFrame)
self.label_.move(10, 10)
self.label_.resize(980, 630)
self.label_.setStyleSheet("background-color: rbg(150, 150, 150)")
#pyqtSlot(QImage)
def ChangeFrame(self, image):
pixmap = QPixmap.fromImage(image)
self.label_.setPixmap(pixmap)
def Capture(self):
self.thread_ = CaptureScreen()
self.connect(self.thread_, SIGNAL("ChangeFrame(QImage)"), self.ChangeFrame, Qt.QueuedConnection)
self.thread_.start()
class CaptureScreen(QThread):
pixmap = pyqtSignal(QImage)
def __init__(self, parent = None):
QThread.__init__(self)
def __del__(self):
print("?????")
self.exiting = True
self.wait()
def run(self):
while(True):
time.sleep(1/60)
img = ImageGrab.grab(bbox=(0, 0, 420, 220))
frame = ImageQt(img)
frame = QImage(frame)
self.emit( SIGNAL("ChangeFrame(QImage)"), frame)
The solution is to use layouts, and set the alignment in QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft.
I also recommend using the new connection syntax, on the other hand if you are going to inherit from QMainWindow in the constructor you must call it. And finally when you use QMainWindow you must set a central widget.
import time
from PyQt4 import QtCore, QtGui
from PIL import ImageGrab, ImageQt
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setGeometry(200, 200, 1000, 700)
self.setWindowTitle('threads')
main_widget = QtGui.QWidget()
self.setCentralWidget(main_widget)
lay = QtGui.QVBoxLayout(main_widget)
self.testButton = QtGui.QPushButton("Click")
self.testButton.setFixedHeight(30)
self.testButton.clicked.connect(self.capture)
mainFrame = QtGui.QFrame()
mainFrame.setStyleSheet("background-color: rbg(50, 50, 50)")
_lay = QtGui.QVBoxLayout(mainFrame)
_lay.setContentsMargins(0, 0, 0, 0)
self.label_ = QtGui.QLabel()
_lay.addWidget(self.label_, 0, QtCore.Qt.AlignTop|QtCore.Qt.AlignLeft)
lay.addWidget(self.testButton)
lay.addWidget(mainFrame)
#QtCore.pyqtSlot(QtGui.QImage)
def changeFrame(self, image):
pixmap = QtGui.QPixmap.fromImage(image)
self.label_.setPixmap(pixmap)
#QtCore.pyqtSlot()
def capture(self):
self.thread_ = CaptureScreen()
self.thread_.changedFrame.connect(self.changeFrame, QtCore.Qt.QueuedConnection)
self.thread_.start()
self.testButton.setDisabled(True)
class CaptureScreen(QtCore.QThread):
changedFrame = QtCore.pyqtSignal(QtGui.QImage)
def __del__(self):
print("?????")
self.exiting = True
self.quit()
self.wait()
def run(self):
while True:
time.sleep(1/60)
w, h = 420, 220
img = ImageGrab.grab(bbox=(0, 0, w, h))
frame = ImageQt.toqimage(img)
self.changedFrame.emit(QtGui.QImage(frame))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())

Webcam streamer with PyQt and Python threading, PySerial and GPIO [duplicate]

Try to link PyQt and Opencv video feed, can't understand how to apply while loop for continuously streaming video. It just take a still picture.Please can anyone help to solve the problem.
PtQt=5
Python=3.6.1
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 Video'
self.left = 100
self.top = 100
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(1800, 1200)
#create a label
label = QLabel(self)
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0],
QtGui.QImage.Format_RGB888)
convertToQtFormat = QtGui.QPixmap.fromImage(convertToQtFormat)
pixmap = QPixmap(convertToQtFormat)
resizeImage = pixmap.scaled(640, 480, QtCore.Qt.KeepAspectRatio)
QApplication.processEvents()
label.setPixmap(resizeImage)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
The problem is that the function that obtains the image is executed only once and not updating the label.
The correct way is to place it inside a loop, but it will result in blocking the main window. This blocking of main window can be solved by using the QThread class and send through a signal QImage to update the label. For example:
import cv2
import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QImage, QPixmap
class Thread(QThread):
changePixmap = pyqtSignal(QImage)
def run(self):
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if ret:
# https://stackoverflow.com/a/55468544/6622587
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgbImage.shape
bytesPerLine = ch * w
convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
class App(QWidget):
def __init__(self):
super().__init__()
[...]
self.initUI()
#pyqtSlot(QImage)
def setImage(self, image):
self.label.setPixmap(QPixmap.fromImage(image))
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(1800, 1200)
# create a label
self.label = QLabel(self)
self.label.move(280, 120)
self.label.resize(640, 480)
th = Thread(self)
th.changePixmap.connect(self.setImage)
th.start()
self.show()
Updating this for PySide2 and qimage2ndarray
from PySide2.QtCore import *
from PySide2.QtGui import *
import cv2 # OpenCV
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting
# Minimal implementation...
def displayFrame():
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
label.setPixmap(QPixmap.fromImage(image))
app = QApplication([])
window = QWidget()
# OPENCV
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
# timer for getting frames
timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec_()
# See also: https://gist.github.com/bsdnoobz/8464000
Thank you Taimur Islam for the question.
Thank you eyllanesc for wonderful answering and I have modified your code little bit. I used PtQt=4 Python=2.7 and I didn't use opencv
import sys
import numpy as np
import flycapture2 as fc2
from PyQt4.QtCore import (QThread, Qt, pyqtSignal)
from PyQt4.QtGui import (QPixmap, QImage, QApplication, QWidget, QLabel)
class Thread(QThread):
changePixmap = pyqtSignal(QImage)
def __init__(self, parent=None):
QThread.__init__(self, parent=parent)
self.cameraSettings()
def run(self):
while True:
im = fc2.Image()
self.c.retrieve_buffer(im)
a = np.array(im)
rawImage = QImage(a.data, a.shape[1], a.shape[0], QImage.Format_Indexed8)
self.changePixmap.emit(rawImage)
def cameraSettings(self):
print(fc2.get_library_version())
self.c = fc2.Context()
numberCam = self.c.get_num_of_cameras()
print(numberCam)
self.c.connect(*self.c.get_camera_from_index(0))
print(self.c.get_camera_info())
m, f = self.c.get_video_mode_and_frame_rate()
print(m, f)
print(self.c.get_property_info(fc2.FRAME_RATE))
p = self.c.get_property(fc2.FRAME_RATE)
print(p)
self.c.set_property(**p)
self.c.start_capture()
class App(QWidget):
def __init__(self):
super(App,self).__init__()
self.title = 'PyQt4 Video'
self.left = 100
self.top = 100
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(800, 600)
# create a label
self.label = QLabel(self)
self.label.move(0, 0)
self.label.resize(640, 480)
th = Thread(self)
th.changePixmap.connect(lambda p: self.setPixMap(p))
th.start()
def setPixMap(self, p):
p = QPixmap.fromImage(p)
p = p.scaled(640, 480, Qt.KeepAspectRatio)
self.label.setPixmap(p)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())

Categories

Resources