Python Multi camera FPS is so slow despite using threads - python

I tried to dynamically display multi live camera in python tkinter with threads.
But I have a FPS problem and performance unfortunately bad. The program use to ip camera links dynamically create ui and then frame using in threads. But despite to using threads, performance not satisfactory. How can I improve performance ?
from functools import partial
from PIL import Image, ImageTk
import tkinter as tk
from tkinter import messagebox
from tkinter import font as tkFont
import argparse
import datetime
import cv2
import imutils
import os
from threading import Thread
variable = 1
threads = []
camera_urls = ['rtsp://mycamera1.cgi', 'mycamera2.cgi']
video_captures=[]
video_panels=[]
video_currentImages=[]
snaphsot_display_panels=[]
video_labels=[]
class Application:
def __init__(self, output_path="./"):
self.output_path = output_path
self.current_image = None
self.root = tk.Tk()
self.root.title("Kamera")
self.root.protocol('WM_DELETE_WINDOW', self.destructor)
for indexofUrl in range(len(camera_urls)):
print("[INFO] URL::" + camera_urls[indexofUrl])
self.vs = cv2.VideoCapture(camera_urls[indexofUrl])
video_captures.append(self.vs)
self.panel = tk.Label(self.root, borderwidth=5, relief="sunken", bg="green")
self.panel.grid(row=0, column=indexofUrl, padx=20, pady=20)
video_panels.append(self.panel)
print("[INFO] STEP:: 1")
mylabel = tk.Label(text="Kamera " +str(indexofUrl), bg="black", fg="white", font=(None, 15))
mylabel.grid(row=1, column=indexofUrl)
print("[INFO] STEP:: 2")
btn = tk.Button(self.root, text="Görüntüyü kaydet(Kamera "+str(indexofUrl)+")", command=partial(self.take_snapshot,indexofUrl), bg="green", fg='white')
btn.grid(row=2, column=indexofUrl, padx=20, pady=20)
print("[INFO] STEP:: 3")
self.panel3 = tk.Label(self.root)
self.panel3.grid(row=3, column=indexofUrl)
print("[INFO] STEP:: 4")
self.mylabel2 = tk.Label(text="Snapshot bilgileri:", bg="blue", fg="white", font=(None, 15))
self.mylabel2.grid(row=4, column=indexofUrl)
video_labels.append(self.mylabel2)
print("[INFO] STEP:: 5")
self.panel4 = tk.Label(self.root,relief="sunken", borderwidth=5, bg="black")
self.panel4.grid(row=5, column=indexofUrl, padx=20, pady=20)
snaphsot_display_panels.append(self.panel4)
mythread = Thread(target=self.my_video_loop, args=())
mythread.daemon = True
mythread.start()
threads.append(mythread)
for t in threads:
t.join()
#self.my_video_loop()
self.thread2 = Thread(target= self.my_video_loop, args=())
self.thread2.daemon=True
self.thread2.start()
def my_video_loop(self):
for indexOfVideCaptures in range(len(video_captures)):
ok, frame= video_captures[indexOfVideCaptures].read()
frame=self.maintain_aspect_ratio_resize(frame , width=400)
if ok:
if len(video_currentImages) > len(camera_urls):
video_currentImages.clear()
cv2image = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
self.current_image = Image.fromarray(cv2image)
video_currentImages.append(self.current_image)
imgtk = ImageTk.PhotoImage(image=self.current_image)
video_panels[indexOfVideCaptures].imgtk =imgtk
video_panels[indexOfVideCaptures].config(image=imgtk)
self.root.after(30, self.my_video_loop)
# Resizes a image and maintains aspect ratio
def maintain_aspect_ratio_resize(self, image, width=None, height=None, inter=cv2.INTER_AREA):
# Grab the image size and initialize dimensions
dim = None
(h, w) = image.shape[:2]
# Return original image if no need to resize
if width is None and height is None:
return image
# We are resizing height if width is none
if width is None:
# Calculate the ratio of the height and construct the dimensions
r = height / float(h)
dim = (int(w * r), height)
# We are resizing width if height is none
else:
# Calculate the ratio of the 0idth and construct the dimensions
r = width / float(w)
dim = (width, int(h * r))
# Return the resized image
return cv2.resize(image, dim, interpolation=inter)
def take_snapshot(self, camera):
ts = datetime.datetime.now() # current timestamp
filename = "{}.png".format(ts.strftime("%Y-%m-%d__%H-%M-%S")) #filename
p = os.path.join(self.output_path, filename) # output path
if camera >= 0:
if len(video_currentImages) == 0:
self.take_snapshot(camera)
# self.root.after(500, self.take_snapshot(camera))
elif len(video_currentImages) == len(camera_urls):
video_currentImages[camera].save(p, "PNG") # save image as jpeg file
print("[INFO] saved {}".format(filename))
imgtk3 = ImageTk.PhotoImage(image=video_currentImages[camera])
snaphsot_display_panels[camera].imgtk =imgtk3
snaphsot_display_panels[camera].config(image=imgtk3)
video_labels[camera].config(text=filename.rstrip(".png") + f"\n KAMERA {camera}")
def destructor(self):
""" Destroy the root object and release all resources """
print("[INFO] closing...")
self.root.destroy()
for indexcameras in range(len(video_captures)):
video_captures[indexcameras].release() # release camera
cv2.destroyAllWindows() # it is not mandatory in this application
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./",
help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())
# start the app
print("[INFO] starting...")
pba = Application(args["output"])
pba.root.mainloop()

