我试图通过将opencv组件集成到程序中,在Windows 8上使用python 3.6.4 64位中的tkinter构建GUI。我可以播放视频,但是有明显的闪烁。也就是说,与原生tkinter背景颜色相同的屏幕每秒会短暂地显示几次。我测试了几台效果相似的相机,并通过原生视频播放软件仔细检查了相机是否正常工作。这是我的代码:
from tkinter import *
from PIL import Image, ImageTk
import cv2
import threading
cap = cv2.VideoCapture(0)
root = Tk()
def videoLoop():
global root
global cap
vidLabel = None
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
frame = ImageTk.PhotoImage(frame)
if vidLabel: vidLabel.configure(image=frame)
else:
vidLabel = Label(root, image=frame, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
videoThread = threading.Thread(target=videoLoop, args=())
videoThread.start()
root.mainloop()
有人能提出我可能做错的事吗?我确实听说过tkinter并不能很好地与线程配合,但那是我能想到的全部内容。为了回应建议闪烁是由标签更新引起的评论,我添加了一些仍然从视频中读取并在循环内更新标签的代码,但使用在循环外部加载的图像来更新标签。闪烁然后消失,虽然(据我的理解)循环的效率和标签的更新没有改变。这是改变的videoLoop功能(闪烁消失): def videoLoop(): 全球根 vidLabel =无 cap = cv2.VideoCapture(0)
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
frame = ImageTk.PhotoImage(frame)
while True:
ret, lame = cap.read()
lame = cv2.cvtColor(lame, cv2.COLOR_BGR2RGB)
lame = Image.fromarray(lame)
lame = ImageTk.PhotoImage(lame)
if vidLabel:
vidLabel.configure(image=None)
vidLabel.configure(image=frame)
else:
vidLabel = Label(root, image=frame, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
答案 0 :(得分:1)
解决方案:确保图像配置调用先于将图像存储在标签image
属性中。
我已经设法解决了这个问题,但是我并不完全理解为什么它有效,因为我是一个python / tkinter新手。我现在将发布解决方案,并在我找到对此行为的正确解释时更新答案。我最好的猜测是,在label属性中存储图像实际上是导致它在屏幕上更新,而configure方法只是声明会附加一个图像,这会导致循环必须经过另一次迭代在获取更新映像更新语句之前。以下代码工作正常,没有闪烁:
from tkinter import *
from PIL import Image, ImageTk
import cv2
import threading
cap = cv2.VideoCapture(0)
root = Tk()
def videoLoop():
global root
global cap
vidLabel = Label(root, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
frame = ImageTk.PhotoImage(frame)
vidLabel.configure(image=frame)
vidLabel.image = frame
videoThread = threading.Thread(target=videoLoop, args=())
videoThread.start()
root.mainloop()
答案 1 :(得分:0)
在tkinter中为了显示图像,图像需要有一个全局引用 reference that doesn't go out of scope and then gets garbage-collected,可能是由于缺少这样的引用而导致闪烁。请参阅以下具有此类图像参考的代码,并且还将if/else
与更好的结构分开:
from tkinter import *
from PIL import Image, ImageTk
import cv2
import threading
cap = cv2.VideoCapture(0)
root = Tk()
def videoLoop():
global root
global cap
vidLabel = Label(root, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
vidLabel.image = ImageTk.PhotoImage(frame)
vidLabel.configure(image=vidLabel.image)
videoThread = threading.Thread(target=videoLoop, args=())
videoThread.start()
root.mainloop()