在GTK中线程化OpenCV视频并保持事件?

fiei3ece  于 2023-10-24  发布在  其他
关注(0)|答案(2)|浏览(150)

我正试图将我的摄像头馈送到GTK窗口中,并且我想保持按钮按下事件和运动通知事件的工作。
我已经找到了如何通过刷新Gtk中的图像来获取视频。图像,在GLib中。idle_add,尝试使用线程。线程,尝试使用GLib.timeout_add,但循环仍然阻止事件。我还尝试在虚拟环境中使用OpenCV无头。
标签:https://pygobject.readthedocs.io/en/latest/guide/threading.html
我不明白什么?有办法解决吗?
下面是我的(简化)代码:

import cv2

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gtk, GdkPixbuf
#import threading

vdo_url="http://my_cam/vdo.mjpg" # simplified, without security

class Cam_GTK(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Cam GTK")

        self.capture = cv2.VideoCapture(vdo_url)

        self.video = Gtk.Image.new()
        self.video.connect("button-press-event", self.on_video_clicked )
        self.video.connect("motion-notify-event", self.on_video_hover )
        # Also tried with event=Gtk.EventBox.new(), event.add(self.video), then "connect"...
    
        page_box = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=1 )
        page_box.pack_start( self.video, True, True, 0 )
        self.add(page_box)
    
        GLib.idle_add(self.show_frame)
        #GLib.timeout_add(100, self.show_frame)
        #tried to Thread(target=self.show_frame) w/out loop
    
        self.connect("destroy", Gtk.main_quit)
        self.show_all()
    
    def on_video_hover( self, event, result ):
        print("video hover")
    def on_video_clicked( self, event, button ):
        print("video clicked")
    
    def show_frame(self):
        ret, frame = self.capture.read()
        #tried with a while
        if ret:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            pb = GdkPixbuf.Pixbuf.new_from_data(frame.tobytes(),
                                        GdkPixbuf.Colorspace.RGB,
                                        False,
                                        8,
                                        frame.shape[1],
                                        frame.shape[0],
                                        frame.shape[2]*frame.shape[1])
            self.video.set_from_pixbuf( pb.copy() )
    
        return True #tried changed to False to stop loop

cam = Cam_GTK()
Gtk.main()
jyztefdp

jyztefdp1#

我回答自己:我找到了一个工作解决方案,耦合多个用途:
1.线程化非阻塞GLib.idle_add
1.使用Gtk.EventBox分隔事件调用
下面是工作代码(我认为它可能对有同样麻烦的人有用):

import cv2
from time import sleep
import gi

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, GdkPixbuf, Gdk, GObject

from threading import Thread

vdo_url="http://my_cam/vdo.mjpg" # simplified, without security

class Cam_GTK(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Cam GTK")

        self.capture = cv2.VideoCapture(vdo_url)

        self.video = Gtk.Image.new()

        refresh_vdo = Thread(target = self.refresh_video)
        refresh_vdo.daemon= True
        refresh_vdo.start()

        self.event_vdo = Gtk.EventBox.new() #This gives the new events processing for signals
        self.event_vdo.add(self.video)
        self.event_vdo.set_above_child(True)

        self.event_vdo.connect("button-press-event", self.on_video_clicked )
        self.event_vdo.connect("motion-notify-event", self.on_video_hover )

        page_box = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=1 )
        page_box.pack_start( self.event_vdo, True, True, 0 )
        self.add(page_box)

        self.connect("destroy", Gtk.main_quit)
        self.show_all()

    def on_video_clicked( self, event, button ):
        print("clicked")

    def on_video_hover(self, widget, event):
        print("hover")

    def refresh_video( self ):
        while not self.done:
            GLib.idle_add(self.show_frame)
            sleep(0.1) #Wait for Gtk.Image refresh (I guess)

    def show_frame(self):
        ret, frame = self.capture.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        pb = GdkPixbuf.Pixbuf.new_from_data(frame.tobytes(),
                                        GdkPixbuf.Colorspace.RGB,
                                        False,
                                        8,
                                        frame.shape[1],
                                        frame.shape[0],
                                        frame.shape[2]*frame.shape[1])
        self.video.set_from_pixbuf(pb.copy())
        return False #Important to be False not to block

camera = Cam_GTK()
Gtk.main()
zfycwa2u

zfycwa2u2#

capture.read()阻塞直到有新的帧可用。这至少会将你的事件处理循环限制到视频源可以提供的任何帧速率。你不希望这样。
您可能希望生成一个线程,该线程在循环中执行capture.read(),然后将新读取的帧发送到GUI,这将GUI事件处理与阅读视频完全分离。
您还必须使用一些 * 消息传递 * 的风格。任何GUI函数的直接调用都可能会阻塞,因为它们可能会隐式地发布一些事件并等待它被处理.如果您当前执行的函数是由同一个事件循环执行的,那么就不会发生这种情况,该事件循环仍然在忙碌该函数,所以它不能做其他事情,但是你的API调用需要它来完成.所以这就是为什么它会阻塞。这对Qt(信号和插槽)是正确的,对GTK也是如此。
随机搜索结果,看起来很有希望:Signals and slots in GTK

相关问题