Related

How to get circles to appear over the video in canvas tkinter?

I have the below code:
import tkinter as tk, threading
from tkinter import *
import imageio
from PIL import Image, ImageTk
from random import *
video_name = "video.mp4" #This is your video file path
video = imageio.get_reader(video_name)
def stream(label):
for image in video.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image))
label.config(image=frame_image)
label.image = frame_image
def circle():
global circ
x = randint(0, 299)
y = randint(0, 299)
diameter = randint(10, 100)
circ = canvas.create_oval(x, y, x + diameter, y + diameter, tags="circle")
canvas.tag_raise(circ)
if __name__ == "__main__":
root = tk.Tk()
canvas = Canvas(root, bg="green")
canvas.pack(expand=True, fill=BOTH)
my_label = tk.Label(canvas)
my_label.pack()
b = Button(canvas, text="Circle", command=circle)
b.pack()
thread = threading.Thread(target=stream, args=(my_label,))
thread.daemon = 1
thread.start()
root.mainloop()
It works fine, and the circles appear, but they go behind the video playing. How can I make the circles appear on top of the video?
Thanks!
You will need to use a text item on the canvas rather than a label. The canvas does not allow you to draw on top of widgets embedded in or on the canvas.

Termination Async call back error in opencv in tkinter gui

I created a master window in tkinter.... then when I press a button on master window it will open another top level window in which I created a canvas and a button....
Canvas for: reading camera frame by frame and show it in tkinter window
Button for: capture the current frame..but in button I used face detection to detect a face when it detects a face I want close the top-level window and go back master window
In master window I have another button to exit the gui(means master.destroy when I press exit) ....but here when I press exit computer is hanging and asking for force close....I am posting my code below
'''
import tkinter
from tkinter import *
import cv2
import PIL.Image, PIL.ImageTk
import time
import os
import numpy as np
class App(object):
def __init__(self,master3):
self.window=Toplevel(master3)
self.window.title("capturing image")
self.window.geometry("{}x{}".format(self.window.winfo_screenwidth(),self.window.winfo_screenheight()))
self.vid = cv2.VideoCapture(0)
#self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
#self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
self.canvas = Canvas(self.window, width = 1000, height = 700)
self.path = os.path.dirname(os.path.abspath(__file__))
self.detector=cv2.CascadeClassifier(self.path+r'\HarrCascade\haarcascade_frontalface.xml')
#self.recognizer = cv2.face.LBPHFaceRecognizer_create()
#self.recognizer.read(self.path+r'\trained_data\trained_file.xml')
self.canvas.pack()
self.canvas.pack()
self.btn_snapshot=Button(self.window, text="Snapshot", width=50,command=self.snap)
self.btn_snapshot.pack(anchor=CENTER, expand=True)
if not self.vid.isOpened():
raise IOError("Unable to open video source")
else:
self.count=0
self.delay=15
self.update()
def g(self):
if self.vid.isOpened():
_,frame = self.vid.read()
self.frame2 = frame
self.count=1
self.vid.release()
cv2.waitKey(1)
cv2.destroyAllWindows()
for i in range(1,5):
cv2.waitKey(1)
master.deiconify()
def snap(self):
self.gray=cv2.cvtColor(self.frame2,cv2.COLOR_BGR2GRAY)
self.faces= self.detector.detectMultiScale(self.gray, scaleFactor=1.2, minNeighbors=5, minSize=(100, 100), flags=cv2.CASCADE_SCALE_IMAGE)
self.no_faces=self.faces.shape[0]
if self.no_faces >= 1:
for(x,y,w,h) in self.faces:
self.g()
cv2.imwrite("face-0.jpg",self.gray)
def update(self):
if self.vid.isOpened():
ret, frame = self.vid.read()
frame1=cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
if ret:
self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame1))
self.canvas.create_image(0, 0, image = self.photo, anchor = NW)
else:
pass
if self.count==0:
self.window.after(self.delay, self.update)
else:
pass
else:
pass
def fun():
App(master)
master.withdraw()
def fun1():
master.quit()
master=Tk(screenName=None,baseName=None,className='Tk',useTk=1)
master.title('face recognition system')
master.geometry("{}x{}".format(master.winfo_screenwidth(),master.winfo_screenheight()))
button2 = Button(master,text = 'Test Image',bg = 'black',fg = 'white',font = ('algerian',10),height = 3,width = 20,command=fun)
button3 = Button(master,text = 'exit',bg = 'black',fg = 'white',font = ('algerian',10),height = 3,width = 20,command=fun1)
button2.pack()
button3.pack()
master.mainloop()
'''
thanks in advance for your kind help

