Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| import tempfile | |
| import os | |
| class VideoInpaintingApp: | |
| def __init__(self): | |
| self.current_video = None | |
| self.current_frame = None | |
| self.fps = 30 | |
| self.total_frames = 0 | |
| def load_video(self, video_path): | |
| """Charge la vidéo et retourne des informations""" | |
| if video_path is None: | |
| return None, "Aucune vidéo chargée", 0, 0, 0 | |
| cap = cv2.VideoCapture(video_path) | |
| self.fps = int(cap.get(cv2.CAP_PROP_FPS)) | |
| self.total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | |
| duration = self.total_frames / self.fps if self.fps > 0 else 0 | |
| cap.release() | |
| self.current_video = video_path | |
| info = f"✅ Vidéo chargée: {self.total_frames} frames, {self.fps} FPS, {duration:.2f}s" | |
| max_frame = max(0, self.total_frames - 1) | |
| return video_path, info, 0, max_frame, max_frame | |
| def get_frame(self, video_path, frame_number): | |
| """Récupère une frame spécifique de la vidéo""" | |
| if video_path is None: | |
| return None | |
| try: | |
| cap = cv2.VideoCapture(video_path) | |
| cap.set(cv2.CAP_PROP_POS_FRAMES, int(frame_number)) | |
| ret, frame = cap.read() | |
| cap.release() | |
| if ret: | |
| frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| self.current_frame = frame_rgb | |
| return Image.fromarray(frame_rgb) | |
| except Exception as e: | |
| print(f"Erreur lors de la lecture de la frame: {e}") | |
| return None | |
| def apply_inpainting(self, video_path, start_frame, end_frame, mask_image, progress=gr.Progress()): | |
| """Applique l'inpainting sur la plage de frames sélectionnée""" | |
| if video_path is None: | |
| return None, "❌ Veuillez charger une vidéo" | |
| if mask_image is None: | |
| return None, "❌ Veuillez dessiner un masque sur la zone à supprimer" | |
| try: | |
| progress(0, desc="Préparation...") | |
| # Ouvrir la vidéo | |
| cap = cv2.VideoCapture(video_path) | |
| fps = int(cap.get(cv2.CAP_PROP_FPS)) | |
| width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
| height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
| total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | |
| # Créer un fichier temporaire pour la sortie | |
| output_path = tempfile.mktemp(suffix='.mp4') | |
| fourcc = cv2.VideoWriter_fourcc(*'mp4v') | |
| out = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) | |
| # Extraire le masque | |
| if isinstance(mask_image, dict) and 'mask' in mask_image: | |
| mask = np.array(mask_image['mask']) | |
| else: | |
| mask = np.array(mask_image) | |
| # Redimensionner le masque si nécessaire | |
| if mask.shape[:2] != (height, width): | |
| mask = cv2.resize(mask, (width, height)) | |
| # Convertir en masque binaire | |
| if len(mask.shape) == 3: | |
| mask = cv2.cvtColor(mask, cv2.COLOR_RGB2GRAY) | |
| _, mask_binary = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY) | |
| # Traiter chaque frame | |
| for frame_idx in range(total_frames): | |
| ret, frame = cap.read() | |
| if not ret: | |
| break | |
| # Appliquer l'inpainting seulement dans la plage sélectionnée | |
| if start_frame <= frame_idx <= end_frame: | |
| inpainted = cv2.inpaint(frame, mask_binary, 3, cv2.INPAINT_TELEA) | |
| out.write(inpainted) | |
| else: | |
| out.write(frame) | |
| # Mise à jour de la progression | |
| progress((frame_idx + 1) / total_frames, desc=f"Frame {frame_idx + 1}/{total_frames}") | |
| cap.release() | |
| out.release() | |
| return output_path, "✅ Inpainting terminé avec succès!" | |
| except Exception as e: | |
| return None, f"❌ Erreur: {str(e)}" | |
| # Créer l'instance de l'application | |
| app = VideoInpaintingApp() | |
| # Interface Gradio | |
| with gr.Blocks(title="Inpainting Vidéo IA", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # 🎬 Inpainting Vidéo avec IA | |
| ### Supprimez des objets indésirables de vos vidéos | |
| **Instructions:** | |
| 1. Chargez votre vidéo | |
| 2. Sélectionnez la plage de frames avec les curseurs | |
| 3. Dessinez sur la zone à supprimer (pinceau blanc) | |
| 4. Cliquez sur "Supprimer l'objet sélectionné" | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 📁 1. Charger la vidéo") | |
| video_input = gr.Video(label="Votre vidéo") | |
| video_info = gr.Textbox(label="Informations", interactive=False) | |
| gr.Markdown("### ⏱️ 2. Sélectionner la plage") | |
| start_frame = gr.Slider( | |
| minimum=0, | |
| maximum=100, | |
| value=0, | |
| step=1, | |
| label="Frame de début" | |
| ) | |
| end_frame = gr.Slider( | |
| minimum=0, | |
| maximum=100, | |
| value=100, | |
| step=1, | |
| label="Frame de fin" | |
| ) | |
| current_frame_slider = gr.Slider( | |
| minimum=0, | |
| maximum=100, | |
| value=0, | |
| step=1, | |
| label="Prévisualiser la frame" | |
| ) | |
| with gr.Column(scale=2): | |
| gr.Markdown("### 🎨 3. Dessiner la zone à supprimer") | |
| frame_preview = gr.Image( | |
| label="Dessinez sur la zone à supprimer", | |
| type="pil", | |
| tool="sketch", | |
| brush=gr.Brush(colors=["#FFFFFF"], default_size=20) | |
| ) | |
| gr.Markdown("### ⚙️ 4. Lancer l'inpainting") | |
| process_btn = gr.Button("🚀 Supprimer l'objet sélectionné", variant="primary", size="lg") | |
| status_text = gr.Textbox(label="Statut", interactive=False) | |
| output_video = gr.Video(label="Vidéo traitée") | |
| # Événements | |
| video_input.change( | |
| fn=app.load_video, | |
| inputs=[video_input], | |
| outputs=[video_input, video_info, start_frame, end_frame, current_frame_slider] | |
| ) | |
| current_frame_slider.change( | |
| fn=app.get_frame, | |
| inputs=[video_input, current_frame_slider], | |
| outputs=[frame_preview] | |
| ) | |
| process_btn.click( | |
| fn=app.apply_inpainting, | |
| inputs=[video_input, start_frame, end_frame, frame_preview], | |
| outputs=[output_video, status_text] | |
| ) | |
| demo.launch() |