opencv-tkinter集成中的 Flink 视频

cl25kdpy  于 2023-05-07  发布在  Flink
关注(0)|答案(3)|浏览(108)

我试图通过将opencv组件集成到程序中,在Windows 8上使用python 3.6.4 64位中的tkinter构建一个GUI。我可以让视频播放,但有明显的 Flink 。也就是说,与原生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并不总是很好地使用线程,但这就是我所能想到的。为了回应一条建议 Flink 是由标签更新引起的评论,我添加了一些代码,这些代码仍然从视频中读取并更新循环内的标签,但使用在循环外加载的图像来更新标签。然后 Flink 消失了,尽管(据我所知)循环的效率和标签的更新没有改变。以下是更改后的videoLoop函数( Flink 消失):def videoLoop():global root vidLabel = None 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)
zd287kbt

zd287kbt1#

解决方案:确保图像配置调用在将图像存储在标签image属性中之前。我已经设法解决了这个问题,但我不完全理解为什么它的工作原理,因为我是一个python/tkinter新手。我现在将发布解决方案,并将更新答案,当我设法找到一个适当的解释,这种行为。我最好的猜测是,将图像存储在label属性中实际上是导致它在屏幕上更新的原因,而configure方法只是声明将附加一个图像,这导致循环在进入update image update语句之前必须经过另一次迭代。下面的代码工作正常,没有 Flink :

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()
xkrw2x1b

xkrw2x1b2#

在tkinter中,为了显示图像,图像需要有一个全局引用 * 引用不会超出范围,然后被垃圾收集 *,也许 Flink 是由于缺乏这样的引用引起的。请参阅下面的代码,它对图像有这样的引用,并且用更好的结构抛弃了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()
hec6srdp

hec6srdp3#

@anvoice解决方案没有解决我的问题,我仍然有一些 Flink 发生。我怀疑这个问题是因为配置标签的调用是在保存图像的同一个线程中完成的。因此,我所做的是将图像捕获/转换和图像更新分开。我在使用.after()方法更新gui时,将cv 2/pillow更新保存在自己的线程中。别再 Flink 了!

def cv2Thread():
    count = 0
    while True:
        
        ret, frame = self.capture.read()
        
        #Triggers the label configure update
        if count == 1:
            self.start_display()

        if ret:          
            count+= 1      
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.frame = frame

            # Convert the OpenCV image to a PIL image
            self.pil_image = Image.fromarray(self.frame)      

            # Display the image on the canvas
            self.photo = ImageTk.PhotoImage(image=self.pil_image)
            
        cv2.waitKey(1)        

def start_display(self):
    self.cam_label.configure(image=self.photo) 
    self.master.after(10, func = self.start_display)

相关问题