python - tkinter + OpenCV WebCam 非常慢的视频流
问题描述
我第一次使用 tkinter 和 opencv 并成功为我的项目构建了 GUI,但是,我无法弄清楚为什么我的视频流更新如此缓慢。我抓帧的速度非常快,但屏幕上的更新似乎呈指数级变慢。当我第一次启动程序时,我看到大约 30 秒的延迟,但它最终会减慢到停止。我正在连接三个摄像头,但一次只显示一个。摄像机全部显示并且选择按钮起作用。我唯一的问题是显示刷新率。
这是在树莓派 pi4 上的 Python3.7 中运行的。我可以通过网络浏览器连接到相机,它似乎没有延迟。
我一直在寻找答案,但似乎找不到任何有帮助的东西。任何人都可以提供一些帮助吗?
这是我的程序(我删除了不相关的代码):
#!/usr/bin/env python3
import time
from tkinter import *
import cv2
from PIL import Image, ImageTk
#GUI
class robotGUI:
def __init__(self):
self.selectedCam = "front"
self.window = Tk()
#Setup the window to fit the Raspberry Pi Touch Display = 800x400 and align top left
self.window.geometry("800x480+0+0")
self.window.overrideredirect(True)
self.window.fullScreenState = False
#Create Frame for Video Window
self.videoFrame = Frame(self.window, relief=SUNKEN, bd=2)
self.videoFrame.place(x=0, y=0, height=457, width=650)
#Create the Video Window
self.video = Label(self.videoFrame, bd=0, relief=FLAT, width=644, height=451)
self.video.place(x=0, y=0)
self.vid = VideoCapture()
self.camUpdateFreq = 250
self.updateCams()
#Create the Button Frame
self.buttonFrame = Frame(self.window, relief=FLAT)
self.buttonFrame.place(x=651, y=0, height=457, width=149)
#Create Buttons
#Select Front Camera Button
self.frontCamButton = Button(self.buttonFrame, text="Front Camera", command=lambda: self.selectCam("front"))
self.frontCamButton.place(x=24, y=50, height=30, width=100)
#Select Boom Camera Button
self.boomCamButton = Button(self.buttonFrame, text="Boom Camera", command=lambda: self.selectCam("boom"))
self.boomCamButton.place(x=24, y=130, height=30, width=100)
#Select Rear Camera Button
self.rearCamButton = Button(self.buttonFrame, text="Rear Camera", command=lambda: self.selectCam("rear"))
self.rearCamButton.place(x=24, y=210, height=30, width=100)
#Close Button
self.exitButton = Button(self.buttonFrame, text="Close", command=self.window.destroy)
self.exitButton.place(x=24, y=400, height=30, width=100)
#Start the main loop for the gui
self.window.mainloop()
def selectCam(self, cam):
if (cam.lower() == "front"):
self.selectedCam = "front"
self.statusBarLeft['text'] = "Front Camera Selected"
elif (cam.lower() == "boom"):
self.selectedCam = "boom"
self.statusBarLeft['text'] = "Boom Camera Selected"
elif (cam.lower() == "rear"):
self.selectedCam = "rear"
self.statusBarLeft['text'] = "Rear Camera Selected"
def updateCams(self):
#Get a frame from the selected camera
ret, frame = self.vid.get_frame(self.selectedCam)
if ret:
imageCV2 = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = Image.fromarray(imageCV2)
imgPhoto = ImageTk.PhotoImage(image=img)
self.video.imgPhoto = imgPhoto
self.video.configure(image=imgPhoto)
self.window.after(self.camUpdateFreq, self.updateCams)
#Video Camera Class
class VideoCapture:
def __init__(self):
#Define Locals
FrontCameraAddress = "rtsp://admin:password@192.168.5.20:8554/12"
BoomCameraAddress = "rtsp://admin:password@192.168.5.21:8554/12"
RearCameraAddress = "rtsp://admin:password@192.168.5.22:8554/12"
#Open Front Video Camera Source
self.vidFront = cv2.VideoCapture(FrontCameraAddress)
self.vidBoom = cv2.VideoCapture(BoomCameraAddress)
self.vidRear = cv2.VideoCapture(RearCameraAddress)
#Verify that the Camera Streams Opened
if not self.vidFront.isOpened():
raise ValueError("Unable to open video source to Front Camera")
if not self.vidBoom.isOpened():
raise ValueError("Unable to open video source to Boom Camera")
if not self.vidRear.isOpened():
raise ValueError("Unable to open video source to Rear Camera")
#Get One Frame from the Selected Camera
def get_frame(self, camera="front"):
#Attempt to Get Front Camera Frame
if (camera.lower() == "front"):
#If Stream Still Open Return a Frame
if self.vidFront.isOpened():
ret, frame = self.vidFront.read()
if ret:
#Return a boolean success flag and the current frame converted to BGR
return (ret, frame)
else:
return (ret, None)
else:
return (ret, None)
#Attempt to Get Boom Camera Frame
elif (camera.lower() == "boom"):
#If Stream Still Open Return a Frame
if self.vidBoom.isOpened():
ret, frame = self.vidBoom.read()
if ret:
#Return a boolean success flag and the current frame converted to BGR
return (ret, frame)
else:
return (ret, None)
else:
return (ret, None)
#Attempt to Get Rear Camera Frame
elif (camera.lower() == "rear"):
#If Stream Still Open Return a Frame
if self.vidRear.isOpened():
ret, frame = self.vidRear.read()
if ret:
#Return a boolean success flag and the current frame converted to BGR
return (ret, frame)
else:
return (ret, None)
else:
return (ret, None)
else:
return (False, None)
#Release the video sources when the object is destroyed
def __del__(self):
if self.vidFront.isOpened():
self.vidFront.release()
if self.vidBoom.isOpened():
self.vidBoom.release()
if self.vidRear.isOpened():
self.vidRear.release()
#Main Routine - Only run if called from main program instance
if __name__ == '__main__':
try:
#Create GUI Object
app = robotGUI()
except Exception as e:
print("Exception: " + str(e))
finally:
print("Cleaning Up")
注意:在这个程序副本中,我每 250 毫秒更新一次,但我尝试将较小的数字降至 3 左右,但帧似乎仍然落后。有一个更好的方法吗?
注意 2:在今天更多地工作之后,我意识到 openCV 肯定会在为每个摄像头调用 cv2.VideoCapture() 函数时开始为每个摄像头缓冲帧。read() 函数似乎确实从缓冲区中拉出下一帧,这解释了为什么更新需要这么长时间以及为什么我在屏幕上看到的图像永远赶不上现实。我将测试代码更改为一次仅连接到一台相机,并在我不主动查看相机的任何时候使用 cv2.release() 函数。这使事情有了很大的改善。我还将更新函数设置为每 1 毫秒运行一次,并且我使用 grab() 函数在每个周期抓取一帧,但我只在每 10 个周期处理和显示一次,这也有所改进。如果有人有任何建议,我仍然希望消除一些滞后。
在 Web 浏览器中查看时,我的 RTSP 流显示零明显延迟。有谁知道我如何在 tkinter 中获得相同的效果?我没有嫁给openCV。
解决方案
推荐阅读
- rest - Linkedin Ads API What's the format of the `dateRange` request parameter?
- speech-recognition - 是否可以从一个帐户同时执行两个语音到文本流?
- r - 有没有办法生成一个新变量,以 R Studio 中其他两个变量中的不同文本值为条件
- python - 如何使用 Python re.split() 制作 ['key=value', 'k=v', 'k=v'] 的列表(不是字典)?
- vis.js - 如何只保留两个节点之间的边?
- java - 无法使用 java 1.8 更新 docker 容器
- python - 使用 Tableauserverclient python 和源用户名和密码刷新数据源
- javascript - array.splice() 删除错误的元素
- python - 如何在 Python 循环期间从列表中的元素设置值。数独求解器
- r - R:条形图标签的不同颜色