Partage
  • Partager sur Facebook
  • Partager sur Twitter

OpenCV + tkinter, problème fluidité flux vidéo

Création d'une GUI

Sujet résolu
    3 février 2023 à 9:30:51

    Bonjour,

    Je suis en train de créer une interface graphique à l'aide de tkinter.

    Mon script permet de récupérer le flux vidéo de deux caméra USB et d'enregistrer lorsque l'on appuie sur un bouton "snapshot", une image sur chacune des deux caméras. Ensuite les les images sont automatiquement enregistrées dans des dossiers etc. De plus une série d'instructions s'affiche sur la fenêtre et se met à jour dès que l'on enregistre une paire d'images.

    Mon problème c'est que l'affichage de la vidéo dans la fenêtre tkinter n'est absolument pas fluide (Je ne suis pas un expert en interfaces graphiques et j'ai regardé de nombreux tutos mais rien à faire je n'arrive pas à régler ce problème)

    Pour l'instant un seul des deux flux vidéos s'affiche dans la fenêtre tkinter mais ce n'est pas bien grave je verrai plus tard comment mettre le deuxième flux vidéo dans un autre onglet de ma fenêtre tkinter par exemple.

    Toute aide sera la bienvenue ! 

    Merci beaucoup

    mon script :

    import tkinter
    import cv2
    import PIL.Image, PIL.ImageTk
    import os
    import tkinter.messagebox
    import datetime
    
    
    class App:
        counter = 1
        result = ""
        date = datetime.datetime.now().strftime("%d.%m.%Y")
    
        def __init__(self, window, window_title, video_source=0):
    
            self.window = window
            self.video_source = video_source
    
            # Ask the user if he wants to do a calibration or not
            self.result = tkinter.messagebox.askquestion("","Do you want to calibrate the microscope ?", icon='info')
            if self.result == 'yes':
                self.window.title("3D DIC light-microscope")
    
                # Define the instructions to calibrate the microscope
                self.instructions = ["picture 1 : 0°", "picture 2 : 90°", "picture 3 : 180°", "picture 4 : 270°"
                    ,"picture 5 : 45°", "picture 6 : 135°", "picture 7 : 225°", "picture 8 : 315°"
                    , "picture 9 : tilt and 0°", "picture 10 : tilt and 90°", "picture 11 : tilt and 180°"
                    , "picture 12 : tilt and 270°", "picture 13 : +Zmm and 0°","picture 14 : +Zmm and 90°"
                    , "picture 15 : +Zmm and 180°", "picture 16 : +Zmm and 270°", "picture 17 : -Zmm and 0°"
                    , "picture 18 : -Zmm and 90°", "picture 19 : -Zmm and 180°", "picture 20: -Zmm and 270°"]
    
            else:
                self.window.title("Light-microscope : Deformation images")
    
            self.vid = MyVideoCapture(self.video_source)
            self.canvas = tkinter.Canvas(window, width=self.vid.width * 2, height=self.vid.height)
            self.canvas.pack()
    
            self.btn_snapshot = tkinter.Button(window, text="Snapshot", width=50, command=self.snapshot)
            self.btn_snapshot.pack(anchor=tkinter.CENTER, expand=True)
    
            self.delay = 50
            self.update()
    
            self.window.mainloop()
    
        def snapshot(self):
            ret, frame_0, frame_1 = self.vid.get_frame()
    
            if ret:
                if not os.path.exists(self.date):
                    os.mkdir(self.date)
                if not os.path.exists(f"{self.date}/left"):
                    os.mkdir(f"{self.date}/left")
                if not os.path.exists(f"{self.date}/right"):
                    os.mkdir(f"{self.date}/right")
    
                cv2.imwrite(f"{self.date}/left/{self.counter}.tiff", frame_0)
                cv2.imwrite(f"{self.date}/right/{self.counter}.tiff", frame_1)
    
                self.counter += 1
    
        def update(self):
            ret, frame_0, frame_1 = self.vid.get_frame()
    
            if ret:
                self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame_1))
                self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
    
                if self.result == 'yes':
                    if self.counter > len(self.instructions):
                        self.canvas.create_text(self.vid.width / 2, 20, text = self.instructions[(self.counter - 1) // len(self.instructions) - 1], font = ("Arial", 20), fill = "white")
                    else:
                        self.canvas.create_text(self.vid.width / 2, 20, text = self.instructions[self.counter - 1], font = ("Arial", 20), fill="white")
                else:
                    self.canvas.create_text(self.vid.width / 2, 20, text = f"picture n°{self.counter}", font = ("Arial", 20), fill="white")
    
            self.window.after(self.delay, self.update)
    
    class MyVideoCapture:
        def __init__(self, video_source=0):
            self.vid = cv2.VideoCapture(video_source, cv2.CAP_DSHOW)
            if not self.vid.isOpened():
                raise ValueError("Unable to open video source", video_source)
    
            self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
            self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
            self.vid.release()
    
        def get_frame(self):
    
            camera = cv2.VideoCapture(0, cv2.CAP_DSHOW)
            camera.set(cv2.CAP_PROP_EXPOSURE, -4)  # Between -4 and -7
            camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
            camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
            ret, frame_0 = camera.read()
            camera.release()
            camera = cv2.VideoCapture(1, cv2.CAP_DSHOW)
            camera.set(cv2.CAP_PROP_EXPOSURE, -4)  # Between -4 and -7
            camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
            camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
            ret, frame_1 = camera.read()
            camera.release()
            return ret, frame_0, frame_1
    
        def __del__(self):
            if self.vid.isOpened():
                self.vid.release()
    
    App(tkinter.Tk(), "Tkinter and OpenCV")
    



    • Partager sur Facebook
    • Partager sur Twitter
      3 février 2023 à 11:27:50

      Si update attend l'arrivée de nouvelles images pour mettre à jour l'affichage, pas la peine d'attendre en plus 50ms.

      Pourquoi attendre la lecture de 2 images....

      Si ces opérations ont une durée variable, autant leur dédier un thread et mettre à jour l'affichage en fonction de l'arrivée de nouvelles images.

      Vous avez des soucis côté logique et conception difficiles à résoudre (à votre place) car dépendant des matériels et de vos attentes.

      -
      Edité par mps 3 février 2023 à 12:40:01

      • Partager sur Facebook
      • Partager sur Twitter
        3 février 2023 à 13:15:24

        mps a écrit:

        "Vous avez des soucis côté logique et conception difficiles à résoudre (à votre place) car dépendant des matériels et de vos attentes."

        L'objectif de tout cela est de récupérer des images de deux caméras présentes dans les tubes oculaires d'un microscope optique (donc à la place de nos yeux), et de les enregistrer à différents instants (Quand l'utilisateur le souhaite). Pour ensuite créer une image stéréoscopique...

        Les caméras utilisées sont : "BRESSER Caméra oculaire MikrOkular Full HD" elles seront par la suite remplacées par des caméras de meilleure qualité mais nous n'en sommes pas encore là.

        Pour l'instant j'ai réalisé un script permettant de visualiser les flux vidéos des deux caméras et d'enregistrer des images en appuyant sur la touche "s". Voici le script : 

        import cv2
        import os
        import datetime
        
        # Open the video source
        cam_1 = cv2.VideoCapture(0, cv2.CAP_DSHOW)
        cam_1.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
        cam_1.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
        
        cam_2 = cv2.VideoCapture(1, cv2.CAP_DSHOW)
        cam_2.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
        cam_2.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
        
        # Set the dimension of the saved images
        dim = (1920, 1080)
        
        if not cam_1.isOpened():
            raise ValueError("Unable to open video source", cam_1)
        
        if not cam_2.isOpened():
            raise ValueError("Unable to open video source", cam_2)
        
        counter = 1
        
        # Infinite loop  to display the images
        while True:
        
            ret1, frame1 = cam_1.read()
            ret2, frame2 = cam_2.read()
        
            # Displays the video sources
            cv2.imshow("Left", frame1)
            cv2.imshow("Right", frame2)
        
            key = cv2.waitKey(1) & 0xFF
        
            date = datetime.datetime.now().strftime("%d.%m.%Y")
        
            # Condition to save the images
            if key == ord("s"):
        
                # Creates a folder with the date of the day
                if not os.path.exists(date):
                    os.mkdir(date)
        
                # Creates folders "right" and "left" if they do not exist
                if not os.path.exists(f"{date}/left"):
                    os.mkdir(f"{date}/left")
                if not os.path.exists(f"{date}/right"):
                    os.mkdir(f"{date}/right")
        
                # Resize the images
                frame1 = cv2.resize(frame1, dim)
                frame2 = cv2.resize(frame2, dim)
        
                # Save the images
                cv2.imwrite(f"{date}/left/{counter}.tiff", frame1)
                cv2.imwrite(f"{date}/right/{counter}.tiff", frame2)
        
                # Increment the counter
                counter += 1
        
            # Press "q" to close the windows
            elif key == ord("q"):
                break
        
        if cam_1.isOpened():
            cam_1.release()
        
        if cam_2.isOpened():
            cam_2.release()
        
        if cam_1.isOpened() and cam_2.isOpened():
            cv2.destroyAllWindows()

        Cependant, ce n'est pas très "visuel" j'aimerai donc créer une interface graphique qui permette de réaliser les mêmes fonctions que le script ci-dessus mais plus intuitive et visuelle pour l'utilisateur.

        De plus j'ai voulu ajouter les étapes de la calibrations du microscope (puisqu'une calibration est nécessaire par la suite), elles sont censées s'afficher sur l'interface si l'utilisateur souhaite réaliser une calibration.

        N'hésitez pas si vous avez des questions si je n'ai pas été très clair.

        -
        Edité par Auréliooo 3 février 2023 à 13:35:15

        • Partager sur Facebook
        • Partager sur Twitter

        OpenCV + tkinter, problème fluidité flux vidéo

        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
        • Editeur
        • Markdown