PyQt+Opencv-python多线程显示摄像头信息至QLabel,摄像头显示区域自由拉伸尺寸

文章目录

  • 前言
  • 效果
  • 资源下载
  • 核心代码
    • 编程思路
    • Camera.py
    • Widget.py


前言

  通过PyQt与Opencv-python实现多线程显示摄像头信息至QLabel,可以同时拉伸窗口,摄像头显示区域自适应拉伸区域。
  与Qt+Opencv实现同样功能的代码思路一致,仅仅是通过python语言实现。

  工程环境
  (1)win10
  (2)Anaconda3管理python虚拟环境。
  (3)cmd打开虚拟环境通过pip安装以下库

pip install pyqt5
pip install pyqt5-tools
pip install opencv-python

  (4)PC机外接一个USB摄像头,或者笔记本自带摄像头


效果

实验效果如下图所示。


资源下载

本次实验全部代码请至https://download.csdn.net/download/wang_chao118/86511419下载。


核心代码

编程思路

  定义一个摄像头类Camera,在该类中实现摄像头相关操作,将摄像头相关的属性、方法放至一个子线程,与显示GUI的主线程区别开,这样做的好处是,后面如果在摄像头显示图片的基础上对图片做一些诸如“目标检测”等耗时的操作时,GUI主线程显示不至于卡顿。
  再定义一个PyQt界面类Widget,在Widget类的构造函数中实例化一个Camera对象,将Camera对象产生的图像信号与Widget类中的图像显示槽函数进行连接,进而实现摄像头视频流的连续显示。

Camera.py

self.thread = QThread()创建一个名称为threadQThread
self.thread = QThread()Camera类中的所有属性及方法全部转移至thread中去

sendPicture = pyqtSignal(QImage)定义一个pyqt信号,发出信号的类型为QIamge,信号名称为sendPicture
此处的发出的信号QIamge为定时器到时间时从摄像头中读出的图像数据

在Camera构造函数中实例化一个QTimer类,当QTimer到设定的事件时,调用槽函数self.display,在槽函数self.display进行摄像头信号的读取工作以及一些耗时的图像操作(本次实验省略耗时操作,以time.sleep(0.5)替代,可以将注释取消看效果)

from PyQt5.Qt import *
from PyQt5 import QtGui
from PyQt5.QtCore import *
import cv2
import time


class Camera(QObject):
    sendPicture = pyqtSignal(QImage)

    def __init__(self, parent=None):
        super(Camera, self).__init__(parent)
        self.thread = QThread()
        self.moveToThread(self.thread)
        self.timer = QTimer()
        self.init_timer()
        self.cap = cv2.VideoCapture()
        self.camera_num = 0

    def init_timer(self):
        self.timer.setInterval(30)
        self.timer.timeout.connect(self.display)

    def set_cam_number(self, n):
        self.camera_num = n

    def open_camera(self):
        print("in open_camera")
        self.cap.set(4, 480)
        self.cap.set(3, 640)
        self.cap.open(self.camera_num)
        self.timer.start()
        self.thread.start()

    def close_camera(self):
        self.cap.release()

    def display(self):
        flag, image = self.cap.read()
        # time.sleep(0.5) # 耗时操作
        showImage = QtGui.QImage(image.data, image.shape[1], image.shape[0],
                                 QtGui.QImage.Format_RGB888).rgbSwapped()
        self.sendPicture.emit(showImage)

Widget.py

  在Widget类的构造函数中实例化一个Camera类,并将Camera类发出的QImage信号与本类中的槽函数receive(self, img)连接,槽函数receive(self, img)是将Camera类发送过来的图像数据显示至Ui_Widget类中的self.label,且按Camera类中定时器的频率刷新。

self.camera_thread.sendPicture[QImage].connect(self.receive)这里要注意PyQt中信号与槽连接的写法与Qt还是大相径庭的。
Pyqt5的信号与槽的具体写法请参考此处https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html

  槽函数receive(self, img)中的img_height = self.label_2.height()不断更新图像大小,使其与label大小相同。这样便做到拉伸显示窗口,使得摄像头显示区域也一并变化的效果。

from UI.ui_Widget import *
from Camera import *


class Widget(QWidget, Ui_Widget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.setupUi(self)
        self.camera_thread = Camera()
        self.camera_thread.set_cam_number(0)
        # 连接Camera的sendPicture信号与receive函数进行展示
        self.camera_thread.sendPicture[QImage].connect(self.receive)
        self.camera_thread.open_camera()

    def receive(self, img):
        img_height = self.label_2.height()
        img_width = self.label_2.width()
        new_img = img.scaled(QSize(img_width, img_height))
        self.label_2.setPixmap(QPixmap.fromImage(new_img))

你可能感兴趣的:(python,PyQt,opencv,python,pyqt)