How to add a CV2 tracking bar in a tkinter window

How to add a cv2 trackbar to a Tkinter window?
I have 2 snippets of code that my teammates have made, but it is difficult to integrate them in the same window.
I am able to use them in separate windows but I want to use them in the same window.
CODE FOR THE VIDEO PLAYING IN TKINTER:
import cv2
import tkinter as tk
from tkinter import *
# from tkinter import ttk
# from ttk import Frame
# import ImageTk
white = "#ffffff"
lightBlue2 = "#adc5ed"
font = "Constantia"
fontButtons = (font, 12)
maxWidth = 800
maxHeight = 480
#Graphics window
mainWindow = tk.Tk()
mainWindow.configure(bg=lightBlue2)
mainWindow.geometry('%dx%d+%d+%d' % (maxWidth,maxHeight,0,0))
mainWindow.resizable(0,0)
# mainWindow.overrideredirect(1)
mainFrame = Frame(mainWindow)
mainFrame.place(x=20, y=20)
#Capture video frames
lmain = tk.Label(mainFrame)
lmain.grid(row=0, column=0)
cap = cv2.VideoCapture('test.mp4')
def show_frame():
ret, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image).resize((760, 400))
imgtk = ImageTk.PhotoImage(image = img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_frame)
closeButton = Button(mainWindow, text = "CLOSE", font = fontButtons, bg = white, width = 20, height= 1)
closeButton.configure(command= lambda: mainWindow.destroy())
closeButton.place(x=270,y=430)
show_frame() #Display
mainWindow.mainloop() #Starts GUI
And I have a second snippet of code that uses cv2 to display the track bar and changes the position of the video.
import cv2
CURRENT_FRAME_FLAG = cv2.CAP_PROP_POS_FRAMES
TOTAL_FRAMES_FLAG = cv2.CAP_PROP_FRAME_COUNT
WIN_NAME = "Frame Grabber"
POS_TRACKBAR = "pos_trackbar"
cap = cv2.VideoCapture('C:/Users/ayush/Desktop/test.mp4')
ret, frame = cap.read()
def dummy():
pass
def save_image():
filename = "image_%0.5f.png" % t.time()
cv2.imwrite(filename, frame)
def seek_callback(x):
global frame
i = cv2.getTrackbarPos(POS_TRACKBAR, WIN_NAME)
cap.set(CURRENT_FRAME_FLAG, i-1)
_, frame = cap.read()
cv2.imshow(WIN_NAME, frame)
def mouse_callback(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDBLCLK:
save_image()
def skip_frame_generator(df):
def skip_frame():
global frame
cf = cap.get(CURRENT_FRAME_FLAG) - 1
cap.set(CURRENT_FRAME_FLAG, cf+df)
cv2.setTrackbarPos(POS_TRACKBAR, WIN_NAME, int(cap.get(CURRENT_FRAME_FLAG)))
_, frame = cap.read()
return skip_frame
cv2.namedWindow(WIN_NAME)
cv2.createTrackbar(POS_TRACKBAR, WIN_NAME, 0, int(cap.get(TOTAL_FRAMES_FLAG)), seek_callback)
cv2.setMouseCallback(WIN_NAME, mouse_callback)
actions = dict()
actions[ord("D")] = skip_frame_generator(10)
actions[ord("d")] = skip_frame_generator(1)
actions[ord("a")] = skip_frame_generator(-1)
actions[ord("A")] = skip_frame_generator(-10)
actions[ord("q")] = lambda: exit(0)
actions[ord("s")] = save_image
while True:
cv2.imshow(WIN_NAME, frame)
key = cv2.waitKey(0) & 0xFF
actions.get(key, dummy)()
I have to integrate the cv2 trackbar into the Tkinter window.
I am able to integrate but as I don't know how to use implement CV2 trackbar in tkinter, I am not able to do anything.
Please Help.
Try using tkinter Scale widgets instead of cv2 trackbars. They are slightly different but they should be fairly easy to swap out and work about the same. Then you can easily add them in your tkinter window/frame.

Cannot create a button

I am creating just a simple application for a live face detection using python opencv tkinter. I could do the live face detection but when I try to create a button to start the live feed, it is not even executing the program. Could anybody help me?
from ttk import *
import Tkinter as tk
from Tkinter import *
import cv2
from PIL import Image, ImageTk
import os
import numpy as np
face_classifier = cv2.CascadeClassifier('Haarcascades/haarcascade_frontalface_default.xml')
global last_frame #creating global variable
last_frame = np.zeros((480, 640, 3), dtype=np.uint8)
global cap
cap = cv2.VideoCapture(0)
def show_vid(): #creating a function
if not cap.isOpened():
print("cant open the camera")
flag, frame = cap.read()
frame = cv2.flip(frame, 1)
if flag is None:
print "Major error!"
elif flag:
global last_frame
last_frame = frame.copy()
pic = cv2.cvtColor(last_frame, cv2.COLOR_BGR2GRAY)
faces = face_classifier.detectMultiScale(pic, 1.3, 5)
for (x,y,w,h) in faces:
x = x - 50
w = w + 50
y = y - 50
h = h + 50
cv2.rectangle(last_frame,(x,y),(x+w,y+h),(255,0,0),2)
last_frame=cv2.cvtColor(last_frame, cv2.COLOR_BGR2GRAY)
img = Image.fromarray(last_frame)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_vid)
if __name__ == '__main__':
root=tk.Tk()
lmain = tk.Label(master=root)
lmain.grid(column=0, rowspan=4, padx=5, pady=5)
root.title("Live face detection")
button = Button(root, text = "Start", command=show_vid).pack()
#show_vid()
root.mainloop()
cap.release()
When I commented button line and uncommented show_vid() line live face detection works, but when I do the opposite it is not working.
You should replace the .pack() part of the line with button(row=yourrow, column=yourcolumn sticky=E) where you specify the column and row to place the button

