繁体   English   中英

tkinter GUI 中的录制视频按钮 python

[英]Record Video Button in tkinter GUI python

我对 python 很陌生,尤其是 tkinter 和 opencv。

我有办法(有点方法)创建一个最终可以控制显微镜和人工智能的图形用户界面。 但是我遇到了一个绊脚石,试图录制在 gui 中显示的视频,我认为它与已经在显示器中捕获的视频源有关,但我找不到解决方法。 一切正常,直到我点击记录然后它崩溃并且我收到错误:打开 VIDEOIO(V4L2:/dev/video0): 无法按索引打开相机。

很抱歉代码太长了,但我已经尽可能地减少了代码。

问题出在 root.recbtn 和 def rec 部分。

import cv2
import tkinter as tk
import multiprocessing
from tkinter import *
from PIL import Image,ImageTk
from datetime import datetime
from tkinter import messagebox, filedialog

e = multiprocessing.Event()
p = None

# Defining CreateWidgets() function to create necessary tkinter widgets
def createwidgets():

    root.cameraLabel = Label(root, bg="gray25", borderwidth=3, relief="ridge")
    root.cameraLabel.grid(row=2, column=1, padx=10, pady=10, columnspan=3)

    root.browseButton = Button(root, bg="gray25", width=10, text="BROWSE", command=destBrowse)
    root.browseButton.grid(row=1, column=1, padx=10, pady=10)

    root.recbtn = Button(root, bg="gray25", width=10, text="Record", command=rec)
    root.recbtn.grid(row=1, column=5, padx=10, pady=10)

    root.saveLocationEntry = Entry(root, width=55, textvariable=destPath)
    root.saveLocationEntry.grid(row=1, column=2, padx=10, pady=10)

    # Calling ShowFeed() function
    ShowFeed()

# Defining ShowFeed() function to display webcam feed in the cameraLabel;

def ShowFeed():
    # t5  # Capturing frame by frame
    ret, frame = root.cap.read()

    if ret:
        # Flipping the frame vertically
        frame = cv2.flip(frame, 1)

        # Changing the frame color from BGR to RGB
        cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)

        # Creating an image memory from the above frame exporting array interface
        videoImg = Image.fromarray(cv2image)

        # Creating object of PhotoImage() class to display the frame
        imgtk = ImageTk.PhotoImage(image = videoImg)

        # Configuring the label to display the frame
        root.cameraLabel.configure(image=imgtk)

        # Keeping a reference
        root.cameraLabel.imgtk = imgtk

        # Calling the function after 10 milliseconds
        root.cameraLabel.after(10, ShowFeed)
    else:
        # Configuring the label to display the frame
        root.cameraLabel.configure(image='')

def destBrowse():
    # Presenting user with a pop-up for directory selection. initialdir argument is optional
    # Retrieving the user-input destination directory and storing it in destinationDirectory
    # Setting the initialdir argument is optional. SET IT TO YOUR DIRECTORY PATH
    destDirectory = filedialog.askdirectory(initialdir="YOUR DIRECTORY PATH")

    # Displaying the directory in the directory textbox
    destPath.set(destDirectory)

def rec():
    vid_name = datetime.now().strftime('%d-%m-%Y %H-%M-%S')

    # If the user has selected the destination directory, then get the directory and save it in image_path
    if destPath.get() != '':
        vid_path = destPath.get()
    # If the user has not selected any destination directory, then set the image_path to default directory
    else:
        messagebox.showerror("ERROR", "No Directory Selected!")

    # Concatenating the image_path with image_name and with .jpg extension and saving it in imgName variable
    vidName = vid_path + '/' + vid_name + ".avi"
    capture = cv2.VideoCapture(0)

    fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
    videoWriter = cv2.VideoWriter(vidName, fourcc, 30.0, (640, 480))

    while (True):

        ret, frame = capture.read()

        if ret:
            cv2.imshow('video', frame)
            videoWriter.write(frame)

        if cv2.waitKey(1) == 27:
            break

    capture.release()
    videoWriter.release()

# Creating object of tk class
root = tk.Tk()

# Creating object of class VideoCapture with webcam index
root.cap = cv2.VideoCapture(0)

