From 35cf1f56049f6a89bf23802440c7a985d69bfa66 Mon Sep 17 00:00:00 2001 From: Paul Chevalier <paul.guillaume.chevalier@gmail.com> Date: Wed, 26 Mar 2025 21:39:29 +0100 Subject: [PATCH] =?UTF-8?q?jsp=20pourquoi=20qqn=20a=20fait=20un=20version?= =?UTF-8?q?=20POO=20du=20programme=20d'avant=20j'esp=C3=A8re=20que=20ct=20?= =?UTF-8?q?pas=20importnant=20(=20je=20le=20met=20dans=20un=20autre=20fich?= =?UTF-8?q?ier=20OK=20ou)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- calssification_video.py | 500 ++++++++++++++++++++++++++++++++++++ "chercher_cam\303\251ra.py" | 133 +++------- 2 files changed, 541 insertions(+), 92 deletions(-) diff --git a/calssification_video.py b/calssification_video.py index e69de29..40611fc 100644 --- a/calssification_video.py +++ b/calssification_video.py @@ -0,0 +1,500 @@ +import cv2 +import os +import shutil +import tkinter as tk +from tkinter import ttk, simpledialog, messagebox, filedialog +import threading +import time + +class VideoPlayer: + def __init__(self): + self.is_playing = False + self.current_video = None + self.stop_event = threading.Event() + self.play_thread = None + self.speed = 1.0 + + def play_video(self, video_path, speed=1.0): + """Démarre la lecture d'une vidéo en boucle dans un thread séparé""" + self.stop_event.clear() + self.is_playing = True + self.current_video = video_path + self.speed = speed + + # Créer un nouveau thread pour la lecture + self.play_thread = threading.Thread(target=self._play_video_loop) + self.play_thread.daemon = True + self.play_thread.start() + + def _play_video_loop(self): + """Joue la vidéo en boucle jusqu'à ce que stop_event soit déclenché""" + while not self.stop_event.is_set(): + cap = cv2.VideoCapture(self.current_video) + if not cap.isOpened(): + print(f"Erreur : Impossible d'ouvrir {self.current_video}") + break + + fps = cap.get(cv2.CAP_PROP_FPS) + delay = int(1000 / (fps * self.speed)) + + while cap.isOpened() and not self.stop_event.is_set(): + ret, frame = cap.read() + if not ret: + break # Fin de la vidéo, on recommence + + cv2.imshow("Lecture Vidéo", frame) + if cv2.waitKey(delay) & 0xFF == ord('q'): + self.stop() + break + + cap.release() + + # Si on a atteint la fin mais que stop_event n'est pas déclenché, + # on recommence la lecture depuis le début + if not self.stop_event.is_set(): + continue + else: + break + + def stop(self): + """Arrête la lecture de la vidéo""" + self.stop_event.set() + if self.play_thread: + self.play_thread.join(timeout=1.0) + self.is_playing = False + cv2.destroyAllWindows() + +def get_selection(title, options, root=None): + """Fenêtre avec un menu déroulant pour faire un choix""" + # Vérifier si une instance root a été passée, sinon en créer une nouvelle + own_root = False + if root is None: + root = tk.Tk() + root.withdraw() + own_root = True + + selection_window = tk.Toplevel(root) + selection_window.title(title) + selection_window.geometry("300x150") + selection_window.lift() # Amener la fenêtre au premier plan + selection_window.focus_force() # Forcer le focus + + tk.Label(selection_window, text=title).pack(pady=5) + selected_value = tk.StringVar() + dropdown = ttk.Combobox(selection_window, textvariable=selected_value, values=options, state='readonly') + dropdown.pack(pady=5) + dropdown.current(0) + + def validate(): + selection_window.destroy() + + tk.Button(selection_window, text="Valider", command=validate).pack(pady=10) + selection_window.wait_window() + + # Si nous avons créé notre propre root, il faut le détruire + if own_root: + root.destroy() + + return selected_value.get() + +def classifier_video(video_path, liste_joueuses, cpt, video_player, root): + """Interface utilisateur pour classifier ou supprimer une vidéo avec des menus déroulants""" + if messagebox.askyesno("Suppression", "Voulez-vous supprimer cette vidéo ?"): + video_player.stop() + os.remove(video_path) + print(f"Vidéo supprimée : {video_path}") + return None + + joueuse = get_selection("Sélectionnez une joueuse", liste_joueuses, root) + if not joueuse: + return None + + action_types = ["Réception", "Passe", "Défense", "Autre"] + action = get_selection("Sélectionnez une action", action_types, root) + if not action: + return None + + passe_types = { + "Bonne passe (verte)": "Bonne passe", + "Bonne hauteur, mauvaise zone (bleue)": "Bonne hauteur, mauvaise zone", + "Bonne zone, mauvaise hauteur (blanche)": "Bonne zone, mauvaise hauteur", + "Mauvaise passe (rouge)": "Mauvaise passe" + } + passe_result = None + if action == "Passe": + passe_result = get_selection("Qualité de Passe", list(passe_types.keys()), root) + passe_result = passe_types.get(passe_result) + + new_name = f"{joueuse}_{action}" + if passe_result: + new_name += f"_{passe_result}" + + # On ne change pas l'extension pour conserver le format d'origine + extension = os.path.splitext(video_path)[-1] + new_name += f"_{cpt}{extension}" + + # Arrêter la lecture de la vidéo une fois la classification terminée + video_player.stop() + + return joueuse, action, passe_result, new_name + +def concatener_videos_cv2(video_paths, destination_path): + """Concatène les vidéos en utilisant OpenCV""" + if not video_paths: + return False + + if len(video_paths) == 1: + # S'il n'y a qu'une seule vidéo, la copier simplement + shutil.copy(video_paths[0], destination_path) + return True + + # Récupérer les propriétés de la première vidéo + cap = cv2.VideoCapture(video_paths[0]) + frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + fps = int(cap.get(cv2.CAP_PROP_FPS)) + cap.release() + + # Créer l'objet VideoWriter pour la vidéo de sortie + fourcc = cv2.VideoWriter_fourcc(*'mp4v') # Codec pour mp4 + out = cv2.VideoWriter(destination_path, fourcc, fps, (frame_width, frame_height)) + + # Parcourir chaque vidéo et ajouter ses frames à la vidéo de sortie + for video_path in video_paths: + cap = cv2.VideoCapture(video_path) + while True: + ret, frame = cap.read() + if not ret: + break + out.write(frame) + cap.release() + + # Libérer l'objet VideoWriter + out.release() + return True + +def organiser_videos(video_list, liste_joueuses): + """Affiche et trie une liste de vidéos en fonction des critères choisis""" + # Créer une seule instance de Tk pour toute l'application + root = tk.Tk() + root.withdraw() # Cacher la fenêtre principale + + cpt = 0 + speed_choice = get_selection("Vitesse de Lecture", ["1", "1.5", "2"], root) + speed = float(speed_choice) if speed_choice else 1.0 + + # Créer un lecteur vidéo + video_player = VideoPlayer() + + # Dictionnaire pour stocker les vidéos par catégorie + videos_par_categorie = {} + + # Liste pour stocker les fichiers à supprimer à la fin + files_to_delete = [] + + for video in video_list: + if os.path.exists(video): + print(f"Affichage de la vidéo : {video}") + + # Lancer la lecture de la vidéo en boucle + video_player.play_video(video, speed) + + # Attendre un peu pour que la vidéo commence à s'afficher + time.sleep(0.5) + + # Classifier la vidéo pendant qu'elle est en lecture + classification = classifier_video(video, liste_joueuses, cpt, video_player, root) + cpt += 1 + + if classification: + joueuse, action, passe_result, new_name = classification + + # Créer une clé unique pour cette catégorie + categorie_key = f"{joueuse}/{action}" + if action == "Passe" and passe_result: + categorie_key += f"/{passe_result}" + + # Ajouter la vidéo à sa catégorie + if categorie_key not in videos_par_categorie: + videos_par_categorie[categorie_key] = [] + + videos_par_categorie[categorie_key].append((video, new_name)) + + # Ajouter le fichier à la liste des fichiers à supprimer + files_to_delete.append(video) + + # S'assurer que la lecture est arrêtée + video_player.stop() + if videos_par_categorie != {}: + # Demander le dossier de destination + base_dir = filedialog.askdirectory(title="Sélectionnez le dossier de destination", parent=root) + if not base_dir: + root.destroy() + return + + + # Traiter chaque catégorie + for categorie, videos in videos_par_categorie.items(): + # Créer le dossier de destination + dest_dir = os.path.join(base_dir, categorie) + os.makedirs(dest_dir, exist_ok=True) + + # Créer les chemins temporaires pour les vidéos + temp_video_paths = [] + for video_path, new_name in videos: + temp_path = os.path.join(dest_dir, new_name) + shutil.copy(video_path, temp_path) + temp_video_paths.append(temp_path) + + # Créer le nom du fichier final concaténé + categorie_name = categorie.replace('/', '_') + output_file = os.path.join(dest_dir, f"{categorie_name}_complet.mp4") + + + # Concaténer les vidéos + if concatener_videos_cv2(temp_video_paths, output_file): + print(f"Vidéos concaténées dans {output_file}") + + # Supprimer les fichiers temporaires + for temp_path in temp_video_paths: + if os.path.exists(temp_path): + os.remove(temp_path) + + # Supprimer les fichiers d'origine + if messagebox.askyesno("Suppression des originaux", "Voulez-vous supprimer les vidéos originales ?", parent=root): + for file_path in files_to_delete: + if os.path.exists(file_path): + try: + os.remove(file_path) + print(f"Fichier original supprimé : {file_path}") + except Exception as e: + print(f"Erreur lors de la suppression de {file_path}: {e}") + + # Détruire la fenêtre principale à la fin + root.destroy() + +# Programme principal +liste_joueuses = ["lilou", "sadio", "ester"] + +# Initialisation de Tk pour le sélecteur de dossier initial +root_initial = tk.Tk() +root_initial.withdraw() +video_folder = filedialog.askdirectory(title="Sélectionnez le dossier contenant les vidéos", parent=root_initial) +root_initial.destroy() + +if video_folder: + videos = [os.path.join(video_folder, f) for f in os.listdir(video_folder) if f.endswith(('.mp4', '.avi', '.mov'))] + organiser_videos(videos, liste_joueuses) + +# import cv2 +# import os +# import shutil +# import tkinter as tk +# from tkinter import ttk, simpledialog, messagebox, filedialog +# import subprocess + +# def get_selection(title, options): +# """Fenêtre avec un menu déroulant pour faire un choix""" +# selection_window = tk.Toplevel() +# selection_window.title(title) +# selection_window.geometry("300x150") + +# tk.Label(selection_window, text=title).pack(pady=5) +# selected_value = tk.StringVar() +# dropdown = ttk.Combobox(selection_window, textvariable=selected_value, values=options, state='readonly') +# dropdown.pack(pady=5) +# dropdown.current(0) + +# def validate(): +# selection_window.destroy() + +# tk.Button(selection_window, text="Valider", command=validate).pack(pady=10) +# selection_window.wait_window() + +# return selected_value.get() + +# def afficher_video(video_path, speed=1.0): +# """Affiche une vidéo avec la vitesse choisie""" +# cap = cv2.VideoCapture(video_path) + +# if not cap.isOpened(): +# print(f"Erreur : Impossible d'ouvrir {video_path}") +# return False + +# fps = int(cap.get(cv2.CAP_PROP_FPS)) +# delay = int(1000 / (fps * speed)) + +# while cap.isOpened(): +# ret, frame = cap.read() +# if not ret: +# break + +# cv2.imshow("Lecture Vidéo", frame) +# if cv2.waitKey(delay) & 0xFF == ord('q'): +# break + +# cap.release() +# cv2.destroyAllWindows() +# return True + +# def classifier_video(video_path, liste_joueuses, cpt): +# """Interface utilisateur pour classifier ou supprimer une vidéo avec des menus déroulants""" +# root = tk.Tk() +# root.withdraw() + +# if messagebox.askyesno("Suppression", "Voulez-vous supprimer cette vidéo ?"): +# os.remove(video_path) +# print(f"Vidéo supprimée : {video_path}") +# return None + +# joueuse = get_selection("Sélectionnez une joueuse", liste_joueuses) +# if not joueuse: +# return None + +# action_types = ["Réception", "Passe", "Défense", "Autre"] +# action = get_selection("Sélectionnez une action", action_types) +# if not action: +# return None + +# passe_types = { +# "Bonne passe (verte)": "Bonne passe", +# "Bonne hauteur, mauvaise zone (bleue)": "Bonne hauteur, mauvaise zone", +# "Bonne zone, mauvaise hauteur (blanche)": "Bonne zone, mauvaise hauteur", +# "Mauvaise passe (rouge)": "Mauvaise passe" +# } +# passe_result = None +# if action == "Passe": +# passe_result = get_selection("Qualité de Passe", list(passe_types.keys())) +# passe_result = passe_types.get(passe_result) + +# new_name = f"{joueuse}_{action}" +# if passe_result: +# new_name += f"_{passe_result}" + +# # On ne change pas l'extension pour conserver le format d'origine +# extension = os.path.splitext(video_path)[-1] +# new_name += f"_{cpt}{extension}" + +# return joueuse, action, passe_result, new_name + +# def concatener_videos_cv2(video_paths, destination_path): +# """Concatène les vidéos en utilisant OpenCV au lieu de FFmpeg""" +# if not video_paths: +# return False + +# if len(video_paths) == 1: +# # S'il n'y a qu'une seule vidéo, la copier simplement +# shutil.copy(video_paths[0], destination_path) +# return True + +# # Récupérer les propriétés de la première vidéo +# cap = cv2.VideoCapture(video_paths[0]) +# frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) +# frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) +# fps = int(cap.get(cv2.CAP_PROP_FPS)) +# cap.release() + +# # Créer l'objet VideoWriter pour la vidéo de sortie +# fourcc = cv2.VideoWriter_fourcc(*'mp4v') # Codec pour mp4 +# out = cv2.VideoWriter(destination_path, fourcc, fps, (frame_width, frame_height)) + +# # Parcourir chaque vidéo et ajouter ses frames à la vidéo de sortie +# for video_path in video_paths: +# cap = cv2.VideoCapture(video_path) +# while True: +# ret, frame = cap.read() +# if not ret: +# break +# out.write(frame) +# cap.release() + +# # Libérer l'objet VideoWriter +# out.release() +# return True + +# def organiser_videos(video_list, liste_joueuses): +# """Affiche et trie une liste de vidéos en fonction des critères choisis""" +# cpt = 0 +# speed_choice = get_selection("Vitesse de Lecture", ["1", "1.5", "2"]) +# speed = float(speed_choice) if speed_choice else 1.0 + +# # Dictionnaire pour stocker les vidéos par catégorie +# videos_par_categorie = {} + +# # Liste pour stocker les fichiers à supprimer à la fin +# files_to_delete = [] + +# for video in video_list: +# if os.path.exists(video): +# print(f"Affichage de la vidéo : {video}") +# afficher_video(video, speed) + +# classification = classifier_video(video, liste_joueuses, cpt) +# cpt += 1 + +# if classification: +# joueuse, action, passe_result, new_name = classification + +# # Créer une clé unique pour cette catégorie +# categorie_key = f"{joueuse}/{action}" +# if action == "Passe" and passe_result: +# categorie_key += f"/{passe_result}" + +# # Ajouter la vidéo à sa catégorie +# if categorie_key not in videos_par_categorie: +# videos_par_categorie[categorie_key] = [] + +# videos_par_categorie[categorie_key].append((video, new_name)) + +# # Ajouter le fichier à la liste des fichiers à supprimer +# files_to_delete.append(video) + +# # Demander le dossier de destination +# base_dir = filedialog.askdirectory(title="Sélectionnez le dossier de destination") +# if not base_dir: +# return + +# # Traiter chaque catégorie +# for categorie, videos in videos_par_categorie.items(): +# # Créer le dossier de destination +# dest_dir = os.path.join(base_dir, categorie) +# os.makedirs(dest_dir, exist_ok=True) + +# # Créer les chemins temporaires pour les vidéos +# temp_video_paths = [] +# for video_path, new_name in videos: +# temp_path = os.path.join(dest_dir, new_name) +# shutil.copy(video_path, temp_path) +# temp_video_paths.append(temp_path) + +# # Créer le nom du fichier final concaténé +# categorie_name = categorie.replace('/', '_') +# output_file = os.path.join(dest_dir, f"{categorie_name}_complet.mp4") + +# # Concaténer les vidéos +# if concatener_videos_cv2(temp_video_paths, output_file): +# print(f"Vidéos concaténées dans {output_file}") + +# # Supprimer les fichiers temporaires +# for temp_path in temp_video_paths: +# if os.path.exists(temp_path): +# os.remove(temp_path) + +# # Supprimer les fichiers d'origine +# if messagebox.askyesno("Suppression des originaux", "Voulez-vous supprimer les vidéos originales ?"): +# for file_path in files_to_delete: +# if os.path.exists(file_path): +# try: +# os.remove(file_path) +# print(f"Fichier original supprimé : {file_path}") +# except Exception as e: +# print(f"Erreur lors de la suppression de {file_path}: {e}") + +# # Programme principal +# liste_joueuses = ["lilou", "sadio", "ester"] +# video_folder = filedialog.askdirectory(title="Sélectionnez le dossier contenant les vidéos") +# if video_folder: +# videos = [os.path.join(video_folder, f) for f in os.listdir(video_folder) if f.endswith(('.mp4', '.avi', '.mov'))] +# organiser_videos(videos, liste_joueuses) + + diff --git "a/chercher_cam\303\251ra.py" "b/chercher_cam\303\251ra.py" index 0dbb4d7..97ed0c6 100644 --- "a/chercher_cam\303\251ra.py" +++ "b/chercher_cam\303\251ra.py" @@ -1,98 +1,47 @@ import cv2 import tkinter as tk from tkinter import ttk -from PIL import Image, ImageTk -import threading -class CameraSelector: - def __init__(self, root): - self.root = root - self.root.title("Sélection de caméra") - self.cameras = [] - self.current_camera = None - self.video_source = None - - # Détection des caméras disponibles - self.detect_cameras() - - # Interface graphique - self.create_widgets() - - def detect_cameras(self): - """Détecte toutes les caméras disponibles jusqu'à l'index 10""" - self.cameras = [] - for i in range(10): # Vous pouvez augmenter ce nombre si nécessaire - cap = cv2.VideoCapture(i) - if cap.isOpened(): - self.cameras.append(i) - cap.release() - - def create_widgets(self): - """Crée l'interface utilisateur""" - # Cadre principal - main_frame = ttk.Frame(self.root, padding=10) - main_frame.pack() - - # Sélection de caméra - ttk.Label(main_frame, text="Caméras détectées:").pack() - self.camera_var = tk.StringVar() - self.cam_selector = ttk.Combobox(main_frame, textvariable=self.camera_var) - self.cam_selector['values'] = [f"Caméra {i}" for i in self.cameras] - self.cam_selector.pack(pady=5) - - # Bouton de confirmation - ttk.Button(main_frame, text="Ouvrir", command=self.start_camera).pack(pady=5) - - # Zone d'affichage vidéo - self.video_label = ttk.Label(main_frame) - self.video_label.pack() - - def start_camera(self): - """Démarre le flux vidéo de la caméra sélectionnée""" - if self.current_camera is not None: - self.stop_camera() - - selected = self.cam_selector.current() - if selected == -1: - return - - self.video_source = self.cameras[selected] - self.current_camera = cv2.VideoCapture(self.video_source) - - # Démarrer l'affichage vidéo dans un thread séparé - self.thread = threading.Thread(target=self.show_video) - self.thread.daemon = True - self.thread.start() - - def show_video(self): - """Affiche le flux vidéo dans le label""" - while self.current_camera.isOpened(): - ret, frame = self.current_camera.read() - if ret: - # Conversion de l'image pour Tkinter - frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - img = Image.fromarray(frame) - imgtk = ImageTk.PhotoImage(image=img) - - # Mise à jour de l'interface - self.video_label.configure(image=imgtk) - self.video_label.image = imgtk - else: - break - - def stop_camera(self): - """Arrête le flux vidéo""" - if self.current_camera is not None: - self.current_camera.release() - self.video_label.configure(image='') - self.video_label.image = None - - def on_close(self): - self.stop_camera() - self.root.destroy() +def detect_cameras(): + """Détecte les caméras connectées à l'ordinateur.""" + cameras = [] + for i in range(4): # Vérifie les 4 premiers indices de caméra + cap = cv2.VideoCapture(i) + if cap.isOpened(): + cameras.append(i) + cap.release() + return cameras + +def select_camera(): + """Affiche une interface Tkinter pour sélectionner une caméra.""" + cameras = detect_cameras() + if not cameras: + print("Aucune caméra détectée.") + return None + + def on_select(): + nonlocal selected_camera + selected_camera = int(camera_var.get()) + root.destroy() -if __name__ == "__main__": root = tk.Tk() - app = CameraSelector(root) - root.protocol("WM_DELETE_WINDOW", app.on_close) - root.mainloop() \ No newline at end of file + root.title("Sélection de la caméra") + + tk.Label(root, text="Sélectionnez une caméra :").pack(pady=10) + + camera_var = tk.StringVar(value=str(cameras[0])) + for cam in cameras: + ttk.Radiobutton(root, text=f"Caméra {cam}", variable=camera_var, value=str(cam)).pack(anchor=tk.W) + + ttk.Button(root, text="Valider", command=on_select).pack(pady=10) + + selected_camera = None + root.mainloop() + return selected_camera + +if __name__ == "__main__": + camera_index = select_camera() + if camera_index is not None: + print(f"Caméra sélectionnée : {camera_index}") + else: + print("Aucune caméra sélectionnée.") \ No newline at end of file -- GitLab