How to show webcam within tkinter canvas?

Question:

I have Tkinter GUI with a canvas that currently shows the image by default and there is a switch that will print Cam On and Cam off when switched back and forth.
What I’m trying to do is to get my webcam to stream instead of that image when I flit the switch on.

Here is the code I have for my GUI:

import tkinter
import tkinter.messagebox
import customtkinter
from PIL import Image,ImageTk


# Setting up theme of GUI
customtkinter.set_appearance_mode("Dark")  # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue")  # Themes: "blue" (standard), "green", "dark-blue"

class App(customtkinter.CTk):
    def __init__(self):
        super().__init__()

        # configure window
        self.is_on = True
        self.image = ImageTk.PhotoImage(Image.open("../data/Mars.PNG"))

        self.title("Cool Blue")
        self.geometry(f"{1200}x{635}")

        # configure grid layout (4x4)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure((2, 3), weight=0)
        self.grid_rowconfigure((0, 1, 2, 3), weight=0)

        ###################################################################
        # Create Sidebar for LED, LIghts and Camera controls
        ###################################################################
        self.lights_control = customtkinter.CTkFrame(self)
        self.lights_control.grid(row=3, column=0, rowspan = 1, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.lights_control.grid_rowconfigure(1, weight=1)

        # Camera
        self.camera_switch = customtkinter.CTkSwitch(master=self.lights_control, text="Camera", command=self.camera_switch)
        self.camera_switch.grid(row=2, column=1, pady=10, padx=20, )

        ###################################################################
        # Create canvas for RPCam live stream
        ###################################################################
        self.picam_frame = customtkinter.CTkFrame(self)
        self.picam_frame.grid(row=0, column=1, rowspan=4, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.picam_frame.grid_rowconfigure(4, weight=1)
        self.picam_canvas = customtkinter.CTkCanvas(self.picam_frame, width=1730, height=944, background="gray")
        self.picam_canvas.create_image(0, 0, image=self.image, anchor="nw")
        self.picam_canvas.pack()

    #########################################################################
    # Camera Switch
    #########################################################################
    def camera_switch(self, event=None):

        if self.is_on:

            print("Cam on")

            self.is_on = False
        else:
            print("Cam off")

            self.is_on = True


if __name__ == "__main__":
    app = App()
    app.mainloop()

Now I did some research and found this code below that will do the trick in its own canvas but I am have not had luck to use the logic from the code below in my own code above.

# Import required Libraries
from tkinter import *
from PIL import Image, ImageTk
import cv2

# Create an instance of TKinter Window or frame
win = Tk()

# Set the size of the window
win.geometry("700x350")

# Create a Label to capture the Video frames
label =Label(win)
label.grid(row=0, column=0)
cap= cv2.VideoCapture(0)

# Define function to show frame
def show_frames():
   # Get the latest frame and convert into Image
   cv2image= cv2.cvtColor(cap.read()[1],cv2.COLOR_BGR2RGB)
   img = Image.fromarray(cv2image)
   # Convert image to PhotoImage
   imgtk = ImageTk.PhotoImage(image = img)
   label.imgtk = imgtk
   label.configure(image=imgtk)
   # Repeat after an interval to capture continiously
   label.after(20, show_frames)

show_frames()
win.mainloop()

So what I really need is help with using the logic from the code below and incorporating it into my own code above.
Can you please help me?

Here is my latest attempt to get this solved. So I was able to merge these two code based on the logic I provided above. And when I start the GUI and switch the camera button on, I can see that camera is starting since the LED light on the camera turns on and I get a windows notification that my program is starting the camera. however, it is not showing on the canvas yet.
So I think I am close but I am not sure what I’m missing.

import tkinter
import tkinter.messagebox
import customtkinter
from PIL import Image,ImageTk
import cv2


# Setting up theme of GUI
customtkinter.set_appearance_mode("Dark")  # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue")  # Themes: "blue" (standard), "green", "dark-blue"

class App(customtkinter.CTk):
    def __init__(self):
        super().__init__()

        # configure window
        self.is_on = True
        self.image = ImageTk.PhotoImage(Image.open("../data/Mars.PNG"))
        self.capture = cv2.VideoCapture(0)

        self.title("Cool Blue")
        self.geometry(f"{1200}x{635}")

        # configure grid layout (4x4)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure((2, 3), weight=0)
        self.grid_rowconfigure((0, 1, 2, 3), weight=0)

        ###################################################################
        # Create Sidebar for LED, LIghts and Camera controls
        ###################################################################
        self.lights_control = customtkinter.CTkFrame(self)
        self.lights_control.grid(row=3, column=0, rowspan = 1, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.lights_control.grid_rowconfigure(1, weight=1)

        # Camera
        self.camera_switch = customtkinter.CTkSwitch(master=self.lights_control, text="Camera", command=self.camera_switch)
        self.camera_switch.grid(row=2, column=1, pady=10, padx=20, )

        ###################################################################
        # Create canvas for RPCam live stream
        ###################################################################
        self.picam_frame = customtkinter.CTkFrame(self)
        self.picam_frame.grid(row=0, column=1, rowspan=4, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.picam_frame.grid_rowconfigure(4, weight=1)


        # self.picam_canvas = customtkinter.CTkCanvas(self.picam_frame, width=1730, height=944, background="gray")
        # self.picam_canvas.create_image(0, 0, image=self.image, anchor="nw")

        self.picam_canvas = tkinter.Canvas(self.picam_frame, width=1730, height=944)
        #self.picam_canvas.pack


    #########################################################################
    # Camera Switch
    #########################################################################
    def camera_switch(self, event=None):

        if self.is_on:
            self.update_frames()

            print("Cam on")

            self.is_on = False
        else:
            #self.close_camera()
            self.image
            print("Cam off")

            self.is_on = True


    def update_frames(self):
        # Get the current frame from the webcam
        _, frame = self.capture.read()

        # Convert the frame to a PhotoImage object
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        frame = Image.fromarray(frame)
        frame = ImageTk.PhotoImage(frame)

        # Update the canvas with the new frame
        self.picam_canvas.create_image(0, 0, image=frame, anchor="nw")
        self.picam_canvas.image = frame

        # Schedule the next update
        self.after(1000, self.update_frames)
    def close_camera(self):
        self.capture.release()

if __name__ == "__main__":
    app = App()
    app.mainloop()

Again, if anyone has any insight I would really appreciate it.

Asked By: Slavisha84

||

Answers:

In the code you can take advantage of the variable self.is_on in the update_frame function to finish the loop. Something like this.

def update_frames(self):

    # Change the frame by the initial image and breaks the loop
    if self.is_on:
        self.picam_canvas.create_image(0, 0, image=self.image, anchor="nw")
        return
    else:
        _, frame = self.capture.read()

    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    frame = Image.fromarray(frame)
    frame = ImageTk.PhotoImage(frame)

    self.picam_canvas.create_image(0, 0, image=frame, anchor="nw")
    self.picam_canvas.image = frame

    self.picam_canvas.after(1, self.update_frames)

But this gave an error because when you turn it back on, the video capture didn’t start. So I modified some lines in the camera_switch function.

def camera_switch(self, event=None):
    if self.is_on:
        self.capture = cv2.VideoCapture(0)
        print("Cam on")
        self.is_on = False
        self.update_frames()
    else:
        self.close_camera()
        self.image
        print("Cam off")

        self.is_on = True

The line self.capture = cv2.VideoCapture(0) was at the beginning but I removed it and put it here so that it initializes the video capture when the button is pressed

Answered By: Josephdavid07
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.