PYQT5笔记 010:视频显示+多线程

      在之前的PYQT5笔记 008 :图片读出界面程序,如果想对界面进行视频的帧进行处理然后显示,最简单的想法可能是,将多个显示内容依次写出来:

dir = r"C:\Users\Administrator\Desktop\car.jpg"
self.label.setPixmap(showImage)
time.sleep(3)
dir = r"C:\Users\Administrator\Desktop\cat.jpg"
self.label.setPixmap(showImage)

线程的解决方案

      但这样写运行的效果为,显示灰色无内容的界面,然后几秒之后直接显示cat图像,而没有car图像。解决方法是使用线程实现:

    def readframe(self):
        th = Thread(target=self.myreadframe) # 需要 from threading import *
        th.start()

    def myreadframe(self):
        dir = r"C:\Users\Administrator\Desktop\car.jpg"
        self.SetPic(dir)
        dir = r"C:\Users\Administrator\Desktop\cat.jpg"
        self.SetPic(dir)

    def SetPic(self,dir):
        pixmap = QPixmap(dir)
        self.label2.setPixmap(pixmap)
        time.sleep(3)
        print(123)

Python 标准库提供了threading,要启动一个单独的线程,需创建一个Thread实例,然后启动它。

x = threading.Thread(target=thread_function, args=(1,))
x.start()

到此位置,可以思考一下线程的停止了,如果创建了一个服务线程,主线程已经停止,但服务线程还能工作,如以下例子:

import threading
import time

def server():
    while True:
        print("服务线程运行中")
        time.sleep(1)

if __name__ == '__main__':
    ser = threading.Thread(target=server)
    ser.start()
    print('主线程开始')
    time.sleep(6)
    print('主线程结束')

使用以下设置为守护线程,设置为守护线程后,在主线程退出后,守护线程也会退出(即当pyqt推出后,守护)。

ser.daemon = True
或者
ser = threading.Thread(target=server, args=(index,), daemon=True)

这样就能正常运行了:

import threading
import time

def server():
    while True:
        print("服务线程运行中")
        time.sleep(1)

if __name__ == '__main__':
    ser = threading.Thread(target=server)
    ser.daemon = True
    ser.start()
    print('主线程开始')
    time.sleep(6)
    print('主线程结束')

还可以进行等待的设置比如ser.join(),当ser的方法运行完之后,在进行下一步的运行。

    ser = threading.Thread(target=server)
    ser.daemon = True
    ser.start()
    ser.join()
    print('主线程开始')
    time.sleep(6)
    print('主线程结束')

还可以创建多线程:

threads = list() # 用一个容器盛放线程,join时可以在启动完成之后进行join
for index in range(3):
    x = threading.Thread(target=thread_function)
    threads.append(x)
    x.start()

ThreadPoolExecutor 线程池

# submit提交
import threading
import time
import concurrent.futures
from concurrent.futures.thread import ThreadPoolExecutor


def server(a,b):
    print(a+b)
    time.sleep(1)
    return a
    # while True:
    #     print("服务线程运行中")
    #     time.sleep(1)

if __name__ == '__main__':
    # ser = threading.Thread(target=server)
    # ser.daemon = True
    # ser.start()
    # ser.join()
    with ThreadPoolExecutor(max_workers=1) as executor:
        future = executor.submit(server, 1, 2)# 返回future对象
        print(future.result()) # 获取函数的返回值 并打印
    print('主线程开始')
    time.sleep(6)
    print('主线程结束')
# map方法提交,执行可迭代的对象
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    executor.map(thread_function, range(3))# range(3)为 iterable object
    # 更多内容详见官方文档
    # 视频教程 https://www.bilibili.com/video/BV1wb4y1S7d6?
https://realpython.com/intro-to-python-threading/
https://docs.python.org/zh-cn/3/library/threading.html

继续视频帧的处理

import cv2
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from threading import *
import time


class mywindow(QWidget):
    def __init__(self):
        super().__init__()
        return self.initUI()

    def initUI(self):
        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle("showcamera")
        self.lable = QLabel("showcamera", self)
        self.lable.setGeometry(0, 0, 800, 600)
        self.lable.setScaledContents(True)
        self.show()

    def SetPic(self, img):
        self.lable.setPixmap(QPixmap.fromImage(img))

def showcamre():
    imgName, imgType = QFileDialog.getOpenFileName(None, "打开mp4", "", "*.mp4;;*.png;;All Files(*)")  
    cap = cv2.VideoCapture(imgName) # 或读取相机cap = cv2.VideoCapture(0)

    # 视频流设置 参数详见https://github.com/opencv/opencv/blob/master/modules/videoio/include/opencv2/videoio.hpp
    cap.set(cv2.CAP_PROP_FPS, 60) //帧率 帧/秒 https://www.javaroad.cn/questions/288491
    while cap.isOpened():
		success, frame = cap.read()
        if success == False:
            continue
        a = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
        mywindow.SetPic(a)
        time.sleep(0.01) # https://stackoverflow.com/questions/52068277/change-frame-rate-in-opencv-3-4-2

app = QApplication(sys.argv)
window = mywindow()
th = Thread(target=showcamre)# https://www.bbsmax.com/A/kmzLkAjKdG/
th.start()
app.exec_()

“不断清除相机的帧缓冲区” - 这是关键,我想知道为什么所有初学者教程都忽略了这一点,只是盲目地循环轮询相机并导致 CPU 利用率过高。或者,我可以只 sleep() 循环一段时间以等待下一帧以避免处理同一帧。

你可能感兴趣的:(语言学习笔记,qt,音视频,python)