# Setting width and height
width, height = 1200, 1200
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

# Setting the title, window size, background color and disabling the resizing property
root.title("Test-AI-tes")
root.geometry("1600x1024")
root.resizable(True, True)
root.configure(background = "gray18")

# Creating tkinter variables
destPath = StringVar()
imagePath = StringVar()

createwidgets()
root.mainloop()

谢谢!

这个答案类似于@Art 的答案,但我删除了after_idqueue

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


def stop_rec():
    global running
    running = False

    start_button.config(state="normal")
    stop_button.config(state="disabled")

def start_capture():
    global capture, last_frame

    capture = cv2.VideoCapture(0)
    
    fourcc = cv2.VideoWriter_fourcc("X", "V", "I", "D")
    video_writer = cv2.VideoWriter(r"sample.avi", fourcc, 30.0, (640, 480))

    while running:
        rect, frame =  capture.read()

        if rect:
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
            last_frame = Image.fromarray(cv2image)
            video_writer.write(frame)

    capture.release()
    video_writer.release()

def update_frame():
    if last_frame is not None:
        tk_img = ImageTk.PhotoImage(master=video_label, image=last_frame)
        video_label.config(image=tk_img)
        video_label.tk_img = tk_img

    if running:
        root.after(10, update_frame)

def start_rec():
    global running

    running = True
    thread = threading.Thread(target=start_capture, daemon=True)
    thread.start()
    update_frame()

    start_button.config(state="disabled")
    stop_button.config(state="normal")

def closeWindow():
    stop_rec()
    root.destroy()


running = False
after_id = None
last_frame = None

root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", closeWindow)

video_label = tk.Label()
video_label.pack(expand=True, fill="both")

start_button = tk.Button(text="Start", command=start_rec)
start_button.pack()
stop_button = tk.Button(text="Stop", command=stop_rec, state="disabled")
stop_button.pack()

root.mainloop()

它使用 boolean 标志running而不是使用after_id 此外,我没有将图像存储在队列中然后显示它,而是只保留最后一张图像。 这样它就可以在我的电脑上实时运行。 不要担心所有的帧仍然存储在视频文件中。

您不能在 GUI 循环的同时进行无限循环。 每当您要完成 IO 操作时,您应该改用线程。

示例代码:

import cv2
import threading
import tkinter as tk
from PIL import Image, ImageTk
from queue import Queue

def stop_rec():
    global running, after_id
    running = False

    if after_id:
        root.after_cancel(after_id)
        after_id = None
        
    with frame_queue.mutex:
        frame_queue.queue.clear()

def start_capture():
    global capture

    capture = cv2.VideoCapture(0)
    
    fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
    video_writer = cv2.VideoWriter(r"sample.avi", fourcc, 30.0, (640, 480))

    while running:
        
        rect, frame =  capture.read()

        if rect:
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
            videoImg = Image.fromarray(cv2image)
            # current_frame = ImageTk.PhotoImage(image = videoImg)
            frame_queue.put(videoImg)
            video_writer.write(frame)
            
    capture.release()
    video_writer.release()

def update_frame():
    global after_id

    if not frame_queue.empty():
        video_label.image_frame = ImageTk.PhotoImage(frame_queue.get_nowait())
        video_label.config(image=video_label.image_frame)
    
    after_id = root.after(10, update_frame)

def start_rec():
    global running

    stop_rec()

    running = True
    thread = threading.Thread(target=start_capture, daemon=True)
    thread.start()
    update_frame()


def closeWindow():
    stop_rec()
    root.destroy()


running = False
after_id = None
frame_queue = Queue()

root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", closeWindow)

video_label = tk.Label()
video_label.pack(expand=True, fill="both")

tk.Button(text="Start", command=start_rec).pack()
tk.Button(text="stop", command=stop_rec).pack()

root.mainloop()

快速解释:

  • 在新线程中开始记录 function。
  • 使用队列来存储帧。
  • 然后使用[widget].after .after 定期更新 label。
  • 要停止记录,请使用[widget].after_cancel(after_id) (使用.after方法时返回 after_id)并将运行变量设置为False以停止循环。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM