如何使用使用使用Vimba SDK的Allied Vision camera在tkinter中预览流媒体图像?

2024-09-30 14:37:22 发布

您现在位置:Python中文网/ 问答频道 /正文

我想使用OpenCV和相机SDK VimbaPython在tkinter框架内显示来自Allied Vision相机的图像

初始化相机的唯一可能方法是使用Python with语句:

with Vimba.get_instance() as vimba:
    cams = vimba.get_all_cameras()
    with cams[0] as camera:
        camera.get_frame()
        # Convert frame to opencv image, then use Image.fromarray and ImageTk.PhotoImage to
        # display it on the tkinter GUI

到目前为止一切正常。但我不仅仅需要一个框架。相反,我需要不断地获取帧并在屏幕上显示它们,以便它是流式的。 我发现一种方法是从tkinter标签小部件调用.after(delay, function)方法。 所以,在获得一帧后,我想调用相同的函数来获得一个新帧并再次显示它。代码如下所示:

with Vimba.get_instance() as vimba:
    cams = vimba.get_all_cameras()
    with cams[0] as camera:

        def show_frame():
            frame = camera.get_frame()
            frame = frame.as_opencv_image()
            im = Image.fromarray(frame)
            img = Image.PhotoImage(im)
            lblVideo.configure(image=img)   # this is the Tkinter Label Widget
            lblVideo.image = img

        show_frame()
        lblVideo.after(20, show_frame)

然后显示第一帧并停止,抛出一个错误,表示需要使用with语句初始化Vimba。我对Python了解不多,但当我使用.after()方法调用函数时,它似乎以语句结尾

我想知道是否可以在不结束with的情况下执行这个show_frame()函数。此外,我不能每次初始化相机,因为程序运行得非常慢。 多谢各位


Tags: 方法imagegettkinterasshowwith语句
2条回答

我试图在openCV中读取帧,并在tkinter标签中显示它们。我可以使用以下代码执行此操作:

import tkinter as tk
import cv2
from PIL import ImageTk, Image

video_path = "SAMPLE/STORED_VIDEO/PATH"

root = tk.Tk()
base_img = Image.open("PATH/TO/DEFAULT/LABLE/IMAGE")
img_obj = ImageTk.PhotoImage(base_img)
lblVideo = tk.Label(root, image=img_obj)
lblVideo.pack()
cap = cv2.VideoCapture(video_path)

if cap.isOpened():
    def show_frame():
        _, frame = cap.read()
        im = Image.fromarray(frame)
        img = ImageTk.PhotoImage(im)
        lblVideo.configure(image=img)
        lblVideo.image = img
        lblVideo.after(1, show_frame)  # Need to create callback here   
    show_frame()
root.mainloop()

虽然它不包含with语句,但您可以尝试替换show_frame函数本身内部的after()回调

您需要使用线程来运行捕获代码,并通过queue传递读取的帧。然后主tkinter应用程序读取queue,并使用.after()定期显示帧

以下是基于您发布的代码的示例:

import threading
from queue import SimpleQueue
import tkinter as tk
from PIL import Image, ImageTk
from vimba import Vimba

def camera_streaming(queue):
    global is_streaming
    is_streaming = True
    print("streaming started")
    with Vimba.get_instance() as vimba:
        with vimba.get_all_cameras()[0] as camera:
            while is_streaming:
                frame = camera.get_frame()
                frame = frame.as_opencv_image()
                im = Image.fromarray(frame)
                img = ImageTk.PhotoImage(im)
                queue.put(img) # put the capture image into queue
    print("streaming stopped")

def start_streaming():
    start_btn["state"] = "disabled" # disable start button to avoid running the threaded task more than once
    stop_btn["state"] = "normal"    # enable stop button to allow user to stop the threaded task
    show_streaming()
    threading.Thread(target=camera_streaming, args=(queue,), daemon=True).start()

def stop_streaming():
    global is_streaming, after_id
    is_streaming = False  # terminate the streaming thread
    if after_id:
        lblVideo.after_cancel(after_id) # cancel the showing task
        after_id = None
    stop_btn["state"] = "disabled" # disable stop button
    start_btn["state"] = "normal"  # enable start button

# periodical task to show frames in queue
def show_streaming():
    global after_id
    if not queue.empty():
        image = queue.get()
        lblVideo.config(image=image)
        lblVideo.image = image
    after_id = lblVideo.after(20, show_streaming)

queue = SimpleQueue() # queue for video frames
after_id = None

root = tk.Tk()

lblVideo = tk.Label(root, image=tk.PhotoImage(), width=640, height=480)
lblVideo.grid(row=0, column=0, columnspan=2)

start_btn = tk.Button(root, text="Start", width=10, command=start_streaming)
start_btn.grid(row=1, column=0)

stop_btn = tk.Button(root, text="Stop", width=10, command=stop_streaming, state="disabled")
stop_btn.grid(row=1, column=1)

root.mainloop()

请注意,我没有安装摄像头和SDK,上面的代码可能不适合您。我只是演示如何使用线程、队列和.after()

下面是一个测试vimba模块(另存为vimba.py),我使用OpenCV和网络摄像头来模拟VimbaPython模块:

import cv2

class Frame:
    def __init__(self, frame):
        self.frame = frame

    def as_opencv_image(self):
        return self.frame

class Camera:
    def __init__(self, cam_id=0):
        self.cap = cv2.VideoCapture(cam_id, cv2.CAP_DSHOW)

    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.cap.release()
        return self

    def get_frame(self):
        ret, frame = self.cap.read()
        if ret:
            return Frame(frame)

class Vimba:
    _instance = None
    
    @classmethod
    def get_instance(self):
        if self._instance is None:
            self._instance = Vimba()
        return self._instance

    def __enter__(self):
        return self

    def __exit__(self, *args):
        return self

    def get_all_cameras(self):
        return (Camera(),)

相关问题 更多 >