通过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类中的图像显示槽函数进行连接,进而实现摄像头视频流的连续显示。
self.thread = QThread()
创建一个名称为thread
的QThread
类
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
类的构造函数中实例化一个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))