在之前的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() 循环一段时间以等待下一帧以避免处理同一帧。