【问题标题】:PyQt5 Paint Circle Over Video in QLabelPyQt5 在 QLabel 中的视频上绘制圆圈
【发布时间】:2020-02-01 23:19:23
【问题描述】:

我想在按下鼠标时在光标位置的视频上绘制一个圆圈。视频在 MainWindow 中的 QLabel 对象中播放。我正在使用 OpenCV 以 10 fps 从网络摄像头读取帧。我将帧转换为 QPixmap 并在 QLabel 对象中显示它们 (self.vidWindow)。

在下面的代码中,当 MainWindow 启动时立即绘制圆圈(不是我想要的),然后被视频流覆盖。文本显示在掩码 Qlabel 对象中,并在按下鼠标按钮时在 MainWindow 中打印一条消息。

我可以在 QLabel 对象中画一个圆吗?如果是这样,我应该使用掩码 QLabel 对象还是有办法直接覆盖self.vidWindow 中的视频?

在代码的最小化版本中,视频显示,但是当我尝试绘制椭圆时触发错误。

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_())

【问题讨论】:

    标签: python video pyqt qpainter qlabel


    【解决方案1】:

    虽然 QPainter 用于绘制小部件,但它不适用于这种情况,因为它将其子级下方的“MainWindow”绘制为 QLabels。至少有两种可能的解决方案:

    • 创建自定义 QLabel 并检测点击并绘制圆圈,

    • 创建一个 QLabel,显示一个 QPixmap,该 QPixmap 具有圆圈并根据鼠标信息移动它。

    在这种情况下,我将实现第二种方法:

    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_())
    

    【讨论】:

    • 效果很好!我同意@s-nick 的观点,即自定义 QLabel 会很有趣,但我认为这个解决方案可能更容易实现并且工作得非常好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-25
    • 2014-04-11
    • 2012-09-19
    • 1970-01-01
    相关资源
    最近更新 更多