import gradio as gr from theme import create_zen_theme from src.frame_bridge import FrameBridge, BatchProcessor import os # Frame Bridge インスタンスを作成 frame_bridge = FrameBridge(exclude_edge_frames=True) batch_processor = BatchProcessor(exclude_edge_frames=True) def process_sample_videos(): """サンプル動画を処理する関数""" video1_path = "examples/assets/example/REI/input/REI-001.mp4" video2_path = "examples/assets/example/REI/input/REI-002.mp4" if not os.path.exists(video1_path) or not os.path.exists(video2_path): return "サンプル動画ファイルが見つかりません。", None, None, None, 0.0 return frame_bridge.process_video_bridge(video1_path, video2_path) def process_batch_videos(input_folder, output_folder, mode, filename): """バッチ動画処理関数""" if not input_folder or not os.path.exists(input_folder): return "入力フォルダが指定されていないか、存在しません。", None if not output_folder: output_folder = "output" try: # バッチプロセッサを初期化 processor = BatchProcessor(output_dir=output_folder, exclude_edge_frames=True) if mode == "順次結合": success, final_output, results = processor.process_sequential_merge(input_folder, filename or "merged_sequence.mp4") if success: report = processor.generate_report(results) return f"✅ 順次結合完了!\n📁 出力: {final_output}\n\n{report}", final_output else: return "❌ 順次結合に失敗しました", None elif mode == "ペア結合": success, output_files, results = processor.process_pairwise_merge(input_folder) if success: report = processor.generate_report(results) # 最初の出力ファイルを返す(複数ある場合) first_output = output_files[0] if output_files else None return f"✅ ペア結合完了!\n📁 出力ファイル数: {len(output_files)}\n\n{report}", first_output else: return "❌ ペア結合に失敗しました", None except Exception as e: return f"処理エラー: {str(e)}", None def process_video_bridge(video1, video2, progress=gr.Progress()): """2つの動画を分析して最適な結合点を見つけ、結合する関数""" if video1 is None or video2 is None: return "2つの動画ファイルをアップロードしてください。", None, None, None, None try: progress(0.1, "動画を分析中...") result_text, output_path, frame1_path, frame2_path, similarity = frame_bridge.process_video_bridge(video1, video2) progress(1.0, "完了!") return result_text, output_path, frame1_path, frame2_path, similarity except Exception as e: return f"処理エラー: {str(e)}", None, None, None, None def analyze_video_details(video_path): """動画の詳細情報を分析する関数""" if video_path is None: return "" return frame_bridge.processor.analyze_video_details(video_path) # Gradioインターフェースの作成 def create_interface(): """Gradioインターフェースを作成する関数""" theme = create_zen_theme() with gr.Blocks(theme=theme, title="Frame Bridge - 動画フレーム結合アプリ") as demo: # ヘッダー gr.HTML("""

🎬 Frame Bridge

2つの動画を最適なフレームで自動結合するAIアプリ

