import cv2 import mediapipe as mp import tempfile import gradio as gr import os import subprocess mp_face_mesh = mp.solutions.face_mesh def process_video_with_landmarks(video_path): cap = cv2.VideoCapture(video_path) if not cap.isOpened(): return "❌ エラー: 動画ファイルを開けませんでした。形式を確認してください。" width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = cap.get(cv2.CAP_PROP_FPS) if fps == 0: fps = 25 # fallback # 一時AVIファイルで書き出す temp_avi = tempfile.NamedTemporaryFile(delete=False, suffix=".avi") out = cv2.VideoWriter(temp_avi.name, cv2.VideoWriter_fourcc(*'XVID'), fps, (width, height)) face_mesh = mp_face_mesh.FaceMesh( static_image_mode=False, max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5 ) while cap.isOpened(): ret, frame = cap.read() if not ret: break rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = face_mesh.process(rgb) if results.multi_face_landmarks: for face_landmarks in results.multi_face_landmarks: for lm in face_landmarks.landmark: x = int(lm.x * width) y = int(lm.y * height) cv2.circle(frame, (x, y), 1, (255, 0, 0), -1) out.write(frame) cap.release() out.release() face_mesh.close() # mp4へ変換 temp_mp4 = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") ffmpeg_cmd = [ "ffmpeg", "-y", "-i", temp_avi.name, "-vcodec", "libx264", "-crf", "23", "-preset", "medium", temp_mp4.name ] try: subprocess.run(ffmpeg_cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except subprocess.CalledProcessError: return "❌ ffmpegによるmp4変換に失敗しました。" return temp_mp4.name def gradio_interface(video_file): if isinstance(video_file, str) and os.path.isfile(video_file): return process_video_with_landmarks(video_file) else: return "❌ 無効なファイルがアップロードされました。" iface = gr.Interface( fn=gradio_interface, inputs=gr.Video(label="動画ファイルをアップロード"), outputs=gr.File(label="再生可能なランドマーク付きmp4動画"), title="Face Mesh ランドマーク付き動画出力(再生保証)", description="動画に顔ランドマーク(青点)を描画し、再生互換性のあるmp4形式で出力します。" ) iface.launch()