0

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


hkyaaa
  • 100
  • 9
  • Not sure if it will answer your question, but check [this](https://stackoverflow.com/questions/58525958/keeping-up-with-camera-frame-rate-in-tkinter-gui) – FrainBr33z3 Sep 23 '20 at 15:00
  • You should not call `after()` in a threaded function, use while loop instead. – acw1668 Sep 23 '20 at 17:26
  • That means I should use thread in while loop ? @acw1668 – hkyaaa Sep 30 '20 at 06:06
  • No, use while loop instead of `after()` in thread (i.e. in `my_video_loop()`). – acw1668 Sep 30 '20 at 06:10
  • I have already used in thread sir, I dont uderstand @acw1668 – hkyaaa Sep 30 '20 at 06:16
  • Yes you have used thread. What I mean is that you should not use `after()` in the threaded function `my_video_loop()`. You better use while loop. – acw1668 Sep 30 '20 at 06:25
  • I tried to use "While True" in an my_video_loop function. But first video so slow and left corner date time very slow counted. Actually nothing changed – hkyaaa Sep 30 '20 at 06:36
  • You should not create threads inside the for loop in `Application.__init__()` function and should not call `join()` as well. – acw1668 Sep 30 '20 at 06:47
  • nothing changed. I changed after with while loop in a my_video_loop, and remove # mythread = Thread(target=self.my_video_loop, args=()) # mythread.daemon = True # mythread.start() # threads.append(mythread) # for t in threads: # t.join() , but nothing changed.First added camera so slow also freezy; but second added camera not bad. @acw1668 – hkyaaa Sep 30 '20 at 07:22
  • 1
    Try changing camera 1 the same as camera 2. If there is improvement, then it is the issue of the protocol used for camera 1. – acw1668 Sep 30 '20 at 07:46

0 Answers0