How to toggle swap camera view between 2 cameras on live feed frame?

I have only 2 USB cameras that are connected to my PC (Note: highly confident that I won’t use more than 2 cameras now and in future) . My aim of this program is to capture front and rear view concurrently. And I have successfully have the working prototype of the program which allowed to view 2 live feed video at the same time .

enter image description here

I attached the sample program here which allowed live video feeds from 2 cameras concurrently at the same time.

import cv2
import tkinter as tk
from datetime import datetime
from PIL import Image, ImageTk, ImageDraw
global photo1, photo2

photo1 = None 
photo2 = None

def print_camera_info(width, height, camera_number):
    print(f"Camera {camera_number}")
    print(f"Width: {width}")
    print(f"Height: {height}")
    print("---")

def swap_camera():
    print("In swap_camera!") #debugging purpose
    global photo1, photo2
    print("After define global in swap_camera") #debugging purpose

    print("Before swap")#debugging purpose
    # Swap the frames algorithm
    tmp = photo1
    photo1 = photo2
    photo2 = tmp
    print("After Swap") #debugging purpose
    
        
def main():    
    # Open two camera devices
    front_cap = cv2.VideoCapture(0)  # Use 0 for the first camera (Front Camera)
    rear_cap = cv2.VideoCapture(1)  # Use 1 for the second camera (Rear Camera)

    # Check if the cameras opened successfully
    if not (front_cap.isOpened() and rear_cap.isOpened()):
        print("Error: Could not open cameras.")
        print("front_cap opened:", front_cap.isOpened())
        print("rear_cap opened:", rear_cap.isOpened())
        return

    # Set the desired width and height for live feed
    target_width, target_height = 320, 240

    # Set the CAP_PROP_FRAME_WIDTH and CAP_PROP_FRAME_HEIGHT properties for both cameras
    front_cap.set(cv2.CAP_PROP_FRAME_WIDTH, target_width)
    front_cap.set(cv2.CAP_PROP_FRAME_HEIGHT, target_height)

    rear_cap.set(cv2.CAP_PROP_FRAME_WIDTH, target_width)
    rear_cap.set(cv2.CAP_PROP_FRAME_HEIGHT, target_height)

    # Get the actual dimensions after setting the properties
    actual_width1 = int(front_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    actual_height1 = int(front_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    actual_width2 = int(rear_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    actual_height2 = int(rear_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    # Print camera information with the actual resized dimensions
    print_camera_info(actual_width1, actual_height1, "Front")
    print_camera_info(actual_width2, actual_height2, "Rear")

    # Create Tkinter window
    root = tk.Tk()
    root.title("Combined Cameras")

    # Create a Checkbutton for camera toggle
    camera_toggle_button = tk.Checkbutton(root, text="Toggle Camera", command=swap_camera)
    camera_toggle_button.grid(row=1, column=1, padx=10, pady=10)

    # Create label widget for date and time
    datetime_label = tk.Label(root, text="", font=("Helvetica", 14))
    datetime_label.grid(row=1, column=2, columnspan=2, pady=10)

    # Create label widgets for camera views
    label1 = tk.Label(root, text="FRONT VIEW")
    label2 = tk.Label(root, text="REAR VIEW")

    label1.grid(row=2, column=2)
    label2.grid(row=2, column=3)

    # Create image labels for camera feeds
    video_livefeedfront = tk.Label(root)
    video_livefeedrear = tk.Label(root)

    video_livefeedfront.grid(row=3, column=2, padx=10)
    video_livefeedrear.grid(row=3, column=3, padx=10)

    while True:
        # Read frames from both cameras
        ret1, frame1 = front_cap.read()
        ret2, frame2 = rear_cap.read()

        # Check if the frames were read successfully
        if not (ret1 and ret2):
            print("Error: Could not read frames from cameras.")
            break

        # Resize frames to the target dimensions
        frame_resized1 = cv2.resize(frame1, (target_width, target_height))
        frame_resized2 = cv2.resize(frame2, (target_width, target_height))

        # Convert frames to RGB format for PIL
        frame_rgb1 = cv2.cvtColor(frame_resized1, cv2.COLOR_BGR2RGB)
        frame_rgb2 = cv2.cvtColor(frame_resized2, cv2.COLOR_BGR2RGB)

        # Convert frames to PhotoImage format for Tkinter
        photo1 = ImageTk.PhotoImage(Image.fromarray(frame_rgb1))
        photo2 = ImageTk.PhotoImage(Image.fromarray(frame_rgb2))

        # Update image live videofeed
        video_livefeedfront.configure(image=photo1)
        video_livefeedfront.photo = photo1

        video_livefeedrear.configure(image=photo2)
        video_livefeedrear.photo = photo2

        # Update the Tkinter window
        root.update()

    # Release the camera devices and destroy the Tkinter window
    front_cap.release()
    rear_cap.release()
    cv2.destroyAllWindows()
   
    root.destroy()

if __name__ == "__main__":
    main() 

There are at times where the front and rear views might be at its designated location when connected (i.e; Front camera show rear live video feed). Therefore, I add a camera_toggle_button to swap the view . However, there are weird bugs right now, when I click on the checkbox camera_toggle_button , I expect the FRONT VIEW should be swapped with REAR VIEW , but as shown below, it does not happened at all. As you can see, I successfully print out the debugging notes (meaning it enter all the algorithm sequences correctly) , but somehow the live video feed does not swapped at all. It is weird at least. enter image description here

  • purely a programming problem, i.e. your understanding of your own code. this may be using opencv but it’s not an opencv problem.

    – 




You need to swap the target labels instead of photo1 and photo2. Also avoid using while loop in the main thread of a tkinter application, use after loop instead.

...

def swap_camera():
    global video_livefeedfront, video_livefeedrear
    # swap the target labels
    video_livefeedfront, video_livefeedrear = video_livefeedrear, video_livefeedfront

def main():
    global video_livefeedfront, video_livefeedrear

    ...

    # Create image labels for camera feeds
    video_livefeedfront = tk.Label(root)
    video_livefeedrear = tk.Label(root)

    video_livefeedfront.grid(row=3, column=2, padx=10)
    video_livefeedrear.grid(row=3, column=3, padx=10)

    # use after loop instead of while loop
    def update_cameras():
        # Read frames from both cameras
        ret1, frame1 = front_cap.read()
        ret2, frame2 = rear_cap.read()

        # Check if the frames were read successfully
        if not (ret1 and ret2):
            print("Error: Could not read frames from cameras.")
            root.destroy()

        # Resize frames to the target dimensions
        frame_resized1 = cv2.resize(frame1, (target_width, target_height))
        frame_resized2 = cv2.resize(frame2, (target_width, target_height))

        # Convert frames to RGB format for PIL
        frame_rgb1 = cv2.cvtColor(frame_resized1, cv2.COLOR_BGR2RGB)
        frame_rgb2 = cv2.cvtColor(frame_resized2, cv2.COLOR_BGR2RGB)

        # Convert frames to PhotoImage format for Tkinter
        photo1 = ImageTk.PhotoImage(Image.fromarray(frame_rgb1))
        photo2 = ImageTk.PhotoImage(Image.fromarray(frame_rgb2))

        # Update image live videofeed
        video_livefeedfront.configure(image=photo1)
        video_livefeedfront.photo = photo1

        video_livefeedrear.configure(image=photo2)
        video_livefeedrear.photo = photo2

        root.after(25, update_cameras) # change 25ms to whatever that suits your requirement

    update_cameras()  # start the after loop to update the camera feeds
    root.mainloop()

    # Release the camera devices and destroy the Tkinter window
    front_cap.release()
    rear_cap.release()
    cv2.destroyAllWindows()

...

Here is the complete code. Many thanks for @acw1668

import cv2
import tkinter as tk
from datetime import datetime
from PIL import Image, ImageTk, ImageDraw
global photo1, photo2

##photo1 = None 
##photo2 = None

def print_camera_info(width, height, camera_number):
    print(f"Camera {camera_number}")
    print(f"Width: {width}")
    print(f"Height: {height}")
    print("---")

def swap_camera():
    print("In swap_camera functions!") #debugging purpose
    global video_livefeedfront, video_livefeedrear
    print("After define global in swap_camera") #debugging purpose

    print("Before swap")#debugging purpose
    # Swap the frames algorithm
    video_livefeedfront, video_livefeedrear = video_livefeedrear, video_livefeedfront

    print("After Swap") #debugging purpose
        
def main():
    global video_livefeedfront, video_livefeedrear
    # Open two camera devices
    front_cap = cv2.VideoCapture(0)  # Use 0 for the first camera (Front Camera)
    rear_cap = cv2.VideoCapture(1)  # Use 1 for the second camera (Rear Camera)

    # Check if the cameras opened successfully
    if not (front_cap.isOpened() and rear_cap.isOpened()):
        print("Error: Could not open cameras.")
        print("front_cap opened:", front_cap.isOpened())
        print("rear_cap opened:", rear_cap.isOpened())
        return

    # Set the desired width and height for live feed
    target_width, target_height = 320, 240

    # Set the CAP_PROP_FRAME_WIDTH and CAP_PROP_FRAME_HEIGHT properties for both cameras
    front_cap.set(cv2.CAP_PROP_FRAME_WIDTH, target_width)
    front_cap.set(cv2.CAP_PROP_FRAME_HEIGHT, target_height)

    rear_cap.set(cv2.CAP_PROP_FRAME_WIDTH, target_width)
    rear_cap.set(cv2.CAP_PROP_FRAME_HEIGHT, target_height)

    # Get the actual dimensions after setting the properties
    actual_width1 = int(front_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    actual_height1 = int(front_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    actual_width2 = int(rear_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    actual_height2 = int(rear_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    # Print camera information with the actual resized dimensions
    print_camera_info(actual_width1, actual_height1, "Front")
    print_camera_info(actual_width2, actual_height2, "Rear")

    # Create Tkinter window
    root = tk.Tk()
    root.title("Combined Cameras")

    # Create a Checkbutton for camera toggle
    camera_toggle_button = tk.Checkbutton(root, text="Toggle Camera", command=swap_camera)
    camera_toggle_button.grid(row=1, column=1, padx=10, pady=10)

    # Create label widget for date and time
    datetime_label = tk.Label(root, text="", font=("Helvetica", 14))
    datetime_label.grid(row=1, column=2, columnspan=2, pady=10)

    # Create label widgets for camera views
    label1 = tk.Label(root, text="FRONT VIEW")
    label2 = tk.Label(root, text="REAR VIEW")

    label1.grid(row=2, column=2)
    label2.grid(row=2, column=3)

    # Create image labels for camera feeds
    video_livefeedfront = tk.Label(root)
    video_livefeedrear = tk.Label(root)

    video_livefeedfront.grid(row=3, column=2, padx=10)
    video_livefeedrear.grid(row=3, column=3, padx=10)

    def update_cameras():
        # Read frames from both cameras
        ret1, frame1 = front_cap.read()
        ret2, frame2 = rear_cap.read()

        # Check if the frames were read successfully
        if not (ret1 and ret2):
            print("Error: Could not read frames from cameras.")
        

        # Resize frames to the target dimensions
        frame_resized1 = cv2.resize(frame1, (target_width, target_height))
        frame_resized2 = cv2.resize(frame2, (target_width, target_height))

        # Convert frames to RGB format for PIL
        frame_rgb1 = cv2.cvtColor(frame_resized1, cv2.COLOR_BGR2RGB)
        frame_rgb2 = cv2.cvtColor(frame_resized2, cv2.COLOR_BGR2RGB)

        # Convert frames to PhotoImage format for Tkinter
        photo1 = ImageTk.PhotoImage(Image.fromarray(frame_rgb1))
        photo2 = ImageTk.PhotoImage(Image.fromarray(frame_rgb2))

        # Update image live videofeed
        video_livefeedfront.configure(image=photo1)
        video_livefeedfront.photo = photo1

        video_livefeedrear.configure(image=photo2)
        video_livefeedrear.photo = photo2

        root.after(25, update_cameras)
        # Update the Tkinter window
    #root.update()

    update_cameras()
    root.mainloop() 
    # Release the camera devices and destroy the Tkinter window
    front_cap.release()
    rear_cap.release()
    cv2.destroyAllWindows()
    root.destroy()

if __name__ == "__main__":
    main()

Leave a Comment