Multiple Video Display using Tkinter in Python for GUI design

My task is to display a webcam stream and its black&white stream in two different frames on a single GUI using Tkinter on python. I have seen some examples on google, but they are of images not videos as in the link here. Example of image display in 2 panes on a single GUI
I need exactly the same thing but for the real time video through my webcam.
Initial question:
"I am having issues in displaying multiple (2) windows for displaying video frames in a GUI using Tkinter in python. Please help me with a
code for this task."
The initial question mentioned 2 windows so here's a basic example on how to create multiple windows with tkinter:
#import tkinter as tk
import Tkinter as tk
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
#super().__init__()
tk.Tk.__init__(self)
self.title("This is the MainWindow")
self._is_hidden = False
self.window1 = OtherWindow(self, title="window 1")
self.window2 = OtherWindow(self, title="window 2")
def toggle_hide(self):
if self._is_hidden:
self.iconify()
self.deiconify()
else:
self.withdraw()
self._is_hidden = not self._is_hidden
class OtherWindow(tk.Toplevel):
def __init__(self, master, *args, **kwargs):
#super().__init__(master)
tk.Toplevel.__init__(self, master)
if 'title' in kwargs:
self.title(kwargs['title'])
self.hide_main_button = tk.Button(self, text="Hide/Show MainWindow")
self.hide_main_button['command'] = self.master.toggle_hide
self.hide_main_button.pack()
if __name__ == '__main__':
root = MainWindow()
root.mainloop()
from ttk import *
import Tkinter as tk
from Tkinter import *
import cv2
from PIL import Image, ImageTk
import os
import numpy as np
global last_frame #creating global variable
last_frame = np.zeros((480, 640, 3), dtype=np.uint8)
global last_frame2 #creating global variable
last_frame2 = np.zeros((480, 640, 3), dtype=np.uint8)
global cap
cap = cv2.VideoCapture(1)
def show_vid(): #creating a function
if not cap.isOpened(): #checks for the opening of camera
print("cant open the camera")
flag, frame = cap.read()
frame = cv2.resize(frame,(400,500))
if flag is None:
print "Major error!"
elif flag:
global last_frame
last_frame = frame.copy()
global last_frame2
last_frame2 = frame.copy()
pic = cv2.cvtColor(last_frame, cv2.COLOR_BGR2RGB) #we can change the display color of the frame gray,black&white here
img = Image.fromarray(pic)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_vid)
def show_vid2():
pic2 = cv2.cvtColor(last_frame2, cv2.COLOR_BGR2GRAY)
img2 = Image.fromarray(pic2)
img2tk = ImageTk.PhotoImage(image=img2)
lmain2.img2tk = img2tk
lmain2.configure(image=img2tk)
lmain2.after(10, show_vid2)
if __name__ == '__main__':
root=tk.Tk() #assigning root variable for Tkinter as tk
lmain = tk.Label(master=root)
lmain2 = tk.Label(master=root)
#lmain.Frame= Frame(width=768, height=576)
#framex.grid(column=3,rowspan=2,padx=5, pady=5)
lmain.pack(side = LEFT)
lmain2.pack(side = RIGHT)
root.title("Fire Alarm Detector") #you can give any title
root.geometry("900x700+100+10") #size of window , x-axis, yaxis
exitbutton = Button(root, text='Quit',fg="red",command= root.destroy).pack(side = BOTTOM,)
show_vid()
show_vid2()
root.mainloop() #keeps the application in an infinite loop so it works continuosly
cap.release()
import tkinter
import PIL.Image
import PIL.ImageTk
import cv2
class App:
def __init__(self, window, video_source1, video_source2):
self.window = window
self.window.title("KEC MEDIA PLAYER")
self.video_source1 = video_source1
self.video_source2 = video_source2
self.photo1 = ""
self.photo2 = ""
# open video source
self.vid1 = MyVideoCapture(self.video_source1, self.video_source2)
# Create a canvas that can fit the above video source size
self.canvas1 = tkinter.Canvas(window, width=500, height=500)
self.canvas2 = tkinter.Canvas(window, width=500, height=500)
self.canvas1.pack(padx=5, pady=10, side="left")
self.canvas2.pack(padx=5, pady=60, side="left")
# After it is called once, the update method will be automatically called every delay milliseconds
self.delay = 15
self.update()
self.window.mainloop()
def update(self):
# Get a frame from the video source
ret1, frame1, ret2, frame2 = self.vid1.get_frame
if ret1 and ret2:
self.photo1 = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame1))
self.photo2 = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame2))
self.canvas1.create_image(0, 0, image=self.photo1, anchor=tkinter.NW)
self.canvas2.create_image(0, 0, image=self.photo2, anchor=tkinter.NW)
self.window.after(self.delay, self.update)
class MyVideoCapture:
def __init__(self, video_source1, video_source2):
# Open the video source
self.vid1 = cv2.VideoCapture(video_source1)
self.vid2 = cv2.VideoCapture(video_source2)
if not self.vid1.isOpened():
raise ValueError("Unable to open video source", video_source1)
#property
def get_frame(self):
ret1 = ""
ret2 = ""
if self.vid1.isOpened() and self.vid2.isOpened():
ret1, frame1 = self.vid1.read()
ret2, frame2 = self.vid2.read()
frame1 = cv2.resize(frame1, (500, 500))
frame2 = cv2.resize(frame2, (500, 500))
if ret1 and ret2:
# Return a boolean success flag and the current frame converted to BGR
return ret1, cv2.cvtColor(frame1, cv2.COLOR_BGR2RGB), ret2, cv2.cvtColor(frame2, cv2.COLOR_BGR2RGB)
else:
return ret1, None, ret2, None
else:
return ret1, None, ret2, None
# Release the video source when the object is destroyed
def __del__(self):
if self.vid1.isOpened():
self.vid1.release()
if self.vid2.isOpened():
self.vid2.release()
def callback():
global v1,v2
v1=E1.get()
v2=E2.get()
if v1 == "" or v2 == "":
L3.pack()
return
initial.destroy()
v1 = ""
v2 = ""
initial = tkinter.Tk()
initial.title("KEC MEDIA PLAYER")
L0 = tkinter.Label(initial, text="Enter the full path")
L0.pack()
L1 = tkinter.Label(initial, text="Video 1")
L1.pack()
E1 = tkinter.Entry(initial, bd =5)
E1.pack()
L2 = tkinter.Label(initial, text="Video 2")
L2.pack()
E2 = tkinter.Entry(initial, bd =5)
E2.pack()
B = tkinter.Button(initial, text ="Next", command = callback)
B.pack()
L3 = tkinter.Label(initial, text="Enter both the names")
initial.mainloop()
# Create a window and pass it to the Application object
App(tkinter.Tk(),v1, v2)
This code works under normal circumstances but I haven't handled situations where one video ends and the other is still playing. Also, the audio has not been handled in this code.
I created two canvases in the same window and ran the video as a series of images. I have resized the video to fit a constant canvas size but you can change the canvas size to fit the video if you want.
You can change the source to be from your webcam.

Categories

Resources