""") # タブの作成 with gr.Tabs(): # 単体処理タブ with gr.TabItem("🎥 単体処理"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📹 動画アップロード") video1_input = gr.Video( label="🎥 動画1(前半)", height=300 ) video1_info = gr.Textbox( label="📊 動画1の情報", lines=6, interactive=False ) video2_input = gr.Video( label="🎥 動画2(後半)", height=300 ) video2_info = gr.Textbox( label="📊 動画2の情報", lines=6, interactive=False ) bridge_btn = gr.Button("🌉 フレームブリッジ実行", variant="primary", size="lg") sample_btn = gr.Button("🎬 サンプル動画で試す", variant="secondary", size="lg") with gr.Column(scale=1): gr.Markdown("### 🎯 結合結果") result_text = gr.Textbox( label="📝 分析結果", lines=10, show_copy_button=True ) merged_video = gr.Video( label="🎬 結合された動画", height=300 ) # 接続フレーム表示 with gr.Row(): connection_frame1 = gr.Image( label="🔗 動画1の接続フレーム", height=200 ) connection_frame2 = gr.Image( label="🔗 動画2の接続フレーム", height=200 ) similarity_score = gr.Number( label="📈 フレーム類似度スコア", precision=3 ) # バッチ処理タブ with gr.TabItem("📁 バッチ処理"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📂 フォルダ指定") input_folder = gr.Textbox( label="📥 入力フォルダパス", placeholder="例: examples/assets/example/REI/input", value="examples/assets/example/REI/input" ) output_folder = gr.Textbox( label="📤 出力フォルダパス", placeholder="例: examples/assets/example/REI/output", value="examples/assets/example/REI/output" ) processing_mode = gr.Radio( label="🔄 処理モード", choices=["順次結合", "ペア結合"], value="順次結合", info="順次結合: 全動画を1つに結合 / ペア結合: 2つずつペアで結合" ) output_filename = gr.Textbox( label="📄 出力ファイル名 (順次結合のみ)", placeholder="REI_merged_sequence.mp4", value="REI_merged_sequence.mp4" ) batch_btn = gr.Button("🚀 バッチ処理実行", variant="primary", size="lg") with gr.Column(scale=1): gr.Markdown("### 📊 処理結果") batch_result = gr.Textbox( label="📝 バッチ処理結果", lines=15, show_copy_button=True ) batch_output = gr.Video( label="🎬 出力動画(プレビュー)", height=300 ) # 動画情報の自動更新 def update_video1_info(video): if video is None: return "" return analyze_video_details(video) def update_video2_info(video): if video is None: return "" return analyze_video_details(video) video1_input.change( fn=update_video1_info, inputs=video1_input, outputs=video1_info ) video2_input.change( fn=update_video2_info, inputs=video2_input, outputs=video2_info ) # メイン処理 bridge_btn.click( fn=process_video_bridge, inputs=[video1_input, video2_input], outputs=[result_text, merged_video, connection_frame1, connection_frame2, similarity_score] ) # サンプル動画処理 sample_btn.click( fn=process_sample_videos, inputs=[], outputs=[result_text, merged_video, connection_frame1, connection_frame2, similarity_score] ) # バッチ処理 batch_btn.click( fn=process_batch_videos, inputs=[input_folder, output_folder, processing_mode, output_filename], outputs=[batch_result, batch_output] ) # 使用方法の説明 gr.Markdown("---") gr.Markdown("### 🎯 使用方法") with gr.Tabs(): with gr.TabItem("🎥 単体処理"): gr.Markdown("1. **動画1(前半)**: 結合したい最初の動画をアップロード") gr.Markdown("2. **動画2(後半)**: 結合したい2番目の動画をアップロード") gr.Markdown("3. **フレームブリッジ実行**: AIが最適な接続点を自動検出して結合") gr.Markdown("4. **サンプル動画で試す**: assetsフォルダのサンプル動画で機能をテスト") with gr.TabItem("📁 バッチ処理"): gr.Markdown("1. **入力フォルダ**: 動画ファイルが格納されたフォルダパスを指定") gr.Markdown("2. **出力フォルダ**: 結合結果を保存するフォルダパスを指定") gr.Markdown("3. **処理モード選択**:") gr.Markdown(" - **順次結合**: フォルダ内の全動画を名前順に1つの動画に結合") gr.Markdown(" - **ペア結合**: 動画を2つずつペアにして結合(複数の出力ファイル)") gr.Markdown("4. **出力ファイル名**: 順次結合の場合の最終ファイル名を指定") gr.Markdown("5. **バッチ処理実行**: 指定した設定で一括処理を開始") gr.Markdown("### 🔬 技術的特徴") gr.Markdown("- **SSIM(構造的類似性指標)**: フレーム間の視覚的類似度を高精度で計算") gr.Markdown("- **自動最適化**: 動画1の終了部分と動画2の開始部分から最適な接続点を検出") gr.Markdown("- **スムーズな結合**: 視覚的に自然な動画結合を実現") gr.Markdown("- **バッチ処理**: 複数動画の自動処理とレポート生成") gr.Markdown("- **ファイル名ソート**: 自然順序でのファイル名ソートによる正確な順序処理") # ZENテーマの説明 gr.HTML("""

🧘‍♀️ ZENテーマ

和モダンなデザインで、直感的な動画編集体験を提供

単体処理とバッチ処理の両方に対応した高機能動画結合アプリ

""") return demo if __name__ == "__main__": # インターフェースを作成 demo = create_interface() # アプリケーションを実行 demo.launch( server_name="0.0.0.0", server_port=7860, share=False, debug=True )