Skip to content
Advertisement

Record Video Button in tkinter GUI python

I’m pretty new to python and espcially tkinter and opencv.

I’ve got someway (a little way) to creating a gui that will eventually control a microscope and ai. But I’ve hit a stumbling block, trying to record the video that is displayed within the gui, I think its to do with the video feed already been captured in the display, but I can’t find a way around it. It all works fine until I hit record then it crashes and i get the error: open VIDEOIO(V4L2:/dev/video0): can’t open camera by index.

Apologies for the long code but I’ve cut it down to as much as I think possible.

The problem is in the root.recbtn and def rec sections.

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

Thanks!

Advertisement

Answer

This answer is similar to @Art’s answer but I removed the after_id and queue.

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

It uses the boolean flag running instead of using after_id. Also instead of storing the images in a queue then showing it, I only keep the last image. That way it can run in real time on my computer. Don’t worry all of the frames are still being stored in the video file.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement