tkinter GUI python中的录制视频按钮

2024-09-27 21:25:16 发布

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

我对python非常陌生,尤其是tkinter和opencv

我已经找到了一些方法(一点方法)来创建一个gui,它最终将控制显微镜和人工智能。但我遇到了一个绊脚石,试图录制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()

谢谢


Tags: andthetofromimageimporttkinterdef
2条回答

这个答案与@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()

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

不能将无限while循环与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()

快速解释:

  • 在新线程中启动record函数
  • 使用队列存储帧
  • 然后使用[widget].after定期更新标签
  • 要停止录制,请使用[widget].after_cancel(after_id)(在使用.after方法时返回_id之后),并将正在运行的变量设置为False以停止循环

相关问题 更多 >

    热门问题